151 lines
4.0 KiB
JavaScript
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');
|