fix: pad-wide view settings apply to the creator's own view (#7900) (#7902)

* fix: pad-wide view settings apply to the creator's own view (#7900)

The pad creator is never "enforced upon themselves" (isPadSettingsEnforcedForMe
is false whenever canEditPadSettings is true), so getEffectivePadOptions always
merges their personal view overrides (cookies) on top of the pad-wide options.

As a result, a creator who had at some point toggled e.g. their personal "Read
content from right to left" carried a stale rtlIsTrue=false cookie that silently
masked the pad-wide value they later set — toggling the pad-wide control (and
then enforcing it) appeared to do nothing on the creator's own screen.

Fix: when the creator changes a pad-wide view option, sync their personal pref
to the chosen value so their own view adopts the pad-wide setting immediately.
This deliberately does NOT change the precedence model — the creator can still
override the setting afterwards via the "My view" controls, so the existing
behaviour where a creator keeps personal overrides while enforcing settings for
other users (pad_settings.spec.ts) is preserved.

Adds a frontend test reproducing the stale-personal-cookie scenario.

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

* test: address Qodo review — clear cookies on the page's own context (#7900)

browser.newContext()+clearCookies() cleared cookies on a throwaway context
(not the page fixture under test) and leaked the context. Use
page.context().clearCookies() so the regression test reliably starts without a
stale rtlIsTrue pref.

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

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
John McLear 2026-06-05 13:41:13 +01:00 committed by GitHub
parent 9af77e7e3a
commit 9e8f9cb513
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 70 additions and 0 deletions

View file

@ -876,6 +876,14 @@ const pad = {
options,
changedBy: pad.myUserInfo.name || 'unnamed',
});
// The pad creator is never "enforced upon themselves", so their personal
// view overrides (cookies) are always merged on top of the pad-wide value
// in getEffectivePadOptions. A stale personal pref would therefore mask the
// pad-wide value they just set, making the control appear to do nothing
// (#7900). Sync the creator's personal pref to the value they chose so
// their own view adopts it immediately. They can still override it
// afterwards via the "My view" controls.
pad.setMyViewOption(key, value);
},
changeViewOption: (key, value) => {
const effectiveOptions = pad.getEffectivePadOptions();

View file

@ -0,0 +1,62 @@
import {expect, test} from "@playwright/test";
import {goToNewPad} from "../helper/padHelper";
import {showSettings} from "../helper/settingsHelper";
// Regression test for ether/etherpad#7900.
//
// The pad creator is never "enforced upon themselves" (they can always edit
// pad settings), so their personal view overrides (cookies) are always merged
// on top of the pad-wide options in getEffectivePadOptions. A creator who had
// at some point toggled their personal "Read content from right to left"
// carried a stale rtlIsTrue=false cookie that silently masked the pad-wide RTL
// value they later set — the pad content stayed LTR and the pad-wide control
// appeared to "do nothing".
//
// Fix: changePadViewOption syncs the creator's personal pref to the value they
// chose, so their own view adopts the pad-wide setting immediately (while still
// allowing them to override it afterwards via the "My view" controls).
test.beforeEach(async ({page}) => {
// Clear cookies on the context of the page under test (not a throwaway
// context) so the test reliably starts without a stale rtlIsTrue pref.
await page.context().clearCookies();
await goToNewPad(page);
});
test.describe('RTL pad-wide + enforce', function () {
test('pad-wide RTL applies to the creator even with a stale personal setting', {tag: '@feature:rtl-toggle'}, async function ({page}) {
const innerBody = page
.frameLocator('iframe[name="ace_outer"]')
.frameLocator('iframe[name="ace_inner"]')
.locator('#innerdocbody');
const computedDir = () => innerBody.evaluate((el) =>
el.ownerDocument.defaultView!.getComputedStyle(el).direction);
await showSettings(page);
// The checkboxes are visually replaced by styled labels, so drive the UI
// the way a user does — by clicking the labels.
// The creator first toggles their PERSONAL RTL on, then off. This writes a
// personal cookie pref rtlIsTrue=false that used to mask the pad-wide value.
await page.locator('label[for="options-rtlcheck"]').click();
await expect(page.locator('#options-rtlcheck')).toBeChecked();
await expect.poll(computedDir).toBe('rtl');
await page.locator('label[for="options-rtlcheck"]').click();
await expect(page.locator('#options-rtlcheck')).not.toBeChecked();
await expect.poll(computedDir).toBe('ltr');
// Pad-wide settings are visible because the new pad's first user is its
// creator (canEditPadSettings). Setting pad-wide RTL must now flip the
// creator's own content despite the stale personal cookie...
await page.locator('label[for="padsettings-options-rtlcheck"]').click();
await expect(page.locator('#padsettings-options-rtlcheck')).toBeChecked();
await expect.poll(computedDir).toBe('rtl');
await expect(innerBody).toHaveClass(/\brtl\b/);
// ...and the personal control reflects the synced value.
await expect(page.locator('#options-rtlcheck')).toBeChecked();
// Enforcing for other users keeps the creator's content RTL.
await page.locator('label[for="padsettings-enforcecheck"]').click();
await expect(page.locator('#padsettings-enforcecheck')).toBeChecked();
await expect.poll(computedDir).toBe('rtl');
});
});