etherpad-lite/doc/PLUGIN_FRONTEND_TESTS.md
John McLear 7f76aa2b81
ci(playwright): discover plugin frontend specs (closes #7622) (#7623)
* ci(playwright): discover plugin frontend specs from node_modules + plugin_packages

Adds two new globs to the Playwright testMatch so any installed
plugin shipping specs at the conventional location is picked up
automatically:

- ../node_modules/ep_*/static/tests/frontend-new/specs/**/*.spec.ts
  (covers `pnpm add -w ep_*` workspace installs, e.g. CI's
  with-plugins matrix and dev-time pnpm installs)
- plugin_packages/ep_*/static/tests/frontend-new/specs/**/*.spec.ts
  (covers admin-UI / live-plugin-manager installs into
  src/plugin_packages)

Mirrors the equivalent backend pattern (`mocha ...
../node_modules/ep_*/static/tests/backend/specs/**`) which already
auto-discovers plugin backend specs.

This re-enables coverage that was lost in commit cc80db2d3 (2023-07)
when the legacy in-page jQuery test runner was removed without a
Playwright replacement. Until now plugin frontend tests have been
silently dead: every plugin's CI runs `pnpm run test-ui` but core's
testDir scoped only to `tests/frontend-new/`, so plugin specs at
`static/tests/frontend/specs/test.js` were never executed and their
green badges were misleading. See #7622.

doc/PLUGIN_FRONTEND_TESTS.md documents the new convention, the
import path for shared helpers (ep_etherpad-lite/tests/...), and a
mocha+helper → Playwright translation table for plugin maintainers
who want to migrate.

Existing core test discovery is unchanged (143 tests in 38 files
listed before and after).

Closes #7622.

**Change type:** patch (test infra; no production behavior change).

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

* fix(playwright): split into per-project testMatch; address Qodo on #7623

Three real Qodo findings on the previous commit, all fixed:

1) test-ui's positional arg `tests/frontend-new/specs` filtered out
   plugin spec paths added to testMatch — the very thing the PR was
   trying to enable. Drop the positional. Discovery is now driven by
   per-project testMatch.

2) The single project-wide testMatch I added excluded
   tests/frontend-new/admin-spec, breaking pnpm run test-admin and the
   frontend-admin-tests workflow. Split into three projects:
     - chromium       : core specs + plugin specs
     - firefox        : core specs + plugin specs
     - chromium-admin : admin specs only
   test-admin now runs --project=chromium-admin (no positional). Net
   coverage unchanged for both workflows.

3) New code re-indented to 2 spaces per .editorconfig.

Discovery verified locally:
  --project=chromium       → 143 tests in 38 files (core)
  --project=firefox        → 143 tests in 38 files (core)
  --project=chromium-admin → 11 tests in 4 files (admin)
With a plugin spec installed at the conventional path:
  --project=chromium       → +1 file, +N tests as expected.

**Change type:** patch (test infra; no production behavior change).

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

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-28 06:33:43 +01:00

3.3 KiB

Plugin frontend tests

Etherpad core's Playwright runner discovers plugin frontend specs from the conventional path:

node_modules/ep_<plugin>/static/tests/frontend-new/specs/**/*.spec.ts

When the plugin is installed alongside core (e.g. via pnpm add -w ep_<plugin> or in a with-plugins CI variant), the plugin's specs run as part of pnpm run test-ui. Same pattern backend tests already use (mocha ... ../node_modules/ep_*/static/tests/backend/specs/**).

This re-enables coverage that was lost in commit cc80db2d3 (2023-07) when the legacy jQuery test runner (static/tests/frontend/specs/test.js

  • in-page mocha+helper) was removed without a Playwright replacement. See #7622.

Layout in your plugin

ep_yourplugin/
├── ep.json
├── package.json
├── static/
│   └── tests/
│       └── frontend-new/
│           └── specs/
│               └── yourplugin.spec.ts
└── ...

A spec is a normal Playwright test file. Import shared helpers from the core package — ep_etherpad-lite is symlinked into node_modules by the workspace, so this resolves anywhere the plugin is installed alongside core:

import {expect, test} from '@playwright/test';
import {clearPadContent, getPadBody, goToNewPad, writeToPad}
    from 'ep_etherpad-lite/tests/frontend-new/helper/padHelper';

test.beforeEach(async ({page}) => {
  await goToNewPad(page);
});

test.describe('ep_yourplugin', () => {
  test('does the thing', async ({page}) => {
    const padBody = await getPadBody(page);
    await padBody.click();
    await clearPadContent(page);
    await writeToPad(page, 'hello');
    // …assertions…
    await expect(padBody.locator('div').first()).toHaveText('hello');
  });
});

Migrating from the legacy static/tests/frontend/specs/test.js

The old format used mocha + a jQuery helper global:

// Legacy — does not run anywhere any more.
describe('ep_yourplugin', function () {
  beforeEach(function (cb) { helper.newPad(cb); });
  it('does the thing', async function () {
    const chrome$ = helper.padChrome$;
    const inner$ = helper.padInner$;
    expect(chrome$('#yourbutton').length).to.be.greaterThan(0);
  });
});

Translation table:

Legacy (mocha + helper) Playwright
describe(...) / it(...) test.describe(...) / test(...)
helper.newPad(cb) await goToNewPad(page)
helper.padChrome$('#x') page.locator('#x')
helper.padInner$('div') (await getPadBody(page)).locator('div')
expect(x).to.equal(y) expect(x).toBe(y) (Playwright's expect)
expect($el.length).to.be.greaterThan(0) await expect(page.locator('#x')).toBeVisible()
$el.sendkeys('text') await page.keyboard.type('text')
$el.simulate('click') await page.locator(...).click()

Most legacy specs translate ~mechanically. After migrating, delete the legacy file so the plugin can't accidentally ship stale tests that nothing executes.

Running them

# Inside core, with the plugin installed:
pnpm run test-ui --project=chromium
# Or via core's with-plugins CI job (see frontend-tests.yml).

pnpm run test-ui automatically picks up plugin specs from any installed ep_* package. To gate per-plugin: use playwright's --grep against your plugin's describe name.