mirror of
https://github.com/ancsemi/Haven.git
synced 2026-10-27 09:05:01 +00:00
media_group_layout is 'grid' or 'stacked': a server setting the admin sets, carried in templates, and a member preference that overrides it, saved on the account.
127 lines
5.8 KiB
JavaScript
127 lines
5.8 KiB
JavaScript
'use strict';
|
|
|
|
/**
|
|
* Pictures sent together show as a grid or full size, one under another
|
|
* (#5799). The admin sets the server's default (media_group_layout) and each
|
|
* member may pick their own, saved on their account. Both are checked here
|
|
* against a real server on a scratch port and data dir:
|
|
* node --test test/mediaGroupLayout.integration.test.js
|
|
*/
|
|
|
|
const test = require('node:test');
|
|
const assert = require('node:assert/strict');
|
|
const http = require('node:http');
|
|
const path = require('node:path');
|
|
const fs = require('node:fs');
|
|
const os = require('node:os');
|
|
const { spawn } = require('node:child_process');
|
|
const { io } = require('socket.io-client');
|
|
const { checkSetting } = require('../src/serverTemplate');
|
|
const { MEDIA_GROUP_LAYOUTS } = require('../src/mediaGroup');
|
|
|
|
const PORT = 3463;
|
|
const BASE = `http://localhost:${PORT}`;
|
|
const DATA = path.join(os.tmpdir(), `haven-media-layout-${Date.now()}`);
|
|
|
|
let server;
|
|
const clients = [];
|
|
|
|
const post = (p, body) => new Promise((res, rej) => {
|
|
const d = JSON.stringify(body);
|
|
const r = http.request({ host: 'localhost', port: PORT, path: p, method: 'POST',
|
|
headers: { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(d) } }, (resp) => {
|
|
let out = ''; resp.on('data', (c) => { out += c; }); resp.on('end', () => res(JSON.parse(out || '{}')));
|
|
});
|
|
r.on('error', rej); r.end(d);
|
|
});
|
|
const wait = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
const register = (username) => post('/api/auth/register', { username, password: 'layout12345', eulaVersion: '2.0', ageVerified: true });
|
|
|
|
function connect(token) {
|
|
const s = io(BASE, { auth: { token }, transports: ['websocket'], forceNew: true });
|
|
clients.push(s);
|
|
return new Promise((res, rej) => { s.on('connect', () => res(s)); s.on('connect_error', rej); });
|
|
}
|
|
// Sends `event` and resolves with the first `reply` event, or null after a while.
|
|
function ask(sock, event, data, reply, ms = 1500) {
|
|
return new Promise((res) => {
|
|
const t = setTimeout(() => { sock.off(reply, on); res(null); }, ms);
|
|
function on(payload) { clearTimeout(t); sock.off(reply, on); res(payload); }
|
|
sock.on(reply, on);
|
|
sock.emit(event, data);
|
|
});
|
|
}
|
|
const settings = (sock) => ask(sock, 'get-server-settings', undefined, 'server-settings');
|
|
const prefs = (sock) => ask(sock, 'get-preferences', undefined, 'preferences');
|
|
|
|
test.before(async () => {
|
|
fs.mkdirSync(DATA, { recursive: true });
|
|
server = spawn(process.execPath, ['server.js'], {
|
|
cwd: path.join(__dirname, '..'),
|
|
env: { ...process.env, PORT: String(PORT), HAVEN_DATA_DIR: DATA, ADMIN_USERNAME: 'admin', FORCE_HTTP: 'true' },
|
|
stdio: 'ignore',
|
|
});
|
|
for (let i = 0; i < 60; i++) {
|
|
try {
|
|
await new Promise((res, rej) => http.get(`${BASE}/api/health`, (r) => (r.statusCode === 200 ? res() : rej())).on('error', rej));
|
|
return;
|
|
} catch { await wait(500); } // not listening yet: try again
|
|
}
|
|
throw new Error('server did not start');
|
|
});
|
|
test.after(async () => {
|
|
clients.forEach((s) => s.close());
|
|
if (server && server.exitCode === null) {
|
|
const exited = new Promise((r) => server.once('exit', r));
|
|
server.kill();
|
|
await exited;
|
|
}
|
|
fs.rmSync(DATA, { recursive: true, force: true, maxRetries: 10, retryDelay: 200 });
|
|
});
|
|
|
|
test('the layouts are grid and stacked, and templates carry only those', () => {
|
|
assert.deepEqual(MEDIA_GROUP_LAYOUTS, ['grid', 'stacked']);
|
|
assert.equal(checkSetting('media_group_layout', 'stacked'), 'stacked');
|
|
assert.equal(checkSetting('media_group_layout', ' grid '), 'grid');
|
|
assert.equal(checkSetting('media_group_layout', 'mosaic'), null);
|
|
assert.equal(checkSetting('media_group_layout', ''), null);
|
|
});
|
|
|
|
test('the admin sets the server default; members see it change and cannot set it', async () => {
|
|
const admin = await register('admin');
|
|
const member = await register('layoutmember');
|
|
const A = await connect(admin.token);
|
|
const M = await connect(member.token);
|
|
|
|
assert.equal((await settings(M)).media_group_layout, 'grid', 'a new server shows the grid');
|
|
|
|
// Anything but the two layouts is refused without a word.
|
|
assert.equal(await ask(A, 'update-server-setting', { key: 'media_group_layout', value: 'mosaic' }, 'server-setting-changed', 800), null);
|
|
assert.equal((await settings(M)).media_group_layout, 'grid');
|
|
|
|
const changed = await ask(A, 'update-server-setting', { key: 'media_group_layout', value: 'stacked' }, 'server-setting-changed');
|
|
assert.deepEqual(changed, { key: 'media_group_layout', value: 'stacked' });
|
|
await wait(200);
|
|
assert.equal((await settings(M)).media_group_layout, 'stacked', 'members are sent the new default');
|
|
|
|
const refused = await ask(M, 'update-server-setting', { key: 'media_group_layout', value: 'grid' }, 'error-msg');
|
|
assert.match(String(refused), /admins/i);
|
|
assert.equal((await settings(M)).media_group_layout, 'stacked');
|
|
});
|
|
|
|
test('a member saves their own layout on the account and can go back to the default', async () => {
|
|
const login = await post('/api/auth/login', { username: 'layoutmember', password: 'layout12345', eulaVersion: '2.0', ageVerified: true });
|
|
const M = await connect(login.token);
|
|
|
|
assert.equal(await ask(M, 'set-preference', { key: 'media_group_layout', value: 'mosaic' }, 'preference-saved', 800), null);
|
|
assert.equal((await prefs(M)).media_group_layout, undefined, 'no row means the server default');
|
|
|
|
assert.deepEqual(await ask(M, 'set-preference', { key: 'media_group_layout', value: 'grid' }, 'preference-saved'), { key: 'media_group_layout', value: 'grid' });
|
|
// Another device of the same account gets it with the rest of the preferences.
|
|
const other = await connect(login.token);
|
|
assert.equal((await prefs(other)).media_group_layout, 'grid');
|
|
|
|
assert.deepEqual(await ask(M, 'delete-preference', { key: 'media_group_layout' }, 'preference-deleted'), { key: 'media_group_layout' });
|
|
assert.equal((await prefs(M)).media_group_layout, undefined);
|
|
});
|