storebound/tests/desktop-coverage.test.ts

44 lines
2.4 KiB
TypeScript

// Every CLI command is reachable in the desktop app, or listed as not for the desktop with a reason
// (cli/lib/desktop-coverage.ts; the same analysis writes docs/desktop-coverage.md).
import { describe, expect, test } from 'bun:test';
import { readFileSync } from 'node:fs';
import { join } from 'node:path';
import { DESKTOP_COVERAGE, OWNER_STEPS, TOOLBOX } from '../cli/lib/desktop-coverage';
import { describeMethods } from '../cli/serve';
import { analyze, render } from '../tools/gen-desktop-coverage';
/** The only commands that make no sense in a window. Adding one here needs a reason in the coverage map. */
const NOT_FOR_DESKTOP = ['mcp', 'serve', 'desktop', 'api', 'settings', 'aab', 'watch', 'events'];
describe('desktop coverage', () => {
const { rows, extra } = analyze();
test('every CLI command has a desktop place (no gaps)', () => {
expect(rows.filter(r => r.status === 'gap').map(r => `${r.command}: ${r.problems.join('; ')}`)).toEqual([]);
expect(extra).toEqual([]);
});
test('not-for-desktop is short and each has a reason', () => {
const not = rows.filter(r => r.status === 'not-for-desktop');
expect(not.map(r => r.command).sort()).toEqual([...NOT_FOR_DESKTOP].sort());
for (const r of not) expect(r.where.length).toBeGreaterThan(20);
});
test('the More tools tab offers no owner action and no unknown method', () => {
const ms = new Map(describeMethods().map(m => [m.method, m] as const));
for (const m of TOOLBOX.flatMap(g => g.methods)) {
expect(ms.has(m)).toBe(true);
expect(['read', 'write']).toContain(ms.get(m)!.kind);
}
for (const s of OWNER_STEPS) expect(s.command.startsWith('storebound ')).toBe(true);
for (const [, c] of Object.entries(DESKTOP_COVERAGE)) if ('owner' in c && c.owner) expect(c.owner.length).toBeGreaterThan(2);
});
test('docs/desktop-coverage.md is generated and current', () => {
expect(readFileSync(join(import.meta.dir, '../docs/desktop-coverage.md'), 'utf8')).toBe(render(rows));
});
test('every More tools group and owner step has en and tr strings', () => {
const en = readFileSync(join(import.meta.dir, '../apps/desktop/src/i18n/en.ts'), 'utf8');
const tr = readFileSync(join(import.meta.dir, '../apps/desktop/src/i18n/tr.ts'), 'utf8');
for (const k of [...TOOLBOX.map(g => `tools.g.${g.id}`), ...OWNER_STEPS.map(s => `tools.o.${s.id}`)]) {
expect(en).toContain(`'${k}'`);
expect(tr).toContain(`'${k}'`);
}
});
});