// What the webview code may do: only the app commands (no plugin JS API, no network of its own, no eval), no secret // ever passes through it, the catalogs are complete, and the release bundle carries no dev fixture mode. import { describe, expect, test } from 'bun:test'; import { existsSync, readdirSync, readFileSync, statSync } from 'node:fs'; import { join } from 'node:path'; import { checkCatalogs } from '../../../tools/i18n-check'; import { en } from '../src/i18n/en'; import { tr } from '../src/i18n/tr'; const ROOT = join(import.meta.dir, '..'); const src = (f: string) => readFileSync(join(ROOT, f), 'utf8'); function files(dir: string): string[] { return readdirSync(join(ROOT, dir)).flatMap(n => { const rel = join(dir, n); return statSync(join(ROOT, rel)).isDirectory() ? files(rel) : /\.(ts|tsx)$/.test(n) ? [rel] : []; }); } describe('webview rules', () => { test('only app commands: no plugin JS APIs, no direct network, no eval', () => { const all = files('src'); expect(all.length).toBeGreaterThan(20); for (const f of all) { const s = src(f); expect(s, f).not.toMatch(/@tauri-apps\/plugin-/); expect(s, f).not.toMatch(/\bfetch\(|XMLHttpRequest|WebSocket|eval\(|new Function|dangerouslySetInnerHTML|innerHTML\s*=/); // only api.ts talks to the host if (!f.endsWith('api.ts')) expect(s, f).not.toMatch(/@tauri-apps\/api/); } // runtime deps: Tauri's API, React, and the chart (visx) and icon (Phosphor) libraries, all bundled by Vite const deps = Object.keys(JSON.parse(src('package.json')).dependencies).sort(); expect(deps.filter(d => !d.startsWith('@visx/'))).toEqual(['@phosphor-icons/react', '@tauri-apps/api', 'react', 'react-dom']); expect(deps.filter(d => d.startsWith('@visx/')).length).toBeGreaterThan(0); }); test('the confirm token and credentials never appear in the webview code', () => { for (const f of files('src')) expect(src(f), f).not.toMatch(/confirmToken|credentials|PLAY_SERVICE_ACCOUNT|ASC_P8/); }); test('fixtures load only behind import.meta.env.DEV', () => { for (const f of files('src')) { const s = src(f); if (f.includes(`${'dev'}/`)) continue; for (const m of s.matchAll(/import\(['"]\.\/dev\/fixtures['"]\)/g)) { const before = s.slice(Math.max(0, m.index! - 120), m.index!); expect(before, f).toContain('import.meta.env.DEV'); } expect(s, f).not.toMatch(/^import .*dev\/fixtures/m); } }); test('built bundle (when present) has no fixture mode and no dev switches', () => { const dist = join(ROOT, 'dist', 'assets'); // tools/ci.ts runs this file again as "desktop bundle check" after the build; its test shards skip it here if (!existsSync(dist) || process.env.STOREBOUND_SKIP_DIST_CHECK === '1') return; for (const n of readdirSync(dist)) { const s = readFileSync(join(dist, n), 'utf8'); expect(s, n).not.toContain('storebound-dev-fixtures'); expect(s, n).not.toContain('mockInvoke'); expect(s, n).not.toContain('VITE_START'); } }); }); describe('i18n', () => { test('en and tr: same keys, no empty strings, matching placeholders, house style', () => { expect(checkCatalogs(en, { tr })).toEqual([]); }); test('every t() key used in the code exists', () => { const keys = new Set(Object.keys(en)); const missing: string[] = []; for (const f of files('src')) { for (const m of src(f).matchAll(/\bt\('([a-zA-Z0-9_.-]+)'/g)) if (!keys.has(m[1]!)) missing.push(`${f}: ${m[1]}`); } expect(missing).toEqual([]); }); });