// "Share my setup" (panel/setupModel.ts): what a shared setup can carry (the look, the mode and the // non-default flags, nothing personal), that a link and a file read back the same, that anything // unknown or malformed is dropped or refused rather than guessed, and that importing changes exactly // what the plan lists. import assert from 'node:assert/strict'; import test from 'node:test'; import type { FeatureTable } from '../../runtime/src/featureTable.ts'; import { tableDefaults } from '../../runtime/src/featureTable.ts'; import { SETUP_FORMAT, SETUP_LINK, SETUP_MAX_BYTES, applySetup, makeSetup, readSetup, setupFile, setupLink, setupPlan, type SetupState, } from '../src/panel/setupModel.ts'; const TABLE: FeatureTable = { areas: [ { id: 'navigation', name: 'Navigation+', description: 'd', owner: 'wb-navigation-plus.plugin.js', namespace: 'navigation-plus' }, { id: 'reader', name: 'Reader+', description: 'd', owner: 'wb-reader-plus.plugin.js', namespace: 'reader-plus' }, ], features: [ { id: 'fixScroll', area: null, group: 'fixes', default: true, backing: 'app' }, { id: 'hideTrends', area: 'navigation', default: false, backing: 'module' }, { id: 'navigation.focusMode', area: 'navigation', default: false, backing: 'module' }, { id: 'hideAds', area: 'reader', default: true, backing: 'app' }, ], modes: [ { id: 'basic', name: 'b', description: 'b', flags: { 'area.navigation': false, 'area.reader': false } }, { id: 'advanced', name: 'a', description: 'a', recommended: true, flags: { 'navigation.focusMode': true } }, ], }; const DEFAULTS = tableDefaults(TABLE); const MINE: SetupState = { theme: { preset: 'nord', accent: '#88c0d0', icons: 'pixel' }, mode: 'advanced', flags: { ...DEFAULTS, 'navigation.focusMode': true, hideTrends: true, hideAds: false }, }; test('a setup carries the look, the mode and the non-default flags, and nothing else', () => { const setup = makeSetup('x', TABLE, MINE); assert.deepEqual(setup, { format: SETUP_FORMAT, version: 1, app: 'x', flags: { hideTrends: true, 'navigation.focusMode': true, hideAds: false }, theme: { preset: 'nord', accent: '#88c0d0', icons: 'pixel' }, mode: 'advanced', }); // Whatever else the state object happens to hold never reaches the file. const leaky = { ...MINE, profiles: ['@someone'], notes: { a: 'b' }, drafts: ['text'] } as SetupState; assert.deepEqual(Object.keys(makeSetup('x', TABLE, leaky)).sort(), ['app', 'flags', 'format', 'mode', 'theme', 'version']); // A mode the table does not know is saved as none; an app without modes saves no mode at all. assert.equal(makeSetup('x', TABLE, { ...MINE, mode: 'gone' }).mode, null); assert.equal('mode' in makeSetup('x', { ...TABLE, modes: [] }, MINE), false); }); test('a link and a file read back to the same setup', () => { const setup = makeSetup('x', TABLE, MINE); const link = setupLink(setup); assert.ok(link.startsWith(`${SETUP_LINK}?app=x&d=`)); assert.match(link.slice(link.indexOf('d=') + 2), /^[A-Za-z0-9_-]+$/, 'URL-safe, no padding'); for (const input of [link, setupFile(setup), ` ${link}\n`]) { const read = readSetup(input, 'x', TABLE); assert.ok(read.ok, input); assert.deepEqual(read.setup, setup); assert.deepEqual(read.dropped, []); } // An empty setup (everything at its default) is still a valid link. const empty = readSetup(setupLink({ format: SETUP_FORMAT, version: 1, app: 'x', flags: {} }), 'x', TABLE); assert.ok(empty.ok); assert.deepEqual(empty.setup.flags, {}); }); test('another app, another format, a newer version or an oversized paste is refused whole', () => { const setup = makeSetup('kick', TABLE, MINE); assert.deepEqual(readSetup(setupLink(setup), 'x', TABLE), { ok: false, error: 'app', app: 'kick' }); assert.deepEqual(readSetup('{"format":"waybetter-backup"}', 'x', TABLE), { ok: false, error: 'format' }); assert.deepEqual(readSetup(JSON.stringify({ ...makeSetup('x', TABLE, MINE), version: 2 }), 'x', TABLE), { ok: false, error: 'version' }); assert.deepEqual(readSetup('not json', 'x', TABLE), { ok: false, error: 'format' }); assert.deepEqual(readSetup(`${SETUP_LINK}?app=x&d=%%%`, 'x', TABLE), { ok: false, error: 'format' }); assert.deepEqual(readSetup(`${SETUP_LINK}?app=x&d=bm90IGpzb24`, 'x', TABLE), { ok: false, error: 'format' }); assert.deepEqual(readSetup('x'.repeat(SETUP_MAX_BYTES + 1), 'x', TABLE), { ok: false, error: 'size' }); assert.deepEqual(readSetup('[]', 'x', TABLE), { ok: false, error: 'format' }); }); test('unknown flags, a foreign mode and a malformed theme are dropped and named, never guessed', () => { const raw = { format: SETUP_FORMAT, version: 1, app: 'x', flags: { hideTrends: true, 'p.someone.plugin': true, hideAds: 'yes', 'area.chat': false }, theme: { preset: '