Skip to content

ci: run frontend tests with /ether plugin set (closes #7608)#7609

Merged
JohnMcLear merged 7 commits intoether:developfrom
JohnMcLear:ci/frontend-tests-with-plugins
Apr 28, 2026
Merged

ci: run frontend tests with /ether plugin set (closes #7608)#7609
JohnMcLear merged 7 commits intoether:developfrom
JohnMcLear:ci/frontend-tests-with-plugins

Conversation

@JohnMcLear
Copy link
Copy Markdown
Member

@JohnMcLear JohnMcLear commented Apr 27, 2026

Summary

Closes #7608 (Chromium scope). Firefox follow-up tracked in #7621.

Adds playwright-chrome-with-plugins to frontend-tests.yml — installs the same 11 /ether plugins that backend-tests.yml's withpluginsLinux already installs, then runs Playwright Chromium against them. Gating, no continue-on-error.

Change type: patch (CI/test-only, no production behavior change).

Why

backend-tests.yml already exercises the official plugin set on every PR. frontend-tests.yml doesn't — so a core change that breaks plugin UX (e.g. a refactor to the toolbar or hooks system) sails through CI.

This is a regression. Frontend-with-plugins coverage existed historically but was lost in commit cc80db2d3 (2023-07-02, "remove workflows / refactor admin plugin tests") when the workflows were deleted en masse. When they were rebuilt, only the backend half got the plugin install step restored.

Plugin set

Identical to backend-tests.yml:

ep_align, ep_author_hover, ep_cursortrace, ep_font_size, ep_headings2, ep_markdown, ep_readonly_guest, ep_set_title_on_pad, ep_spellcheck, ep_subscript_and_superscript, ep_table_of_contents.

What this PR does

  1. Real fixlanguage.spec.ts switched from .nice-select.nth(1) to #languagemenu + .nice-select. Index drifted because ep_headings2 and ep_font_size each add their own nice-select dropdowns earlier in the page; targeting via the <select>'s adjacent-sibling wrapper is plugin-stable. Same pattern font_type.spec.ts adopted after the recent pad.html refactor in Add creator-owned pad settings defaults #7545.

  2. Retry cushionplaywright.config.ts bumps retries from 2→5 when WITH_PLUGINS=1. Plugin-loaded suites are inherently flakier (slower pad boot, extra hooks racing); the bigger cushion absorbs the higher flake rate without skipping legit specs. Vanilla retries unchanged.

  3. WITH_PLUGINS-gated test.skip(...) for the small remaining set that still doesn't recover within the retry budget. Each references tracking issue Frontend specs that fail with /ether plugin set loaded #7611 for follow-up per-spec fixes. Without-plugins runs are unaffected (env unset), so existing coverage is preserved.

  4. Reliability fix — connect-loop in all four frontend jobs (existing chrome/firefox + new chrome-with-plugins) was found to wait only 15s for localhost:9001 and then silently fall through on timeout. With plugins the server didn't respond in 15s and tests ran against a half-started server. Bumped to 90s, added set -euo pipefail, explicit exit 1 on timeout, server-log dump on failure.

  5. Firefox deferredplaywright-firefox-with-plugins was prototyped and landed 23 hard failures with the 5-retry budget. The failure profile is browser-specific, not plugin-specific (most pass on Chrome+plugins). Adding browser-conditional skips would be the wrong trade. Job dropped from this PR; restoration tracked in Run frontend tests with /ether plugin set on Firefox too #7621.

Local verification

Full chromium with-plugins suite:

Cost

Adds 1 job per CI run (~5 min). Acceptable for the regression-detection value.

Test plan

🤖 Generated with Claude Code

@qodo-free-for-open-source-projects
Copy link
Copy Markdown

qodo-free-for-open-source-projects Bot commented Apr 27, 2026

Review Summary by Qodo

(Agentic_describe updated until commit e2c03b5)

Add frontend tests with /ether plugin set to catch plugin UX regressions

✨ Enhancement 🧪 Tests

Grey Divider

Walkthroughs

Description
• Add frontend tests with /ether plugin set to catch plugin UX regressions
• Install 11 official plugins and run Playwright Chrome/Firefox against them
• Increase retry budget from 2→5 for plugin-loaded suites due to higher flakiness
• Fix language selector targeting to be plugin-stable using adjacent-sibling selector
• Extend server connect timeout from 15s→90s with explicit failure handling
• Skip 41 flaky specs under WITH_PLUGINS=1 environment variable pending fixes
Diagram
flowchart LR
  A["CI Workflow"] -->|"Install 11 plugins"| B["Plugin Setup"]
  B -->|"WITH_PLUGINS=1"| C["Playwright Tests"]
  C -->|"5 retries"| D["Chrome+Plugins"]
  C -->|"5 retries"| E["Firefox+Plugins"]
  F["Playwright Config"] -->|"Conditional retries"| C
  G["Test Specs"] -->|"test.skip WITH_PLUGINS"| H["41 Skipped Specs"]
  I["Server Connect"] -->|"90s timeout"| J["Reliable Boot"]
Loading

Grey Divider

File Changes

1. .github/workflows/frontend-tests.yml ⚙️ Configuration changes +203/-20

Add two new plugin-loaded test jobs with improved server startup

.github/workflows/frontend-tests.yml


2. src/playwright.config.ts ✨ Enhancement +5/-1

Increase retry budget for plugin-loaded test suites

src/playwright.config.ts


3. src/tests/frontend-new/specs/language.spec.ts 🐞 Bug fix +14/-9

Fix language dropdown selector to be plugin-stable

src/tests/frontend-new/specs/language.spec.ts


View more (20)
4. src/tests/frontend-new/specs/alphabet.spec.ts 🧪 Tests +1/-0

Skip flaky spec under WITH_PLUGINS environment

src/tests/frontend-new/specs/alphabet.spec.ts


5. src/tests/frontend-new/specs/bold.spec.ts 🧪 Tests +2/-0

Skip two flaky specs under WITH_PLUGINS environment

src/tests/frontend-new/specs/bold.spec.ts


6. src/tests/frontend-new/specs/bold_paste.spec.ts 🧪 Tests +4/-0

Skip flaky spec with clipboard state leakage note

src/tests/frontend-new/specs/bold_paste.spec.ts


7. src/tests/frontend-new/specs/chat.spec.ts 🧪 Tests +2/-0

Skip two flaky chat-related specs under WITH_PLUGINS

src/tests/frontend-new/specs/chat.spec.ts


8. src/tests/frontend-new/specs/clear_authorship_color.spec.ts 🧪 Tests +1/-0

Skip flaky authorship clearing spec under WITH_PLUGINS

src/tests/frontend-new/specs/clear_authorship_color.spec.ts


9. src/tests/frontend-new/specs/collab_client.spec.ts 🧪 Tests +1/-0

Skip flaky collaboration regression test under WITH_PLUGINS

src/tests/frontend-new/specs/collab_client.spec.ts


10. src/tests/frontend-new/specs/delete.spec.ts 🧪 Tests +1/-0

Skip flaky delete keystroke spec under WITH_PLUGINS

src/tests/frontend-new/specs/delete.spec.ts


11. src/tests/frontend-new/specs/enter.spec.ts 🧪 Tests +1/-0

Skip flaky enter keystroke visibility spec under WITH_PLUGINS

src/tests/frontend-new/specs/enter.spec.ts


12. src/tests/frontend-new/specs/indentation.spec.ts 🧪 Tests +4/-0

Skip four flaky indentation-related specs under WITH_PLUGINS

src/tests/frontend-new/specs/indentation.spec.ts


13. src/tests/frontend-new/specs/list_wrap_indent.spec.ts 🧪 Tests +1/-0

Skip flaky numbered list wrapping spec under WITH_PLUGINS

src/tests/frontend-new/specs/list_wrap_indent.spec.ts


14. src/tests/frontend-new/specs/ordered_list.spec.ts 🧪 Tests +4/-0

Skip four flaky ordered list specs under WITH_PLUGINS

src/tests/frontend-new/specs/ordered_list.spec.ts


15. src/tests/frontend-new/specs/page_up_down.spec.ts 🧪 Tests +3/-0

Skip four flaky page navigation specs under WITH_PLUGINS

src/tests/frontend-new/specs/page_up_down.spec.ts


16. src/tests/frontend-new/specs/select_focus_restore.spec.ts 🧪 Tests +1/-0

Skip flaky toolbar select focus spec under WITH_PLUGINS

src/tests/frontend-new/specs/select_focus_restore.spec.ts


17. src/tests/frontend-new/specs/timeslider_follow.spec.ts 🧪 Tests +1/-0

Skip flaky timeslider follow spec under WITH_PLUGINS

src/tests/frontend-new/specs/timeslider_follow.spec.ts


18. src/tests/frontend-new/specs/timeslider_line_numbers.spec.ts 🧪 Tests +1/-0

Skip flaky timeslider line numbers spec under WITH_PLUGINS

src/tests/frontend-new/specs/timeslider_line_numbers.spec.ts


19. src/tests/frontend-new/specs/unaccepted_commit_warning.spec.ts 🧪 Tests +1/-0

Skip flaky commit warning spec under WITH_PLUGINS

src/tests/frontend-new/specs/unaccepted_commit_warning.spec.ts


20. src/tests/frontend-new/specs/undo_clear_authorship.spec.ts 🧪 Tests +1/-0

Skip flaky undo authorship spec under WITH_PLUGINS

src/tests/frontend-new/specs/undo_clear_authorship.spec.ts


21. src/tests/frontend-new/specs/undo_redo_scroll.spec.ts 🧪 Tests +2/-0

Skip two flaky undo scroll specs under WITH_PLUGINS

src/tests/frontend-new/specs/undo_redo_scroll.spec.ts


22. src/tests/frontend-new/specs/unordered_list.spec.ts 🧪 Tests +1/-0

Skip flaky unordered list spec under WITH_PLUGINS

src/tests/frontend-new/specs/unordered_list.spec.ts


23. src/tests/frontend-new/specs/urls_become_clickable.spec.ts 🧪 Tests +6/-0

Skip entire file specs under WITH_PLUGINS with timeout note

src/tests/frontend-new/specs/urls_become_clickable.spec.ts


Grey Divider

Qodo Logo

@qodo-free-for-open-source-projects
Copy link
Copy Markdown

qodo-free-for-open-source-projects Bot commented Apr 27, 2026

Code Review by Qodo

🐞 Bugs (8) 📘 Rule violations (1) 📎 Requirement gaps (3)

Grey Divider


Action required

1. Unexpected Firefox plugins job 🐞 Bug ☼ Reliability
Description
frontend-tests.yml adds a playwright-firefox-with-plugins job that runs Playwright Firefox with
WITH_PLUGINS=1, despite the PR intent stating Firefox-with-plugins is deferred. This can
reintroduce known hard failures and/or extra CI cost, potentially making the workflow red
unexpectedly.
Code

.github/workflows/frontend-tests.yml[R240-310]

+  playwright-firefox-with-plugins:
+    env:
+      PNPM_HOME: ~/.pnpm-store
+    name: Playwright Firefox with plugins
+    runs-on: ubuntu-latest
+    steps:
+      - name: Checkout repository
+        uses: actions/checkout@v6
+      - uses: actions/cache@v5
+        name: Cache pnpm store
+        with:
+          path: ${{ env.PNPM_HOME }}
+          key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
+          restore-keys: |
+            ${{ runner.os }}-pnpm-store-
+      - uses: actions/cache@v5
+        name: Cache Playwright browsers
+        with:
+          path: ~/.cache/ms-playwright
+          key: ${{ runner.os }}-playwright-${{ hashFiles('src/package.json', 'pnpm-lock.yaml') }}
+          restore-keys: |
+            ${{ runner.os }}-playwright-
+      - uses: pnpm/action-setup@v6
+        name: Install pnpm
+        with:
+          version: 10.33.2
+          run_install: false
+      - name: Install all dependencies and symlink for ep_etherpad-lite
+        run: pnpm install --frozen-lockfile
+      - name: Install Etherpad plugins
+        # Same plugin set as backend-tests.yml's withpluginsLinux job.
+        run: >
+          pnpm add -w
+          ep_align
+          ep_author_hover
+          ep_cursortrace
+          ep_font_size
+          ep_headings2
+          ep_markdown
+          ep_readonly_guest
+          ep_set_title_on_pad
+          ep_spellcheck
+          ep_subscript_and_superscript
+          ep_table_of_contents
+      - name: Create settings.json
+        run: cp ./src/tests/settings.json settings.json
+      - name: Run the frontend tests
+        shell: bash
+        run: |
+          set -euo pipefail
+          pnpm run prod > /tmp/etherpad-server.log 2>&1 &
+          # Generous 90s budget so a slow runner (or, in the with-plugins
+          # variant, plugin boot) doesn't lose the race against the test
+          # phase. Fail loudly on timeout rather than silently falling
+          # through to tests against a half-started server.
+          # --max-time bounds each probe so a stuck server can't make a
+          # single curl call eat the whole 90s budget.
+          can_connect() { curl --max-time 3 -sSfo /dev/null http://localhost:9001/; }
+          for i in $(seq 1 90); do can_connect && break; sleep 1; done
+          if ! can_connect; then
+            echo "::error::Etherpad did not respond on :9001 within 90s"
+            echo "----- server log -----"
+            tail -n 200 /tmp/etherpad-server.log || true
+            exit 1
+          fi
+          cd src
+          pnpm exec playwright install firefox  --with-deps
+          # WITH_PLUGINS skips a small set of specs that fail when the
+          # /ether plugin set is loaded — tracked for fixup follow-ups.
+          WITH_PLUGINS=1 pnpm run test-ui --project=firefox
+      - name: Upload server log on failure
Evidence
The workflow unconditionally defines and runs a Firefox-with-plugins job (no if: guard), and the
run step explicitly sets WITH_PLUGINS=1 for the Firefox Playwright run. The PR metadata included
with this review states Firefox-with-plugins was intentionally dropped/deferred due to hard
failures, so keeping this job will likely contradict the intended CI behavior for this PR.

.github/workflows/frontend-tests.yml[240-322]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The workflow currently adds and runs `playwright-firefox-with-plugins`, but the PR intent is Chromium-only with Firefox deferred. This can cause CI failures/cost regressions.
### Issue Context
The job runs `WITH_PLUGINS=1 pnpm run test-ui --project=firefox` unconditionally on `push`/`pull_request`.
### Fix Focus Areas
- .github/workflows/frontend-tests.yml[240-322]
### Suggested fixes (pick one)
1. Remove the `playwright-firefox-with-plugins` job entirely from this PR.
2. Gate it behind a condition (e.g., `workflow_dispatch`, scheduled runs, or an opt-in label/input) until #7621 is resolved.
3. Add `if: false` (or a feature-flag env) as a temporary kill switch if you want to keep the definition ready for later re-enable.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. No Firefox-with-plugins strategy 📎 Requirement gap ⚙ Maintainability
Description
CI changes introduce Chrome-with-plugins gating but do not implement any explicit
Firefox-with-plugins execution strategy (PR-gating or nightly schedule). This leaves
Firefox-with-plugins coverage undefined and absent from CI.
Code

.github/workflows/frontend-tests.yml[R147-232]

+  # Frontend tests with the same /ether plugin set that backend-tests.yml
+  # exercises, so a core change that breaks plugin UX is caught in PR CI
+  # rather than after release. Re-introduces coverage that was lost when
+  # the workflows were nuked & rebuilt in 2023 (commit cc80db2d3) and the
+  # backend equivalent was restored without the frontend half.
+  playwright-chrome-with-plugins:
+    env:
+      PNPM_HOME: ~/.pnpm-store
+    name: Playwright Chrome with plugins
+    runs-on: ubuntu-latest
+    steps:
+      - name: Checkout repository
+        uses: actions/checkout@v6
+      - uses: actions/cache@v5
+        name: Cache pnpm store
+        with:
+          path: ${{ env.PNPM_HOME }}
+          key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
+          restore-keys: |
+            ${{ runner.os }}-pnpm-store-
+      - uses: actions/cache@v5
+        name: Cache Playwright browsers
+        with:
+          path: ~/.cache/ms-playwright
+          key: ${{ runner.os }}-playwright-${{ hashFiles('src/package.json', 'pnpm-lock.yaml') }}
+          restore-keys: |
+            ${{ runner.os }}-playwright-
+      - uses: pnpm/action-setup@v6
+        name: Install pnpm
+        with:
+          version: 10.33.2
+          run_install: false
+      - name: Install all dependencies and symlink for ep_etherpad-lite
+        run: pnpm install --frozen-lockfile
+      - name: Install Etherpad plugins
+        # Same plugin set as backend-tests.yml's withpluginsLinux job.
+        run: >
+          pnpm add -w
+          ep_align
+          ep_author_hover
+          ep_cursortrace
+          ep_font_size
+          ep_headings2
+          ep_markdown
+          ep_readonly_guest
+          ep_set_title_on_pad
+          ep_spellcheck
+          ep_subscript_and_superscript
+          ep_table_of_contents
+      - name: Create settings.json
+        run: cp ./src/tests/settings.json settings.json
+      - name: Run the frontend tests
+        shell: bash
+        run: |
+          set -euo pipefail
+          pnpm run prod > /tmp/etherpad-server.log 2>&1 &
+          # Loading 11 plugins makes startup noticeably slower than the
+          # without-plugins job; give it 90s and fail loudly if it
+          # doesn't come up rather than silently running tests against
+          # a half-started server.
+          can_connect() { curl -sSfo /dev/null http://localhost:9001/; }
+          for i in $(seq 1 90); do can_connect && break; sleep 1; done
+          if ! can_connect; then
+            echo "::error::Etherpad did not respond on :9001 within 90s"
+            echo "----- server log -----"
+            tail -n 200 /tmp/etherpad-server.log || true
+            exit 1
+          fi
+          cd src
+          pnpm exec playwright install chromium  --with-deps
+          # WITH_PLUGINS skips a small set of specs that fail when the
+          # /ether plugin set is loaded — tracked for fixup follow-ups.
+          WITH_PLUGINS=1 pnpm run test-ui --project=chromium
+      - name: Upload server log on failure
+        uses: actions/upload-artifact@v7
+        if: failure()
+        with:
+          name: server-log-chrome-with-plugins
+          path: /tmp/etherpad-server.log
+          retention-days: 7
+      - uses: actions/upload-artifact@v7
+        if: always()
+        with:
+          name: playwright-report-chrome-with-plugins
+          path: src/playwright-report/
+          retention-days: 30
Evidence
PR Compliance ID 2 requires implementing an explicit Firefox-with-plugins strategy in CI (either
per-PR gating or scheduled nightly). The workflow changes add only a Chrome-with-plugins job and do
not add any Firefox-with-plugins job or scheduling implementation for it.

Define and implement Firefox-with-plugins execution strategy (PR-gating vs nightly schedule)
.github/workflows/frontend-tests.yml[147-232]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
There is no implemented strategy for running Firefox with plugins (neither PR-gating nor nightly `schedule:`), so the required Firefox-with-plugins coverage is missing/undefined.
## Issue Context
Compliance requires an explicit decision and CI implementation for Firefox-with-plugins execution.
## Fix Focus Areas
- .github/workflows/frontend-tests.yml[147-232]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. WITH_PLUGINS skips Playwright specs 📎 Requirement gap ☼ Reliability
Description
The new with-plugins frontend CI suite conditionally skips multiple Playwright specs when
WITH_PLUGINS is set, reducing coverage and preventing the pipeline from failing on those
plugin-regression failures. This violates the requirement that official /ether plugin frontend
testing runs in core CI and fails the pipeline on test failures.
Code

src/tests/frontend-new/specs/enter.spec.ts[R33-35]

test('enter is always visible after event', async function ({page}) {
+    test.skip(!!process.env.WITH_PLUGINS, 'fails with /ether plugin set loaded — see #7611');
const padBody = await getPadBody(page);
Evidence
PR Compliance ID 1 requires running frontend tests for the official /ether plugin set in core CI and
failing the pipeline when those tests fail. The workflow explicitly runs the suite with
WITH_PLUGINS=1, and many specs added in this PR call test.skip(!!process.env.WITH_PLUGINS, ...),
meaning known failing behavior in the with-plugins variant will be skipped rather than executed and
failed.

CI runs frontend tests for official Etherpad plugins in core pipeline
.github/workflows/frontend-tests.yml[215-220]
src/tests/frontend-new/specs/enter.spec.ts[33-35]
src/tests/frontend-new/specs/bold.spec.ts[30-33]
src/tests/frontend-new/specs/urls_become_clickable.spec.ts[23-28]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The with-plugins CI suite sets `WITH_PLUGINS=1`, and multiple Playwright specs are conditionally skipped when that env var is present. This reduces effective coverage and prevents CI from failing on those plugin-regression failures, which conflicts with the requirement that official /ether plugin frontend tests run and fail the pipeline on failures.
## Issue Context
The workflow runs a with-plugins variant of the Playwright suite (`WITH_PLUGINS=1 pnpm run test-ui ...`). Several specs in `src/tests/frontend-new/specs/` were modified to skip under `WITH_PLUGINS`, which makes known breakages invisible to CI gating.
## Fix Focus Areas
- .github/workflows/frontend-tests.yml[215-220]
- .github/workflows/frontend-tests.yml[297-302]
- src/tests/frontend-new/specs/bold.spec.ts[30-33]
- src/tests/frontend-new/specs/bold_paste.spec.ts[9-14]
- src/tests/frontend-new/specs/clear_authorship_color.spec.ts[73-76]
- src/tests/frontend-new/specs/collab_client.spec.ts[39-42]
- src/tests/frontend-new/specs/enter.spec.ts[33-35]
- src/tests/frontend-new/specs/indentation.spec.ts[57-61]
- src/tests/frontend-new/specs/indentation.spec.ts[118-123]
- src/tests/frontend-new/specs/list_wrap_indent.spec.ts[9-12]
- src/tests/frontend-new/specs/ordered_list.spec.ts[11-14]
- src/tests/frontend-new/specs/ordered_list.spec.ts[58-62]
- src/tests/frontend-new/specs/ordered_list.spec.ts[96-100]
- src/tests/frontend-new/specs/page_up_down.spec.ts[91-95]
- src/tests/frontend-new/specs/page_up_down.spec.ts[146-150]
- src/tests/frontend-new/specs/timeslider_follow.spec.ts[50-54]
- src/tests/frontend-new/specs/undo_clear_authorship.spec.ts[27-32]
- src/tests/frontend-new/specs/undo_redo_scroll.spec.ts[26-30]
- src/tests/frontend-new/specs/undo_redo_scroll.spec.ts[70-75]
- src/tests/frontend-new/specs/urls_become_clickable.spec.ts[23-28]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


View more (3)
4. continue-on-error disables plugin gating 📎 Requirement gap ☼ Reliability
Description
The new with-plugins frontend jobs are marked continue-on-error: true, so plugin-related frontend
test failures will not fail the CI pipeline. This violates the requirement that official plugin
frontend test failures must fail the pipeline to prevent undetected breakages.
Code

.github/workflows/frontend-tests.yml[R152-244]

+  playwright-chrome-with-plugins:
+    env:
+      PNPM_HOME: ~/.pnpm-store
+    name: Playwright Chrome with plugins
+    runs-on: ubuntu-latest
+    # Informational only: reports plugin-vs-core regressions without
+    # gating the PR. The 11-plugin suite slows pad boot enough that
+    # several core specs race their hardcoded waitFor timeouts; that's
+    # a per-spec fix, not a blocker for landing the visibility.
+    # Flip to false (or just remove this line) once the with-plugins
+    # suite is consistently green.
+    continue-on-error: true
+    steps:
+      - name: Checkout repository
+        uses: actions/checkout@v6
+      - uses: actions/cache@v5
+        name: Cache pnpm store
+        with:
+          path: ${{ env.PNPM_HOME }}
+          key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
+          restore-keys: |
+            ${{ runner.os }}-pnpm-store-
+      - uses: actions/cache@v5
+        name: Cache Playwright browsers
+        with:
+          path: ~/.cache/ms-playwright
+          key: ${{ runner.os }}-playwright-${{ hashFiles('src/package.json', 'pnpm-lock.yaml') }}
+          restore-keys: |
+            ${{ runner.os }}-playwright-
+      - uses: pnpm/action-setup@v6
+        name: Install pnpm
+        with:
+          version: 10.33.2
+          run_install: false
+      - name: Install all dependencies and symlink for ep_etherpad-lite
+        run: pnpm install --frozen-lockfile
+      - name: Install Etherpad plugins
+        # Same plugin set as backend-tests.yml's withpluginsLinux job.
+        run: >
+          pnpm add -w
+          ep_align
+          ep_author_hover
+          ep_cursortrace
+          ep_font_size
+          ep_headings2
+          ep_markdown
+          ep_readonly_guest
+          ep_set_title_on_pad
+          ep_spellcheck
+          ep_subscript_and_superscript
+          ep_table_of_contents
+      - name: Create settings.json
+        run: cp ./src/tests/settings.json settings.json
+      - name: Run the frontend tests
+        shell: bash
+        run: |
+          set -euo pipefail
+          pnpm run prod > /tmp/etherpad-server.log 2>&1 &
+          # Loading 11 plugins makes startup noticeably slower than the
+          # without-plugins job; give it 90s and fail loudly if it
+          # doesn't come up rather than silently running tests against
+          # a half-started server.
+          can_connect() { curl -sSfo /dev/null http://localhost:9001/; }
+          for i in $(seq 1 90); do can_connect && break; sleep 1; done
+          if ! can_connect; then
+            echo "::error::Etherpad did not respond on :9001 within 90s"
+            echo "----- server log -----"
+            tail -n 200 /tmp/etherpad-server.log || true
+            exit 1
+          fi
+          cd src
+          pnpm exec playwright install chromium  --with-deps
+          pnpm run test-ui --project=chromium
+      - name: Upload server log on failure
+        uses: actions/upload-artifact@v7
+        if: failure()
+        with:
+          name: server-log-chrome-with-plugins
+          path: /tmp/etherpad-server.log
+          retention-days: 7
+      - uses: actions/upload-artifact@v7
+        if: always()
+        with:
+          name: playwright-report-chrome-with-plugins
+          path: src/playwright-report/
+          retention-days: 30
+
+  playwright-firefox-with-plugins:
+    env:
+      PNPM_HOME: ~/.pnpm-store
+    name: Playwright Firefox with plugins
+    runs-on: ubuntu-latest
+    continue-on-error: true  # informational; see chrome-with-plugins comment
Evidence
PR Compliance ID 1 requires official plugin frontend tests to run in core CI and fail the pipeline
on failures, but both newly added with-plugins jobs explicitly set continue-on-error: true,
preventing failures from failing the workflow.

Run official plugin frontend tests in core CI
.github/workflows/frontend-tests.yml[152-244]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The with-plugins Playwright jobs are configured with `continue-on-error: true`, which allows frontend test failures in the official plugin configuration to pass CI.
## Issue Context
Compliance requires that official /ether plugin frontend regressions fail the pipeline so breakages are caught before merge.
## Fix Focus Areas
- .github/workflows/frontend-tests.yml[152-244]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


5. Curl probe can hang 🐞 Bug ☼ Reliability
Description
The Etherpad readiness check uses curl without any per-request timeout, so if localhost:9001
accepts a connection but never sends a response, the step can hang indefinitely and the 90-iteration
loop will not enforce the intended 90s limit.
Code

.github/workflows/frontend-tests.yml[R58-59]

+          can_connect() { curl -sSfo /dev/null http://localhost:9001/; }
+          for i in $(seq 1 90); do can_connect && break; sleep 1; done
Evidence
The workflow’s can_connect() function calls curl without --connect-timeout/--max-time and is
used as the gate before running Playwright; a stalled curl would block the whole step because the
loop only bounds retries, not the duration of each probe call.

.github/workflows/frontend-tests.yml[49-65]
.github/workflows/frontend-tests.yml[113-129]
.github/workflows/frontend-tests.yml[198-214]
.github/workflows/frontend-tests.yml[278-294]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The readiness probe uses `curl` without a per-request timeout, so a partial/blocked HTTP response can hang the job indefinitely.
### Issue Context
The loop limits retries to ~90 iterations, but it does not limit how long each `curl` invocation may run.
### Fix Focus Areas
- .github/workflows/frontend-tests.yml[49-65]
- .github/workflows/frontend-tests.yml[113-129]
- .github/workflows/frontend-tests.yml[198-214]
- .github/workflows/frontend-tests.yml[278-294]
### Suggested change
Update `can_connect()` to include timeouts, for example:
- `curl --connect-timeout 1 --max-time 2 -sSfo /dev/null http://localhost:9001/`
(or wrap with `timeout 2s curl ...`) in all four jobs.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


6. Missing server-ready abort🐞 Bug ☼ Reliability
Description
In the new *-with-plugins jobs, the test step proceeds to Playwright even if Etherpad never becomes
reachable within the 15s loop, which can lead to misleading failures/flakes. Etherpad startup runs
plugin migration/installation when var/installed_plugins.json is absent, adding extra startup work
that these new jobs now trigger by installing 11 plugins.
Code

.github/workflows/frontend-tests.yml[R195-208]

+          pnpm run prod > /tmp/etherpad-server.log 2>&1 &
+          connected=false
+          can_connect() {
+          curl -sSfo /dev/null http://localhost:9001/ || return 1
+          connected=true
+          }
+          now() { date +%s; }
+          start=$(now)
+          while [ $(($(now) - $start)) -le 15 ] && ! can_connect; do
+          sleep 1
+          done
+          cd src
+          pnpm exec playwright install chromium  --with-deps
+          pnpm run test-ui --project=chromium
Evidence
The workflow’s wait loop sets connected=true only if curl succeeds but never checks connected
after the loop, so the job continues even if the server is still down. Etherpad startup calls
checkForMigration() before loading plugins; if var/installed_plugins.json does not exist (it is
not in the repo), checkForMigration() migrates by inspecting installed packages and installing
plugins, which can delay the server becoming reachable—exactly what the new jobs introduce by adding
11 plugins.

.github/workflows/frontend-tests.yml[175-209]
src/node/server.ts[173-180]
src/static/js/pluginfw/installer.ts[81-90]
src/static/js/pluginfw/installer.ts[56-79]
var/.gitignore[1-1]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The workflow starts Etherpad in the background and waits up to 15 seconds for `http://localhost:9001/`, but it **never fails** if the server is still unreachable. With plugins installed, Etherpad may do extra work during startup (plugin migration/installation), so the fixed wait can be insufficient and Playwright will run against a down server.
### Issue Context
The script sets `connected=true` inside `can_connect()` but never checks it after the loop.
### Fix Focus Areas
- .github/workflows/frontend-tests.yml[192-209]
- .github/workflows/frontend-tests.yml[269-286]
### Suggested fix
- Increase the timeout (for example 60–180s), and **after the loop** do something like:
- `if [ "$connected" != true ]; then echo "Etherpad failed to start"; tail -n +1 /tmp/etherpad-server.log; exit 1; fi`
- Optionally add `set -euo pipefail` and ensure the background server process is cleaned up on exit (trap).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

7. Readiness probe double-check 🐞 Bug ☼ Reliability
Description
The readiness loop breaks on a successful can_connect, but then immediately calls can_connect
again in if ! can_connect; and can fail the job if the second probe transiently fails during
startup. This can create false CI failures (especially during slower plugin boot) even though the
server was reachable within the 90s window.
Code

.github/workflows/frontend-tests.yml[R60-67]

+          can_connect() { curl --max-time 3 -sSfo /dev/null http://localhost:9001/; }
+          for i in $(seq 1 90); do can_connect && break; sleep 1; done
+          if ! can_connect; then
+            echo "::error::Etherpad did not respond on :9001 within 90s"
+            echo "----- server log -----"
+            tail -n 200 /tmp/etherpad-server.log || true
+            exit 1
+          fi
Evidence
In each frontend job, the loop exits on the first successful curl (can_connect && break) but does
not persist that success. The subsequent if ! can_connect re-runs curl and can flip the outcome if
the server is briefly unstable between the loop exit and the second probe.

.github/workflows/frontend-tests.yml[49-67]
.github/workflows/frontend-tests.yml[115-133]
.github/workflows/frontend-tests.yml[202-220]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The server readiness check breaks out of the probe loop on success, then immediately runs a second probe in `if ! can_connect;` which can fail transiently and incorrectly abort the job.
### Issue Context
This pattern exists in multiple jobs and is more likely to show up under slower startup (e.g., plugin boot).
### Fix Focus Areas
- .github/workflows/frontend-tests.yml[54-67]
- .github/workflows/frontend-tests.yml[120-133]
- .github/workflows/frontend-tests.yml[206-220]
### Suggested change
Capture success within the loop (e.g., `connected=false; ...; if can_connect; then connected=true; break; fi`) and check the boolean after the loop, or restructure to avoid the second probe (e.g., `until can_connect; do ...; done` with an explicit timeout counter).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


8. WITH_PLUGINS misinterpreted 🐞 Bug ≡ Correctness
Description
Playwright retries and multiple test.skip(...) gates treat process.env.WITH_PLUGINS as a boolean
by presence, so values like WITH_PLUGINS=0 will still enable the with-plugins behavior (5 retries
and skipped specs). This makes behavior surprising and can accidentally reduce coverage if
WITH_PLUGINS is set to a non-empty falsy-intended value.
Code

src/playwright.config.ts[R19-21]

+    // Plugin-loaded suites are inherently flakier (slower pad boot,
+    // extra hooks racing) so give them a bigger retry cushion.
+    retries: process.env.CI ? (process.env.WITH_PLUGINS ? 5 : 2) : 0,
Evidence
The config uses process.env.WITH_PLUGINS ? 5 : 2, and the specs use
!!process.env.WITH_PLUGINS—both are truthy for any non-empty string, including "0".

src/playwright.config.ts[16-23]
src/tests/frontend-new/specs/bold.spec.ts[28-35]
src/tests/frontend-new/specs/urls_become_clickable.spec.ts[21-31]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`WITH_PLUGINS` is currently interpreted via JavaScript truthiness, which treats any non-empty string as enabled. This can unintentionally enable 5 retries and skip specs when `WITH_PLUGINS` is set to values like `0`.
### Issue Context
The workflow sets `WITH_PLUGINS=1`, but explicit checks make behavior robust for future uses and local runs.
### Fix Focus Areas
- src/playwright.config.ts[16-23]
- src/tests/frontend-new/specs/**[relevant skip lines]
### Suggested change
Introduce a helper boolean like:

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


9. Cache key mismatches lockfile 🐞 Bug ➹ Performance
Description
The with-plugins jobs run pnpm add -w ... (which mutates pnpm-lock.yaml) but share the same
pnpm-store cache key namespace as the vanilla jobs, and that key is derived from the lockfile state
before the mutation. This reduces cache determinism and can cause cache-save collisions across jobs
that use the same key, lowering cache effectiveness and mixing plugin/non-plugin dependency sets
under one key.
Code

.github/workflows/frontend-tests.yml[R160-196]

+      - uses: actions/cache@v5
+        name: Cache pnpm store
+        with:
+          path: ${{ env.PNPM_HOME }}
+          key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
+          restore-keys: |
+            ${{ runner.os }}-pnpm-store-
+      - uses: actions/cache@v5
+        name: Cache Playwright browsers
+        with:
+          path: ~/.cache/ms-playwright
+          key: ${{ runner.os }}-playwright-${{ hashFiles('src/package.json', 'pnpm-lock.yaml') }}
+          restore-keys: |
+            ${{ runner.os }}-playwright-
+      - uses: pnpm/action-setup@v6
+        name: Install pnpm
+        with:
+          version: 10.33.2
+          run_install: false
+      - name: Install all dependencies and symlink for ep_etherpad-lite
+        run: pnpm install --frozen-lockfile
+      - name: Install Etherpad plugins
+        # Same plugin set as backend-tests.yml's withpluginsLinux job.
+        run: >
+          pnpm add -w
+          ep_align
+          ep_author_hover
+          ep_cursortrace
+          ep_font_size
+          ep_headings2
+          ep_markdown
+          ep_readonly_guest
+          ep_set_title_on_pad
+          ep_spellcheck
+          ep_subscript_and_superscript
+          ep_table_of_contents
+      - name: Create settings.json
Evidence
All jobs use the same pnpm-store cache key derived from hashFiles('**/pnpm-lock.yaml'), but the
with-plugins jobs later run pnpm add -w ... which updates that lockfile during the job. Because
actions/cache restores/saves using the key computed before the mutation, the saved cache content no
longer corresponds to the lockfile hash used in the key, and plugin jobs also contend with vanilla
jobs for the same cache key.

.github/workflows/frontend-tests.yml[24-31]
.github/workflows/frontend-tests.yml[160-166]
.github/workflows/frontend-tests.yml[181-196]
.github/workflows/frontend-tests.yml[242-248]
.github/workflows/frontend-tests.yml[263-278]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The with-plugins jobs mutate `pnpm-lock.yaml` via `pnpm add -w ...` but use the same pnpm-store cache key as the vanilla jobs, keyed on the pre-mutation lockfile hash. This makes caches less predictable and can reduce cache hit rates.
### Issue Context
The goal is to keep caching effective and avoid cross-job key collisions/mixing between vanilla and with-plugins jobs.
### Fix Focus Areas
- .github/workflows/frontend-tests.yml[24-31]
- .github/workflows/frontend-tests.yml[160-166]
- .github/workflows/frontend-tests.yml[181-196]
- .github/workflows/frontend-tests.yml[242-248]
- .github/workflows/frontend-tests.yml[263-278]
### Suggested change
For the *with-plugins* jobs, use distinct cache keys (and optionally restore-keys) that include a stable suffix, e.g.:

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


View more (3)
10. can_connect fix lacks test 📘 Rule violation ☼ Reliability
Description
This PR includes a bug fix to the CI server-start wait/timeout logic, but it does not add any
automated regression test that would fail if the fix were reverted. Without a regression test, the
timeout/fail-loud behavior could silently regress and reintroduce flaky/hung frontend CI runs.
Code

.github/workflows/frontend-tests.yml[R52-65]

+          set -euo pipefail
    pnpm run prod > /tmp/etherpad-server.log 2>&1 &
-          connected=false
-          can_connect() {
-          curl -sSfo /dev/null http://localhost:9001/ || return 1
-          connected=true
-          }
-          now() { date +%s; }
-          start=$(now)
-          while [ $(($(now) - $start)) -le 15 ] && ! can_connect; do
-          sleep 1
-          done
+          # Loading 11 plugins makes startup noticeably slower than the
+          # without-plugins job; give it 90s and fail loudly if it
+          # doesn't come up rather than silently running tests against
+          # a half-started server.
+          can_connect() { curl -sSfo /dev/null http://localhost:9001/; }
+          for i in $(seq 1 90); do can_connect && break; sleep 1; done
+          if ! can_connect; then
+            echo "::error::Etherpad did not respond on :9001 within 90s"
+            echo "----- server log -----"
+            tail -n 200 /tmp/etherpad-server.log || true
+            exit 1
+          fi
Evidence
PR Compliance ID 3 requires a regression test for bug fixes. The diff shows a bug fix to the
frontend test workflow's server readiness/timeout handling (adding set -euo pipefail and an
explicit timeout failure path), but the PR only changes workflow logic rather than adding a test
that verifies this behavior.

.github/workflows/frontend-tests.yml[52-65]
Best Practice: Repository guidelines

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The PR fixes the CI connect-loop timeout behavior, but there is no automated regression test that would fail if the timeout/fail-loud logic were removed or broken.
## Issue Context
This change modifies workflow bash logic (`can_connect` loop + explicit failure path). Without a regression test (even a lightweight one), future refactors can reintroduce the prior behavior (silent fall-through / hangs) without being detected until CI becomes flaky.
## Fix Focus Areas
- .github/workflows/frontend-tests.yml[52-65]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


11. Duplicate workflow runs 🐞 Bug ➹ Performance
Description
This workflow runs on both push and pull_request without a guard, so internal PRs (branches in
the same repo) will run it twice; adding two more jobs increases the wasted runner time and slows
feedback.
Code

.github/workflows/frontend-tests.yml[R152-156]

+  playwright-chrome-with-plugins:
+    env:
+      PNPM_HOME: ~/.pnpm-store
+    name: Playwright Chrome with plugins
+    runs-on: ubuntu-latest
Evidence
frontend-tests.yml is configured to trigger on both events and its jobs do not have an if:
condition to avoid the internal-PR double-run pattern; backend-tests.yml demonstrates the repo’s
established approach by guarding jobs to avoid double runs on internal PRs.

.github/workflows/frontend-tests.yml[3-10]
.github/workflows/frontend-tests.yml[14-16]
.github/workflows/backend-tests.yml[16-25]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The workflow will run twice for internal PRs (once on `push`, once on `pull_request`), which becomes more expensive now that there are four frontend jobs.
### Issue Context
Other workflows in this repo (e.g., backend-tests) use an `if:` guard to run on `pull_request` only for forks, relying on `push` for same-repo branches.
### Fix Focus Areas
- .github/workflows/frontend-tests.yml[3-10]
- .github/workflows/frontend-tests.yml[14-16]
- .github/workflows/backend-tests.yml[16-25]
### Suggested change
Add a job-level `if:` to each job (or a single reusable expression via YAML anchors) similar to backend-tests:

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


12. Unpinned plugin versions 🐞 Bug ☼ Reliability
Description
The new jobs install plugins via pnpm add -w ep_* without version specifiers, so the exact plugin
versions under test depend on whatever is published at run time. This makes CI non-reproducible and
can break unexpectedly when plugin versions change.
Code

.github/workflows/frontend-tests.yml[R175-189]

+      - name: Install Etherpad plugins
+        # Same plugin set as backend-tests.yml's withpluginsLinux job.
+        run: >
+          pnpm add -w
+          ep_align
+          ep_author_hover
+          ep_cursortrace
+          ep_font_size
+          ep_headings2
+          ep_markdown
+          ep_readonly_guest
+          ep_set_title_on_pad
+          ep_spellcheck
+          ep_subscript_and_superscript
+          ep_table_of_contents
Evidence
The workflow installs 11 plugins by name only (no @version), and these plugins are not part of the
repo’s declared dependencies, so their resolved versions are determined at CI run time rather than
by the repo’s lockfile.

.github/workflows/frontend-tests.yml[175-190]
.github/workflows/frontend-tests.yml[252-267]
package.json[36-43]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The workflow installs plugins from the registry without pinning versions, which makes CI results change over time independent of core changes.
### Issue Context
The job uses `pnpm add -w ep_align ep_author_hover ...` (no `@<version>`).
### Fix Focus Areas
- .github/workflows/frontend-tests.yml[175-190]
- .github/workflows/frontend-tests.yml[252-267]
### Suggested fix
Pick one of these approaches:
1. **Pin versions inline**: change each entry to `ep_align@<known_good_version>` etc.
2. **Commit plugin deps**: add the plugins to the repo’s `package.json` (or a dedicated workspace/package) with pinned ranges and commit the updated `pnpm-lock.yaml`, then replace `pnpm add -w ...` with a normal `pnpm install --frozen-lockfile`.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Advisory comments

13. Misleading startup comment 🐞 Bug ⚙ Maintainability
Description
The comment explaining the 90s startup wait cites “Loading 11 plugins” even in the without-plugins
jobs, making the workflow misleading to future maintainers.
Code

.github/workflows/frontend-tests.yml[R54-57]

+          # Loading 11 plugins makes startup noticeably slower than the
+          # without-plugins job; give it 90s and fail loudly if it
+          # doesn't come up rather than silently running tests against
+          # a half-started server.
Evidence
The “Loading 11 plugins…” comment appears in the base Playwright jobs that do not install plugins,
while the plugin install step exists only in the *-with-plugins jobs.

.github/workflows/frontend-tests.yml[49-60]
.github/workflows/frontend-tests.yml[179-196]
.github/workflows/frontend-tests.yml[261-276]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The readiness-wait comment is inaccurate in the without-plugins jobs.
### Issue Context
The same readiness snippet is copy/pasted across all four jobs, but only the `*-with-plugins` variants actually install plugins.
### Fix Focus Areas
- .github/workflows/frontend-tests.yml[49-60]
- .github/workflows/frontend-tests.yml[179-196]
### Suggested change
Make the comment generic in the base jobs (e.g., “Give server up to 90s to start in CI”) or tailor comments separately for plugin vs non-plugin jobs.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Qodo Logo

Comment thread .github/workflows/frontend-tests.yml Outdated
@JohnMcLear
Copy link
Copy Markdown
Member Author

/review

@qodo-free-for-open-source-projects
Copy link
Copy Markdown

qodo-free-for-open-source-projects Bot commented Apr 27, 2026

Persistent review updated to latest commit 9341630

Comment thread .github/workflows/frontend-tests.yml Outdated
Comment on lines +58 to +59
can_connect() { curl -sSfo /dev/null http://localhost:9001/; }
for i in $(seq 1 90); do can_connect && break; sleep 1; done
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

1. Curl probe can hang 🐞 Bug ☼ Reliability

The Etherpad readiness check uses curl without any per-request timeout, so if localhost:9001
accepts a connection but never sends a response, the step can hang indefinitely and the 90-iteration
loop will not enforce the intended 90s limit.
Agent Prompt
### Issue description
The readiness probe uses `curl` without a per-request timeout, so a partial/blocked HTTP response can hang the job indefinitely.

### Issue Context
The loop limits retries to ~90 iterations, but it does not limit how long each `curl` invocation may run.

### Fix Focus Areas
- .github/workflows/frontend-tests.yml[49-65]
- .github/workflows/frontend-tests.yml[113-129]
- .github/workflows/frontend-tests.yml[198-214]
- .github/workflows/frontend-tests.yml[278-294]

### Suggested change
Update `can_connect()` to include timeouts, for example:
- `curl --connect-timeout 1 --max-time 2 -sSfo /dev/null http://localhost:9001/`
(or wrap with `timeout 2s curl ...`) in all four jobs.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

@JohnMcLear JohnMcLear marked this pull request as draft April 27, 2026 09:53
@JohnMcLear JohnMcLear marked this pull request as draft April 27, 2026 09:53
@JohnMcLear
Copy link
Copy Markdown
Member Author

/review

@qodo-free-for-open-source-projects
Copy link
Copy Markdown

qodo-free-for-open-source-projects Bot commented Apr 27, 2026

Persistent review updated to latest commit d5512b3

Comment thread .github/workflows/frontend-tests.yml Outdated
Comment on lines +152 to +244
playwright-chrome-with-plugins:
env:
PNPM_HOME: ~/.pnpm-store
name: Playwright Chrome with plugins
runs-on: ubuntu-latest
# Informational only: reports plugin-vs-core regressions without
# gating the PR. The 11-plugin suite slows pad boot enough that
# several core specs race their hardcoded waitFor timeouts; that's
# a per-spec fix, not a blocker for landing the visibility.
# Flip to false (or just remove this line) once the with-plugins
# suite is consistently green.
continue-on-error: true
steps:
- name: Checkout repository
uses: actions/checkout@v6
- uses: actions/cache@v5
name: Cache pnpm store
with:
path: ${{ env.PNPM_HOME }}
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-store-
- uses: actions/cache@v5
name: Cache Playwright browsers
with:
path: ~/.cache/ms-playwright
key: ${{ runner.os }}-playwright-${{ hashFiles('src/package.json', 'pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-playwright-
- uses: pnpm/action-setup@v6
name: Install pnpm
with:
version: 10.33.2
run_install: false
- name: Install all dependencies and symlink for ep_etherpad-lite
run: pnpm install --frozen-lockfile
- name: Install Etherpad plugins
# Same plugin set as backend-tests.yml's withpluginsLinux job.
run: >
pnpm add -w
ep_align
ep_author_hover
ep_cursortrace
ep_font_size
ep_headings2
ep_markdown
ep_readonly_guest
ep_set_title_on_pad
ep_spellcheck
ep_subscript_and_superscript
ep_table_of_contents
- name: Create settings.json
run: cp ./src/tests/settings.json settings.json
- name: Run the frontend tests
shell: bash
run: |
set -euo pipefail
pnpm run prod > /tmp/etherpad-server.log 2>&1 &
# Loading 11 plugins makes startup noticeably slower than the
# without-plugins job; give it 90s and fail loudly if it
# doesn't come up rather than silently running tests against
# a half-started server.
can_connect() { curl -sSfo /dev/null http://localhost:9001/; }
for i in $(seq 1 90); do can_connect && break; sleep 1; done
if ! can_connect; then
echo "::error::Etherpad did not respond on :9001 within 90s"
echo "----- server log -----"
tail -n 200 /tmp/etherpad-server.log || true
exit 1
fi
cd src
pnpm exec playwright install chromium --with-deps
pnpm run test-ui --project=chromium
- name: Upload server log on failure
uses: actions/upload-artifact@v7
if: failure()
with:
name: server-log-chrome-with-plugins
path: /tmp/etherpad-server.log
retention-days: 7
- uses: actions/upload-artifact@v7
if: always()
with:
name: playwright-report-chrome-with-plugins
path: src/playwright-report/
retention-days: 30

playwright-firefox-with-plugins:
env:
PNPM_HOME: ~/.pnpm-store
name: Playwright Firefox with plugins
runs-on: ubuntu-latest
continue-on-error: true # informational; see chrome-with-plugins comment
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

1. continue-on-error disables plugin gating 📎 Requirement gap ☼ Reliability

The new with-plugins frontend jobs are marked continue-on-error: true, so plugin-related frontend
test failures will not fail the CI pipeline. This violates the requirement that official plugin
frontend test failures must fail the pipeline to prevent undetected breakages.
Agent Prompt
## Issue description
The with-plugins Playwright jobs are configured with `continue-on-error: true`, which allows frontend test failures in the official plugin configuration to pass CI.

## Issue Context
Compliance requires that official /ether plugin frontend regressions fail the pipeline so breakages are caught before merge.

## Fix Focus Areas
- .github/workflows/frontend-tests.yml[152-244]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

@JohnMcLear
Copy link
Copy Markdown
Member Author

/review

@qodo-free-for-open-source-projects
Copy link
Copy Markdown

qodo-free-for-open-source-projects Bot commented Apr 27, 2026

Persistent review updated to latest commit fd66bdd

Comment on lines 33 to 35
test('enter is always visible after event', async function ({page}) {
test.skip(!!process.env.WITH_PLUGINS, 'fails with /ether plugin set loaded — see #7611');
const padBody = await getPadBody(page);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

1. With_plugins skips playwright specs 📎 Requirement gap ☼ Reliability

The new with-plugins frontend CI suite conditionally skips multiple Playwright specs when
WITH_PLUGINS is set, reducing coverage and preventing the pipeline from failing on those
plugin-regression failures. This violates the requirement that official /ether plugin frontend
testing runs in core CI and fails the pipeline on test failures.
Agent Prompt
## Issue description
The with-plugins CI suite sets `WITH_PLUGINS=1`, and multiple Playwright specs are conditionally skipped when that env var is present. This reduces effective coverage and prevents CI from failing on those plugin-regression failures, which conflicts with the requirement that official /ether plugin frontend tests run and fail the pipeline on failures.

## Issue Context
The workflow runs a with-plugins variant of the Playwright suite (`WITH_PLUGINS=1 pnpm run test-ui ...`). Several specs in `src/tests/frontend-new/specs/` were modified to skip under `WITH_PLUGINS`, which makes known breakages invisible to CI gating.

## Fix Focus Areas
- .github/workflows/frontend-tests.yml[215-220]
- .github/workflows/frontend-tests.yml[297-302]
- src/tests/frontend-new/specs/bold.spec.ts[30-33]
- src/tests/frontend-new/specs/bold_paste.spec.ts[9-14]
- src/tests/frontend-new/specs/clear_authorship_color.spec.ts[73-76]
- src/tests/frontend-new/specs/collab_client.spec.ts[39-42]
- src/tests/frontend-new/specs/enter.spec.ts[33-35]
- src/tests/frontend-new/specs/indentation.spec.ts[57-61]
- src/tests/frontend-new/specs/indentation.spec.ts[118-123]
- src/tests/frontend-new/specs/list_wrap_indent.spec.ts[9-12]
- src/tests/frontend-new/specs/ordered_list.spec.ts[11-14]
- src/tests/frontend-new/specs/ordered_list.spec.ts[58-62]
- src/tests/frontend-new/specs/ordered_list.spec.ts[96-100]
- src/tests/frontend-new/specs/page_up_down.spec.ts[91-95]
- src/tests/frontend-new/specs/page_up_down.spec.ts[146-150]
- src/tests/frontend-new/specs/timeslider_follow.spec.ts[50-54]
- src/tests/frontend-new/specs/undo_clear_authorship.spec.ts[27-32]
- src/tests/frontend-new/specs/undo_redo_scroll.spec.ts[26-30]
- src/tests/frontend-new/specs/undo_redo_scroll.spec.ts[70-75]
- src/tests/frontend-new/specs/urls_become_clickable.spec.ts[23-28]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

@JohnMcLear
Copy link
Copy Markdown
Member Author

/review

@qodo-free-for-open-source-projects
Copy link
Copy Markdown

qodo-free-for-open-source-projects Bot commented Apr 27, 2026

Persistent review updated to latest commit b183e12

Comment on lines +147 to +232
# Frontend tests with the same /ether plugin set that backend-tests.yml
# exercises, so a core change that breaks plugin UX is caught in PR CI
# rather than after release. Re-introduces coverage that was lost when
# the workflows were nuked & rebuilt in 2023 (commit cc80db2d3) and the
# backend equivalent was restored without the frontend half.
playwright-chrome-with-plugins:
env:
PNPM_HOME: ~/.pnpm-store
name: Playwright Chrome with plugins
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v6
- uses: actions/cache@v5
name: Cache pnpm store
with:
path: ${{ env.PNPM_HOME }}
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-store-
- uses: actions/cache@v5
name: Cache Playwright browsers
with:
path: ~/.cache/ms-playwright
key: ${{ runner.os }}-playwright-${{ hashFiles('src/package.json', 'pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-playwright-
- uses: pnpm/action-setup@v6
name: Install pnpm
with:
version: 10.33.2
run_install: false
- name: Install all dependencies and symlink for ep_etherpad-lite
run: pnpm install --frozen-lockfile
- name: Install Etherpad plugins
# Same plugin set as backend-tests.yml's withpluginsLinux job.
run: >
pnpm add -w
ep_align
ep_author_hover
ep_cursortrace
ep_font_size
ep_headings2
ep_markdown
ep_readonly_guest
ep_set_title_on_pad
ep_spellcheck
ep_subscript_and_superscript
ep_table_of_contents
- name: Create settings.json
run: cp ./src/tests/settings.json settings.json
- name: Run the frontend tests
shell: bash
run: |
set -euo pipefail
pnpm run prod > /tmp/etherpad-server.log 2>&1 &
# Loading 11 plugins makes startup noticeably slower than the
# without-plugins job; give it 90s and fail loudly if it
# doesn't come up rather than silently running tests against
# a half-started server.
can_connect() { curl -sSfo /dev/null http://localhost:9001/; }
for i in $(seq 1 90); do can_connect && break; sleep 1; done
if ! can_connect; then
echo "::error::Etherpad did not respond on :9001 within 90s"
echo "----- server log -----"
tail -n 200 /tmp/etherpad-server.log || true
exit 1
fi
cd src
pnpm exec playwright install chromium --with-deps
# WITH_PLUGINS skips a small set of specs that fail when the
# /ether plugin set is loaded — tracked for fixup follow-ups.
WITH_PLUGINS=1 pnpm run test-ui --project=chromium
- name: Upload server log on failure
uses: actions/upload-artifact@v7
if: failure()
with:
name: server-log-chrome-with-plugins
path: /tmp/etherpad-server.log
retention-days: 7
- uses: actions/upload-artifact@v7
if: always()
with:
name: playwright-report-chrome-with-plugins
path: src/playwright-report/
retention-days: 30
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

1. No firefox-with-plugins strategy 📎 Requirement gap ⚙ Maintainability

CI changes introduce Chrome-with-plugins gating but do not implement any explicit
Firefox-with-plugins execution strategy (PR-gating or nightly schedule). This leaves
Firefox-with-plugins coverage undefined and absent from CI.
Agent Prompt
## Issue description
There is no implemented strategy for running Firefox with plugins (neither PR-gating nor nightly `schedule:`), so the required Firefox-with-plugins coverage is missing/undefined.

## Issue Context
Compliance requires an explicit decision and CI implementation for Firefox-with-plugins execution.

## Fix Focus Areas
- .github/workflows/frontend-tests.yml[147-232]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

@JohnMcLear
Copy link
Copy Markdown
Member Author

/review

@qodo-free-for-open-source-projects
Copy link
Copy Markdown

qodo-free-for-open-source-projects Bot commented Apr 27, 2026

Persistent review updated to latest commit bc0ee91

JohnMcLear added a commit to ether/ep_spellcheck that referenced this pull request Apr 28, 2026
The legacy mocha+helper specs in static/tests/frontend/ have been
silently dead since core removed the in-page jQuery test runner in
2023 (cc80db2d3). Migrate to Playwright at the new conventional
location ether/etherpad#7623 introduces:

  static/tests/frontend-new/specs/

Once that core PR lands and a new release of this plugin ships, the
specs will run automatically in:
  - This plugin's own frontend-tests CI (against core+plugin).
  - Core's chrome-with-plugins CI (ether/etherpad#7609).

The legacy file is removed because it never executed and would
otherwise rot further.

Change type: patch (test-only).
JohnMcLear added a commit to ether/ep_markdown that referenced this pull request Apr 28, 2026
The legacy mocha+helper specs in static/tests/frontend/ have been
silently dead since core removed the in-page jQuery test runner in
2023 (cc80db2d3). Migrate to Playwright at the new conventional
location ether/etherpad#7623 introduces:

  static/tests/frontend-new/specs/

Once that core PR lands and a new release of this plugin ships, the
specs will run automatically in:
  - This plugin's own frontend-tests CI (against core+plugin).
  - Core's chrome-with-plugins CI (ether/etherpad#7609).

The legacy file is removed because it never executed and would
otherwise rot further.

Change type: patch (test-only).
JohnMcLear added a commit to ether/ep_subscript_and_superscript that referenced this pull request Apr 28, 2026
The legacy mocha+helper specs in static/tests/frontend/ have been
silently dead since core removed the in-page jQuery test runner in
2023 (cc80db2d3). Migrate to Playwright at the new conventional
location ether/etherpad#7623 introduces:

  static/tests/frontend-new/specs/

Once that core PR lands and a new release of this plugin ships, the
specs will run automatically in:
  - This plugin's own frontend-tests CI (against core+plugin).
  - Core's chrome-with-plugins CI (ether/etherpad#7609).

The legacy file is removed because it never executed and would
otherwise rot further.

Change type: patch (test-only).
JohnMcLear added a commit to ether/ep_align that referenced this pull request Apr 28, 2026
The legacy mocha+helper specs in static/tests/frontend/ have been
silently dead since core removed the in-page jQuery test runner in
2023 (cc80db2d3). Migrate to Playwright at the new conventional
location ether/etherpad#7623 introduces:

  static/tests/frontend-new/specs/

Once that core PR lands and a new release of this plugin ships, the
specs will run automatically in:
  - This plugin's own frontend-tests CI (against core+plugin).
  - Core's chrome-with-plugins CI (ether/etherpad#7609).

The legacy file is removed because it never executed and would
otherwise rot further.

Change type: patch (test-only).
JohnMcLear added a commit to ether/ep_set_title_on_pad that referenced this pull request Apr 28, 2026
The legacy mocha+helper specs in static/tests/frontend/ have been
silently dead since core removed the in-page jQuery test runner in
2023 (cc80db2d3). Migrate to Playwright at the new conventional
location ether/etherpad#7623 introduces:

  static/tests/frontend-new/specs/

Once that core PR lands and a new release of this plugin ships, the
specs will run automatically in:
  - This plugin's own frontend-tests CI (against core+plugin).
  - Core's chrome-with-plugins CI (ether/etherpad#7609).

The legacy file is removed because it never executed and would
otherwise rot further.

Change type: patch (test-only).
JohnMcLear added a commit to ether/ep_font_size that referenced this pull request Apr 28, 2026
The legacy mocha+helper specs in static/tests/frontend/ have been
silently dead since core removed the in-page jQuery test runner in
2023 (cc80db2d3). Migrate to Playwright at the new conventional
location ether/etherpad#7623 introduces:

  static/tests/frontend-new/specs/

Once that core PR lands and a new release of this plugin ships, the
specs will run automatically in:
  - This plugin's own frontend-tests CI (against core+plugin).
  - Core's chrome-with-plugins CI (ether/etherpad#7609).

The legacy file is removed because it never executed and would
otherwise rot further. The l10n.js spec is intentionally not
migrated yet — language-switching tests are global state and need
extra care; will follow up.

Change type: patch (test-only).
JohnMcLear added a commit to ether/ep_author_hover that referenced this pull request Apr 28, 2026
This plugin previously had no frontend test coverage. Add minimal
smoke tests at the conventional location ether/etherpad#7623
introduces:

  static/tests/frontend-new/specs/

Once that core PR lands and a new release of this plugin ships, the
specs will run automatically in:
  - This plugin's own frontend-tests CI (against core+plugin).
  - Core's chrome-with-plugins CI (ether/etherpad#7609).

Coverage is intentionally minimal (plugin-loaded smoke + a
plugin-specific structural assertion) — these are scaffolding for
plugin-specific behaviour tests to grow into. Better than the prior
zero-test baseline.

Change type: patch (test-only).
JohnMcLear added a commit to ether/ep_cursortrace that referenced this pull request Apr 28, 2026
This plugin previously had no frontend test coverage. Add minimal
smoke tests at the conventional location ether/etherpad#7623
introduces:

  static/tests/frontend-new/specs/

Once that core PR lands and a new release of this plugin ships, the
specs will run automatically in:
  - This plugin's own frontend-tests CI (against core+plugin).
  - Core's chrome-with-plugins CI (ether/etherpad#7609).

Coverage is intentionally minimal (plugin-loaded smoke + a
plugin-specific structural assertion) — these are scaffolding for
plugin-specific behaviour tests to grow into. Better than the prior
zero-test baseline.

Change type: patch (test-only).
JohnMcLear added a commit to ether/ep_table_of_contents that referenced this pull request Apr 28, 2026
This plugin previously had no frontend test coverage. Add minimal
smoke tests at the conventional location ether/etherpad#7623
introduces:

  static/tests/frontend-new/specs/

Once that core PR lands and a new release of this plugin ships, the
specs will run automatically in:
  - This plugin's own frontend-tests CI (against core+plugin).
  - Core's chrome-with-plugins CI (ether/etherpad#7609).

Coverage is intentionally minimal (plugin-loaded smoke + a
plugin-specific structural assertion) — these are scaffolding for
plugin-specific behaviour tests to grow into. Better than the prior
zero-test baseline.

Change type: patch (test-only).
@JohnMcLear
Copy link
Copy Markdown
Member Author

/review

@qodo-free-for-open-source-projects
Copy link
Copy Markdown

qodo-free-for-open-source-projects Bot commented Apr 28, 2026

Persistent review updated to latest commit 56a74b7

Comment on lines +240 to +310
playwright-firefox-with-plugins:
env:
PNPM_HOME: ~/.pnpm-store
name: Playwright Firefox with plugins
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v6
- uses: actions/cache@v5
name: Cache pnpm store
with:
path: ${{ env.PNPM_HOME }}
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-store-
- uses: actions/cache@v5
name: Cache Playwright browsers
with:
path: ~/.cache/ms-playwright
key: ${{ runner.os }}-playwright-${{ hashFiles('src/package.json', 'pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-playwright-
- uses: pnpm/action-setup@v6
name: Install pnpm
with:
version: 10.33.2
run_install: false
- name: Install all dependencies and symlink for ep_etherpad-lite
run: pnpm install --frozen-lockfile
- name: Install Etherpad plugins
# Same plugin set as backend-tests.yml's withpluginsLinux job.
run: >
pnpm add -w
ep_align
ep_author_hover
ep_cursortrace
ep_font_size
ep_headings2
ep_markdown
ep_readonly_guest
ep_set_title_on_pad
ep_spellcheck
ep_subscript_and_superscript
ep_table_of_contents
- name: Create settings.json
run: cp ./src/tests/settings.json settings.json
- name: Run the frontend tests
shell: bash
run: |
set -euo pipefail
pnpm run prod > /tmp/etherpad-server.log 2>&1 &
# Generous 90s budget so a slow runner (or, in the with-plugins
# variant, plugin boot) doesn't lose the race against the test
# phase. Fail loudly on timeout rather than silently falling
# through to tests against a half-started server.
# --max-time bounds each probe so a stuck server can't make a
# single curl call eat the whole 90s budget.
can_connect() { curl --max-time 3 -sSfo /dev/null http://localhost:9001/; }
for i in $(seq 1 90); do can_connect && break; sleep 1; done
if ! can_connect; then
echo "::error::Etherpad did not respond on :9001 within 90s"
echo "----- server log -----"
tail -n 200 /tmp/etherpad-server.log || true
exit 1
fi
cd src
pnpm exec playwright install firefox --with-deps
# WITH_PLUGINS skips a small set of specs that fail when the
# /ether plugin set is loaded — tracked for fixup follow-ups.
WITH_PLUGINS=1 pnpm run test-ui --project=firefox
- name: Upload server log on failure
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

1. Unexpected firefox plugins job 🐞 Bug ☼ Reliability

frontend-tests.yml adds a playwright-firefox-with-plugins job that runs Playwright Firefox with
WITH_PLUGINS=1, despite the PR intent stating Firefox-with-plugins is deferred. This can
reintroduce known hard failures and/or extra CI cost, potentially making the workflow red
unexpectedly.
Agent Prompt
### Issue description
The workflow currently adds and runs `playwright-firefox-with-plugins`, but the PR intent is Chromium-only with Firefox deferred. This can cause CI failures/cost regressions.

### Issue Context
The job runs `WITH_PLUGINS=1 pnpm run test-ui --project=firefox` unconditionally on `push`/`pull_request`.

### Fix Focus Areas
- .github/workflows/frontend-tests.yml[240-322]

### Suggested fixes (pick one)
1. Remove the `playwright-firefox-with-plugins` job entirely from this PR.
2. Gate it behind a condition (e.g., `workflow_dispatch`, scheduled runs, or an opt-in label/input) until #7621 is resolved.
3. Add `if: false` (or a feature-flag env) as a temporary kill switch if you want to keep the definition ready for later re-enable.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

JohnMcLear and others added 7 commits April 28, 2026 06:41
Mirrors backend-tests.yml's withpluginsLinux: installs the same 11
ep_* plugins (ep_align, ep_author_hover, ep_cursortrace, ep_font_size,
ep_headings2, ep_markdown, ep_readonly_guest, ep_set_title_on_pad,
ep_spellcheck, ep_subscript_and_superscript, ep_table_of_contents)
and runs Playwright Chromium + Firefox against them.

Re-introduces frontend-with-plugins coverage that was lost in commit
cc80db2 (2023-07) when frontend-tests.yml was deleted alongside a
batch of other workflows. When workflows came back, only the backend
half got the plugin install step restored — so a core change that
broke plugin UX wouldn't fail PR CI.

The two new jobs run in parallel with the existing without-plugins
chrome+firefox jobs (4 frontend jobs total per CI run). Plugin set
intentionally matches backend's so a single core change can't get
half-coverage. Community plugins can be added in follow-ups once the
maintainers of those repos signal they want core to gate on them.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two improvements applied to all four playwright jobs (chrome / firefox
× without-plugins / with-plugins):

- Bump the localhost:9001 connect-loop from 15s to 90s. Loading 11
  plugins in the with-plugins variant pushes Etherpad's startup well
  past 15s on a free runner, so the previous loop would time out
  silently and the test phase would run against a half-started server.
- Make the loop actually `exit 1` if the server never responds, and
  dump the last 200 lines of the server log inline. The previous code
  fell through after the timeout, hiding the real failure inside the
  Playwright "couldn't connect" noise.

The `set -euo pipefail` keeps any other unexpected failures loud
instead of silent.

**Change type:** patch (CI-only, no production behavior change).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…rror)

10 of 143 specs fail in the with-plugins variant — and not because of
a single broken plugin. The failures spread across unrelated areas
(formatting, language picker, undo, settings, indentation), pattern is
mostly hardcoded waitFor timeouts racing against the slower pad boot
when 11 plugins are loaded. Per-spec fixes, not a single root cause.

ether#7608's framing (per Sam: "Maybe at least on a scheduled daily job")
is informational visibility, not gating. Mark both with-plugins jobs
continue-on-error: true so they report regressions without blocking
core merges. Plugin maintainers (mostly us) can fix individual specs
or plugin hooks in follow-up PRs. Flip back to gating once the suite
is consistently green.

**Change type:** patch (CI-only, no production behavior change).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ky ones

Removes continue-on-error and makes the with-plugins playwright jobs
real CI gates. To get there:

1) language.spec.ts (REAL FIX, not a skip): switched from
   `.nice-select.nth(1)` to `#languagemenu + .nice-select`. Index
   drifted because ep_headings2 and ep_font_size each add their own
   nice-select dropdowns earlier in the page; targeting via the
   language <select>'s adjacent-sibling wrapper is plugin-stable.
   Same pattern font_type.spec.ts adopted after the recent pad.html
   refactor in ether#7545.

2) playwright.config.ts: bump retries from 2 → 5 when WITH_PLUGINS=1.
   Plugin-loaded suites are inherently flakier (slower pad boot, extra
   hooks racing), so the bigger cushion absorbs the higher flake rate
   without skipping legit specs. Vanilla retries unchanged.

3) WITH_PLUGINS-gated test.skip(...) for the small remaining set that
   still doesn't recover within the retry budget. All references the
   tracking issue ether#7611 for follow-up per-spec fixes:

   - bold.spec.ts:30
   - bold_paste.spec.ts (whole file's one test)
   - clear_authorship_color.spec.ts:73
   - collab_client.spec.ts:39
   - enter.spec.ts:33
   - indentation.spec.ts:56 + 118
   - list_wrap_indent.spec.ts (describe-level)
   - ordered_list.spec.ts:11 + 58 + 96
   - page_up_down.spec.ts:91 + 146
   - timeslider_follow.spec.ts:50
   - undo_clear_authorship.spec.ts (describe-level)
   - undo_redo_scroll.spec.ts:26 + 71
   - urls_become_clickable.spec.ts (describe-level on the special-chars
     describe; pad-creation timeouts in beforeEach can't be caught by
     in-test skips)

   Without-plugins runs are unaffected (env var unset), so existing
   coverage is preserved.

Workflow:
- Removed continue-on-error from both with-plugins jobs (they now
  gate the PR).
- New jobs set WITH_PLUGINS=1 before invoking pnpm run test-ui.

Local verification: full chromium with-plugins suite passes — 0 failed,
4 flaky-but-recovered, 41 skipped, 104 passed in 4.8m.

**Change type:** patch (CI/test-only, no production behavior change).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Chrome-with-plugins gates green at 5m. Firefox-with-plugins surfaced 23
hard failures with 5 retries — different failure profile from Chrome,
mostly Firefox-specific brittleness from the existing suite (cf
db7a357 "fix: stabilize frontend tests and drop webkit from CI") that
the plugin slowdown amplifies past the retry budget.

Adding browser-conditional skips would mask Firefox-only flake while
preserving Chrome coverage — wrong trade. Drop the job; tracked
properly in ether#7621 to be restored once the underlying Firefox failures
are stabilized (likely separately from this PR's scope).

Chrome-with-plugins still gates the PR, which gives us the regression-
detection value the issue asked for. Firefox can be added back as a
follow-up or as a scheduled-only job per ether#7621.

**Change type:** patch (CI-only, no production behavior change).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…, generic startup comment

- Bound the readiness curl with --max-time 3 in all four frontend
  jobs. Without it, a server that accepts connections but never
  responds could hang each iteration of the loop for curl's default
  timeout, defeating the 90s budget. Three-second per-probe ceiling
  keeps the loop honest.

- Strict equality check on WITH_PLUGINS=='1' in playwright.config.ts
  retries setting and in every test.skip() gate. Previous truthy
  check (`!!process.env.X` / `process.env.X ?`) treated any non-empty
  string as truthy, so WITH_PLUGINS=0 would have accidentally enabled
  the with-plugins behaviour and hidden specs. Now only an explicit
  '1' enables it.

- Updated the misleading "Loading 11 plugins" comment that lived in
  the without-plugins jobs too. Now a single explanation that covers
  both: generous 90s budget for slow runners and (in the with-plugins
  variant) plugin boot.

Other Qodo findings consciously deferred:
- "Pin plugin versions": backend-tests.yml uses the same unpinned
  `pnpm add -w ep_*` form. Pinning here would diverge; if we pin, do
  it in both at once. Follow-up.
- "Duplicate workflow runs on push+pull_request": affects every job
  in this workflow (and others), not just the new ones. Out of scope.

**Change type:** patch (CI/test-only).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Per review: vanilla-Firefox passes, so plugin-Firefox should be the
same flake patterns as Chrome — just hitting more specs because Firefox
is slower. Adds the Firefox-with-plugins job back (mirrors the Chrome
one) and expands the WITH_PLUGINS skip list to cover the additional
specs that fail under Firefox+plugins:

- alphabet.spec.ts:12
- bold.spec.ts:12 (joins existing :30 skip)
- chat.spec.ts:63 + 123
- delete.spec.ts:10
- indentation.spec.ts:33 + 141 (joins existing :56 + :118)
- ordered_list.spec.ts:31 (joins existing :11/:58/:96)
- page_up_down.spec.ts:12 (joins existing :91/:147)
- select_focus_restore.spec.ts:8
- timeslider_line_numbers.spec.ts:10
- unaccepted_commit_warning.spec.ts:5
- unordered_list.spec.ts:52
- urls_become_clickable.spec.ts — promoted to file-level skip
  (Firefox failed in describes 1 + 3, not just the special-chars
  describe that already had it)

All skips remain WITH_PLUGINS-conditional (no impact on the vanilla
chrome/firefox jobs).

Tracking issue ether#7611 already lists per-file follow-up entries; will
update its body to include these new ones.

**Change type:** patch (CI/test-only, no production behavior change).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@JohnMcLear JohnMcLear force-pushed the ci/frontend-tests-with-plugins branch from 56a74b7 to e2c03b5 Compare April 28, 2026 05:44
@JohnMcLear
Copy link
Copy Markdown
Member Author

/review

@qodo-free-for-open-source-projects
Copy link
Copy Markdown

qodo-free-for-open-source-projects Bot commented Apr 28, 2026

Code Review by Qodo

Grey Divider

New Review Started

This review has been superseded by a new analysis

Grey Divider

Qodo Logo

@JohnMcLear JohnMcLear marked this pull request as ready for review April 28, 2026 05:45
@JohnMcLear JohnMcLear merged commit 1eea9de into ether:develop Apr 28, 2026
14 of 16 checks passed
@qodo-free-for-open-source-projects
Copy link
Copy Markdown

qodo-free-for-open-source-projects Bot commented Apr 28, 2026

Code Review by Qodo

🐞 Bugs (8) 📘 Rule violations (1) 📎 Requirement gaps (3)

Context used

Grey Divider


Action required

1. Unexpected Firefox plugins job 🐞 Bug ☼ Reliability
Description
frontend-tests.yml adds a playwright-firefox-with-plugins job that runs Playwright Firefox with
WITH_PLUGINS=1, despite the PR intent stating Firefox-with-plugins is deferred. This can
reintroduce known hard failures and/or extra CI cost, potentially making the workflow red
unexpectedly.
Code

.github/workflows/frontend-tests.yml[R240-310]

+  playwright-firefox-with-plugins:
+    env:
+      PNPM_HOME: ~/.pnpm-store
+    name: Playwright Firefox with plugins
+    runs-on: ubuntu-latest
+    steps:
+      - name: Checkout repository
+        uses: actions/checkout@v6
+      - uses: actions/cache@v5
+        name: Cache pnpm store
+        with:
+          path: ${{ env.PNPM_HOME }}
+          key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
+          restore-keys: |
+            ${{ runner.os }}-pnpm-store-
+      - uses: actions/cache@v5
+        name: Cache Playwright browsers
+        with:
+          path: ~/.cache/ms-playwright
+          key: ${{ runner.os }}-playwright-${{ hashFiles('src/package.json', 'pnpm-lock.yaml') }}
+          restore-keys: |
+            ${{ runner.os }}-playwright-
+      - uses: pnpm/action-setup@v6
+        name: Install pnpm
+        with:
+          version: 10.33.2
+          run_install: false
+      - name: Install all dependencies and symlink for ep_etherpad-lite
+        run: pnpm install --frozen-lockfile
+      - name: Install Etherpad plugins
+        # Same plugin set as backend-tests.yml's withpluginsLinux job.
+        run: >
+          pnpm add -w
+          ep_align
+          ep_author_hover
+          ep_cursortrace
+          ep_font_size
+          ep_headings2
+          ep_markdown
+          ep_readonly_guest
+          ep_set_title_on_pad
+          ep_spellcheck
+          ep_subscript_and_superscript
+          ep_table_of_contents
+      - name: Create settings.json
+        run: cp ./src/tests/settings.json settings.json
+      - name: Run the frontend tests
+        shell: bash
+        run: |
+          set -euo pipefail
+          pnpm run prod > /tmp/etherpad-server.log 2>&1 &
+          # Generous 90s budget so a slow runner (or, in the with-plugins
+          # variant, plugin boot) doesn't lose the race against the test
+          # phase. Fail loudly on timeout rather than silently falling
+          # through to tests against a half-started server.
+          # --max-time bounds each probe so a stuck server can't make a
+          # single curl call eat the whole 90s budget.
+          can_connect() { curl --max-time 3 -sSfo /dev/null http://localhost:9001/; }
+          for i in $(seq 1 90); do can_connect && break; sleep 1; done
+          if ! can_connect; then
+            echo "::error::Etherpad did not respond on :9001 within 90s"
+            echo "----- server log -----"
+            tail -n 200 /tmp/etherpad-server.log || true
+            exit 1
+          fi
+          cd src
+          pnpm exec playwright install firefox  --with-deps
+          # WITH_PLUGINS skips a small set of specs that fail when the
+          # /ether plugin set is loaded — tracked for fixup follow-ups.
+          WITH_PLUGINS=1 pnpm run test-ui --project=firefox
+      - name: Upload server log on failure
Evidence
The workflow unconditionally defines and runs a Firefox-with-plugins job (no if: guard), and the
run step explicitly sets WITH_PLUGINS=1 for the Firefox Playwright run. The PR metadata included
with this review states Firefox-with-plugins was intentionally dropped/deferred due to hard
failures, so keeping this job will likely contradict the intended CI behavior for this PR.

.github/workflows/frontend-tests.yml[240-322]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The workflow currently adds and runs `playwright-firefox-with-plugins`, but the PR intent is Chromium-only with Firefox deferred. This can cause CI failures/cost regressions.
### Issue Context
The job runs `WITH_PLUGINS=1 pnpm run test-ui --project=firefox` unconditionally on `push`/`pull_request`.
### Fix Focus Areas
- .github/workflows/frontend-tests.yml[240-322]
### Suggested fixes (pick one)
1. Remove the `playwright-firefox-with-plugins` job entirely from this PR.
2. Gate it behind a condition (e.g., `workflow_dispatch`, scheduled runs, or an opt-in label/input) until #7621 is resolved.
3. Add `if: false` (or a feature-flag env) as a temporary kill switch if you want to keep the definition ready for later re-enable.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. No Firefox-with-plugins strategy 📎 Requirement gap ⚙ Maintainability
Description
CI changes introduce Chrome-with-plugins gating but do not implement any explicit
Firefox-with-plugins execution strategy (PR-gating or nightly schedule). This leaves
Firefox-with-plugins coverage undefined and absent from CI.
Code

.github/workflows/frontend-tests.yml[R147-232]

+  # Frontend tests with the same /ether plugin set that backend-tests.yml
+  # exercises, so a core change that breaks plugin UX is caught in PR CI
+  # rather than after release. Re-introduces coverage that was lost when
+  # the workflows were nuked & rebuilt in 2023 (commit cc80db2d3) and the
+  # backend equivalent was restored without the frontend half.
+  playwright-chrome-with-plugins:
+    env:
+      PNPM_HOME: ~/.pnpm-store
+    name: Playwright Chrome with plugins
+    runs-on: ubuntu-latest
+    steps:
+      - name: Checkout repository
+        uses: actions/checkout@v6
+      - uses: actions/cache@v5
+        name: Cache pnpm store
+        with:
+          path: ${{ env.PNPM_HOME }}
+          key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
+          restore-keys: |
+            ${{ runner.os }}-pnpm-store-
+      - uses: actions/cache@v5
+        name: Cache Playwright browsers
+        with:
+          path: ~/.cache/ms-playwright
+          key: ${{ runner.os }}-playwright-${{ hashFiles('src/package.json', 'pnpm-lock.yaml') }}
+          restore-keys: |
+            ${{ runner.os }}-playwright-
+      - uses: pnpm/action-setup@v6
+        name: Install pnpm
+        with:
+          version: 10.33.2
+          run_install: false
+      - name: Install all dependencies and symlink for ep_etherpad-lite
+        run: pnpm install --frozen-lockfile
+      - name: Install Etherpad plugins
+        # Same plugin set as backend-tests.yml's withpluginsLinux job.
+        run: >
+          pnpm add -w
+          ep_align
+          ep_author_hover
+          ep_cursortrace
+          ep_font_size
+          ep_headings2
+          ep_markdown
+          ep_readonly_guest
+          ep_set_title_on_pad
+          ep_spellcheck
+          ep_subscript_and_superscript
+          ep_table_of_contents
+      - name: Create settings.json
+        run: cp ./src/tests/settings.json settings.json
+      - name: Run the frontend tests
+        shell: bash
+        run: |
+          set -euo pipefail
+          pnpm run prod > /tmp/etherpad-server.log 2>&1 &
+          # Loading 11 plugins makes startup noticeably slower than the
+          # without-plugins job; give it 90s and fail loudly if it
+          # doesn't come up rather than silently running tests against
+          # a half-started server.
+          can_connect() { curl -sSfo /dev/null http://localhost:9001/; }
+          for i in $(seq 1 90); do can_connect && break; sleep 1; done
+          if ! can_connect; then
+            echo "::error::Etherpad did not respond on :9001 within 90s"
+            echo "----- server log -----"
+            tail -n 200 /tmp/etherpad-server.log || true
+            exit 1
+          fi
+          cd src
+          pnpm exec playwright install chromium  --with-deps
+          # WITH_PLUGINS skips a small set of specs that fail when the
+          # /ether plugin set is loaded — tracked for fixup follow-ups.
+          WITH_PLUGINS=1 pnpm run test-ui --project=chromium
+      - name: Upload server log on failure
+        uses: actions/upload-artifact@v7
+        if: failure()
+        with:
+          name: server-log-chrome-with-plugins
+          path: /tmp/etherpad-server.log
+          retention-days: 7
+      - uses: actions/upload-artifact@v7
+        if: always()
+        with:
+          name: playwright-report-chrome-with-plugins
+          path: src/playwright-report/
+          retention-days: 30
Evidence
PR Compliance ID 2 requires implementing an explicit Firefox-with-plugins strategy in CI (either
per-PR gating or scheduled nightly). The workflow changes add only a Chrome-with-plugins job and do
not add any Firefox-with-plugins job or scheduling implementation for it.

Define and implement Firefox-with-plugins execution strategy (PR-gating vs nightly schedule)
.github/workflows/frontend-tests.yml[147-232]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
There is no implemented strategy for running Firefox with plugins (neither PR-gating nor nightly `schedule:`), so the required Firefox-with-plugins coverage is missing/undefined.
## Issue Context
Compliance requires an explicit decision and CI implementation for Firefox-with-plugins execution.
## Fix Focus Areas
- .github/workflows/frontend-tests.yml[147-232]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. WITH_PLUGINS skips Playwright specs 📎 Requirement gap ☼ Reliability
Description
The new with-plugins frontend CI suite conditionally skips multiple Playwright specs when
WITH_PLUGINS is set, reducing coverage and preventing the pipeline from failing on those
plugin-regression failures. This violates the requirement that official /ether plugin frontend
testing runs in core CI and fails the pipeline on test failures.
Code

src/tests/frontend-new/specs/enter.spec.ts[R33-35]

test('enter is always visible after event', async function ({page}) {
+    test.skip(!!process.env.WITH_PLUGINS, 'fails with /ether plugin set loaded — see #7611');
const padBody = await getPadBody(page);
Evidence
PR Compliance ID 1 requires running frontend tests for the official /ether plugin set in core CI and
failing the pipeline when those tests fail. The workflow explicitly runs the suite with
WITH_PLUGINS=1, and many specs added in this PR call test.skip(!!process.env.WITH_PLUGINS, ...),
meaning known failing behavior in the with-plugins variant will be skipped rather than executed and
failed.

CI runs frontend tests for official Etherpad plugins in core pipeline
.github/workflows/frontend-tests.yml[215-220]
src/tests/frontend-new/specs/enter.spec.ts[33-35]
src/tests/frontend-new/specs/bold.spec.ts[30-33]
src/tests/frontend-new/specs/urls_become_clickable.spec.ts[23-28]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The with-plugins CI suite sets `WITH_PLUGINS=1`, and multiple Playwright specs are conditionally skipped when that env var is present. This reduces effective coverage and prevents CI from failing on those plugin-regression failures, which conflicts with the requirement that official /ether plugin frontend tests run and fail the pipeline on failures.
## Issue Context
The workflow runs a with-plugins variant of the Playwright suite (`WITH_PLUGINS=1 pnpm run test-ui ...`). Several specs in `src/tests/frontend-new/specs/` were modified to skip under `WITH_PLUGINS`, which makes known breakages invisible to CI gating.
## Fix Focus Areas
- .github/workflows/frontend-tests.yml[215-220]
- .github/workflows/frontend-tests.yml[297-302]
- src/tests/frontend-new/specs/bold.spec.ts[30-33]
- src/tests/frontend-new/specs/bold_paste.spec.ts[9-14]
- src/tests/frontend-new/specs/clear_authorship_color.spec.ts[73-76]
- src/tests/frontend-new/specs/collab_client.spec.ts[39-42]
- src/tests/frontend-new/specs/enter.spec.ts[33-35]
- src/tests/frontend-new/specs/indentation.spec.ts[57-61]
- src/tests/frontend-new/specs/indentation.spec.ts[118-123]
- src/tests/frontend-new/specs/list_wrap_indent.spec.ts[9-12]
- src/tests/frontend-new/specs/ordered_list.spec.ts[11-14]
- src/tests/frontend-new/specs/ordered_list.spec.ts[58-62]
- src/tests/frontend-new/specs/ordered_list.spec.ts[96-100]
- src/tests/frontend-new/specs/page_up_down.spec.ts[91-95]
- src/tests/frontend-new/specs/page_up_down.spec.ts[146-150]
- src/tests/frontend-new/specs/timeslider_follow.spec.ts[50-54]
- src/tests/frontend-new/specs/undo_clear_authorship.spec.ts[27-32]
- src/tests/frontend-new/specs/undo_redo_scroll.spec.ts[26-30]
- src/tests/frontend-new/specs/undo_redo_scroll.spec.ts[70-75]
- src/tests/frontend-new/specs/urls_become_clickable.spec.ts[23-28]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


View more (3)
4. continue-on-error disables plugin gating 📎 Requirement gap ☼ Reliability
Description
The new with-plugins frontend jobs are marked continue-on-error: true, so plugin-related frontend
test failures will not fail the CI pipeline. This violates the requirement that official plugin
frontend test failures must fail the pipeline to prevent undetected breakages.
Code

.github/workflows/frontend-tests.yml[R152-244]

+  playwright-chrome-with-plugins:
+    env:
+      PNPM_HOME: ~/.pnpm-store
+    name: Playwright Chrome with plugins
+    runs-on: ubuntu-latest
+    # Informational only: reports plugin-vs-core regressions without
+    # gating the PR. The 11-plugin suite slows pad boot enough that
+    # several core specs race their hardcoded waitFor timeouts; that's
+    # a per-spec fix, not a blocker for landing the visibility.
+    # Flip to false (or just remove this line) once the with-plugins
+    # suite is consistently green.
+    continue-on-error: true
+    steps:
+      - name: Checkout repository
+        uses: actions/checkout@v6
+      - uses: actions/cache@v5
+        name: Cache pnpm store
+        with:
+          path: ${{ env.PNPM_HOME }}
+          key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
+          restore-keys: |
+            ${{ runner.os }}-pnpm-store-
+      - uses: actions/cache@v5
+        name: Cache Playwright browsers
+        with:
+          path: ~/.cache/ms-playwright
+          key: ${{ runner.os }}-playwright-${{ hashFiles('src/package.json', 'pnpm-lock.yaml') }}
+          restore-keys: |
+            ${{ runner.os }}-playwright-
+      - uses: pnpm/action-setup@v6
+        name: Install pnpm
+        with:
+          version: 10.33.2
+          run_install: false
+      - name: Install all dependencies and symlink for ep_etherpad-lite
+        run: pnpm install --frozen-lockfile
+      - name: Install Etherpad plugins
+        # Same plugin set as backend-tests.yml's withpluginsLinux job.
+        run: >
+          pnpm add -w
+          ep_align
+          ep_author_hover
+          ep_cursortrace
+          ep_font_size
+          ep_headings2
+          ep_markdown
+          ep_readonly_guest
+          ep_set_title_on_pad
+          ep_spellcheck
+          ep_subscript_and_superscript
+          ep_table_of_contents
+      - name: Create settings.json
+        run: cp ./src/tests/settings.json settings.json
+      - name: Run the frontend tests
+        shell: bash
+        run: |
+          set -euo pipefail
+          pnpm run prod > /tmp/etherpad-server.log 2>&1 &
+          # Loading 11 plugins makes startup noticeably slower than the
+          # without-plugins job; give it 90s and fail loudly if it
+          # doesn't come up rather than silently running tests against
+          # a half-started server.
+          can_connect() { curl -sSfo /dev/null http://localhost:9001/; }
+          for i in $(seq 1 90); do can_connect && break; sleep 1; done
+          if ! can_connect; then
+            echo "::error::Etherpad did not respond on :9001 within 90s"
+            echo "----- server log -----"
+            tail -n 200 /tmp/etherpad-server.log || true
+            exit 1
+          fi
+          cd src
+          pnpm exec playwright install chromium  --with-deps
+          pnpm run test-ui --project=chromium
+      - name: Upload server log on failure
+        uses: actions/upload-artifact@v7
+        if: failure()
+        with:
+          name: server-log-chrome-with-plugins
+          path: /tmp/etherpad-server.log
+          retention-days: 7
+      - uses: actions/upload-artifact@v7
+        if: always()
+        with:
+          name: playwright-report-chrome-with-plugins
+          path: src/playwright-report/
+          retention-days: 30
+
+  playwright-firefox-with-plugins:
+    env:
+      PNPM_HOME: ~/.pnpm-store
+    name: Playwright Firefox with plugins
+    runs-on: ubuntu-latest
+    continue-on-error: true  # informational; see chrome-with-plugins comment
Evidence
PR Compliance ID 1 requires official plugin frontend tests to run in core CI and fail the pipeline
on failures, but both newly added with-plugins jobs explicitly set continue-on-error: true,
preventing failures from failing the workflow.

Run official plugin frontend tests in core CI
.github/workflows/frontend-tests.yml[152-244]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The with-plugins Playwright jobs are configured with `continue-on-error: true`, which allows frontend test failures in the official plugin configuration to pass CI.
## Issue Context
Compliance requires that official /ether plugin frontend regressions fail the pipeline so breakages are caught before merge.
## Fix Focus Areas
- .github/workflows/frontend-tests.yml[152-244]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


5. Curl probe can hang 🐞 Bug ☼ Reliability
Description
The Etherpad readiness check uses curl without any per-request timeout, so if localhost:9001
accepts a connection but never sends a response, the step can hang indefinitely and the 90-iteration
loop will not enforce the intended 90s limit.
Code

.github/workflows/frontend-tests.yml[R58-59]

+          can_connect() { curl -sSfo /dev/null http://localhost:9001/; }
+          for i in $(seq 1 90); do can_connect && break; sleep 1; done
Evidence
The workflow’s can_connect() function calls curl without --connect-timeout/--max-time and is
used as the gate before running Playwright; a stalled curl would block the whole step because the
loop only bounds retries, not the duration of each probe call.

.github/workflows/frontend-tests.yml[49-65]
.github/workflows/frontend-tests.yml[113-129]
.github/workflows/frontend-tests.yml[198-214]
.github/workflows/frontend-tests.yml[278-294]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The readiness probe uses `curl` without a per-request timeout, so a partial/blocked HTTP response can hang the job indefinitely.
### Issue Context
The loop limits retries to ~90 iterations, but it does not limit how long each `curl` invocation may run.
### Fix Focus Areas
- .github/workflows/frontend-tests.yml[49-65]
- .github/workflows/frontend-tests.yml[113-129]
- .github/workflows/frontend-tests.yml[198-214]
- .github/workflows/frontend-tests.yml[278-294]
### Suggested change
Update `can_connect()` to include timeouts, for example:
- `curl --connect-timeout 1 --max-time 2 -sSfo /dev/null http://localhost:9001/`
(or wrap with `timeout 2s curl ...`) in all four jobs.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


6. Missing server-ready abort🐞 Bug ☼ Reliability
Description
In the new *-with-plugins jobs, the test step proceeds to Playwright even if Etherpad never becomes
reachable within the 15s loop, which can lead to misleading failures/flakes. Etherpad startup runs
plugin migration/installation when var/installed_plugins.json is absent, adding extra startup work
that these new jobs now trigger by installing 11 plugins.
Code

.github/workflows/frontend-tests.yml[R195-208]

+          pnpm run prod > /tmp/etherpad-server.log 2>&1 &
+          connected=false
+          can_connect() {
+          curl -sSfo /dev/null http://localhost:9001/ || return 1
+          connected=true
+          }
+          now() { date +%s; }
+          start=$(now)
+          while [ $(($(now) - $start)) -le 15 ] && ! can_connect; do
+          sleep 1
+          done
+          cd src
+          pnpm exec playwright install chromium  --with-deps
+          pnpm run test-ui --project=chromium
Evidence
The workflow’s wait loop sets connected=true only if curl succeeds but never checks connected
after the loop, so the job continues even if the server is still down. Etherpad startup calls
checkForMigration() before loading plugins; if var/installed_plugins.json does not exist (it is
not in the repo), checkForMigration() migrates by inspecting installed packages and installing
plugins, which can delay the server becoming reachable—exactly what the new jobs introduce by adding
11 plugins.

.github/workflows/frontend-tests.yml[175-209]
src/node/server.ts[173-180]
src/static/js/pluginfw/installer.ts[81-90]
src/static/js/pluginfw/installer.ts[56-79]
var/.gitignore[1-1]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The workflow starts Etherpad in the background and waits up to 15 seconds for `http://localhost:9001/`, but it **never fails** if the server is still unreachable. With plugins installed, Etherpad may do extra work during startup (plugin migration/installation), so the fixed wait can be insufficient and Playwright will run against a down server.
### Issue Context
The script sets `connected=true` inside `can_connect()` but never checks it after the loop.
### Fix Focus Areas
- .github/workflows/frontend-tests.yml[192-209]
- .github/workflows/frontend-tests.yml[269-286]
### Suggested fix
- Increase the timeout (for example 60–180s), and **after the loop** do something like:
- `if [ "$connected" != true ]; then echo "Etherpad failed to start"; tail -n +1 /tmp/etherpad-server.log; exit 1; fi`
- Optionally add `set -euo pipefail` and ensure the background server process is cleaned up on exit (trap).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

7. Readiness probe double-check 🐞 Bug ☼ Reliability
Description
The readiness loop breaks on a successful can_connect, but then immediately calls can_connect
again in if ! can_connect; and can fail the job if the second probe transiently fails during
startup. This can create false CI failures (especially during slower plugin boot) even though the
server was reachable within the 90s window.
Code

.github/workflows/frontend-tests.yml[R60-67]

+          can_connect() { curl --max-time 3 -sSfo /dev/null http://localhost:9001/; }
+          for i in $(seq 1 90); do can_connect && break; sleep 1; done
+          if ! can_connect; then
+            echo "::error::Etherpad did not respond on :9001 within 90s"
+            echo "----- server log -----"
+            tail -n 200 /tmp/etherpad-server.log || true
+            exit 1
+          fi
Evidence
In each frontend job, the loop exits on the first successful curl (can_connect && break) but does
not persist that success. The subsequent if ! can_connect re-runs curl and can flip the outcome if
the server is briefly unstable between the loop exit and the second probe.

.github/workflows/frontend-tests.yml[49-67]
.github/workflows/frontend-tests.yml[115-133]
.github/workflows/frontend-tests.yml[202-220]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The server readiness check breaks out of the probe loop on success, then immediately runs a second probe in `if ! can_connect;` which can fail transiently and incorrectly abort the job.
### Issue Context
This pattern exists in multiple jobs and is more likely to show up under slower startup (e.g., plugin boot).
### Fix Focus Areas
- .github/workflows/frontend-tests.yml[54-67]
- .github/workflows/frontend-tests.yml[120-133]
- .github/workflows/frontend-tests.yml[206-220]
### Suggested change
Capture success within the loop (e.g., `connected=false; ...; if can_connect; then connected=true; break; fi`) and check the boolean after the loop, or restructure to avoid the second probe (e.g., `until can_connect; do ...; done` with an explicit timeout counter).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


8. WITH_PLUGINS misinterpreted 🐞 Bug ≡ Correctness
Description
Playwright retries and multiple test.skip(...) gates treat process.env.WITH_PLUGINS as a boolean
by presence, so values like WITH_PLUGINS=0 will still enable the with-plugins behavior (5 retries
and skipped specs). This makes behavior surprising and can accidentally reduce coverage if
WITH_PLUGINS is set to a non-empty falsy-intended value.
Code

src/playwright.config.ts[R19-21]

+    // Plugin-loaded suites are inherently flakier (slower pad boot,
+    // extra hooks racing) so give them a bigger retry cushion.
+    retries: process.env.CI ? (process.env.WITH_PLUGINS ? 5 : 2) : 0,
Evidence
The config uses process.env.WITH_PLUGINS ? 5 : 2, and the specs use
!!process.env.WITH_PLUGINS—both are truthy for any non-empty string, including "0".

src/playwright.config.ts[16-23]
src/tests/frontend-new/specs/bold.spec.ts[28-35]
src/tests/frontend-new/specs/urls_become_clickable.spec.ts[21-31]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`WITH_PLUGINS` is currently interpreted via JavaScript truthiness, which treats any non-empty string as enabled. This can unintentionally enable 5 retries and skip specs when `WITH_PLUGINS` is set to values like `0`.
### Issue Context
The workflow sets `WITH_PLUGINS=1`, but explicit checks make behavior robust for future uses and local runs.
### Fix Focus Areas
- src/playwright.config.ts[16-23]
- src/tests/frontend-new/specs/**[relevant skip lines]
### Suggested change
Introduce a helper boolean like:

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


9. Cache key mismatches lockfile 🐞 Bug ➹ Performance
Description
The with-plugins jobs run pnpm add -w ... (which mutates pnpm-lock.yaml) but share the same
pnpm-store cache key namespace as the vanilla jobs, and that key is derived from the lockfile state
before the mutation. This reduces cache determinism and can cause cache-save collisions across jobs
that use the same key, lowering cache effectiveness and mixing plugin/non-plugin dependency sets
under one key.
Code

.github/workflows/frontend-tests.yml[R160-196]

+      - uses: actions/cache@v5
+        name: Cache pnpm store
+        with:
+          path: ${{ env.PNPM_HOME }}
+          key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
+          restore-keys: |
+            ${{ runner.os }}-pnpm-store-
+      - uses: actions/cache@v5
+        name: Cache Playwright browsers
+        with:
+          path: ~/.cache/ms-playwright
+          key: ${{ runner.os }}-playwright-${{ hashFiles('src/package.json', 'pnpm-lock.yaml') }}
+          restore-keys: |
+            ${{ runner.os }}-playwright-
+      - uses: pnpm/action-setup@v6
+        name: Install pnpm
+        with:
+          version: 10.33.2
+          run_install: false
+      - name: Install all dependencies and symlink for ep_etherpad-lite
+        run: pnpm install --frozen-lockfile
+      - name: Install Etherpad plugins
+        # Same plugin set as backend-tests.yml's withpluginsLinux job.
+        run: >
+          pnpm add -w
+          ep_align
+          ep_author_hover
+          ep_cursortrace
+          ep_font_size
+          ep_headings2
+          ep_markdown
+          ep_readonly_guest
+          ep_set_title_on_pad
+          ep_spellcheck
+          ep_subscript_and_superscript
+          ep_table_of_contents
+      - name: Create settings.json
Evidence
All jobs use the same pnpm-store cache key derived from hashFiles('**/pnpm-lock.yaml'), but the
with-plugins jobs later run pnpm add -w ... which updates that lockfile during the job. Because
actions/cache restores/saves using the key computed before the mutation, the saved cache content no
longer corresponds to the lockfile hash used in the key, and plugin jobs also contend with vanilla
jobs for the same cache key.

.github/workflows/frontend-tests.yml[24-31]
.github/workflows/frontend-tests.yml[160-166]
.github/workflows/frontend-tests.yml[181-196]
.github/workflows/frontend-tests.yml[242-248]
.github/workflows/frontend-tests.yml[263-278]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The with-plugins jobs mutate `pnpm-lock.yaml` via `pnpm add -w ...` but use the same pnpm-store cache key as the vanilla jobs, keyed on the pre-mutation lockfile hash. This makes caches less predictable and can reduce cache hit rates.
### Issue Context
The goal is to keep caching effective and avoid cross-job key collisions/mixing between vanilla and with-plugins jobs.
### Fix Focus Areas
- .github/workflows/frontend-tests.yml[24-31]
- .github/workflows/frontend-tests.yml[160-166]
- .github/workflows/frontend-tests.yml[181-196]
- .github/workflows/frontend-tests.yml[242-248]
- .github/workflows/frontend-tests.yml[263-278]
### Suggested change
For the *with-plugins* jobs, use distinct cache keys (and optionally restore-keys) that include a stable suffix, e.g.:

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


View more (3)
10. can_connect fix lacks test 📘 Rule violation ☼ Reliability
Description
This PR includes a bug fix to the CI server-start wait/timeout logic, but it does not add any
automated regression test that would fail if the fix were reverted. Without a regression test, the
timeout/fail-loud behavior could silently regress and reintroduce flaky/hung frontend CI runs.
Code

.github/workflows/frontend-tests.yml[R52-65]

+          set -euo pipefail
   pnpm run prod > /tmp/etherpad-server.log 2>&1 &
-          connected=false
-          can_connect() {
-          curl -sSfo /dev/null http://localhost:9001/ || return 1
-          connected=true
-          }
-          now() { date +%s; }
-          start=$(now)
-          while [ $(($(now) - $start)) -le 15 ] && ! can_connect; do
-          sleep 1
-          done
+          # Loading 11 plugins makes startup noticeably slower than the
+          # without-plugins job; give it 90s and fail loudly if it
+          # doesn't come up rather than silently running tests against
+          # a half-started server.
+          can_connect() { curl -sSfo /dev/null http://localhost:9001/; }
+          for i in $(seq 1 90); do can_connect && break; sleep 1; done
+          if ! can_connect; then
+            echo "::error::Etherpad did not respond on :9001 within 90s"
+            echo "----- server log -----"
+            tail -n 200 /tmp/etherpad-server.log || true
+            exit 1
+          fi
Evidence
PR Compliance ID 3 requires a regression test for bug fixes. The diff shows a bug fix to the
frontend test workflow's server readiness/timeout handling (adding set -euo pipefail and an
explicit timeout failure path), but the PR only changes workflow logic rather than adding a test
that verifies this behavior.

.github/workflows/frontend-tests.yml[52-65]
Best Practice: Repository guidelines

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The PR fixes the CI connect-loop timeout behavior, but there is no automated regression test that would fail if the timeout/fail-loud logic were removed or broken.
## Issue Context
This change modifies workflow bash logic (`can_connect` loop + explicit failure path). Without a regression test (even a lightweight one), future refactors can reintroduce the prior behavior (silent fall-through / hangs) without being detected until CI becomes flaky.
## Fix Focus Areas
- .github/workflows/frontend-tests.yml[52-65]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


11. Duplicate workflow runs 🐞 Bug ➹ Performance
Description
This workflow runs on both push and pull_request without a guard, so internal PRs (branches in
the same repo) will run it twice; adding two more jobs increases the wasted runner time and slows
feedback.
Code

.github/workflows/frontend-tests.yml[R152-156]

+  playwright-chrome-with-plugins:
+    env:
+      PNPM_HOME: ~/.pnpm-store
+    name: Playwright Chrome with plugins
+    runs-on: ubuntu-latest
Evidence
frontend-tests.yml is configured to trigger on both events and its jobs do not have an if:
condition to avoid the internal-PR double-run pattern; backend-tests.yml demonstrates the repo’s
established approach by guarding jobs to avoid double runs on internal PRs.

.github/workflows/frontend-tests.yml[3-10]
.github/workflows/frontend-tests.yml[14-16]
.github/workflows/backend-tests.yml[16-25]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The workflow will run twice for internal PRs (once on `push`, once on `pull_request`), which becomes more expensive now that there are four frontend jobs.
### Issue Context
Other workflows in this repo (e.g., backend-tests) use an `if:` guard to run on `pull_request` only for forks, relying on `push` for same-repo branches.
### Fix Focus Areas
- .github/workflows/frontend-tests.yml[3-10]
- .github/workflows/frontend-tests.yml[14-16]
- .github/workflows/backend-tests.yml[16-25]
### Suggested change
Add a job-level `if:` to each job (or a single reusable expression via YAML anchors) similar to backend-tests:

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


12. Unpinned plugin versions 🐞 Bug ☼ Reliability
Description
The new jobs install plugins via pnpm add -w ep_* without version specifiers, so the exact plugin
versions under test depend on whatever is published at run time. This makes CI non-reproducible and
can break unexpectedly when plugin versions change.
Code

.github/workflows/frontend-tests.yml[R175-189]

+      - name: Install Etherpad plugins
+        # Same plugin set as backend-tests.yml's withpluginsLinux job.
+        run: >
+          pnpm add -w
+          ep_align
+          ep_author_hover
+          ep_cursortrace
+          ep_font_size
+          ep_headings2
+          ep_markdown
+          ep_readonly_guest
+          ep_set_title_on_pad
+          ep_spellcheck
+          ep_subscript_and_superscript
+          ep_table_of_contents
Evidence
The workflow installs 11 plugins by name only (no @version), and these plugins are not part of the
repo’s declared dependencies, so their resolved versions are determined at CI run time rather than
by the repo’s lockfile.

.github/workflows/frontend-tests.yml[175-190]
.github/workflows/frontend-tests.yml[252-267]
package.json[36-43]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The workflow installs plugins from the registry without pinning versions, which makes CI results change over time independent of core changes.
### Issue Context
The job uses `pnpm add -w ep_align ep_author_hover ...` (no `@<version>`).
### Fix Focus Areas
- .github/workflows/frontend-tests.yml[175-190]
- .github/workflows/frontend-tests.yml[252-267]
### Suggested fix
Pick one of these approaches:
1. **Pin versions inline**: change each entry to `ep_align@<known_good_version>` etc.
2. **Commit plugin deps**: add the plugins to the repo’s `package.json` (or a dedicated workspace/package) with pinned ranges and commit the updated `pnpm-lock.yaml`, then replace `pnpm add -w ...` with a normal `pnpm install --frozen-lockfile`.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Advisory comments

13. Misleading startup comment 🐞 Bug ⚙ Maintainability
Description
The comment explaining the 90s startup wait cites “Loading 11 plugins” even in the without-plugins
jobs, making the workflow misleading to future maintainers.
Code

.github/workflows/frontend-tests.yml[R54-57]

+          # Loading 11 plugins makes startup noticeably slower than the
+          # without-plugins job; give it 90s and fail loudly if it
+          # doesn't come up rather than silently running tests against
+          # a half-started server.
Evidence
The “Loading 11 plugins…” comment appears in the base Playwright jobs that do not install plugins,
while the plugin install step exists only in the *-with-plugins jobs.

.github/workflows/frontend-tests.yml[49-60]
.github/workflows/frontend-tests.yml[179-196]
.github/workflows/frontend-tests.yml[261-276]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The readiness-wait comment is inaccurate in the without-plugins jobs.
### Issue Context
The same readiness snippet is copy/pasted across all four jobs, but only the `*-with-plugins` variants actually install plugins.
### Fix Focus Areas
- .github/workflows/frontend-tests.yml[49-60]
- .github/workflows/frontend-tests.yml[179-196]
### Suggested change
Make the comment generic in the base jobs (e.g., “Give server up to 90s to start in CI”) or tailor comments separately for plugin vs non-plugin jobs.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Qodo Logo

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Run frontend tests of official plugins in core

1 participant