@ic-reactor/vite-plugin 0.12.1 → 0.13.1

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/src/index.test.ts DELETED
@@ -1,349 +0,0 @@
1
- import { describe, it, expect, vi, beforeEach } from "vitest"
2
- import * as vitePluginModule from "./index.js"
3
- import type { IcReactorPluginOptions } from "./index.js"
4
- import path from "path"
5
- import { execFileSync } from "child_process"
6
- import { runCanisterPipeline } from "@ic-reactor/codegen"
7
-
8
- const createVitePlugin =
9
- (vitePluginModule as any).icReactor ??
10
- (vitePluginModule as any).icReactorPlugin
11
-
12
- // Mock internal dependencies
13
- vi.mock("@ic-reactor/codegen", () => ({
14
- runCanisterPipeline: vi.fn(),
15
- }))
16
-
17
- // Mock child_process
18
- vi.mock("child_process", () => ({
19
- execFileSync: vi.fn(),
20
- }))
21
-
22
- // Mock fs
23
- vi.mock("fs", () => ({
24
- default: {
25
- existsSync: vi.fn(() => true),
26
- readFileSync: vi.fn(),
27
- mkdirSync: vi.fn(),
28
- writeFileSync: vi.fn(),
29
- },
30
- }))
31
-
32
- describe("icReactor", () => {
33
- const mockOptions: IcReactorPluginOptions = {
34
- canisters: [
35
- {
36
- name: "test_canister",
37
- didFile: "src/declarations/test.did",
38
- outDir: "src/declarations/test_canister",
39
- },
40
- ],
41
- outDir: "src/declarations",
42
- }
43
-
44
- const mockServer: any = {
45
- config: {
46
- root: "/mock/root",
47
- logger: {
48
- info: vi.fn(),
49
- },
50
- },
51
- middlewares: {
52
- use: vi.fn(),
53
- },
54
- restart: vi.fn(),
55
- ws: {
56
- send: vi.fn(),
57
- },
58
- watcher: {
59
- add: vi.fn(),
60
- },
61
- }
62
-
63
- beforeEach(() => {
64
- vi.resetAllMocks()
65
- ;(runCanisterPipeline as any).mockResolvedValue({
66
- success: true,
67
- })
68
- })
69
-
70
- it("should return correct plugin structure", () => {
71
- const plugin = createVitePlugin(mockOptions)
72
- expect(plugin.name).toBe("ic-reactor-plugin")
73
- expect(plugin.buildStart).toBeDefined()
74
- expect(plugin.handleHotUpdate).toBeDefined()
75
- expect((plugin as any).config).toBeDefined()
76
- })
77
-
78
- describe("config", () => {
79
- it("should set up API proxy and headers when icp-cli is available", () => {
80
- ;(execFileSync as any).mockImplementation(
81
- (command: string, args: string[], _options: any) => {
82
- if (
83
- command === "icp" &&
84
- args.includes("network") &&
85
- args.includes("status")
86
- ) {
87
- return JSON.stringify({ root_key: "mock-root-key", port: 4943 })
88
- }
89
- if (
90
- command === "icp" &&
91
- args.includes("canister") &&
92
- args.includes("status")
93
- ) {
94
- return "mock-canister-id"
95
- }
96
- return ""
97
- }
98
- )
99
-
100
- const plugin = createVitePlugin(mockOptions)
101
- const config = (plugin as any).config({}, { command: "serve" })
102
-
103
- expect(config.server.headers["Set-Cookie"]).toContain("ic_env=")
104
- expect(config.server.headers["Set-Cookie"]).toContain(
105
- "PUBLIC_CANISTER_ID%3Atest_canister%3Dmock-canister-id"
106
- )
107
- expect(config.server.headers["Set-Cookie"]).toContain(
108
- "ic_root_key%3Dmock-root-key"
109
- )
110
- expect(config.server.proxy["/api"].target).toBe("http://127.0.0.1:4943")
111
- })
112
-
113
- it("should prefer configured canisterId over CLI-discovered env values", () => {
114
- ;(execFileSync as any).mockImplementation(
115
- (command: string, args: string[], _options: any) => {
116
- if (
117
- command === "icp" &&
118
- args.includes("network") &&
119
- args.includes("status")
120
- ) {
121
- return JSON.stringify({ root_key: "mock-root-key", port: 4943 })
122
- }
123
- if (
124
- command === "icp" &&
125
- args.includes("canister") &&
126
- args.includes("status")
127
- ) {
128
- return "local-cli-canister-id"
129
- }
130
- return ""
131
- }
132
- )
133
-
134
- const plugin = createVitePlugin({
135
- ...mockOptions,
136
- canisters: [
137
- {
138
- ...mockOptions.canisters[0],
139
- canisterId: "yq4ns-hyaaa-aaaap-akbna-cai",
140
- },
141
- ],
142
- })
143
- const config = (plugin as any).config({}, { command: "serve" })
144
-
145
- expect(config.server.headers["Set-Cookie"]).toContain(
146
- "PUBLIC_CANISTER_ID%3Atest_canister%3Dyq4ns-hyaaa-aaaap-akbna-cai"
147
- )
148
- expect(config.server.headers["Set-Cookie"]).not.toContain(
149
- "PUBLIC_CANISTER_ID%3Atest_canister%3Dlocal-cli-canister-id"
150
- )
151
- })
152
-
153
- it("should inject built-in local Internet Identity provider when no project II canister is found", () => {
154
- ;(execFileSync as any).mockImplementation(
155
- (command: string, args: string[], _options: any) => {
156
- if (
157
- command === "icp" &&
158
- args.includes("network") &&
159
- args.includes("status")
160
- ) {
161
- return JSON.stringify({
162
- root_key: "mock-root-key",
163
- api_url: "http://localhost:8000/",
164
- })
165
- }
166
- if (
167
- command === "icp" &&
168
- args.includes("canister") &&
169
- args.includes("status")
170
- ) {
171
- if (args.includes("internet_identity")) {
172
- throw new Error("system canister is not a project canister")
173
- }
174
- return "mock-canister-id"
175
- }
176
- return ""
177
- }
178
- )
179
-
180
- const plugin = createVitePlugin(mockOptions)
181
- const config = (plugin as any).config({}, { command: "serve" })
182
-
183
- expect(config.server.headers["Set-Cookie"]).toContain(
184
- "INTERNET_IDENTITY_PROVIDER%3Dhttp%3A%2F%2Fid.ai.localhost%3A8000%2Fauthorize"
185
- )
186
- expect(config.server.proxy["/api"].target).toBe("http://localhost:8000/")
187
- })
188
-
189
- it("should fallback to default proxy when icp-cli fails", () => {
190
- ;(execFileSync as any).mockImplementation(() => {
191
- throw new Error("Command not found")
192
- })
193
-
194
- const plugin = createVitePlugin(mockOptions)
195
- const config = (plugin as any).config({}, { command: "serve" })
196
-
197
- expect(config.server.headers).toBeUndefined()
198
- expect(config.server.proxy["/api"].target).toBe("http://127.0.0.1:4943")
199
- })
200
-
201
- it("should inject default local II provider in env-only mode when icp-cli project detection fails", () => {
202
- ;(execFileSync as any).mockImplementation(() => {
203
- throw new Error("project manifest not found")
204
- })
205
-
206
- const plugin = createVitePlugin({ canisters: [] })
207
- const config = (plugin as any).config({}, { command: "serve" })
208
-
209
- expect(config.server.headers["Set-Cookie"]).toContain(
210
- "INTERNET_IDENTITY_PROVIDER%3Dhttp%3A%2F%2Fid.ai.localhost%3A8000%2Fauthorize"
211
- )
212
- expect(config.server.proxy["/api"].target).toBe("http://127.0.0.1:4943")
213
- })
214
-
215
- it("should return empty config for build command", () => {
216
- const plugin = createVitePlugin(mockOptions)
217
- const config = (plugin as any).config({}, { command: "build" })
218
-
219
- expect(config).toEqual({})
220
- })
221
- })
222
-
223
- describe("buildStart", () => {
224
- it("should generate declarations and reactor file", async () => {
225
- const plugin = createVitePlugin(mockOptions)
226
- await (plugin.buildStart as any)()
227
-
228
- expect(runCanisterPipeline).toHaveBeenCalledWith({
229
- canisterConfig: mockOptions.canisters[0],
230
- projectRoot: expect.any(String),
231
- globalConfig: {
232
- outDir: "src/declarations",
233
- clientManagerPath: "../../clients",
234
- target: "react",
235
- },
236
- })
237
- })
238
-
239
- it("should handle generation errors", async () => {
240
- const consoleErrorSpy = vi
241
- .spyOn(console, "error")
242
- .mockImplementation(() => {})
243
-
244
- ;(runCanisterPipeline as any).mockResolvedValue({
245
- success: false,
246
- error: "Failed to generate",
247
- })
248
-
249
- const plugin = createVitePlugin(mockOptions)
250
- await (plugin.buildStart as any)()
251
-
252
- expect(consoleErrorSpy).toHaveBeenCalledWith(
253
- expect.stringContaining("Failed to generate test_canister")
254
- )
255
-
256
- consoleErrorSpy.mockRestore()
257
- })
258
-
259
- it("should pass canister mode through to codegen via canister config", async () => {
260
- const plugin = createVitePlugin({
261
- canisters: [
262
- {
263
- ...mockOptions.canisters[0],
264
- mode: "Reactor",
265
- },
266
- ],
267
- outDir: mockOptions.outDir,
268
- })
269
-
270
- await (plugin.buildStart as any)()
271
-
272
- expect(runCanisterPipeline).toHaveBeenCalledWith({
273
- canisterConfig: {
274
- ...mockOptions.canisters[0],
275
- mode: "Reactor",
276
- },
277
- projectRoot: expect.any(String),
278
- globalConfig: {
279
- outDir: "src/declarations",
280
- clientManagerPath: "../../clients",
281
- target: "react",
282
- },
283
- })
284
- })
285
-
286
- it("should pass the configured runtime target to codegen", async () => {
287
- const plugin = createVitePlugin({
288
- ...mockOptions,
289
- target: "core",
290
- })
291
-
292
- await (plugin.buildStart as any)()
293
-
294
- expect(runCanisterPipeline).toHaveBeenCalledWith({
295
- canisterConfig: mockOptions.canisters[0],
296
- projectRoot: expect.any(String),
297
- globalConfig: {
298
- outDir: "src/declarations",
299
- clientManagerPath: "../../clients",
300
- target: "core",
301
- },
302
- })
303
- })
304
- })
305
-
306
- describe("handleHotUpdate", () => {
307
- it("should register configured .did files with the watcher", () => {
308
- const plugin = createVitePlugin(mockOptions)
309
- ;(plugin.configureServer as any)(mockServer)
310
-
311
- expect(mockServer.watcher.add).toHaveBeenCalledWith([
312
- expect.stringContaining("src/declarations/test.did"),
313
- ])
314
- })
315
-
316
- it("should restart server when .did file changes", async () => {
317
- const plugin = createVitePlugin(mockOptions)
318
- const ctx = {
319
- file: "/absolute/path/to/src/declarations/test.did",
320
- server: mockServer,
321
- }
322
-
323
- // Mock path.resolve to match the test case
324
- const originalResolve = path.resolve
325
- vi.spyOn(path, "resolve").mockImplementation((...args) => {
326
- if (args.some((a) => a && a.includes("test.did"))) {
327
- return "/absolute/path/to/src/declarations/test.did"
328
- }
329
- return originalResolve(...args)
330
- })
331
-
332
- await (plugin.handleHotUpdate as any)(ctx)
333
-
334
- expect(mockServer.ws.send).toHaveBeenCalledWith({ type: "full-reload" })
335
- })
336
-
337
- it("should ignore other files", () => {
338
- const plugin = createVitePlugin(mockOptions)
339
- const ctx = {
340
- file: "/some/other/file.ts",
341
- server: mockServer,
342
- }
343
-
344
- ;(plugin.handleHotUpdate as any)(ctx)
345
-
346
- expect(mockServer.ws.send).not.toHaveBeenCalled()
347
- })
348
- })
349
- })