609 lines
25 KiB
Python
609 lines
25 KiB
Python
"""
|
|
Step 12 — Timers & Combo Execution Tests
|
|
|
|
- test_tabata_form_inputs_shown: TABATA combo form shows work/rest time inputs
|
|
- test_tabata_form_inputs_not_shown_for_other_types: Work/rest inputs hidden for non-TABATA types
|
|
- test_tabata_timer_config_saved: TABATA work/rest times saved to timer_config
|
|
- test_amrap_label_in_session_form: AMRAP combo item shows "Duration (min)" label
|
|
- test_amrap_label_in_session_detail: AMRAP combo item shows minutes in detail view
|
|
- test_emom_timer_visible: EMOM combo steps show a timer display
|
|
- test_emom_timer_pause_resume: Pause button toggles pause state on EMOM timer
|
|
- test_emom_done_early_advances: Tapping Done during EMOM advances to next step immediately
|
|
- test_emom_auto_advance: EMOM timer auto-advances to next step after 60 seconds
|
|
- test_amrap_timer_visible: AMRAP combo steps show a timer display
|
|
- test_amrap_manual_done_cycles: Done button manually advances through AMRAP exercises
|
|
- test_amrap_timeup_screen: Time-up overlay appears when AMRAP timer expires
|
|
- test_amrap_continue_advances: Continue after time-up advances past AMRAP combo
|
|
- test_tabata_work_phase_shows_timer: TABATA shows timer on work phase
|
|
- test_tabata_rest_screen_after_work: REST screen appears after work phase timer expires
|
|
- test_tabata_advances_after_rest: Next exercise appears after rest phase timer expires
|
|
- test_tabata_pause_during_work: Pause freezes TABATA work timer
|
|
- test_tabata_pause_during_rest: Pause freezes TABATA rest timer
|
|
- test_non_timed_combo_unchanged: NONE-type combos still show final-round screen as before
|
|
"""
|
|
import pytest
|
|
from playwright.sync_api import Page, expect
|
|
|
|
|
|
# ── helpers ───────────────────────────────────────────────────────────────────
|
|
|
|
|
|
def _wait_for_db(page: Page, app_url: str):
|
|
page.goto(app_url)
|
|
page.wait_for_function(
|
|
"document.documentElement.getAttribute('data-db-ready') === 'true'",
|
|
timeout=15000,
|
|
)
|
|
|
|
|
|
def _wait_for_execute(page: Page):
|
|
page.wait_for_selector('[data-testid="execute-session-title"]', timeout=10000)
|
|
page.wait_for_timeout(400)
|
|
|
|
|
|
def _create_exercise(page: Page, title: str, default_reps=10, default_weight=0.0):
|
|
return page.evaluate(f"""async () => {{
|
|
const userId = await window.__repos.settings.get('user_uuid')
|
|
return window.__repos.exercises.create({{
|
|
title: {repr(title)}, description: '', asymmetric: false, alternate: false,
|
|
image_urls: '[]', video_urls: '[]',
|
|
default_reps: {default_reps}, default_weight: {default_weight},
|
|
created_by: userId
|
|
}})
|
|
}}""")
|
|
|
|
|
|
def _create_timed_combo(page: Page, title: str, combo_type: str, exercise_ids: list,
|
|
timer_config: dict = None):
|
|
if timer_config is None:
|
|
timer_config = {}
|
|
import json
|
|
tc_json = json.dumps(timer_config)
|
|
ex_json = str([{"exerciseId": eid, "defaultReps": 10, "defaultWeight": 0}
|
|
for eid in exercise_ids]).replace("'", '"')
|
|
return page.evaluate(f"""async () => {{
|
|
const userId = await window.__repos.settings.get('user_uuid')
|
|
const comboId = await window.__repos.combos.create({{
|
|
title: {repr(title)}, description: '', type: {repr(combo_type)},
|
|
timer_config: {tc_json}, created_by: userId
|
|
}})
|
|
const exercises = {ex_json}
|
|
await window.__repos.combos.setExercises(comboId, exercises)
|
|
return comboId
|
|
}}""")
|
|
|
|
|
|
def _create_session_with_items(page: Page, title: str, items: list):
|
|
import json
|
|
items_json = json.dumps(items)
|
|
return page.evaluate(f"""async () => {{
|
|
const userId = await window.__repos.settings.get('user_uuid')
|
|
const sessId = await window.__repos.sessions.create({{
|
|
title: {repr(title)}, description: '', created_by: userId
|
|
}})
|
|
const items = {items_json}
|
|
await window.__repos.sessions.setItems(sessId, items.map((it, i) => ({{
|
|
item_id: it.item_id, item_type: it.item_type,
|
|
position: i, repetitions: it.repetitions, weight: it.weight || 0
|
|
}})))
|
|
return sessId
|
|
}}""")
|
|
|
|
|
|
def _clean_all(page: Page):
|
|
page.evaluate("""async () => {
|
|
const sessions = await window.__repos.sessions.getAll()
|
|
for (const s of sessions) await window.__repos.sessions.delete(s.id)
|
|
const combos = await window.__repos.combos.getAll()
|
|
for (const c of combos) await window.__repos.combos.delete(c.id)
|
|
const exercises = await window.__repos.exercises.getAll()
|
|
for (const e of exercises) await window.__repos.exercises.delete(e.id)
|
|
}""")
|
|
|
|
|
|
def _navigate_to_execute(page: Page, app_url: str, sess_id: str):
|
|
page.goto(f"{app_url}#/sessions/{sess_id}/execute")
|
|
_wait_for_execute(page)
|
|
|
|
|
|
# ── Combo form ────────────────────────────────────────────────────────────────
|
|
|
|
|
|
def test_tabata_form_inputs_shown(page: Page, app_url: str, browser_name: str):
|
|
"""TABATA combo form shows work time and rest time inputs."""
|
|
_wait_for_db(page, app_url)
|
|
page.goto(f"{app_url}#/combos/new")
|
|
page.wait_for_selector('[data-testid="combo-type"]', timeout=5000)
|
|
|
|
# Before selecting TABATA, inputs should not be visible
|
|
expect(page.locator('[data-testid="combo-tabata-work-time"]')).not_to_be_visible()
|
|
expect(page.locator('[data-testid="combo-tabata-rest-time"]')).not_to_be_visible()
|
|
|
|
# Select TABATA
|
|
page.select_option('[data-testid="combo-type"]', "TABATA")
|
|
expect(page.locator('[data-testid="combo-tabata-work-time"]')).to_be_visible()
|
|
expect(page.locator('[data-testid="combo-tabata-rest-time"]')).to_be_visible()
|
|
|
|
|
|
def test_tabata_form_inputs_not_shown_for_other_types(page: Page, app_url: str, browser_name: str):
|
|
"""Work/rest inputs are hidden for EMOM and AMRAP types."""
|
|
_wait_for_db(page, app_url)
|
|
page.goto(f"{app_url}#/combos/new")
|
|
page.wait_for_selector('[data-testid="combo-type"]', timeout=5000)
|
|
|
|
for combo_type in ["EMOM", "AMRAP", "NONE"]:
|
|
page.select_option('[data-testid="combo-type"]', combo_type)
|
|
expect(page.locator('[data-testid="combo-tabata-work-time"]')).not_to_be_visible()
|
|
expect(page.locator('[data-testid="combo-tabata-rest-time"]')).not_to_be_visible()
|
|
|
|
|
|
def test_tabata_timer_config_saved(page: Page, app_url: str, browser_name: str):
|
|
"""TABATA work/rest times are persisted in timer_config."""
|
|
_wait_for_db(page, app_url)
|
|
_clean_all(page)
|
|
|
|
page.goto(f"{app_url}#/combos/new")
|
|
page.wait_for_selector('[data-testid="combo-type"]', timeout=5000)
|
|
|
|
page.fill('[data-testid="combo-title"]', "My Tabata")
|
|
page.select_option('[data-testid="combo-type"]', "TABATA")
|
|
page.fill('[data-testid="combo-tabata-work-time"]', "15")
|
|
page.fill('[data-testid="combo-tabata-rest-time"]', "5")
|
|
page.click('[data-testid="combo-save"]')
|
|
page.wait_for_selector('[data-testid="combo-detail-title"]', timeout=8000)
|
|
|
|
combo_id = page.url.split("/")[-1]
|
|
timer_config = page.evaluate(f"""async () => {{
|
|
const c = await window.__repos.combos.getById({repr(combo_id)})
|
|
return c.timer_config
|
|
}}""")
|
|
assert timer_config.get("workTime") == 15
|
|
assert timer_config.get("restTime") == 5
|
|
|
|
|
|
# ── Session form / detail labels for AMRAP ────────────────────────────────────
|
|
|
|
|
|
def test_amrap_label_in_session_form(page: Page, app_url: str, browser_name: str):
|
|
"""AMRAP combo items show 'Duration (min)' instead of 'Reps' in session form."""
|
|
_wait_for_db(page, app_url)
|
|
_clean_all(page)
|
|
|
|
ex_id = _create_exercise(page, "Jump")
|
|
amrap_id = _create_timed_combo(page, "AMRAP Combo", "AMRAP", [ex_id])
|
|
none_id = _create_timed_combo(page, "None Combo", "NONE", [ex_id])
|
|
|
|
page.goto(f"{app_url}#/sessions/new")
|
|
page.wait_for_selector('[data-testid="session-title"]', timeout=5000)
|
|
|
|
# Add AMRAP combo
|
|
page.select_option('[data-testid="session-item-type-select"]', "combo")
|
|
page.wait_for_timeout(300)
|
|
page.select_option('[data-testid="session-item-select"]', amrap_id)
|
|
page.click('[data-testid="session-item-add"]')
|
|
page.wait_for_timeout(300)
|
|
|
|
# The reps label for AMRAP item should say "Duration (min)"
|
|
label = page.locator('[data-testid="session-item-0"] label').first.inner_text()
|
|
assert "min" in label.lower() or "duration" in label.lower()
|
|
|
|
# Add NONE combo
|
|
page.select_option('[data-testid="session-item-type-select"]', "combo")
|
|
page.wait_for_timeout(300)
|
|
page.select_option('[data-testid="session-item-select"]', none_id)
|
|
page.click('[data-testid="session-item-add"]')
|
|
page.wait_for_timeout(300)
|
|
|
|
# The reps label for NONE combo should say "Reps" (not duration)
|
|
label2 = page.locator('[data-testid="session-item-1"] label').first.inner_text()
|
|
assert "rep" in label2.lower() or label2.lower() == "reps"
|
|
|
|
|
|
def test_amrap_label_in_session_detail(page: Page, app_url: str, browser_name: str):
|
|
"""AMRAP combo items show minutes unit in session detail view."""
|
|
_wait_for_db(page, app_url)
|
|
_clean_all(page)
|
|
|
|
ex_id = _create_exercise(page, "Burpee")
|
|
amrap_id = _create_timed_combo(page, "AMRAP Detail", "AMRAP", [ex_id])
|
|
sess_id = _create_session_with_items(page, "AMRAP Session", [
|
|
{"item_id": amrap_id, "item_type": "combo", "repetitions": 10, "weight": 0}
|
|
])
|
|
|
|
page.goto(f"{app_url}#/sessions/{sess_id}")
|
|
page.wait_for_selector('[data-testid="session-detail-title"]', timeout=5000)
|
|
|
|
detail_text = page.locator('[data-testid="session-detail-item-details-0"]').inner_text()
|
|
# Should show "10 minutes" (not "10 reps")
|
|
assert "10" in detail_text
|
|
assert "min" in detail_text.lower()
|
|
|
|
|
|
# ── EMOM timer ────────────────────────────────────────────────────────────────
|
|
|
|
|
|
def test_emom_timer_visible(page: Page, app_url: str, browser_name: str):
|
|
"""EMOM combo steps show a timer display."""
|
|
_wait_for_db(page, app_url)
|
|
_clean_all(page)
|
|
|
|
ex_id = _create_exercise(page, "EMOM Press")
|
|
combo_id = _create_timed_combo(page, "EMOM Combo", "EMOM", [ex_id])
|
|
sess_id = _create_session_with_items(page, "EMOM Session", [
|
|
{"item_id": combo_id, "item_type": "combo", "repetitions": 1, "weight": 0}
|
|
])
|
|
|
|
_navigate_to_execute(page, app_url, sess_id)
|
|
|
|
timer = page.locator('[data-testid="execute-timer"]')
|
|
expect(timer).to_be_visible()
|
|
# Should show countdown format MM:SS
|
|
timer_text = timer.inner_text()
|
|
assert ":" in timer_text
|
|
|
|
|
|
def test_emom_timer_pause_resume(page: Page, app_url: str, browser_name: str):
|
|
"""Pause button toggles pause state — button label changes."""
|
|
_wait_for_db(page, app_url)
|
|
_clean_all(page)
|
|
|
|
ex_id = _create_exercise(page, "EMOM Press P")
|
|
combo_id = _create_timed_combo(page, "EMOM Pause Combo", "EMOM", [ex_id])
|
|
sess_id = _create_session_with_items(page, "EMOM Pause Session", [
|
|
{"item_id": combo_id, "item_type": "combo", "repetitions": 1, "weight": 0}
|
|
])
|
|
|
|
_navigate_to_execute(page, app_url, sess_id)
|
|
|
|
timer = page.locator('[data-testid="execute-timer"]')
|
|
expect(timer).to_be_visible()
|
|
|
|
# Initially timer is running — button should say Pause
|
|
pause_btn = timer.locator('button')
|
|
initial_label = pause_btn.inner_text()
|
|
assert "pause" in initial_label.lower() or "pause" in pause_btn.get_attribute("aria-label", "").lower()
|
|
|
|
# Click pause
|
|
pause_btn.click()
|
|
page.wait_for_timeout(300)
|
|
|
|
# Button should now say Resume
|
|
resume_label = pause_btn.inner_text()
|
|
assert "resume" in resume_label.lower() or "resume" in pause_btn.get_attribute("aria-label", "").lower()
|
|
|
|
# Click again to resume
|
|
pause_btn.click()
|
|
page.wait_for_timeout(300)
|
|
|
|
after_resume = pause_btn.inner_text()
|
|
assert "pause" in after_resume.lower() or "pause" in pause_btn.get_attribute("aria-label", "").lower()
|
|
|
|
|
|
def test_emom_done_early_advances(page: Page, app_url: str, browser_name: str):
|
|
"""Tapping Done during EMOM advances to the next step immediately."""
|
|
_wait_for_db(page, app_url)
|
|
_clean_all(page)
|
|
|
|
ex1 = _create_exercise(page, "EMOM Ex1")
|
|
ex2 = _create_exercise(page, "EMOM Ex2")
|
|
combo_id = _create_timed_combo(page, "EMOM Two", "EMOM", [ex1, ex2])
|
|
sess_id = _create_session_with_items(page, "EMOM Two Session", [
|
|
{"item_id": combo_id, "item_type": "combo", "repetitions": 1, "weight": 0}
|
|
])
|
|
|
|
_navigate_to_execute(page, app_url, sess_id)
|
|
|
|
# We're on step 1 (EMOM Ex1)
|
|
expect(page.locator('[data-testid="execute-exercise-title"]')).to_have_text("EMOM Ex1")
|
|
|
|
# Tap Done early (should advance without waiting 60 seconds)
|
|
page.click('[data-testid="execute-done-btn"]')
|
|
page.wait_for_timeout(500)
|
|
|
|
# Now we should be on step 2
|
|
expect(page.locator('[data-testid="execute-exercise-title"]')).to_have_text("EMOM Ex2")
|
|
|
|
|
|
def test_emom_auto_advance(page: Page, app_url: str, browser_name: str):
|
|
"""EMOM timer auto-advances to next step after 60 seconds."""
|
|
page.clock.install()
|
|
_wait_for_db(page, app_url)
|
|
_clean_all(page)
|
|
|
|
ex1 = _create_exercise(page, "EMOM Auto1")
|
|
ex2 = _create_exercise(page, "EMOM Auto2")
|
|
combo_id = _create_timed_combo(page, "EMOM Auto", "EMOM", [ex1, ex2])
|
|
sess_id = _create_session_with_items(page, "EMOM Auto Session", [
|
|
{"item_id": combo_id, "item_type": "combo", "repetitions": 1, "weight": 0}
|
|
])
|
|
|
|
_navigate_to_execute(page, app_url, sess_id)
|
|
expect(page.locator('[data-testid="execute-exercise-title"]')).to_have_text("EMOM Auto1")
|
|
|
|
# Advance 61 seconds — timer should fire and auto-advance
|
|
page.clock.run_for(61000)
|
|
page.wait_for_timeout(500)
|
|
|
|
expect(page.locator('[data-testid="execute-exercise-title"]')).to_have_text("EMOM Auto2")
|
|
|
|
|
|
# ── AMRAP timer ───────────────────────────────────────────────────────────────
|
|
|
|
|
|
def test_amrap_timer_visible(page: Page, app_url: str, browser_name: str):
|
|
"""AMRAP combo steps show a timer display."""
|
|
_wait_for_db(page, app_url)
|
|
_clean_all(page)
|
|
|
|
ex_id = _create_exercise(page, "AMRAP Ex")
|
|
combo_id = _create_timed_combo(page, "AMRAP Combo Vis", "AMRAP", [ex_id])
|
|
sess_id = _create_session_with_items(page, "AMRAP Vis Session", [
|
|
{"item_id": combo_id, "item_type": "combo", "repetitions": 5, "weight": 0}
|
|
])
|
|
|
|
_navigate_to_execute(page, app_url, sess_id)
|
|
|
|
timer = page.locator('[data-testid="execute-timer"]')
|
|
expect(timer).to_be_visible()
|
|
# Timer should show 05:00 (5 minutes)
|
|
timer_text = timer.inner_text()
|
|
assert "05:00" in timer_text or "5:" in timer_text
|
|
|
|
|
|
def test_amrap_manual_done_cycles(page: Page, app_url: str, browser_name: str):
|
|
"""Done button manually cycles through AMRAP exercises (auto-adds next round)."""
|
|
_wait_for_db(page, app_url)
|
|
_clean_all(page)
|
|
|
|
ex1 = _create_exercise(page, "AMRAP Cycle1")
|
|
ex2 = _create_exercise(page, "AMRAP Cycle2")
|
|
combo_id = _create_timed_combo(page, "AMRAP Cycle", "AMRAP", [ex1, ex2])
|
|
sess_id = _create_session_with_items(page, "AMRAP Cycle Session", [
|
|
{"item_id": combo_id, "item_type": "combo", "repetitions": 3, "weight": 0}
|
|
])
|
|
|
|
_navigate_to_execute(page, app_url, sess_id)
|
|
|
|
# Step 1: AMRAP Cycle1
|
|
expect(page.locator('[data-testid="execute-exercise-title"]')).to_have_text("AMRAP Cycle1")
|
|
|
|
# Done → advances to AMRAP Cycle2
|
|
page.click('[data-testid="execute-done-btn"]')
|
|
page.wait_for_timeout(400)
|
|
expect(page.locator('[data-testid="execute-exercise-title"]')).to_have_text("AMRAP Cycle2")
|
|
|
|
# Done → auto-cycles back to AMRAP Cycle1 (round 2)
|
|
page.click('[data-testid="execute-done-btn"]')
|
|
page.wait_for_timeout(400)
|
|
expect(page.locator('[data-testid="execute-exercise-title"]')).to_have_text("AMRAP Cycle1")
|
|
|
|
|
|
def test_amrap_timeup_screen(page: Page, app_url: str, browser_name: str):
|
|
"""Time-up overlay appears when AMRAP timer expires."""
|
|
page.clock.install()
|
|
_wait_for_db(page, app_url)
|
|
_clean_all(page)
|
|
|
|
ex_id = _create_exercise(page, "AMRAP TimeUp Ex")
|
|
combo_id = _create_timed_combo(page, "AMRAP TimeUp", "AMRAP", [ex_id])
|
|
sess_id = _create_session_with_items(page, "AMRAP TimeUp Session", [
|
|
{"item_id": combo_id, "item_type": "combo", "repetitions": 1, "weight": 0}
|
|
])
|
|
|
|
_navigate_to_execute(page, app_url, sess_id)
|
|
|
|
# Advance 61 seconds (1 minute AMRAP)
|
|
page.clock.run_for(61000)
|
|
page.wait_for_timeout(500)
|
|
|
|
timeup = page.locator('[data-testid="execute-amrap-timeup"]')
|
|
expect(timeup).to_be_visible()
|
|
|
|
|
|
def test_amrap_continue_advances(page: Page, app_url: str, browser_name: str):
|
|
"""Continue after AMRAP time-up advances to the next session item."""
|
|
page.clock.install()
|
|
_wait_for_db(page, app_url)
|
|
_clean_all(page)
|
|
|
|
ex_amrap = _create_exercise(page, "AMRAP Cont Ex")
|
|
ex_next = _create_exercise(page, "After AMRAP")
|
|
combo_id = _create_timed_combo(page, "AMRAP Cont", "AMRAP", [ex_amrap])
|
|
sess_id = _create_session_with_items(page, "AMRAP Cont Session", [
|
|
{"item_id": combo_id, "item_type": "combo", "repetitions": 1, "weight": 0},
|
|
{"item_id": ex_next, "item_type": "exercise", "repetitions": 5, "weight": 0}
|
|
])
|
|
|
|
_navigate_to_execute(page, app_url, sess_id)
|
|
|
|
# Expire the AMRAP timer
|
|
page.clock.run_for(61000)
|
|
page.wait_for_timeout(500)
|
|
|
|
expect(page.locator('[data-testid="execute-amrap-timeup"]')).to_be_visible()
|
|
|
|
# Click Continue
|
|
page.click('[data-testid="execute-amrap-continue-btn"]')
|
|
page.wait_for_timeout(400)
|
|
|
|
# Should now be on the next exercise
|
|
expect(page.locator('[data-testid="execute-exercise-title"]')).to_have_text("After AMRAP")
|
|
|
|
|
|
# ── TABATA timer ──────────────────────────────────────────────────────────────
|
|
|
|
|
|
def test_tabata_work_phase_shows_timer(page: Page, app_url: str, browser_name: str):
|
|
"""TABATA step shows timer during work phase."""
|
|
_wait_for_db(page, app_url)
|
|
_clean_all(page)
|
|
|
|
ex_id = _create_exercise(page, "TABATA Work Ex")
|
|
combo_id = _create_timed_combo(page, "TABATA Work", "TABATA", [ex_id],
|
|
timer_config={"workTime": 30, "restTime": 10})
|
|
sess_id = _create_session_with_items(page, "TABATA Work Session", [
|
|
{"item_id": combo_id, "item_type": "combo", "repetitions": 1, "weight": 0}
|
|
])
|
|
|
|
_navigate_to_execute(page, app_url, sess_id)
|
|
|
|
timer = page.locator('[data-testid="execute-timer"]')
|
|
expect(timer).to_be_visible()
|
|
timer_text = timer.inner_text()
|
|
assert ":" in timer_text
|
|
|
|
|
|
def test_tabata_rest_screen_after_work(page: Page, app_url: str, browser_name: str):
|
|
"""REST screen appears after TABATA work phase timer expires (using short times)."""
|
|
_wait_for_db(page, app_url)
|
|
_clean_all(page)
|
|
|
|
ex_id = _create_exercise(page, "TABATA Rest Ex")
|
|
combo_id = _create_timed_combo(page, "TABATA Rest", "TABATA", [ex_id],
|
|
timer_config={"workTime": 2, "restTime": 10})
|
|
sess_id = _create_session_with_items(page, "TABATA Rest Session", [
|
|
{"item_id": combo_id, "item_type": "combo", "repetitions": 1, "weight": 0}
|
|
])
|
|
|
|
_navigate_to_execute(page, app_url, sess_id)
|
|
|
|
# Wait for work phase to expire (2 seconds + margin)
|
|
page.wait_for_timeout(3500)
|
|
|
|
rest_card = page.locator('[data-testid="execute-tabata-rest"]')
|
|
expect(rest_card).to_be_visible()
|
|
|
|
|
|
def test_tabata_advances_after_rest(page: Page, app_url: str, browser_name: str):
|
|
"""Next exercise appears after TABATA rest phase timer expires."""
|
|
_wait_for_db(page, app_url)
|
|
_clean_all(page)
|
|
|
|
ex1 = _create_exercise(page, "TABATA Adv1")
|
|
ex2 = _create_exercise(page, "TABATA Adv2")
|
|
combo_id = _create_timed_combo(page, "TABATA Adv", "TABATA", [ex1, ex2],
|
|
timer_config={"workTime": 2, "restTime": 1})
|
|
sess_id = _create_session_with_items(page, "TABATA Adv Session", [
|
|
{"item_id": combo_id, "item_type": "combo", "repetitions": 1, "weight": 0}
|
|
])
|
|
|
|
_navigate_to_execute(page, app_url, sess_id)
|
|
|
|
# Step 1 is TABATA Adv1
|
|
expect(page.locator('[data-testid="execute-exercise-title"]')).to_have_text("TABATA Adv1")
|
|
|
|
# Wait for work (2s) + rest (1s) + margin → Playwright polls until ex2 appears
|
|
expect(page.locator('[data-testid="execute-exercise-title"]')).to_have_text(
|
|
"TABATA Adv2", timeout=8000
|
|
)
|
|
|
|
|
|
def test_tabata_done_starts_rest(page: Page, app_url: str, browser_name: str):
|
|
"""Tapping Done during TABATA work phase starts the rest phase immediately."""
|
|
_wait_for_db(page, app_url)
|
|
_clean_all(page)
|
|
|
|
ex1 = _create_exercise(page, "TABATA Done1")
|
|
ex2 = _create_exercise(page, "TABATA Done2")
|
|
combo_id = _create_timed_combo(page, "TABATA Done", "TABATA", [ex1, ex2],
|
|
timer_config={"workTime": 60, "restTime": 60})
|
|
sess_id = _create_session_with_items(page, "TABATA Done Session", [
|
|
{"item_id": combo_id, "item_type": "combo", "repetitions": 1, "weight": 0}
|
|
])
|
|
|
|
_navigate_to_execute(page, app_url, sess_id)
|
|
|
|
expect(page.locator('[data-testid="execute-exercise-title"]')).to_have_text("TABATA Done1")
|
|
|
|
# Tap Done early — should start rest phase
|
|
page.click('[data-testid="execute-done-btn"]')
|
|
page.wait_for_timeout(400)
|
|
|
|
rest_card = page.locator('[data-testid="execute-tabata-rest"]')
|
|
expect(rest_card).to_be_visible()
|
|
|
|
|
|
def test_tabata_pause_during_work(page: Page, app_url: str, browser_name: str):
|
|
"""Pause freezes TABATA work timer — REST screen does not appear while paused."""
|
|
_wait_for_db(page, app_url)
|
|
_clean_all(page)
|
|
|
|
ex_id = _create_exercise(page, "TABATA Pause Ex")
|
|
combo_id = _create_timed_combo(page, "TABATA Pause", "TABATA", [ex_id],
|
|
timer_config={"workTime": 2, "restTime": 1})
|
|
sess_id = _create_session_with_items(page, "TABATA Pause Session", [
|
|
{"item_id": combo_id, "item_type": "combo", "repetitions": 1, "weight": 0}
|
|
])
|
|
|
|
_navigate_to_execute(page, app_url, sess_id)
|
|
|
|
timer = page.locator('[data-testid="execute-timer"]')
|
|
expect(timer).to_be_visible()
|
|
|
|
# Pause immediately
|
|
timer.locator('button').click()
|
|
page.wait_for_timeout(3500) # Wait longer than workTime (2s)
|
|
|
|
# Timer is paused — REST screen should NOT appear
|
|
expect(page.locator('[data-testid="execute-tabata-rest"]')).not_to_be_visible()
|
|
expect(page.locator('[data-testid="execute-current-step"]')).to_be_visible()
|
|
|
|
|
|
def test_tabata_pause_during_rest(page: Page, app_url: str, browser_name: str):
|
|
"""Pause freezes TABATA rest timer — next exercise does not appear while paused."""
|
|
_wait_for_db(page, app_url)
|
|
_clean_all(page)
|
|
|
|
ex1 = _create_exercise(page, "TABATA PauseR1")
|
|
ex2 = _create_exercise(page, "TABATA PauseR2")
|
|
combo_id = _create_timed_combo(page, "TABATA PauseR", "TABATA", [ex1, ex2],
|
|
timer_config={"workTime": 2, "restTime": 10})
|
|
sess_id = _create_session_with_items(page, "TABATA PauseR Session", [
|
|
{"item_id": combo_id, "item_type": "combo", "repetitions": 1, "weight": 0}
|
|
])
|
|
|
|
_navigate_to_execute(page, app_url, sess_id)
|
|
|
|
# Wait for work phase to complete (2s)
|
|
page.wait_for_timeout(3000)
|
|
|
|
rest_card = page.locator('[data-testid="execute-tabata-rest"]')
|
|
expect(rest_card).to_be_visible()
|
|
|
|
# Pause during rest
|
|
rest_card.locator('button').click()
|
|
page.wait_for_timeout(3000) # Wait well beyond rest time (paused)
|
|
|
|
# Should still be on rest screen (not advanced to ex2)
|
|
expect(rest_card).to_be_visible()
|
|
expect(page.locator('[data-testid="execute-exercise-title"]')).not_to_be_visible()
|
|
|
|
|
|
# ── Non-timed combo unchanged ─────────────────────────────────────────────────
|
|
|
|
|
|
def test_non_timed_combo_unchanged(page: Page, app_url: str, browser_name: str):
|
|
"""NONE-type combos still show the final-round screen — no timer displayed."""
|
|
_wait_for_db(page, app_url)
|
|
_clean_all(page)
|
|
|
|
ex_id = _create_exercise(page, "None Combo Ex")
|
|
combo_id = _create_timed_combo(page, "None Combo", "NONE", [ex_id])
|
|
sess_id = _create_session_with_items(page, "None Session", [
|
|
{"item_id": combo_id, "item_type": "combo", "repetitions": 1, "weight": 0}
|
|
])
|
|
|
|
_navigate_to_execute(page, app_url, sess_id)
|
|
|
|
# No timer should be shown
|
|
expect(page.locator('[data-testid="execute-timer"]')).not_to_be_visible()
|
|
|
|
# Tap Done
|
|
page.click('[data-testid="execute-done-btn"]')
|
|
page.wait_for_timeout(400)
|
|
|
|
# Final-round screen should appear (unchanged from Step 8 behaviour)
|
|
expect(page.locator('[data-testid="execute-final-round"]')).to_be_visible()
|
|
expect(page.locator('[data-testid="execute-continue-btn"]')).to_be_visible()
|
|
expect(page.locator('[data-testid="execute-one-more-btn"]')).to_be_visible()
|