Release v2.6.19
This commit is contained in:
@@ -17,7 +17,7 @@ function createHandlers() {
|
||||
return { app, handlers };
|
||||
}
|
||||
|
||||
test('clients list defaults to client then ip ordering', async () => {
|
||||
test('clients list defaults to client ordering', async () => {
|
||||
const { app, handlers } = createHandlers();
|
||||
|
||||
registerClientsRoutes(app, {
|
||||
@@ -38,7 +38,7 @@ test('clients list defaults to client then ip ordering', async () => {
|
||||
return JSON.stringify(data.clients.map(function (client) {
|
||||
return {
|
||||
client_name: client.client_name,
|
||||
clientIp: client.clientIp
|
||||
clientId: client.clientId
|
||||
};
|
||||
}));
|
||||
}
|
||||
@@ -46,9 +46,9 @@ test('clients list defaults to client then ip ordering', async () => {
|
||||
buildDashboardState: async () => ({
|
||||
screens: [],
|
||||
clients: [
|
||||
{ client_name: 'Beta', clientIp: '10.0.0.9' },
|
||||
{ client_name: 'Alpha', clientIp: '10.0.0.20' },
|
||||
{ client_name: 'Alpha', clientIp: '10.0.0.2' }
|
||||
{ client_name: 'Beta', clientId: 'beta-1' },
|
||||
{ client_name: 'Alpha', clientId: 'alpha-1' },
|
||||
{ client_name: 'Alpha', clientId: 'alpha-2' }
|
||||
]
|
||||
}),
|
||||
requirePermission() {
|
||||
@@ -76,9 +76,9 @@ test('clients list defaults to client then ip ordering', async () => {
|
||||
}, response, () => {});
|
||||
|
||||
assert.deepEqual(JSON.parse(response.body), [
|
||||
{ client_name: 'Alpha', clientIp: '10.0.0.2' },
|
||||
{ client_name: 'Alpha', clientIp: '10.0.0.20' },
|
||||
{ client_name: 'Beta', clientIp: '10.0.0.9' }
|
||||
{ client_name: 'Alpha', clientId: 'alpha-1' },
|
||||
{ client_name: 'Alpha', clientId: 'alpha-2' },
|
||||
{ client_name: 'Beta', clientId: 'beta-1' }
|
||||
]);
|
||||
});
|
||||
|
||||
|
||||
@@ -97,26 +97,9 @@ function createRow(startValue, endValue) {
|
||||
return row;
|
||||
}
|
||||
|
||||
function formatDateTimeLocalValue(date) {
|
||||
function pad(value) {
|
||||
return String(value).padStart(2, '0');
|
||||
}
|
||||
|
||||
return [
|
||||
String(date.getFullYear()).padStart(4, '0'),
|
||||
'-',
|
||||
pad(date.getMonth() + 1),
|
||||
'-',
|
||||
pad(date.getDate()),
|
||||
'T',
|
||||
pad(date.getHours()),
|
||||
':',
|
||||
pad(date.getMinutes())
|
||||
].join('');
|
||||
}
|
||||
|
||||
test('timetable group form highlights an end time that is too early', () => {
|
||||
const row = createRow('2026-08-07T10:00', '2026-08-07T10:00');
|
||||
const row = createRow('2026-08-07T12:00', '2026-08-07T12:00');
|
||||
const timezoneInput = createInput('timezone', 'Europe/Berlin');
|
||||
const body = {
|
||||
rows: [row],
|
||||
querySelectorAll(selector) {
|
||||
@@ -140,7 +123,10 @@ test('timetable group form highlights an end time that is too early', () => {
|
||||
};
|
||||
const form = {
|
||||
dataset: {},
|
||||
addEventListener() {}
|
||||
listeners: Object.create(null),
|
||||
addEventListener(type, handler) {
|
||||
this.listeners[type] = handler;
|
||||
}
|
||||
};
|
||||
const templateRow = createRow('', '');
|
||||
const template = {
|
||||
@@ -161,6 +147,9 @@ test('timetable group form highlights an end time that is too early', () => {
|
||||
if (id === 'timetable-group-form') {
|
||||
return form;
|
||||
}
|
||||
if (id === 'timetable-group-timezone') {
|
||||
return timezoneInput;
|
||||
}
|
||||
if (id === 'timetable-entry-row-template') {
|
||||
return template;
|
||||
}
|
||||
@@ -194,14 +183,47 @@ test('timetable group form highlights an end time that is too early', () => {
|
||||
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 = formatDateTimeLocalValue(new Date(new Date(row.startInput.value).getTime() + 60000));
|
||||
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');
|
||||
});
|
||||
@@ -0,0 +1,295 @@
|
||||
const test = require('node:test');
|
||||
const assert = require('node:assert/strict');
|
||||
const fs = require('node:fs');
|
||||
const vm = require('node:vm');
|
||||
|
||||
function loadScheduleModule(overrides) {
|
||||
const timeDatePlaceholdersScript = fs.readFileSync(require.resolve('../src/web/public/js/shared/time-date-placeholders.js'), 'utf8');
|
||||
const placeholderScript = fs.readFileSync(require.resolve('../src/web/public/js/shared/placeholder-utils.js'), 'utf8');
|
||||
const scriptPath = require.resolve('../src/web/public/js/regions/type/schedule.js');
|
||||
const script = fs.readFileSync(scriptPath, 'utf8');
|
||||
const registry = new Map();
|
||||
const customWindow = overrides && overrides.window ? overrides.window : {};
|
||||
const sandbox = {
|
||||
document: {
|
||||
addEventListener() {}
|
||||
},
|
||||
window: {
|
||||
pulseRegionTypes: {
|
||||
register(type, module) {
|
||||
registry.set(type, module);
|
||||
}
|
||||
},
|
||||
pulseRegionUtils: {
|
||||
escapeHtml(value) {
|
||||
return String(value === undefined || value === null ? '' : value)
|
||||
.replace(/&/g, '&')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/"/g, '"')
|
||||
.replace(/'/g, ''');
|
||||
},
|
||||
sanitizeRichText(value) {
|
||||
return String(value === undefined || value === null ? '' : value);
|
||||
}
|
||||
},
|
||||
placeholderChips: {
|
||||
renderChips(tokens) {
|
||||
return tokens.map((token) => '<span class="chip">{{' + token + '}}</span>').join(' ');
|
||||
}
|
||||
},
|
||||
initialData: {
|
||||
timetableGroups: [
|
||||
{
|
||||
id: 1,
|
||||
name: 'Main board',
|
||||
timezone: 'UTC',
|
||||
entries: [
|
||||
{
|
||||
id: 101,
|
||||
title: 'Launch',
|
||||
short_description: 'Doors open',
|
||||
start_datetime: '2026-08-09T10:00:00.000Z',
|
||||
end_datetime: '2026-08-09T11:00:00.000Z'
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
Intl: Intl,
|
||||
Date: Date,
|
||||
Object: Object,
|
||||
Array: Array,
|
||||
Number: Number,
|
||||
String: String,
|
||||
Boolean: Boolean,
|
||||
Math: Math,
|
||||
JSON: JSON,
|
||||
RegExp: RegExp,
|
||||
console: console
|
||||
},
|
||||
...customWindow
|
||||
};
|
||||
sandbox.window = Object.assign({}, sandbox.window, customWindow);
|
||||
|
||||
vm.runInNewContext(timeDatePlaceholdersScript, sandbox, { filename: 'time-date-placeholders.js' });
|
||||
vm.runInNewContext(placeholderScript, sandbox, { filename: 'placeholder-utils.js' });
|
||||
if (customWindow.placeholderUtils) {
|
||||
sandbox.window.placeholderUtils = customWindow.placeholderUtils;
|
||||
}
|
||||
vm.runInNewContext(script, sandbox, { filename: scriptPath });
|
||||
return registry.get('timetable');
|
||||
}
|
||||
|
||||
test('timetable region editor lists timezone transforms in helper text', () => {
|
||||
const module = loadScheduleModule();
|
||||
const html = module.renderEditorCard({
|
||||
region: { id: 47, label: 'Timetable' },
|
||||
current: {
|
||||
value: '<p>{{title}} {{start.tz()}} {{start.tz_short()}}</p>',
|
||||
timetable_group_id: 1,
|
||||
display_mode: 'current',
|
||||
max_items: 3
|
||||
},
|
||||
timetableGroups: [
|
||||
{
|
||||
id: 1,
|
||||
name: 'Main board',
|
||||
timezone: 'UTC',
|
||||
entries: []
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
assert.match(html, /Placeholder values support transforms/);
|
||||
assert.match(html, /<code>\{\{start\.tz\(\)\}\}<\/code>/);
|
||||
assert.match(html, /<code>\{\{start\.tz_short\(\)\}\}<\/code>/);
|
||||
assert.doesNotMatch(html, /class="chip">\{\{start\.tz\(\)\}\}<\/span>/);
|
||||
});
|
||||
|
||||
test('timetable region editor shows supported date format tokens', () => {
|
||||
const module = loadScheduleModule();
|
||||
const html = module.renderEditorCard({
|
||||
region: { id: 47, label: 'Timetable' },
|
||||
current: {
|
||||
value: '<p>{{start.format("MMM D, YYYY h:mm A")}}</p>',
|
||||
timetable_group_id: 1,
|
||||
display_mode: 'current',
|
||||
max_items: 3
|
||||
},
|
||||
timetableGroups: [
|
||||
{
|
||||
id: 1,
|
||||
name: 'Main board',
|
||||
timezone: 'UTC',
|
||||
entries: []
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
assert.match(html, /Supported date format tokens/);
|
||||
assert.match(html, /<th scope="col">Format<\/th>/);
|
||||
assert.ok(html.indexOf('<td><code>D</code></td><td>1-31</td><td>The day of the month</td>') < html.indexOf('<td><code>ddd</code></td><td>Mon</td><td>The abbreviated weekday name</td>'));
|
||||
assert.ok(html.indexOf('<td><code>ddd</code></td><td>Mon</td><td>The abbreviated weekday name</td>') < html.indexOf('<td><code>M</code></td><td>1-12</td><td>The month, beginning at 1</td>'));
|
||||
assert.ok(html.indexOf('<td><code>YY</code></td><td>18</td><td>The two-digit year</td>') < html.indexOf('<td><code>YYYY</code></td><td>2018</td><td>The four-digit year</td>'));
|
||||
assert.doesNotMatch(html, /<td><code>y<\/code><\/td>/);
|
||||
assert.doesNotMatch(html, /<td><code>yyyy<\/code><\/td>/);
|
||||
assert.doesNotMatch(html, /<td><code>yy<\/code><\/td>/);
|
||||
assert.doesNotMatch(html, /<td><code>tz<\/code><\/td>/);
|
||||
assert.doesNotMatch(html, /<td><code>tz_short<\/code><\/td>/);
|
||||
assert.match(html, /Use these tokens inside <code>\.format\(\.\.\.\)<\/code>/);
|
||||
});
|
||||
|
||||
test('timetable region preview resolves timezone placeholders from explicit timezone transforms', () => {
|
||||
const module = loadScheduleModule();
|
||||
const preview = module.renderPreview(
|
||||
{ id: 47, width: 500, height: 280 },
|
||||
{
|
||||
value: '<p>{{start.tz("UTC")}} {{start.tz_short("UTC")}}</p>',
|
||||
timetable_group_id: 1,
|
||||
display_mode: 'both',
|
||||
max_items: 3
|
||||
},
|
||||
{
|
||||
timetableGroups: [
|
||||
{
|
||||
id: 1,
|
||||
name: 'Main board',
|
||||
timezone: 'UTC',
|
||||
entries: [
|
||||
{
|
||||
id: 101,
|
||||
title: 'Launch',
|
||||
short_description: 'Doors open',
|
||||
start_datetime: '2026-08-10T10:00:00.000Z',
|
||||
end_datetime: '2026-08-10T11:00:00.000Z'
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
);
|
||||
|
||||
assert.match(preview, /UTC/);
|
||||
});
|
||||
|
||||
test('timetable placeholder format transform respects uppercase and lowercase day period tokens', () => {
|
||||
const placeholderScript = fs.readFileSync(require.resolve('../src/web/public/js/shared/placeholder-utils.js'), 'utf8');
|
||||
const sandbox = {
|
||||
window: {},
|
||||
Intl: Intl,
|
||||
Date: Date,
|
||||
Object: Object,
|
||||
Array: Array,
|
||||
Number: Number,
|
||||
String: String,
|
||||
Boolean: Boolean,
|
||||
Math: Math,
|
||||
JSON: JSON,
|
||||
RegExp: RegExp,
|
||||
console: console
|
||||
};
|
||||
|
||||
vm.runInNewContext(placeholderScript, sandbox, { filename: 'placeholder-utils.js' });
|
||||
|
||||
const upper = sandbox.window.placeholderUtils.resolvePlaceholderExpression(
|
||||
{ start: '2026-08-09T13:05:00.000Z' },
|
||||
'start.format("MMM D, YYYY h:mm A")'
|
||||
);
|
||||
const lower = sandbox.window.placeholderUtils.resolvePlaceholderExpression(
|
||||
{ start: '2026-08-09T13:05:00.000Z' },
|
||||
'start.format("MMM D, YYYY h:mm a")'
|
||||
);
|
||||
|
||||
assert.match(String(upper), /PM$/);
|
||||
assert.match(String(lower), /pm$/);
|
||||
});
|
||||
|
||||
test('timetable timezone abbreviation changes across daylight saving boundaries', () => {
|
||||
const module = loadScheduleModule();
|
||||
const preview = module.renderPreview(
|
||||
{ id: 47, width: 500, height: 280 },
|
||||
{
|
||||
value: '<p>{{start.tz_short("Europe/London")}}</p>',
|
||||
timetable_group_id: 1,
|
||||
display_mode: 'both',
|
||||
max_items: 10
|
||||
},
|
||||
{
|
||||
timetableGroups: [
|
||||
{
|
||||
id: 1,
|
||||
name: 'London board',
|
||||
timezone: 'Europe/London',
|
||||
entries: [
|
||||
{
|
||||
id: 201,
|
||||
title: 'Before DST ends',
|
||||
short_description: '',
|
||||
start_datetime: '2026-10-24T12:00:00.000Z',
|
||||
end_datetime: '2026-10-24T13:00:00.000Z'
|
||||
},
|
||||
{
|
||||
id: 202,
|
||||
title: 'After DST ends',
|
||||
short_description: '',
|
||||
start_datetime: '2026-10-26T12:00:00.000Z',
|
||||
end_datetime: '2026-10-26T13:00:00.000Z'
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
);
|
||||
|
||||
assert.match(preview, /BST/);
|
||||
assert.match(preview, /(GMT|UTC)/);
|
||||
});
|
||||
|
||||
test('timetable preview does not force the group timezone into placeholder transforms', () => {
|
||||
const calls = [];
|
||||
const module = loadScheduleModule({
|
||||
window: {
|
||||
placeholderUtils: {
|
||||
resolvePlaceholderExpression(value, expression, options) {
|
||||
calls.push({ expression, options: options || {} });
|
||||
return options && options.timeZone ? options.timeZone : 'local';
|
||||
},
|
||||
formatPlaceholderValue(value) {
|
||||
return String(value === undefined || value === null ? '' : value);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const preview = module.renderPreview(
|
||||
{ id: 47, width: 500, height: 280 },
|
||||
{
|
||||
value: '<p>{{start.tz_short()}}</p>',
|
||||
timetable_group_id: 1,
|
||||
display_mode: 'both',
|
||||
max_items: 3
|
||||
},
|
||||
{
|
||||
timetableGroups: [
|
||||
{
|
||||
id: 1,
|
||||
name: 'Madrid board',
|
||||
timezone: 'Europe/Madrid',
|
||||
entries: [
|
||||
{
|
||||
id: 101,
|
||||
title: 'Launch',
|
||||
short_description: 'Doors open',
|
||||
start_datetime: '2026-08-10T10:00:00.000Z',
|
||||
end_datetime: '2026-08-10T11:00:00.000Z'
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
);
|
||||
|
||||
assert.match(preview, /local/);
|
||||
assert.equal(calls.length > 0 ? Boolean(calls[0].options && calls[0].options.timeZone) : false, false);
|
||||
});
|
||||
@@ -0,0 +1,42 @@
|
||||
const test = require('node:test');
|
||||
const assert = require('node:assert/strict');
|
||||
|
||||
const { buildSlidePayload } = require('../src/data/slides');
|
||||
|
||||
test('buildSlidePayload normalizes timetable region fields', async () => {
|
||||
const pool = {
|
||||
async query(sql) {
|
||||
if (sql.includes('FROM c_templates st')) {
|
||||
return [[{ id: 9, name: 'Template 9', canvas_size_id: 1, canvas_size_width: 1920, canvas_size_height: 1080 }]];
|
||||
}
|
||||
|
||||
if (sql.includes('FROM c_template_regions')) {
|
||||
return [[{ id: 47, template_id: 9, region_key: 'timetable', region_type: 'timetable', label: 'Timetable' }]];
|
||||
}
|
||||
|
||||
return [[]];
|
||||
}
|
||||
};
|
||||
|
||||
const payload = await buildSlidePayload(pool, {
|
||||
body: {
|
||||
title: 'Timetable slide',
|
||||
template_id: '9',
|
||||
region_text_47: '<p>{{title}}</p>',
|
||||
region_timetable_group_id_47: '1',
|
||||
region_timetable_display_mode_47: 'current',
|
||||
region_timetable_max_items_47: '7'
|
||||
},
|
||||
files: []
|
||||
}, null);
|
||||
|
||||
const content = JSON.parse(payload.contentJson);
|
||||
assert.equal(content.timetable.type, 'timetable');
|
||||
assert.equal(content.timetable.text, '<p>{{title}}</p>');
|
||||
assert.equal(content.timetable.value, '<p>{{title}}</p>');
|
||||
assert.equal(content.timetable.timetable_group_id, '1');
|
||||
assert.equal(content.timetable.display_mode, 'current');
|
||||
assert.equal(content.timetable.max_items, '7');
|
||||
assert.equal(Object.prototype.hasOwnProperty.call(content.timetable, 'timetable_display_mode'), false);
|
||||
assert.equal(Object.prototype.hasOwnProperty.call(content.timetable, 'timetable_max_items'), false);
|
||||
});
|
||||
@@ -14,7 +14,8 @@ test('timetable duplicate helper copies group and entry fields', () => {
|
||||
const group = {
|
||||
id: 5,
|
||||
name: 'Morning Shift',
|
||||
short_description: 'Weekday events'
|
||||
short_description: 'Weekday events',
|
||||
timezone: 'Europe/Berlin'
|
||||
};
|
||||
const entry = {
|
||||
id: 12,
|
||||
@@ -32,6 +33,7 @@ test('timetable duplicate helper copies group and entry fields', () => {
|
||||
assert.equal(duplicateGroup.name, 'Copy of Morning Shift');
|
||||
assert.equal(duplicateGroup.short_description, 'Weekday events');
|
||||
assert.equal(duplicateGroup.shortDescription, 'Weekday events');
|
||||
assert.equal(duplicateGroup.timezone, 'Europe/Berlin');
|
||||
assert.equal(duplicateEntry.id, null);
|
||||
assert.equal(duplicateEntry.schedule_group_id, null);
|
||||
assert.equal(duplicateEntry.title, 'Opening');
|
||||
@@ -42,7 +44,7 @@ test('timetable duplicate helper copies group and entry fields', () => {
|
||||
|
||||
test('timetable duplicate form model keeps info toast variant and secondary actions', () => {
|
||||
const model = buildTimetableGroupFormViewModel(
|
||||
{ id: null, name: 'Morning Shift', shortDescription: 'Weekday events' },
|
||||
{ id: null, name: 'Morning Shift', shortDescription: 'Weekday events', timezone: 'Europe/Berlin' },
|
||||
[],
|
||||
'Review the copied values and save when ready.',
|
||||
{ permissionKeys: ['timetables.create'] },
|
||||
@@ -52,6 +54,7 @@ test('timetable duplicate form model keeps info toast variant and secondary acti
|
||||
|
||||
assert.equal(model.messageVariant, 'info');
|
||||
assert.equal(model.showSaveSecondaryActions, true);
|
||||
assert.equal(model.timetableGroup.timezone, 'Europe/Berlin');
|
||||
});
|
||||
|
||||
test('timetable list template includes duplicate action', () => {
|
||||
|
||||
@@ -0,0 +1,155 @@
|
||||
const test = require('node:test');
|
||||
const assert = require('node:assert/strict');
|
||||
|
||||
require('../src/common');
|
||||
|
||||
const registerTimetableRoutes = require('../src/web/routes/data-sources/timetables/routes');
|
||||
|
||||
function createApp() {
|
||||
const handlers = {};
|
||||
return {
|
||||
handlers: handlers,
|
||||
app: {
|
||||
get(path, ...routeHandlers) {
|
||||
handlers['GET ' + path] = routeHandlers;
|
||||
},
|
||||
post(path, ...routeHandlers) {
|
||||
handlers['POST ' + path] = routeHandlers;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function createConnection() {
|
||||
const queries = [];
|
||||
return {
|
||||
queries: queries,
|
||||
async beginTransaction() {
|
||||
queries.push(['beginTransaction']);
|
||||
},
|
||||
async query(sql, params) {
|
||||
queries.push([sql, params]);
|
||||
if (String(sql).indexOf('INSERT INTO i_timetable_groups') !== -1) {
|
||||
return [{ insertId: 42 }];
|
||||
}
|
||||
return [{}];
|
||||
},
|
||||
async commit() {
|
||||
queries.push(['commit']);
|
||||
},
|
||||
async rollback() {
|
||||
queries.push(['rollback']);
|
||||
},
|
||||
release() {
|
||||
queries.push(['release']);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
test('timetable create and update persist the selected timezone', async () => {
|
||||
const connection = createConnection();
|
||||
const { app, handlers } = createApp();
|
||||
|
||||
registerTimetableRoutes(app, {
|
||||
pool: {
|
||||
getConnection() {
|
||||
return connection;
|
||||
}
|
||||
},
|
||||
common: {
|
||||
fetchTimetableGroupById: async function () {
|
||||
return {
|
||||
id: 7,
|
||||
name: 'Existing',
|
||||
short_description: 'Old description',
|
||||
timezone: 'Europe/London'
|
||||
};
|
||||
},
|
||||
fetchDuplicateName: async function () {
|
||||
return false;
|
||||
},
|
||||
buildTimetableGroupPayload: function (req) {
|
||||
return {
|
||||
name: req.body.name,
|
||||
shortDescription: req.body.short_description,
|
||||
timezone: req.body.timezone
|
||||
};
|
||||
},
|
||||
fetchTimetableEntriesByGroupId: async function () {
|
||||
return [];
|
||||
}
|
||||
},
|
||||
getAuditUserId: function () {
|
||||
return 9;
|
||||
},
|
||||
redirectAfterSave: function (_req, res, url) {
|
||||
res.redirect(url);
|
||||
},
|
||||
parseDateTimeLocal: function (value) {
|
||||
return value ? new Date(value) : null;
|
||||
},
|
||||
requirePermission: function () {
|
||||
return function (_req, _res, next) {
|
||||
next();
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
const createRoute = handlers['POST /data-sources/timetables'][1];
|
||||
const updateRoute = handlers['POST /data-sources/timetables/:id'][1];
|
||||
|
||||
const createResponse = {
|
||||
redirects: [],
|
||||
redirect(url) {
|
||||
this.redirects.push(url);
|
||||
},
|
||||
status() {
|
||||
return this;
|
||||
}
|
||||
};
|
||||
|
||||
await createRoute({
|
||||
body: {
|
||||
name: 'Morning',
|
||||
short_description: 'Breakfast block',
|
||||
timezone: 'Europe/Berlin'
|
||||
},
|
||||
currentUser: { id: 1 },
|
||||
query: {}
|
||||
}, createResponse, function () {});
|
||||
|
||||
assert.deepEqual(connection.queries[1], [
|
||||
'INSERT INTO i_timetable_groups (name, short_description, timezone, created_by, modified_by) VALUES (?, ?, ?, ?, ?)',
|
||||
['Morning', 'Breakfast block', 'Europe/Berlin', 9, 9]
|
||||
]);
|
||||
assert.equal(createResponse.redirects[0], '/data-sources/timetables/42/edit');
|
||||
|
||||
connection.queries.length = 0;
|
||||
|
||||
const updateResponse = {
|
||||
redirects: [],
|
||||
redirect(url) {
|
||||
this.redirects.push(url);
|
||||
},
|
||||
status() {
|
||||
return this;
|
||||
}
|
||||
};
|
||||
|
||||
await updateRoute({
|
||||
params: { id: '7' },
|
||||
body: {
|
||||
name: 'Morning',
|
||||
short_description: 'Breakfast block',
|
||||
timezone: 'Europe/Berlin'
|
||||
},
|
||||
currentUser: { id: 1 },
|
||||
query: {}
|
||||
}, updateResponse, function () {});
|
||||
|
||||
assert.deepEqual(connection.queries[1], [
|
||||
'UPDATE i_timetable_groups SET name = ?, short_description = ?, timezone = ?, modified_by = ? WHERE id = ?',
|
||||
['Morning', 'Breakfast block', 'Europe/Berlin', 9, 7]
|
||||
]);
|
||||
assert.equal(updateResponse.redirects[0], '/data-sources/timetables/7/edit');
|
||||
});
|
||||
Reference in New Issue
Block a user