@codeyam/codeyam-cli 0.1.13 → 0.1.15
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 +6 -6
- package/analyzer-template/log.txt +3 -3
- package/codeyam-cli/src/commands/editor.js +62 -27
- package/codeyam-cli/src/commands/editor.js.map +1 -1
- package/codeyam-cli/src/utils/__tests__/editorAudit.test.js +440 -1
- package/codeyam-cli/src/utils/__tests__/editorAudit.test.js.map +1 -1
- package/codeyam-cli/src/utils/__tests__/editorEntityHelpers.test.js +58 -4
- package/codeyam-cli/src/utils/__tests__/editorEntityHelpers.test.js.map +1 -1
- package/codeyam-cli/src/utils/__tests__/editorScenarios.test.js +76 -0
- package/codeyam-cli/src/utils/__tests__/editorScenarios.test.js.map +1 -1
- package/codeyam-cli/src/utils/__tests__/scenariosManifest.test.js +139 -1
- package/codeyam-cli/src/utils/__tests__/scenariosManifest.test.js.map +1 -1
- package/codeyam-cli/src/utils/backgroundServer.js +1 -1
- package/codeyam-cli/src/utils/backgroundServer.js.map +1 -1
- package/codeyam-cli/src/utils/editorAudit.js +107 -6
- package/codeyam-cli/src/utils/editorAudit.js.map +1 -1
- package/codeyam-cli/src/utils/editorEntityHelpers.js +18 -3
- package/codeyam-cli/src/utils/editorEntityHelpers.js.map +1 -1
- package/codeyam-cli/src/utils/editorScenarios.js +10 -1
- package/codeyam-cli/src/utils/editorScenarios.js.map +1 -1
- package/codeyam-cli/src/utils/scenariosManifest.js +30 -0
- package/codeyam-cli/src/utils/scenariosManifest.js.map +1 -1
- package/codeyam-cli/src/webserver/backgroundServer.js +42 -57
- package/codeyam-cli/src/webserver/backgroundServer.js.map +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{CopyButton-CzTDWkF2.js → CopyButton-CLe80MMu.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{EntityItem-BFbq6iFk.js → EntityItem-Crt_KN_U.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{EntityTypeIcon-B6OMi58N.js → EntityTypeIcon-CD7lGABo.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{InlineSpinner-DuYodzo1.js → InlineSpinner-CgTNOhnu.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{InteractivePreview-CXo9EeCl.js → InteractivePreview-CKeQT5Ty.js} +2 -2
- package/codeyam-cli/src/webserver/build/client/assets/{LibraryFunctionPreview-DYCNb2It.js → LibraryFunctionPreview-D3s1MFkb.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{LogViewer-CZgY3sxX.js → LogViewer-CM5zg40N.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{ReportIssueModal-CnYYwRDw.js → ReportIssueModal-C2PLkej3.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{SafeScreenshot-CDoF7ZpU.js → SafeScreenshot-DanvyBPb.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{ScenarioViewer-DrnfvaLL.js → ScenarioViewer-DUMfcNVK.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{Spinner-Df3UCi8k.js → Spinner-D0LgAaSa.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{ViewportInspectBar-DRKR9T0U.js → ViewportInspectBar-BA_Ry-rs.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{_index-ClR-g3tY.js → _index-BAWd-Xjf.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{activity.(_tab)-DTH6ydEA.js → activity.(_tab)-BOARiB-g.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{addon-web-links-74hnHF59.js → addon-web-links-CHx25PAe.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{agent-transcripts-B8CYhCO9.js → agent-transcripts-Bg3e7q4S.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{book-open-CLaoh4ac.js → book-open-CL-lMgHh.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{chevron-down-BZ2DZxbW.js → chevron-down-GmAjGS9-.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{chunk-JZWAC4HX-BBXArFPl.js → chunk-JZWAC4HX-BAdwhyCx.js} +11 -11
- package/codeyam-cli/src/webserver/build/client/assets/{circle-check-CT4unAk-.js → circle-check-DFcQkN5j.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{copy-zK0B6Nu-.js → copy-C6iF61Xs.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{createLucideIcon-DJB0YQJL.js → createLucideIcon-4ImjHTVC.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{dev.empty-CkXFP_i-.js → dev.empty-C8y4mmyv.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/editor._tab-Gbk_i5Js.js +1 -0
- package/codeyam-cli/src/webserver/build/client/assets/editor.entity.(_sha)-B7xQ9Sjy.js +58 -0
- package/codeyam-cli/src/webserver/build/client/assets/editorPreview-CxmrE6AF.js +41 -0
- package/codeyam-cli/src/webserver/build/client/assets/{entity._sha._-BqAN7hyG.js → entity._sha._-Blfy9UlN.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{entity._sha.scenarios._scenarioId.dev-BOi8kpwd.js → entity._sha.scenarios._scenarioId.dev-CUobbQdQ.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{entity._sha.scenarios._scenarioId.fullscreen-Dg1NhIms.js → entity._sha.scenarios._scenarioId.fullscreen-C6eeL24i.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{entity._sha_.create-scenario-CJX6kkkV.js → entity._sha_.create-scenario-DQM8E7L4.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{entity._sha_.edit._scenarioId-BhVjZhKg.js → entity._sha_.edit._scenarioId-CAoXLsQr.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{entry.client-_gzKltPN.js → entry.client-SuW9syRS.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{files-CV_17tZS.js → files-D-xGrg29.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{git-D-YXmMbR.js → git-Bq_fbXP5.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/globals-fAqOD9ex.css +1 -0
- package/codeyam-cli/src/webserver/build/client/assets/{index-CCrgCshv.js → index-Bp1l4hSv.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{index-BsX0F-9C.js → index-CWV9XZiG.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{index-Blo6EK8G.js → index-DE3jI_dv.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{labs-Byazq8Pv.js → labs-B_IX45ih.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{loader-circle-DVQ0oHR7.js → loader-circle-De-7qQ2u.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/manifest-5d53342d.js +1 -0
- package/codeyam-cli/src/webserver/build/client/assets/{memory-b-VmA2Vj.js → memory-Cx2xEx7s.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{pause-DGcndCAa.js → pause-CFxEKL1u.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/root-DB3O9_9j.js +67 -0
- package/codeyam-cli/src/webserver/build/client/assets/{search-C0Uw0bcK.js → search-BdBb5aqc.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{settings-OoNgHIfW.js → settings-DdE-Untf.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{simulations-Bcemfu8a.js → simulations-DSCdE99u.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{terminal-BgMmG7R9.js → terminal-CrplD4b1.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{triangle-alert-Cs87hJYK.js → triangle-alert-DqJ0j69l.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{useCustomSizes-BR3Rs7JY.js → useCustomSizes-DhXHbEjP.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{useLastLogLine-BxxP_XF9.js → useLastLogLine-BNd5hYuW.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{useReportContext-BermyNU5.js → useReportContext-Cy5Qg_UR.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{useToast-a_QN_W9_.js → useToast-5HR2j9ZE.js} +1 -1
- package/codeyam-cli/src/webserver/build/server/assets/{analysisRunner-lv2ooewK.js → analysisRunner-DcJSnBCE.js} +1 -1
- package/codeyam-cli/src/webserver/build/server/assets/{index-Im3Smyei.js → index-CEaDhUiv.js} +1 -1
- package/codeyam-cli/src/webserver/build/server/assets/{init-BjuAFKGM.js → init-DA7guOrE.js} +1 -1
- package/codeyam-cli/src/webserver/build/server/assets/server-build-juyiY2m6.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/terminalServer.js +1 -1
- package/codeyam-cli/src/webserver/terminalServer.js.map +1 -1
- package/codeyam-cli/templates/nextjs-prisma-sqlite/package.json +1 -1
- package/codeyam-cli/templates/nextjs-prisma-supabase/package.json +1 -1
- package/package.json +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/editor._tab-DPw7NZHc.js +0 -1
- package/codeyam-cli/src/webserver/build/client/assets/editor.entity.(_sha)-CjC3_6JI.js +0 -58
- package/codeyam-cli/src/webserver/build/client/assets/editorPreview-DBa7T2FK.js +0 -41
- package/codeyam-cli/src/webserver/build/client/assets/globals-DRvOjyO3.css +0 -1
- package/codeyam-cli/src/webserver/build/client/assets/manifest-75b1b319.js +0 -1
- package/codeyam-cli/src/webserver/build/client/assets/root-F-k2uYj5.js +0 -67
- package/codeyam-cli/src/webserver/build/server/assets/server-build-CNjF0B9B.js +0 -551
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import Database from 'better-sqlite3';
|
|
2
2
|
import { Kysely, SqliteDialect } from 'kysely';
|
|
3
|
-
import { isComponent, classifyGlossaryEntries, computeAudit, filterGlossaryByChangeStatus, resolveAuditSessionScope, queryScenarioCounts, queryIncompleteEntities, queryMiscategorizedScenarios, isOnlyIncompleteEntities, isAutoRemediable, } from "../editorAudit.js";
|
|
3
|
+
import { isComponent, classifyGlossaryEntries, computeAudit, filterGlossaryByChangeStatus, resolveAuditSessionScope, queryScenarioCounts, queryPageScenarioCounts, queryIncompleteEntities, queryMiscategorizedScenarios, isOnlyIncompleteEntities, isAutoRemediable, identifyScenariosNeedingRecapture, } from "../editorAudit.js";
|
|
4
4
|
describe('editorAudit', () => {
|
|
5
5
|
describe('isComponent', () => {
|
|
6
6
|
it('should return true for JSX.Element return type', () => {
|
|
@@ -290,6 +290,29 @@ describe('editorAudit', () => {
|
|
|
290
290
|
expect(result.functions[0].testsPassing).toBe(false);
|
|
291
291
|
expect(result.functions[0].testsVisibleInUi).toBe(true);
|
|
292
292
|
});
|
|
293
|
+
it('should distinguish runner errors from test failures and include error message', () => {
|
|
294
|
+
const result = computeAudit({
|
|
295
|
+
components: [],
|
|
296
|
+
functions: [
|
|
297
|
+
{
|
|
298
|
+
name: 'getTimeAgo',
|
|
299
|
+
filePath: 'src/lib/format.ts',
|
|
300
|
+
testFile: 'src/lib/format.test.ts',
|
|
301
|
+
},
|
|
302
|
+
],
|
|
303
|
+
scenarioCounts: {},
|
|
304
|
+
testFileExistence: { 'src/lib/format.test.ts': true },
|
|
305
|
+
testResults: {
|
|
306
|
+
'src/lib/format.test.ts': {
|
|
307
|
+
passing: false,
|
|
308
|
+
hasEntityNameDescribe: false,
|
|
309
|
+
errorMessage: 'Error: Cannot find module "@/lib/format" from "src/lib/format.test.ts"',
|
|
310
|
+
},
|
|
311
|
+
},
|
|
312
|
+
});
|
|
313
|
+
expect(result.functions[0].status).toBe('runner_error');
|
|
314
|
+
expect(result.functions[0].errorMessage).toBe('Error: Cannot find module "@/lib/format" from "src/lib/format.test.ts"');
|
|
315
|
+
});
|
|
293
316
|
it('should mark function as name_mismatch when tests pass but no describe matches', () => {
|
|
294
317
|
const result = computeAudit({
|
|
295
318
|
components: [],
|
|
@@ -983,6 +1006,158 @@ describe('editorAudit', () => {
|
|
|
983
1006
|
expect(counts).toEqual({ ArticleCard: 1, ArticleRow: 1 });
|
|
984
1007
|
});
|
|
985
1008
|
});
|
|
1009
|
+
// ── queryPageScenarioCounts ──────────────────────────────────────────
|
|
1010
|
+
describe('queryPageScenarioCounts', () => {
|
|
1011
|
+
let db;
|
|
1012
|
+
let rawDb;
|
|
1013
|
+
const projectId = 'test-project-id';
|
|
1014
|
+
beforeEach(async () => {
|
|
1015
|
+
rawDb = new Database(':memory:');
|
|
1016
|
+
db = new Kysely({ dialect: new SqliteDialect({ database: rawDb }) });
|
|
1017
|
+
await db.schema
|
|
1018
|
+
.createTable('editor_scenarios')
|
|
1019
|
+
.addColumn('id', 'varchar', (col) => col.primaryKey())
|
|
1020
|
+
.addColumn('project_id', 'varchar', (col) => col.notNull())
|
|
1021
|
+
.addColumn('name', 'varchar', (col) => col.notNull())
|
|
1022
|
+
.addColumn('description', 'text')
|
|
1023
|
+
.addColumn('component_name', 'varchar')
|
|
1024
|
+
.addColumn('component_path', 'varchar')
|
|
1025
|
+
.addColumn('page_file_path', 'varchar')
|
|
1026
|
+
.addColumn('url', 'varchar')
|
|
1027
|
+
.addColumn('type', 'varchar')
|
|
1028
|
+
.addColumn('screenshot_path', 'varchar')
|
|
1029
|
+
.addColumn('viewport_width', 'integer')
|
|
1030
|
+
.addColumn('viewport_height', 'integer')
|
|
1031
|
+
.addColumn('dimension', 'varchar')
|
|
1032
|
+
.addColumn('created_at', 'datetime')
|
|
1033
|
+
.addColumn('updated_at', 'datetime')
|
|
1034
|
+
.execute();
|
|
1035
|
+
});
|
|
1036
|
+
afterEach(async () => {
|
|
1037
|
+
await db.destroy();
|
|
1038
|
+
});
|
|
1039
|
+
it('should count app-level scenarios by page_file_path', async () => {
|
|
1040
|
+
// App-level scenario: has page_file_path but no component_name
|
|
1041
|
+
await db
|
|
1042
|
+
.insertInto('editor_scenarios')
|
|
1043
|
+
.values({
|
|
1044
|
+
id: 'sc-1',
|
|
1045
|
+
project_id: projectId,
|
|
1046
|
+
name: 'Library - Default',
|
|
1047
|
+
component_name: null,
|
|
1048
|
+
page_file_path: 'app/library/page.tsx',
|
|
1049
|
+
created_at: '2026-03-12 13:00:00',
|
|
1050
|
+
})
|
|
1051
|
+
.execute();
|
|
1052
|
+
await db
|
|
1053
|
+
.insertInto('editor_scenarios')
|
|
1054
|
+
.values({
|
|
1055
|
+
id: 'sc-2',
|
|
1056
|
+
project_id: projectId,
|
|
1057
|
+
name: 'Library - Empty',
|
|
1058
|
+
component_name: null,
|
|
1059
|
+
page_file_path: 'app/library/page.tsx',
|
|
1060
|
+
created_at: '2026-03-12 13:05:00',
|
|
1061
|
+
})
|
|
1062
|
+
.execute();
|
|
1063
|
+
const counts = await queryPageScenarioCounts(db, projectId, null);
|
|
1064
|
+
expect(counts).toEqual({ 'app/library/page.tsx': 2 });
|
|
1065
|
+
});
|
|
1066
|
+
it('should not count component scenarios (those have component_name)', async () => {
|
|
1067
|
+
// Component scenario — should NOT appear in page counts
|
|
1068
|
+
await db
|
|
1069
|
+
.insertInto('editor_scenarios')
|
|
1070
|
+
.values({
|
|
1071
|
+
id: 'sc-1',
|
|
1072
|
+
project_id: projectId,
|
|
1073
|
+
name: 'ArticleRow - Default',
|
|
1074
|
+
component_name: 'ArticleRow',
|
|
1075
|
+
page_file_path: null,
|
|
1076
|
+
created_at: '2026-03-12 13:00:00',
|
|
1077
|
+
})
|
|
1078
|
+
.execute();
|
|
1079
|
+
const counts = await queryPageScenarioCounts(db, projectId, null);
|
|
1080
|
+
expect(counts).toEqual({});
|
|
1081
|
+
});
|
|
1082
|
+
it('should respect featureStartedAt filter', async () => {
|
|
1083
|
+
await db
|
|
1084
|
+
.insertInto('editor_scenarios')
|
|
1085
|
+
.values({
|
|
1086
|
+
id: 'sc-1',
|
|
1087
|
+
project_id: projectId,
|
|
1088
|
+
name: 'Library - Old',
|
|
1089
|
+
component_name: null,
|
|
1090
|
+
page_file_path: 'app/library/page.tsx',
|
|
1091
|
+
created_at: '2026-03-12 13:00:00',
|
|
1092
|
+
})
|
|
1093
|
+
.execute();
|
|
1094
|
+
await db
|
|
1095
|
+
.insertInto('editor_scenarios')
|
|
1096
|
+
.values({
|
|
1097
|
+
id: 'sc-2',
|
|
1098
|
+
project_id: projectId,
|
|
1099
|
+
name: 'Library - New',
|
|
1100
|
+
component_name: null,
|
|
1101
|
+
page_file_path: 'app/library/page.tsx',
|
|
1102
|
+
created_at: '2026-03-12 15:00:00',
|
|
1103
|
+
})
|
|
1104
|
+
.execute();
|
|
1105
|
+
const counts = await queryPageScenarioCounts(db, projectId, '2026-03-12T14:01:31.291Z');
|
|
1106
|
+
expect(counts).toEqual({ 'app/library/page.tsx': 1 });
|
|
1107
|
+
});
|
|
1108
|
+
it('should count re-registered page scenarios via updated_at', async () => {
|
|
1109
|
+
await db
|
|
1110
|
+
.insertInto('editor_scenarios')
|
|
1111
|
+
.values({
|
|
1112
|
+
id: 'sc-1',
|
|
1113
|
+
project_id: projectId,
|
|
1114
|
+
name: 'Library - Default',
|
|
1115
|
+
component_name: null,
|
|
1116
|
+
page_file_path: 'app/library/page.tsx',
|
|
1117
|
+
created_at: '2026-03-12 13:00:00',
|
|
1118
|
+
updated_at: '2026-03-12 15:00:00',
|
|
1119
|
+
})
|
|
1120
|
+
.execute();
|
|
1121
|
+
const counts = await queryPageScenarioCounts(db, projectId, '2026-03-12T14:01:31.291Z');
|
|
1122
|
+
expect(counts).toEqual({ 'app/library/page.tsx': 1 });
|
|
1123
|
+
});
|
|
1124
|
+
it('should group counts by page_file_path across multiple pages', async () => {
|
|
1125
|
+
await db
|
|
1126
|
+
.insertInto('editor_scenarios')
|
|
1127
|
+
.values([
|
|
1128
|
+
{
|
|
1129
|
+
id: 'sc-1',
|
|
1130
|
+
project_id: projectId,
|
|
1131
|
+
name: 'Library - Default',
|
|
1132
|
+
component_name: null,
|
|
1133
|
+
page_file_path: 'app/library/page.tsx',
|
|
1134
|
+
created_at: '2026-03-12 13:00:00',
|
|
1135
|
+
},
|
|
1136
|
+
{
|
|
1137
|
+
id: 'sc-2',
|
|
1138
|
+
project_id: projectId,
|
|
1139
|
+
name: 'Library - Rich',
|
|
1140
|
+
component_name: null,
|
|
1141
|
+
page_file_path: 'app/library/page.tsx',
|
|
1142
|
+
created_at: '2026-03-12 13:00:00',
|
|
1143
|
+
},
|
|
1144
|
+
{
|
|
1145
|
+
id: 'sc-3',
|
|
1146
|
+
project_id: projectId,
|
|
1147
|
+
name: 'Collections - Default',
|
|
1148
|
+
component_name: null,
|
|
1149
|
+
page_file_path: 'app/library/collections/page.tsx',
|
|
1150
|
+
created_at: '2026-03-12 13:00:00',
|
|
1151
|
+
},
|
|
1152
|
+
])
|
|
1153
|
+
.execute();
|
|
1154
|
+
const counts = await queryPageScenarioCounts(db, projectId, null);
|
|
1155
|
+
expect(counts).toEqual({
|
|
1156
|
+
'app/library/page.tsx': 2,
|
|
1157
|
+
'app/library/collections/page.tsx': 1,
|
|
1158
|
+
});
|
|
1159
|
+
});
|
|
1160
|
+
});
|
|
986
1161
|
// ── Audit + entity completeness integration ─────────────────────────
|
|
987
1162
|
describe('audit should catch incomplete entities (bug reproduction)', () => {
|
|
988
1163
|
let db;
|
|
@@ -1856,6 +2031,84 @@ describe('editorAudit', () => {
|
|
|
1856
2031
|
const result = await queryIncompleteEntities(db, projectId, null);
|
|
1857
2032
|
expect(result).toEqual([]);
|
|
1858
2033
|
});
|
|
2034
|
+
it('should not flag entities when a sibling version (same name+filePath) has analyses', async () => {
|
|
2035
|
+
// Old entity version WITH analysis
|
|
2036
|
+
await db
|
|
2037
|
+
.insertInto('entities')
|
|
2038
|
+
.values({
|
|
2039
|
+
sha: 'sha-btn-v1',
|
|
2040
|
+
name: 'OpenLibraryButton',
|
|
2041
|
+
entity_type: 'visual',
|
|
2042
|
+
file_path: 'src/components/OpenLibraryButton.tsx',
|
|
2043
|
+
})
|
|
2044
|
+
.execute();
|
|
2045
|
+
await db
|
|
2046
|
+
.insertInto('analyses')
|
|
2047
|
+
.values({
|
|
2048
|
+
id: 'a-btn-v1',
|
|
2049
|
+
entity_sha: 'sha-btn-v1',
|
|
2050
|
+
entity_name: 'OpenLibraryButton',
|
|
2051
|
+
project_id: projectId,
|
|
2052
|
+
})
|
|
2053
|
+
.execute();
|
|
2054
|
+
// New entity version WITHOUT analysis (created by file watcher)
|
|
2055
|
+
await db
|
|
2056
|
+
.insertInto('entities')
|
|
2057
|
+
.values({
|
|
2058
|
+
sha: 'sha-btn-v2',
|
|
2059
|
+
name: 'OpenLibraryButton',
|
|
2060
|
+
entity_type: 'visual',
|
|
2061
|
+
file_path: 'src/components/OpenLibraryButton.tsx',
|
|
2062
|
+
})
|
|
2063
|
+
.execute();
|
|
2064
|
+
// Scenario points to the NEW version (backfilled after file watcher)
|
|
2065
|
+
await db
|
|
2066
|
+
.insertInto('editor_scenarios')
|
|
2067
|
+
.values({
|
|
2068
|
+
id: 'sc-btn',
|
|
2069
|
+
project_id: projectId,
|
|
2070
|
+
name: 'OpenLibraryButton - Default',
|
|
2071
|
+
component_name: 'OpenLibraryButton',
|
|
2072
|
+
entity_sha: 'sha-btn-v2',
|
|
2073
|
+
created_at: '2026-03-16 23:00:00',
|
|
2074
|
+
})
|
|
2075
|
+
.execute();
|
|
2076
|
+
// Should NOT flag as incomplete — sibling version has analyses
|
|
2077
|
+
const result = await queryIncompleteEntities(db, projectId, null);
|
|
2078
|
+
expect(result).toEqual([]);
|
|
2079
|
+
});
|
|
2080
|
+
it('should still flag entities when no sibling version has analyses', async () => {
|
|
2081
|
+
// Only one version, no analyses
|
|
2082
|
+
await db
|
|
2083
|
+
.insertInto('entities')
|
|
2084
|
+
.values({
|
|
2085
|
+
sha: 'sha-icon',
|
|
2086
|
+
name: 'ExternalLinkIcon',
|
|
2087
|
+
entity_type: 'visual',
|
|
2088
|
+
file_path: 'src/components/ExternalLinkIcon.tsx',
|
|
2089
|
+
})
|
|
2090
|
+
.execute();
|
|
2091
|
+
await db
|
|
2092
|
+
.insertInto('editor_scenarios')
|
|
2093
|
+
.values({
|
|
2094
|
+
id: 'sc-icon',
|
|
2095
|
+
project_id: projectId,
|
|
2096
|
+
name: 'ExternalLinkIcon - Default',
|
|
2097
|
+
component_name: 'ExternalLinkIcon',
|
|
2098
|
+
entity_sha: 'sha-icon',
|
|
2099
|
+
created_at: '2026-03-16 23:00:00',
|
|
2100
|
+
})
|
|
2101
|
+
.execute();
|
|
2102
|
+
// Should flag as incomplete — no version has analyses
|
|
2103
|
+
const result = await queryIncompleteEntities(db, projectId, null);
|
|
2104
|
+
expect(result).toEqual([
|
|
2105
|
+
{
|
|
2106
|
+
entitySha: 'sha-icon',
|
|
2107
|
+
name: 'ExternalLinkIcon',
|
|
2108
|
+
scenarioCount: 1,
|
|
2109
|
+
},
|
|
2110
|
+
]);
|
|
2111
|
+
});
|
|
1859
2112
|
it('should use entity name from entities table, falling back to component_name', async () => {
|
|
1860
2113
|
// Scenario has entity_sha but entity record doesn't exist
|
|
1861
2114
|
await db
|
|
@@ -1875,5 +2128,191 @@ describe('editorAudit', () => {
|
|
|
1875
2128
|
]);
|
|
1876
2129
|
});
|
|
1877
2130
|
});
|
|
2131
|
+
// ── identifyScenariosNeedingRecapture ──────────────────────────────
|
|
2132
|
+
describe('identifyScenariosNeedingRecapture', () => {
|
|
2133
|
+
// Reproduces the Margo bug: Feature 1 built app-level popup scenarios,
|
|
2134
|
+
// Feature 2 edited LibraryView (used by App), but app-level scenarios
|
|
2135
|
+
// were never flagged for recapture because the audit only checked
|
|
2136
|
+
// component scenario existence — not whether app-level scenarios are stale.
|
|
2137
|
+
//
|
|
2138
|
+
// Each scenario's entityName is resolved by the caller via
|
|
2139
|
+
// entity_sha → entities.name (the default export for app-level scenarios).
|
|
2140
|
+
it('should flag app-level scenario when its entity is impacted by transitive dependency change', () => {
|
|
2141
|
+
// LibraryView was edited → App is impacted (imports LibraryView)
|
|
2142
|
+
// App-level scenario "Library - Rich Library" has entity_sha pointing to App
|
|
2143
|
+
// It was NOT recaptured during Feature 2 → should be flagged
|
|
2144
|
+
const entityChangeStatus = {
|
|
2145
|
+
LibraryView: { status: 'edited' },
|
|
2146
|
+
App: {
|
|
2147
|
+
status: 'impacted',
|
|
2148
|
+
impactedBy: [
|
|
2149
|
+
{
|
|
2150
|
+
name: 'LibraryView',
|
|
2151
|
+
filePath: 'src/components/LibraryView.tsx',
|
|
2152
|
+
changeType: 'edited',
|
|
2153
|
+
},
|
|
2154
|
+
],
|
|
2155
|
+
},
|
|
2156
|
+
};
|
|
2157
|
+
const result = identifyScenariosNeedingRecapture({
|
|
2158
|
+
scenarios: [
|
|
2159
|
+
{
|
|
2160
|
+
name: 'Library - Rich Library',
|
|
2161
|
+
entityName: 'App', // resolved from entity_sha → entities.name
|
|
2162
|
+
updatedInSession: false,
|
|
2163
|
+
},
|
|
2164
|
+
],
|
|
2165
|
+
entityChangeStatus,
|
|
2166
|
+
});
|
|
2167
|
+
expect(result).toHaveLength(1);
|
|
2168
|
+
expect(result[0].scenarioName).toBe('Library - Rich Library');
|
|
2169
|
+
expect(result[0].entityName).toBe('App');
|
|
2170
|
+
expect(result[0].status.status).toBe('impacted');
|
|
2171
|
+
});
|
|
2172
|
+
it('should flag component scenario when its entity is directly edited and not recaptured', () => {
|
|
2173
|
+
const entityChangeStatus = {
|
|
2174
|
+
LibraryView: { status: 'edited' },
|
|
2175
|
+
};
|
|
2176
|
+
const result = identifyScenariosNeedingRecapture({
|
|
2177
|
+
scenarios: [
|
|
2178
|
+
{
|
|
2179
|
+
name: 'LibraryView - Empty',
|
|
2180
|
+
entityName: 'LibraryView',
|
|
2181
|
+
updatedInSession: false,
|
|
2182
|
+
},
|
|
2183
|
+
],
|
|
2184
|
+
entityChangeStatus,
|
|
2185
|
+
});
|
|
2186
|
+
expect(result).toHaveLength(1);
|
|
2187
|
+
expect(result[0].scenarioName).toBe('LibraryView - Empty');
|
|
2188
|
+
expect(result[0].entityName).toBe('LibraryView');
|
|
2189
|
+
expect(result[0].status.status).toBe('edited');
|
|
2190
|
+
});
|
|
2191
|
+
it('should NOT flag scenario that was already recaptured in the current session', () => {
|
|
2192
|
+
const entityChangeStatus = {
|
|
2193
|
+
App: {
|
|
2194
|
+
status: 'impacted',
|
|
2195
|
+
impactedBy: [
|
|
2196
|
+
{
|
|
2197
|
+
name: 'LibraryView',
|
|
2198
|
+
filePath: 'src/components/LibraryView.tsx',
|
|
2199
|
+
changeType: 'edited',
|
|
2200
|
+
},
|
|
2201
|
+
],
|
|
2202
|
+
},
|
|
2203
|
+
};
|
|
2204
|
+
const result = identifyScenariosNeedingRecapture({
|
|
2205
|
+
scenarios: [
|
|
2206
|
+
{
|
|
2207
|
+
name: 'App - Default',
|
|
2208
|
+
entityName: 'App',
|
|
2209
|
+
updatedInSession: true, // re-registered during Feature 2
|
|
2210
|
+
},
|
|
2211
|
+
],
|
|
2212
|
+
entityChangeStatus,
|
|
2213
|
+
});
|
|
2214
|
+
expect(result).toHaveLength(0);
|
|
2215
|
+
});
|
|
2216
|
+
it('should NOT flag scenario whose entity has no change status', () => {
|
|
2217
|
+
const entityChangeStatus = {
|
|
2218
|
+
LibraryView: { status: 'edited' },
|
|
2219
|
+
};
|
|
2220
|
+
const result = identifyScenariosNeedingRecapture({
|
|
2221
|
+
scenarios: [
|
|
2222
|
+
{
|
|
2223
|
+
name: 'WelcomeScreen - Default',
|
|
2224
|
+
entityName: 'WelcomeScreen',
|
|
2225
|
+
updatedInSession: false,
|
|
2226
|
+
},
|
|
2227
|
+
],
|
|
2228
|
+
entityChangeStatus,
|
|
2229
|
+
});
|
|
2230
|
+
expect(result).toHaveLength(0);
|
|
2231
|
+
});
|
|
2232
|
+
it('should return empty array when entityChangeStatus is undefined', () => {
|
|
2233
|
+
const result = identifyScenariosNeedingRecapture({
|
|
2234
|
+
scenarios: [
|
|
2235
|
+
{
|
|
2236
|
+
name: 'Library - Rich Library',
|
|
2237
|
+
entityName: 'App',
|
|
2238
|
+
updatedInSession: false,
|
|
2239
|
+
},
|
|
2240
|
+
],
|
|
2241
|
+
entityChangeStatus: undefined,
|
|
2242
|
+
});
|
|
2243
|
+
expect(result).toHaveLength(0);
|
|
2244
|
+
});
|
|
2245
|
+
it('should return empty array when entityChangeStatus is empty', () => {
|
|
2246
|
+
const result = identifyScenariosNeedingRecapture({
|
|
2247
|
+
scenarios: [
|
|
2248
|
+
{
|
|
2249
|
+
name: 'Library - Rich Library',
|
|
2250
|
+
entityName: 'App',
|
|
2251
|
+
updatedInSession: false,
|
|
2252
|
+
},
|
|
2253
|
+
],
|
|
2254
|
+
entityChangeStatus: {},
|
|
2255
|
+
});
|
|
2256
|
+
expect(result).toHaveLength(0);
|
|
2257
|
+
});
|
|
2258
|
+
it('should flag multiple app-level scenarios sharing the same impacted entity', () => {
|
|
2259
|
+
const entityChangeStatus = {
|
|
2260
|
+
LibraryView: { status: 'edited' },
|
|
2261
|
+
App: {
|
|
2262
|
+
status: 'impacted',
|
|
2263
|
+
impactedBy: [
|
|
2264
|
+
{
|
|
2265
|
+
name: 'LibraryView',
|
|
2266
|
+
filePath: 'src/components/LibraryView.tsx',
|
|
2267
|
+
changeType: 'edited',
|
|
2268
|
+
},
|
|
2269
|
+
],
|
|
2270
|
+
},
|
|
2271
|
+
};
|
|
2272
|
+
const result = identifyScenariosNeedingRecapture({
|
|
2273
|
+
scenarios: [
|
|
2274
|
+
{
|
|
2275
|
+
name: 'Library - Empty',
|
|
2276
|
+
entityName: 'App',
|
|
2277
|
+
updatedInSession: false,
|
|
2278
|
+
},
|
|
2279
|
+
{
|
|
2280
|
+
name: 'Library - Rich Library',
|
|
2281
|
+
entityName: 'App',
|
|
2282
|
+
updatedInSession: false,
|
|
2283
|
+
},
|
|
2284
|
+
{
|
|
2285
|
+
name: 'First Article Saved',
|
|
2286
|
+
entityName: 'App',
|
|
2287
|
+
updatedInSession: false,
|
|
2288
|
+
},
|
|
2289
|
+
],
|
|
2290
|
+
entityChangeStatus,
|
|
2291
|
+
});
|
|
2292
|
+
expect(result).toHaveLength(3);
|
|
2293
|
+
expect(result.map((r) => r.scenarioName).sort()).toEqual([
|
|
2294
|
+
'First Article Saved',
|
|
2295
|
+
'Library - Empty',
|
|
2296
|
+
'Library - Rich Library',
|
|
2297
|
+
]);
|
|
2298
|
+
expect(result.every((r) => r.entityName === 'App')).toBe(true);
|
|
2299
|
+
});
|
|
2300
|
+
it('should skip scenarios with null entityName (no entity_sha set)', () => {
|
|
2301
|
+
const entityChangeStatus = {
|
|
2302
|
+
App: { status: 'edited' },
|
|
2303
|
+
};
|
|
2304
|
+
const result = identifyScenariosNeedingRecapture({
|
|
2305
|
+
scenarios: [
|
|
2306
|
+
{
|
|
2307
|
+
name: 'Mystery Scenario',
|
|
2308
|
+
entityName: null, // no entity_sha → no entity name
|
|
2309
|
+
updatedInSession: false,
|
|
2310
|
+
},
|
|
2311
|
+
],
|
|
2312
|
+
entityChangeStatus,
|
|
2313
|
+
});
|
|
2314
|
+
expect(result).toHaveLength(0);
|
|
2315
|
+
});
|
|
2316
|
+
});
|
|
1878
2317
|
});
|
|
1879
2318
|
//# sourceMappingURL=editorAudit.test.js.map
|