Skip to content

Commit c4add02

Browse files
JohnMcLearclaude
andauthored
test: regression tests for Settings CJS compat (#7543) (#7551)
#7421 fixed the ESM/CJS interop bug where plugins using require('ep_etherpad-lite/node/utils/Settings') got an object whose .toolbar (and every other top-level field) was undefined, crashing ep_font_color/ep_font_size/ep_plugin_helpers with "Cannot read properties of undefined (reading 'indexOf')" during pad.html rendering. That fix landed without a regression test. Pin the contract: top-level settings fields must be reachable via a CJS require(), the toolbar must keep its {left, right, timeslider} shape, and setters on the shim must propagate to the underlying settings object so reloadSettings() is visible to plugins. Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent e58dfa4 commit c4add02

1 file changed

Lines changed: 58 additions & 0 deletions

File tree

src/tests/backend/specs/settings.ts

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,4 +89,62 @@ describe(__filename, function () {
8989
assert.deepEqual(settings!.ep_webrtc, {"enabled": true});
9090
})
9191
})
92+
93+
// Regression test for https://github.com/ether/etherpad/issues/7543.
94+
// Plugins (ep_font_color, ep_font_size, ep_plugin_helpers, …) consume
95+
// Settings via CommonJS require(), which under tsx/ESM interop would place
96+
// the default export under .default and leave top-level fields undefined.
97+
// That broke template rendering with:
98+
// TypeError: Cannot read properties of undefined (reading 'indexOf')
99+
// when plugins called settings.toolbar.left / etc.
100+
//
101+
// The CJS compat layer in Settings.ts re-exposes every top-level field on
102+
// module.exports via accessor properties, so require(...).<field> resolves
103+
// even though the source uses `export default`. This test asserts that
104+
// contract so a future refactor can't regress it silently.
105+
describe('CJS compatibility for plugin consumers', function () {
106+
it('exposes top-level fields directly on require() result', function () {
107+
const cjs = require('../../../node/utils/Settings');
108+
// The three fields most commonly read by first-party plugins.
109+
assert.notStrictEqual(cjs.toolbar, undefined,
110+
'settings.toolbar must be reachable via CJS require');
111+
assert.notStrictEqual(cjs.skinName, undefined,
112+
'settings.skinName must be reachable via CJS require');
113+
assert.notStrictEqual(cjs.padOptions, undefined,
114+
'settings.padOptions must be reachable via CJS require');
115+
});
116+
117+
it('toolbar has the shape plugins index into (left/right/timeslider)', function () {
118+
const cjs = require('../../../node/utils/Settings');
119+
// ep_font_color and friends JSON.stringify(settings.toolbar) then call
120+
// .indexOf on the result, so the object must be present and well-formed.
121+
assert.ok(cjs.toolbar && typeof cjs.toolbar === 'object');
122+
assert.ok(Array.isArray(cjs.toolbar.left));
123+
assert.ok(Array.isArray(cjs.toolbar.right));
124+
assert.ok(Array.isArray(cjs.toolbar.timeslider));
125+
});
126+
127+
it('does not hide the real value under a .default wrapper', function () {
128+
const cjs = require('../../../node/utils/Settings');
129+
// If export-default handling regresses, consumers end up seeing a
130+
// {default: {...}} wrapper and .toolbar on the wrapper is undefined.
131+
// Either shape is acceptable as long as .toolbar is directly present,
132+
// which is what the CJS compat shim guarantees.
133+
if (cjs.default != null && cjs.default.toolbar != null) {
134+
assert.strictEqual(cjs.toolbar, cjs.default.toolbar,
135+
'require().toolbar must be the same object as require().default.toolbar');
136+
}
137+
});
138+
139+
it('setters propagate so reloadSettings() changes are visible to plugins', function () {
140+
const cjs = require('../../../node/utils/Settings');
141+
const original = cjs.title;
142+
try {
143+
cjs.title = 'cjs-shim-test';
144+
assert.strictEqual(cjs.title, 'cjs-shim-test');
145+
} finally {
146+
cjs.title = original;
147+
}
148+
});
149+
});
92150
});

0 commit comments

Comments
 (0)