|
1 | 1 | import { bootstrapApp, globalFilters } from "@/utils/setupUtils.js"; |
2 | | -import { beforeEach, describe, expect, it, vi } from "vitest"; |
3 | | -import store from "@/store"; |
4 | | -import router from "@/router"; |
5 | | - |
6 | | -// Mock the Store |
7 | | -vi.mock("@/store", () => ({ |
8 | | - default: { |
9 | | - dispatch: vi.fn(), |
10 | | - commit: vi.fn(), |
11 | | - }, |
12 | | -})); |
13 | | - |
14 | | -// Mock the Router |
15 | | -vi.mock("@/router", () => ({ |
16 | | - default: { |
17 | | - replace: vi.fn(), |
18 | | - }, |
19 | | -})); |
20 | | - |
21 | | -describe("Bootstrap & Filters Logic", () => { |
22 | | - // Clear mock history before every test to ensure clean assertions |
23 | | - beforeEach(() => { |
24 | | - vi.clearAllMocks(); |
25 | | - }); |
| 2 | +import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; |
| 3 | + |
| 4 | +describe("setupUtils.js", () => { |
26 | 5 | describe("bootstrapApp()", () => { |
27 | | - it("dispatches the 4 required initialization actions on success", async () => { |
28 | | - // Setup: Mock dispatch to resolve successfully |
29 | | - store.dispatch.mockResolvedValue(true); |
| 6 | + let mockStore; |
| 7 | + let mockRouter; |
| 8 | + |
| 9 | + beforeEach(() => { |
| 10 | + // Create fresh mock objects for dependency injection before each test |
| 11 | + mockStore = { |
| 12 | + dispatch: vi.fn().mockResolvedValue(true), |
| 13 | + commit: vi.fn(), |
| 14 | + }; |
| 15 | + |
| 16 | + mockRouter = { |
| 17 | + replace: vi.fn(), |
| 18 | + }; |
| 19 | + |
| 20 | + // Suppress console.error in tests to keep the terminal output clean |
| 21 | + vi.spyOn(console, "error").mockImplementation(() => {}); |
| 22 | + }); |
30 | 23 |
|
31 | | - // Execute |
32 | | - await bootstrapApp(); |
| 24 | + afterEach(() => { |
| 25 | + vi.restoreAllMocks(); |
| 26 | + }); |
| 27 | + |
| 28 | + it("dispatches the 4 required initialization actions on success", async () => { |
| 29 | + // Pass the mock instances directly as arguments |
| 30 | + await bootstrapApp(mockStore, mockRouter); |
33 | 31 |
|
34 | | - // Assert: Check all 4 calls |
35 | | - expect(store.dispatch).toHaveBeenCalledTimes(4); |
36 | | - expect(store.dispatch).toHaveBeenCalledWith("users/login"); |
37 | | - expect(store.dispatch).toHaveBeenCalledWith( |
| 32 | + expect(mockStore.dispatch).toHaveBeenCalledTimes(4); |
| 33 | + expect(mockStore.dispatch).toHaveBeenNthCalledWith(1, "users/login"); |
| 34 | + expect(mockStore.dispatch).toHaveBeenNthCalledWith( |
| 35 | + 2, |
38 | 36 | "introspection/fetchParameters", |
39 | 37 | ); |
40 | | - expect(store.dispatch).toHaveBeenCalledWith( |
| 38 | + expect(mockStore.dispatch).toHaveBeenNthCalledWith( |
| 39 | + 3, |
41 | 40 | "searchFilters/assembleFilters", |
42 | 41 | ); |
43 | | - expect(store.dispatch).toHaveBeenCalledWith("messages/setMessages"); |
| 42 | + expect(mockStore.dispatch).toHaveBeenNthCalledWith( |
| 43 | + 4, |
| 44 | + "messages/setMessages", |
| 45 | + ); |
44 | 46 | }); |
45 | 47 |
|
46 | 48 | it("commits maintenance mode if API returns 503 Service Unavailable", async () => { |
47 | | - // Setup: specific 503 error structure |
48 | 49 | const error503 = { response: { status: 503 } }; |
49 | | - store.dispatch.mockRejectedValueOnce(error503); |
| 50 | + mockStore.dispatch.mockRejectedValueOnce(error503); |
50 | 51 |
|
51 | | - // Execute |
52 | | - await bootstrapApp(); |
| 52 | + await bootstrapApp(mockStore, mockRouter); |
53 | 53 |
|
54 | | - // Assert |
55 | | - expect(store.commit).toHaveBeenCalledWith( |
| 54 | + expect(mockStore.commit).toHaveBeenCalledWith( |
56 | 55 | "introspection/setMaintenanceMode", |
57 | 56 | ); |
58 | | - // Should NOT redirect to 500 |
59 | | - expect(router.replace).not.toHaveBeenCalled(); |
| 57 | + expect(mockRouter.replace).not.toHaveBeenCalled(); |
60 | 58 | }); |
61 | 59 |
|
62 | 60 | it("redirects to /error/500 for generic errors", async () => { |
63 | | - // Setup: Generic error |
64 | 61 | const errorGeneric = new Error("Random API Failure"); |
65 | | - store.dispatch.mockRejectedValueOnce(errorGeneric); |
| 62 | + mockStore.dispatch.mockRejectedValueOnce(errorGeneric); |
66 | 63 |
|
67 | | - // Execute |
68 | | - await bootstrapApp(); |
| 64 | + await bootstrapApp(mockStore, mockRouter); |
69 | 65 |
|
70 | | - //Assert; |
71 | | - expect(store.commit).not.toHaveBeenCalledWith( |
72 | | - "introspection/setMaintenanceMode", |
| 66 | + expect(mockStore.commit).not.toHaveBeenCalled(); |
| 67 | + expect(mockRouter.replace).toHaveBeenCalledWith("/error/500"); |
| 68 | + }); |
| 69 | + |
| 70 | + it("logs a console error if bootstrap fails and router is unavailable", async () => { |
| 71 | + const errorGeneric = new Error("No Router Exists"); |
| 72 | + mockStore.dispatch.mockRejectedValueOnce(errorGeneric); |
| 73 | + |
| 74 | + // Pass null for the router to trigger the final catch block |
| 75 | + await bootstrapApp(mockStore, null); |
| 76 | + |
| 77 | + expect(mockStore.commit).not.toHaveBeenCalled(); |
| 78 | + expect(console.error).toHaveBeenCalledWith( |
| 79 | + "Bootstrap failed and router instance was unavailable:", |
| 80 | + errorGeneric, |
73 | 81 | ); |
74 | | - expect(router.replace).toHaveBeenCalledWith("/error/500"); |
75 | 82 | }); |
76 | 83 | }); |
77 | 84 |
|
78 | 85 | describe("Global Filters", () => { |
79 | | - describe("capitalize", () => { |
| 86 | + describe("capitalize()", () => { |
80 | 87 | it("capitalizes the first letter of a string", () => { |
81 | 88 | expect(globalFilters.capitalize("hello")).toBe("Hello"); |
82 | 89 | }); |
83 | 90 |
|
84 | | - it("returns empty string if input is null/undefined", () => { |
| 91 | + it("returns an empty string if input is falsy", () => { |
85 | 92 | expect(globalFilters.capitalize(null)).toBe(""); |
| 93 | + expect(globalFilters.capitalize("")).toBe(""); |
86 | 94 | }); |
87 | 95 | }); |
88 | 96 |
|
89 | | - describe("cleanString", () => { |
90 | | - it("replaces underscores with spaces", () => { |
91 | | - expect(globalFilters.cleanString("hello_world")).toContain( |
92 | | - "Hello world", |
93 | | - ); |
| 97 | + describe("cleanString()", () => { |
| 98 | + it("replaces underscores with spaces and capitalizes the first letter", () => { |
| 99 | + expect(globalFilters.cleanString("hello_world")).toBe("Hello world"); |
94 | 100 | }); |
95 | 101 |
|
96 | | - it("capitalizes the first letter", () => { |
97 | | - expect(globalFilters.cleanString("test_string")).toMatch(/^Test/); |
| 102 | + it("adds spaces before uppercase letters (camelCase splitting)", () => { |
| 103 | + // The regex replaces ([A-Z]) with " $1" and capitalizes the first char |
| 104 | + expect(globalFilters.cleanString("camelCase")).toBe("Camel Case"); |
98 | 105 | }); |
99 | 106 |
|
100 | | - // Note: Based on your current regex logic `replace(/([A-Z])/g, "$1")`, |
101 | | - // "camelCase" remains "camelCase". If you intend to split it, update logic in main.backup.js. |
102 | | - it("handles camelCase based on current logic", () => { |
103 | | - const result = globalFilters.cleanString("camelCase"); |
104 | | - // Logic check: "camelCase" -> "CamelCase" (capitalizes first char, keeps rest) |
105 | | - expect(result).toBe("CamelCase"); |
| 107 | + it("returns an empty string if input is falsy", () => { |
| 108 | + expect(globalFilters.cleanString(null)).toBe(""); |
| 109 | + expect(globalFilters.cleanString("")).toBe(""); |
106 | 110 | }); |
107 | 111 | }); |
108 | 112 | }); |
|
0 commit comments