Harden e2e waits against loaded-box flakes; add AGENTS.md
This commit is contained in:
@@ -0,0 +1,16 @@
|
|||||||
|
# AGENTS.md
|
||||||
|
|
||||||
|
## Commands
|
||||||
|
|
||||||
|
- `make test` - run the full test suite
|
||||||
|
- `make dev` - run the development task
|
||||||
|
- `make prod` - start the project
|
||||||
|
|
||||||
|
## Code Map
|
||||||
|
|
||||||
|
- `molodetz` - application source
|
||||||
|
- `tests` - automated tests
|
||||||
|
|
||||||
|
## Conventions
|
||||||
|
|
||||||
|
- Use ES6 module syntax in JavaScript files.
|
||||||
+1
-1
@@ -5,7 +5,7 @@ build-backend = "setuptools.build_meta"
|
|||||||
|
|
||||||
[project]
|
[project]
|
||||||
name = "molodetz"
|
name = "molodetz"
|
||||||
version = "1.0.9"
|
version = "1.0.10"
|
||||||
description = "Molodetz, a calm community blog roll."
|
description = "Molodetz, a calm community blog roll."
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
requires-python = ">=3.12"
|
requires-python = ">=3.12"
|
||||||
|
|||||||
+18
-1
@@ -128,17 +128,34 @@ def browser(app_server):
|
|||||||
browser.close()
|
browser.close()
|
||||||
|
|
||||||
|
|
||||||
|
def _record_console_error(page, message):
|
||||||
|
if message.type == "error" and "ERR_NETWORK_CHANGED" not in message.text:
|
||||||
|
page.console_errors.append(message.text)
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture()
|
@pytest.fixture()
|
||||||
def page(browser):
|
def page(browser):
|
||||||
context = browser.new_context(viewport={"width": 1280, "height": 900}, locale="nl-NL")
|
context = browser.new_context(viewport={"width": 1280, "height": 900}, locale="nl-NL")
|
||||||
page = context.new_page()
|
page = context.new_page()
|
||||||
|
page.set_default_timeout(60000)
|
||||||
page.console_errors = []
|
page.console_errors = []
|
||||||
page.on("pageerror", lambda error: page.console_errors.append(str(error)))
|
page.on("pageerror", lambda error: page.console_errors.append(str(error)))
|
||||||
page.on("console", lambda message: message.type == "error" and page.console_errors.append(message.text))
|
page.on("console", lambda message: _record_console_error(page, message))
|
||||||
yield page
|
yield page
|
||||||
context.close()
|
context.close()
|
||||||
|
|
||||||
|
|
||||||
|
def goto_ready(page, url):
|
||||||
|
from playwright.sync_api import TimeoutError as PlaywrightTimeoutError
|
||||||
|
|
||||||
|
page.goto(url, wait_until="domcontentloaded")
|
||||||
|
try:
|
||||||
|
page.wait_for_selector("html[data-app=ready]")
|
||||||
|
except PlaywrightTimeoutError:
|
||||||
|
page.reload(wait_until="domcontentloaded")
|
||||||
|
page.wait_for_selector("html[data-app=ready]")
|
||||||
|
|
||||||
|
|
||||||
def pytest_sessionfinish(session, exitstatus):
|
def pytest_sessionfinish(session, exitstatus):
|
||||||
shutil.rmtree(TEST_DATA_DIR, ignore_errors=True)
|
shutil.rmtree(TEST_DATA_DIR, ignore_errors=True)
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# retoor <retoor@molodetz.nl>
|
# retoor <retoor@molodetz.nl>
|
||||||
from tests.conftest import login_admin
|
from tests.conftest import goto_ready, login_admin
|
||||||
|
|
||||||
ADMIN_PAGES = ["/admin", "/admin/posts", "/admin/joins", "/admin/services", "/admin/services/backup", "/admin/trash", "/admin/settings", "/admin/users", "/admin/audit", "/admin/backups", "/admin/stats", "/notifications", "/profile/api-key", "/docs/api/admin"]
|
ADMIN_PAGES = ["/admin", "/admin/posts", "/admin/joins", "/admin/services", "/admin/services/backup", "/admin/trash", "/admin/settings", "/admin/users", "/admin/audit", "/admin/backups", "/admin/stats", "/notifications", "/profile/api-key", "/docs/api/admin"]
|
||||||
|
|
||||||
@@ -7,8 +7,7 @@ ADMIN_PAGES = ["/admin", "/admin/posts", "/admin/joins", "/admin/services", "/ad
|
|||||||
def test_admin_pages_have_no_js_errors(page, app_server):
|
def test_admin_pages_have_no_js_errors(page, app_server):
|
||||||
login_admin(page, app_server)
|
login_admin(page, app_server)
|
||||||
for path in ADMIN_PAGES:
|
for path in ADMIN_PAGES:
|
||||||
page.goto(app_server + path, wait_until="domcontentloaded")
|
goto_ready(page, app_server + path)
|
||||||
page.wait_for_selector("html[data-app=ready]")
|
|
||||||
page.goto(app_server + "/admin/services/backup", wait_until="domcontentloaded")
|
page.goto(app_server + "/admin/services/backup", wait_until="domcontentloaded")
|
||||||
page.click("[data-tab=logs]")
|
page.click("[data-tab=logs]")
|
||||||
assert page.locator("[data-panel=logs]").is_visible()
|
assert page.locator("[data-panel=logs]").is_visible()
|
||||||
|
|||||||
@@ -1,11 +1,10 @@
|
|||||||
# retoor <retoor@molodetz.nl>
|
# retoor <retoor@molodetz.nl>
|
||||||
from tests.conftest import login_admin
|
from tests.conftest import goto_ready, login_admin
|
||||||
|
|
||||||
|
|
||||||
def test_login_and_editor_preview(page, app_server):
|
def test_login_and_editor_preview(page, app_server):
|
||||||
login_admin(page, app_server)
|
login_admin(page, app_server)
|
||||||
page.goto(app_server + "/admin/posts/new", wait_until="domcontentloaded")
|
goto_ready(page, app_server + "/admin/posts/new")
|
||||||
page.wait_for_selector("html[data-app=ready]")
|
|
||||||
page.fill("#body", "Een **vette** zin <script>alert(1)</script> [link](javascript:alert(1)) <b onclick=alert(1)>b</b>")
|
page.fill("#body", "Een **vette** zin <script>alert(1)</script> [link](javascript:alert(1)) <b onclick=alert(1)>b</b>")
|
||||||
page.wait_for_function("document.querySelector('[data-preview-target] strong')")
|
page.wait_for_function("document.querySelector('[data-preview-target] strong')")
|
||||||
preview = page.locator("[data-preview-target]").inner_html()
|
preview = page.locator("[data-preview-target]").inner_html()
|
||||||
|
|||||||
+3
-4
@@ -1,17 +1,16 @@
|
|||||||
# retoor <retoor@molodetz.nl>
|
# retoor <retoor@molodetz.nl>
|
||||||
|
from tests.conftest import goto_ready
|
||||||
|
|
||||||
|
|
||||||
def test_docs_tabs_switch(page, app_server):
|
def test_docs_tabs_switch(page, app_server):
|
||||||
page.goto(app_server + "/docs/api/content", wait_until="domcontentloaded")
|
goto_ready(page, app_server + "/docs/api/content")
|
||||||
page.wait_for_selector("html[data-app=ready]")
|
|
||||||
page.click("[data-endpoint] [data-tab=expected] >> nth=0")
|
page.click("[data-endpoint] [data-tab=expected] >> nth=0")
|
||||||
assert page.locator("[data-endpoint] [data-panel=expected]").first.is_visible()
|
assert page.locator("[data-endpoint] [data-panel=expected]").first.is_visible()
|
||||||
assert page.console_errors == []
|
assert page.console_errors == []
|
||||||
|
|
||||||
|
|
||||||
def test_docs_code_blocks_get_highlight_and_copy(page, app_server):
|
def test_docs_code_blocks_get_highlight_and_copy(page, app_server):
|
||||||
page.goto(app_server + "/docs/api", wait_until="domcontentloaded")
|
goto_ready(page, app_server + "/docs/api")
|
||||||
page.wait_for_selector("html[data-app=ready]")
|
|
||||||
page.wait_for_selector("dp-code .copy-button")
|
page.wait_for_selector("dp-code .copy-button")
|
||||||
assert page.locator("dp-code pre code.hljs").count() >= 1
|
assert page.locator("dp-code pre code.hljs").count() >= 1
|
||||||
assert page.console_errors == []
|
assert page.console_errors == []
|
||||||
|
|||||||
+2
-2
@@ -1,9 +1,9 @@
|
|||||||
# retoor <retoor@molodetz.nl>
|
# retoor <retoor@molodetz.nl>
|
||||||
|
from tests.conftest import goto_ready
|
||||||
|
|
||||||
|
|
||||||
def test_lightbox_opens_and_closes(page, app_server):
|
def test_lightbox_opens_and_closes(page, app_server):
|
||||||
page.goto(app_server + "/flyers", wait_until="domcontentloaded")
|
goto_ready(page, app_server + "/flyers")
|
||||||
page.wait_for_selector("html[data-app=ready]")
|
|
||||||
page.locator("[data-lightbox]").first.click()
|
page.locator("[data-lightbox]").first.click()
|
||||||
lightbox = page.locator("dp-lightbox")
|
lightbox = page.locator("dp-lightbox")
|
||||||
assert lightbox.evaluate("el => el.classList.contains('visible')")
|
assert lightbox.evaluate("el => el.classList.contains('visible')")
|
||||||
|
|||||||
+5
-7
@@ -1,12 +1,11 @@
|
|||||||
# retoor <retoor@molodetz.nl>
|
# retoor <retoor@molodetz.nl>
|
||||||
from tests.conftest import assert_no_horizontal_overflow
|
from tests.conftest import assert_no_horizontal_overflow, goto_ready
|
||||||
|
|
||||||
PUBLIC_PAGES = ["/", "/roll", "/standard", "/flyers", "/memes", "/people", "/people/retoor", "/join", "/docs", "/docs/welcome", "/docs/api/content", "/docs/component-code", "/terms", "/privacy"]
|
PUBLIC_PAGES = ["/", "/roll", "/standard", "/flyers", "/memes", "/people", "/people/retoor", "/join", "/docs", "/docs/welcome", "/docs/api/content", "/docs/component-code", "/terms", "/privacy"]
|
||||||
|
|
||||||
|
|
||||||
def test_home_renders_without_errors(page, app_server):
|
def test_home_renders_without_errors(page, app_server):
|
||||||
page.goto(app_server + "/", wait_until="domcontentloaded")
|
goto_ready(page, app_server + "/")
|
||||||
page.wait_for_selector("html[data-app=ready]")
|
|
||||||
assert page.locator("h1").first.inner_text().strip().lower() == "molodetz"
|
assert page.locator("h1").first.inner_text().strip().lower() == "molodetz"
|
||||||
assert page.locator("nav .nav-links a", has_text="Join").count() == 1
|
assert page.locator("nav .nav-links a", has_text="Join").count() == 1
|
||||||
assert page.console_errors == []
|
assert page.console_errors == []
|
||||||
@@ -15,8 +14,8 @@ def test_home_renders_without_errors(page, app_server):
|
|||||||
def test_mobile_nav_toggle(browser, app_server):
|
def test_mobile_nav_toggle(browser, app_server):
|
||||||
context = browser.new_context(viewport={"width": 390, "height": 800})
|
context = browser.new_context(viewport={"width": 390, "height": 800})
|
||||||
page = context.new_page()
|
page = context.new_page()
|
||||||
page.goto(app_server + "/", wait_until="domcontentloaded")
|
page.set_default_timeout(60000)
|
||||||
page.wait_for_selector("html[data-app=ready]")
|
goto_ready(page, app_server + "/")
|
||||||
assert_no_horizontal_overflow(page)
|
assert_no_horizontal_overflow(page)
|
||||||
assert not page.locator("#nav-links").is_visible()
|
assert not page.locator("#nav-links").is_visible()
|
||||||
page.click("[data-nav-toggle]")
|
page.click("[data-nav-toggle]")
|
||||||
@@ -26,6 +25,5 @@ def test_mobile_nav_toggle(browser, app_server):
|
|||||||
|
|
||||||
def test_public_pages_have_no_js_errors(page, app_server):
|
def test_public_pages_have_no_js_errors(page, app_server):
|
||||||
for path in PUBLIC_PAGES:
|
for path in PUBLIC_PAGES:
|
||||||
page.goto(app_server + path, wait_until="domcontentloaded")
|
goto_ready(page, app_server + path)
|
||||||
page.wait_for_selector("html[data-app=ready]")
|
|
||||||
assert page.console_errors == []
|
assert page.console_errors == []
|
||||||
|
|||||||
Reference in New Issue
Block a user