swallowkit 1.0.0-beta.4 → 1.0.0-beta.40
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/LICENSE +21 -21
- package/README.ja.md +153 -215
- package/README.md +153 -216
- package/dist/__tests__/fixtures.d.ts +22 -0
- package/dist/__tests__/fixtures.d.ts.map +1 -0
- package/dist/__tests__/fixtures.js +146 -0
- package/dist/__tests__/fixtures.js.map +1 -0
- package/dist/cli/commands/add-auth.d.ts +10 -0
- package/dist/cli/commands/add-auth.d.ts.map +1 -0
- package/dist/cli/commands/add-auth.js +426 -0
- package/dist/cli/commands/add-auth.js.map +1 -0
- package/dist/cli/commands/add-connector.d.ts +20 -0
- package/dist/cli/commands/add-connector.d.ts.map +1 -0
- package/dist/cli/commands/add-connector.js +163 -0
- package/dist/cli/commands/add-connector.js.map +1 -0
- package/dist/cli/commands/create-model.d.ts +1 -4
- package/dist/cli/commands/create-model.d.ts.map +1 -1
- package/dist/cli/commands/create-model.js +21 -82
- package/dist/cli/commands/create-model.js.map +1 -1
- package/dist/cli/commands/dev-seeds.d.ts +57 -0
- package/dist/cli/commands/dev-seeds.d.ts.map +1 -0
- package/dist/cli/commands/dev-seeds.js +470 -0
- package/dist/cli/commands/dev-seeds.js.map +1 -0
- package/dist/cli/commands/dev.d.ts +33 -0
- package/dist/cli/commands/dev.d.ts.map +1 -1
- package/dist/cli/commands/dev.js +628 -146
- package/dist/cli/commands/dev.js.map +1 -1
- package/dist/cli/commands/index.d.ts +1 -0
- package/dist/cli/commands/index.d.ts.map +1 -1
- package/dist/cli/commands/index.js +3 -1
- package/dist/cli/commands/index.js.map +1 -1
- package/dist/cli/commands/init.d.ts +45 -0
- package/dist/cli/commands/init.d.ts.map +1 -1
- package/dist/cli/commands/init.js +2851 -1754
- package/dist/cli/commands/init.js.map +1 -1
- package/dist/cli/commands/scaffold.d.ts +4 -0
- package/dist/cli/commands/scaffold.d.ts.map +1 -1
- package/dist/cli/commands/scaffold.js +439 -130
- package/dist/cli/commands/scaffold.js.map +1 -1
- package/dist/cli/index.d.ts +5 -1
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +200 -42
- package/dist/cli/index.js.map +1 -1
- package/dist/core/config.d.ts +8 -2
- package/dist/core/config.d.ts.map +1 -1
- package/dist/core/config.js +94 -5
- package/dist/core/config.js.map +1 -1
- package/dist/core/mock/connector-mock-server.d.ts +101 -0
- package/dist/core/mock/connector-mock-server.d.ts.map +1 -0
- package/dist/core/mock/connector-mock-server.js +480 -0
- package/dist/core/mock/connector-mock-server.js.map +1 -0
- package/dist/core/mock/zod-mock-generator.d.ts +14 -0
- package/dist/core/mock/zod-mock-generator.d.ts.map +1 -0
- package/dist/core/mock/zod-mock-generator.js +163 -0
- package/dist/core/mock/zod-mock-generator.js.map +1 -0
- package/dist/core/operations/create-model.d.ts +15 -0
- package/dist/core/operations/create-model.d.ts.map +1 -0
- package/dist/core/operations/create-model.js +171 -0
- package/dist/core/operations/create-model.js.map +1 -0
- package/dist/core/operations/runtime.d.ts +32 -0
- package/dist/core/operations/runtime.d.ts.map +1 -0
- package/dist/core/operations/runtime.js +225 -0
- package/dist/core/operations/runtime.js.map +1 -0
- package/dist/core/operations/scaffold-machine.d.ts +16 -0
- package/dist/core/operations/scaffold-machine.d.ts.map +1 -0
- package/dist/core/operations/scaffold-machine.js +63 -0
- package/dist/core/operations/scaffold-machine.js.map +1 -0
- package/dist/core/project/manifest.d.ts +92 -0
- package/dist/core/project/manifest.d.ts.map +1 -0
- package/dist/core/project/manifest.js +321 -0
- package/dist/core/project/manifest.js.map +1 -0
- package/dist/core/project/validation.d.ts +20 -0
- package/dist/core/project/validation.d.ts.map +1 -0
- package/dist/core/project/validation.js +209 -0
- package/dist/core/project/validation.js.map +1 -0
- package/dist/core/scaffold/auth-generator.d.ts +38 -0
- package/dist/core/scaffold/auth-generator.d.ts.map +1 -0
- package/dist/core/scaffold/auth-generator.js +1244 -0
- package/dist/core/scaffold/auth-generator.js.map +1 -0
- package/dist/core/scaffold/connector-functions-generator.d.ts +41 -0
- package/dist/core/scaffold/connector-functions-generator.d.ts.map +1 -0
- package/dist/core/scaffold/connector-functions-generator.js +1027 -0
- package/dist/core/scaffold/connector-functions-generator.js.map +1 -0
- package/dist/core/scaffold/functions-generator.d.ts +7 -1
- package/dist/core/scaffold/functions-generator.d.ts.map +1 -1
- package/dist/core/scaffold/functions-generator.js +920 -220
- package/dist/core/scaffold/functions-generator.js.map +1 -1
- package/dist/core/scaffold/model-parser.d.ts +20 -1
- package/dist/core/scaffold/model-parser.d.ts.map +1 -1
- package/dist/core/scaffold/model-parser.js +328 -135
- package/dist/core/scaffold/model-parser.js.map +1 -1
- package/dist/core/scaffold/native-schema-generator.d.ts +13 -0
- package/dist/core/scaffold/native-schema-generator.d.ts.map +1 -0
- package/dist/core/scaffold/native-schema-generator.js +696 -0
- package/dist/core/scaffold/native-schema-generator.js.map +1 -0
- package/dist/core/scaffold/nextjs-generator.d.ts +8 -0
- package/dist/core/scaffold/nextjs-generator.d.ts.map +1 -1
- package/dist/core/scaffold/nextjs-generator.js +314 -182
- package/dist/core/scaffold/nextjs-generator.js.map +1 -1
- package/dist/core/scaffold/openapi-generator.d.ts +3 -0
- package/dist/core/scaffold/openapi-generator.d.ts.map +1 -0
- package/dist/core/scaffold/openapi-generator.js +190 -0
- package/dist/core/scaffold/openapi-generator.js.map +1 -0
- package/dist/core/scaffold/ui-generator.d.ts +10 -4
- package/dist/core/scaffold/ui-generator.d.ts.map +1 -1
- package/dist/core/scaffold/ui-generator.js +768 -663
- package/dist/core/scaffold/ui-generator.js.map +1 -1
- package/dist/database/base-model.d.ts +3 -3
- package/dist/database/base-model.js +3 -3
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/machine/contracts.d.ts +16 -0
- package/dist/machine/contracts.d.ts.map +1 -0
- package/dist/machine/contracts.js +3 -0
- package/dist/machine/contracts.js.map +1 -0
- package/dist/machine/errors.d.ts +11 -0
- package/dist/machine/errors.d.ts.map +1 -0
- package/dist/machine/errors.js +34 -0
- package/dist/machine/errors.js.map +1 -0
- package/dist/machine/index.d.ts +3 -0
- package/dist/machine/index.d.ts.map +1 -0
- package/dist/machine/index.js +156 -0
- package/dist/machine/index.js.map +1 -0
- package/dist/mcp/index.d.ts +25 -0
- package/dist/mcp/index.d.ts.map +1 -0
- package/dist/mcp/index.js +186 -0
- package/dist/mcp/index.js.map +1 -0
- package/dist/types/index.d.ts +65 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/utils/package-manager.d.ts +110 -0
- package/dist/utils/package-manager.d.ts.map +1 -0
- package/dist/utils/package-manager.js +224 -0
- package/dist/utils/package-manager.js.map +1 -0
- package/dist/utils/python-uv.d.ts +21 -0
- package/dist/utils/python-uv.d.ts.map +1 -0
- package/dist/utils/python-uv.js +111 -0
- package/dist/utils/python-uv.js.map +1 -0
- package/package.json +84 -73
- package/src/__tests__/__snapshots__/functions-generator.test.ts.snap +1139 -0
- package/src/__tests__/__snapshots__/nextjs-generator.test.ts.snap +194 -0
- package/src/__tests__/__snapshots__/ui-generator.test.ts.snap +532 -0
- package/src/__tests__/auth.test.ts +654 -0
- package/src/__tests__/config.test.ts +274 -0
- package/src/__tests__/connector-functions-generator.test.ts +288 -0
- package/src/__tests__/connector-mock-server.test.ts +439 -0
- package/src/__tests__/connector-model-bff.test.ts +162 -0
- package/src/__tests__/dev-seeds.test.ts +173 -0
- package/src/__tests__/dev.test.ts +252 -0
- package/src/__tests__/fixtures.ts +144 -0
- package/src/__tests__/functions-generator.test.ts +237 -0
- package/src/__tests__/init.test.ts +197 -0
- package/src/__tests__/jest-globals.d.ts +8 -0
- package/src/__tests__/machine.test.ts +356 -0
- package/src/__tests__/mcp.test.ts +124 -0
- package/src/__tests__/model-parser.test.ts +52 -0
- package/src/__tests__/nextjs-generator.test.ts +97 -0
- package/src/__tests__/openapi-generator.test.ts +43 -0
- package/src/__tests__/package-manager.test.ts +201 -0
- package/src/__tests__/python-uv.test.ts +48 -0
- package/src/__tests__/scaffold.test.ts +191 -0
- package/src/__tests__/string-utils.test.ts +75 -0
- package/src/__tests__/tsconfig.json +15 -0
- package/src/__tests__/ui-generator.test.ts +144 -0
- package/src/__tests__/zod-mock-generator.test.ts +132 -0
- package/src/cli/commands/add-auth.ts +482 -0
- package/src/cli/commands/add-connector.ts +158 -0
- package/src/cli/commands/create-model.ts +62 -0
- package/src/cli/commands/dev-seeds.ts +614 -0
- package/src/cli/commands/dev.ts +1134 -0
- package/src/cli/commands/index.ts +9 -0
- package/src/cli/commands/init.ts +3600 -0
- package/src/cli/commands/provision.ts +193 -0
- package/src/cli/commands/scaffold.ts +991 -0
- package/src/cli/index.ts +196 -0
- package/src/core/config.ts +312 -0
- package/src/core/mock/connector-mock-server.ts +555 -0
- package/src/core/mock/zod-mock-generator.ts +205 -0
- package/src/core/operations/create-model.ts +174 -0
- package/src/core/operations/runtime.ts +235 -0
- package/src/core/operations/scaffold-machine.ts +91 -0
- package/src/core/project/manifest.ts +402 -0
- package/src/core/project/validation.ts +229 -0
- package/src/core/scaffold/auth-generator.ts +1284 -0
- package/src/core/scaffold/connector-functions-generator.ts +1128 -0
- package/src/core/scaffold/functions-generator.ts +970 -0
- package/src/core/scaffold/model-parser.ts +841 -0
- package/src/core/scaffold/native-schema-generator.ts +822 -0
- package/src/core/scaffold/nextjs-generator.ts +370 -0
- package/src/core/scaffold/openapi-generator.ts +212 -0
- package/src/core/scaffold/ui-generator.ts +1061 -0
- package/src/database/base-model.ts +184 -0
- package/src/database/client.ts +140 -0
- package/src/database/repository.ts +104 -0
- package/src/database/runtime-check.ts +25 -0
- package/src/index.ts +27 -0
- package/src/machine/contracts.ts +17 -0
- package/src/machine/errors.ts +34 -0
- package/src/machine/index.ts +173 -0
- package/src/mcp/index.ts +191 -0
- package/src/types/index.ts +134 -0
- package/src/utils/package-manager.ts +238 -0
- package/src/utils/python-uv.ts +96 -0
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
import {
|
|
2
|
+
getCommands,
|
|
3
|
+
getWorkspaceConfig,
|
|
4
|
+
getCiSetupStep,
|
|
5
|
+
getAzurePipelinesSetup,
|
|
6
|
+
getBuildScript,
|
|
7
|
+
getSwaAppBuildCommand,
|
|
8
|
+
getFunctionsPrestart,
|
|
9
|
+
getFunctionsStartScript,
|
|
10
|
+
spawnArgs,
|
|
11
|
+
} from "../utils/package-manager";
|
|
12
|
+
|
|
13
|
+
describe("getCommands", () => {
|
|
14
|
+
describe("pnpm", () => {
|
|
15
|
+
const cmds = getCommands("pnpm");
|
|
16
|
+
|
|
17
|
+
it("returns correct name", () => {
|
|
18
|
+
expect(cmds.name).toBe("pnpm");
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it("returns correct install command", () => {
|
|
22
|
+
expect(cmds.install).toBe("pnpm install");
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it("returns correct ci command", () => {
|
|
26
|
+
expect(cmds.ci).toBe("pnpm install --frozen-lockfile");
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it("returns correct add command", () => {
|
|
30
|
+
expect(cmds.add).toBe("pnpm add");
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it("returns correct addDev command", () => {
|
|
34
|
+
expect(cmds.addDev).toBe("pnpm add -D");
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it("returns correct exec command", () => {
|
|
38
|
+
expect(cmds.exec).toBe("pnpm exec");
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it("returns correct dlx command", () => {
|
|
42
|
+
expect(cmds.dlx).toBe("pnpm dlx");
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it("returns correct runFilter for workspace", () => {
|
|
46
|
+
expect(cmds.runFilter("shared")).toBe("pnpm run --filter shared");
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it("returns --use-pnpm flag for create-next-app", () => {
|
|
50
|
+
expect(cmds.createNextAppFlag).toBe("--use-pnpm");
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
describe("npm", () => {
|
|
55
|
+
const cmds = getCommands("npm");
|
|
56
|
+
|
|
57
|
+
it("returns correct name", () => {
|
|
58
|
+
expect(cmds.name).toBe("npm");
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
it("returns correct install command", () => {
|
|
62
|
+
expect(cmds.install).toBe("npm install");
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it("returns correct ci command", () => {
|
|
66
|
+
expect(cmds.ci).toBe("npm ci");
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it("returns correct add command", () => {
|
|
70
|
+
expect(cmds.add).toBe("npm install");
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it("returns correct addDev command", () => {
|
|
74
|
+
expect(cmds.addDev).toBe("npm install -D");
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it("returns correct exec command", () => {
|
|
78
|
+
expect(cmds.exec).toBe("npx");
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
it("returns correct dlx command", () => {
|
|
82
|
+
expect(cmds.dlx).toBe("npx");
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
it("returns correct runFilter for workspace", () => {
|
|
86
|
+
expect(cmds.runFilter("shared")).toBe("npm run --workspace=shared");
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it("returns null for create-next-app flag", () => {
|
|
90
|
+
expect(cmds.createNextAppFlag).toBeNull();
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
describe("getWorkspaceConfig", () => {
|
|
96
|
+
it("returns pnpm-workspace.yaml config for pnpm", () => {
|
|
97
|
+
const config = getWorkspaceConfig("pnpm", ["packages/*", "apps/*"]);
|
|
98
|
+
expect(config.type).toBe("file");
|
|
99
|
+
if (config.type === "file") {
|
|
100
|
+
expect(config.filename).toBe("pnpm-workspace.yaml");
|
|
101
|
+
expect(config.content).toContain("packages:");
|
|
102
|
+
expect(config.content).toContain(" - packages/*");
|
|
103
|
+
expect(config.content).toContain(" - apps/*");
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
it("returns packageJson config for npm", () => {
|
|
108
|
+
const config = getWorkspaceConfig("npm", ["packages/*", "apps/*"]);
|
|
109
|
+
expect(config.type).toBe("packageJson");
|
|
110
|
+
if (config.type === "packageJson") {
|
|
111
|
+
expect(config.field).toBe("workspaces");
|
|
112
|
+
expect(config.value).toEqual(["packages/*", "apps/*"]);
|
|
113
|
+
}
|
|
114
|
+
});
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
describe("getCiSetupStep", () => {
|
|
118
|
+
it("returns pnpm setup step for pnpm", () => {
|
|
119
|
+
const step = getCiSetupStep("pnpm");
|
|
120
|
+
expect(step).toContain("pnpm/action-setup");
|
|
121
|
+
expect(step).toContain("version: latest");
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
it("returns empty string for npm", () => {
|
|
125
|
+
expect(getCiSetupStep("npm")).toBe("");
|
|
126
|
+
});
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
describe("getSwaAppBuildCommand", () => {
|
|
130
|
+
it("returns a pnpm build command for pnpm projects", () => {
|
|
131
|
+
expect(getSwaAppBuildCommand("pnpm")).toBe("pnpm install --frozen-lockfile && pnpm run build");
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
it("returns an npm build command for npm projects", () => {
|
|
135
|
+
expect(getSwaAppBuildCommand("npm")).toBe("npm ci && npm run build");
|
|
136
|
+
});
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
describe("getAzurePipelinesSetup", () => {
|
|
140
|
+
it("returns corepack step for pnpm", () => {
|
|
141
|
+
const step = getAzurePipelinesSetup("pnpm");
|
|
142
|
+
expect(step).toContain("corepack enable");
|
|
143
|
+
expect(step).toContain("pnpm");
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
it("returns empty string for npm", () => {
|
|
147
|
+
expect(getAzurePipelinesSetup("npm")).toBe("");
|
|
148
|
+
});
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
describe("getBuildScript", () => {
|
|
152
|
+
it("uses pnpm run --filter for pnpm", () => {
|
|
153
|
+
expect(getBuildScript("pnpm")).toContain("pnpm run --filter shared build");
|
|
154
|
+
expect(getBuildScript("pnpm")).toContain("fs.cpSync");
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
it("uses npm run --workspace for npm", () => {
|
|
158
|
+
expect(getBuildScript("npm")).toContain("npm run --workspace=shared build");
|
|
159
|
+
expect(getBuildScript("npm")).toContain("fs.cpSync");
|
|
160
|
+
});
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
describe("getFunctionsPrestart", () => {
|
|
164
|
+
it("uses pnpm run build for pnpm", () => {
|
|
165
|
+
expect(getFunctionsPrestart("pnpm")).toBe("pnpm run build");
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
it("uses npm run build for npm", () => {
|
|
169
|
+
expect(getFunctionsPrestart("npm")).toBe("npm run build");
|
|
170
|
+
});
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
describe("getFunctionsStartScript", () => {
|
|
174
|
+
it("uses pnpm start for pnpm", () => {
|
|
175
|
+
expect(getFunctionsStartScript("pnpm")).toContain("pnpm start");
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
it("uses npm start for npm", () => {
|
|
179
|
+
expect(getFunctionsStartScript("npm")).toContain("npm start");
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
it("uses func start for csharp backends", () => {
|
|
183
|
+
expect(getFunctionsStartScript("pnpm", "csharp")).toBe("cd functions && func start");
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
it("uses func start for python backends", () => {
|
|
187
|
+
expect(getFunctionsStartScript("npm", "python")).toBe("cd functions && func start");
|
|
188
|
+
});
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
describe("spawnArgs", () => {
|
|
192
|
+
it("returns cmd and args for pnpm", () => {
|
|
193
|
+
const result = spawnArgs("pnpm", ["add", "next@latest"]);
|
|
194
|
+
expect(result).toEqual({ cmd: "pnpm", args: ["add", "next@latest"] });
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
it("returns cmd and args for npm", () => {
|
|
198
|
+
const result = spawnArgs("npm", ["install", "next@latest"]);
|
|
199
|
+
expect(result).toEqual({ cmd: "npm", args: ["install", "next@latest"] });
|
|
200
|
+
});
|
|
201
|
+
});
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import * as path from "path";
|
|
2
|
+
import {
|
|
3
|
+
buildProjectLocalUvEnv,
|
|
4
|
+
buildUvPipInstallArgs,
|
|
5
|
+
buildUvVenvArgs,
|
|
6
|
+
getProjectLocalUvPaths,
|
|
7
|
+
getPythonProjectRoot,
|
|
8
|
+
} from "../utils/python-uv";
|
|
9
|
+
|
|
10
|
+
describe("python uv helpers", () => {
|
|
11
|
+
it("keeps uv-managed state inside the project", () => {
|
|
12
|
+
const projectRoot = "C:\\repo";
|
|
13
|
+
const uvPaths = getProjectLocalUvPaths(projectRoot);
|
|
14
|
+
|
|
15
|
+
expect(uvPaths.stateDir).toBe(path.join(projectRoot, ".uv"));
|
|
16
|
+
expect(uvPaths.cacheDir).toBe(path.join(projectRoot, ".uv", "cache"));
|
|
17
|
+
expect(uvPaths.pythonInstallDir).toBe(path.join(projectRoot, ".uv", "python"));
|
|
18
|
+
expect(uvPaths.localUvExecutable.endsWith(process.platform === "win32" ? path.join(".uv", "bin", "uv.exe") : path.join(".uv", "bin", "uv"))).toBe(true);
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it("derives the project root from the functions directory", () => {
|
|
22
|
+
expect(getPythonProjectRoot(path.join("C:\\repo", "functions"))).toBe("C:\\repo");
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it("builds project-local uv environment variables", () => {
|
|
26
|
+
const env = buildProjectLocalUvEnv({ PATH: "C:\\Windows\\System32" }, "C:\\repo");
|
|
27
|
+
|
|
28
|
+
expect(env.UV_CACHE_DIR).toBe(path.join("C:\\repo", ".uv", "cache"));
|
|
29
|
+
expect(env.UV_PYTHON_INSTALL_DIR).toBe(path.join("C:\\repo", ".uv", "python"));
|
|
30
|
+
expect(env.UV_TOOL_DIR).toBe(path.join("C:\\repo", ".uv", "tools"));
|
|
31
|
+
expect(env.UV_TOOL_BIN_DIR).toBe(path.join("C:\\repo", ".uv", "tools", "bin"));
|
|
32
|
+
expect(env.UV_MANAGED_PYTHON).toBeUndefined();
|
|
33
|
+
expect(env.UV_PYTHON_PREFERENCE).toBe("only-managed");
|
|
34
|
+
expect(env.UV_PYTHON_NO_REGISTRY).toBe(process.platform === "win32" ? "true" : undefined);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it("builds uv commands for venv creation and requirements installation", () => {
|
|
38
|
+
expect(buildUvVenvArgs(".venv")).toEqual(["venv", ".venv", "--python", "3.11"]);
|
|
39
|
+
expect(buildUvPipInstallArgs(path.join("C:\\repo", "functions", ".venv", "Scripts", "python.exe"), "requirements.txt")).toEqual([
|
|
40
|
+
"pip",
|
|
41
|
+
"install",
|
|
42
|
+
"--python",
|
|
43
|
+
path.join("C:\\repo", "functions", ".venv", "Scripts", "python.exe"),
|
|
44
|
+
"-r",
|
|
45
|
+
"requirements.txt",
|
|
46
|
+
]);
|
|
47
|
+
});
|
|
48
|
+
});
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
import * as path from "path";
|
|
2
|
+
import * as fs from "fs";
|
|
3
|
+
import * as os from "os";
|
|
4
|
+
import { EventEmitter } from "events";
|
|
5
|
+
import * as childProcess from "child_process";
|
|
6
|
+
import { generateCosmosContainer } from "../cli/commands/scaffold";
|
|
7
|
+
import {
|
|
8
|
+
NSWAG_CONSOLECORE_VERSION,
|
|
9
|
+
buildCSharpCodegenToolManifestSource,
|
|
10
|
+
buildPythonCodegenRequirementsSource,
|
|
11
|
+
generateLanguageSchemaArtifacts,
|
|
12
|
+
getCSharpNativeGeneratorArgs,
|
|
13
|
+
getCSharpSchemaModelPath,
|
|
14
|
+
getCSharpSchemaOptionPath,
|
|
15
|
+
getPythonNativeGeneratorArgs,
|
|
16
|
+
getPythonSchemaModelPath,
|
|
17
|
+
} from "../core/scaffold/native-schema-generator";
|
|
18
|
+
import { createBasicModelInfo } from "./fixtures";
|
|
19
|
+
|
|
20
|
+
jest.mock("child_process", () => {
|
|
21
|
+
const actual = jest.requireActual<typeof import("child_process")>("child_process");
|
|
22
|
+
return {
|
|
23
|
+
...actual,
|
|
24
|
+
spawn: jest.fn(),
|
|
25
|
+
spawnSync: jest.fn(),
|
|
26
|
+
};
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
function writeFile(filePath: string, content: string): void {
|
|
30
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
31
|
+
fs.writeFileSync(filePath, content, "utf-8");
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function mockSuccessfulSpawn() {
|
|
35
|
+
const spawnMock = childProcess.spawn as unknown as any;
|
|
36
|
+
spawnMock.mockImplementation(() => {
|
|
37
|
+
const child = new EventEmitter() as childProcess.ChildProcess;
|
|
38
|
+
process.nextTick(() => child.emit("close", 0));
|
|
39
|
+
return child;
|
|
40
|
+
});
|
|
41
|
+
return spawnMock;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
describe("native schema generators", () => {
|
|
45
|
+
it("writes a dotnet tool manifest for NSwag", () => {
|
|
46
|
+
const manifest = JSON.parse(buildCSharpCodegenToolManifestSource());
|
|
47
|
+
|
|
48
|
+
expect(manifest.version).toBe(1);
|
|
49
|
+
expect(manifest.tools["nswag.consolecore"].version).toBe(NSWAG_CONSOLECORE_VERSION);
|
|
50
|
+
expect(manifest.tools["nswag.consolecore"].commands).toEqual(["nswag"]);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it("pins python schema generation requirements without Java", () => {
|
|
54
|
+
expect(buildPythonCodegenRequirementsSource()).toContain("datamodel-code-generator");
|
|
55
|
+
expect(buildPythonCodegenRequirementsSource()).not.toContain("openapi-generator");
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it("builds NSwag arguments for model-only C# generation", () => {
|
|
59
|
+
const outputPath = path.join("C:\\temp\\generated\\csharp-models", ".native-temp", "Contracts.cs");
|
|
60
|
+
const args = getCSharpNativeGeneratorArgs("C:\\temp\\todo.openapi.json", outputPath);
|
|
61
|
+
|
|
62
|
+
expect(args.slice(0, 4)).toEqual(["tool", "run", "nswag", "openapi2csclient"]);
|
|
63
|
+
expect(args).toContain("/GenerateClientClasses:false");
|
|
64
|
+
expect(args).toContain("/GenerateDtoTypes:true");
|
|
65
|
+
expect(args).toContain("/GenerateNullableReferenceTypes:true");
|
|
66
|
+
expect(args).toContain(`/output:${outputPath}`);
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it("builds datamodel-code-generator arguments for python assets", () => {
|
|
70
|
+
const outputPath = path.join("C:\\temp\\generated\\python-models", ".native-temp", "models.py");
|
|
71
|
+
const args = getPythonNativeGeneratorArgs("C:\\temp\\todo.openapi.json", outputPath);
|
|
72
|
+
|
|
73
|
+
expect(args).toEqual(
|
|
74
|
+
expect.arrayContaining([
|
|
75
|
+
"-m",
|
|
76
|
+
"datamodel_code_generator",
|
|
77
|
+
"--input-file-type",
|
|
78
|
+
"openapi",
|
|
79
|
+
"--output-model-type",
|
|
80
|
+
"pydantic_v2.BaseModel",
|
|
81
|
+
"--disable-timestamp",
|
|
82
|
+
outputPath,
|
|
83
|
+
])
|
|
84
|
+
);
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
it("keeps generated asset paths under functions/generated", () => {
|
|
88
|
+
expect(getCSharpSchemaModelPath("C:\\temp\\generated\\csharp-models", "Product")).toBe(
|
|
89
|
+
path.join("C:\\temp\\generated\\csharp-models", "src", "SwallowKitBackendModels", "Model", "Product.cs")
|
|
90
|
+
);
|
|
91
|
+
expect(getCSharpSchemaOptionPath("C:\\temp\\generated\\csharp-models")).toBe(
|
|
92
|
+
path.join("C:\\temp\\generated\\csharp-models", "src", "SwallowKitBackendModels", "Client", "Option.cs")
|
|
93
|
+
);
|
|
94
|
+
expect(getPythonSchemaModelPath("C:\\temp\\generated\\python-models", "Product")).toBe(
|
|
95
|
+
path.join("C:\\temp\\generated\\python-models", "backend_models", "models", "product.py")
|
|
96
|
+
);
|
|
97
|
+
});
|
|
98
|
+
describe("generateCosmosContainer", () => {
|
|
99
|
+
const originalCwd = process.cwd();
|
|
100
|
+
let tempDir: string;
|
|
101
|
+
|
|
102
|
+
beforeEach(() => {
|
|
103
|
+
tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "swallowkit-cosmos-container-"));
|
|
104
|
+
process.chdir(tempDir);
|
|
105
|
+
fs.mkdirSync(path.join(tempDir, "infra", "containers"), { recursive: true });
|
|
106
|
+
fs.writeFileSync(path.join(tempDir, "infra", "main.bicep"), "module cosmosDbServerless 'modules/cosmosdb-serverless.bicep' = if (cosmosDbMode == 'serverless') {\n name: 'cosmosDb'\n}\n");
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
afterEach(() => {
|
|
110
|
+
process.chdir(originalCwd);
|
|
111
|
+
fs.rmSync(tempDir, { recursive: true, force: true });
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
it("generates container templates without container-level throughput settings", async () => {
|
|
115
|
+
await generateCosmosContainer({ name: "SurveyResponse", partitionKey: "/id" });
|
|
116
|
+
|
|
117
|
+
const containerFilePath = path.join(tempDir, "infra", "containers", "survey-response-container.bicep");
|
|
118
|
+
const containerContent = fs.readFileSync(containerFilePath, "utf-8");
|
|
119
|
+
const mainContent = fs.readFileSync(path.join(tempDir, "infra", "main.bicep"), "utf-8");
|
|
120
|
+
|
|
121
|
+
expect(containerContent).toContain("resource container");
|
|
122
|
+
expect(containerContent).not.toContain("throughput");
|
|
123
|
+
expect(containerContent).not.toContain("options:");
|
|
124
|
+
expect(mainContent).toContain("module surveyResponseContainer 'containers/survey-response-container.bicep'");
|
|
125
|
+
expect(mainContent).not.toContain("dependsOn");
|
|
126
|
+
});
|
|
127
|
+
});
|
|
128
|
+
describe("non-destructive native schema generation", () => {
|
|
129
|
+
const originalCwd = process.cwd();
|
|
130
|
+
let tempDir: string;
|
|
131
|
+
let spawnSyncMock: any;
|
|
132
|
+
let spawnMock: any;
|
|
133
|
+
|
|
134
|
+
beforeEach(() => {
|
|
135
|
+
tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "swallowkit-native-schema-"));
|
|
136
|
+
process.chdir(tempDir);
|
|
137
|
+
fs.mkdirSync(path.join(tempDir, "functions"), { recursive: true });
|
|
138
|
+
spawnSyncMock = childProcess.spawnSync as unknown as any;
|
|
139
|
+
spawnSyncMock.mockReturnValue({ status: 0 });
|
|
140
|
+
spawnMock = mockSuccessfulSpawn();
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
afterEach(() => {
|
|
144
|
+
spawnMock.mockReset();
|
|
145
|
+
spawnSyncMock.mockReset();
|
|
146
|
+
process.chdir(originalCwd);
|
|
147
|
+
fs.rmSync(tempDir, { recursive: true, force: true });
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
it("preserves existing C# generated directory contents while writing only requested model files", async () => {
|
|
151
|
+
const outputDir = path.join(tempDir, "functions", "generated", "csharp-models");
|
|
152
|
+
writeFile(path.join(outputDir, "README.md"), "keep me\n");
|
|
153
|
+
writeFile(path.join(outputDir, "SwallowKitBackendModels.csproj"), "<Project />\n");
|
|
154
|
+
writeFile(path.join(outputDir, "src", "SwallowKitBackendModels", "Model", "Member.cs"), "// existing member\n");
|
|
155
|
+
|
|
156
|
+
const estimate = createBasicModelInfo({ name: "Estimate", filePath: path.join(tempDir, "shared", "models", "estimate.ts") });
|
|
157
|
+
|
|
158
|
+
await generateLanguageSchemaArtifacts([estimate], estimate, "functions", "csharp");
|
|
159
|
+
|
|
160
|
+
expect(fs.readFileSync(path.join(outputDir, "README.md"), "utf-8")).toBe("keep me\n");
|
|
161
|
+
expect(fs.existsSync(path.join(outputDir, "SwallowKitBackendModels.csproj"))).toBe(true);
|
|
162
|
+
expect(fs.readFileSync(getCSharpSchemaModelPath(outputDir, "Member"), "utf-8")).toBe("// existing member\n");
|
|
163
|
+
expect(fs.existsSync(getCSharpSchemaModelPath(outputDir, "Todo"))).toBe(false);
|
|
164
|
+
expect(fs.existsSync(getCSharpSchemaModelPath(outputDir, "Estimate"))).toBe(true);
|
|
165
|
+
expect(fs.existsSync(getCSharpSchemaOptionPath(outputDir))).toBe(true);
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
it("preserves existing Python generated directory contents while appending requested model exports", async () => {
|
|
169
|
+
const outputDir = path.join(tempDir, "functions", "generated", "python-models");
|
|
170
|
+
writeFile(path.join(outputDir, "README.md"), "keep python\n");
|
|
171
|
+
writeFile(path.join(outputDir, "backend_models", "models", "member.py"), "# existing member\n");
|
|
172
|
+
writeFile(path.join(outputDir, "backend_models", "__init__.py"), "from .models.member import Member\n");
|
|
173
|
+
writeFile(path.join(outputDir, "backend_models", "models", "__init__.py"), "from .member import Member\n");
|
|
174
|
+
|
|
175
|
+
const estimate = createBasicModelInfo({ name: "Estimate", filePath: path.join(tempDir, "shared", "models", "estimate.ts") });
|
|
176
|
+
|
|
177
|
+
await generateLanguageSchemaArtifacts([estimate], estimate, "functions", "python");
|
|
178
|
+
|
|
179
|
+
expect(fs.readFileSync(path.join(outputDir, "README.md"), "utf-8")).toBe("keep python\n");
|
|
180
|
+
expect(fs.readFileSync(path.join(outputDir, "backend_models", "models", "member.py"), "utf-8")).toBe("# existing member\n");
|
|
181
|
+
expect(fs.existsSync(getPythonSchemaModelPath(outputDir, "Todo"))).toBe(false);
|
|
182
|
+
expect(fs.existsSync(getPythonSchemaModelPath(outputDir, "Estimate"))).toBe(true);
|
|
183
|
+
const packageInit = fs.readFileSync(path.join(outputDir, "backend_models", "__init__.py"), "utf-8");
|
|
184
|
+
const modelsInit = fs.readFileSync(path.join(outputDir, "backend_models", "models", "__init__.py"), "utf-8");
|
|
185
|
+
expect(packageInit).toContain("from .models.member import Member");
|
|
186
|
+
expect(packageInit).toContain("from .models.estimate import Estimate");
|
|
187
|
+
expect(modelsInit).toContain("from .member import Member");
|
|
188
|
+
expect(modelsInit).toContain("from .estimate import Estimate");
|
|
189
|
+
});
|
|
190
|
+
});
|
|
191
|
+
});
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { toPascalCase, toCamelCase, toKebabCase } from "../core/scaffold/model-parser";
|
|
2
|
+
|
|
3
|
+
describe("toPascalCase", () => {
|
|
4
|
+
it("converts kebab-case to PascalCase", () => {
|
|
5
|
+
expect(toPascalCase("todo-item")).toBe("TodoItem");
|
|
6
|
+
});
|
|
7
|
+
|
|
8
|
+
it("converts snake_case to PascalCase", () => {
|
|
9
|
+
expect(toPascalCase("todo_item")).toBe("TodoItem");
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
it("handles single word", () => {
|
|
13
|
+
expect(toPascalCase("todo")).toBe("Todo");
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
it("handles already PascalCase (no separators)", () => {
|
|
17
|
+
expect(toPascalCase("Todo")).toBe("Todo");
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it("handles multiple segments", () => {
|
|
21
|
+
expect(toPascalCase("my-cool-model")).toBe("MyCoolModel");
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it("handles empty string", () => {
|
|
25
|
+
expect(toPascalCase("")).toBe("");
|
|
26
|
+
});
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
describe("toCamelCase", () => {
|
|
30
|
+
it("converts kebab-case to camelCase", () => {
|
|
31
|
+
expect(toCamelCase("todo-item")).toBe("todoItem");
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it("converts snake_case to camelCase", () => {
|
|
35
|
+
expect(toCamelCase("todo_item")).toBe("todoItem");
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it("handles single word", () => {
|
|
39
|
+
expect(toCamelCase("Todo")).toBe("todo");
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it("handles PascalCase input (lowercases first char)", () => {
|
|
43
|
+
expect(toCamelCase("TodoItem")).toBe("todoItem");
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it("handles empty string", () => {
|
|
47
|
+
expect(toCamelCase("")).toBe("");
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
describe("toKebabCase", () => {
|
|
52
|
+
it("converts PascalCase to kebab-case", () => {
|
|
53
|
+
expect(toKebabCase("TodoItem")).toBe("todo-item");
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it("converts camelCase to kebab-case", () => {
|
|
57
|
+
expect(toKebabCase("todoItem")).toBe("todo-item");
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it("handles single lowercase word", () => {
|
|
61
|
+
expect(toKebabCase("todo")).toBe("todo");
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it("handles multiple capitals", () => {
|
|
65
|
+
expect(toKebabCase("MyCoolModel")).toBe("my-cool-model");
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it("handles already kebab-case", () => {
|
|
69
|
+
expect(toKebabCase("todo-item")).toBe("todo-item");
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it("handles empty string", () => {
|
|
73
|
+
expect(toKebabCase("")).toBe("");
|
|
74
|
+
});
|
|
75
|
+
});
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import {
|
|
2
|
+
generateListPage,
|
|
3
|
+
generateDetailPage,
|
|
4
|
+
generateFormComponent,
|
|
5
|
+
generateNewPage,
|
|
6
|
+
generateEditPage,
|
|
7
|
+
} from "../core/scaffold/ui-generator";
|
|
8
|
+
import {
|
|
9
|
+
createBasicModelInfo,
|
|
10
|
+
createModelInfoWithForeignKey,
|
|
11
|
+
createModelInfoWithEnum,
|
|
12
|
+
} from "./fixtures";
|
|
13
|
+
|
|
14
|
+
describe("generateListPage", () => {
|
|
15
|
+
it("generates list page for basic model (snapshot)", () => {
|
|
16
|
+
const model = createBasicModelInfo();
|
|
17
|
+
const code = generateListPage(model, "@myapp/shared");
|
|
18
|
+
expect(code).toMatchSnapshot();
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it("contains 'use client' directive", () => {
|
|
22
|
+
const model = createBasicModelInfo();
|
|
23
|
+
const code = generateListPage(model, "@myapp/shared");
|
|
24
|
+
expect(code).toContain("'use client'");
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it("imports schema from shared package", () => {
|
|
28
|
+
const model = createBasicModelInfo();
|
|
29
|
+
const code = generateListPage(model, "@myapp/shared");
|
|
30
|
+
expect(code).toContain("@myapp/shared");
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it("fetches from correct API endpoint", () => {
|
|
34
|
+
const model = createBasicModelInfo();
|
|
35
|
+
const code = generateListPage(model, "@myapp/shared");
|
|
36
|
+
expect(code).toContain("/api/todo");
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it("displays up to 3 non-id fields", () => {
|
|
40
|
+
const model = createBasicModelInfo();
|
|
41
|
+
const code = generateListPage(model, "@myapp/shared");
|
|
42
|
+
// title, description, completed — first 3 non-id fields
|
|
43
|
+
expect(code).toContain("title");
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it("handles foreign key fields", () => {
|
|
47
|
+
const model = createModelInfoWithForeignKey();
|
|
48
|
+
const code = generateListPage(model, "@myapp/shared");
|
|
49
|
+
expect(code).toContain("categoryId");
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
describe("generateDetailPage", () => {
|
|
54
|
+
it("generates detail page (snapshot)", () => {
|
|
55
|
+
const model = createBasicModelInfo();
|
|
56
|
+
const code = generateDetailPage(model, "@myapp/shared");
|
|
57
|
+
expect(code).toMatchSnapshot();
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it("contains 'use client' directive", () => {
|
|
61
|
+
const model = createBasicModelInfo();
|
|
62
|
+
const code = generateDetailPage(model, "@myapp/shared");
|
|
63
|
+
expect(code).toContain("'use client'");
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it("includes delete button", () => {
|
|
67
|
+
const model = createBasicModelInfo();
|
|
68
|
+
const code = generateDetailPage(model, "@myapp/shared");
|
|
69
|
+
expect(code).toContain("DELETE");
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
describe("generateFormComponent", () => {
|
|
74
|
+
it("generates form component (snapshot)", () => {
|
|
75
|
+
const model = createBasicModelInfo();
|
|
76
|
+
const code = generateFormComponent(model, "@myapp/shared");
|
|
77
|
+
expect(code).toMatchSnapshot();
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it("generates inputs for non-managed fields", () => {
|
|
81
|
+
const model = createBasicModelInfo();
|
|
82
|
+
const code = generateFormComponent(model, "@myapp/shared");
|
|
83
|
+
// title and description should have form inputs
|
|
84
|
+
expect(code).toContain("title");
|
|
85
|
+
expect(code).toContain("description");
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it("generates enum select for enum fields", () => {
|
|
89
|
+
const model = createModelInfoWithEnum();
|
|
90
|
+
const code = generateFormComponent(model, "@myapp/shared");
|
|
91
|
+
expect(code).toContain("select");
|
|
92
|
+
expect(code).toContain("open");
|
|
93
|
+
expect(code).toContain("in_progress");
|
|
94
|
+
expect(code).toContain("closed");
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
it("generates checkbox for boolean fields", () => {
|
|
98
|
+
const model = createBasicModelInfo();
|
|
99
|
+
const code = generateFormComponent(model, "@myapp/shared");
|
|
100
|
+
expect(code).toContain("checkbox");
|
|
101
|
+
});
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
describe("generateNewPage", () => {
|
|
105
|
+
it("generates new page (snapshot)", () => {
|
|
106
|
+
const model = createBasicModelInfo();
|
|
107
|
+
const code = generateNewPage(model);
|
|
108
|
+
expect(code).toMatchSnapshot();
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
it("contains form component reference", () => {
|
|
112
|
+
const model = createBasicModelInfo();
|
|
113
|
+
const code = generateNewPage(model);
|
|
114
|
+
expect(code).toContain("Form");
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
it("references the form component", () => {
|
|
118
|
+
const model = createBasicModelInfo();
|
|
119
|
+
const code = generateNewPage(model);
|
|
120
|
+
expect(code).toContain("TodoForm");
|
|
121
|
+
expect(code).toContain("Create New Todo");
|
|
122
|
+
});
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
describe("generateEditPage", () => {
|
|
126
|
+
it("generates edit page (snapshot)", () => {
|
|
127
|
+
const model = createBasicModelInfo();
|
|
128
|
+
const code = generateEditPage(model, "@myapp/shared");
|
|
129
|
+
expect(code).toMatchSnapshot();
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
it("loads existing data and passes to form", () => {
|
|
133
|
+
const model = createBasicModelInfo();
|
|
134
|
+
const code = generateEditPage(model, "@myapp/shared");
|
|
135
|
+
expect(code).toContain("initialData");
|
|
136
|
+
expect(code).toContain("isEdit={true}");
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
it("fetches existing data for editing", () => {
|
|
140
|
+
const model = createBasicModelInfo();
|
|
141
|
+
const code = generateEditPage(model, "@myapp/shared");
|
|
142
|
+
expect(code).toContain("/api/todo/");
|
|
143
|
+
});
|
|
144
|
+
});
|