When an ordered list followed directly after an unordered list (no blank
line between), all OL items showed "1" instead of incrementing. This was
because renumberList's applyNumberList function counted bullet items in
the position counter, so the first OL item got start=3 (after 2 bullet
items) instead of start=1, preventing the CSS counter-reset class from
being applied.
The fix resets the position counter when the list type changes at the
same level (e.g., bullet -> number).
Fixes: https://github.com/ether/etherpad-lite/issues/5160
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: wait for server confirmation before navigating after pad delete
The delete pad handler navigated to '/' immediately after sending the
PAD_DELETE message. Firefox (and some mobile Chrome) would close the
WebSocket before the message reached the server, causing the delete to
silently fail.
Now the client waits for the server's {disconnect: 'deleted'} response
before navigating. Also awaits pad.remove() on the server side to
ensure the operation completes before the response is sent.
Fixes: https://github.com/ether/etherpad-lite/issues/7306
Fixes: https://github.com/ether/etherpad-lite/issues/7311
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: handle non-creator delete and add timeout fallback
- Listen for 'shout' event to show error when non-creator tries to
delete (server sends shoutMessage instead of deleting)
- Add 5-second timeout fallback in case the server doesn't respond
(socket dropped, server crashed, etc.)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When HTML containing <li> elements without a wrapping <ul> or <ol> is
pasted (e.g., from ChatGPT), the contentcollector crashes with
"TypeError: lineAttributes.list is undefined" because it assumes
_enterList() was already called by a parent list element.
The fix defaults bare <li> elements to bullet1 list type and properly
sets oldListTypeOrNull so the list state is cleaned up after the <li>
is processed. Also guards the .indexOf() call on lineAttributes.list.
Fixes: https://github.com/ether/etherpad-lite/issues/6665
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Drop webkit from CI workflow and Playwright config (Chrome + Firefox
are the supported browsers)
- Set retries: 2 in CI to handle intermittent failures from timing
sensitive operations (list attribute clearing, server restarts)
- Fix clearAuthorship helper to use force:true to bypass toolbar-overlay
div that intermittently intercepts clicks after text selection
- Fix admin restartEtherpad helper: increase poll intervals, add
explicit timeout, use toHaveValue with timeout instead of toBeEmpty
- Convert clear_authorship_color tests to use Playwright auto-retry
assertions (toHaveAttribute) instead of one-shot getAttribute calls
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Enforce 2-space indentation across codebase
Convert all 4-space indented source files to 2-space to match
.editorconfig and project contributor guidelines.
74 files converted: admin UI components, type definitions, security
modules, test files, helpers, and utilities.
No functional changes — 2882 insertions, 2882 deletions (pure
whitespace).
Fixes#7353
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Limit admin tests to chromium and firefox
Webkit is already tested in the dedicated frontend-tests workflow.
Running it again in admin tests causes flaky failures due to slow
socket connections and external API timeouts on webkit CI runners.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Load tests are slow and don't need to run on every push. Schedule
daily at 08:00 UTC with manual trigger option.
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The settings textarea content is populated asynchronously via socket.
On slow CI (especially Node 20 + Firefox), the default 20s timeout
isn't enough. Increase to 30s for all toBeEmpty checks.
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Fix flaky undo keypress test
Each Ctrl+Z may only undo one keystroke depending on how Etherpad
batches undo operations (varies between dev and prod mode). Loop
Ctrl+Z presses until content is restored instead of assuming one
press undoes everything.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Fix flaky admin restart test causing cascading failures
restartEtherpad used a hardcoded 500ms wait which wasn't enough for
the server to restart on slow CI. Subsequent tests got
ERR_CONNECTION_REFUSED because the server was still down.
- Poll the server until it responds instead of hardcoded timeout
- Re-login after restart since the session cookie is lost
- Remove unnecessary waitForTimeout(5000) at end of test
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Speed up restart poll: accept any response, poll at 500ms
The previous check required response.ok() (2xx) but the server
returns redirects (3xx) which caused the loop to run all 30 seconds.
Accept any non-zero status and reduce poll interval to 500ms.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Fix loginToAdmin: navigate to /admin/login directly
The admin SPA only shows the login form at /admin/login. Navigating to
/admin/ loads the HomePage route which doesn't have the login fields.
After a server restart the session is lost, but the SPA doesn't
automatically redirect to /admin/login, causing the test to timeout
waiting for input[name="username"].
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Fix plugin search test: wait for results, increase timeout
The search is debounce-triggered (500ms), not Enter-triggered. The
toHaveCount(1) check passed on the "not found" row before results
loaded. Removed the Enter press and count check, increased timeout
to 30s since the search depends on an external API call to
static.etherpad.org.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Fix plugin search: normalize numeric keys from registry
The plugin registry at static.etherpad.org/plugins.json changed format
from {ep_name: {data}} to {index: {name: "ep_name", data}}. The search
code iterated keys expecting plugin names starting with "ep_", but got
numeric keys like "41", skipping all plugins. Normalize the data after
fetching to use plugin names as keys.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Pin plugins to last-known-good versions in backend tests
Pin ep_font_size@0.4.65, ep_headings2@0.2.76, ep_markdown@10.0.1
to the versions that passed on March 31. The newer versions cause
a template crash: Cannot read properties of undefined (reading
'indexOf') at pad.html:67 in toolbar.menu().
This will help narrow down which plugin update is the culprit.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Unpin ep_markdown, 1.0.8 is latest and code-identical to 10.0.1
Only ep_font_size@0.4.65 and ep_headings2@0.2.76 remain pinned to
narrow down which plugin update causes the toolbar template crash.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Use pnpm instead of gnpm for plugin install in backend tests
gnpm ignores version pins — it reports installing the pinned version
but the plugin loader picks up the latest from its store. Switching
to pnpm for the plugin install step so version pins actually work.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Use gnpm exec pnpm for plugin install to bypass gnpm caching
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Remove ep_hash_auth from backend test plugin list
ep_hash_auth blocks unauthenticated requests, causing 28 backend tests
to get 500 Internal Server Error when accessing pads. The tests don't
provide credentials, so any auth plugin will break them.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Fix ESM/CJS interop for Settings module and harden toolbar
Plugins use require('ep_etherpad-lite/node/utils/Settings') (CJS) but
Settings.ts uses export default (ESM). With tsx, CJS require puts the
default export under .default, so settings.toolbar is undefined and
ep_font_size crashes with "Cannot read properties of undefined
(reading 'indexOf')" when rendering pad.html.
Two fixes:
- Settings.ts: add property getters on module.exports so CJS consumers
can access settings properties directly
- toolbar.ts: guard against undefined buttons array to prevent crashes
if Settings interop doesn't propagate through gnpm's plugin_packages
Tested locally: 735 passing, 0 failing with all plugins.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Fix flaky unordered_list and undo tests
- unordered_list: re-query the DOM element after clearPadContent()
instead of holding a stale reference that detaches when the editor
rebuilds the DOM.
- undo: remove hardcoded waitForTimeout(1000) and re-query the element
so Playwright's auto-retry on toHaveText handles the timing.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Fix flaky tests: wait for DOM settle, undo all keystrokes
- unordered_list: wait for DOM to settle after clearPadContent() with
toHaveCount assertion, then use click() instead of selectText() which
races with async DOM rebuilds.
- undo: press Ctrl+Z three times since writeToPad('foo') produces 3
keystrokes and each Ctrl+Z only undoes one.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Fix undo keypress test: don't clear pad before typing
Clearing the pad added an undoable operation to the history, so Ctrl+Z
could undo past the typing and restore the original welcome text.
Simplified to match the button-based undo test: type on existing
content and undo once, since Etherpad batches rapid keystrokes.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Fix undo keypress test: restore clear step, mirror button test
writeToPad needs clearPadContent first to ensure focus is in the
editor iframe. Without it, keyboard.type() doesn't reach the editor.
Now mirrors the button-based undo test exactly, just using Ctrl+Z
instead of clicking the undo button, and without the hardcoded
waitForTimeout.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Fix frontend test failures across all browsers
- Fix home button using fragile relative URL (window.location.href +
"/../..") that WebKit doesn't resolve correctly. Use
window.location.origin instead.
- Wait for #editorcontainer.initialized in goToNewPad/goToPad/
appendQueryParams so toolbar, chat, and cookie handlers are fully
set up before tests interact with them.
- Clear cookies in chat test beforeEach to prevent chatAndUsers cookie
from prior tests disabling the sticky chat checkbox.
- Wait for navigation to complete in editbar home button test.
Fixes#7405
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Run frontend tests on pull requests
Playwright runs locally and doesn't need Sauce Labs secrets, so
there's no reason to limit frontend tests to push events only.
Also remove stale Sauce Labs references from workflow names/comments.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Fix sticky chat test: use click() instead of check()/uncheck()
The stickToScreen() handler manages checkbox state internally with its
own toggle logic and a setTimeout. Playwright's check()/uncheck()
methods verify state after clicking, but race with the async toggle,
causing "Clicking the checkbox did not change its state" errors.
Using click() avoids this — the waitForSelector calls already verify
the final state.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Fix sticky chat handler and reduce parallel workers
- Remove force:true from sticky chat checkbox clicks — it can bypass
jQuery event handlers preventing stickToScreen() from firing.
- Wait for chatbox stickyChat class instead of checkbox state, since
stickToScreen() manages the checkbox asynchronously via setTimeout.
- Reduce workers from 5 to 2 to avoid overloading the single Etherpad
server instance, which causes goToNewPad timeouts on CI.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Clean up workflows: remove Sauce Labs, load test push-only
- Remove all Sauce Labs references (steps, comments, secrets) from
frontend test workflows — Playwright replaced Sauce Labs
- Remove unused set-output steps and GIT_HASH exports
- Remove stale commented-out code from admin tests
- Restrict load test to push events only (no need on PRs)
- Fix artifact names to not reference undefined matrix.node
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Fix sticky chat test: click label instead of checkbox
The label element intercepts pointer events on the checkbox (reported
by Webkit). On Chrome/Firefox the checkbox is "not stable" due to
animations. Clicking the label is how a real user interacts with it
and properly triggers the jQuery click handler.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Fix home button to preserve subpath installations
Use URL API to resolve '../..' relative to current URL instead of
hardcoding origin + '/'. This preserves any configured base path
(e.g. /etherpad) for reverse-proxy installations.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: re-apply retries: 0
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Enable globstar in backend-tests template for recursive test discovery
Without shopt -s globstar, bash ** doesn't recurse into subdirectories,
causing mocha to miss test files in paths like specs/api/exportHTML.ts.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Set retries to 0 so test failures are reported honestly. With retries: 2,
tests could fail twice and still pass on the third attempt, hiding real
bugs as "flaky" tests that count as passing.
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Remove the 2020 workaround that disabled nice-select on Safari due to
a position:fixed + overflow:hidden rendering bug. This bug has been
fixed in modern WebKit, and disabling nice-select meant Safari/WebKit
users got native selects while tests expected the custom dropdowns,
causing all font_type and language tests to fail on webkit.
Fixes#7405
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Improve update-plugins workflow resilience and add summary
Continue processing remaining plugins when one fails instead of
crashing. Add summary at the end showing succeeded/failed/skipped
counts and plugin names.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Fix webkit frontend tests silently passing when they fail
Remove `|| true` from the webkit Playwright test step that was
swallowing non-zero exit codes, causing the workflow to always
report success regardless of test results.
Fixes#7405
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Continue processing remaining plugins when one fails instead of
crashing. Add summary at the end showing succeeded/failed/skipped
counts and plugin names.
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Runs checkPlugin with autopush on all ether/ep_* repos daily at
06:00 UTC. Updates workflows, dependencies, linting, and version
bumps across all plugins.
Requires PLUGINS_PAT org secret with push access to all ep_* repos.
Can also be triggered manually via workflow_dispatch.
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Run pnpm update on plugins during autofix to bump all dependencies
to their latest compatible versions, reducing dependabot noise and
keeping plugins current.
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Prod mode enables rate limiting which causes frontend tests to fail
silently. Dev mode disables rate limiting for testing.
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Revert plugin workflow template changes
Reverts the following commits to replace with a single clean fix:
- e97e203d7 Fix backend-tests find pattern for versioned plugin paths
- 45fe8a310 Fix backend-tests find path for plugin test discovery
- 892c52ba2 Fix plugin backend-tests workflow pnpm 10 symlink error
- 7484d9ea6 Update deprecated GitHub Actions in plugin workflow templates
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Modernize plugin workflow templates for pnpm 10 and new plugin paths
- Bump actions/checkout v3 → v4, cache-apt-pkgs-action v1.4.2 → v1.6.0
- Replace pnpm link --global with pnpm run plugins i --path (fixes
pnpm 10 "symlink path same as target" error)
- Fix backend test discovery: plugins install to src/plugin_packages/
via live-plugin-manager, not node_modules/
- Run mocha directly from src/ against node_modules/ep_* symlinks
so tests resolve correctly
Tested and verified on ep_table_of_contents.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
live-plugin-manager installs to src/plugin_packages/ep_name@version/,
not src/plugin_packages/ep_name/. Update the glob to match the
versioned directory format.
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
plugins i --path installs to src/plugin_packages/, not node_modules/.
Update the find command to look in the correct location so backend
tests are actually discovered and run.
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Remove redundant pnpm link --global steps that conflict with
pnpm run plugins i --path on pnpm 10, causing "Symlink path is
the same as the target path" errors. The plugins i command handles
all linking internally via LinkInstaller.
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Bump actions/checkout v3 → v4 and awalsh128/cache-apt-pkgs-action v1.4.2 → v1.6.0
to fix CI failures caused by deprecated actions/upload-artifact v3 dependency.
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>