@codeyam/codeyam-cli 0.1.10 → 0.1.12
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 +7 -7
- 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 +1214 -217
- 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__/editorAudit.test.js +893 -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 +441 -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/editorAudit.js +145 -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 +163 -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-BWAyuj0r.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-ChX0hPcu.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 +9 -0
- package/codeyam-cli/src/webserver/server.js.map +1 -1
- package/codeyam-cli/src/webserver/terminalServer.js +105 -9
- package/codeyam-cli/src/webserver/terminalServer.js.map +1 -1
- package/codeyam-cli/templates/editor-step-hook.py +104 -20
- 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-COWCNVyV.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-phvmGvat.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-6134dc40.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-kSNsMjj8.js +0 -10
- package/codeyam-cli/src/webserver/build/server/assets/server-build-Bm2xIhmh.js +0 -439
|
@@ -3,7 +3,7 @@ import os from 'os';
|
|
|
3
3
|
import path from 'path';
|
|
4
4
|
import Database from 'better-sqlite3';
|
|
5
5
|
import { Kysely, SqliteDialect } from 'kysely';
|
|
6
|
-
import { deduplicateByName, generateScenarioSlug, convertIsoToSqliteTimestamp, determineCaptureUrl, resolvePreviewNavPath, clearEditorState, clearEditorUserPrompt, readDefaultScreenSize, readScreenSizes, resolveScenarioViewport, resolveViewportWithProjectDefault, upsertEditorScenario, cleanupScenarioFiles, readPreservedConfigProperties, validateStepTransition, slugifyDimension, isRowInFeatureSession, } from "../editorScenarios.js";
|
|
6
|
+
import { deduplicateByName, generateScenarioSlug, convertIsoToSqliteTimestamp, determineCaptureUrl, resolvePreviewNavPath, clearEditorState, clearEditorUserPrompt, readDefaultScreenSize, readScreenSizes, resolveScenarioViewport, resolveViewportWithProjectDefault, upsertEditorScenario, cleanupScenarioFiles, readPreservedConfigProperties, validateStepTransition, slugifyDimension, isRowInFeatureSession, backfillEntityShaOnScenarios, countScenariosNeedingEntityBackfill, validateEntityLinkageForAppScenario, } from "../editorScenarios.js";
|
|
7
7
|
describe('editorScenarios', () => {
|
|
8
8
|
describe('deduplicateByName', () => {
|
|
9
9
|
it('should keep only the last item for each key', () => {
|
|
@@ -497,7 +497,6 @@ describe('editorScenarios', () => {
|
|
|
497
497
|
type: null,
|
|
498
498
|
viewportWidth: 1280,
|
|
499
499
|
viewportHeight: 720,
|
|
500
|
-
dimension: null,
|
|
501
500
|
});
|
|
502
501
|
expect(result.isNew).toBe(true);
|
|
503
502
|
expect(result.cleanedUpIds).toEqual([]);
|
|
@@ -522,7 +521,6 @@ describe('editorScenarios', () => {
|
|
|
522
521
|
type: null,
|
|
523
522
|
viewportWidth: 1280,
|
|
524
523
|
viewportHeight: 720,
|
|
525
|
-
dimension: null,
|
|
526
524
|
});
|
|
527
525
|
// Second registration with same name
|
|
528
526
|
const second = await upsertEditorScenario(db, {
|
|
@@ -535,7 +533,6 @@ describe('editorScenarios', () => {
|
|
|
535
533
|
type: null,
|
|
536
534
|
viewportWidth: 800,
|
|
537
535
|
viewportHeight: 600,
|
|
538
|
-
dimension: null,
|
|
539
536
|
});
|
|
540
537
|
// Should reuse the same ID
|
|
541
538
|
expect(second.scenarioId).toBe(first.scenarioId);
|
|
@@ -576,7 +573,6 @@ describe('editorScenarios', () => {
|
|
|
576
573
|
type: null,
|
|
577
574
|
viewportWidth: 1280,
|
|
578
575
|
viewportHeight: 720,
|
|
579
|
-
dimension: null,
|
|
580
576
|
});
|
|
581
577
|
expect(result.isNew).toBe(false);
|
|
582
578
|
// Should return the IDs of cleaned-up duplicates
|
|
@@ -600,7 +596,6 @@ describe('editorScenarios', () => {
|
|
|
600
596
|
type: null,
|
|
601
597
|
viewportWidth: 1280,
|
|
602
598
|
viewportHeight: 720,
|
|
603
|
-
dimension: null,
|
|
604
599
|
});
|
|
605
600
|
await upsertEditorScenario(db, {
|
|
606
601
|
projectId,
|
|
@@ -612,7 +607,6 @@ describe('editorScenarios', () => {
|
|
|
612
607
|
type: null,
|
|
613
608
|
viewportWidth: 1280,
|
|
614
609
|
viewportHeight: 720,
|
|
615
|
-
dimension: null,
|
|
616
610
|
});
|
|
617
611
|
const rows = await db
|
|
618
612
|
.selectFrom('editor_scenarios')
|
|
@@ -631,7 +625,6 @@ describe('editorScenarios', () => {
|
|
|
631
625
|
type: 'application',
|
|
632
626
|
viewportWidth: 1280,
|
|
633
627
|
viewportHeight: 720,
|
|
634
|
-
dimension: null,
|
|
635
628
|
});
|
|
636
629
|
const result2 = await upsertEditorScenario(db, {
|
|
637
630
|
projectId: 'project-b',
|
|
@@ -643,7 +636,6 @@ describe('editorScenarios', () => {
|
|
|
643
636
|
type: 'application',
|
|
644
637
|
viewportWidth: 1280,
|
|
645
638
|
viewportHeight: 720,
|
|
646
|
-
dimension: null,
|
|
647
639
|
});
|
|
648
640
|
expect(result1.scenarioId).not.toBe(result2.scenarioId);
|
|
649
641
|
expect(result1.isNew).toBe(true);
|
|
@@ -848,7 +840,7 @@ describe('editorScenarios', () => {
|
|
|
848
840
|
afterEach(async () => {
|
|
849
841
|
await db.destroy();
|
|
850
842
|
});
|
|
851
|
-
it('should store dimensions as JSON array
|
|
843
|
+
it('should store dimensions as JSON array', async () => {
|
|
852
844
|
const result = await upsertEditorScenario(db, {
|
|
853
845
|
projectId,
|
|
854
846
|
name: 'Home Page',
|
|
@@ -859,7 +851,6 @@ describe('editorScenarios', () => {
|
|
|
859
851
|
type: 'application',
|
|
860
852
|
viewportWidth: 1440,
|
|
861
853
|
viewportHeight: 900,
|
|
862
|
-
dimension: 'Desktop',
|
|
863
854
|
dimensions: ['Desktop', 'Mobile'],
|
|
864
855
|
screenshotPaths: null,
|
|
865
856
|
});
|
|
@@ -873,8 +864,6 @@ describe('editorScenarios', () => {
|
|
|
873
864
|
'Desktop',
|
|
874
865
|
'Mobile',
|
|
875
866
|
]);
|
|
876
|
-
// dimension (singular) should be set to first element for backward compat
|
|
877
|
-
expect(row.dimension).toBe('Desktop');
|
|
878
867
|
});
|
|
879
868
|
it('should store screenshot_paths as JSON object', async () => {
|
|
880
869
|
const screenshotPaths = {
|
|
@@ -891,7 +880,6 @@ describe('editorScenarios', () => {
|
|
|
891
880
|
type: 'application',
|
|
892
881
|
viewportWidth: 1440,
|
|
893
882
|
viewportHeight: 900,
|
|
894
|
-
dimension: 'Desktop',
|
|
895
883
|
dimensions: ['Desktop', 'Mobile'],
|
|
896
884
|
screenshotPaths,
|
|
897
885
|
});
|
|
@@ -913,7 +901,6 @@ describe('editorScenarios', () => {
|
|
|
913
901
|
type: 'application',
|
|
914
902
|
viewportWidth: 1440,
|
|
915
903
|
viewportHeight: 900,
|
|
916
|
-
dimension: 'Desktop',
|
|
917
904
|
dimensions: ['Desktop'],
|
|
918
905
|
screenshotPaths: { Desktop: 'screenshots/old--desktop.png' },
|
|
919
906
|
});
|
|
@@ -927,7 +914,6 @@ describe('editorScenarios', () => {
|
|
|
927
914
|
type: 'application',
|
|
928
915
|
viewportWidth: 1440,
|
|
929
916
|
viewportHeight: 900,
|
|
930
|
-
dimension: 'Desktop',
|
|
931
917
|
dimensions: ['Desktop', 'Mobile'],
|
|
932
918
|
screenshotPaths: {
|
|
933
919
|
Desktop: 'screenshots/new--desktop.png',
|
|
@@ -960,7 +946,6 @@ describe('editorScenarios', () => {
|
|
|
960
946
|
type: null,
|
|
961
947
|
viewportWidth: 1280,
|
|
962
948
|
viewportHeight: 720,
|
|
963
|
-
dimension: null,
|
|
964
949
|
});
|
|
965
950
|
const row = await db
|
|
966
951
|
.selectFrom('editor_scenarios')
|
|
@@ -1055,5 +1040,444 @@ describe('editorScenarios', () => {
|
|
|
1055
1040
|
expect(isRowInFeatureSession({ created_at: '2026-03-12 14:30:00', updated_at: null }, featureStart)).toBe(true);
|
|
1056
1041
|
});
|
|
1057
1042
|
});
|
|
1043
|
+
describe('upsertEditorScenario with entitySha and displayName', () => {
|
|
1044
|
+
let db;
|
|
1045
|
+
let rawDb;
|
|
1046
|
+
const projectId = 'test-project-id';
|
|
1047
|
+
beforeEach(async () => {
|
|
1048
|
+
rawDb = new Database(':memory:');
|
|
1049
|
+
db = new Kysely({ dialect: new SqliteDialect({ database: rawDb }) });
|
|
1050
|
+
await db.schema
|
|
1051
|
+
.createTable('editor_scenarios')
|
|
1052
|
+
.addColumn('id', 'varchar', (col) => col.primaryKey())
|
|
1053
|
+
.addColumn('project_id', 'varchar', (col) => col.notNull())
|
|
1054
|
+
.addColumn('name', 'varchar', (col) => col.notNull())
|
|
1055
|
+
.addColumn('description', 'text')
|
|
1056
|
+
.addColumn('component_name', 'varchar')
|
|
1057
|
+
.addColumn('component_path', 'varchar')
|
|
1058
|
+
.addColumn('url', 'varchar')
|
|
1059
|
+
.addColumn('type', 'varchar')
|
|
1060
|
+
.addColumn('screenshot_path', 'varchar')
|
|
1061
|
+
.addColumn('viewport_width', 'integer')
|
|
1062
|
+
.addColumn('viewport_height', 'integer')
|
|
1063
|
+
.addColumn('dimension', 'varchar')
|
|
1064
|
+
.addColumn('dimensions', 'text')
|
|
1065
|
+
.addColumn('screenshot_paths', 'text')
|
|
1066
|
+
.addColumn('page_file_path', 'varchar')
|
|
1067
|
+
.addColumn('entity_sha', 'varchar')
|
|
1068
|
+
.addColumn('display_name', 'varchar')
|
|
1069
|
+
.addColumn('created_at', 'datetime', (col) => col.defaultTo(new Date().toISOString()))
|
|
1070
|
+
.addColumn('updated_at', 'datetime', (col) => col.defaultTo(new Date().toISOString()))
|
|
1071
|
+
.execute();
|
|
1072
|
+
});
|
|
1073
|
+
afterEach(async () => {
|
|
1074
|
+
await db.destroy();
|
|
1075
|
+
});
|
|
1076
|
+
it('should store entitySha and displayName on insert', async () => {
|
|
1077
|
+
const result = await upsertEditorScenario(db, {
|
|
1078
|
+
projectId,
|
|
1079
|
+
name: 'Home - Default',
|
|
1080
|
+
description: null,
|
|
1081
|
+
componentName: null,
|
|
1082
|
+
componentPath: null,
|
|
1083
|
+
url: '/',
|
|
1084
|
+
type: 'application',
|
|
1085
|
+
viewportWidth: 400,
|
|
1086
|
+
viewportHeight: 600,
|
|
1087
|
+
entitySha: 'abc123',
|
|
1088
|
+
displayName: 'Home',
|
|
1089
|
+
});
|
|
1090
|
+
expect(result.isNew).toBe(true);
|
|
1091
|
+
const row = rawDb
|
|
1092
|
+
.prepare('SELECT entity_sha, display_name FROM editor_scenarios WHERE id = ?')
|
|
1093
|
+
.get(result.scenarioId);
|
|
1094
|
+
expect(row.entity_sha).toBe('abc123');
|
|
1095
|
+
expect(row.display_name).toBe('Home');
|
|
1096
|
+
});
|
|
1097
|
+
it('should update entitySha and displayName on upsert', async () => {
|
|
1098
|
+
// Insert first
|
|
1099
|
+
await upsertEditorScenario(db, {
|
|
1100
|
+
projectId,
|
|
1101
|
+
name: 'Home - Default',
|
|
1102
|
+
description: null,
|
|
1103
|
+
componentName: null,
|
|
1104
|
+
componentPath: null,
|
|
1105
|
+
url: '/',
|
|
1106
|
+
type: 'application',
|
|
1107
|
+
viewportWidth: 400,
|
|
1108
|
+
viewportHeight: 600,
|
|
1109
|
+
entitySha: 'old-sha',
|
|
1110
|
+
displayName: 'OldName',
|
|
1111
|
+
});
|
|
1112
|
+
// Upsert with new values
|
|
1113
|
+
const result = await upsertEditorScenario(db, {
|
|
1114
|
+
projectId,
|
|
1115
|
+
name: 'Home - Default',
|
|
1116
|
+
description: null,
|
|
1117
|
+
componentName: null,
|
|
1118
|
+
componentPath: null,
|
|
1119
|
+
url: '/',
|
|
1120
|
+
type: 'application',
|
|
1121
|
+
viewportWidth: 400,
|
|
1122
|
+
viewportHeight: 600,
|
|
1123
|
+
entitySha: 'new-sha',
|
|
1124
|
+
displayName: 'Home',
|
|
1125
|
+
});
|
|
1126
|
+
expect(result.isNew).toBe(false);
|
|
1127
|
+
const row = rawDb
|
|
1128
|
+
.prepare('SELECT entity_sha, display_name FROM editor_scenarios WHERE id = ?')
|
|
1129
|
+
.get(result.scenarioId);
|
|
1130
|
+
expect(row.entity_sha).toBe('new-sha');
|
|
1131
|
+
expect(row.display_name).toBe('Home');
|
|
1132
|
+
});
|
|
1133
|
+
it('should not overwrite entitySha when not provided', async () => {
|
|
1134
|
+
// Insert with SHA
|
|
1135
|
+
const first = await upsertEditorScenario(db, {
|
|
1136
|
+
projectId,
|
|
1137
|
+
name: 'Home - Default',
|
|
1138
|
+
description: null,
|
|
1139
|
+
componentName: null,
|
|
1140
|
+
componentPath: null,
|
|
1141
|
+
url: '/',
|
|
1142
|
+
type: 'application',
|
|
1143
|
+
viewportWidth: 400,
|
|
1144
|
+
viewportHeight: 600,
|
|
1145
|
+
entitySha: 'keep-this',
|
|
1146
|
+
displayName: 'Home',
|
|
1147
|
+
});
|
|
1148
|
+
// Upsert without entitySha/displayName
|
|
1149
|
+
await upsertEditorScenario(db, {
|
|
1150
|
+
projectId,
|
|
1151
|
+
name: 'Home - Default',
|
|
1152
|
+
description: 'updated',
|
|
1153
|
+
componentName: null,
|
|
1154
|
+
componentPath: null,
|
|
1155
|
+
url: '/',
|
|
1156
|
+
type: 'application',
|
|
1157
|
+
viewportWidth: 400,
|
|
1158
|
+
viewportHeight: 600,
|
|
1159
|
+
});
|
|
1160
|
+
const row = rawDb
|
|
1161
|
+
.prepare('SELECT entity_sha, display_name FROM editor_scenarios WHERE id = ?')
|
|
1162
|
+
.get(first.scenarioId);
|
|
1163
|
+
expect(row.entity_sha).toBe('keep-this');
|
|
1164
|
+
expect(row.display_name).toBe('Home');
|
|
1165
|
+
});
|
|
1166
|
+
});
|
|
1167
|
+
describe('backfillEntityShaOnScenarios', () => {
|
|
1168
|
+
let db;
|
|
1169
|
+
let rawDb;
|
|
1170
|
+
const projectId = 'test-project-id';
|
|
1171
|
+
beforeEach(async () => {
|
|
1172
|
+
rawDb = new Database(':memory:');
|
|
1173
|
+
db = new Kysely({ dialect: new SqliteDialect({ database: rawDb }) });
|
|
1174
|
+
await db.schema
|
|
1175
|
+
.createTable('editor_scenarios')
|
|
1176
|
+
.addColumn('id', 'varchar', (col) => col.primaryKey())
|
|
1177
|
+
.addColumn('project_id', 'varchar', (col) => col.notNull())
|
|
1178
|
+
.addColumn('name', 'varchar', (col) => col.notNull())
|
|
1179
|
+
.addColumn('description', 'text')
|
|
1180
|
+
.addColumn('component_name', 'varchar')
|
|
1181
|
+
.addColumn('component_path', 'varchar')
|
|
1182
|
+
.addColumn('url', 'varchar')
|
|
1183
|
+
.addColumn('type', 'varchar')
|
|
1184
|
+
.addColumn('screenshot_path', 'varchar')
|
|
1185
|
+
.addColumn('viewport_width', 'integer')
|
|
1186
|
+
.addColumn('viewport_height', 'integer')
|
|
1187
|
+
.addColumn('dimension', 'varchar')
|
|
1188
|
+
.addColumn('dimensions', 'text')
|
|
1189
|
+
.addColumn('screenshot_paths', 'text')
|
|
1190
|
+
.addColumn('page_file_path', 'varchar')
|
|
1191
|
+
.addColumn('entity_sha', 'varchar')
|
|
1192
|
+
.addColumn('display_name', 'varchar')
|
|
1193
|
+
.addColumn('created_at', 'datetime', (col) => col.defaultTo(new Date().toISOString()))
|
|
1194
|
+
.addColumn('updated_at', 'datetime', (col) => col.defaultTo(new Date().toISOString()))
|
|
1195
|
+
.execute();
|
|
1196
|
+
});
|
|
1197
|
+
afterEach(async () => {
|
|
1198
|
+
await db.destroy();
|
|
1199
|
+
});
|
|
1200
|
+
it('should backfill component scenario by component_path match', async () => {
|
|
1201
|
+
// Insert a scenario with null entity_sha but with component_path
|
|
1202
|
+
rawDb
|
|
1203
|
+
.prepare(`INSERT INTO editor_scenarios (id, project_id, name, component_name, component_path, url, type, viewport_width, viewport_height)
|
|
1204
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`)
|
|
1205
|
+
.run('sc-1', projectId, 'Header - Default', 'Header', 'src/components/Header.tsx', '/isolated-components/Header', 'component', 1280, 720);
|
|
1206
|
+
const result = await backfillEntityShaOnScenarios(db, [
|
|
1207
|
+
{
|
|
1208
|
+
sha: 'entity-sha-1',
|
|
1209
|
+
name: 'Header',
|
|
1210
|
+
filePath: 'src/components/Header.tsx',
|
|
1211
|
+
},
|
|
1212
|
+
]);
|
|
1213
|
+
expect(result.updated).toBe(1);
|
|
1214
|
+
const row = rawDb
|
|
1215
|
+
.prepare('SELECT entity_sha, display_name FROM editor_scenarios WHERE id = ?')
|
|
1216
|
+
.get('sc-1');
|
|
1217
|
+
expect(row.entity_sha).toBe('entity-sha-1');
|
|
1218
|
+
expect(row.display_name).toBe('Header');
|
|
1219
|
+
});
|
|
1220
|
+
it('should backfill page scenario by page_file_path match and derive display_name from route', async () => {
|
|
1221
|
+
// Insert a scenario with null entity_sha but with page_file_path
|
|
1222
|
+
rawDb
|
|
1223
|
+
.prepare(`INSERT INTO editor_scenarios (id, project_id, name, page_file_path, url, type, viewport_width, viewport_height)
|
|
1224
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`)
|
|
1225
|
+
.run('sc-2', projectId, 'Feedback - Default', 'app/feedback/page.tsx', '/feedback', 'application', 1280, 720);
|
|
1226
|
+
const result = await backfillEntityShaOnScenarios(db, [
|
|
1227
|
+
{
|
|
1228
|
+
sha: 'entity-sha-2',
|
|
1229
|
+
name: 'Feedback',
|
|
1230
|
+
filePath: 'app/feedback/page.tsx',
|
|
1231
|
+
},
|
|
1232
|
+
]);
|
|
1233
|
+
expect(result.updated).toBe(1);
|
|
1234
|
+
const row = rawDb
|
|
1235
|
+
.prepare('SELECT entity_sha, display_name FROM editor_scenarios WHERE id = ?')
|
|
1236
|
+
.get('sc-2');
|
|
1237
|
+
expect(row.entity_sha).toBe('entity-sha-2');
|
|
1238
|
+
// display_name derived from page_file_path via routeDisplayName(buildRoutePattern())
|
|
1239
|
+
expect(row.display_name).toBe('Feedback');
|
|
1240
|
+
});
|
|
1241
|
+
it('should update scenarios with stale entity_sha to latest', async () => {
|
|
1242
|
+
// Margo bug: entity code changes create new versions with new SHAs,
|
|
1243
|
+
// but scenarios still point to the old SHA. Since journals preserve
|
|
1244
|
+
// screenshots and everything is in git, we always want scenarios
|
|
1245
|
+
// pointing to the latest entity version.
|
|
1246
|
+
rawDb
|
|
1247
|
+
.prepare(`INSERT INTO editor_scenarios (id, project_id, name, component_name, component_path, url, type, viewport_width, viewport_height, entity_sha, display_name)
|
|
1248
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`)
|
|
1249
|
+
.run('sc-3', projectId, 'Header - Dark', 'Header', 'src/components/Header.tsx', '/isolated-components/Header', 'component', 1280, 720, 'old-sha', 'Header');
|
|
1250
|
+
const result = await backfillEntityShaOnScenarios(db, [
|
|
1251
|
+
{
|
|
1252
|
+
sha: 'latest-sha',
|
|
1253
|
+
name: 'Header',
|
|
1254
|
+
filePath: 'src/components/Header.tsx',
|
|
1255
|
+
},
|
|
1256
|
+
]);
|
|
1257
|
+
expect(result.updated).toBe(1);
|
|
1258
|
+
const row = rawDb
|
|
1259
|
+
.prepare('SELECT entity_sha FROM editor_scenarios WHERE id = ?')
|
|
1260
|
+
.get('sc-3');
|
|
1261
|
+
expect(row.entity_sha).toBe('latest-sha');
|
|
1262
|
+
});
|
|
1263
|
+
it('should not update scenarios already pointing to the latest SHA', async () => {
|
|
1264
|
+
rawDb
|
|
1265
|
+
.prepare(`INSERT INTO editor_scenarios (id, project_id, name, component_name, component_path, url, type, viewport_width, viewport_height, entity_sha, display_name)
|
|
1266
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`)
|
|
1267
|
+
.run('sc-3b', projectId, 'Header - Dark', 'Header', 'src/components/Header.tsx', '/isolated-components/Header', 'component', 1280, 720, 'current-sha', 'Header');
|
|
1268
|
+
const result = await backfillEntityShaOnScenarios(db, [
|
|
1269
|
+
{
|
|
1270
|
+
sha: 'current-sha',
|
|
1271
|
+
name: 'Header',
|
|
1272
|
+
filePath: 'src/components/Header.tsx',
|
|
1273
|
+
},
|
|
1274
|
+
]);
|
|
1275
|
+
expect(result.updated).toBe(0);
|
|
1276
|
+
});
|
|
1277
|
+
it('should skip scenarios with no matching entity', async () => {
|
|
1278
|
+
rawDb
|
|
1279
|
+
.prepare(`INSERT INTO editor_scenarios (id, project_id, name, component_name, component_path, url, type, viewport_width, viewport_height)
|
|
1280
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`)
|
|
1281
|
+
.run('sc-4', projectId, 'Footer - Default', 'Footer', 'src/components/Footer.tsx', '/isolated-components/Footer', 'component', 1280, 720);
|
|
1282
|
+
// Entities list doesn't include Footer.tsx
|
|
1283
|
+
const result = await backfillEntityShaOnScenarios(db, [
|
|
1284
|
+
{
|
|
1285
|
+
sha: 'entity-sha-1',
|
|
1286
|
+
name: 'Header',
|
|
1287
|
+
filePath: 'src/components/Header.tsx',
|
|
1288
|
+
},
|
|
1289
|
+
]);
|
|
1290
|
+
expect(result.updated).toBe(0);
|
|
1291
|
+
const row = rawDb
|
|
1292
|
+
.prepare('SELECT entity_sha FROM editor_scenarios WHERE id = ?')
|
|
1293
|
+
.get('sc-4');
|
|
1294
|
+
expect(row.entity_sha).toBeNull();
|
|
1295
|
+
});
|
|
1296
|
+
it('should match component scenarios by name+path, not just path (multi-entity files)', async () => {
|
|
1297
|
+
// A single file can export multiple components (e.g., FullPageLibrary.tsx
|
|
1298
|
+
// exports FullPageLibrary, FullPageEmptyState, FullPageArticleCard).
|
|
1299
|
+
// Each component scenario should get the SHA for its specific entity,
|
|
1300
|
+
// not whichever entity happens to be last in the map.
|
|
1301
|
+
rawDb
|
|
1302
|
+
.prepare(`INSERT INTO editor_scenarios (id, project_id, name, component_name, component_path, url, type, viewport_width, viewport_height, entity_sha)
|
|
1303
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`)
|
|
1304
|
+
.run('sc-fplib', projectId, 'FullPageLibrary - Default', 'FullPageLibrary', 'src/library/FullPageLibrary.tsx', '/isolated-components/FullPageLibrary', 'component', 1280, 720, 'old-fplib-sha');
|
|
1305
|
+
rawDb
|
|
1306
|
+
.prepare(`INSERT INTO editor_scenarios (id, project_id, name, component_name, component_path, url, type, viewport_width, viewport_height, entity_sha)
|
|
1307
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`)
|
|
1308
|
+
.run('sc-fpempty', projectId, 'FullPageEmptyState - Default', 'FullPageEmptyState', 'src/library/FullPageLibrary.tsx', '/isolated-components/FullPageEmptyState', 'component', 1280, 720, 'old-fpempty-sha');
|
|
1309
|
+
const result = await backfillEntityShaOnScenarios(db, [
|
|
1310
|
+
{
|
|
1311
|
+
sha: 'new-fplib-sha',
|
|
1312
|
+
name: 'FullPageLibrary',
|
|
1313
|
+
filePath: 'src/library/FullPageLibrary.tsx',
|
|
1314
|
+
},
|
|
1315
|
+
{
|
|
1316
|
+
sha: 'new-fpempty-sha',
|
|
1317
|
+
name: 'FullPageEmptyState',
|
|
1318
|
+
filePath: 'src/library/FullPageLibrary.tsx',
|
|
1319
|
+
},
|
|
1320
|
+
]);
|
|
1321
|
+
expect(result.updated).toBe(2);
|
|
1322
|
+
const fplib = rawDb
|
|
1323
|
+
.prepare('SELECT entity_sha FROM editor_scenarios WHERE id = ?')
|
|
1324
|
+
.get('sc-fplib');
|
|
1325
|
+
expect(fplib.entity_sha).toBe('new-fplib-sha');
|
|
1326
|
+
const fpempty = rawDb
|
|
1327
|
+
.prepare('SELECT entity_sha FROM editor_scenarios WHERE id = ?')
|
|
1328
|
+
.get('sc-fpempty');
|
|
1329
|
+
expect(fpempty.entity_sha).toBe('new-fpempty-sha');
|
|
1330
|
+
});
|
|
1331
|
+
});
|
|
1332
|
+
describe('countScenariosNeedingEntityBackfill', () => {
|
|
1333
|
+
let db;
|
|
1334
|
+
let rawDb;
|
|
1335
|
+
const projectId = 'test-project-id';
|
|
1336
|
+
beforeEach(async () => {
|
|
1337
|
+
rawDb = new Database(':memory:');
|
|
1338
|
+
db = new Kysely({ dialect: new SqliteDialect({ database: rawDb }) });
|
|
1339
|
+
await db.schema
|
|
1340
|
+
.createTable('editor_scenarios')
|
|
1341
|
+
.addColumn('id', 'varchar', (col) => col.primaryKey())
|
|
1342
|
+
.addColumn('project_id', 'varchar', (col) => col.notNull())
|
|
1343
|
+
.addColumn('name', 'varchar', (col) => col.notNull())
|
|
1344
|
+
.addColumn('description', 'text')
|
|
1345
|
+
.addColumn('component_name', 'varchar')
|
|
1346
|
+
.addColumn('component_path', 'varchar')
|
|
1347
|
+
.addColumn('url', 'varchar')
|
|
1348
|
+
.addColumn('type', 'varchar')
|
|
1349
|
+
.addColumn('screenshot_path', 'varchar')
|
|
1350
|
+
.addColumn('viewport_width', 'integer')
|
|
1351
|
+
.addColumn('viewport_height', 'integer')
|
|
1352
|
+
.addColumn('dimension', 'varchar')
|
|
1353
|
+
.addColumn('dimensions', 'text')
|
|
1354
|
+
.addColumn('screenshot_paths', 'text')
|
|
1355
|
+
.addColumn('page_file_path', 'varchar')
|
|
1356
|
+
.addColumn('entity_sha', 'varchar')
|
|
1357
|
+
.addColumn('display_name', 'varchar')
|
|
1358
|
+
.addColumn('created_at', 'datetime', (col) => col.defaultTo(new Date().toISOString()))
|
|
1359
|
+
.addColumn('updated_at', 'datetime', (col) => col.defaultTo(new Date().toISOString()))
|
|
1360
|
+
.execute();
|
|
1361
|
+
});
|
|
1362
|
+
afterEach(async () => {
|
|
1363
|
+
await db.destroy();
|
|
1364
|
+
});
|
|
1365
|
+
it('should NOT count scenarios that already have entity_sha', async () => {
|
|
1366
|
+
rawDb
|
|
1367
|
+
.prepare(`INSERT INTO editor_scenarios (id, project_id, name, component_name, component_path, url, type, viewport_width, viewport_height, entity_sha)
|
|
1368
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`)
|
|
1369
|
+
.run('sc-1', projectId, 'Header - Default', 'Header', 'src/components/Header.tsx', '/isolated-components/Header', 'component', 1280, 720, 'existing-sha');
|
|
1370
|
+
const count = await countScenariosNeedingEntityBackfill(db);
|
|
1371
|
+
expect(count).toBe(0);
|
|
1372
|
+
});
|
|
1373
|
+
it('should count scenarios with null entity_sha and page_file_path', async () => {
|
|
1374
|
+
rawDb
|
|
1375
|
+
.prepare(`INSERT INTO editor_scenarios (id, project_id, name, page_file_path, url, type, viewport_width, viewport_height)
|
|
1376
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`)
|
|
1377
|
+
.run('sc-2', projectId, 'Home - Default', 'app/page.tsx', '/', 'application', 1280, 720);
|
|
1378
|
+
const count = await countScenariosNeedingEntityBackfill(db);
|
|
1379
|
+
expect(count).toBe(1);
|
|
1380
|
+
});
|
|
1381
|
+
it('should count scenarios with null entity_sha and component_path', async () => {
|
|
1382
|
+
rawDb
|
|
1383
|
+
.prepare(`INSERT INTO editor_scenarios (id, project_id, name, component_name, component_path, url, type, viewport_width, viewport_height)
|
|
1384
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`)
|
|
1385
|
+
.run('sc-3', projectId, 'Footer - Default', 'Footer', 'src/components/Footer.tsx', '/isolated-components/Footer', 'component', 1280, 720);
|
|
1386
|
+
const count = await countScenariosNeedingEntityBackfill(db);
|
|
1387
|
+
expect(count).toBe(1);
|
|
1388
|
+
});
|
|
1389
|
+
it('should NOT count scenarios with null entity_sha and no file paths', async () => {
|
|
1390
|
+
rawDb
|
|
1391
|
+
.prepare(`INSERT INTO editor_scenarios (id, project_id, name, url, type, viewport_width, viewport_height)
|
|
1392
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)`)
|
|
1393
|
+
.run('sc-4', projectId, 'No File Path', '/', 'application', 1280, 720);
|
|
1394
|
+
const count = await countScenariosNeedingEntityBackfill(db);
|
|
1395
|
+
expect(count).toBe(0);
|
|
1396
|
+
});
|
|
1397
|
+
});
|
|
1398
|
+
describe('validateEntityLinkageForAppScenario', () => {
|
|
1399
|
+
let tmpDir;
|
|
1400
|
+
let glossaryPath;
|
|
1401
|
+
beforeEach(() => {
|
|
1402
|
+
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'entity-linkage-'));
|
|
1403
|
+
const codeyamDir = path.join(tmpDir, '.codeyam');
|
|
1404
|
+
fs.mkdirSync(codeyamDir, { recursive: true });
|
|
1405
|
+
glossaryPath = path.join(codeyamDir, 'glossary.json');
|
|
1406
|
+
});
|
|
1407
|
+
afterEach(() => {
|
|
1408
|
+
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
1409
|
+
});
|
|
1410
|
+
it('should return error when file is not in glossary', () => {
|
|
1411
|
+
// Write a glossary that does NOT include the target file
|
|
1412
|
+
fs.writeFileSync(glossaryPath, JSON.stringify([
|
|
1413
|
+
{ name: 'Header', filePath: 'src/components/Header.tsx' },
|
|
1414
|
+
]));
|
|
1415
|
+
const result = validateEntityLinkageForAppScenario({
|
|
1416
|
+
lookupFilePath: 'src/App.tsx',
|
|
1417
|
+
scenarioType: 'application',
|
|
1418
|
+
projectRoot: tmpDir,
|
|
1419
|
+
});
|
|
1420
|
+
expect(result.valid).toBe(false);
|
|
1421
|
+
expect(result.error).toContain('No glossary entry found');
|
|
1422
|
+
expect(result.error).toContain('src/App.tsx');
|
|
1423
|
+
});
|
|
1424
|
+
it('should return needsAnalysis when file IS in glossary but no entity exists', () => {
|
|
1425
|
+
// Write a glossary that includes the target file
|
|
1426
|
+
fs.writeFileSync(glossaryPath, JSON.stringify([
|
|
1427
|
+
{ name: 'App', filePath: 'src/App.tsx', description: 'Main app' },
|
|
1428
|
+
]));
|
|
1429
|
+
const result = validateEntityLinkageForAppScenario({
|
|
1430
|
+
lookupFilePath: 'src/App.tsx',
|
|
1431
|
+
scenarioType: 'application',
|
|
1432
|
+
projectRoot: tmpDir,
|
|
1433
|
+
});
|
|
1434
|
+
expect(result.valid).toBe(true);
|
|
1435
|
+
expect(result.needsAnalysis).toBe(true);
|
|
1436
|
+
});
|
|
1437
|
+
it('should skip validation for component-type scenarios', () => {
|
|
1438
|
+
// No glossary file exists at all
|
|
1439
|
+
const result = validateEntityLinkageForAppScenario({
|
|
1440
|
+
lookupFilePath: 'src/components/Header.tsx',
|
|
1441
|
+
scenarioType: 'component',
|
|
1442
|
+
projectRoot: tmpDir,
|
|
1443
|
+
});
|
|
1444
|
+
expect(result.valid).toBe(true);
|
|
1445
|
+
expect(result.needsAnalysis).toBeUndefined();
|
|
1446
|
+
});
|
|
1447
|
+
it('should skip validation when lookupFilePath is null', () => {
|
|
1448
|
+
const result = validateEntityLinkageForAppScenario({
|
|
1449
|
+
lookupFilePath: null,
|
|
1450
|
+
scenarioType: 'application',
|
|
1451
|
+
projectRoot: tmpDir,
|
|
1452
|
+
});
|
|
1453
|
+
expect(result.valid).toBe(true);
|
|
1454
|
+
expect(result.needsAnalysis).toBeUndefined();
|
|
1455
|
+
});
|
|
1456
|
+
it('should handle glossary wrapped in object format', () => {
|
|
1457
|
+
// LLMs sometimes write glossary as {"components": [...]}
|
|
1458
|
+
fs.writeFileSync(glossaryPath, JSON.stringify({
|
|
1459
|
+
components: [
|
|
1460
|
+
{ name: 'App', filePath: 'src/App.tsx', description: 'Main app' },
|
|
1461
|
+
],
|
|
1462
|
+
}));
|
|
1463
|
+
const result = validateEntityLinkageForAppScenario({
|
|
1464
|
+
lookupFilePath: 'src/App.tsx',
|
|
1465
|
+
scenarioType: 'user',
|
|
1466
|
+
projectRoot: tmpDir,
|
|
1467
|
+
});
|
|
1468
|
+
expect(result.valid).toBe(true);
|
|
1469
|
+
expect(result.needsAnalysis).toBe(true);
|
|
1470
|
+
});
|
|
1471
|
+
it('should return error when glossary file does not exist', () => {
|
|
1472
|
+
// Don't create a glossary file
|
|
1473
|
+
const result = validateEntityLinkageForAppScenario({
|
|
1474
|
+
lookupFilePath: 'src/App.tsx',
|
|
1475
|
+
scenarioType: 'application',
|
|
1476
|
+
projectRoot: tmpDir,
|
|
1477
|
+
});
|
|
1478
|
+
expect(result.valid).toBe(false);
|
|
1479
|
+
expect(result.error).toContain('No glossary entry found');
|
|
1480
|
+
});
|
|
1481
|
+
});
|
|
1058
1482
|
});
|
|
1059
1483
|
//# sourceMappingURL=editorScenarios.test.js.map
|