Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 58 additions & 0 deletions src/tests/backend/specs/settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,4 +89,62 @@ describe(__filename, function () {
assert.deepEqual(settings!.ep_webrtc, {"enabled": true});
})
})

// Regression test for https://github.com/ether/etherpad/issues/7543.
// Plugins (ep_font_color, ep_font_size, ep_plugin_helpers, …) consume
// Settings via CommonJS require(), which under tsx/ESM interop would place
// the default export under .default and leave top-level fields undefined.
// That broke template rendering with:
// TypeError: Cannot read properties of undefined (reading 'indexOf')
// when plugins called settings.toolbar.left / etc.
//
// The CJS compat layer in Settings.ts re-exposes every top-level field on
// module.exports via accessor properties, so require(...).<field> resolves
// even though the source uses `export default`. This test asserts that
// contract so a future refactor can't regress it silently.
describe('CJS compatibility for plugin consumers', function () {
it('exposes top-level fields directly on require() result', function () {
const cjs = require('../../../node/utils/Settings');
// The three fields most commonly read by first-party plugins.
assert.notStrictEqual(cjs.toolbar, undefined,
'settings.toolbar must be reachable via CJS require');
assert.notStrictEqual(cjs.skinName, undefined,
'settings.skinName must be reachable via CJS require');
assert.notStrictEqual(cjs.padOptions, undefined,
'settings.padOptions must be reachable via CJS require');
});

it('toolbar has the shape plugins index into (left/right/timeslider)', function () {
const cjs = require('../../../node/utils/Settings');
// ep_font_color and friends JSON.stringify(settings.toolbar) then call
// .indexOf on the result, so the object must be present and well-formed.
assert.ok(cjs.toolbar && typeof cjs.toolbar === 'object');
assert.ok(Array.isArray(cjs.toolbar.left));
assert.ok(Array.isArray(cjs.toolbar.right));
assert.ok(Array.isArray(cjs.toolbar.timeslider));
});

it('does not hide the real value under a .default wrapper', function () {
const cjs = require('../../../node/utils/Settings');
// If export-default handling regresses, consumers end up seeing a
// {default: {...}} wrapper and .toolbar on the wrapper is undefined.
// Either shape is acceptable as long as .toolbar is directly present,
// which is what the CJS compat shim guarantees.
if (cjs.default != null && cjs.default.toolbar != null) {
assert.strictEqual(cjs.toolbar, cjs.default.toolbar,
'require().toolbar must be the same object as require().default.toolbar');
}
});

it('setters propagate so reloadSettings() changes are visible to plugins', function () {
const cjs = require('../../../node/utils/Settings');
const original = cjs.title;
try {
cjs.title = 'cjs-shim-test';
assert.strictEqual(cjs.title, 'cjs-shim-test');
} finally {
cjs.title = original;
}
});
});
});
Loading