trainUs/tests/test_step13_markdown.py
David 352a3d5e88
Some checks are pending
CI / Lint & Format (push) Waiting to run
CI / Build (push) Waiting to run
CI / Tests (${{ matrix.browser }}) (chromium) (push) Waiting to run
CI / Tests (${{ matrix.browser }}) (firefox) (push) Waiting to run
Initial commit
2026-06-18 13:06:57 +02:00

269 lines
9.4 KiB
Python

"""
Step 13 — Markdown in Description Fields
- test_markdown_hint_on_exercise_form: Hint text visible next to description label on exercise form
- test_markdown_hint_on_combo_form: Hint text visible next to description label on combo form
- test_markdown_hint_on_session_form: Hint text visible next to description label on session form
- test_markdown_rendered_in_exercise_detail: Bold, italic and list render as HTML in exercise detail
- test_markdown_rendered_in_combo_detail: Bold text renders as HTML in combo detail
- test_markdown_rendered_in_session_detail: Bold text renders as HTML in session detail
- test_description_absent_from_exercise_list: Description is not shown on exercise list cards
- test_description_absent_from_combo_list: Description is not shown on combo list cards
- test_description_absent_from_session_list: Description is not shown on session list cards
"""
from playwright.sync_api import Page, expect
HINT_TEXT = "Basic Markdown supported"
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 _clean_all(page: Page):
"""Remove all exercises, combos, and sessions via the repositories."""
page.evaluate(
"""async () => {
for (const e of await window.__repos.exercises.getAll())
await window.__repos.exercises.delete(e.id);
for (const c of await window.__repos.combos.getAll())
await window.__repos.combos.delete(c.id);
for (const s of await window.__repos.sessions.getAll())
await window.__repos.sessions.delete(s.id);
}"""
)
# ---------------------------------------------------------------------------
# Hint tests
# ---------------------------------------------------------------------------
def test_markdown_hint_on_exercise_form(page: Page, app_url: str):
"""Hint 'Basic Markdown supported' appears next to the description label on the exercise form."""
_wait_for_db(page, app_url)
page.click('a[href="#/exercises"]')
page.wait_for_timeout(500)
page.click('[data-testid="exercise-new-btn"]')
page.wait_for_selector('[data-testid="exercise-description"]')
hint = page.locator('[data-testid="exercise-description"]').locator(
'xpath=../label .field-hint'
)
# Locate via the form-group label
hint = page.locator('label[for="exercise-description"] .field-hint')
expect(hint).to_have_text(HINT_TEXT)
def test_markdown_hint_on_combo_form(page: Page, app_url: str):
"""Hint 'Basic Markdown supported' appears next to the description label on the combo form."""
_wait_for_db(page, app_url)
page.click('a[href="#/combos"]')
page.wait_for_timeout(500)
page.click('[data-testid="combo-new-btn"]')
page.wait_for_selector('[data-testid="combo-description"]')
hint = page.locator('label[for="combo-description"] .field-hint')
expect(hint).to_have_text(HINT_TEXT)
def test_markdown_hint_on_session_form(page: Page, app_url: str):
"""Hint 'Basic Markdown supported' appears next to the description label on the session form."""
_wait_for_db(page, app_url)
page.click('a[href="#/sessions"]')
page.wait_for_timeout(500)
page.click('[data-testid="session-new-btn"]')
page.wait_for_selector('[data-testid="session-description"]')
hint = page.locator('label[for="session-description"] .field-hint')
expect(hint).to_have_text(HINT_TEXT)
# ---------------------------------------------------------------------------
# Rendering tests
# ---------------------------------------------------------------------------
def test_markdown_rendered_in_exercise_detail(page: Page, app_url: str):
"""Bold, italic, and list items in a description render as HTML elements in the exercise detail view."""
_wait_for_db(page, app_url)
_clean_all(page)
exercise_id = page.evaluate(
"""async () => {
const uuid = await window.__repos.settings.get('user_uuid');
return window.__repos.exercises.create({
title: 'MD Exercise',
description: '**bold** and *italic*\\n- item one',
created_by: uuid,
});
}"""
)
page.goto(f"{app_url}/#/exercises/{exercise_id}")
page.wait_for_selector('[data-testid="exercise-detail-description"]')
desc = page.locator('[data-testid="exercise-detail-description"]')
expect(desc.locator('strong')).to_have_text('bold')
expect(desc.locator('em')).to_have_text('italic')
expect(desc.locator('li').first).to_have_text('item one')
_clean_all(page)
def test_markdown_rendered_in_combo_detail(page: Page, app_url: str):
"""Bold text in a combo description renders as a <strong> element in the detail view."""
_wait_for_db(page, app_url)
_clean_all(page)
combo_id = page.evaluate(
"""async () => {
const uuid = await window.__repos.settings.get('user_uuid');
return window.__repos.combos.create({
title: 'MD Combo',
description: '**important**',
type: 'NONE',
exercises: [],
timer_config: {},
created_by: uuid,
});
}"""
)
page.goto(f"{app_url}/#/combos/{combo_id}")
page.wait_for_selector('[data-testid="combo-detail-description"]')
desc = page.locator('[data-testid="combo-detail-description"]')
expect(desc.locator('strong')).to_have_text('important')
_clean_all(page)
def test_markdown_rendered_in_session_detail(page: Page, app_url: str):
"""Bold text in a session description renders as a <strong> element in the detail view."""
_wait_for_db(page, app_url)
_clean_all(page)
session_id = page.evaluate(
"""async () => {
const uuid = await window.__repos.settings.get('user_uuid');
return window.__repos.sessions.create({
title: 'MD Session',
description: '**focus**',
items: [],
created_by: uuid,
});
}"""
)
page.goto(f"{app_url}/#/sessions/{session_id}")
page.wait_for_selector('[data-testid="session-detail-description"]')
desc = page.locator('[data-testid="session-detail-description"]')
expect(desc.locator('strong')).to_have_text('focus')
_clean_all(page)
# ---------------------------------------------------------------------------
# List view — description must not appear
# ---------------------------------------------------------------------------
def test_description_absent_from_exercise_list(page: Page, app_url: str):
"""Exercise list cards do not display the description text."""
_wait_for_db(page, app_url)
_clean_all(page)
page.evaluate(
"""async () => {
const uuid = await window.__repos.settings.get('user_uuid');
await window.__repos.exercises.create({
title: 'No Desc Exercise',
description: 'should not be visible in list',
created_by: uuid,
});
}"""
)
page.click('a[href="#/exercises"]')
page.wait_for_timeout(500)
page.reload()
page.wait_for_function(
"document.documentElement.getAttribute('data-db-ready') === 'true'",
timeout=15000,
)
page.click('a[href="#/exercises"]')
page.wait_for_timeout(500)
expect(page.get_by_text('should not be visible in list')).not_to_be_visible()
_clean_all(page)
def test_description_absent_from_combo_list(page: Page, app_url: str):
"""Combo list cards do not display the description text."""
_wait_for_db(page, app_url)
_clean_all(page)
page.evaluate(
"""async () => {
const uuid = await window.__repos.settings.get('user_uuid');
await window.__repos.combos.create({
title: 'No Desc Combo',
description: 'combo desc not in list',
type: 'NONE',
exercises: [],
timer_config: {},
created_by: uuid,
});
}"""
)
page.click('a[href="#/combos"]')
page.wait_for_timeout(500)
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.get_by_text('combo desc not in list')).not_to_be_visible()
_clean_all(page)
def test_description_absent_from_session_list(page: Page, app_url: str):
"""Session list cards do not display the description text."""
_wait_for_db(page, app_url)
_clean_all(page)
page.evaluate(
"""async () => {
const uuid = await window.__repos.settings.get('user_uuid');
await window.__repos.sessions.create({
title: 'No Desc Session',
description: 'session desc not in list',
items: [],
created_by: uuid,
});
}"""
)
page.click('a[href="#/sessions"]')
page.wait_for_timeout(500)
page.reload()
page.wait_for_function(
"document.documentElement.getAttribute('data-db-ready') === 'true'",
timeout=15000,
)
page.click('a[href="#/sessions"]')
page.wait_for_timeout(500)
expect(page.get_by_text('session desc not in list')).not_to_be_visible()
_clean_all(page)