86 lines
5.3 KiB
JavaScript
86 lines
5.3 KiB
JavaScript
/* Regression: custom-layout helpers must load full shared documents before editing. */
|
|
const assert = require('node:assert/strict');
|
|
const fs = require('node:fs');
|
|
const path = require('node:path');
|
|
const vm = require('node:vm');
|
|
const source = fs.readFileSync(path.join(__dirname, '../js/inline-editor.js'), 'utf8');
|
|
function implementation(name) {
|
|
const match = source.match(new RegExp(` (?:async )?function ${name}\\([^]*?\\n }`));
|
|
assert.ok(match, name);
|
|
return match[0];
|
|
}
|
|
const fixtures = [
|
|
{'data-content-page': 'de_home'},
|
|
{class: 'editable-field', 'data-page': 'de_shared'},
|
|
{class: 'editable-field editable-field-html', 'data-page': 'de_shared'},
|
|
{'data-editable-group': '', 'data-page': 'de_groups'},
|
|
{'data-editable-link': '', 'data-page': 'de_links'},
|
|
{'data-editable-image': '', 'data-page': 'de_images'},
|
|
{'data-editable-icon': '', 'data-page': 'de_icons'},
|
|
{'data-editable-list': '', 'data-list-page': 'de_lists'},
|
|
{'data-editable-image': '', 'data-page': 'events'},
|
|
{'data-page': 'unrelated-widget'}
|
|
];
|
|
const nodes = fixtures.map(attrs => ({attrs, dataset: Object.fromEntries(Object.entries(attrs)
|
|
.filter(([key]) => key.startsWith('data-'))
|
|
.map(([key, value]) => [key.slice(5).replace(/-([a-z])/g, (_, letter) => letter.toUpperCase()), value]))}));
|
|
const matches = (node, selector) => selector.split(',').some(part => {
|
|
const classes = [...part.matchAll(/\.([\w-]+)/g)].map(match => match[1]);
|
|
const attributes = [...part.matchAll(/\[([\w-]+)\]/g)].map(match => match[1]);
|
|
return classes.every(cls => (node.attrs.class || '').split(' ').includes(cls))
|
|
&& attributes.every(attr => Object.hasOwn(node.attrs, attr));
|
|
});
|
|
const config = {apiUrl: '/admin/api.php', contentData: {}, contentRevisions: {}, loadedPages: [], dirtyPages: new Set()};
|
|
const loads = [];
|
|
const shared = {ui: {jurists: 'Juristen'}, footer: {company: 'Keep the complete footer'}, contact: {phone: '123'}};
|
|
const context = vm.createContext({
|
|
console, URL, Set, JSON, EditorConfig: config,
|
|
document: {
|
|
addEventListener() {},
|
|
querySelector(selector) {
|
|
return selector === 'meta[name="csrf-token"]' ? {content: 'fixture-token'}
|
|
: selector === 'meta[name="content-page"]' ? {content: 'de_home'} : null;
|
|
},
|
|
querySelectorAll: selector => nodes.filter(node => matches(node, selector))
|
|
},
|
|
window: {}, localStorage: {getItem: () => null}, sessionStorage: {getItem: () => null},
|
|
fetch: async url => {
|
|
const page = new URL(url, 'http://localhost').searchParams.get('page');
|
|
loads.push(page);
|
|
return {json: async () => ({success: true, data: structuredClone(page === 'de_shared' ? shared : {page}), revision: 'revision-' + page})};
|
|
}
|
|
});
|
|
for (const name of ['exitEditModeClean', 'loadEvents', 'attachEditorChromeHoverStates', 'createEditorUI',
|
|
'createGroupEditorUI', 'createEventEditorUI', 'createAddSectionUI', 'attachEditHandlers',
|
|
'attachEventEditHandlers', 'attachFooterEditHandlers', 'attachGroupEditHandlers', 'attachFieldEditHandlers',
|
|
'attachLinkEditHandlers', 'attachImageEditHandlers', 'attachListEditHandlers', 'attachComparisonRowHandlers',
|
|
'attachComparisonCellToggles', 'showAdminBar', 'attachEditModeHint', 'pushUndoState', 'updateUndoRedoButtons']) context[name] = () => {};
|
|
vm.runInContext(['initEditor', 'loadContent', 'setNestedValue', 'saveField', 'toggleListItemHidden'].map(implementation).join('\n'), context);
|
|
(async () => {
|
|
vm.runInContext('initEditor()', context);
|
|
await new Promise(setImmediate);
|
|
assert.deepEqual(loads.sort(), ['de_home', 'de_shared', 'de_groups', 'de_links', 'de_images', 'de_icons', 'de_lists'].sort());
|
|
assert.equal(config.contentRevisions.de_shared, 'revision-de_shared');
|
|
context.edited = {textContent: ''};
|
|
vm.runInContext("saveField('de_shared', 'ui.jurists', 'Updated heading', edited, false)", context);
|
|
assert.deepEqual(config.contentData.de_shared, {...shared, ui: {jurists: 'Updated heading'}});
|
|
assert.equal(context.edited.textContent, 'Updated heading');
|
|
assert.ok(config.dirtyPages.has('de_shared'));
|
|
config.contentData.de_team = {staff: [{name: 'First'}, {name: 'Second'}]};
|
|
Object.assign(context, {
|
|
getNestedObj: (value, key) => value[key], t: key => key, showToast() {},
|
|
Icons: {eyeClosed: 'closed', eyeOpen: 'open'}, item: {dataset: {}},
|
|
button: {dataset: {}, setAttribute(key, value) { this[key] = value; }}
|
|
});
|
|
for (const hidden of [true, false]) {
|
|
vm.runInContext("toggleListItemHidden('de_team', 'staff', 0, item, button)", context);
|
|
assert.equal(!!config.contentData.de_team.staff[0].hidden, hidden);
|
|
assert.equal(context.item.dataset.hidden, hidden ? 'true' : undefined);
|
|
assert.equal(context.button['aria-label'], hidden ? 'show' : 'hide');
|
|
assert.equal(context.button.dataset.editorTooltip, hidden ? 'show' : 'hide');
|
|
assert.deepEqual(config.contentData.de_team.staff[1], {name: 'Second'});
|
|
}
|
|
console.log('Shared editor content: all helper types loaded once with revisions; unrelated fields preserved; event images use their separate API.');
|
|
console.log('List visibility: hide/show updates data, accessible labels and tooltips without affecting the next item.');
|
|
})().catch(error => { console.error(error); process.exitCode = 1; });
|