Files
sow-nodebb-theme/tests/wiki-mobile-table-client.test.js
archvillainette 653205c117 clean brittle tests (#3)
Reviewed-on: #3
Reviewed-by: xtul <mpiasecki720@protonmail.com>
Co-authored-by: vickydotbat <vickydotbat@tutamail.com>
Co-committed-by: vickydotbat <vickydotbat@tutamail.com>
2026-06-20 10:33:00 +00:00

151 lines
4.0 KiB
JavaScript

'use strict';
const assert = require('assert');
const fs = require('fs');
const path = require('path');
const vm = require('vm');
const source = fs.readFileSync(
path.join(__dirname, '..', 'public', 'client.js'),
'utf8'
);
const stylesheet = fs.readFileSync(
path.join(__dirname, '..', 'scss', 'westgate', '_wiki-prose.scss'),
'utf8'
);
const readyCallbacks = [];
const eventHandlers = [];
const wrappers = [];
const fakeApi = {
get: function () {},
};
function fakeJquery() {
return {
ready: function (callback) {
readyCallbacks.push(callback);
},
on: function (eventName, callback) {
eventHandlers.push({ eventName, callback });
},
};
}
function createParent() {
return {
children: [],
insertBefore: function (node, before) {
const index = this.children.indexOf(before);
assert.notEqual(index, -1, 'table fixture should belong to its parent');
this.children.splice(index, 0, node);
node.parentNode = this;
},
};
}
function createTable(parent, options = {}) {
const cells = [{ marker: 'first' }, { marker: 'second' }];
const table = {
parentNode: parent,
style: {},
rows: [{ children: cells }],
closest: function () {
return options.existingWrapper || null;
},
getAttribute: function () {
return null;
},
getBoundingClientRect: function () {
return { width: 320 };
},
querySelectorAll: function (selector) {
if (selector === 'tr') {
return this.rows;
}
return [];
},
};
parent.children.push(table);
return table;
}
function createWrapper() {
const wrapper = {
attributes: {},
children: [],
style: {
setProperty: function () {},
},
appendChild: function (child) {
const oldParent = child.parentNode;
const oldIndex = oldParent.children.indexOf(child);
oldParent.children.splice(oldIndex, 1);
this.children.push(child);
child.parentNode = this;
},
setAttribute: function (name, value) {
this.attributes[name] = value;
},
};
wrappers.push(wrapper);
return wrapper;
}
const firstParent = createParent();
const firstTable = createTable(firstParent);
let queriedTables = [firstTable];
const fakeDocument = {
querySelectorAll: function () {
return queriedTables;
},
createElement: function () {
return createWrapper();
},
};
const context = {
console,
document: fakeDocument,
require: function (deps, callback) {
assert(Array.isArray(deps));
callback(fakeApi);
},
window: {},
$: fakeJquery,
};
vm.runInNewContext(source, context, { filename: 'public/client.js' });
readyCallbacks.forEach(callback => callback());
assert.equal(wrappers.length, 1, 'plain rendered wiki tables should gain a scroll container');
assert.equal(wrappers[0].children[0], firstTable, 'wrapping must preserve the original table node');
assert(
wrappers[0].className && stylesheet.includes(`.${wrappers[0].className}`),
'the generated scroll container should be handled by the theme stylesheet'
);
assert.deepEqual(
firstTable.rows[0].children.map(cell => cell.marker),
['first', 'second'],
'wrapping must preserve table cell order and semantics'
);
assert.equal(wrappers[0].attributes.tabindex, '0', 'the scroll container should be keyboard focusable');
assert(wrappers[0].attributes['aria-label'], 'the scroll container should have an accessible name');
const ajaxifyHandler = eventHandlers.find(handler => handler.eventName === 'action:ajaxify.end');
assert(ajaxifyHandler, 'client should rerun wiki table wrapping after NodeBB ajaxify navigation');
const secondParent = createParent();
const secondTable = createTable(secondParent);
queriedTables = [secondTable];
ajaxifyHandler.callback();
assert.equal(wrappers.length, 2, 'tables loaded by ajaxify navigation should also be wrapped');
assert.equal(wrappers[1].children[0], secondTable);
const existingWrapper = {};
const wrappedParent = createParent();
const alreadyWrappedTable = createTable(wrappedParent, { existingWrapper });
queriedTables = [alreadyWrappedTable];
ajaxifyHandler.callback();
assert.equal(wrappers.length, 2, 'tables already handled by a compatible wrapper should not be nested again');