From f6edcadd46ab278bce0b9f121e5c7e516fdda181 Mon Sep 17 00:00:00 2001 From: codex_a Date: Sat, 28 Feb 2026 20:20:18 +0000 Subject: [PATCH] fix: run E2E tests properly with mounted browsers and real postgres MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Mount /opt/playwright-tools/browsers into web container (docker-compose.yml and CI docker run) — never download browsers, use the ones on this host - Set PLAYWRIGHT_BROWSERS_PATH in all container envs (compose + CI) - Drop 'playwright install chromium' steps from pr-e2e and nightly-e2e jobs - Bump playwright requirement to ~1.57.0 to match the installed browser builds - Fix seed_e2e_content: de-duplicate default Site entries left by unit test fixtures so Wagtail always routes to the seeded home page - Fix test_comments_section_absent_when_disabled: use exact=True on heading locator to avoid matching 'No Comments Article' h1 as 'Comments' heading - Fix test_copy_link_button_updates_text: use [data-copy-link] data-attr locator (stable across text change) and force-override clipboard.writeText via page.evaluate() rather than relying on init_script polyfill E2E suite verified locally: 34 passed via docker compose exec Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .gitea/workflows/ci.yml | 8 ++++---- apps/core/management/commands/seed_e2e_content.py | 2 ++ docker-compose.yml | 2 ++ e2e/test_article_detail.py | 7 ++++--- e2e/test_comments.py | 4 ++-- requirements/base.txt | 2 +- 6 files changed, 15 insertions(+), 10 deletions(-) diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml index 803847b..decde0b 100644 --- a/.gitea/workflows/ci.yml +++ b/.gitea/workflows/ci.yml @@ -97,12 +97,14 @@ jobs: - name: Start app with seeded content run: | docker run -d --name pr-e2e-app --network container:pr-e2e-postgres \ + -v /opt/playwright-tools/browsers:/opt/playwright-tools/browsers:ro \ -e SECRET_KEY=ci-secret-key \ -e DATABASE_URL=postgres://nohype:nohype@127.0.0.1:5432/nohype \ -e CONSENT_POLICY_VERSION=1 \ -e EMAIL_BACKEND=django.core.mail.backends.console.EmailBackend \ -e DEFAULT_FROM_EMAIL=hello@nohypeai.com \ -e NEWSLETTER_PROVIDER=buttondown \ + -e PLAYWRIGHT_BROWSERS_PATH=/opt/playwright-tools/browsers \ "$CI_IMAGE" \ sh -lc "python manage.py migrate --noinput && python manage.py seed_e2e_content && python manage.py runserver 0.0.0.0:8000" for i in $(seq 1 40); do @@ -114,9 +116,6 @@ jobs: docker logs pr-e2e-app || true exit 1 - - name: Install Playwright browsers - run: docker exec pr-e2e-app python -m playwright install chromium - - name: Run E2E tests run: | docker exec -e E2E_BASE_URL=http://127.0.0.1:8000 pr-e2e-app \ @@ -159,12 +158,14 @@ jobs: - name: Start dev server with seeded content run: | docker run -d --name nightly-e2e --network container:nightly-postgres \ + -v /opt/playwright-tools/browsers:/opt/playwright-tools/browsers:ro \ -e SECRET_KEY=ci-secret-key \ -e DATABASE_URL=postgres://nohype:nohype@127.0.0.1:5432/nohype \ -e CONSENT_POLICY_VERSION=1 \ -e EMAIL_BACKEND=django.core.mail.backends.console.EmailBackend \ -e DEFAULT_FROM_EMAIL=hello@nohypeai.com \ -e NEWSLETTER_PROVIDER=buttondown \ + -e PLAYWRIGHT_BROWSERS_PATH=/opt/playwright-tools/browsers \ "$CI_IMAGE" \ sh -lc "python manage.py migrate --noinput && python manage.py seed_e2e_content && python manage.py runserver 0.0.0.0:8000" for i in $(seq 1 40); do @@ -177,7 +178,6 @@ jobs: exit 1 - name: Run Playwright E2E tests run: | - docker exec nightly-e2e python -m playwright install chromium docker exec -e E2E_BASE_URL=http://127.0.0.1:8000 nightly-e2e \ pytest e2e/ apps/core/tests/test_nightly_e2e_playwright.py -o addopts='' -q --tb=short - name: Remove nightly container diff --git a/apps/core/management/commands/seed_e2e_content.py b/apps/core/management/commands/seed_e2e_content.py index 04ecea9..37112b6 100644 --- a/apps/core/management/commands/seed_e2e_content.py +++ b/apps/core/management/commands/seed_e2e_content.py @@ -129,5 +129,7 @@ class Command(BaseCommand): site.is_default_site = True site.site_name = "No Hype AI" site.save() + # Remove any other conflicting default-site entries left by test fixtures + Site.objects.exclude(pk=site.pk).filter(is_default_site=True).update(is_default_site=False) self.stdout.write(self.style.SUCCESS("Seeded E2E content.")) diff --git a/docker-compose.yml b/docker-compose.yml index 4355132..6a2ef70 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -7,6 +7,7 @@ services: python manage.py runserver 0.0.0.0:8000" volumes: - .:/app + - /opt/playwright-tools/browsers:/opt/playwright-tools/browsers:ro ports: - "8035:8000" environment: @@ -21,6 +22,7 @@ services: EMAIL_BACKEND: django.core.mail.backends.console.EmailBackend DEFAULT_FROM_EMAIL: hello@nohypeai.com NEWSLETTER_PROVIDER: buttondown + PLAYWRIGHT_BROWSERS_PATH: /opt/playwright-tools/browsers depends_on: db: condition: service_healthy diff --git a/e2e/test_article_detail.py b/e2e/test_article_detail.py index 2afef72..37b173f 100644 --- a/e2e/test_article_detail.py +++ b/e2e/test_article_detail.py @@ -41,7 +41,7 @@ def test_article_share_section_present(page: Page, base_url: str) -> None: def test_article_comments_section_present(page: Page, base_url: str) -> None: _go_to_article(page, base_url) # The article has comments_enabled=True - expect(page.get_by_role("heading", name="Comments")).to_be_visible() + expect(page.get_by_role("heading", name="Comments", exact=True)).to_be_visible() expect(page.get_by_role("button", name="Post comment")).to_be_visible() @@ -64,8 +64,9 @@ def test_article_related_section_present(page: Page, base_url: str) -> None: @pytest.mark.e2e def test_copy_link_button_updates_text(page: Page, base_url: str) -> None: _go_to_article(page, base_url) - copy_btn = page.get_by_role("button", name="Copy link") + copy_btn = page.locator("[data-copy-link]") expect(copy_btn).to_be_visible() + # Force-override clipboard so writeText always resolves, even in non-HTTPS headless context + page.evaluate("navigator.clipboard.writeText = () => Promise.resolve()") copy_btn.click() - # Clipboard polyfill in conftest ensures writeText resolves; button shows "Copied" expect(copy_btn).to_have_text("Copied") diff --git a/e2e/test_comments.py b/e2e/test_comments.py index 1b365c4..c104588 100644 --- a/e2e/test_comments.py +++ b/e2e/test_comments.py @@ -54,6 +54,6 @@ def test_comments_section_absent_when_disabled(page: Page, base_url: str) -> Non ) # Confirm we're on the right page expect(page.get_by_role("heading", level=1)).to_have_text("No Comments Article") - # Comments section must be absent - expect(page.get_by_role("heading", name="Comments")).to_have_count(0) + # Comments section must be absent — exact=True prevents matching "No Comments Article" h1 + expect(page.get_by_role("heading", name="Comments", exact=True)).to_have_count(0) expect(page.get_by_role("button", name="Post comment")).to_have_count(0) diff --git a/requirements/base.txt b/requirements/base.txt index af13702..9015b0d 100644 --- a/requirements/base.txt +++ b/requirements/base.txt @@ -17,7 +17,7 @@ pytest-benchmark~=4.0.0 factory-boy~=3.3.0 wagtail-factories~=4.2.0 feedparser~=6.0.0 -playwright~=1.52.0 +playwright~=1.57.0 pytest-playwright~=0.7.0 ruff~=0.6.0 mypy~=1.11.0