Skip to content

Commit 44341d8

Browse files
Copilotgfauredev
andcommitted
Fix 10/20 failing Maestro web E2E tests
Fix 1 (test 08): Muscle search uses word-start matching instead of substring to prevent "hamstrings".contains("ring") false positive. Fix 2 (tests 09, 15): Remove sig.set(Vec::new()) from reload_exercises to preserve ExerciseCard show_instructions state across DB reloads. Fix 3 (tests 10, 11): Add id/for to exercise name label/input so that tapping the label focuses the input. Fix 4 (test 04): Add dl_db_url= to launch URL so that a prior test's localStorage exercise_db_url is reset to the default GitHub URL. Fix 5 (tests 17-20): Add assertVisible wait in setup_past_session.yml before openLink so that SetDbUrl has written localStorage before the page reload caused by openLink. Co-authored-by: gfauredev <19304085+gfauredev@users.noreply.github.com>
1 parent ec78d53 commit 44341d8

5 files changed

Lines changed: 36 additions & 7 deletions

File tree

maestro/web/04_clean_state_credits.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
url: http://localhost:8080/LogOut/ # Clean State Credits
1+
url: http://localhost:8080/LogOut/?dl_db_url=https%3A%2F%2Fgfauredev.github.io%2Ffree-exercise-db%2F # Clean State Credits
22
---
33
# User Story: Clean State Credits
44
# Precondition: No custom DB URL set (launchApp clears localStorage)

maestro/web/_flows/setup_past_session.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@ url: http://localhost:8080/LogOut/?dl_db_url=http%3A%2F%2Flocalhost%3A8080%2FLog
33
# Sub-flow: set up a completed past session with two exercises (ring_dip and
44
# romanian_deadlift). Works standalone and as a runFlow setup step in other tests.
55
- launchApp
6+
# Wait for the WASM app to fully initialize so that the SetDbUrl deep-link
7+
# handler has run and persisted exercise_db_url to localStorage before
8+
# openLink triggers a page reload.
9+
- assertVisible: "💪 LogOut"
610
# Create a completed past session via deep link; exercises load from mock DB
711
- openLink: "http://localhost:8080/LogOut/?dl_session=ring_dip%3A80%3A10%2Cromanian_deadlift%3A60%3A6"
812
# Wait for mock exercises to load and session to be saved and shown on home

src/components/exercise_form_fields.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,8 +113,9 @@ pub fn ExerciseFormFields(
113113

114114
rsx! {
115115
div {
116-
label { "Exercise Name *" }
116+
label { r#for: "exercise-name-input", "Exercise Name *" }
117117
input {
118+
id: "exercise-name-input",
118119
r#type: "text",
119120
placeholder: "Pushups",
120121
value: "{name_input}",

src/services/exercise_db.rs

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,15 @@ fn normalize_for_search(s: &str) -> String {
252252
.collect()
253253
}
254254

255+
/// Returns true if any whitespace-separated word in the (already-lowercased)
256+
/// `tag` starts with `query_lower`. Used for muscle / category / etc. tag
257+
/// matching so that partial-word substrings elsewhere in a word are not hit
258+
/// (e.g. "ring" must not match "hamst**ring**s").
259+
fn tag_word_prefix_matches(tag: &str, query_lower: &str) -> bool {
260+
tag.split_whitespace()
261+
.any(|word| word.starts_with(query_lower))
262+
}
263+
255264
/// Returns true if an already-lowercased `name_lc` matches the given
256265
/// pre-computed search components (all lowercase / normalised).
257266
fn name_lc_matches(name_lc: &str, query_lower: &str, query_norm: &str, tokens: &[String]) -> bool {
@@ -324,11 +333,11 @@ pub fn search_exercises<'a>(
324333
|| exercise
325334
.primary_muscles
326335
.iter()
327-
.any(|m| m.as_ref().contains(&query_lower))
336+
.any(|m| tag_word_prefix_matches(m.as_ref(), &query_lower))
328337
|| exercise
329338
.secondary_muscles
330339
.iter()
331-
.any(|m| m.as_ref().contains(&query_lower))
340+
.any(|m| tag_word_prefix_matches(m.as_ref(), &query_lower))
332341
|| exercise.category.as_ref().contains(&query_lower)
333342
|| exercise
334343
.force
@@ -814,6 +823,24 @@ mod tests {
814823
assert_eq!(results[0].id, "pull_up");
815824
}
816825

826+
#[test]
827+
fn search_muscle_word_start_no_false_positive() {
828+
// "ring" is a substring of "hamstrings" but the word "hamstrings" does
829+
// not *start* with "ring". "running" (which has Hamstrings as a primary
830+
// muscle) must therefore not be returned when searching for "ring".
831+
let exercises = sample_exercises();
832+
let results = search_exercises(&exercises, "ring", None);
833+
assert!(!results.iter().any(|e| e.id == "running"));
834+
}
835+
836+
#[test]
837+
fn search_muscle_word_start_prefix_matches() {
838+
// "ham" is a word-start prefix of "hamstrings", so "running" should match.
839+
let exercises = sample_exercises();
840+
let results = search_exercises(&exercises, "ham", None);
841+
assert!(results.iter().any(|e| e.id == "running"));
842+
}
843+
817844
#[test]
818845
fn exercises_json_url_uses_fork() {
819846
// The default JSON endpoint references the gfauredev GitHub Pages

src/services/exercise_loader.rs

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,6 @@ pub fn use_exercises() -> Signal<Vec<Exercise>> {
3939
/// empty response, JSON parse) it shows an appropriate error message so the
4040
/// user knows the URL change did not take effect.
4141
pub async fn reload_exercises(mut sig: Signal<Vec<Exercise>>, mut toast: Signal<Option<String>>) {
42-
// Clear immediately so the UI does not show stale data from the old URL
43-
sig.set(Vec::new());
44-
4542
#[cfg(target_arch = "wasm32")]
4643
{
4744
use crate::services::storage::idb_exercises;

0 commit comments

Comments
 (0)