import assert from 'node:assert/strict'; import test from 'node:test'; import '../src/i18n/index.ts'; import { canonical, diffSections, isSecretKey, localSections, pickSections, stripSecrets } from '../../../waybetter/packages/shell/src/frame/backupModel.ts'; const current = { themes: { theme: { preset: 'nord', themes: [] }, scopes: null }, plugins: { enabled: ['bt-focus-mode.plugin.js'], files: [{ file: 'mine.plugin.js', source: 'v1' }] }, customCss: { css: 'a{}' }, features: { hideViewCounts: true }, settings: { homeUrl: 'https://x.com/home', externalLinks: 'pane', siteLinks: 'same', restoreTabs: true }, alerts: { enabled: false, keywords: [] }, accounts: { profiles: [{ id: 'default', name: 'Main', color: '#1d9bf0' }], active: 'default' }, workspaces: { version: 1, default: null, items: [] }, decks: { decks: [], settings: {} }, }; type Loose = Record; // eslint-disable-line @typescript-eslint/no-explicit-any test('the preview shows one row per section with what would change; unchanged sections are unticked', () => { const incoming = structuredClone(current) as Loose; incoming.themes.theme = { preset: 'paper', themes: [{ id: 'mine', name: 'Mine' }] }; incoming.plugins = { enabled: ['mine.plugin.js', 'bt-thread-reader.plugin.js'], files: [{ file: 'mine.plugin.js', source: 'v2' }, { file: 'new.plugin.js', source: 'x' }] }; incoming.accounts.profiles.push({ id: 'work', name: 'Work', color: '#f91880' }); incoming.drafts = { drafts: [{ kind: 'post', text: 'hello' }] }; const rows = diffSections(current, incoming); const by = Object.fromEntries(rows.map(row => [row.section, row])); assert.deepEqual(rows.map(row => row.section), ['themes', 'plugins', 'customCss', 'features', 'settings', 'alerts', 'accounts', 'workspaces', 'decks', 'drafts']); assert.ok(by.themes!.details.includes('Theme: nord → paper') && by.themes!.details.some(line => line.includes('1 imported theme'))); assert.ok(by.plugins!.details.some(line => line.includes('new.plugin.js')) && by.plugins!.details.some(line => line.startsWith('1 changed'))); assert.ok(by.plugins!.details.some(line => line.startsWith('Turns off: bt-focus-mode'))); assert.ok(by.accounts!.details[0]!.includes('Work') && by.accounts!.details.includes('No account is removed.')); assert.equal(by.customCss!.status, 'same'); assert.equal(by.customCss!.checked, false); assert.equal(by.themes!.checked, true); assert.equal(by.drafts!.status, 'new'); assert.equal(by.drafts!.checked, false, 'drafts only when asked'); }); test('a backup from before the host kept the settings (xLinks) compares as the same setting', () => { const old: Loose = { settings: { homeUrl: 'https://x.com/home', externalLinks: 'pane', xLinks: 'same', restoreTabs: true } }; assert.equal(diffSections(current, old)[0]!.status, 'same'); assert.equal(diffSections(old, { settings: current.settings })[0]!.status, 'same', 'and a settings file that was not saved since'); const changed = diffSections(current, { settings: { ...old.settings, xLinks: 'tab' } })[0]!; assert.equal(changed.status, 'changed'); assert.ok(changed.details[0]!.includes('siteLinks') && !changed.details[0]!.includes('xLinks'), changed.details[0]); }); test('only ticked sections are applied; decks by the shell, the rest natively; secrets never pass', () => { const incoming: Loose = { ...current, settings: { ...current.settings, authToken: 'abc', nested: { Cookie: 'c' } }, unknown: { x: 1 } }; const { native, local } = pickSections(incoming, ['settings', 'decks', 'unknown', 'plugins']); assert.deepEqual(Object.keys(native).sort(), ['plugins', 'settings']); assert.deepEqual(Object.keys(local), ['decks']); assert.ok(!canonical(native).includes('abc') && !canonical(native).includes('Cookie')); }); test('secret-looking keys are recognized like the native backup does', () => { for (const key of ['cookie', 'Cookies', 'auth_token', 'accessToken', 'ct0', 'password', 'x-csrf-token', 'sessionId', 'privateKey', 'passphrase', 'apiSecret']) assert.ok(isSecretKey(key), key); for (const key of ['keywords', 'homeUrl', 'profiles', 'restoreTabs', 'theme', 'enabled']) assert.ok(!isSecretKey(key), key); assert.deepEqual(stripSecrets({ a: [{ token: 1, b: 2 }] }), { a: [{ b: 2 }] }); const shell = localSections({ decks: [{ id: 'd1', name: 'Deck', columns: [], session: 'x' }], deckSettings: { width: 360 } }); assert.deepEqual(shell, { decks: { decks: [{ id: 'd1', name: 'Deck', columns: [] }], settings: { width: 360 } } }); assert.equal(canonical({ b: 1, a: [2, { d: 1, c: 2 }] }), canonical({ a: [2, { c: 2, d: 1 }], b: 1 })); });