@codeyam/codeyam-cli 0.1.0-staging.c276166 → 0.1.0-staging.c85943e
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/analyzer-template/.build-info.json +8 -8
- package/analyzer-template/log.txt +3 -3
- package/analyzer-template/package.json +2 -2
- package/analyzer-template/packages/ai/package.json +1 -1
- package/analyzer-template/packages/aws/package.json +1 -1
- package/analyzer-template/packages/database/package.json +1 -1
- package/analyzer-template/packages/database/src/lib/kysely/tables/editorScenariosTable.ts +42 -16
- package/analyzer-template/packages/github/dist/database/src/lib/kysely/tables/editorScenariosTable.d.ts +3 -1
- package/analyzer-template/packages/github/dist/database/src/lib/kysely/tables/editorScenariosTable.d.ts.map +1 -1
- package/analyzer-template/packages/github/dist/database/src/lib/kysely/tables/editorScenariosTable.js +44 -16
- package/analyzer-template/packages/github/dist/database/src/lib/kysely/tables/editorScenariosTable.js.map +1 -1
- package/codeyam-cli/src/commands/__tests__/editor.stepDispatch.test.js +11 -0
- package/codeyam-cli/src/commands/__tests__/editor.stepDispatch.test.js.map +1 -1
- package/codeyam-cli/src/commands/editor.js +1202 -219
- package/codeyam-cli/src/commands/editor.js.map +1 -1
- package/codeyam-cli/src/commands/init.js +1 -0
- package/codeyam-cli/src/commands/init.js.map +1 -1
- package/codeyam-cli/src/utils/__tests__/editorApi.test.js +18 -8
- package/codeyam-cli/src/utils/__tests__/editorApi.test.js.map +1 -1
- package/codeyam-cli/src/utils/__tests__/editorAudit.test.js +977 -1
- package/codeyam-cli/src/utils/__tests__/editorAudit.test.js.map +1 -1
- package/codeyam-cli/src/utils/__tests__/editorBroadcastViewport.test.js +76 -0
- package/codeyam-cli/src/utils/__tests__/editorBroadcastViewport.test.js.map +1 -0
- package/codeyam-cli/src/utils/__tests__/editorDeleteScenario.test.js +100 -0
- package/codeyam-cli/src/utils/__tests__/editorDeleteScenario.test.js.map +1 -0
- package/codeyam-cli/src/utils/__tests__/editorEntityChangeStatus.test.js +6 -3
- package/codeyam-cli/src/utils/__tests__/editorEntityChangeStatus.test.js.map +1 -1
- package/codeyam-cli/src/utils/__tests__/editorEntityHelpers.test.js +261 -0
- package/codeyam-cli/src/utils/__tests__/editorEntityHelpers.test.js.map +1 -0
- package/codeyam-cli/src/utils/__tests__/editorLoaderHelpers.test.js +75 -1
- package/codeyam-cli/src/utils/__tests__/editorLoaderHelpers.test.js.map +1 -1
- package/codeyam-cli/src/utils/__tests__/editorMigration.test.js +435 -0
- package/codeyam-cli/src/utils/__tests__/editorMigration.test.js.map +1 -0
- package/codeyam-cli/src/utils/__tests__/editorScenarios.test.js +471 -17
- package/codeyam-cli/src/utils/__tests__/editorScenarios.test.js.map +1 -1
- package/codeyam-cli/src/utils/__tests__/editorSeedAdapter.test.js +67 -0
- package/codeyam-cli/src/utils/__tests__/editorSeedAdapter.test.js.map +1 -1
- package/codeyam-cli/src/utils/__tests__/editorSeedAdapterPrismaValidation.test.js +143 -0
- package/codeyam-cli/src/utils/__tests__/editorSeedAdapterPrismaValidation.test.js.map +1 -0
- package/codeyam-cli/src/utils/__tests__/editorSessionFilter.test.js +66 -0
- package/codeyam-cli/src/utils/__tests__/editorSessionFilter.test.js.map +1 -0
- package/codeyam-cli/src/utils/__tests__/editorShouldRevalidate.test.js +53 -0
- package/codeyam-cli/src/utils/__tests__/editorShouldRevalidate.test.js.map +1 -0
- package/codeyam-cli/src/utils/__tests__/entityChangeStatus.test.js +125 -10
- package/codeyam-cli/src/utils/__tests__/entityChangeStatus.test.js.map +1 -1
- package/codeyam-cli/src/utils/__tests__/routePatternMatching.test.js +118 -0
- package/codeyam-cli/src/utils/__tests__/routePatternMatching.test.js.map +1 -0
- package/codeyam-cli/src/utils/__tests__/scenariosManifest.test.js +40 -1
- package/codeyam-cli/src/utils/__tests__/scenariosManifest.test.js.map +1 -1
- package/codeyam-cli/src/utils/__tests__/setupClaudeCodeSettings.test.js +1 -0
- package/codeyam-cli/src/utils/__tests__/setupClaudeCodeSettings.test.js.map +1 -1
- package/codeyam-cli/src/utils/analysisRunner.js +3 -1
- package/codeyam-cli/src/utils/analysisRunner.js.map +1 -1
- package/codeyam-cli/src/utils/editorApi.js +11 -5
- package/codeyam-cli/src/utils/editorApi.js.map +1 -1
- package/codeyam-cli/src/utils/editorAudit.js +167 -0
- package/codeyam-cli/src/utils/editorAudit.js.map +1 -1
- package/codeyam-cli/src/utils/editorBroadcastViewport.js +26 -0
- package/codeyam-cli/src/utils/editorBroadcastViewport.js.map +1 -0
- package/codeyam-cli/src/utils/editorDeleteScenario.js +67 -0
- package/codeyam-cli/src/utils/editorDeleteScenario.js.map +1 -0
- package/codeyam-cli/src/utils/editorEntityChangeStatus.js +1 -1
- package/codeyam-cli/src/utils/editorEntityChangeStatus.js.map +1 -1
- package/codeyam-cli/src/utils/editorEntityHelpers.js +129 -0
- package/codeyam-cli/src/utils/editorEntityHelpers.js.map +1 -0
- package/codeyam-cli/src/utils/editorLoaderHelpers.js +40 -1
- package/codeyam-cli/src/utils/editorLoaderHelpers.js.map +1 -1
- package/codeyam-cli/src/utils/editorMigration.js +224 -0
- package/codeyam-cli/src/utils/editorMigration.js.map +1 -0
- package/codeyam-cli/src/utils/editorScenarios.js +175 -2
- package/codeyam-cli/src/utils/editorScenarios.js.map +1 -1
- package/codeyam-cli/src/utils/editorSeedAdapter.js +253 -4
- package/codeyam-cli/src/utils/editorSeedAdapter.js.map +1 -1
- package/codeyam-cli/src/utils/editorShouldRevalidate.js +21 -0
- package/codeyam-cli/src/utils/editorShouldRevalidate.js.map +1 -0
- package/codeyam-cli/src/utils/entityChangeStatus.js +19 -2
- package/codeyam-cli/src/utils/entityChangeStatus.js.map +1 -1
- package/codeyam-cli/src/utils/entityChangeStatus.server.js +41 -3
- package/codeyam-cli/src/utils/entityChangeStatus.server.js.map +1 -1
- package/codeyam-cli/src/utils/fileWatcher.js +38 -0
- package/codeyam-cli/src/utils/fileWatcher.js.map +1 -1
- package/codeyam-cli/src/utils/install-skills.js +9 -0
- package/codeyam-cli/src/utils/install-skills.js.map +1 -1
- package/codeyam-cli/src/utils/routePatternMatching.js +129 -0
- package/codeyam-cli/src/utils/routePatternMatching.js.map +1 -0
- package/codeyam-cli/src/utils/scenarioCoverage.js +8 -9
- package/codeyam-cli/src/utils/scenarioCoverage.js.map +1 -1
- package/codeyam-cli/src/utils/scenariosManifest.js +18 -10
- package/codeyam-cli/src/utils/scenariosManifest.js.map +1 -1
- package/codeyam-cli/src/utils/setupClaudeCodeSettings.js +1 -0
- package/codeyam-cli/src/utils/setupClaudeCodeSettings.js.map +1 -1
- package/codeyam-cli/src/webserver/__tests__/buildPtyEnv.test.js +35 -0
- package/codeyam-cli/src/webserver/__tests__/buildPtyEnv.test.js.map +1 -0
- package/codeyam-cli/src/webserver/__tests__/editorProxy.test.js +33 -0
- package/codeyam-cli/src/webserver/__tests__/editorProxy.test.js.map +1 -1
- package/codeyam-cli/src/webserver/app/types/editor.js +8 -0
- package/codeyam-cli/src/webserver/app/types/editor.js.map +1 -0
- package/codeyam-cli/src/webserver/backgroundServer.js +18 -4
- package/codeyam-cli/src/webserver/backgroundServer.js.map +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/CopyButton-CzTDWkF2.js +1 -0
- package/codeyam-cli/src/webserver/build/client/assets/{EntityItem-BcgbViKV.js → EntityItem-BFbq6iFk.js} +3 -3
- package/codeyam-cli/src/webserver/build/client/assets/EntityTypeBadge-CQgyEGV-.js +1 -0
- package/codeyam-cli/src/webserver/build/client/assets/{EntityTypeIcon-CQIG2qda.js → EntityTypeIcon-B6OMi58N.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/InlineSpinner-DuYodzo1.js +1 -0
- package/codeyam-cli/src/webserver/build/client/assets/InteractivePreview-CXo9EeCl.js +25 -0
- package/codeyam-cli/src/webserver/build/client/assets/LibraryFunctionPreview-DYCNb2It.js +3 -0
- package/codeyam-cli/src/webserver/build/client/assets/{LoadingDots-BU_OAEMP.js → LoadingDots-By5zI316.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{LogViewer-ceAyBX-H.js → LogViewer-CZgY3sxX.js} +3 -3
- package/codeyam-cli/src/webserver/build/client/assets/{ReportIssueModal-BzHcG7SE.js → ReportIssueModal-CnYYwRDw.js} +2 -2
- package/codeyam-cli/src/webserver/build/client/assets/SafeScreenshot-CDoF7ZpU.js +1 -0
- package/codeyam-cli/src/webserver/build/client/assets/{ScenarioViewer-TSD3C211.js → ScenarioViewer-DrnfvaLL.js} +3 -3
- package/codeyam-cli/src/webserver/build/client/assets/Spinner-Df3UCi8k.js +34 -0
- package/codeyam-cli/src/webserver/build/client/assets/TruncatedFilePath-CK7-NaPZ.js +1 -0
- package/codeyam-cli/src/webserver/build/client/assets/ViewportInspectBar-DRKR9T0U.js +1 -0
- package/codeyam-cli/src/webserver/build/client/assets/{_index-DLxKhri3.js → _index-ClR-g3tY.js} +2 -2
- package/codeyam-cli/src/webserver/build/client/assets/{activity.(_tab)-BcY3q6nt.js → activity.(_tab)-DTH6ydEA.js} +3 -3
- package/codeyam-cli/src/webserver/build/client/assets/{addon-web-links-Duc5hnl7.js → addon-web-links-74hnHF59.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{agent-transcripts-Bni3iiUj.js → agent-transcripts-B8CYhCO9.js} +3 -3
- package/codeyam-cli/src/webserver/build/client/assets/api.editor-rename-scenario-l0sNRNKZ.js +1 -0
- package/codeyam-cli/src/webserver/build/client/assets/api.editor-save-seed-state-l0sNRNKZ.js +1 -0
- package/codeyam-cli/src/webserver/build/client/assets/api.editor-scenario-prompt-l0sNRNKZ.js +1 -0
- package/codeyam-cli/src/webserver/build/client/assets/api.editor-session-l0sNRNKZ.js +1 -0
- package/codeyam-cli/src/webserver/build/client/assets/{book-open-BYOypzCa.js → book-open-CLaoh4ac.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{chevron-down-C_Pmso5S.js → chevron-down-BZ2DZxbW.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{chunk-JZWAC4HX-C4pqxYJB.js → chunk-JZWAC4HX-BBXArFPl.js} +13 -21
- package/codeyam-cli/src/webserver/build/client/assets/{circle-check-BVMi9VA5.js → circle-check-CT4unAk-.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{copy-n2FB0_Sw.js → copy-zK0B6Nu-.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{createLucideIcon-CC6AbExI.js → createLucideIcon-DJB0YQJL.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/dev.empty-CkXFP_i-.js +1 -0
- package/codeyam-cli/src/webserver/build/client/assets/editor._tab-DPw7NZHc.js +1 -0
- package/codeyam-cli/src/webserver/build/client/assets/editor.entity.(_sha)-DmBK1JBK.js +58 -0
- package/codeyam-cli/src/webserver/build/client/assets/editorPreview-DBa7T2FK.js +41 -0
- package/codeyam-cli/src/webserver/build/client/assets/{entity._sha._-DwCV5__E.js → entity._sha._-BqAN7hyG.js} +2 -2
- package/codeyam-cli/src/webserver/build/client/assets/entity._sha.scenarios._scenarioId.dev-BOi8kpwd.js +6 -0
- package/codeyam-cli/src/webserver/build/client/assets/entity._sha.scenarios._scenarioId.fullscreen-Dg1NhIms.js +6 -0
- package/codeyam-cli/src/webserver/build/client/assets/entity._sha_.create-scenario-CJX6kkkV.js +6 -0
- package/codeyam-cli/src/webserver/build/client/assets/{entity._sha_.edit._scenarioId-BMvVHNXU.js → entity._sha_.edit._scenarioId-BhVjZhKg.js} +2 -2
- package/codeyam-cli/src/webserver/build/client/assets/{entry.client-DTvKq3TY.js → entry.client-_gzKltPN.js} +6 -6
- package/codeyam-cli/src/webserver/build/client/assets/fileTableUtils-Daa96Fr1.js +1 -0
- package/codeyam-cli/src/webserver/build/client/assets/files-CV_17tZS.js +1 -0
- package/codeyam-cli/src/webserver/build/client/assets/git-D-YXmMbR.js +1 -0
- package/codeyam-cli/src/webserver/build/client/assets/globals-CGrDAxj0.css +1 -0
- package/codeyam-cli/src/webserver/build/client/assets/{index-yHOVb4rc.js → index-Blo6EK8G.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{index-10oVnAAH.js → index-BsX0F-9C.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{index-BcvgDzbZ.js → index-CCrgCshv.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/jsx-runtime-D_zvdyIk.js +9 -0
- package/codeyam-cli/src/webserver/build/client/assets/labs-Byazq8Pv.js +1 -0
- package/codeyam-cli/src/webserver/build/client/assets/{loader-circle-DaAZ_H2w.js → loader-circle-DVQ0oHR7.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/manifest-b3f77062.js +1 -0
- package/codeyam-cli/src/webserver/build/client/assets/{memory-9gnxSZlb.js → memory-b-VmA2Vj.js} +2 -2
- package/codeyam-cli/src/webserver/build/client/assets/{pause-f5-1lKBt.js → pause-DGcndCAa.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{root-20m9-3-z.js → root-D5Zi3U2Z.js} +4 -4
- package/codeyam-cli/src/webserver/build/client/assets/{search-Di64LWVb.js → search-C0Uw0bcK.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/settings-OoNgHIfW.js +1 -0
- package/codeyam-cli/src/webserver/build/client/assets/simulations-Bcemfu8a.js +1 -0
- package/codeyam-cli/src/webserver/build/client/assets/{terminal-Br7MOqts.js → terminal-BgMmG7R9.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{triangle-alert-BLdiCuG-.js → triangle-alert-Cs87hJYK.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/useCustomSizes-BR3Rs7JY.js +1 -0
- package/codeyam-cli/src/webserver/build/client/assets/{useLastLogLine-C14nCb1q.js → useLastLogLine-BxxP_XF9.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/useReportContext-BermyNU5.js +1 -0
- package/codeyam-cli/src/webserver/build/client/assets/useToast-a_QN_W9_.js +1 -0
- package/codeyam-cli/src/webserver/build/server/assets/analysisRunner-yTyb36j3.js +13 -0
- package/codeyam-cli/src/webserver/build/server/assets/{index-WJ6ysHIA.js → index-Cr7d_IsG.js} +1 -1
- package/codeyam-cli/src/webserver/build/server/assets/init-M_wqNAfu.js +10 -0
- package/codeyam-cli/src/webserver/build/server/assets/progress-CHTtrxFG.js +1 -0
- package/codeyam-cli/src/webserver/build/server/assets/server-build-_ybRgrlc.js +551 -0
- package/codeyam-cli/src/webserver/build/server/index.js +1 -1
- package/codeyam-cli/src/webserver/build-info.json +5 -5
- package/codeyam-cli/src/webserver/editorProxy.js +93 -11
- package/codeyam-cli/src/webserver/editorProxy.js.map +1 -1
- package/codeyam-cli/src/webserver/mockStateEvents.js +28 -0
- package/codeyam-cli/src/webserver/mockStateEvents.js.map +1 -0
- package/codeyam-cli/src/webserver/server.js +52 -43
- package/codeyam-cli/src/webserver/server.js.map +1 -1
- package/codeyam-cli/src/webserver/terminalServer.js +108 -12
- package/codeyam-cli/src/webserver/terminalServer.js.map +1 -1
- package/codeyam-cli/templates/editor-step-hook.py +107 -22
- package/codeyam-cli/templates/nextjs-prisma-sqlite/seed-adapter.ts +42 -7
- package/codeyam-cli/templates/seed-adapters/supabase.ts +282 -0
- package/codeyam-cli/templates/skills/codeyam-editor/SKILL.md +62 -0
- package/package.json +1 -1
- package/packages/database/src/lib/kysely/tables/editorScenariosTable.js +44 -16
- package/packages/database/src/lib/kysely/tables/editorScenariosTable.js.map +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/CopyButton-BPXZwM4t.js +0 -1
- package/codeyam-cli/src/webserver/build/client/assets/EntityTypeBadge-g3saevPb.js +0 -1
- package/codeyam-cli/src/webserver/build/client/assets/InlineSpinner-Bu6c6aDe.js +0 -1
- package/codeyam-cli/src/webserver/build/client/assets/InteractivePreview-DYFW3lDD.js +0 -25
- package/codeyam-cli/src/webserver/build/client/assets/LibraryFunctionPreview-DLeucoVX.js +0 -3
- package/codeyam-cli/src/webserver/build/client/assets/SafeScreenshot-BED4B6sP.js +0 -1
- package/codeyam-cli/src/webserver/build/client/assets/Spinner-Bb5uFQ5V.js +0 -34
- package/codeyam-cli/src/webserver/build/client/assets/TruncatedFilePath-C8OKAR5x.js +0 -1
- package/codeyam-cli/src/webserver/build/client/assets/ViewportInspectBar-oAf2Kqsf.js +0 -1
- package/codeyam-cli/src/webserver/build/client/assets/dev.empty-Ii3inc0_.js +0 -1
- package/codeyam-cli/src/webserver/build/client/assets/editor-CtRVY4nn.js +0 -10
- package/codeyam-cli/src/webserver/build/client/assets/editorPreview-CNB06EIa.js +0 -41
- package/codeyam-cli/src/webserver/build/client/assets/entity._sha.scenarios._scenarioId.dev-CXSi2aeZ.js +0 -6
- package/codeyam-cli/src/webserver/build/client/assets/entity._sha.scenarios._scenarioId.fullscreen-CHMiAog3.js +0 -6
- package/codeyam-cli/src/webserver/build/client/assets/entity._sha_.create-scenario-p9hhkjJM.js +0 -6
- package/codeyam-cli/src/webserver/build/client/assets/fileTableUtils-cPo8LiG3.js +0 -1
- package/codeyam-cli/src/webserver/build/client/assets/files-BZrlFE1F.js +0 -1
- package/codeyam-cli/src/webserver/build/client/assets/git-DdZcvjGh.js +0 -1
- package/codeyam-cli/src/webserver/build/client/assets/globals-DozjVXrE.css +0 -1
- package/codeyam-cli/src/webserver/build/client/assets/labs-Zk7ryIM1.js +0 -1
- package/codeyam-cli/src/webserver/build/client/assets/manifest-b431cb81.js +0 -1
- package/codeyam-cli/src/webserver/build/client/assets/settings-0OrEMU6J.js +0 -1
- package/codeyam-cli/src/webserver/build/client/assets/simulations-DWT-CvLy.js +0 -1
- package/codeyam-cli/src/webserver/build/client/assets/useCustomSizes-C-_hOl_g.js +0 -1
- package/codeyam-cli/src/webserver/build/client/assets/useReportContext-O-jkvSPx.js +0 -1
- package/codeyam-cli/src/webserver/build/client/assets/useToast-9FIWuYfK.js +0 -1
- package/codeyam-cli/src/webserver/build/server/assets/init-3Om_In9B.js +0 -10
- package/codeyam-cli/src/webserver/build/server/assets/server-build-kfIP9lFK.js +0 -439
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import
|
|
1
|
+
import Database from 'better-sqlite3';
|
|
2
|
+
import { Kysely, SqliteDialect } from 'kysely';
|
|
3
|
+
import { isComponent, classifyGlossaryEntries, computeAudit, filterGlossaryByChangeStatus, resolveAuditSessionScope, queryScenarioCounts, queryIncompleteEntities, queryMiscategorizedScenarios, isOnlyIncompleteEntities, } from "../editorAudit.js";
|
|
2
4
|
describe('editorAudit', () => {
|
|
3
5
|
describe('isComponent', () => {
|
|
4
6
|
it('should return true for JSX.Element return type', () => {
|
|
@@ -851,5 +853,979 @@ describe('editorAudit', () => {
|
|
|
851
853
|
});
|
|
852
854
|
});
|
|
853
855
|
});
|
|
856
|
+
// ── queryScenarioCounts ─────────────────────────────────────────────
|
|
857
|
+
describe('queryScenarioCounts', () => {
|
|
858
|
+
let db;
|
|
859
|
+
let rawDb;
|
|
860
|
+
const projectId = 'test-project-id';
|
|
861
|
+
beforeEach(async () => {
|
|
862
|
+
rawDb = new Database(':memory:');
|
|
863
|
+
db = new Kysely({ dialect: new SqliteDialect({ database: rawDb }) });
|
|
864
|
+
await db.schema
|
|
865
|
+
.createTable('editor_scenarios')
|
|
866
|
+
.addColumn('id', 'varchar', (col) => col.primaryKey())
|
|
867
|
+
.addColumn('project_id', 'varchar', (col) => col.notNull())
|
|
868
|
+
.addColumn('name', 'varchar', (col) => col.notNull())
|
|
869
|
+
.addColumn('description', 'text')
|
|
870
|
+
.addColumn('component_name', 'varchar')
|
|
871
|
+
.addColumn('component_path', 'varchar')
|
|
872
|
+
.addColumn('url', 'varchar')
|
|
873
|
+
.addColumn('type', 'varchar')
|
|
874
|
+
.addColumn('screenshot_path', 'varchar')
|
|
875
|
+
.addColumn('viewport_width', 'integer')
|
|
876
|
+
.addColumn('viewport_height', 'integer')
|
|
877
|
+
.addColumn('dimension', 'varchar')
|
|
878
|
+
.addColumn('created_at', 'datetime')
|
|
879
|
+
.addColumn('updated_at', 'datetime')
|
|
880
|
+
.execute();
|
|
881
|
+
});
|
|
882
|
+
afterEach(async () => {
|
|
883
|
+
await db.destroy();
|
|
884
|
+
});
|
|
885
|
+
it('should count all scenarios when featureStartedAt is null', async () => {
|
|
886
|
+
await db
|
|
887
|
+
.insertInto('editor_scenarios')
|
|
888
|
+
.values({
|
|
889
|
+
id: 'sc-1',
|
|
890
|
+
project_id: projectId,
|
|
891
|
+
name: 'ArticleRow - Default',
|
|
892
|
+
component_name: 'ArticleRow',
|
|
893
|
+
viewport_width: 1280,
|
|
894
|
+
viewport_height: 720,
|
|
895
|
+
created_at: '2026-03-12 13:00:00',
|
|
896
|
+
})
|
|
897
|
+
.execute();
|
|
898
|
+
const counts = await queryScenarioCounts(db, projectId, null);
|
|
899
|
+
expect(counts).toEqual({ ArticleRow: 1 });
|
|
900
|
+
});
|
|
901
|
+
it('should count scenarios created after featureStartedAt', async () => {
|
|
902
|
+
await db
|
|
903
|
+
.insertInto('editor_scenarios')
|
|
904
|
+
.values({
|
|
905
|
+
id: 'sc-1',
|
|
906
|
+
project_id: projectId,
|
|
907
|
+
name: 'ArticleRow - Default',
|
|
908
|
+
component_name: 'ArticleRow',
|
|
909
|
+
viewport_width: 1280,
|
|
910
|
+
viewport_height: 720,
|
|
911
|
+
created_at: '2026-03-12 14:30:00',
|
|
912
|
+
})
|
|
913
|
+
.execute();
|
|
914
|
+
const counts = await queryScenarioCounts(db, projectId, '2026-03-12T14:01:31.291Z');
|
|
915
|
+
expect(counts).toEqual({ ArticleRow: 1 });
|
|
916
|
+
});
|
|
917
|
+
it('should exclude scenarios created before featureStartedAt', async () => {
|
|
918
|
+
await db
|
|
919
|
+
.insertInto('editor_scenarios')
|
|
920
|
+
.values({
|
|
921
|
+
id: 'sc-1',
|
|
922
|
+
project_id: projectId,
|
|
923
|
+
name: 'ArticleRow - Default',
|
|
924
|
+
component_name: 'ArticleRow',
|
|
925
|
+
viewport_width: 1280,
|
|
926
|
+
viewport_height: 720,
|
|
927
|
+
created_at: '2026-03-12 13:00:00',
|
|
928
|
+
})
|
|
929
|
+
.execute();
|
|
930
|
+
const counts = await queryScenarioCounts(db, projectId, '2026-03-12T14:01:31.291Z');
|
|
931
|
+
expect(counts).toEqual({});
|
|
932
|
+
});
|
|
933
|
+
it('should count re-registered scenarios whose updated_at is after featureStartedAt', async () => {
|
|
934
|
+
// This is the bug: a scenario from Feature 1 (created_at before featureStartedAt)
|
|
935
|
+
// is re-registered in Feature 2 (updated_at after featureStartedAt).
|
|
936
|
+
// The audit should count it because it was actively re-registered in this session.
|
|
937
|
+
await db
|
|
938
|
+
.insertInto('editor_scenarios')
|
|
939
|
+
.values({
|
|
940
|
+
id: 'sc-1',
|
|
941
|
+
project_id: projectId,
|
|
942
|
+
name: 'ArticleRow - Default',
|
|
943
|
+
component_name: 'ArticleRow',
|
|
944
|
+
viewport_width: 400,
|
|
945
|
+
viewport_height: 600,
|
|
946
|
+
created_at: '2026-03-12 13:28:00', // Feature 1
|
|
947
|
+
updated_at: '2026-03-12 14:32:00', // Re-registered in Feature 2
|
|
948
|
+
})
|
|
949
|
+
.execute();
|
|
950
|
+
const counts = await queryScenarioCounts(db, projectId, '2026-03-12T14:01:31.291Z');
|
|
951
|
+
// Should count because updated_at is after featureStartedAt
|
|
952
|
+
expect(counts).toEqual({ ArticleRow: 1 });
|
|
953
|
+
});
|
|
954
|
+
it('should handle mixed scenarios: some new, some re-registered', async () => {
|
|
955
|
+
// ArticleCard: new in Feature 2 (created_at after featureStartedAt)
|
|
956
|
+
await db
|
|
957
|
+
.insertInto('editor_scenarios')
|
|
958
|
+
.values({
|
|
959
|
+
id: 'sc-1',
|
|
960
|
+
project_id: projectId,
|
|
961
|
+
name: 'ArticleCard - Default',
|
|
962
|
+
component_name: 'ArticleCard',
|
|
963
|
+
viewport_width: 1280,
|
|
964
|
+
viewport_height: 720,
|
|
965
|
+
created_at: '2026-03-12 14:14:00',
|
|
966
|
+
})
|
|
967
|
+
.execute();
|
|
968
|
+
// ArticleRow: from Feature 1, re-registered in Feature 2
|
|
969
|
+
await db
|
|
970
|
+
.insertInto('editor_scenarios')
|
|
971
|
+
.values({
|
|
972
|
+
id: 'sc-2',
|
|
973
|
+
project_id: projectId,
|
|
974
|
+
name: 'ArticleRow - Default',
|
|
975
|
+
component_name: 'ArticleRow',
|
|
976
|
+
viewport_width: 400,
|
|
977
|
+
viewport_height: 600,
|
|
978
|
+
created_at: '2026-03-12 13:28:00', // Feature 1
|
|
979
|
+
updated_at: '2026-03-12 14:32:00', // Re-registered in Feature 2
|
|
980
|
+
})
|
|
981
|
+
.execute();
|
|
982
|
+
const counts = await queryScenarioCounts(db, projectId, '2026-03-12T14:01:31.291Z');
|
|
983
|
+
expect(counts).toEqual({ ArticleCard: 1, ArticleRow: 1 });
|
|
984
|
+
});
|
|
985
|
+
});
|
|
986
|
+
// ── Audit + entity completeness integration ─────────────────────────
|
|
987
|
+
describe('audit should catch incomplete entities (bug reproduction)', () => {
|
|
988
|
+
let db;
|
|
989
|
+
let rawDb;
|
|
990
|
+
const projectId = 'test-project-id';
|
|
991
|
+
beforeEach(async () => {
|
|
992
|
+
rawDb = new Database(':memory:');
|
|
993
|
+
db = new Kysely({ dialect: new SqliteDialect({ database: rawDb }) });
|
|
994
|
+
await db.schema
|
|
995
|
+
.createTable('editor_scenarios')
|
|
996
|
+
.addColumn('id', 'varchar', (col) => col.primaryKey())
|
|
997
|
+
.addColumn('project_id', 'varchar', (col) => col.notNull())
|
|
998
|
+
.addColumn('name', 'varchar', (col) => col.notNull())
|
|
999
|
+
.addColumn('component_name', 'varchar')
|
|
1000
|
+
.addColumn('component_path', 'varchar')
|
|
1001
|
+
.addColumn('entity_sha', 'varchar')
|
|
1002
|
+
.addColumn('display_name', 'varchar')
|
|
1003
|
+
.addColumn('page_file_path', 'varchar')
|
|
1004
|
+
.addColumn('url', 'varchar')
|
|
1005
|
+
.addColumn('created_at', 'datetime')
|
|
1006
|
+
.addColumn('updated_at', 'datetime')
|
|
1007
|
+
.execute();
|
|
1008
|
+
await db.schema
|
|
1009
|
+
.createTable('analyses')
|
|
1010
|
+
.addColumn('id', 'varchar', (col) => col.primaryKey())
|
|
1011
|
+
.addColumn('entity_sha', 'varchar')
|
|
1012
|
+
.addColumn('entity_name', 'varchar')
|
|
1013
|
+
.addColumn('project_id', 'varchar')
|
|
1014
|
+
.execute();
|
|
1015
|
+
await db.schema
|
|
1016
|
+
.createTable('entities')
|
|
1017
|
+
.addColumn('sha', 'varchar', (col) => col.primaryKey())
|
|
1018
|
+
.addColumn('name', 'varchar')
|
|
1019
|
+
.addColumn('entity_type', 'varchar')
|
|
1020
|
+
.addColumn('file_path', 'varchar')
|
|
1021
|
+
.execute();
|
|
1022
|
+
});
|
|
1023
|
+
afterEach(async () => {
|
|
1024
|
+
await db.destroy();
|
|
1025
|
+
});
|
|
1026
|
+
it('demonstrates the bug: computeAudit passes but entities are incomplete', async () => {
|
|
1027
|
+
// Setup: Two components in glossary, both have scenarios registered.
|
|
1028
|
+
// CollectionChips has scenarios but NO analysis records — it's "incomplete."
|
|
1029
|
+
// The glossary-based audit (computeAudit) doesn't know about entity analyses
|
|
1030
|
+
// so it says allPassing: true. This is the bug.
|
|
1031
|
+
// Entities in DB
|
|
1032
|
+
await db
|
|
1033
|
+
.insertInto('entities')
|
|
1034
|
+
.values({
|
|
1035
|
+
sha: 'sha-header',
|
|
1036
|
+
name: 'Header',
|
|
1037
|
+
entity_type: 'visual',
|
|
1038
|
+
file_path: 'src/components/Header.tsx',
|
|
1039
|
+
})
|
|
1040
|
+
.execute();
|
|
1041
|
+
await db
|
|
1042
|
+
.insertInto('entities')
|
|
1043
|
+
.values({
|
|
1044
|
+
sha: 'sha-chips',
|
|
1045
|
+
name: 'CollectionChips',
|
|
1046
|
+
entity_type: 'visual',
|
|
1047
|
+
file_path: 'src/components/CollectionChips.tsx',
|
|
1048
|
+
})
|
|
1049
|
+
.execute();
|
|
1050
|
+
// Header has an analysis — it's complete
|
|
1051
|
+
await db
|
|
1052
|
+
.insertInto('analyses')
|
|
1053
|
+
.values({
|
|
1054
|
+
id: 'a-1',
|
|
1055
|
+
entity_sha: 'sha-header',
|
|
1056
|
+
entity_name: 'Header',
|
|
1057
|
+
project_id: projectId,
|
|
1058
|
+
})
|
|
1059
|
+
.execute();
|
|
1060
|
+
// CollectionChips has NO analysis — it's incomplete
|
|
1061
|
+
// Both have scenarios
|
|
1062
|
+
await db
|
|
1063
|
+
.insertInto('editor_scenarios')
|
|
1064
|
+
.values({
|
|
1065
|
+
id: 'sc-1',
|
|
1066
|
+
project_id: projectId,
|
|
1067
|
+
name: 'Header - Default',
|
|
1068
|
+
component_name: 'Header',
|
|
1069
|
+
entity_sha: 'sha-header',
|
|
1070
|
+
created_at: '2026-03-16 23:00:00',
|
|
1071
|
+
})
|
|
1072
|
+
.execute();
|
|
1073
|
+
await db
|
|
1074
|
+
.insertInto('editor_scenarios')
|
|
1075
|
+
.values({
|
|
1076
|
+
id: 'sc-2',
|
|
1077
|
+
project_id: projectId,
|
|
1078
|
+
name: 'CollectionChips - Default',
|
|
1079
|
+
component_name: 'CollectionChips',
|
|
1080
|
+
entity_sha: 'sha-chips',
|
|
1081
|
+
created_at: '2026-03-16 23:19:00',
|
|
1082
|
+
})
|
|
1083
|
+
.execute();
|
|
1084
|
+
await db
|
|
1085
|
+
.insertInto('editor_scenarios')
|
|
1086
|
+
.values({
|
|
1087
|
+
id: 'sc-3',
|
|
1088
|
+
project_id: projectId,
|
|
1089
|
+
name: 'CollectionChips - Many',
|
|
1090
|
+
component_name: 'CollectionChips',
|
|
1091
|
+
entity_sha: 'sha-chips',
|
|
1092
|
+
created_at: '2026-03-16 23:19:05',
|
|
1093
|
+
})
|
|
1094
|
+
.execute();
|
|
1095
|
+
// The glossary says both are components with scenarios
|
|
1096
|
+
const scenarioCounts = await queryScenarioCounts(db, projectId, null);
|
|
1097
|
+
expect(scenarioCounts).toEqual({ Header: 1, CollectionChips: 2 });
|
|
1098
|
+
// computeAudit only checks glossary coverage — it passes!
|
|
1099
|
+
const auditResult = computeAudit({
|
|
1100
|
+
components: [
|
|
1101
|
+
{
|
|
1102
|
+
name: 'Header',
|
|
1103
|
+
filePath: 'src/components/Header.tsx',
|
|
1104
|
+
returnType: 'JSX.Element',
|
|
1105
|
+
},
|
|
1106
|
+
{
|
|
1107
|
+
name: 'CollectionChips',
|
|
1108
|
+
filePath: 'src/components/CollectionChips.tsx',
|
|
1109
|
+
returnType: 'JSX.Element',
|
|
1110
|
+
},
|
|
1111
|
+
],
|
|
1112
|
+
functions: [],
|
|
1113
|
+
scenarioCounts,
|
|
1114
|
+
testFileExistence: {},
|
|
1115
|
+
});
|
|
1116
|
+
// BUG: computeAudit alone says everything is fine
|
|
1117
|
+
expect(auditResult.summary.allPassing).toBe(true);
|
|
1118
|
+
expect(auditResult.summary.componentsOk).toBe(2);
|
|
1119
|
+
// But queryIncompleteEntities catches the real issue
|
|
1120
|
+
const incomplete = await queryIncompleteEntities(db, projectId, null);
|
|
1121
|
+
expect(incomplete).toHaveLength(1);
|
|
1122
|
+
expect(incomplete[0].name).toBe('CollectionChips');
|
|
1123
|
+
expect(incomplete[0].scenarioCount).toBe(2);
|
|
1124
|
+
});
|
|
1125
|
+
it('audit should fail when combining computeAudit with incomplete entity check', async () => {
|
|
1126
|
+
// Same setup as above — this test shows the FIX working:
|
|
1127
|
+
// after computeAudit, we also check queryIncompleteEntities,
|
|
1128
|
+
// and if any are found, allPassing becomes false.
|
|
1129
|
+
await db
|
|
1130
|
+
.insertInto('entities')
|
|
1131
|
+
.values({
|
|
1132
|
+
sha: 'sha-header',
|
|
1133
|
+
name: 'Header',
|
|
1134
|
+
entity_type: 'visual',
|
|
1135
|
+
file_path: 'src/components/Header.tsx',
|
|
1136
|
+
})
|
|
1137
|
+
.execute();
|
|
1138
|
+
await db
|
|
1139
|
+
.insertInto('entities')
|
|
1140
|
+
.values({
|
|
1141
|
+
sha: 'sha-chips',
|
|
1142
|
+
name: 'CollectionChips',
|
|
1143
|
+
entity_type: 'visual',
|
|
1144
|
+
file_path: 'src/components/CollectionChips.tsx',
|
|
1145
|
+
})
|
|
1146
|
+
.execute();
|
|
1147
|
+
await db
|
|
1148
|
+
.insertInto('analyses')
|
|
1149
|
+
.values({
|
|
1150
|
+
id: 'a-1',
|
|
1151
|
+
entity_sha: 'sha-header',
|
|
1152
|
+
entity_name: 'Header',
|
|
1153
|
+
project_id: projectId,
|
|
1154
|
+
})
|
|
1155
|
+
.execute();
|
|
1156
|
+
await db
|
|
1157
|
+
.insertInto('editor_scenarios')
|
|
1158
|
+
.values({
|
|
1159
|
+
id: 'sc-1',
|
|
1160
|
+
project_id: projectId,
|
|
1161
|
+
name: 'Header - Default',
|
|
1162
|
+
component_name: 'Header',
|
|
1163
|
+
entity_sha: 'sha-header',
|
|
1164
|
+
created_at: '2026-03-16 23:00:00',
|
|
1165
|
+
})
|
|
1166
|
+
.execute();
|
|
1167
|
+
await db
|
|
1168
|
+
.insertInto('editor_scenarios')
|
|
1169
|
+
.values({
|
|
1170
|
+
id: 'sc-2',
|
|
1171
|
+
project_id: projectId,
|
|
1172
|
+
name: 'CollectionChips - Default',
|
|
1173
|
+
component_name: 'CollectionChips',
|
|
1174
|
+
entity_sha: 'sha-chips',
|
|
1175
|
+
created_at: '2026-03-16 23:19:00',
|
|
1176
|
+
})
|
|
1177
|
+
.execute();
|
|
1178
|
+
const scenarioCounts = await queryScenarioCounts(db, projectId, null);
|
|
1179
|
+
const auditResult = computeAudit({
|
|
1180
|
+
components: [
|
|
1181
|
+
{
|
|
1182
|
+
name: 'Header',
|
|
1183
|
+
filePath: 'src/components/Header.tsx',
|
|
1184
|
+
returnType: 'JSX.Element',
|
|
1185
|
+
},
|
|
1186
|
+
{
|
|
1187
|
+
name: 'CollectionChips',
|
|
1188
|
+
filePath: 'src/components/CollectionChips.tsx',
|
|
1189
|
+
returnType: 'JSX.Element',
|
|
1190
|
+
},
|
|
1191
|
+
],
|
|
1192
|
+
functions: [],
|
|
1193
|
+
scenarioCounts,
|
|
1194
|
+
testFileExistence: {},
|
|
1195
|
+
});
|
|
1196
|
+
// Apply the same post-processing the audit endpoint does
|
|
1197
|
+
const incomplete = await queryIncompleteEntities(db, projectId, null);
|
|
1198
|
+
if (incomplete.length > 0) {
|
|
1199
|
+
auditResult.summary.allPassing = false;
|
|
1200
|
+
auditResult.summary.incompleteEntities = incomplete.length;
|
|
1201
|
+
}
|
|
1202
|
+
// NOW the audit correctly fails
|
|
1203
|
+
expect(auditResult.summary.allPassing).toBe(false);
|
|
1204
|
+
expect(auditResult.summary.incompleteEntities).toBe(1);
|
|
1205
|
+
});
|
|
1206
|
+
it('audit should pass when all entities have analyses', async () => {
|
|
1207
|
+
// Both entities have analyses — everything is complete
|
|
1208
|
+
await db
|
|
1209
|
+
.insertInto('entities')
|
|
1210
|
+
.values({
|
|
1211
|
+
sha: 'sha-header',
|
|
1212
|
+
name: 'Header',
|
|
1213
|
+
entity_type: 'visual',
|
|
1214
|
+
file_path: 'src/components/Header.tsx',
|
|
1215
|
+
})
|
|
1216
|
+
.execute();
|
|
1217
|
+
await db
|
|
1218
|
+
.insertInto('entities')
|
|
1219
|
+
.values({
|
|
1220
|
+
sha: 'sha-chips',
|
|
1221
|
+
name: 'CollectionChips',
|
|
1222
|
+
entity_type: 'visual',
|
|
1223
|
+
file_path: 'src/components/CollectionChips.tsx',
|
|
1224
|
+
})
|
|
1225
|
+
.execute();
|
|
1226
|
+
await db
|
|
1227
|
+
.insertInto('analyses')
|
|
1228
|
+
.values({
|
|
1229
|
+
id: 'a-1',
|
|
1230
|
+
entity_sha: 'sha-header',
|
|
1231
|
+
entity_name: 'Header',
|
|
1232
|
+
project_id: projectId,
|
|
1233
|
+
})
|
|
1234
|
+
.execute();
|
|
1235
|
+
await db
|
|
1236
|
+
.insertInto('analyses')
|
|
1237
|
+
.values({
|
|
1238
|
+
id: 'a-2',
|
|
1239
|
+
entity_sha: 'sha-chips',
|
|
1240
|
+
entity_name: 'CollectionChips',
|
|
1241
|
+
project_id: projectId,
|
|
1242
|
+
})
|
|
1243
|
+
.execute();
|
|
1244
|
+
await db
|
|
1245
|
+
.insertInto('editor_scenarios')
|
|
1246
|
+
.values({
|
|
1247
|
+
id: 'sc-1',
|
|
1248
|
+
project_id: projectId,
|
|
1249
|
+
name: 'Header - Default',
|
|
1250
|
+
component_name: 'Header',
|
|
1251
|
+
entity_sha: 'sha-header',
|
|
1252
|
+
created_at: '2026-03-16 23:00:00',
|
|
1253
|
+
})
|
|
1254
|
+
.execute();
|
|
1255
|
+
await db
|
|
1256
|
+
.insertInto('editor_scenarios')
|
|
1257
|
+
.values({
|
|
1258
|
+
id: 'sc-2',
|
|
1259
|
+
project_id: projectId,
|
|
1260
|
+
name: 'CollectionChips - Default',
|
|
1261
|
+
component_name: 'CollectionChips',
|
|
1262
|
+
entity_sha: 'sha-chips',
|
|
1263
|
+
created_at: '2026-03-16 23:19:00',
|
|
1264
|
+
})
|
|
1265
|
+
.execute();
|
|
1266
|
+
const scenarioCounts = await queryScenarioCounts(db, projectId, null);
|
|
1267
|
+
const auditResult = computeAudit({
|
|
1268
|
+
components: [
|
|
1269
|
+
{
|
|
1270
|
+
name: 'Header',
|
|
1271
|
+
filePath: 'src/components/Header.tsx',
|
|
1272
|
+
returnType: 'JSX.Element',
|
|
1273
|
+
},
|
|
1274
|
+
{
|
|
1275
|
+
name: 'CollectionChips',
|
|
1276
|
+
filePath: 'src/components/CollectionChips.tsx',
|
|
1277
|
+
returnType: 'JSX.Element',
|
|
1278
|
+
},
|
|
1279
|
+
],
|
|
1280
|
+
functions: [],
|
|
1281
|
+
scenarioCounts,
|
|
1282
|
+
testFileExistence: {},
|
|
1283
|
+
});
|
|
1284
|
+
const incomplete = await queryIncompleteEntities(db, projectId, null);
|
|
1285
|
+
if (incomplete.length > 0) {
|
|
1286
|
+
auditResult.summary.allPassing = false;
|
|
1287
|
+
auditResult.summary.incompleteEntities = incomplete.length;
|
|
1288
|
+
}
|
|
1289
|
+
// Everything complete — audit passes
|
|
1290
|
+
expect(auditResult.summary.allPassing).toBe(true);
|
|
1291
|
+
expect(auditResult.summary.incompleteEntities).toBeUndefined();
|
|
1292
|
+
});
|
|
1293
|
+
});
|
|
1294
|
+
// ── queryMiscategorizedScenarios ─────────────────────────────────────
|
|
1295
|
+
describe('queryMiscategorizedScenarios', () => {
|
|
1296
|
+
let db;
|
|
1297
|
+
let rawDb;
|
|
1298
|
+
const projectId = 'test-project-id';
|
|
1299
|
+
beforeEach(async () => {
|
|
1300
|
+
rawDb = new Database(':memory:');
|
|
1301
|
+
db = new Kysely({ dialect: new SqliteDialect({ database: rawDb }) });
|
|
1302
|
+
await db.schema
|
|
1303
|
+
.createTable('editor_scenarios')
|
|
1304
|
+
.addColumn('id', 'varchar', (col) => col.primaryKey())
|
|
1305
|
+
.addColumn('project_id', 'varchar', (col) => col.notNull())
|
|
1306
|
+
.addColumn('name', 'varchar', (col) => col.notNull())
|
|
1307
|
+
.addColumn('component_name', 'varchar')
|
|
1308
|
+
.addColumn('component_path', 'varchar')
|
|
1309
|
+
.addColumn('entity_sha', 'varchar')
|
|
1310
|
+
.addColumn('display_name', 'varchar')
|
|
1311
|
+
.addColumn('page_file_path', 'varchar')
|
|
1312
|
+
.addColumn('url', 'varchar')
|
|
1313
|
+
.addColumn('created_at', 'datetime')
|
|
1314
|
+
.addColumn('updated_at', 'datetime')
|
|
1315
|
+
.execute();
|
|
1316
|
+
});
|
|
1317
|
+
afterEach(async () => {
|
|
1318
|
+
await db.destroy();
|
|
1319
|
+
});
|
|
1320
|
+
it('should return empty when all component scenarios use isolation routes', async () => {
|
|
1321
|
+
await db
|
|
1322
|
+
.insertInto('editor_scenarios')
|
|
1323
|
+
.values({
|
|
1324
|
+
id: 'sc-1',
|
|
1325
|
+
project_id: projectId,
|
|
1326
|
+
name: 'LibraryCard - Default',
|
|
1327
|
+
component_name: 'LibraryCard',
|
|
1328
|
+
url: '/isolated-components/LibraryCard?s=Default',
|
|
1329
|
+
created_at: '2026-03-17 12:00:00',
|
|
1330
|
+
})
|
|
1331
|
+
.execute();
|
|
1332
|
+
const result = await queryMiscategorizedScenarios(db, projectId, null);
|
|
1333
|
+
expect(result).toEqual([]);
|
|
1334
|
+
});
|
|
1335
|
+
it('should flag component scenarios that use non-isolation URLs', async () => {
|
|
1336
|
+
// This is the bug: "Full Library Page" registered as component_name=LibraryPage
|
|
1337
|
+
// but url=/library — it's pointing at the real page, not an isolation route
|
|
1338
|
+
await db
|
|
1339
|
+
.insertInto('editor_scenarios')
|
|
1340
|
+
.values({
|
|
1341
|
+
id: 'sc-1',
|
|
1342
|
+
project_id: projectId,
|
|
1343
|
+
name: 'Full Library Page',
|
|
1344
|
+
component_name: 'LibraryPage',
|
|
1345
|
+
url: '/library',
|
|
1346
|
+
created_at: '2026-03-17 12:41:40',
|
|
1347
|
+
})
|
|
1348
|
+
.execute();
|
|
1349
|
+
await db
|
|
1350
|
+
.insertInto('editor_scenarios')
|
|
1351
|
+
.values({
|
|
1352
|
+
id: 'sc-2',
|
|
1353
|
+
project_id: projectId,
|
|
1354
|
+
name: 'Empty Library Page',
|
|
1355
|
+
component_name: 'LibraryPage',
|
|
1356
|
+
url: '/library',
|
|
1357
|
+
created_at: '2026-03-17 12:41:51',
|
|
1358
|
+
})
|
|
1359
|
+
.execute();
|
|
1360
|
+
const result = await queryMiscategorizedScenarios(db, projectId, null);
|
|
1361
|
+
expect(result).toEqual([
|
|
1362
|
+
{
|
|
1363
|
+
componentName: 'LibraryPage',
|
|
1364
|
+
scenarioNames: ['Full Library Page', 'Empty Library Page'],
|
|
1365
|
+
url: '/library',
|
|
1366
|
+
},
|
|
1367
|
+
]);
|
|
1368
|
+
});
|
|
1369
|
+
it('should not flag page-level scenarios (no component_name)', async () => {
|
|
1370
|
+
// App-level scenarios have no component_name — they're fine with real URLs
|
|
1371
|
+
await db
|
|
1372
|
+
.insertInto('editor_scenarios')
|
|
1373
|
+
.values({
|
|
1374
|
+
id: 'sc-1',
|
|
1375
|
+
project_id: projectId,
|
|
1376
|
+
name: 'Library with Articles',
|
|
1377
|
+
url: '/',
|
|
1378
|
+
created_at: '2026-03-17 12:25:14',
|
|
1379
|
+
})
|
|
1380
|
+
.execute();
|
|
1381
|
+
const result = await queryMiscategorizedScenarios(db, projectId, null);
|
|
1382
|
+
expect(result).toEqual([]);
|
|
1383
|
+
});
|
|
1384
|
+
it('should group miscategorized scenarios by component and URL', async () => {
|
|
1385
|
+
// Two different components both misusing real URLs
|
|
1386
|
+
await db
|
|
1387
|
+
.insertInto('editor_scenarios')
|
|
1388
|
+
.values({
|
|
1389
|
+
id: 'sc-1',
|
|
1390
|
+
project_id: projectId,
|
|
1391
|
+
name: 'Full Library Page',
|
|
1392
|
+
component_name: 'LibraryPage',
|
|
1393
|
+
url: '/library',
|
|
1394
|
+
created_at: '2026-03-17 12:41:40',
|
|
1395
|
+
})
|
|
1396
|
+
.execute();
|
|
1397
|
+
await db
|
|
1398
|
+
.insertInto('editor_scenarios')
|
|
1399
|
+
.values({
|
|
1400
|
+
id: 'sc-2',
|
|
1401
|
+
project_id: projectId,
|
|
1402
|
+
name: 'Dashboard - Full',
|
|
1403
|
+
component_name: 'Dashboard',
|
|
1404
|
+
url: '/dashboard',
|
|
1405
|
+
created_at: '2026-03-17 12:50:00',
|
|
1406
|
+
})
|
|
1407
|
+
.execute();
|
|
1408
|
+
const result = await queryMiscategorizedScenarios(db, projectId, null);
|
|
1409
|
+
expect(result).toHaveLength(2);
|
|
1410
|
+
expect(result.map((r) => r.componentName).sort()).toEqual([
|
|
1411
|
+
'Dashboard',
|
|
1412
|
+
'LibraryPage',
|
|
1413
|
+
]);
|
|
1414
|
+
});
|
|
1415
|
+
it('should scope to session when featureStartedAt is provided', async () => {
|
|
1416
|
+
// Old miscategorized scenario — before session
|
|
1417
|
+
await db
|
|
1418
|
+
.insertInto('editor_scenarios')
|
|
1419
|
+
.values({
|
|
1420
|
+
id: 'sc-old',
|
|
1421
|
+
project_id: projectId,
|
|
1422
|
+
name: 'Old Page',
|
|
1423
|
+
component_name: 'OldComponent',
|
|
1424
|
+
url: '/old',
|
|
1425
|
+
created_at: '2026-03-16 10:00:00',
|
|
1426
|
+
})
|
|
1427
|
+
.execute();
|
|
1428
|
+
// New miscategorized scenario — in session
|
|
1429
|
+
await db
|
|
1430
|
+
.insertInto('editor_scenarios')
|
|
1431
|
+
.values({
|
|
1432
|
+
id: 'sc-new',
|
|
1433
|
+
project_id: projectId,
|
|
1434
|
+
name: 'Full Library Page',
|
|
1435
|
+
component_name: 'LibraryPage',
|
|
1436
|
+
url: '/library',
|
|
1437
|
+
created_at: '2026-03-17 12:41:40',
|
|
1438
|
+
})
|
|
1439
|
+
.execute();
|
|
1440
|
+
const result = await queryMiscategorizedScenarios(db, projectId, '2026-03-17T11:58:55.562Z');
|
|
1441
|
+
expect(result).toHaveLength(1);
|
|
1442
|
+
expect(result[0].componentName).toBe('LibraryPage');
|
|
1443
|
+
});
|
|
1444
|
+
it('should not flag component scenarios with null URL', async () => {
|
|
1445
|
+
await db
|
|
1446
|
+
.insertInto('editor_scenarios')
|
|
1447
|
+
.values({
|
|
1448
|
+
id: 'sc-1',
|
|
1449
|
+
project_id: projectId,
|
|
1450
|
+
name: 'NoUrl - Default',
|
|
1451
|
+
component_name: 'NoUrl',
|
|
1452
|
+
created_at: '2026-03-17 12:00:00',
|
|
1453
|
+
})
|
|
1454
|
+
.execute();
|
|
1455
|
+
const result = await queryMiscategorizedScenarios(db, projectId, null);
|
|
1456
|
+
expect(result).toEqual([]);
|
|
1457
|
+
});
|
|
1458
|
+
});
|
|
1459
|
+
// ── isOnlyIncompleteEntities ─────────────────────────────────────────
|
|
1460
|
+
describe('isOnlyIncompleteEntities', () => {
|
|
1461
|
+
it('should return true when incompleteEntities is the only failure', () => {
|
|
1462
|
+
expect(isOnlyIncompleteEntities({
|
|
1463
|
+
componentsMissing: 0,
|
|
1464
|
+
componentsWithErrors: 0,
|
|
1465
|
+
functionsFailing: 0,
|
|
1466
|
+
functionsNameMismatch: 0,
|
|
1467
|
+
functionsMissing: 0,
|
|
1468
|
+
missingFromGlossary: 0,
|
|
1469
|
+
incompleteEntities: 3,
|
|
1470
|
+
allPassing: false,
|
|
1471
|
+
})).toBe(true);
|
|
1472
|
+
});
|
|
1473
|
+
it('should return false when there are also missing components', () => {
|
|
1474
|
+
expect(isOnlyIncompleteEntities({
|
|
1475
|
+
componentsMissing: 1,
|
|
1476
|
+
componentsWithErrors: 0,
|
|
1477
|
+
functionsFailing: 0,
|
|
1478
|
+
functionsNameMismatch: 0,
|
|
1479
|
+
functionsMissing: 0,
|
|
1480
|
+
missingFromGlossary: 0,
|
|
1481
|
+
incompleteEntities: 2,
|
|
1482
|
+
allPassing: false,
|
|
1483
|
+
})).toBe(false);
|
|
1484
|
+
});
|
|
1485
|
+
it('should return false when there are also failing tests', () => {
|
|
1486
|
+
expect(isOnlyIncompleteEntities({
|
|
1487
|
+
componentsMissing: 0,
|
|
1488
|
+
componentsWithErrors: 0,
|
|
1489
|
+
functionsFailing: 1,
|
|
1490
|
+
functionsNameMismatch: 0,
|
|
1491
|
+
functionsMissing: 0,
|
|
1492
|
+
missingFromGlossary: 0,
|
|
1493
|
+
incompleteEntities: 2,
|
|
1494
|
+
allPassing: false,
|
|
1495
|
+
})).toBe(false);
|
|
1496
|
+
});
|
|
1497
|
+
it('should return false when there are also missing glossary entries', () => {
|
|
1498
|
+
expect(isOnlyIncompleteEntities({
|
|
1499
|
+
componentsMissing: 0,
|
|
1500
|
+
componentsWithErrors: 0,
|
|
1501
|
+
functionsFailing: 0,
|
|
1502
|
+
functionsNameMismatch: 0,
|
|
1503
|
+
functionsMissing: 0,
|
|
1504
|
+
missingFromGlossary: 1,
|
|
1505
|
+
incompleteEntities: 2,
|
|
1506
|
+
allPassing: false,
|
|
1507
|
+
})).toBe(false);
|
|
1508
|
+
});
|
|
1509
|
+
it('should return true even when incompleteEntities is 0 (no failures at all)', () => {
|
|
1510
|
+
// Edge case: all zeros means nothing is failing
|
|
1511
|
+
expect(isOnlyIncompleteEntities({
|
|
1512
|
+
componentsMissing: 0,
|
|
1513
|
+
componentsWithErrors: 0,
|
|
1514
|
+
functionsFailing: 0,
|
|
1515
|
+
functionsNameMismatch: 0,
|
|
1516
|
+
functionsMissing: 0,
|
|
1517
|
+
missingFromGlossary: 0,
|
|
1518
|
+
incompleteEntities: 0,
|
|
1519
|
+
allPassing: true,
|
|
1520
|
+
})).toBe(true);
|
|
1521
|
+
});
|
|
1522
|
+
it('should return false when there are also miscategorized scenarios', () => {
|
|
1523
|
+
expect(isOnlyIncompleteEntities({
|
|
1524
|
+
componentsMissing: 0,
|
|
1525
|
+
componentsWithErrors: 0,
|
|
1526
|
+
functionsFailing: 0,
|
|
1527
|
+
functionsNameMismatch: 0,
|
|
1528
|
+
functionsMissing: 0,
|
|
1529
|
+
missingFromGlossary: 0,
|
|
1530
|
+
miscategorizedScenarios: 1,
|
|
1531
|
+
incompleteEntities: 2,
|
|
1532
|
+
allPassing: false,
|
|
1533
|
+
})).toBe(false);
|
|
1534
|
+
});
|
|
1535
|
+
it('should handle missing fields gracefully', () => {
|
|
1536
|
+
// Summary from older API version might not have all fields
|
|
1537
|
+
expect(isOnlyIncompleteEntities({
|
|
1538
|
+
incompleteEntities: 2,
|
|
1539
|
+
allPassing: false,
|
|
1540
|
+
})).toBe(true);
|
|
1541
|
+
});
|
|
1542
|
+
});
|
|
1543
|
+
// ── queryIncompleteEntities ─────────────────────────────────────────
|
|
1544
|
+
describe('queryIncompleteEntities', () => {
|
|
1545
|
+
let db;
|
|
1546
|
+
let rawDb;
|
|
1547
|
+
const projectId = 'test-project-id';
|
|
1548
|
+
beforeEach(async () => {
|
|
1549
|
+
rawDb = new Database(':memory:');
|
|
1550
|
+
db = new Kysely({ dialect: new SqliteDialect({ database: rawDb }) });
|
|
1551
|
+
await db.schema
|
|
1552
|
+
.createTable('editor_scenarios')
|
|
1553
|
+
.addColumn('id', 'varchar', (col) => col.primaryKey())
|
|
1554
|
+
.addColumn('project_id', 'varchar', (col) => col.notNull())
|
|
1555
|
+
.addColumn('name', 'varchar', (col) => col.notNull())
|
|
1556
|
+
.addColumn('component_name', 'varchar')
|
|
1557
|
+
.addColumn('component_path', 'varchar')
|
|
1558
|
+
.addColumn('entity_sha', 'varchar')
|
|
1559
|
+
.addColumn('display_name', 'varchar')
|
|
1560
|
+
.addColumn('page_file_path', 'varchar')
|
|
1561
|
+
.addColumn('url', 'varchar')
|
|
1562
|
+
.addColumn('created_at', 'datetime')
|
|
1563
|
+
.addColumn('updated_at', 'datetime')
|
|
1564
|
+
.execute();
|
|
1565
|
+
await db.schema
|
|
1566
|
+
.createTable('analyses')
|
|
1567
|
+
.addColumn('id', 'varchar', (col) => col.primaryKey())
|
|
1568
|
+
.addColumn('entity_sha', 'varchar')
|
|
1569
|
+
.addColumn('entity_name', 'varchar')
|
|
1570
|
+
.addColumn('project_id', 'varchar')
|
|
1571
|
+
.execute();
|
|
1572
|
+
await db.schema
|
|
1573
|
+
.createTable('entities')
|
|
1574
|
+
.addColumn('sha', 'varchar', (col) => col.primaryKey())
|
|
1575
|
+
.addColumn('name', 'varchar')
|
|
1576
|
+
.addColumn('entity_type', 'varchar')
|
|
1577
|
+
.addColumn('file_path', 'varchar')
|
|
1578
|
+
.execute();
|
|
1579
|
+
});
|
|
1580
|
+
afterEach(async () => {
|
|
1581
|
+
await db.destroy();
|
|
1582
|
+
});
|
|
1583
|
+
it('should return empty when all scenario entity SHAs have analyses', async () => {
|
|
1584
|
+
// Entity with analysis
|
|
1585
|
+
await db
|
|
1586
|
+
.insertInto('entities')
|
|
1587
|
+
.values({
|
|
1588
|
+
sha: 'sha-header',
|
|
1589
|
+
name: 'Header',
|
|
1590
|
+
entity_type: 'visual',
|
|
1591
|
+
file_path: 'src/Header.tsx',
|
|
1592
|
+
})
|
|
1593
|
+
.execute();
|
|
1594
|
+
await db
|
|
1595
|
+
.insertInto('analyses')
|
|
1596
|
+
.values({
|
|
1597
|
+
id: 'a-1',
|
|
1598
|
+
entity_sha: 'sha-header',
|
|
1599
|
+
entity_name: 'Header',
|
|
1600
|
+
project_id: projectId,
|
|
1601
|
+
})
|
|
1602
|
+
.execute();
|
|
1603
|
+
await db
|
|
1604
|
+
.insertInto('editor_scenarios')
|
|
1605
|
+
.values({
|
|
1606
|
+
id: 'sc-1',
|
|
1607
|
+
project_id: projectId,
|
|
1608
|
+
name: 'Header - Default',
|
|
1609
|
+
component_name: 'Header',
|
|
1610
|
+
entity_sha: 'sha-header',
|
|
1611
|
+
created_at: '2026-03-16 23:00:00',
|
|
1612
|
+
})
|
|
1613
|
+
.execute();
|
|
1614
|
+
const result = await queryIncompleteEntities(db, projectId, null);
|
|
1615
|
+
expect(result).toEqual([]);
|
|
1616
|
+
});
|
|
1617
|
+
it('should return entities with scenarios but no analyses', async () => {
|
|
1618
|
+
// Entity WITHOUT analysis
|
|
1619
|
+
await db
|
|
1620
|
+
.insertInto('entities')
|
|
1621
|
+
.values({
|
|
1622
|
+
sha: 'sha-chips',
|
|
1623
|
+
name: 'CollectionChips',
|
|
1624
|
+
entity_type: 'visual',
|
|
1625
|
+
file_path: 'src/components/CollectionChips.tsx',
|
|
1626
|
+
})
|
|
1627
|
+
.execute();
|
|
1628
|
+
// Scenario referencing that entity
|
|
1629
|
+
await db
|
|
1630
|
+
.insertInto('editor_scenarios')
|
|
1631
|
+
.values({
|
|
1632
|
+
id: 'sc-1',
|
|
1633
|
+
project_id: projectId,
|
|
1634
|
+
name: 'CollectionChips - Default',
|
|
1635
|
+
component_name: 'CollectionChips',
|
|
1636
|
+
entity_sha: 'sha-chips',
|
|
1637
|
+
created_at: '2026-03-16 23:00:00',
|
|
1638
|
+
})
|
|
1639
|
+
.execute();
|
|
1640
|
+
await db
|
|
1641
|
+
.insertInto('editor_scenarios')
|
|
1642
|
+
.values({
|
|
1643
|
+
id: 'sc-2',
|
|
1644
|
+
project_id: projectId,
|
|
1645
|
+
name: 'CollectionChips - Many',
|
|
1646
|
+
component_name: 'CollectionChips',
|
|
1647
|
+
entity_sha: 'sha-chips',
|
|
1648
|
+
created_at: '2026-03-16 23:01:00',
|
|
1649
|
+
})
|
|
1650
|
+
.execute();
|
|
1651
|
+
const result = await queryIncompleteEntities(db, projectId, null);
|
|
1652
|
+
expect(result).toEqual([
|
|
1653
|
+
{ entitySha: 'sha-chips', name: 'CollectionChips', scenarioCount: 2 },
|
|
1654
|
+
]);
|
|
1655
|
+
});
|
|
1656
|
+
it('should only return entities without analyses, not those with analyses', async () => {
|
|
1657
|
+
// Entity WITH analysis (Header)
|
|
1658
|
+
await db
|
|
1659
|
+
.insertInto('entities')
|
|
1660
|
+
.values({
|
|
1661
|
+
sha: 'sha-header',
|
|
1662
|
+
name: 'Header',
|
|
1663
|
+
entity_type: 'visual',
|
|
1664
|
+
file_path: 'src/Header.tsx',
|
|
1665
|
+
})
|
|
1666
|
+
.execute();
|
|
1667
|
+
await db
|
|
1668
|
+
.insertInto('analyses')
|
|
1669
|
+
.values({
|
|
1670
|
+
id: 'a-1',
|
|
1671
|
+
entity_sha: 'sha-header',
|
|
1672
|
+
entity_name: 'Header',
|
|
1673
|
+
project_id: projectId,
|
|
1674
|
+
})
|
|
1675
|
+
.execute();
|
|
1676
|
+
await db
|
|
1677
|
+
.insertInto('editor_scenarios')
|
|
1678
|
+
.values({
|
|
1679
|
+
id: 'sc-1',
|
|
1680
|
+
project_id: projectId,
|
|
1681
|
+
name: 'Header - Default',
|
|
1682
|
+
component_name: 'Header',
|
|
1683
|
+
entity_sha: 'sha-header',
|
|
1684
|
+
created_at: '2026-03-16 23:00:00',
|
|
1685
|
+
})
|
|
1686
|
+
.execute();
|
|
1687
|
+
// Entity WITHOUT analysis (CollectionPicker)
|
|
1688
|
+
await db
|
|
1689
|
+
.insertInto('entities')
|
|
1690
|
+
.values({
|
|
1691
|
+
sha: 'sha-picker',
|
|
1692
|
+
name: 'CollectionPicker',
|
|
1693
|
+
entity_type: 'visual',
|
|
1694
|
+
file_path: 'src/components/CollectionPicker.tsx',
|
|
1695
|
+
})
|
|
1696
|
+
.execute();
|
|
1697
|
+
await db
|
|
1698
|
+
.insertInto('editor_scenarios')
|
|
1699
|
+
.values({
|
|
1700
|
+
id: 'sc-2',
|
|
1701
|
+
project_id: projectId,
|
|
1702
|
+
name: 'CollectionPicker - Default',
|
|
1703
|
+
component_name: 'CollectionPicker',
|
|
1704
|
+
entity_sha: 'sha-picker',
|
|
1705
|
+
created_at: '2026-03-16 23:00:00',
|
|
1706
|
+
})
|
|
1707
|
+
.execute();
|
|
1708
|
+
const result = await queryIncompleteEntities(db, projectId, null);
|
|
1709
|
+
expect(result).toEqual([
|
|
1710
|
+
{ entitySha: 'sha-picker', name: 'CollectionPicker', scenarioCount: 1 },
|
|
1711
|
+
]);
|
|
1712
|
+
});
|
|
1713
|
+
it('should scope to session when featureStartedAt is provided', async () => {
|
|
1714
|
+
// Entity without analysis, scenario created BEFORE session
|
|
1715
|
+
await db
|
|
1716
|
+
.insertInto('entities')
|
|
1717
|
+
.values({
|
|
1718
|
+
sha: 'sha-old',
|
|
1719
|
+
name: 'OldComponent',
|
|
1720
|
+
entity_type: 'visual',
|
|
1721
|
+
file_path: 'src/OldComponent.tsx',
|
|
1722
|
+
})
|
|
1723
|
+
.execute();
|
|
1724
|
+
await db
|
|
1725
|
+
.insertInto('editor_scenarios')
|
|
1726
|
+
.values({
|
|
1727
|
+
id: 'sc-old',
|
|
1728
|
+
project_id: projectId,
|
|
1729
|
+
name: 'OldComponent - Default',
|
|
1730
|
+
component_name: 'OldComponent',
|
|
1731
|
+
entity_sha: 'sha-old',
|
|
1732
|
+
created_at: '2026-03-16 20:00:00',
|
|
1733
|
+
})
|
|
1734
|
+
.execute();
|
|
1735
|
+
// Entity without analysis, scenario created DURING session
|
|
1736
|
+
await db
|
|
1737
|
+
.insertInto('entities')
|
|
1738
|
+
.values({
|
|
1739
|
+
sha: 'sha-new',
|
|
1740
|
+
name: 'NewComponent',
|
|
1741
|
+
entity_type: 'visual',
|
|
1742
|
+
file_path: 'src/NewComponent.tsx',
|
|
1743
|
+
})
|
|
1744
|
+
.execute();
|
|
1745
|
+
await db
|
|
1746
|
+
.insertInto('editor_scenarios')
|
|
1747
|
+
.values({
|
|
1748
|
+
id: 'sc-new',
|
|
1749
|
+
project_id: projectId,
|
|
1750
|
+
name: 'NewComponent - Default',
|
|
1751
|
+
component_name: 'NewComponent',
|
|
1752
|
+
entity_sha: 'sha-new',
|
|
1753
|
+
created_at: '2026-03-16 23:10:00',
|
|
1754
|
+
})
|
|
1755
|
+
.execute();
|
|
1756
|
+
const result = await queryIncompleteEntities(db, projectId, '2026-03-16T23:07:12.698Z');
|
|
1757
|
+
// Only NewComponent should be flagged (created in session)
|
|
1758
|
+
expect(result).toEqual([
|
|
1759
|
+
{ entitySha: 'sha-new', name: 'NewComponent', scenarioCount: 1 },
|
|
1760
|
+
]);
|
|
1761
|
+
});
|
|
1762
|
+
it('should include scenarios updated in session even if created before', async () => {
|
|
1763
|
+
await db
|
|
1764
|
+
.insertInto('entities')
|
|
1765
|
+
.values({
|
|
1766
|
+
sha: 'sha-updated',
|
|
1767
|
+
name: 'UpdatedComponent',
|
|
1768
|
+
entity_type: 'visual',
|
|
1769
|
+
file_path: 'src/Updated.tsx',
|
|
1770
|
+
})
|
|
1771
|
+
.execute();
|
|
1772
|
+
await db
|
|
1773
|
+
.insertInto('editor_scenarios')
|
|
1774
|
+
.values({
|
|
1775
|
+
id: 'sc-updated',
|
|
1776
|
+
project_id: projectId,
|
|
1777
|
+
name: 'UpdatedComponent - Default',
|
|
1778
|
+
component_name: 'UpdatedComponent',
|
|
1779
|
+
entity_sha: 'sha-updated',
|
|
1780
|
+
created_at: '2026-03-16 20:00:00',
|
|
1781
|
+
updated_at: '2026-03-16 23:20:00', // Updated in session
|
|
1782
|
+
})
|
|
1783
|
+
.execute();
|
|
1784
|
+
const result = await queryIncompleteEntities(db, projectId, '2026-03-16T23:07:12.698Z');
|
|
1785
|
+
expect(result).toEqual([
|
|
1786
|
+
{
|
|
1787
|
+
entitySha: 'sha-updated',
|
|
1788
|
+
name: 'UpdatedComponent',
|
|
1789
|
+
scenarioCount: 1,
|
|
1790
|
+
},
|
|
1791
|
+
]);
|
|
1792
|
+
});
|
|
1793
|
+
it('should skip scenarios with null entity_sha', async () => {
|
|
1794
|
+
await db
|
|
1795
|
+
.insertInto('editor_scenarios')
|
|
1796
|
+
.values({
|
|
1797
|
+
id: 'sc-null',
|
|
1798
|
+
project_id: projectId,
|
|
1799
|
+
name: 'Orphan Scenario',
|
|
1800
|
+
component_name: 'Orphan',
|
|
1801
|
+
created_at: '2026-03-16 23:00:00',
|
|
1802
|
+
})
|
|
1803
|
+
.execute();
|
|
1804
|
+
const result = await queryIncompleteEntities(db, projectId, null);
|
|
1805
|
+
expect(result).toEqual([]);
|
|
1806
|
+
});
|
|
1807
|
+
it('should return empty when there are no scenarios', async () => {
|
|
1808
|
+
const result = await queryIncompleteEntities(db, projectId, null);
|
|
1809
|
+
expect(result).toEqual([]);
|
|
1810
|
+
});
|
|
1811
|
+
it('should use entity name from entities table, falling back to component_name', async () => {
|
|
1812
|
+
// Scenario has entity_sha but entity record doesn't exist
|
|
1813
|
+
await db
|
|
1814
|
+
.insertInto('editor_scenarios')
|
|
1815
|
+
.values({
|
|
1816
|
+
id: 'sc-1',
|
|
1817
|
+
project_id: projectId,
|
|
1818
|
+
name: 'Ghost - Default',
|
|
1819
|
+
component_name: 'GhostComponent',
|
|
1820
|
+
entity_sha: 'sha-ghost',
|
|
1821
|
+
created_at: '2026-03-16 23:00:00',
|
|
1822
|
+
})
|
|
1823
|
+
.execute();
|
|
1824
|
+
const result = await queryIncompleteEntities(db, projectId, null);
|
|
1825
|
+
expect(result).toEqual([
|
|
1826
|
+
{ entitySha: 'sha-ghost', name: 'GhostComponent', scenarioCount: 1 },
|
|
1827
|
+
]);
|
|
1828
|
+
});
|
|
1829
|
+
});
|
|
854
1830
|
});
|
|
855
1831
|
//# sourceMappingURL=editorAudit.test.js.map
|