229 lines
6.1 KiB
JavaScript
229 lines
6.1 KiB
JavaScript
const test = require('node:test');
|
|
const assert = require('node:assert/strict');
|
|
const fs = require('node:fs');
|
|
const path = require('node:path');
|
|
const vm = require('node:vm');
|
|
|
|
function createClassList() {
|
|
const classes = new Set();
|
|
|
|
return {
|
|
add(name) {
|
|
classes.add(name);
|
|
},
|
|
remove(name) {
|
|
classes.delete(name);
|
|
},
|
|
contains(name) {
|
|
return classes.has(name);
|
|
}
|
|
};
|
|
}
|
|
|
|
function createInput(name, value) {
|
|
const listeners = Object.create(null);
|
|
const attributes = Object.create(null);
|
|
|
|
return {
|
|
name: name,
|
|
value: value || '',
|
|
dataset: {},
|
|
classList: createClassList(),
|
|
attributes: attributes,
|
|
validityMessage: '',
|
|
addEventListener(type, handler) {
|
|
if (!listeners[type]) {
|
|
listeners[type] = [];
|
|
}
|
|
listeners[type].push(handler);
|
|
},
|
|
dispatchEvent(event) {
|
|
const handlers = listeners[event.type] || [];
|
|
handlers.forEach(function (handler) {
|
|
handler.call(this, event);
|
|
}, this);
|
|
},
|
|
setCustomValidity(message) {
|
|
this.validityMessage = String(message || '');
|
|
},
|
|
setAttribute(name, value) {
|
|
attributes[name] = String(value);
|
|
},
|
|
removeAttribute(name) {
|
|
delete attributes[name];
|
|
}
|
|
};
|
|
}
|
|
|
|
function createRow(startValue, endValue) {
|
|
const startInput = createInput('entry_start_datetime[]', startValue);
|
|
const endInput = createInput('entry_end_datetime[]', endValue);
|
|
const titleInput = createInput('entry_title[]', 'Opening');
|
|
const descriptionInput = createInput('entry_short_description[]', '');
|
|
const idInput = createInput('entry_id[]', '1');
|
|
const removeButton = {
|
|
addEventListener() {}
|
|
};
|
|
|
|
const row = {
|
|
startInput: startInput,
|
|
endInput: endInput,
|
|
querySelector(selector) {
|
|
if (selector === '[name="entry_start_datetime[]"]') {
|
|
return startInput;
|
|
}
|
|
if (selector === '[name="entry_end_datetime[]"]') {
|
|
return endInput;
|
|
}
|
|
if (selector === '[name="entry_title[]"]') {
|
|
return titleInput;
|
|
}
|
|
if (selector === '[name="entry_short_description[]"]') {
|
|
return descriptionInput;
|
|
}
|
|
if (selector === '[name="entry_id[]"]') {
|
|
return idInput;
|
|
}
|
|
if (selector === '[data-remove-timetable-entry]') {
|
|
return removeButton;
|
|
}
|
|
return null;
|
|
},
|
|
remove() {
|
|
row.removed = true;
|
|
}
|
|
};
|
|
|
|
return row;
|
|
}
|
|
|
|
test('timetable group form highlights an end time that is too early', () => {
|
|
const row = createRow('2026-08-07T12:00', '2026-08-07T12:00');
|
|
const timezoneInput = createInput('timezone', 'Europe/Berlin');
|
|
const body = {
|
|
rows: [row],
|
|
querySelectorAll(selector) {
|
|
return selector === '[data-timetable-entry-row]' ? this.rows.slice() : [];
|
|
},
|
|
appendChild(node) {
|
|
this.rows.push(node);
|
|
return node;
|
|
}
|
|
};
|
|
const addButton = {
|
|
listeners: Object.create(null),
|
|
addEventListener(type, handler) {
|
|
this.listeners[type] = handler;
|
|
},
|
|
click() {
|
|
if (this.listeners.click) {
|
|
this.listeners.click();
|
|
}
|
|
}
|
|
};
|
|
const form = {
|
|
dataset: {},
|
|
listeners: Object.create(null),
|
|
addEventListener(type, handler) {
|
|
this.listeners[type] = handler;
|
|
}
|
|
};
|
|
const templateRow = createRow('', '');
|
|
const template = {
|
|
content: {
|
|
cloneNode() {
|
|
return {
|
|
querySelector(selector) {
|
|
return selector === '[data-timetable-entry-row]' ? templateRow : null;
|
|
}
|
|
};
|
|
}
|
|
}
|
|
};
|
|
|
|
const sandbox = {
|
|
document: {
|
|
getElementById(id) {
|
|
if (id === 'timetable-group-form') {
|
|
return form;
|
|
}
|
|
if (id === 'timetable-group-timezone') {
|
|
return timezoneInput;
|
|
}
|
|
if (id === 'timetable-entry-row-template') {
|
|
return template;
|
|
}
|
|
return null;
|
|
},
|
|
querySelector(selector) {
|
|
if (selector === '[data-timetable-entries-body]') {
|
|
return body;
|
|
}
|
|
if (selector === '[data-add-timetable-entry]') {
|
|
return addButton;
|
|
}
|
|
return null;
|
|
}
|
|
},
|
|
window: {},
|
|
Date: Date,
|
|
Number: Number,
|
|
String: String,
|
|
Array: Array,
|
|
Object: Object,
|
|
Math: Math,
|
|
JSON: JSON,
|
|
console: console,
|
|
module: { exports: {} },
|
|
exports: {}
|
|
};
|
|
sandbox.window = sandbox;
|
|
|
|
const scriptPath = path.join(__dirname, '..', 'src', 'web', 'public', 'js', 'data-sources', 'timetable-group-form.js');
|
|
const script = fs.readFileSync(scriptPath, 'utf8');
|
|
vm.runInNewContext(script, sandbox, { filename: scriptPath });
|
|
|
|
assert.equal(row.startInput.value, '2026-08-07T12:00');
|
|
assert.equal(row.endInput.value, '2026-08-07T12:00');
|
|
assert.equal(row.endInput.validityMessage, 'End time must be at least 1 minute after the start time.');
|
|
assert.equal(row.endInput.classList.contains('is-invalid'), true);
|
|
assert.equal(row.endInput.attributes['aria-invalid'], 'true');
|
|
|
|
row.endInput.value = '2026-08-07T12:01';
|
|
row.endInput.dispatchEvent({ type: 'input' });
|
|
|
|
assert.equal(row.endInput.validityMessage, '');
|
|
assert.equal(row.endInput.classList.contains('is-invalid'), false);
|
|
assert.equal(Object.prototype.hasOwnProperty.call(row.endInput.attributes, 'aria-invalid'), false);
|
|
|
|
timezoneInput.value = 'Europe/London';
|
|
timezoneInput.dispatchEvent({ type: 'change' });
|
|
|
|
assert.equal(row.startInput.value, '2026-08-07T12:00');
|
|
assert.equal(row.endInput.value, '2026-08-07T12:01');
|
|
assert.equal(form.dataset.timetableTimezone, 'Europe/London');
|
|
assert.equal(form.dataset.dirty, 'true');
|
|
|
|
const formData = {
|
|
deleted: [],
|
|
appended: [],
|
|
delete(name) {
|
|
this.deleted.push(name);
|
|
},
|
|
append(name, value) {
|
|
this.appended.push([name, value]);
|
|
}
|
|
};
|
|
|
|
form.listeners.formdata({ formData: formData });
|
|
|
|
const startEntry = formData.appended.find(function (item) {
|
|
return item[0] === 'entry_start_datetime[]';
|
|
});
|
|
const endEntry = formData.appended.find(function (item) {
|
|
return item[0] === 'entry_end_datetime[]';
|
|
});
|
|
|
|
assert.equal(startEntry[1], '2026-08-07T11:00:00.000Z');
|
|
assert.equal(endEntry[1], '2026-08-07T11:01:00.000Z');
|
|
}); |