Release 2.22.0: normalize playlist schedules and unify player sync
Publish Docker Image / build-and-push-existing-registry (./build/Dockerfile, web, pulse-signage-web) (push) Successful in 1m21s
Publish Docker Image / build-and-push-existing-registry (./build/Dockerfile.player, player, pulse-signage-player) (push) Successful in 32s

This commit is contained in:
lzstealth committed 2026-10-27 22:54:38 +00:00
1 parent 7748129307
commit b71958cb38
39 files changed
+1174 -314

No files matched your search

+38 -16
View File
@@ -38,6 +38,8 @@ function createResponse() {
test('playlist create and update redirect to the canonical edit path', async () => {
const calls = [];
let groupInsertParams = null;
let slideInsertParams = null;
const groupRuleInsertParams = [];
let canvasSizeEnabled = 1;
const connection = {
async beginTransaction() {},
@@ -56,14 +58,28 @@ test('playlist create and update redirect to the canonical edit path', async ()
calls.push('delete-slides');
return [{ affectedRows: 1 }];
}
if (sql.startsWith('DELETE FROM c_playlist_groups')) {
calls.push('delete-groups');
return [{ affectedRows: 1 }];
}
if (sql.startsWith('SELECT sl.id, st.canvas_size_id')) {
return [[{ id: 11, canvas_size_id: 7 }]];
}
if (sql.startsWith('INSERT INTO c_playlist_slides')) {
if (sql.startsWith('INSERT INTO c_playlist_groups')) {
groupInsertParams = params;
calls.push('insert-group');
return [{ insertId: 71 }];
}
if (sql.startsWith('INSERT INTO c_playlist_slides')) {
slideInsertParams = params;
calls.push('insert-slide');
return [{ insertId: 101 }];
}
if (sql.startsWith('INSERT INTO c_playlist_schedule_rules')) {
groupRuleInsertParams.push(params);
calls.push('insert-group-rule');
return [{ insertId: 81 }];
}
return [[]];
}
};
@@ -139,26 +155,31 @@ test('playlist create and update redirect to the canonical edit path', async ()
group_shuffle: ['1'],
group_mode: ['repeat'],
group_repeat_count: ['2'],
group_schedule_rules_json: [JSON.stringify([{ start_time: '08:00', end_time: '17:00', days: [1, 2, 3] }])]
group_schedule_rules_json: [JSON.stringify([
{ start_datetime: '2026-10-01T09:00', days: [1, 2, 3] },
{ end_datetime: '2026-10-31T18:00' }
])]
}
}, updateRes, () => {});
}, updateRes, error => { throw error; });
assert.equal(createRes.redirectedTo, '/playlists/4/edit');
assert.equal(updateRes.redirectedTo, '/playlists/4/edit', updateRes.statusCode + ': ' + String(updateRes.body || ''));
assert.deepEqual(groupInsertParams.slice(6, 10), [
'news',
'News',
1,
JSON.stringify([{ start_time: '08:00', end_time: '17:00', days: [1, 2, 3] }])
assert.deepEqual(groupInsertParams, [4, 'news', 'News', 1, 1, 'repeat', 2, 12, 12]);
assert.equal(slideInsertParams[6], 71);
assert.deepEqual(groupRuleInsertParams, [
[71, 0, '2026-10-01T09:00', null, null, null, '[1,2,3]', 12, 12],
[71, 1, null, '2026-10-31T18:00', null, null, null, 12, 12]
]);
assert.equal(groupInsertParams[10], 1);
assert.equal(groupInsertParams[11], 'repeat');
assert.equal(groupInsertParams[12], 2);
assert.deepEqual(calls, [
'delete-slides',
'delete-groups',
{ kind: 'redirectAfterSave', url: '/playlists/4/edit', path: '/playlists' },
'update-playlist',
'delete-slides',
'delete-groups',
'insert-group',
'insert-group-rule',
'insert-group-rule',
'insert-slide',
{ kind: 'redirectAfterSave', url: '/playlists/4/edit', path: '/playlists/4' }
]);
@@ -200,21 +221,22 @@ test('playlist create and update redirect to the canonical edit path', async ()
});
test('playlist audit compares persisted items and schedules without recreated row IDs', async () => {
let items = [{ id: 101, slide_id: 11, duration_seconds: 10 }];
let items = [{ id: 101, slide_id: 11, group_id: null, duration_seconds: 10 }];
let rules = [{ playlist_slide_id: 101, start_time: '08:00:00', end_time: '17:00:00', schedule_days_json: '[1,2]' }];
const events = [];
const connection = {
async commit() {}, async rollback() {}, release() {},
async query(sql, params) {
if (sql.includes('FROM c_playlist_slides WHERE playlist_id') && sql.startsWith('SELECT id, slide_id')) return [structuredClone(items)];
if (sql.includes('FROM c_playlist_slide_schedule_rules r')) return [structuredClone(rules)];
if (sql.includes('FROM c_playlist_slides ps') && sql.includes('WHERE ps.playlist_id')) return [structuredClone(items)];
if (sql.includes('FROM c_playlist_schedule_rules r')) return [structuredClone(rules)];
if (sql.startsWith('SELECT sl.id, st.canvas_size_id')) return [[{ id: 11, canvas_size_id: 7 }]];
if (sql.startsWith('DELETE FROM c_playlist_slides')) { items = []; rules = []; }
if (sql.startsWith('DELETE FROM c_playlist_groups')) return [{ affectedRows: 0 }];
if (sql.startsWith('INSERT INTO c_playlist_slides')) {
items = [{ id: 202, slide_id: params[1], duration_seconds: params[3] }];
items = [{ id: 202, slide_id: params[1], group_id: params[6], duration_seconds: params[3] }];
return [{ insertId: 202 }];
}
if (sql.startsWith('INSERT INTO c_playlist_slide_schedule_rules')) {
if (sql.startsWith('INSERT INTO c_playlist_schedule_rules')) {
rules.push({ playlist_slide_id: params[0], start_time: params[4] + ':00', end_time: params[5] + ':00', schedule_days_json: params[6] });
}
return [[]];