@mmapp/react-compiler 0.1.0-alpha.1 → 0.1.0-alpha.5
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/ATOM-PIPELINE.md +144 -0
- package/README.md +88 -40
- package/dist/babel/index.js +2814 -277
- package/dist/babel/index.mjs +2 -2
- package/dist/chunk-3USIFFE4.mjs +2190 -0
- package/dist/chunk-45YMGEVT.mjs +186 -0
- package/dist/chunk-4FN2AISW.mjs +148 -0
- package/dist/chunk-4OPI5L7G.mjs +2593 -0
- package/dist/chunk-4RYTKOOJ.mjs +186 -0
- package/dist/chunk-52XHYD2V.mjs +214 -0
- package/dist/chunk-5GUFFFGL.mjs +148 -0
- package/dist/chunk-5RKTOVR5.mjs +244 -0
- package/dist/chunk-5YDMOO4X.mjs +214 -0
- package/dist/chunk-64ZWEMLJ.mjs +148 -0
- package/dist/chunk-6XP4KSWQ.mjs +2190 -0
- package/dist/chunk-72QWL54I.mjs +175 -0
- package/dist/chunk-7B4TRI7C.mjs +4835 -0
- package/dist/chunk-7ZKGHTNB.mjs +4952 -0
- package/dist/chunk-CIESM3BP.mjs +33 -0
- package/dist/chunk-DE3ZGQAC.mjs +148 -0
- package/dist/chunk-DMCY3BBG.mjs +1933 -0
- package/dist/chunk-DPIK3PJS.mjs +244 -0
- package/dist/chunk-E5IVH4RE.mjs +186 -0
- package/dist/chunk-E6FZNUR5.mjs +4953 -0
- package/dist/chunk-EJRBDQDP.mjs +2607 -0
- package/dist/chunk-ELO4TXJL.mjs +186 -0
- package/dist/chunk-EO6SYNCG.mjs +175 -0
- package/dist/chunk-FKRO52XH.mjs +3446 -0
- package/dist/chunk-FL4YAKU6.mjs +4941 -0
- package/dist/chunk-FYT47UBU.mjs +5076 -0
- package/dist/chunk-GCLGPOJZ.mjs +148 -0
- package/dist/chunk-GXB4JOP7.mjs +5072 -0
- package/dist/chunk-HFXOUMTD.mjs +175 -0
- package/dist/chunk-HWIZ47US.mjs +214 -0
- package/dist/chunk-IB7MNPQL.mjs +4953 -0
- package/dist/chunk-ICSIHQCG.mjs +148 -0
- package/dist/chunk-J7JUAHS4.mjs +186 -0
- package/dist/chunk-JLA5VNQ3.mjs +186 -0
- package/dist/chunk-JQLWFCTM.mjs +214 -0
- package/dist/chunk-KFJJCQAL.mjs +148 -0
- package/dist/chunk-KJUIIEQE.mjs +186 -0
- package/dist/chunk-KNWTHRVQ.mjs +175 -0
- package/dist/chunk-KSG4XSZF.mjs +175 -0
- package/dist/chunk-LF5N6DOU.mjs +175 -0
- package/dist/chunk-LJQCM2IM.mjs +214 -0
- package/dist/chunk-NTB7OEX2.mjs +2918 -0
- package/dist/chunk-NW6555WJ.mjs +186 -0
- package/dist/chunk-OMZE6VLQ.mjs +214 -0
- package/dist/chunk-OPJKP747.mjs +7506 -0
- package/dist/chunk-P4BR7WVO.mjs +2190 -0
- package/dist/chunk-QQHVYH2X.mjs +244 -0
- package/dist/chunk-S5QLWLLT.mjs +186 -0
- package/dist/chunk-SCWGT2FY.mjs +2190 -0
- package/dist/chunk-SMKJUSB3.mjs +2190 -0
- package/dist/chunk-THFYE5ZX.mjs +244 -0
- package/dist/chunk-VCAY2KGM.mjs +175 -0
- package/dist/chunk-WBYMW4NQ.mjs +3450 -0
- package/dist/chunk-WECAV6QB.mjs +148 -0
- package/dist/chunk-WMKBXUCE.mjs +3228 -0
- package/dist/chunk-XAJ5BKKL.mjs +4947 -0
- package/dist/chunk-XG2X7AEA.mjs +175 -0
- package/dist/chunk-XG7Z23NQ.mjs +148 -0
- package/dist/chunk-XWZAOCQ7.mjs +2607 -0
- package/dist/chunk-Y6MA7ULW.mjs +148 -0
- package/dist/chunk-YMS7Q7LG.mjs +214 -0
- package/dist/chunk-ZA37XTGA.mjs +175 -0
- package/dist/cli/index.js +13189 -6838
- package/dist/cli/index.mjs +140 -22
- package/dist/codemod/cli.mjs +1 -1
- package/dist/codemod/index.mjs +1 -1
- package/dist/config-PL24KEWL.mjs +219 -0
- package/dist/dev-server-RmGHIntF.d.mts +113 -0
- package/dist/dev-server-RmGHIntF.d.ts +113 -0
- package/dist/dev-server.d.mts +1 -1
- package/dist/dev-server.d.ts +1 -1
- package/dist/dev-server.js +4135 -440
- package/dist/dev-server.mjs +5 -5
- package/dist/envelope.js +2812 -275
- package/dist/envelope.mjs +3 -3
- package/dist/index.d.mts +161 -2
- package/dist/index.d.ts +161 -2
- package/dist/index.js +4429 -428
- package/dist/index.mjs +217 -9
- package/{src/cli/init.ts → dist/init-7JQMAAXS.mjs} +70 -95
- package/dist/init-DQDX3QK6.mjs +369 -0
- package/dist/init-EHO4VQ22.mjs +369 -0
- package/dist/init-UC3FWPIW.mjs +367 -0
- package/dist/init-UNSMVKIK.mjs +366 -0
- package/dist/init-UNV5XIDE.mjs +367 -0
- package/dist/project-compiler-2P4N4DR7.mjs +10 -0
- package/dist/project-compiler-D2LCC27O.mjs +10 -0
- package/dist/project-compiler-EJ3GANJE.mjs +10 -0
- package/dist/project-compiler-LOQKVRZJ.mjs +10 -0
- package/dist/project-compiler-OP2VVGJQ.mjs +10 -0
- package/dist/project-compiler-RQ6OQKRM.mjs +10 -0
- package/dist/project-compiler-VWNNCHGO.mjs +10 -0
- package/dist/project-compiler-XVAAU4C5.mjs +10 -0
- package/dist/project-compiler-YES5FGMD.mjs +10 -0
- package/dist/project-compiler-ZKMQDLGU.mjs +10 -0
- package/dist/project-decompiler-FLXCEJHS.mjs +7 -0
- package/dist/project-decompiler-US7GAVIC.mjs +7 -0
- package/dist/project-decompiler-VLPR22QF.mjs +7 -0
- package/dist/pull-FUS5QYZS.mjs +109 -0
- package/dist/pull-LD5ENLGY.mjs +109 -0
- package/dist/pull-P44LDRWB.mjs +109 -0
- package/dist/testing/index.js +2822 -285
- package/dist/testing/index.mjs +2 -2
- package/dist/verify-SEIXUGN4.mjs +1833 -0
- package/dist/vite/index.js +2815 -278
- package/dist/vite/index.mjs +3 -3
- package/examples/uber-app/app/admin/fleet.tsx +19 -19
- package/package.json +16 -6
- package/compile-blueprint-chat.mjs +0 -99
- package/compile-blueprint-glass-console.mjs +0 -98
- package/compile-chat-defs.mjs +0 -92
- package/examples/uber-app/tests/payment.test.tsx +0 -129
- package/examples/uber-app/tests/ride-flow.test.tsx +0 -123
- package/package.json.backup +0 -86
- package/scripts/decompile.ts +0 -226
- package/scripts/seed-auth.ts +0 -267
- package/scripts/seed-uber.ts +0 -248
- package/scripts/validate-uber.ts +0 -119
- package/seed-blueprint-chat.mjs +0 -444
- package/seed-blueprint-glass-console.mjs +0 -445
- package/seed-compiled.mjs +0 -318
- package/src/RoundTripValidator.ts +0 -400
- package/src/__tests__/atom-rendering-coverage.test.ts +0 -680
- package/src/__tests__/auth-module-compilation.test.ts +0 -247
- package/src/__tests__/auth-template-compilation.test.ts +0 -589
- package/src/__tests__/change-extractor.test.ts +0 -142
- package/src/__tests__/cli-pull.test.ts +0 -73
- package/src/__tests__/cli-test.test.ts +0 -72
- package/src/__tests__/component-extractor.test.ts +0 -331
- package/src/__tests__/context-extractor.test.ts +0 -145
- package/src/__tests__/decompiler.test.ts +0 -718
- package/src/__tests__/define-blueprint.test.ts +0 -133
- package/src/__tests__/definition-validator.test.ts +0 -519
- package/src/__tests__/during-extractor.test.ts +0 -152
- package/src/__tests__/effect-extractor.test.ts +0 -107
- package/src/__tests__/event-emission.test.ts +0 -127
- package/src/__tests__/examples.test.ts +0 -236
- package/src/__tests__/full-blueprint-coverage.test.ts +0 -1221
- package/src/__tests__/golden-suite.test.ts +0 -403
- package/src/__tests__/grammar-island-extractor.test.ts +0 -289
- package/src/__tests__/instance-key.test.ts +0 -82
- package/src/__tests__/ir-migration.test.ts +0 -255
- package/src/__tests__/lock-file.test.ts +0 -117
- package/src/__tests__/model-extractor.test.ts +0 -195
- package/src/__tests__/model-field-acl.test.ts +0 -237
- package/src/__tests__/model-hooks.test.ts +0 -130
- package/src/__tests__/model-ref-resolution.test.ts +0 -268
- package/src/__tests__/model-roundtrip.test.ts +0 -502
- package/src/__tests__/model-runtime.test.ts +0 -112
- package/src/__tests__/model-transitions.test.ts +0 -183
- package/src/__tests__/nrt-action-trace.test.ts +0 -391
- package/src/__tests__/pipeline-hardening.test.ts +0 -413
- package/src/__tests__/project-compiler.test.ts +0 -546
- package/src/__tests__/project-decompiler.test.ts +0 -343
- package/src/__tests__/query-compilation.test.ts +0 -145
- package/src/__tests__/round-trip/PLAN.md +0 -158
- package/src/__tests__/round-trip/README.md +0 -52
- package/src/__tests__/round-trip/RESULTS.md +0 -86
- package/src/__tests__/round-trip/fixtures/data-heavy/main.workflow.tsx +0 -55
- package/src/__tests__/round-trip/fixtures/data-heavy/mm.config.ts +0 -11
- package/src/__tests__/round-trip/fixtures/data-heavy/models/contact.ts +0 -54
- package/src/__tests__/round-trip/fixtures/full-workflow/main.workflow.tsx +0 -79
- package/src/__tests__/round-trip/fixtures/full-workflow/mm.config.ts +0 -12
- package/src/__tests__/round-trip/fixtures/full-workflow/models/order.ts +0 -50
- package/src/__tests__/round-trip/fixtures/simple-crud/main.workflow.tsx +0 -25
- package/src/__tests__/round-trip/fixtures/simple-crud/mm.config.ts +0 -11
- package/src/__tests__/round-trip/fixtures/simple-crud/models/task.ts +0 -32
- package/src/__tests__/round-trip/fixtures/view-heavy/main.workflow.tsx +0 -79
- package/src/__tests__/round-trip/fixtures/view-heavy/mm.config.ts +0 -10
- package/src/__tests__/round-trip/round-trip.test.ts +0 -2598
- package/src/__tests__/round-trip-ir.test.ts +0 -300
- package/src/__tests__/round-trip.test.ts +0 -1212
- package/src/__tests__/route-merging.test.ts +0 -372
- package/src/__tests__/router-composition.test.ts +0 -489
- package/src/__tests__/router-extractor.test.ts +0 -176
- package/src/__tests__/server-action-extractor.test.ts +0 -128
- package/src/__tests__/smart-type-inference.test.ts +0 -365
- package/src/__tests__/source-envelope.test.ts +0 -284
- package/src/__tests__/source-fidelity.test.ts +0 -516
- package/src/__tests__/state-extractor.test.ts +0 -115
- package/src/__tests__/strict-mode.test.ts +0 -227
- package/src/__tests__/transition-effect-extractor.test.ts +0 -119
- package/src/__tests__/transition-extractor.test.ts +0 -68
- package/src/__tests__/ts-to-expression.test.ts +0 -462
- package/src/__tests__/type-generator.test.ts +0 -201
- package/src/__tests__/uber-validation.test.ts +0 -502
- package/src/action-compiler.ts +0 -361
- package/src/babel/emitters/experience-transform.ts +0 -199
- package/src/babel/emitters/ir-to-tsx-emitter.ts +0 -110
- package/src/babel/emitters/pure-form-emitter.ts +0 -1023
- package/src/babel/emitters/runtime-glue-emitter.ts +0 -39
- package/src/babel/extractors/change-extractor.ts +0 -199
- package/src/babel/extractors/component-extractor.ts +0 -907
- package/src/babel/extractors/computed-extractor.ts +0 -262
- package/src/babel/extractors/context-extractor.ts +0 -277
- package/src/babel/extractors/during-extractor.ts +0 -295
- package/src/babel/extractors/effect-extractor.ts +0 -340
- package/src/babel/extractors/event-extractor.ts +0 -235
- package/src/babel/extractors/grammar-island-extractor.ts +0 -302
- package/src/babel/extractors/model-extractor.ts +0 -1018
- package/src/babel/extractors/router-extractor.ts +0 -303
- package/src/babel/extractors/server-action-extractor.ts +0 -173
- package/src/babel/extractors/server-action-hook-extractor.ts +0 -72
- package/src/babel/extractors/server-state-extractor.ts +0 -88
- package/src/babel/extractors/state-extractor.ts +0 -214
- package/src/babel/extractors/transition-effect-extractor.ts +0 -176
- package/src/babel/extractors/transition-extractor.ts +0 -143
- package/src/babel/index.ts +0 -24
- package/src/babel/transpilers/ts-to-expression.ts +0 -674
- package/src/babel/visitor.ts +0 -807
- package/src/cli/auth.ts +0 -255
- package/src/cli/build.ts +0 -288
- package/src/cli/deploy.ts +0 -206
- package/src/cli/index.ts +0 -328
- package/src/cli/installer.ts +0 -261
- package/src/cli/lock-file.ts +0 -94
- package/src/cli/mmrc.ts +0 -22
- package/src/cli/pull.ts +0 -172
- package/src/cli/registry-client.ts +0 -175
- package/src/cli/test.ts +0 -397
- package/src/cli/type-generator.ts +0 -243
- package/src/codemod/__tests__/forward.test.ts +0 -239
- package/src/codemod/__tests__/reverse.test.ts +0 -145
- package/src/codemod/__tests__/round-trip.test.ts +0 -137
- package/src/codemod/annotation.ts +0 -97
- package/src/codemod/classify.ts +0 -197
- package/src/codemod/cli.ts +0 -207
- package/src/codemod/control-flow.ts +0 -409
- package/src/codemod/forward.ts +0 -244
- package/src/codemod/import-manager.ts +0 -171
- package/src/codemod/index.ts +0 -120
- package/src/codemod/reverse.ts +0 -197
- package/src/codemod/rules.ts +0 -174
- package/src/codemod/state-transform.ts +0 -126
- package/src/decompiler/ast-builder.ts +0 -538
- package/src/decompiler/config-generator.ts +0 -151
- package/src/decompiler/index.ts +0 -315
- package/src/decompiler/project-decompiler.ts +0 -1776
- package/src/decompiler/project.ts +0 -862
- package/src/decompiler/split-strategy.ts +0 -140
- package/src/decompiler/state-emitter.ts +0 -1053
- package/src/decompiler/sx-emitter.ts +0 -318
- package/src/decompiler/workspace-hydrator.ts +0 -189
- package/src/dev-server.ts +0 -238
- package/src/envelope/fs-tree.ts +0 -217
- package/src/envelope/source-envelope.ts +0 -264
- package/src/envelope.ts +0 -315
- package/src/incremental-compiler.ts +0 -401
- package/src/index.ts +0 -99
- package/src/model-compiler.ts +0 -277
- package/src/project-compiler.ts +0 -1629
- package/src/route-extractor.ts +0 -333
- package/src/testing/index.ts +0 -32
- package/src/testing/snapshot.ts +0 -252
- package/src/testing/test-utils.ts +0 -226
- package/src/types.ts +0 -68
- package/src/vite/index.ts +0 -288
- package/test-compile.mjs +0 -142
- package/tsconfig.json +0 -25
- package/tsup.config.ts +0 -23
- package/vitest.config.ts +0 -9
|
@@ -1,489 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Router/Route Experience Composition Tests
|
|
3
|
-
*
|
|
4
|
-
* Validates that compileProject() correctly composes Router/Route atoms
|
|
5
|
-
* from app/ page files, including nav links, index routes, blueprint_manifest,
|
|
6
|
-
* and componentDefinitions metadata.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
import { describe, it, expect } from 'vitest';
|
|
10
|
-
import { compileProject } from '../project-compiler';
|
|
11
|
-
import type { IRExperienceNode } from '@mindmatrix/player-core';
|
|
12
|
-
|
|
13
|
-
// =============================================================================
|
|
14
|
-
// Helpers
|
|
15
|
-
// =============================================================================
|
|
16
|
-
|
|
17
|
-
/** Minimal page TSX that the babel plugin can compile (returns JSX). */
|
|
18
|
-
function page(title: string): string {
|
|
19
|
-
return `
|
|
20
|
-
export default function ${title.replace(/[^a-zA-Z]/g, '')}Page() {
|
|
21
|
-
return <div><h1>${title}</h1></div>;
|
|
22
|
-
}
|
|
23
|
-
`;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
/** Extract the composed experience from the parent IR. */
|
|
27
|
-
function getExperience(ir: any): IRExperienceNode | undefined {
|
|
28
|
-
return ir.experience as IRExperienceNode | undefined;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
/** Find a child node by component type (recursive). */
|
|
32
|
-
function findByComponent(node: IRExperienceNode, component: string): IRExperienceNode | undefined {
|
|
33
|
-
if (node.component === component) return node;
|
|
34
|
-
for (const child of node.children ?? []) {
|
|
35
|
-
const found = findByComponent(child, component);
|
|
36
|
-
if (found) return found;
|
|
37
|
-
}
|
|
38
|
-
return undefined;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
/** Collect all nodes matching a component type (recursive). */
|
|
42
|
-
function findAllByComponent(node: IRExperienceNode, component: string): IRExperienceNode[] {
|
|
43
|
-
const results: IRExperienceNode[] = [];
|
|
44
|
-
if (node.component === component) results.push(node);
|
|
45
|
-
for (const child of node.children ?? []) {
|
|
46
|
-
results.push(...findAllByComponent(child, component));
|
|
47
|
-
}
|
|
48
|
-
return results;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
// =============================================================================
|
|
52
|
-
// Tests
|
|
53
|
-
// =============================================================================
|
|
54
|
-
|
|
55
|
-
describe('Router/Route Experience Composition', () => {
|
|
56
|
-
// -------------------------------------------------------------------------
|
|
57
|
-
// 1. Single page file → Router with one route + index route
|
|
58
|
-
// -------------------------------------------------------------------------
|
|
59
|
-
it('should compose a Router with one route + index route from a single page', () => {
|
|
60
|
-
const result = compileProject({
|
|
61
|
-
'mm.config.ts': `
|
|
62
|
-
import { defineBlueprint } from '@mindmatrix/react';
|
|
63
|
-
export default defineBlueprint({ slug: 'single-page', name: 'Single Page App', version: '1.0.0' });
|
|
64
|
-
`,
|
|
65
|
-
'app/home.tsx': page('Home'),
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
expect(result.errors).toHaveLength(0);
|
|
69
|
-
|
|
70
|
-
const exp = getExperience(result.ir);
|
|
71
|
-
expect(exp).toBeDefined();
|
|
72
|
-
|
|
73
|
-
// Root should be a Stack
|
|
74
|
-
expect(exp!.component).toBe('Stack');
|
|
75
|
-
|
|
76
|
-
// Should have a Router child
|
|
77
|
-
const router = findByComponent(exp!, 'Router');
|
|
78
|
-
expect(router).toBeDefined();
|
|
79
|
-
|
|
80
|
-
// Router should contain Route children
|
|
81
|
-
const routes = findAllByComponent(router!, 'Route');
|
|
82
|
-
expect(routes.length).toBeGreaterThanOrEqual(2); // index + /home
|
|
83
|
-
|
|
84
|
-
// One route should be the index with exact:true
|
|
85
|
-
const indexRoute = routes.find(r => r.config?.path === '/' && r.config?.exact === true);
|
|
86
|
-
expect(indexRoute).toBeDefined();
|
|
87
|
-
|
|
88
|
-
// One route should be /home
|
|
89
|
-
const homeRoute = routes.find(r => r.config?.path === '/home');
|
|
90
|
-
expect(homeRoute).toBeDefined();
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
// -------------------------------------------------------------------------
|
|
94
|
-
// 2. Multiple pages → Router with correct routes + nav links
|
|
95
|
-
// -------------------------------------------------------------------------
|
|
96
|
-
it('should compose Router with multiple routes and nav links', () => {
|
|
97
|
-
const result = compileProject({
|
|
98
|
-
'mm.config.ts': `
|
|
99
|
-
import { defineBlueprint } from '@mindmatrix/react';
|
|
100
|
-
export default defineBlueprint({ slug: 'multi-page', name: 'Multi Page', version: '1.0.0' });
|
|
101
|
-
`,
|
|
102
|
-
'app/home.tsx': page('Home'),
|
|
103
|
-
'app/settings.tsx': page('Settings'),
|
|
104
|
-
});
|
|
105
|
-
|
|
106
|
-
expect(result.errors).toHaveLength(0);
|
|
107
|
-
|
|
108
|
-
const exp = getExperience(result.ir);
|
|
109
|
-
expect(exp).toBeDefined();
|
|
110
|
-
|
|
111
|
-
const router = findByComponent(exp!, 'Router');
|
|
112
|
-
expect(router).toBeDefined();
|
|
113
|
-
|
|
114
|
-
// Should have routes for /, /home, /settings
|
|
115
|
-
const routes = findAllByComponent(router!, 'Route');
|
|
116
|
-
const routePaths = routes.map(r => r.config?.path);
|
|
117
|
-
expect(routePaths).toContain('/home');
|
|
118
|
-
expect(routePaths).toContain('/settings');
|
|
119
|
-
expect(routePaths).toContain('/');
|
|
120
|
-
|
|
121
|
-
// Should have NavLink children in a nav bar
|
|
122
|
-
const navLinks = findAllByComponent(exp!, 'NavLink');
|
|
123
|
-
expect(navLinks.length).toBeGreaterThanOrEqual(2);
|
|
124
|
-
|
|
125
|
-
// NavLinks should have 'to' config pointing to routes
|
|
126
|
-
const navTargets = navLinks.map(n => n.config?.to);
|
|
127
|
-
expect(navTargets).toContain('/home');
|
|
128
|
-
expect(navTargets).toContain('/settings');
|
|
129
|
-
});
|
|
130
|
-
|
|
131
|
-
// -------------------------------------------------------------------------
|
|
132
|
-
// 3. Nested pages → correct route paths
|
|
133
|
-
// -------------------------------------------------------------------------
|
|
134
|
-
it('should generate correct paths for nested pages', () => {
|
|
135
|
-
const result = compileProject({
|
|
136
|
-
'mm.config.ts': `
|
|
137
|
-
import { defineBlueprint } from '@mindmatrix/react';
|
|
138
|
-
export default defineBlueprint({ slug: 'nested-app', name: 'Nested', version: '1.0.0' });
|
|
139
|
-
`,
|
|
140
|
-
'app/admin/dashboard.tsx': page('Dashboard'),
|
|
141
|
-
'app/admin/users.tsx': page('Users'),
|
|
142
|
-
});
|
|
143
|
-
|
|
144
|
-
expect(result.errors).toHaveLength(0);
|
|
145
|
-
|
|
146
|
-
const exp = getExperience(result.ir);
|
|
147
|
-
expect(exp).toBeDefined();
|
|
148
|
-
|
|
149
|
-
const router = findByComponent(exp!, 'Router');
|
|
150
|
-
expect(router).toBeDefined();
|
|
151
|
-
|
|
152
|
-
const routes = findAllByComponent(router!, 'Route');
|
|
153
|
-
const routePaths = routes.map(r => r.config?.path);
|
|
154
|
-
expect(routePaths).toContain('/admin/dashboard');
|
|
155
|
-
expect(routePaths).toContain('/admin/users');
|
|
156
|
-
});
|
|
157
|
-
|
|
158
|
-
// -------------------------------------------------------------------------
|
|
159
|
-
// 4. Route path generation: index.tsx → /, page.tsx → /, projects.tsx → /projects
|
|
160
|
-
// -------------------------------------------------------------------------
|
|
161
|
-
it('should map app/index.tsx to / route path', () => {
|
|
162
|
-
const result = compileProject({
|
|
163
|
-
'app/index.tsx': page('Index'),
|
|
164
|
-
});
|
|
165
|
-
|
|
166
|
-
expect(result.errors).toHaveLength(0);
|
|
167
|
-
|
|
168
|
-
const exp = getExperience(result.ir);
|
|
169
|
-
expect(exp).toBeDefined();
|
|
170
|
-
|
|
171
|
-
const routes = findAllByComponent(exp!, 'Route');
|
|
172
|
-
// index.tsx strips to '/' → route path should be '/'
|
|
173
|
-
const indexPaths = routes.filter(r => r.config?.path === '/');
|
|
174
|
-
expect(indexPaths.length).toBeGreaterThanOrEqual(1);
|
|
175
|
-
});
|
|
176
|
-
|
|
177
|
-
it('should map app/page.tsx to / route path', () => {
|
|
178
|
-
const result = compileProject({
|
|
179
|
-
'app/page.tsx': page('Page'),
|
|
180
|
-
});
|
|
181
|
-
|
|
182
|
-
expect(result.errors).toHaveLength(0);
|
|
183
|
-
|
|
184
|
-
const exp = getExperience(result.ir);
|
|
185
|
-
expect(exp).toBeDefined();
|
|
186
|
-
|
|
187
|
-
const routes = findAllByComponent(exp!, 'Route');
|
|
188
|
-
const indexPaths = routes.filter(r => r.config?.path === '/');
|
|
189
|
-
expect(indexPaths.length).toBeGreaterThanOrEqual(1);
|
|
190
|
-
});
|
|
191
|
-
|
|
192
|
-
it('should map app/projects.tsx to /projects route path', () => {
|
|
193
|
-
const result = compileProject({
|
|
194
|
-
'app/projects.tsx': page('Projects'),
|
|
195
|
-
});
|
|
196
|
-
|
|
197
|
-
expect(result.errors).toHaveLength(0);
|
|
198
|
-
|
|
199
|
-
const exp = getExperience(result.ir);
|
|
200
|
-
expect(exp).toBeDefined();
|
|
201
|
-
|
|
202
|
-
const routes = findAllByComponent(exp!, 'Route');
|
|
203
|
-
const routePaths = routes.map(r => r.config?.path);
|
|
204
|
-
expect(routePaths).toContain('/projects');
|
|
205
|
-
});
|
|
206
|
-
|
|
207
|
-
// -------------------------------------------------------------------------
|
|
208
|
-
// 5. Nav link labels: kebab-case → Title Case
|
|
209
|
-
// -------------------------------------------------------------------------
|
|
210
|
-
it('should convert kebab-case filenames to Title Case nav labels', () => {
|
|
211
|
-
const result = compileProject({
|
|
212
|
-
'app/surge-pricing.tsx': page('SurgePricing'),
|
|
213
|
-
'app/ride-history.tsx': page('RideHistory'),
|
|
214
|
-
});
|
|
215
|
-
|
|
216
|
-
expect(result.errors).toHaveLength(0);
|
|
217
|
-
|
|
218
|
-
const exp = getExperience(result.ir);
|
|
219
|
-
expect(exp).toBeDefined();
|
|
220
|
-
|
|
221
|
-
const navLinks = findAllByComponent(exp!, 'NavLink');
|
|
222
|
-
const labels = navLinks.map(n => n.config?.label);
|
|
223
|
-
expect(labels).toContain('Surge Pricing');
|
|
224
|
-
expect(labels).toContain('Ride History');
|
|
225
|
-
});
|
|
226
|
-
|
|
227
|
-
// -------------------------------------------------------------------------
|
|
228
|
-
// 6. Index route has exact:true
|
|
229
|
-
// -------------------------------------------------------------------------
|
|
230
|
-
it('should set exact:true on the index route', () => {
|
|
231
|
-
const result = compileProject({
|
|
232
|
-
'app/home.tsx': page('Home'),
|
|
233
|
-
'app/about.tsx': page('About'),
|
|
234
|
-
});
|
|
235
|
-
|
|
236
|
-
expect(result.errors).toHaveLength(0);
|
|
237
|
-
|
|
238
|
-
const exp = getExperience(result.ir);
|
|
239
|
-
expect(exp).toBeDefined();
|
|
240
|
-
|
|
241
|
-
const routes = findAllByComponent(exp!, 'Route');
|
|
242
|
-
const indexRoute = routes.find(r => r.config?.path === '/' && r.config?.exact === true);
|
|
243
|
-
expect(indexRoute).toBeDefined();
|
|
244
|
-
|
|
245
|
-
// Non-index routes should NOT have exact:true
|
|
246
|
-
const homeRoute = routes.find(r => r.config?.path === '/home');
|
|
247
|
-
expect(homeRoute).toBeDefined();
|
|
248
|
-
expect(homeRoute!.config?.exact).toBeUndefined();
|
|
249
|
-
});
|
|
250
|
-
|
|
251
|
-
// -------------------------------------------------------------------------
|
|
252
|
-
// 7. blueprint_manifest added to metadata with correct routes
|
|
253
|
-
// -------------------------------------------------------------------------
|
|
254
|
-
it('should add blueprint_manifest to metadata', () => {
|
|
255
|
-
const result = compileProject({
|
|
256
|
-
'mm.config.ts': `
|
|
257
|
-
import { defineBlueprint } from '@mindmatrix/react';
|
|
258
|
-
export default defineBlueprint({ slug: 'uber-clone', name: 'Uber Clone', version: '1.0.0' });
|
|
259
|
-
`,
|
|
260
|
-
'app/home.tsx': page('Home'),
|
|
261
|
-
'app/rides.tsx': page('Rides'),
|
|
262
|
-
});
|
|
263
|
-
|
|
264
|
-
expect(result.errors).toHaveLength(0);
|
|
265
|
-
|
|
266
|
-
const meta = result.ir.metadata as Record<string, any>;
|
|
267
|
-
expect(meta.blueprint_manifest).toBeDefined();
|
|
268
|
-
expect(meta.blueprint_manifest.routes).toBeDefined();
|
|
269
|
-
expect(meta.blueprint_manifest.routes).toHaveLength(1);
|
|
270
|
-
|
|
271
|
-
const manifestRoute = meta.blueprint_manifest.routes[0];
|
|
272
|
-
expect(manifestRoute.path).toBe('uber-clone/*');
|
|
273
|
-
expect(manifestRoute.node).toBe('uber-clone');
|
|
274
|
-
expect(manifestRoute.label).toBe('Uber Clone');
|
|
275
|
-
});
|
|
276
|
-
|
|
277
|
-
// -------------------------------------------------------------------------
|
|
278
|
-
// 8. full_bleed: true in manifest config
|
|
279
|
-
// -------------------------------------------------------------------------
|
|
280
|
-
it('should set full_bleed: true in blueprint_manifest config', () => {
|
|
281
|
-
const result = compileProject({
|
|
282
|
-
'mm.config.ts': `
|
|
283
|
-
import { defineBlueprint } from '@mindmatrix/react';
|
|
284
|
-
export default defineBlueprint({ slug: 'my-app', name: 'My App', version: '1.0.0' });
|
|
285
|
-
`,
|
|
286
|
-
'app/dashboard.tsx': page('Dashboard'),
|
|
287
|
-
});
|
|
288
|
-
|
|
289
|
-
expect(result.errors).toHaveLength(0);
|
|
290
|
-
|
|
291
|
-
const meta = result.ir.metadata as Record<string, any>;
|
|
292
|
-
expect(meta.blueprint_manifest).toBeDefined();
|
|
293
|
-
expect(meta.blueprint_manifest.config).toBeDefined();
|
|
294
|
-
expect(meta.blueprint_manifest.config.full_bleed).toBe(true);
|
|
295
|
-
});
|
|
296
|
-
|
|
297
|
-
// -------------------------------------------------------------------------
|
|
298
|
-
// 9. componentDefinitions included in metadata when components/*.tsx exist
|
|
299
|
-
// -------------------------------------------------------------------------
|
|
300
|
-
it('should include componentDefinitions in metadata for components/*.tsx files', () => {
|
|
301
|
-
const result = compileProject({
|
|
302
|
-
'mm.config.ts': `
|
|
303
|
-
import { defineBlueprint } from '@mindmatrix/react';
|
|
304
|
-
export default defineBlueprint({ slug: 'comp-app', name: 'Comp App', version: '1.0.0' });
|
|
305
|
-
`,
|
|
306
|
-
'app/home.tsx': page('Home'),
|
|
307
|
-
'components/MapView.tsx': `
|
|
308
|
-
export function MapView({ latitude, longitude }: { latitude: number; longitude: number }) {
|
|
309
|
-
return <div>Map at {latitude}, {longitude}</div>;
|
|
310
|
-
}
|
|
311
|
-
`,
|
|
312
|
-
});
|
|
313
|
-
|
|
314
|
-
expect(result.errors).toHaveLength(0);
|
|
315
|
-
|
|
316
|
-
// componentDefinitions should be in the result
|
|
317
|
-
expect(result.componentDefinitions).toBeDefined();
|
|
318
|
-
expect(result.componentDefinitions['MapView']).toBeDefined();
|
|
319
|
-
expect(result.componentDefinitions['MapView'].props).toContain('latitude');
|
|
320
|
-
expect(result.componentDefinitions['MapView'].props).toContain('longitude');
|
|
321
|
-
|
|
322
|
-
// componentDefinitions should also be in metadata
|
|
323
|
-
const meta = result.ir.metadata as Record<string, any>;
|
|
324
|
-
expect(meta.componentDefinitions).toBeDefined();
|
|
325
|
-
expect(meta.componentDefinitions['MapView']).toBeDefined();
|
|
326
|
-
});
|
|
327
|
-
|
|
328
|
-
// -------------------------------------------------------------------------
|
|
329
|
-
// 10. Page experiences NOT flattened into a single Stack (the old bug)
|
|
330
|
-
// -------------------------------------------------------------------------
|
|
331
|
-
it('should NOT flatten page experiences into a single Stack', () => {
|
|
332
|
-
const result = compileProject({
|
|
333
|
-
'mm.config.ts': `
|
|
334
|
-
import { defineBlueprint } from '@mindmatrix/react';
|
|
335
|
-
export default defineBlueprint({ slug: 'no-flatten', name: 'No Flatten', version: '1.0.0' });
|
|
336
|
-
`,
|
|
337
|
-
'app/page-one.tsx': page('PageOne'),
|
|
338
|
-
'app/page-two.tsx': page('PageTwo'),
|
|
339
|
-
'app/page-three.tsx': page('PageThree'),
|
|
340
|
-
});
|
|
341
|
-
|
|
342
|
-
expect(result.errors).toHaveLength(0);
|
|
343
|
-
|
|
344
|
-
const exp = getExperience(result.ir);
|
|
345
|
-
expect(exp).toBeDefined();
|
|
346
|
-
|
|
347
|
-
// The root experience should be a Stack containing a nav-bar (Row) and a Router.
|
|
348
|
-
// It should NOT be a bare Stack where all pages are direct children
|
|
349
|
-
// (that would be the old bug — flattening pages).
|
|
350
|
-
expect(exp!.component).toBe('Stack');
|
|
351
|
-
expect(exp!.id).toBe('blueprint-root');
|
|
352
|
-
|
|
353
|
-
// Root children: nav-bar (Row) + page-router (Router)
|
|
354
|
-
const children = exp!.children ?? [];
|
|
355
|
-
expect(children.length).toBe(2);
|
|
356
|
-
|
|
357
|
-
const navBar = children.find((c: any) => c.component === 'Row');
|
|
358
|
-
expect(navBar).toBeDefined();
|
|
359
|
-
|
|
360
|
-
const router = children.find((c: any) => c.component === 'Router');
|
|
361
|
-
expect(router).toBeDefined();
|
|
362
|
-
|
|
363
|
-
// Each page should be inside a Route node, not directly in the Stack
|
|
364
|
-
const routes = findAllByComponent(router!, 'Route');
|
|
365
|
-
expect(routes.length).toBeGreaterThanOrEqual(3); // 3 pages + 1 index
|
|
366
|
-
|
|
367
|
-
// Verify page content is nested inside Route children, not at the top-level
|
|
368
|
-
for (const route of routes) {
|
|
369
|
-
expect(route.children).toBeDefined();
|
|
370
|
-
expect(route.children!.length).toBeGreaterThanOrEqual(1);
|
|
371
|
-
}
|
|
372
|
-
});
|
|
373
|
-
|
|
374
|
-
// -------------------------------------------------------------------------
|
|
375
|
-
// Additional: pageExperiences returned correctly
|
|
376
|
-
// -------------------------------------------------------------------------
|
|
377
|
-
it('should return per-page experience trees in pageExperiences', () => {
|
|
378
|
-
const result = compileProject({
|
|
379
|
-
'app/home.tsx': page('Home'),
|
|
380
|
-
'app/settings.tsx': page('Settings'),
|
|
381
|
-
});
|
|
382
|
-
|
|
383
|
-
expect(result.errors).toHaveLength(0);
|
|
384
|
-
expect(result.pageExperiences).toBeDefined();
|
|
385
|
-
expect(Object.keys(result.pageExperiences)).toHaveLength(2);
|
|
386
|
-
expect(result.pageExperiences['app/home.tsx']).toBeDefined();
|
|
387
|
-
expect(result.pageExperiences['app/settings.tsx']).toBeDefined();
|
|
388
|
-
});
|
|
389
|
-
|
|
390
|
-
// -------------------------------------------------------------------------
|
|
391
|
-
// Additional: routeTable populated for page files
|
|
392
|
-
// -------------------------------------------------------------------------
|
|
393
|
-
it('should populate routeTable with entries for each page file', () => {
|
|
394
|
-
const result = compileProject({
|
|
395
|
-
'mm.config.ts': `
|
|
396
|
-
import { defineBlueprint } from '@mindmatrix/react';
|
|
397
|
-
export default defineBlueprint({ slug: 'route-test', name: 'Route Test', version: '1.0.0' });
|
|
398
|
-
`,
|
|
399
|
-
'app/home.tsx': page('Home'),
|
|
400
|
-
'app/settings.tsx': page('Settings'),
|
|
401
|
-
'app/admin/users.tsx': page('Users'),
|
|
402
|
-
});
|
|
403
|
-
|
|
404
|
-
expect(result.errors).toHaveLength(0);
|
|
405
|
-
expect(result.routeTable.length).toBeGreaterThanOrEqual(3);
|
|
406
|
-
|
|
407
|
-
const paths = result.routeTable.map(r => r.path);
|
|
408
|
-
// Verify that route paths are generated (exact format depends on Phase 1 vs Phase 2)
|
|
409
|
-
expect(paths.length).toBeGreaterThanOrEqual(3);
|
|
410
|
-
});
|
|
411
|
-
|
|
412
|
-
// -------------------------------------------------------------------------
|
|
413
|
-
// Additional: Router child definition created
|
|
414
|
-
// -------------------------------------------------------------------------
|
|
415
|
-
it('should create a router child definition from page files', () => {
|
|
416
|
-
const result = compileProject({
|
|
417
|
-
'mm.config.ts': `
|
|
418
|
-
import { defineBlueprint } from '@mindmatrix/react';
|
|
419
|
-
export default defineBlueprint({ slug: 'child-test', name: 'Child Test', version: '1.0.0' });
|
|
420
|
-
`,
|
|
421
|
-
'app/home.tsx': page('Home'),
|
|
422
|
-
'app/about.tsx': page('About'),
|
|
423
|
-
});
|
|
424
|
-
|
|
425
|
-
expect(result.errors).toHaveLength(0);
|
|
426
|
-
|
|
427
|
-
// A router child definition should exist
|
|
428
|
-
const routerChild = result.childDefinitions.find(
|
|
429
|
-
cd => cd.category === 'router' || cd.slug?.includes('router')
|
|
430
|
-
);
|
|
431
|
-
expect(routerChild).toBeDefined();
|
|
432
|
-
});
|
|
433
|
-
|
|
434
|
-
// -------------------------------------------------------------------------
|
|
435
|
-
// Additional: nav bar has Row component with correct structure
|
|
436
|
-
// -------------------------------------------------------------------------
|
|
437
|
-
it('should render nav bar as a Row with NavLink children', () => {
|
|
438
|
-
const result = compileProject({
|
|
439
|
-
'app/home.tsx': page('Home'),
|
|
440
|
-
'app/profile.tsx': page('Profile'),
|
|
441
|
-
'app/settings.tsx': page('Settings'),
|
|
442
|
-
});
|
|
443
|
-
|
|
444
|
-
expect(result.errors).toHaveLength(0);
|
|
445
|
-
|
|
446
|
-
const exp = getExperience(result.ir);
|
|
447
|
-
expect(exp).toBeDefined();
|
|
448
|
-
|
|
449
|
-
// Find the nav bar (Row)
|
|
450
|
-
const navBar = (exp!.children ?? []).find((c: any) => c.component === 'Row');
|
|
451
|
-
expect(navBar).toBeDefined();
|
|
452
|
-
expect(navBar!.id).toBe('nav-bar');
|
|
453
|
-
expect(navBar!.config?.gap).toBe(1);
|
|
454
|
-
|
|
455
|
-
// Nav bar should contain NavLinks for each page
|
|
456
|
-
const navLinks = findAllByComponent(navBar!, 'NavLink');
|
|
457
|
-
expect(navLinks).toHaveLength(3);
|
|
458
|
-
});
|
|
459
|
-
|
|
460
|
-
// -------------------------------------------------------------------------
|
|
461
|
-
// Additional: No experience when no page files exist
|
|
462
|
-
// -------------------------------------------------------------------------
|
|
463
|
-
it('should not set Router experience when no app/ page files exist', () => {
|
|
464
|
-
const result = compileProject({
|
|
465
|
-
'mm.config.ts': `
|
|
466
|
-
import { defineBlueprint } from '@mindmatrix/react';
|
|
467
|
-
export default defineBlueprint({ slug: 'no-pages', name: 'No Pages', version: '1.0.0' });
|
|
468
|
-
`,
|
|
469
|
-
'main.workflow.tsx': `
|
|
470
|
-
export function Main() {
|
|
471
|
-
const [x, setX] = useState(0);
|
|
472
|
-
return <div>{x}</div>;
|
|
473
|
-
}
|
|
474
|
-
`,
|
|
475
|
-
});
|
|
476
|
-
|
|
477
|
-
expect(result.errors).toHaveLength(0);
|
|
478
|
-
|
|
479
|
-
// No page files → no Router composition
|
|
480
|
-
const exp = getExperience(result.ir);
|
|
481
|
-
// Experience may exist from the workflow view, but should NOT be a Router
|
|
482
|
-
if (exp) {
|
|
483
|
-
expect(exp.id).not.toBe('blueprint-root');
|
|
484
|
-
}
|
|
485
|
-
|
|
486
|
-
const meta = result.ir.metadata as Record<string, any>;
|
|
487
|
-
expect(meta.blueprint_manifest).toBeUndefined();
|
|
488
|
-
});
|
|
489
|
-
});
|
|
@@ -1,176 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Router Extractor Tests — validates file-based route → state derivation.
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import { describe, it, expect } from 'vitest';
|
|
6
|
-
import {
|
|
7
|
-
extractRouterWorkflow,
|
|
8
|
-
pathToStateName,
|
|
9
|
-
pathToUrlPattern,
|
|
10
|
-
extractParams,
|
|
11
|
-
} from '../babel/extractors/router-extractor';
|
|
12
|
-
import type { PageFile } from '../babel/extractors/router-extractor';
|
|
13
|
-
|
|
14
|
-
describe('Router Extractor', () => {
|
|
15
|
-
describe('pathToStateName', () => {
|
|
16
|
-
it('should convert root path to HOME', () => {
|
|
17
|
-
expect(pathToStateName('')).toBe('HOME');
|
|
18
|
-
expect(pathToStateName('/')).toBe('HOME');
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
it('should convert simple paths to UPPER_CASE', () => {
|
|
22
|
-
expect(pathToStateName('chat')).toBe('CHAT');
|
|
23
|
-
expect(pathToStateName('settings')).toBe('SETTINGS');
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
it('should handle nested paths', () => {
|
|
27
|
-
expect(pathToStateName('chat/messages')).toBe('CHAT_MESSAGES');
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
it('should handle parameterized segments', () => {
|
|
31
|
-
expect(pathToStateName('chat/[channelId]')).toBe('CHAT_CHANNEL_ID');
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
it('should handle camelCase segments', () => {
|
|
35
|
-
expect(pathToStateName('userProfile')).toBe('USER_PROFILE');
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
it('should handle kebab-case segments', () => {
|
|
39
|
-
expect(pathToStateName('user-settings')).toBe('USER_SETTINGS');
|
|
40
|
-
});
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
describe('pathToUrlPattern', () => {
|
|
44
|
-
it('should convert simple page path', () => {
|
|
45
|
-
expect(pathToUrlPattern('chat/page.tsx')).toBe('/chat');
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
it('should convert root page path', () => {
|
|
49
|
-
expect(pathToUrlPattern('page.tsx')).toBe('/');
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
it('should convert parameterized paths', () => {
|
|
53
|
-
expect(pathToUrlPattern('chat/[channelId]/page.tsx')).toBe('/chat/:channelId');
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
it('should handle nested params', () => {
|
|
57
|
-
expect(pathToUrlPattern('users/[userId]/posts/[postId]/page.tsx')).toBe('/users/:userId/posts/:postId');
|
|
58
|
-
});
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
describe('extractParams', () => {
|
|
62
|
-
it('should extract no params from static paths', () => {
|
|
63
|
-
expect(extractParams('chat/page.tsx')).toEqual([]);
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
it('should extract single param', () => {
|
|
67
|
-
expect(extractParams('chat/[channelId]/page.tsx')).toEqual(['channelId']);
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
it('should extract multiple params', () => {
|
|
71
|
-
expect(extractParams('users/[userId]/posts/[postId]/page.tsx')).toEqual(['userId', 'postId']);
|
|
72
|
-
});
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
describe('extractRouterWorkflow', () => {
|
|
76
|
-
it('should create states from page files', () => {
|
|
77
|
-
const pages: PageFile[] = [
|
|
78
|
-
{ relativePath: 'page.tsx', absolutePath: '/app/page.tsx' },
|
|
79
|
-
{ relativePath: 'chat/page.tsx', absolutePath: '/app/chat/page.tsx' },
|
|
80
|
-
{ relativePath: 'settings/page.tsx', absolutePath: '/app/settings/page.tsx' },
|
|
81
|
-
];
|
|
82
|
-
|
|
83
|
-
const ir = extractRouterWorkflow(pages);
|
|
84
|
-
|
|
85
|
-
expect(ir.states).toHaveLength(3);
|
|
86
|
-
expect(ir.states.map((s) => s.name)).toContain('HOME');
|
|
87
|
-
expect(ir.states.map((s) => s.name)).toContain('CHAT');
|
|
88
|
-
expect(ir.states.map((s) => s.name)).toContain('SETTINGS');
|
|
89
|
-
});
|
|
90
|
-
|
|
91
|
-
it('should mark root as START state', () => {
|
|
92
|
-
const pages: PageFile[] = [
|
|
93
|
-
{ relativePath: 'page.tsx', absolutePath: '/app/page.tsx' },
|
|
94
|
-
{ relativePath: 'chat/page.tsx', absolutePath: '/app/chat/page.tsx' },
|
|
95
|
-
];
|
|
96
|
-
|
|
97
|
-
const ir = extractRouterWorkflow(pages);
|
|
98
|
-
|
|
99
|
-
const homeState = ir.states.find((s) => s.name === 'HOME');
|
|
100
|
-
expect(homeState?.type).toBe('START');
|
|
101
|
-
});
|
|
102
|
-
|
|
103
|
-
it('should create fields for route params', () => {
|
|
104
|
-
const pages: PageFile[] = [
|
|
105
|
-
{ relativePath: 'page.tsx', absolutePath: '/app/page.tsx' },
|
|
106
|
-
{ relativePath: 'chat/[channelId]/page.tsx', absolutePath: '/app/chat/[channelId]/page.tsx' },
|
|
107
|
-
];
|
|
108
|
-
|
|
109
|
-
const ir = extractRouterWorkflow(pages);
|
|
110
|
-
|
|
111
|
-
const channelIdField = ir.fields.find((f) => f.name === 'channelId');
|
|
112
|
-
expect(channelIdField).toBeDefined();
|
|
113
|
-
expect(channelIdField!.type).toBe('text');
|
|
114
|
-
});
|
|
115
|
-
|
|
116
|
-
it('should deduplicate param fields', () => {
|
|
117
|
-
const pages: PageFile[] = [
|
|
118
|
-
{ relativePath: 'users/[userId]/page.tsx', absolutePath: '/app/users/[userId]/page.tsx' },
|
|
119
|
-
{ relativePath: 'users/[userId]/posts/page.tsx', absolutePath: '/app/users/[userId]/posts/page.tsx' },
|
|
120
|
-
];
|
|
121
|
-
|
|
122
|
-
const ir = extractRouterWorkflow(pages);
|
|
123
|
-
|
|
124
|
-
const userIdFields = ir.fields.filter((f) => f.name === 'userId');
|
|
125
|
-
expect(userIdFields).toHaveLength(1);
|
|
126
|
-
});
|
|
127
|
-
|
|
128
|
-
it('should create navigate_home transitions', () => {
|
|
129
|
-
const pages: PageFile[] = [
|
|
130
|
-
{ relativePath: 'page.tsx', absolutePath: '/app/page.tsx' },
|
|
131
|
-
{ relativePath: 'chat/page.tsx', absolutePath: '/app/chat/page.tsx' },
|
|
132
|
-
{ relativePath: 'settings/page.tsx', absolutePath: '/app/settings/page.tsx' },
|
|
133
|
-
];
|
|
134
|
-
|
|
135
|
-
const ir = extractRouterWorkflow(pages);
|
|
136
|
-
|
|
137
|
-
const homeTransitions = ir.transitions.filter((t) => t.to === 'HOME');
|
|
138
|
-
expect(homeTransitions.length).toBeGreaterThanOrEqual(2);
|
|
139
|
-
});
|
|
140
|
-
|
|
141
|
-
it('should set router category and metadata', () => {
|
|
142
|
-
const pages: PageFile[] = [
|
|
143
|
-
{ relativePath: 'page.tsx', absolutePath: '/app/page.tsx' },
|
|
144
|
-
];
|
|
145
|
-
|
|
146
|
-
const ir = extractRouterWorkflow(pages);
|
|
147
|
-
|
|
148
|
-
expect(ir.category).toBe('router');
|
|
149
|
-
expect(ir.tags).toContainEqual({ tag_name: 'auto-derived' });
|
|
150
|
-
expect((ir.metadata as any).routes).toBeDefined();
|
|
151
|
-
});
|
|
152
|
-
|
|
153
|
-
it('should detect layout boundaries', () => {
|
|
154
|
-
const pages: PageFile[] = [
|
|
155
|
-
{ relativePath: 'page.tsx', absolutePath: '/app/page.tsx' },
|
|
156
|
-
{ relativePath: 'chat/page.tsx', absolutePath: '/app/chat/page.tsx' },
|
|
157
|
-
{ relativePath: 'layout.tsx', absolutePath: '/app/layout.tsx', isLayout: true },
|
|
158
|
-
{ relativePath: 'chat/layout.tsx', absolutePath: '/app/chat/layout.tsx', isLayout: true },
|
|
159
|
-
];
|
|
160
|
-
|
|
161
|
-
const ir = extractRouterWorkflow(pages);
|
|
162
|
-
|
|
163
|
-
const chatRoute = (ir.metadata as any).routes.find((r: any) => r.state === 'CHAT');
|
|
164
|
-
expect(chatRoute.layoutBoundary).toBe('chat/layout.tsx');
|
|
165
|
-
});
|
|
166
|
-
|
|
167
|
-
it('should use custom slug', () => {
|
|
168
|
-
const pages: PageFile[] = [
|
|
169
|
-
{ relativePath: 'page.tsx', absolutePath: '/app/page.tsx' },
|
|
170
|
-
];
|
|
171
|
-
|
|
172
|
-
const ir = extractRouterWorkflow(pages, { slug: 'mm-chat-router' });
|
|
173
|
-
expect(ir.slug).toBe('mm-chat-router');
|
|
174
|
-
});
|
|
175
|
-
});
|
|
176
|
-
});
|