763 lines
26 KiB
Python
763 lines
26 KiB
Python
"""
|
|
Step 4 — Combo Management Tests
|
|
- test_combo_list_empty: Shows empty state when no combos exist
|
|
- test_create_combo_minimal: Create combo with only required field (title)
|
|
- test_create_combo_full: Create combo with all fields including exercises
|
|
- test_combo_appears_in_list: Created combo appears in the list with badges
|
|
- test_combo_detail_view: Detail view shows all combo data
|
|
- test_edit_combo: Edit a combo and verify changes
|
|
- test_delete_combo: Delete a combo with confirmation
|
|
- test_delete_combo_cancel: Cancelling delete does not remove combo
|
|
- test_search_combos: Search filters combos by title
|
|
- test_search_no_results: Search with no matches shows empty message
|
|
- test_search_clear_button: Clear button empties the search field and restores the full list
|
|
- test_validation_title_required: Empty title shows validation error
|
|
- test_duplicate_title_rejected: Creating combo with duplicate title shows error
|
|
- test_reorder_exercises: Reorder exercises in combo
|
|
- test_remove_exercise: Remove an exercise from combo
|
|
- test_type_change: Changing type shows/hides timer config fields
|
|
- test_action_bar_buttons: New and Search buttons appear in action bar
|
|
"""
|
|
from playwright.sync_api import Page, expect
|
|
|
|
|
|
def _wait_for_db(page: Page, app_url: str):
|
|
"""Navigate and wait for the database to be ready."""
|
|
page.goto(app_url)
|
|
page.wait_for_function(
|
|
"document.documentElement.getAttribute('data-db-ready') === 'true'",
|
|
timeout=15000,
|
|
)
|
|
|
|
|
|
def _go_to_combos(page: Page, app_url: str):
|
|
"""Navigate to the combos list and wait for it to load."""
|
|
_wait_for_db(page, app_url)
|
|
page.click('a[href="#/combos"]')
|
|
page.wait_for_timeout(500)
|
|
|
|
|
|
def _clean_combos(page: Page):
|
|
"""Remove all combos via the repository."""
|
|
page.evaluate(
|
|
"""async () => {
|
|
const combos = await window.__repos.combos.getAll();
|
|
for (const c of combos) {
|
|
await window.__repos.combos.delete(c.id);
|
|
}
|
|
}"""
|
|
)
|
|
|
|
|
|
def _create_exercise_via_repo(page: Page, title):
|
|
"""Create an exercise via the repository and return its ID."""
|
|
return page.evaluate(
|
|
"""async (title) => {
|
|
const uuid = await window.__repos.settings.get('user_uuid');
|
|
return window.__repos.exercises.create({
|
|
title: title, description: '', asymmetric: false, alternate: false,
|
|
image_urls: [], video_urls: [], default_reps: 10, default_weight: 0,
|
|
created_by: uuid
|
|
});
|
|
}""",
|
|
title,
|
|
)
|
|
|
|
|
|
def _create_combo_via_ui(page: Page, title, description="", combo_type="NONE", exercise_ids=None):
|
|
"""Fill the combo form and save via UI."""
|
|
page.click('[data-testid="combo-new-btn"]')
|
|
page.wait_for_selector('[data-testid="combo-title"]')
|
|
|
|
page.fill('[data-testid="combo-title"]', title)
|
|
|
|
if description:
|
|
page.fill('[data-testid="combo-description"]', description)
|
|
|
|
if combo_type != "NONE":
|
|
page.select_option('[data-testid="combo-type"]', combo_type)
|
|
|
|
if exercise_ids:
|
|
for ex_id in exercise_ids:
|
|
page.select_option('[data-testid="combo-exercise-select"]', ex_id)
|
|
page.click('[data-testid="combo-exercise-add"]')
|
|
|
|
page.click('[data-testid="combo-save"]')
|
|
page.wait_for_selector('[data-testid="combo-detail-title"]', timeout=5000)
|
|
|
|
|
|
def test_combo_list_empty(page: Page, app_url: str):
|
|
"""Empty state is shown when no combos exist."""
|
|
_go_to_combos(page, app_url)
|
|
_clean_combos(page)
|
|
page.reload()
|
|
page.wait_for_function(
|
|
"document.documentElement.getAttribute('data-db-ready') === 'true'",
|
|
timeout=15000,
|
|
)
|
|
page.click('a[href="#/combos"]')
|
|
page.wait_for_timeout(500)
|
|
expect(page.locator('[data-testid="combo-empty"]')).to_be_visible()
|
|
|
|
|
|
def test_create_combo_minimal(page: Page, app_url: str):
|
|
"""Create a combo with only the required title field."""
|
|
_go_to_combos(page, app_url)
|
|
_clean_combos(page)
|
|
page.reload()
|
|
page.wait_for_function(
|
|
"document.documentElement.getAttribute('data-db-ready') === 'true'",
|
|
timeout=15000,
|
|
)
|
|
page.click('a[href="#/combos"]')
|
|
page.wait_for_timeout(500)
|
|
|
|
_create_combo_via_ui(page, "My First Combo")
|
|
|
|
expect(page.locator('[data-testid="combo-detail-title"]')).to_have_text("My First Combo")
|
|
|
|
_clean_combos(page)
|
|
|
|
|
|
def test_create_combo_full(page: Page, app_url: str):
|
|
"""Create a combo with all fields including exercises."""
|
|
_go_to_combos(page, app_url)
|
|
_clean_combos(page)
|
|
page.reload()
|
|
page.wait_for_function(
|
|
"document.documentElement.getAttribute('data-db-ready') === 'true'",
|
|
timeout=15000,
|
|
)
|
|
page.click('a[href="#/combos"]')
|
|
page.wait_for_timeout(500)
|
|
|
|
# Create exercises first
|
|
ex1_id = _create_exercise_via_repo(page, "Push-ups")
|
|
ex2_id = _create_exercise_via_repo(page, "Squats")
|
|
|
|
page.reload()
|
|
page.wait_for_function(
|
|
"document.documentElement.getAttribute('data-db-ready') === 'true'",
|
|
timeout=15000,
|
|
)
|
|
page.click('a[href="#/combos"]')
|
|
page.wait_for_timeout(500)
|
|
|
|
_create_combo_via_ui(
|
|
page,
|
|
title="Upper Body Blast",
|
|
description="Quick combo for upper body",
|
|
combo_type="EMOM",
|
|
exercise_ids=[ex1_id, ex2_id],
|
|
)
|
|
|
|
expect(page.locator('[data-testid="combo-detail-title"]')).to_have_text("Upper Body Blast")
|
|
expect(page.locator('[data-testid="combo-detail-description"]')).to_have_text(
|
|
"Quick combo for upper body"
|
|
)
|
|
expect(page.locator('[data-testid="combo-detail-type"]')).to_have_text("EMOM")
|
|
expect(page.locator('[data-testid="combo-detail-exercise-0"]')).to_contain_text("Push-ups")
|
|
expect(page.locator('[data-testid="combo-detail-exercise-1"]')).to_contain_text("Squats")
|
|
|
|
_clean_combos(page)
|
|
|
|
|
|
def test_combo_appears_in_list(page: Page, app_url: str):
|
|
"""Created combo appears in the list view with correct info."""
|
|
_go_to_combos(page, app_url)
|
|
_clean_combos(page)
|
|
page.reload()
|
|
page.wait_for_function(
|
|
"document.documentElement.getAttribute('data-db-ready') === 'true'",
|
|
timeout=15000,
|
|
)
|
|
page.click('a[href="#/combos"]')
|
|
page.wait_for_timeout(500)
|
|
|
|
_create_combo_via_ui(page, title="Test Combo", combo_type="AMRAP")
|
|
|
|
page.click('[data-testid="combo-back-btn"]')
|
|
page.wait_for_timeout(500)
|
|
|
|
card_title = page.locator('[data-testid="combo-card-title"]')
|
|
expect(card_title.first).to_have_text("Test Combo")
|
|
expect(page.locator('[data-testid="combo-card-type"]').first).to_have_text("AMRAP")
|
|
|
|
_clean_combos(page)
|
|
|
|
|
|
def test_combo_detail_view(page: Page, app_url: str):
|
|
"""Detail view shows all combo data correctly."""
|
|
_go_to_combos(page, app_url)
|
|
_clean_combos(page)
|
|
|
|
# Create exercise first
|
|
ex_id = _create_exercise_via_repo(page, "Burpees")
|
|
|
|
# Create combo via repo
|
|
combo_id = page.evaluate(
|
|
"""async ({ exId }) => {
|
|
const uuid = await window.__repos.settings.get('user_uuid');
|
|
const id = await window.__repos.combos.create({
|
|
title: 'HIIT Combo',
|
|
description: 'High intensity interval training',
|
|
type: 'TABATA',
|
|
timer_config: { rounds: 8 },
|
|
created_by: uuid
|
|
});
|
|
await window.__repos.combos.setExercises(id, [exId]);
|
|
return id;
|
|
}""",
|
|
{"exId": ex_id},
|
|
)
|
|
|
|
page.goto(f"{app_url}/#/combos/{combo_id}")
|
|
page.wait_for_selector('[data-testid="combo-detail-title"]', timeout=5000)
|
|
|
|
expect(page.locator('[data-testid="combo-detail-title"]')).to_have_text("HIIT Combo")
|
|
expect(page.locator('[data-testid="combo-detail-description"]')).to_have_text(
|
|
"High intensity interval training"
|
|
)
|
|
expect(page.locator('[data-testid="combo-detail-type"]')).to_have_text("TABATA")
|
|
expect(page.locator('[data-testid="combo-detail-tabata-rounds"]')).to_contain_text("8")
|
|
|
|
_clean_combos(page)
|
|
|
|
|
|
def test_edit_combo(page: Page, app_url: str):
|
|
"""Edit a combo and verify changes are persisted."""
|
|
_go_to_combos(page, app_url)
|
|
_clean_combos(page)
|
|
page.reload()
|
|
page.wait_for_function(
|
|
"document.documentElement.getAttribute('data-db-ready') === 'true'",
|
|
timeout=15000,
|
|
)
|
|
page.click('a[href="#/combos"]')
|
|
page.wait_for_timeout(500)
|
|
|
|
_create_combo_via_ui(page, title="Original Combo", combo_type="NONE")
|
|
|
|
page.click('[data-testid="combo-edit-btn"]')
|
|
page.wait_for_selector('[data-testid="combo-title"]')
|
|
|
|
page.fill('[data-testid="combo-title"]', "Updated Combo")
|
|
page.select_option('[data-testid="combo-type"]', "EMOM")
|
|
page.fill('[data-testid="combo-emom-duration"]', "15")
|
|
|
|
page.click('[data-testid="combo-save"]')
|
|
page.wait_for_selector('[data-testid="combo-detail-title"]', timeout=5000)
|
|
|
|
expect(page.locator('[data-testid="combo-detail-title"]')).to_have_text("Updated Combo")
|
|
expect(page.locator('[data-testid="combo-detail-type"]')).to_have_text("EMOM")
|
|
|
|
_clean_combos(page)
|
|
|
|
|
|
def test_delete_combo(page: Page, app_url: str):
|
|
"""Delete a combo with confirmation dialog."""
|
|
_go_to_combos(page, app_url)
|
|
_clean_combos(page)
|
|
page.reload()
|
|
page.wait_for_function(
|
|
"document.documentElement.getAttribute('data-db-ready') === 'true'",
|
|
timeout=15000,
|
|
)
|
|
page.click('a[href="#/combos"]')
|
|
page.wait_for_timeout(500)
|
|
|
|
_create_combo_via_ui(page, title="To Delete")
|
|
|
|
page.on("dialog", lambda dialog: dialog.accept())
|
|
page.click('[data-testid="combo-delete-btn"]')
|
|
page.wait_for_timeout(500)
|
|
|
|
expect(page.locator('[data-testid="combo-empty"]')).to_be_visible()
|
|
|
|
|
|
def test_delete_combo_cancel(page: Page, app_url: str):
|
|
"""Cancelling the delete dialog does not remove the combo."""
|
|
_go_to_combos(page, app_url)
|
|
_clean_combos(page)
|
|
page.reload()
|
|
page.wait_for_function(
|
|
"document.documentElement.getAttribute('data-db-ready') === 'true'",
|
|
timeout=15000,
|
|
)
|
|
page.click('a[href="#/combos"]')
|
|
page.wait_for_timeout(500)
|
|
|
|
_create_combo_via_ui(page, title="Keep Me")
|
|
|
|
page.on("dialog", lambda dialog: dialog.dismiss())
|
|
page.click('[data-testid="combo-delete-btn"]')
|
|
page.wait_for_timeout(500)
|
|
|
|
expect(page.locator('[data-testid="combo-detail-title"]')).to_have_text("Keep Me")
|
|
|
|
_clean_combos(page)
|
|
|
|
|
|
def test_search_combos(page: Page, app_url: str):
|
|
"""Search filters combos by title."""
|
|
_go_to_combos(page, app_url)
|
|
_clean_combos(page)
|
|
|
|
# Create multiple combos via repo
|
|
page.evaluate(
|
|
"""async () => {
|
|
const uuid = await window.__repos.settings.get('user_uuid');
|
|
await window.__repos.combos.create({
|
|
title: 'Upper Body', description: '', type: 'EMOM',
|
|
timer_config: {}, created_by: uuid
|
|
});
|
|
await window.__repos.combos.create({
|
|
title: 'Lower Body', description: '', type: 'AMRAP',
|
|
timer_config: {}, created_by: uuid
|
|
});
|
|
await window.__repos.combos.create({
|
|
title: 'Full Body', description: '', type: 'TABATA',
|
|
timer_config: {}, created_by: uuid
|
|
});
|
|
}"""
|
|
)
|
|
|
|
page.reload()
|
|
page.wait_for_function(
|
|
"document.documentElement.getAttribute('data-db-ready') === 'true'",
|
|
timeout=15000,
|
|
)
|
|
page.click('a[href="#/combos"]')
|
|
page.wait_for_timeout(500)
|
|
|
|
expect(page.locator('[data-testid="combo-card-title"]')).to_have_count(3)
|
|
|
|
page.click('[data-testid="combo-search-toggle"]')
|
|
page.wait_for_selector('[data-testid="combo-search-input"]')
|
|
|
|
page.fill('[data-testid="combo-search-input"]', "Body")
|
|
page.wait_for_timeout(500)
|
|
|
|
expect(page.locator('[data-testid="combo-card-title"]')).to_have_count(3)
|
|
|
|
page.fill('[data-testid="combo-search-input"]', "Upper")
|
|
page.wait_for_timeout(500)
|
|
|
|
expect(page.locator('[data-testid="combo-card-title"]')).to_have_count(1)
|
|
expect(page.locator('[data-testid="combo-card-title"]')).to_have_text("Upper Body")
|
|
|
|
_clean_combos(page)
|
|
|
|
|
|
def test_search_no_results(page: Page, app_url: str):
|
|
"""Search with no matches shows no-results message."""
|
|
_go_to_combos(page, app_url)
|
|
_clean_combos(page)
|
|
|
|
page.evaluate(
|
|
"""async () => {
|
|
const uuid = await window.__repos.settings.get('user_uuid');
|
|
await window.__repos.combos.create({
|
|
title: 'Upper Body', description: '', type: 'EMOM',
|
|
timer_config: {}, created_by: uuid
|
|
});
|
|
}"""
|
|
)
|
|
|
|
page.reload()
|
|
page.wait_for_function(
|
|
"document.documentElement.getAttribute('data-db-ready') === 'true'",
|
|
timeout=15000,
|
|
)
|
|
page.click('a[href="#/combos"]')
|
|
page.wait_for_timeout(500)
|
|
|
|
page.click('[data-testid="combo-search-toggle"]')
|
|
page.fill('[data-testid="combo-search-input"]', "zzzznotfound")
|
|
page.wait_for_timeout(500)
|
|
|
|
expect(page.locator('[data-testid="combo-no-results"]')).to_be_visible()
|
|
|
|
_clean_combos(page)
|
|
|
|
|
|
def test_search_clear_button(page: Page, app_url: str):
|
|
"""Clear button empties the search field and restores the full list."""
|
|
_go_to_combos(page, app_url)
|
|
_clean_combos(page)
|
|
|
|
page.evaluate(
|
|
"""async () => {
|
|
const uuid = await window.__repos.settings.get('user_uuid');
|
|
await window.__repos.combos.create({
|
|
title: 'Upper Body', description: '', type: 'EMOM',
|
|
timer_config: {}, created_by: uuid
|
|
});
|
|
await window.__repos.combos.create({
|
|
title: 'Lower Body', description: '', type: 'AMRAP',
|
|
timer_config: {}, created_by: uuid
|
|
});
|
|
}"""
|
|
)
|
|
|
|
page.reload()
|
|
page.wait_for_function(
|
|
"document.documentElement.getAttribute('data-db-ready') === 'true'",
|
|
timeout=15000,
|
|
)
|
|
page.click('a[href="#/combos"]')
|
|
page.wait_for_timeout(500)
|
|
|
|
page.click('[data-testid="combo-search-toggle"]')
|
|
|
|
expect(page.locator('[data-testid="combo-search-clear"]')).not_to_be_visible()
|
|
|
|
page.fill('[data-testid="combo-search-input"]', "Upper")
|
|
page.wait_for_timeout(500)
|
|
expect(page.locator('[data-testid="combo-card-title"]')).to_have_count(1)
|
|
|
|
page.click('[data-testid="combo-search-clear"]')
|
|
expect(page.locator('[data-testid="combo-search-input"]')).to_have_value("")
|
|
page.wait_for_timeout(500)
|
|
expect(page.locator('[data-testid="combo-card-title"]')).to_have_count(2)
|
|
|
|
expect(page.locator('[data-testid="combo-search-input"]')).to_be_focused()
|
|
|
|
_clean_combos(page)
|
|
|
|
|
|
def test_validation_title_required(page: Page, app_url: str):
|
|
"""Empty title shows validation error and prevents save."""
|
|
_go_to_combos(page, app_url)
|
|
_clean_combos(page)
|
|
page.reload()
|
|
page.wait_for_function(
|
|
"document.documentElement.getAttribute('data-db-ready') === 'true'",
|
|
timeout=15000,
|
|
)
|
|
page.click('a[href="#/combos"]')
|
|
page.wait_for_timeout(500)
|
|
|
|
page.click('[data-testid="combo-new-btn"]')
|
|
page.wait_for_selector('[data-testid="combo-title"]')
|
|
|
|
page.click('[data-testid="combo-save"]')
|
|
|
|
expect(page.locator('[data-testid="combo-title-error"]')).to_be_visible()
|
|
expect(page.locator('[data-testid="combo-title"]')).to_be_visible()
|
|
|
|
|
|
def test_duplicate_title_rejected(page: Page, app_url: str):
|
|
"""Creating a combo with a duplicate title shows an error."""
|
|
_go_to_combos(page, app_url)
|
|
_clean_combos(page)
|
|
page.reload()
|
|
page.wait_for_function(
|
|
"document.documentElement.getAttribute('data-db-ready') === 'true'",
|
|
timeout=15000,
|
|
)
|
|
page.click('a[href="#/combos"]')
|
|
page.wait_for_timeout(500)
|
|
|
|
_create_combo_via_ui(page, title="Unique Combo")
|
|
|
|
page.click('[data-testid="combo-back-btn"]')
|
|
page.wait_for_timeout(500)
|
|
|
|
page.click('[data-testid="combo-new-btn"]')
|
|
page.wait_for_selector('[data-testid="combo-title"]')
|
|
page.fill('[data-testid="combo-title"]', "Unique Combo")
|
|
page.click('[data-testid="combo-save"]')
|
|
page.wait_for_timeout(500)
|
|
|
|
title_error_visible = page.locator('[data-testid="combo-title-error"]').is_visible()
|
|
save_error_visible = page.locator('[data-testid="combo-save-error"]').is_visible()
|
|
assert title_error_visible or save_error_visible, "Expected duplicate title error"
|
|
|
|
_clean_combos(page)
|
|
|
|
|
|
def test_reorder_exercises(page: Page, app_url: str):
|
|
"""Reorder exercises in a combo."""
|
|
_go_to_combos(page, app_url)
|
|
_clean_combos(page)
|
|
page.reload()
|
|
page.wait_for_function(
|
|
"document.documentElement.getAttribute('data-db-ready') === 'true'",
|
|
timeout=15000,
|
|
)
|
|
page.click('a[href="#/combos"]')
|
|
page.wait_for_timeout(500)
|
|
|
|
# Create exercises
|
|
ex1_id = _create_exercise_via_repo(page, "Push-ups")
|
|
ex2_id = _create_exercise_via_repo(page, "Squats")
|
|
ex3_id = _create_exercise_via_repo(page, "Burpees")
|
|
|
|
page.reload()
|
|
page.wait_for_function(
|
|
"document.documentElement.getAttribute('data-db-ready') === 'true'",
|
|
timeout=15000,
|
|
)
|
|
page.click('a[href="#/combos"]')
|
|
page.wait_for_timeout(500)
|
|
|
|
# Create combo with 3 exercises
|
|
_create_combo_via_ui(
|
|
page,
|
|
title="Reorder Test",
|
|
exercise_ids=[ex1_id, ex2_id, ex3_id],
|
|
)
|
|
|
|
# Go to edit
|
|
page.click('[data-testid="combo-edit-btn"]')
|
|
page.wait_for_selector('[data-testid="combo-title"]')
|
|
|
|
# Move Squats (index 1) up
|
|
page.click('[data-testid="combo-exercise-move-up-1"]')
|
|
|
|
# Save
|
|
page.click('[data-testid="combo-save"]')
|
|
page.wait_for_selector('[data-testid="combo-detail-title"]', timeout=5000)
|
|
|
|
# Verify order: Squats, Push-ups, Burpees
|
|
expect(page.locator('[data-testid="combo-detail-exercise-0"]')).to_contain_text("Squats")
|
|
expect(page.locator('[data-testid="combo-detail-exercise-1"]')).to_contain_text("Push-ups")
|
|
expect(page.locator('[data-testid="combo-detail-exercise-2"]')).to_contain_text("Burpees")
|
|
|
|
_clean_combos(page)
|
|
|
|
|
|
def test_remove_exercise(page: Page, app_url: str):
|
|
"""Remove an exercise from a combo."""
|
|
_go_to_combos(page, app_url)
|
|
_clean_combos(page)
|
|
page.reload()
|
|
page.wait_for_function(
|
|
"document.documentElement.getAttribute('data-db-ready') === 'true'",
|
|
timeout=15000,
|
|
)
|
|
page.click('a[href="#/combos"]')
|
|
page.wait_for_timeout(500)
|
|
|
|
ex1_id = _create_exercise_via_repo(page, "Push-ups")
|
|
ex2_id = _create_exercise_via_repo(page, "Squats")
|
|
|
|
page.reload()
|
|
page.wait_for_function(
|
|
"document.documentElement.getAttribute('data-db-ready') === 'true'",
|
|
timeout=15000,
|
|
)
|
|
page.click('a[href="#/combos"]')
|
|
page.wait_for_timeout(500)
|
|
|
|
_create_combo_via_ui(
|
|
page,
|
|
title="Remove Test",
|
|
exercise_ids=[ex1_id, ex2_id],
|
|
)
|
|
|
|
page.click('[data-testid="combo-edit-btn"]')
|
|
page.wait_for_selector('[data-testid="combo-title"]')
|
|
|
|
# Remove first exercise
|
|
page.click('[data-testid="combo-exercise-remove-0"]')
|
|
|
|
page.click('[data-testid="combo-save"]')
|
|
page.wait_for_selector('[data-testid="combo-detail-title"]', timeout=5000)
|
|
|
|
# Only Squats should remain
|
|
expect(page.locator('[data-testid="combo-detail-exercise-0"]')).to_contain_text("Squats")
|
|
expect(page.locator('[data-testid="combo-detail-exercise-1"]')).not_to_be_attached()
|
|
|
|
_clean_combos(page)
|
|
|
|
|
|
def test_type_change(page: Page, app_url: str):
|
|
"""Changing type shows/hides timer config fields."""
|
|
_go_to_combos(page, app_url)
|
|
_clean_combos(page)
|
|
page.reload()
|
|
page.wait_for_function(
|
|
"document.documentElement.getAttribute('data-db-ready') === 'true'",
|
|
timeout=15000,
|
|
)
|
|
page.click('a[href="#/combos"]')
|
|
page.wait_for_timeout(500)
|
|
|
|
page.click('[data-testid="combo-new-btn"]')
|
|
page.wait_for_selector('[data-testid="combo-title"]')
|
|
|
|
# NONE type: no timer config fields
|
|
expect(page.locator('[data-testid="combo-emom-duration"]')).not_to_be_visible()
|
|
expect(page.locator('[data-testid="combo-amrap-duration"]')).not_to_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()
|
|
|
|
# Switch to EMOM
|
|
page.select_option('[data-testid="combo-type"]', "EMOM")
|
|
expect(page.locator('[data-testid="combo-emom-duration"]')).to_be_visible()
|
|
expect(page.locator('[data-testid="combo-amrap-duration"]')).not_to_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()
|
|
|
|
# Switch to TABATA
|
|
page.select_option('[data-testid="combo-type"]', "TABATA")
|
|
expect(page.locator('[data-testid="combo-emom-duration"]')).not_to_be_visible()
|
|
expect(page.locator('[data-testid="combo-amrap-duration"]')).not_to_be_visible()
|
|
expect(page.locator('[data-testid="combo-tabata-work-time"]')).to_be_visible()
|
|
expect(page.locator('[data-testid="combo-tabata-rest-time"]')).to_be_visible()
|
|
|
|
_clean_combos(page)
|
|
|
|
|
|
def test_action_bar_buttons(page: Page, app_url: str):
|
|
"""New and Search buttons are visible in the action bar on the combo list."""
|
|
_go_to_combos(page, app_url)
|
|
_clean_combos(page)
|
|
page.reload()
|
|
page.wait_for_function(
|
|
"document.documentElement.getAttribute('data-db-ready') === 'true'",
|
|
timeout=15000,
|
|
)
|
|
page.click('a[href="#/combos"]')
|
|
page.wait_for_timeout(500)
|
|
|
|
expect(page.locator('[data-testid="combo-new-btn"]')).to_be_visible()
|
|
expect(page.locator('[data-testid="combo-search-toggle"]')).to_be_visible()
|
|
|
|
|
|
def test_create_combo_with_reps_and_weight(page: Page, app_url: str):
|
|
"""Create combo with exercises and set reps/weight values."""
|
|
_go_to_combos(page, app_url)
|
|
_clean_combos(page)
|
|
page.reload()
|
|
page.wait_for_function(
|
|
"document.documentElement.getAttribute('data-db-ready') === 'true'",
|
|
timeout=15000,
|
|
)
|
|
page.click('a[href="#/combos"]')
|
|
page.wait_for_timeout(500)
|
|
|
|
# Create exercises
|
|
ex1_id = _create_exercise_via_repo(page, "Push-ups")
|
|
ex2_id = _create_exercise_via_repo(page, "Squats")
|
|
|
|
# Create combo with exercises
|
|
page.click('[data-testid="combo-new-btn"]')
|
|
page.wait_for_selector('[data-testid="combo-title"]')
|
|
|
|
page.fill('[data-testid="combo-title"]', "Reps Weight Test")
|
|
|
|
# Add first exercise
|
|
page.select_option('[data-testid="combo-exercise-select"]', ex1_id)
|
|
page.click('[data-testid="combo-exercise-add"]')
|
|
|
|
# Add second exercise
|
|
page.select_option('[data-testid="combo-exercise-select"]', ex2_id)
|
|
page.click('[data-testid="combo-exercise-add"]')
|
|
|
|
# Set reps and weight for first exercise
|
|
page.fill('[data-testid="combo-exercise-reps-0"]', "12")
|
|
page.fill('[data-testid="combo-exercise-weight-0"]', "50")
|
|
|
|
# Set reps for second exercise (weight stays default)
|
|
page.fill('[data-testid="combo-exercise-reps-1"]', "15")
|
|
|
|
page.click('[data-testid="combo-save"]')
|
|
page.wait_for_selector('[data-testid="combo-detail-title"]', timeout=5000)
|
|
|
|
# Verify detail view shows reps and weight
|
|
expect(page.locator('[data-testid="combo-detail-exercise-0"]')).to_contain_text("Push-ups")
|
|
expect(page.locator('[data-testid="combo-detail-exercise-0"]')).to_contain_text("12")
|
|
expect(page.locator('[data-testid="combo-detail-exercise-0"]')).to_contain_text("50")
|
|
expect(page.locator('[data-testid="combo-detail-exercise-1"]')).to_contain_text("Squats")
|
|
expect(page.locator('[data-testid="combo-detail-exercise-1"]')).to_contain_text("15")
|
|
|
|
_clean_combos(page)
|
|
|
|
|
|
def test_edit_combo_reps_and_weight(page: Page, app_url: str):
|
|
"""Edit combo and change reps/weight values."""
|
|
_go_to_combos(page, app_url)
|
|
_clean_combos(page)
|
|
page.reload()
|
|
page.wait_for_function(
|
|
"document.documentElement.getAttribute('data-db-ready') === 'true'",
|
|
timeout=15000,
|
|
)
|
|
page.click('a[href="#/combos"]')
|
|
page.wait_for_timeout(500)
|
|
|
|
# Create exercise
|
|
ex_id = _create_exercise_via_repo(page, "Deadlifts")
|
|
|
|
# Create combo
|
|
_create_combo_via_ui(
|
|
page,
|
|
title="Edit Reps Weight Test",
|
|
exercise_ids=[ex_id],
|
|
)
|
|
|
|
# Edit combo
|
|
page.click('[data-testid="combo-edit-btn"]')
|
|
page.wait_for_selector('[data-testid="combo-title"]')
|
|
|
|
# Change reps and weight
|
|
page.fill('[data-testid="combo-exercise-reps-0"]', "8")
|
|
page.fill('[data-testid="combo-exercise-weight-0"]', "100")
|
|
|
|
page.click('[data-testid="combo-save"]')
|
|
page.wait_for_selector('[data-testid="combo-detail-title"]', timeout=5000)
|
|
|
|
# Verify updated values
|
|
expect(page.locator('[data-testid="combo-detail-exercise-0"]')).to_contain_text("Deadlifts")
|
|
expect(page.locator('[data-testid="combo-detail-exercise-0"]')).to_contain_text("8")
|
|
expect(page.locator('[data-testid="combo-detail-exercise-0"]')).to_contain_text("100")
|
|
|
|
_clean_combos(page)
|
|
|
|
|
|
def test_reps_weight_defaults(page: Page, app_url: str):
|
|
"""Reps prefill from the exercise's default_reps and weight defaults to 0."""
|
|
_go_to_combos(page, app_url)
|
|
_clean_combos(page)
|
|
page.reload()
|
|
page.wait_for_function(
|
|
"document.documentElement.getAttribute('data-db-ready') === 'true'",
|
|
timeout=15000,
|
|
)
|
|
page.click('a[href="#/combos"]')
|
|
page.wait_for_timeout(500)
|
|
|
|
# Create exercise
|
|
ex_id = _create_exercise_via_repo(page, "Lunges")
|
|
|
|
# Create combo
|
|
page.click('[data-testid="combo-new-btn"]')
|
|
page.wait_for_selector('[data-testid="combo-title"]')
|
|
|
|
page.fill('[data-testid="combo-title"]', "Defaults Test")
|
|
|
|
# Add exercise
|
|
page.select_option('[data-testid="combo-exercise-select"]', ex_id)
|
|
page.click('[data-testid="combo-exercise-add"]')
|
|
|
|
# Verify default values in inputs (_create_exercise_via_repo sets default_reps=10)
|
|
reps_input = page.locator('[data-testid="combo-exercise-reps-0"]')
|
|
weight_input = page.locator('[data-testid="combo-exercise-weight-0"]')
|
|
expect(reps_input).to_have_value("10")
|
|
expect(weight_input).to_have_value("0")
|
|
|
|
# Save without changing
|
|
page.click('[data-testid="combo-save"]')
|
|
page.wait_for_selector('[data-testid="combo-detail-title"]', timeout=5000)
|
|
|
|
# Verify defaults are shown in detail view (always shown now)
|
|
exercise_item = page.locator('[data-testid="combo-detail-exercise-0"]')
|
|
expect(exercise_item).to_contain_text("Lunges")
|
|
expect(page.locator('[data-testid="combo-detail-exercise-details-0"]')).to_contain_text("10")
|
|
expect(page.locator('[data-testid="combo-detail-exercise-details-0"]')).to_contain_text("0")
|
|
|
|
_clean_combos(page)
|