import assert from 'node:assert/strict'; import { readFileSync } from 'node:fs'; import { resolve } from 'node:path'; import test from 'node:test'; function apiFile(path: string): string { return readFileSync(resolve(process.cwd(), path), 'utf8'); } function repoFile(path: string): string { return readFileSync(resolve(process.cwd(), '..', path), 'utf8'); } test('F6.4 Android Installation picker searches live and selects a concrete parent', () => { const screen = repoFile('android-app/app/src/main/java/com/korexlabs/dhinspeccion/ui/ModernVisitRoot.kt'); assert.match(screen, /LaunchedEffect\(modeName, parentSearch\)/); assert.match(screen, /delay\(280\)/); assert.match(screen, /model\.searchInventory\(parentSearch\.trim\(\), visit\.scopeAsset\?\.id\)/); assert.match(screen, /ElevatedCard\(onClick = \{ chooseParent\(item\) \}/); assert.match(screen, /model\.loadFieldTypes\(item\.id\)/); }); test('F6.4 Android field shell uses the modern theme and workspace', () => { const gate = repoFile('android-app/app/src/main/java/com/korexlabs/dhinspeccion/ui/LoginGate.kt'); const theme = repoFile('android-app/app/src/main/java/com/korexlabs/dhinspeccion/ui/DhTheme.kt'); assert.match(gate, /DhTheme \{/); assert.match(gate, /model\.visit != null -> ModernVisitRoot\(model\)/); assert.match(theme, /RoundedCornerShape\(16\.dp\)/); }); test('F6.4 Android Act list uses a mobile read model independent from office reports', () => { const android = repoFile('android-app/app/src/main/java/com/korexlabs/dhinspeccion/data/MobileActs.kt'); const controller = apiFile('src/inspection-acts/mobile-inspection-acts.controller.ts'); const service = apiFile('src/inspection-acts/mobile-inspection-acts.service.ts'); assert.match(android, /inspection-visits\/\{visitId\}\/acts\/mobile/); assert.match(controller, /inspection-visits\/:visitId\/acts\/mobile/); assert.match(service, /FROM inspection_acts act/); assert.doesNotMatch(service, /inspection_reports/); assert.doesNotMatch(service, /inspection_document/); });