@nosto/nosto-cli 1.2.0 → 1.2.2

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.
@@ -0,0 +1,101 @@
1
+ import { describe, expect, it } from "vitest"
2
+
3
+ import { deploymentsList } from "#modules/deployments/list.ts"
4
+ import { setupMockConfig } from "#test/utils/mockConfig.ts"
5
+ import { setupMockConsole } from "#test/utils/mockConsole.ts"
6
+ import { createMockDeployment } from "#test/utils/mockDeployment.ts"
7
+ import { mockListDeployments, setupMockServer } from "#test/utils/mockServer.ts"
8
+
9
+ const server = setupMockServer()
10
+ const terminal = setupMockConsole()
11
+ setupMockConfig()
12
+
13
+ describe("deploymentsList", () => {
14
+ it("should display list of deployments", async () => {
15
+ const mockDeployments = [
16
+ createMockDeployment({
17
+ id: "1763737018",
18
+ created: 1732200000000,
19
+ latest: true,
20
+ userId: "user@nosto.com",
21
+ description: "Latest deployment"
22
+ }),
23
+ createMockDeployment({
24
+ id: "1763737609",
25
+ created: 1732199000000,
26
+ active: true,
27
+ userId: "user@nosto.com",
28
+ description: "Additional fixes etc.."
29
+ })
30
+ ]
31
+
32
+ mockListDeployments(server, { response: mockDeployments })
33
+
34
+ await deploymentsList()
35
+
36
+ expect(terminal.getSpy("info")).toHaveBeenCalledWith(expect.stringContaining("Found 2 deployment(s):"))
37
+ expect(terminal.getSpy("info")).toHaveBeenCalledWith(expect.stringContaining("ID: 1763737018"))
38
+ expect(terminal.getSpy("info")).toHaveBeenCalledWith(expect.stringContaining("ID: 1763737609"))
39
+ expect(terminal.getSpy("info")).toHaveBeenCalledWith(expect.stringContaining("Latest deployment"))
40
+ expect(terminal.getSpy("info")).toHaveBeenCalledWith(expect.stringContaining("Additional fixes etc.."))
41
+ })
42
+
43
+ it("should display message when no deployments found", async () => {
44
+ mockListDeployments(server, { response: [] })
45
+
46
+ await deploymentsList()
47
+
48
+ expect(terminal.getSpy("info")).toHaveBeenCalledWith(expect.stringContaining("No deployments found"))
49
+ })
50
+
51
+ it("should display deployments without optional fields", async () => {
52
+ const mockDeployments = [createMockDeployment({ id: "1763737018", latest: true })]
53
+
54
+ mockListDeployments(server, { response: mockDeployments })
55
+
56
+ await deploymentsList()
57
+
58
+ expect(terminal.getSpy("info")).toHaveBeenCalledWith(expect.stringContaining("Found 1 deployment(s):"))
59
+ expect(terminal.getSpy("info")).toHaveBeenCalledWith(expect.stringContaining("ID: 1763737018"))
60
+ })
61
+
62
+ it("should display active status correctly", async () => {
63
+ const mockDeployments = [createMockDeployment({ active: true })]
64
+
65
+ mockListDeployments(server, { response: mockDeployments })
66
+
67
+ await deploymentsList()
68
+
69
+ expect(terminal.getSpy("info")).toHaveBeenCalledWith(expect.stringContaining("Active"))
70
+ })
71
+
72
+ it("should display inactive status correctly", async () => {
73
+ const mockDeployments = [createMockDeployment()]
74
+
75
+ mockListDeployments(server, { response: mockDeployments })
76
+
77
+ await deploymentsList()
78
+
79
+ expect(terminal.getSpy("info")).toHaveBeenCalledWith(expect.stringContaining("Inactive"))
80
+ })
81
+
82
+ it("should display latest badge for latest deployment", async () => {
83
+ const mockDeployments = [createMockDeployment({ latest: true })]
84
+
85
+ mockListDeployments(server, { response: mockDeployments })
86
+
87
+ await deploymentsList()
88
+
89
+ expect(terminal.getSpy("info")).toHaveBeenCalledWith(expect.stringContaining("[LATEST]"))
90
+ })
91
+
92
+ it("should format dates correctly", async () => {
93
+ const mockDeployments = [createMockDeployment({ latest: true })]
94
+
95
+ mockListDeployments(server, { response: mockDeployments })
96
+
97
+ await deploymentsList()
98
+
99
+ expect(terminal.getSpy("info")).toHaveBeenCalledWith(expect.stringMatching(/\d{4}-\d{2}-\d{2} \d{2}:\d{2}/))
100
+ })
101
+ })
@@ -0,0 +1,218 @@
1
+ import { describe, expect, it } from "vitest"
2
+
3
+ import { deploymentsRedeploy, selectDeploymentInteractively } from "#modules/deployments/redeploy.ts"
4
+ import { setupMockConfig } from "#test/utils/mockConfig.ts"
5
+ import { setupMockConsole } from "#test/utils/mockConsole.ts"
6
+ import { createMockDeployment } from "#test/utils/mockDeployment.ts"
7
+ import { mockListDeployments, mockUpdateDeployment, setupMockServer } from "#test/utils/mockServer.ts"
8
+
9
+ const server = setupMockServer()
10
+ const terminal = setupMockConsole()
11
+ setupMockConfig()
12
+
13
+ describe("deploymentsRedeploy", () => {
14
+ it("should redeploy deployment by ID when provided", async () => {
15
+ const mockDeployments = [
16
+ createMockDeployment({
17
+ id: "1763737018",
18
+ latest: true,
19
+ description: "Test deployment"
20
+ })
21
+ ]
22
+ mockListDeployments(server, { response: mockDeployments })
23
+ const mock = mockUpdateDeployment(server, { deploymentId: "1763737018" })
24
+
25
+ await deploymentsRedeploy({ deploymentId: "1763737018", force: true })
26
+
27
+ expect(mock.invocations).toHaveLength(1)
28
+ expect(terminal.getSpy("success")).toHaveBeenCalledWith("Redeployed successfully!")
29
+ })
30
+
31
+ it("should show error when deployment ID not found", async () => {
32
+ const mockDeployments = [createMockDeployment({ id: "1763737018", latest: true })]
33
+ mockListDeployments(server, { response: mockDeployments })
34
+ const mock = mockUpdateDeployment(server, { deploymentId: "9999999999" })
35
+
36
+ await deploymentsRedeploy({ deploymentId: "9999999999", force: true })
37
+
38
+ expect(mock.invocations).toHaveLength(0)
39
+ expect(terminal.getSpy("error")).toHaveBeenCalledWith('Deployment with ID "9999999999" not found.')
40
+ })
41
+
42
+ it("should display deployment info", async () => {
43
+ const mockDeployments = [
44
+ createMockDeployment({
45
+ id: "1763737018",
46
+ latest: true,
47
+ description: "Test deployment"
48
+ })
49
+ ]
50
+ mockListDeployments(server, { response: mockDeployments })
51
+ mockUpdateDeployment(server, { deploymentId: "1763737018" })
52
+
53
+ await deploymentsRedeploy({ deploymentId: "1763737018", force: true })
54
+
55
+ expect(terminal.getSpy("info")).toHaveBeenCalledWith(expect.stringContaining("Selected deployment: "))
56
+ expect(terminal.getSpy("info")).toHaveBeenCalledWith("Description: Test deployment")
57
+ })
58
+
59
+ it("should prompt for confirmation when not forced", async () => {
60
+ const mockDeployments = [createMockDeployment({ id: "1763737018", latest: true })]
61
+ mockListDeployments(server, { response: mockDeployments })
62
+ terminal.setUserResponse("y")
63
+ const mock = mockUpdateDeployment(server, { deploymentId: "1763737018" })
64
+
65
+ await deploymentsRedeploy({ deploymentId: "1763737018", force: false })
66
+
67
+ expect(mock.invocations).toHaveLength(1)
68
+ terminal.expect.user.toHaveBeenPromptedWith("Are you sure you want to redeploy version 1763737018? (y/N):")
69
+ })
70
+
71
+ it("should cancel when user declines confirmation", async () => {
72
+ const mockDeployments = [createMockDeployment({ id: "1763737018", latest: true })]
73
+ mockListDeployments(server, { response: mockDeployments })
74
+ terminal.setUserResponse("n")
75
+ const mock = mockUpdateDeployment(server, { deploymentId: "1763737018" })
76
+
77
+ await deploymentsRedeploy({ deploymentId: "1763737018", force: false })
78
+
79
+ expect(mock.invocations).toHaveLength(0)
80
+ expect(terminal.getSpy("info")).toHaveBeenCalledWith("Redeployment cancelled by user.")
81
+ })
82
+
83
+ it("should handle deployment without description field", async () => {
84
+ const mockDeployments = [createMockDeployment({ id: "1763737018", latest: true })]
85
+ mockListDeployments(server, { response: mockDeployments })
86
+ mockUpdateDeployment(server, { deploymentId: "1763737018" })
87
+
88
+ await deploymentsRedeploy({ deploymentId: "1763737018", force: true })
89
+
90
+ expect(terminal.getSpy("info")).toHaveBeenCalledWith(expect.stringContaining("Selected deployment: "))
91
+ expect(terminal.getSpy("info")).not.toHaveBeenCalledWith(expect.stringContaining("Description:"))
92
+ })
93
+
94
+ it("should display redeployment progress message", async () => {
95
+ const mockDeployments = [
96
+ {
97
+ id: "1763737018",
98
+ created: 1732200000000,
99
+ active: false,
100
+ latest: true
101
+ }
102
+ ]
103
+ mockListDeployments(server, { response: mockDeployments })
104
+ mockUpdateDeployment(server, { deploymentId: "1763737018" })
105
+
106
+ await deploymentsRedeploy({ deploymentId: "1763737018", force: true })
107
+
108
+ expect(terminal.getSpy("info")).toHaveBeenCalledWith(expect.stringContaining("Selected deployment"))
109
+ expect(terminal.getSpy("success")).toHaveBeenCalledWith("Redeployed successfully!")
110
+ })
111
+
112
+ it("should error when no deployment selected interactively", async () => {
113
+ mockListDeployments(server, { response: [] })
114
+
115
+ await deploymentsRedeploy({ force: true })
116
+
117
+ expect(terminal.getSpy("error")).toHaveBeenCalledWith("No deployment selected. Aborting.")
118
+ })
119
+ })
120
+
121
+ describe("selectDeploymentInteractively", () => {
122
+ it("should return null when no deployments found", async () => {
123
+ mockListDeployments(server, { response: [] })
124
+
125
+ const result = await selectDeploymentInteractively("Select deployment:")
126
+
127
+ expect(result).toBeNull()
128
+ expect(terminal.getSpy("error")).toHaveBeenCalledWith("No deployments found.")
129
+ })
130
+
131
+ it("should display active deployment indicator and format choices correctly", async () => {
132
+ const mockDeployments = [
133
+ createMockDeployment({
134
+ id: "1763737018",
135
+ created: 1732200000000,
136
+ active: true,
137
+ description: "Active deployment"
138
+ }),
139
+ createMockDeployment({
140
+ id: "1763737019",
141
+ created: 1732199000000,
142
+ latest: true,
143
+ description: "Latest deployment"
144
+ }),
145
+ createMockDeployment({
146
+ id: "1763737020",
147
+ created: 1732198000000
148
+ })
149
+ ]
150
+ mockListDeployments(server, { response: mockDeployments })
151
+ terminal.setSelectResponse("1763737018")
152
+
153
+ const result = await selectDeploymentInteractively("Select deployment:")
154
+
155
+ expect(terminal.getSpy("info")).toHaveBeenCalledWith(expect.stringContaining("Currently active deployment"))
156
+ expect(result).toEqual({
157
+ id: "1763737018",
158
+ deployment: mockDeployments[0]
159
+ })
160
+ expect(terminal.getSelectSpy()).toHaveBeenCalledWith({
161
+ message: "Select deployment:",
162
+ choices: expect.arrayContaining([
163
+ expect.objectContaining({
164
+ value: "1763737018",
165
+ description: "Active deployment"
166
+ }),
167
+ expect.objectContaining({
168
+ value: "1763737019",
169
+ description: "Latest deployment"
170
+ }),
171
+ expect.objectContaining({
172
+ value: "1763737020",
173
+ description: "No description"
174
+ })
175
+ ]),
176
+ pageSize: 10
177
+ })
178
+ })
179
+
180
+ it("should return null when no selection is made", async () => {
181
+ const mockDeployments = [createMockDeployment({ id: "1763737018", latest: true })]
182
+ mockListDeployments(server, { response: mockDeployments })
183
+ terminal.setSelectResponse(undefined)
184
+
185
+ const result = await selectDeploymentInteractively("Select deployment:")
186
+
187
+ expect(result).toBeNull()
188
+ })
189
+
190
+ it("should return null when selected deployment not found", async () => {
191
+ const mockDeployments = [createMockDeployment({ id: "1763737018", latest: true })]
192
+ mockListDeployments(server, { response: mockDeployments })
193
+ terminal.setSelectResponse("nonexistent")
194
+
195
+ const result = await selectDeploymentInteractively("Select deployment:")
196
+
197
+ expect(result).toBeNull()
198
+ })
199
+
200
+ it("should use interactive selection when no deploymentId provided", async () => {
201
+ const mockDeployments = [
202
+ createMockDeployment({
203
+ id: "1763737018",
204
+ latest: true,
205
+ description: "Test deployment"
206
+ })
207
+ ]
208
+ mockListDeployments(server, { response: mockDeployments })
209
+ terminal.setSelectResponse("1763737018")
210
+ const mock = mockUpdateDeployment(server, { deploymentId: "1763737018" })
211
+
212
+ await deploymentsRedeploy({ force: true })
213
+
214
+ expect(terminal.getSelectSpy()).toHaveBeenCalled()
215
+ expect(mock.invocations).toHaveLength(1)
216
+ expect(terminal.getSpy("success")).toHaveBeenCalledWith("Redeployed successfully!")
217
+ })
218
+ })
@@ -0,0 +1,60 @@
1
+ import { describe, expect, it } from "vitest"
2
+
3
+ import { deploymentsRollback } from "#modules/deployments/rollback.ts"
4
+ import { setupMockConfig } from "#test/utils/mockConfig.ts"
5
+ import { setupMockConsole } from "#test/utils/mockConsole.ts"
6
+ import { mockRollbackDeployment, setupMockServer } from "#test/utils/mockServer.ts"
7
+
8
+ const server = setupMockServer()
9
+ const terminal = setupMockConsole()
10
+ setupMockConfig()
11
+
12
+ describe("deploymentsRollback", () => {
13
+ it("should disable deployment when user confirms", async () => {
14
+ terminal.setUserResponse("y")
15
+ const mock = mockRollbackDeployment(server, {})
16
+
17
+ await deploymentsRollback({ force: false })
18
+
19
+ expect(mock.invocations).toHaveLength(1)
20
+ expect(terminal.getSpy("success")).toHaveBeenCalledWith("Active deployment disabled successfully!")
21
+ })
22
+
23
+ it("should cancel when user declines", async () => {
24
+ terminal.setUserResponse("n")
25
+ const mock = mockRollbackDeployment(server, {})
26
+
27
+ await deploymentsRollback({ force: false })
28
+
29
+ expect(mock.invocations).toHaveLength(0)
30
+ expect(terminal.getSpy("info")).toHaveBeenCalledWith("Operation cancelled by user.")
31
+ })
32
+
33
+ it("should skip confirmation when force is true", async () => {
34
+ const mock = mockRollbackDeployment(server, {})
35
+
36
+ await deploymentsRollback({ force: true })
37
+
38
+ expect(mock.invocations).toHaveLength(1)
39
+ expect(terminal.getSpy("success")).toHaveBeenCalledWith("Active deployment disabled successfully!")
40
+ })
41
+
42
+ it("should display confirmation message", async () => {
43
+ terminal.setUserResponse("y")
44
+ mockRollbackDeployment(server, {})
45
+
46
+ await deploymentsRollback({ force: false })
47
+
48
+ terminal.expect.user.toHaveBeenPromptedWith(
49
+ "Are you sure you want to disable the currently active deployment? (y/N):"
50
+ )
51
+ })
52
+
53
+ it("should display progress message", async () => {
54
+ mockRollbackDeployment(server, {})
55
+
56
+ await deploymentsRollback({ force: true })
57
+
58
+ expect(terminal.getSpy("info")).toHaveBeenCalledWith("Disabling active deployment...")
59
+ })
60
+ })
@@ -4,8 +4,15 @@ import { beforeEach, describe, expect, it, vi } from "vitest"
4
4
  import { buildSearchTemplate } from "#modules/search-templates/build.ts"
5
5
  import { setupMockConfig } from "#test/utils/mockConfig.ts"
6
6
  import { setupMockConsole } from "#test/utils/mockConsole.ts"
7
- import { mockFetchLibraryFile, setupMockServer } from "#test/utils/mockServer.ts"
8
-
7
+ import { setupMockFileSystem } from "#test/utils/mockFileSystem.ts"
8
+ import {
9
+ mockFetchLibraryFile,
10
+ mockFetchSourceFile,
11
+ mockPutSourceFile,
12
+ setupMockServer
13
+ } from "#test/utils/mockServer.ts"
14
+
15
+ const fs = setupMockFileSystem()
9
16
  const server = setupMockServer()
10
17
  const terminal = setupMockConsole()
11
18
 
@@ -95,5 +102,81 @@ describe("Search Templates build / legacy", () => {
95
102
  processOnSpy.mockRestore()
96
103
  processExitSpy.mockRestore()
97
104
  })
105
+
106
+ it("should push templates after build when push is true", async () => {
107
+ // Create source file with Nosto import
108
+ fs.writeFile("/test-project/index.ts", "import { search } from '@nosto/preact'")
109
+
110
+ // Mock rebuild to create build output
111
+ mockContext.rebuild.mockImplementation(async () => {
112
+ fs.writeFile("/test-project/build/index.js", "console.log('built')")
113
+ return { errors: [], warnings: [] }
114
+ })
115
+
116
+ // Mock the API endpoints
117
+ mockFetchSourceFile(server, {
118
+ path: "build/hash",
119
+ error: { status: 404, message: "Not Found" }
120
+ })
121
+
122
+ const indexMock = mockPutSourceFile(server, { path: "build/index.js" })
123
+ const hashMock = mockPutSourceFile(server, { path: "build/hash" })
124
+
125
+ terminal.setUserResponse("y")
126
+
127
+ await buildSearchTemplate({ watch: false, push: true })
128
+
129
+ expect(mockContext.rebuild).toHaveBeenCalled()
130
+ expect(mockContext.dispose).toHaveBeenCalled()
131
+ expect(indexMock.invocations).toHaveLength(1)
132
+ expect(hashMock.invocations).toHaveLength(1)
133
+ })
134
+
135
+ it("should not push templates when push is false", async () => {
136
+ // Create source file with Nosto import
137
+ fs.writeFile("/test-project/index.ts", "import { search } from '@nosto/preact'")
138
+
139
+ // Create build output
140
+ fs.writeFile("/test-project/build/index.js", "console.log('built')")
141
+
142
+ const indexMock = mockPutSourceFile(server, { path: "build/index.js" })
143
+
144
+ await buildSearchTemplate({ watch: false, push: false })
145
+
146
+ expect(mockContext.rebuild).toHaveBeenCalled()
147
+ expect(mockContext.dispose).toHaveBeenCalled()
148
+ expect(indexMock.invocations).toHaveLength(0)
149
+ })
150
+
151
+ it("should not push templates when push is undefined", async () => {
152
+ // Create source file with Nosto import
153
+ fs.writeFile("/test-project/index.ts", "import { search } from '@nosto/preact'")
154
+
155
+ // Create build output
156
+ fs.writeFile("/test-project/build/index.js", "console.log('built')")
157
+
158
+ const indexMock = mockPutSourceFile(server, { path: "build/index.js" })
159
+
160
+ await buildSearchTemplate({ watch: false })
161
+
162
+ expect(mockContext.rebuild).toHaveBeenCalled()
163
+ expect(mockContext.dispose).toHaveBeenCalled()
164
+ expect(indexMock.invocations).toHaveLength(0)
165
+ })
166
+
167
+ it("should not push templates in watch mode even if push is true", async () => {
168
+ // Create source file with Nosto import
169
+ fs.writeFile("/test-project/index.ts", "import { search } from '@nosto/preact'")
170
+
171
+ // Create build output
172
+ fs.writeFile("/test-project/build/index.js", "console.log('built')")
173
+
174
+ const indexMock = mockPutSourceFile(server, { path: "build/index.js" })
175
+
176
+ await buildSearchTemplate({ watch: true, push: true })
177
+
178
+ expect(mockContext.watch).toHaveBeenCalled()
179
+ expect(indexMock.invocations).toHaveLength(0)
180
+ })
98
181
  })
99
182
  })
@@ -3,8 +3,15 @@ import { beforeEach, describe, expect, it, vi } from "vitest"
3
3
  import { parseSearchTemplatesConfigFile } from "#config/searchTemplatesConfig.ts"
4
4
  import { buildSearchTemplate } from "#modules/search-templates/build.ts"
5
5
  import { setupMockConfig } from "#test/utils/mockConfig.ts"
6
+ import { setupMockConsole } from "#test/utils/mockConsole.ts"
7
+ import { setupMockFileSystem } from "#test/utils/mockFileSystem.ts"
8
+ import { mockFetchSourceFile, mockPutSourceFile, setupMockServer } from "#test/utils/mockServer.ts"
6
9
  import { setupMockStarterManifest } from "#test/utils/mockStarterManifest.ts"
7
10
 
11
+ const fs = setupMockFileSystem()
12
+ const server = setupMockServer()
13
+ const terminal = setupMockConsole()
14
+
8
15
  describe("Search Templates build / modern", () => {
9
16
  beforeEach(() => {
10
17
  setupMockStarterManifest()
@@ -34,4 +41,103 @@ describe("Search Templates build / modern", () => {
34
41
  await buildSearchTemplate({ watch: true })
35
42
  expect(manifest.onBuildWatch).toHaveBeenCalled()
36
43
  })
44
+
45
+ it("should push templates after build when push is true", async () => {
46
+ const manifest = setupMockStarterManifest({
47
+ mockScript: {
48
+ onBuild: vi.fn().mockImplementation(async () => {
49
+ // Simulate the build creating output files
50
+ fs.writeFile("build/index.js", "console.log('built')")
51
+ })
52
+ }
53
+ })
54
+ setupMockConfig({
55
+ searchTemplates: await parseSearchTemplatesConfigFile({ projectPath: "." })
56
+ })
57
+
58
+ // Create a source file that contains Nosto import (required by push logic)
59
+ fs.writeFile("index.ts", "import { search } from '@nosto/preact'")
60
+
61
+ // Mock the API endpoints - the push operation will check for remote hash first
62
+ mockFetchSourceFile(server, {
63
+ path: "build/hash",
64
+ error: { status: 404, message: "Not Found" }
65
+ })
66
+
67
+ const indexMock = mockPutSourceFile(server, { path: "build/index.js" })
68
+ const hashMock = mockPutSourceFile(server, { path: "build/hash" })
69
+
70
+ // Set force: true to skip the confirmation prompt
71
+ terminal.setUserResponse("y")
72
+
73
+ await buildSearchTemplate({ watch: false, push: true })
74
+
75
+ expect(manifest.onBuild).toHaveBeenCalled()
76
+ expect(indexMock.invocations).toHaveLength(1)
77
+ expect(hashMock.invocations).toHaveLength(1)
78
+ })
79
+
80
+ it("should not push templates when push is false", async () => {
81
+ const manifest = setupMockStarterManifest({
82
+ mockScript: { onBuild: vi.fn() }
83
+ })
84
+ setupMockConfig({
85
+ searchTemplates: await parseSearchTemplatesConfigFile({ projectPath: "." })
86
+ })
87
+
88
+ // Create build output files that would be pushed if push was true
89
+ fs.writeFile("build/index.js", "console.log('built')")
90
+
91
+ // Mock the API endpoints to track if they're called
92
+ const indexMock = mockPutSourceFile(server, { path: "build/index.js" })
93
+
94
+ await buildSearchTemplate({ watch: false, push: false })
95
+
96
+ expect(manifest.onBuild).toHaveBeenCalled()
97
+ expect(indexMock.invocations).toHaveLength(0)
98
+ })
99
+
100
+ it("should not push templates when push is undefined", async () => {
101
+ const manifest = setupMockStarterManifest({
102
+ mockScript: { onBuild: vi.fn() }
103
+ })
104
+ setupMockConfig({
105
+ searchTemplates: await parseSearchTemplatesConfigFile({ projectPath: "." })
106
+ })
107
+
108
+ // Create build output files that would be pushed if push was true
109
+ fs.writeFile("build/index.js", "console.log('built')")
110
+
111
+ // Mock the API endpoints to track if they're called
112
+ const indexMock = mockPutSourceFile(server, { path: "build/index.js" })
113
+
114
+ await buildSearchTemplate({ watch: false })
115
+
116
+ expect(manifest.onBuild).toHaveBeenCalled()
117
+ expect(indexMock.invocations).toHaveLength(0)
118
+ })
119
+
120
+ it("should not push templates in watch mode even if push is true", async () => {
121
+ const manifest = setupMockStarterManifest({
122
+ mockScript: {
123
+ onBuildWatch: vi.fn().mockImplementation(async ({ onAfterBuild }) => {
124
+ await onAfterBuild()
125
+ })
126
+ }
127
+ })
128
+ setupMockConfig({
129
+ searchTemplates: await parseSearchTemplatesConfigFile({ projectPath: "." })
130
+ })
131
+
132
+ // Create build output files that would be pushed if not in watch mode
133
+ fs.writeFile("build/index.js", "console.log('built')")
134
+
135
+ // Mock the API endpoints to track if they're called
136
+ const indexMock = mockPutSourceFile(server, { path: "build/index.js" })
137
+
138
+ await buildSearchTemplate({ watch: true, push: true })
139
+
140
+ expect(manifest.onBuildWatch).toHaveBeenCalled()
141
+ expect(indexMock.invocations).toHaveLength(0)
142
+ })
37
143
  })
package/test/setup.ts CHANGED
@@ -3,7 +3,7 @@ import { beforeEach } from "vitest"
3
3
  import { vi } from "vitest"
4
4
 
5
5
  import { mockHttpServer } from "./utils/mockAuthServer.ts"
6
- import { mockedConsoleIn, mockedConsoleOut, mockedSpinner } from "./utils/mockConsole.ts"
6
+ import { mockedConsoleIn, mockedConsoleOut, mockedInquirer, mockedSpinner } from "./utils/mockConsole.ts"
7
7
 
8
8
  export const testVolume = Volume.fromJSON({}, "/")
9
9
 
@@ -24,6 +24,8 @@ vi.mock("readline/promises", () => {
24
24
  })
25
25
  vi.mock("#/console/logger.ts", () => mockedConsoleOut)
26
26
 
27
+ vi.mock("@inquirer/prompts", () => mockedInquirer)
28
+
27
29
  vi.mock("node:test", () => {
28
30
  throw new Error("You seem to have accidentally imported node:test instead of vitest.")
29
31
  })
@@ -0,0 +1,54 @@
1
+ import { describe, expect, it } from "vitest"
2
+
3
+ import { formatDate } from "#utils/formatDate.ts"
4
+
5
+ describe("formatDate", () => {
6
+ it("should format a Date object correctly", () => {
7
+ const date = new Date("2025-11-21T14:56:58Z")
8
+ const formatted = formatDate(date)
9
+
10
+ expect(formatted).toBe("2025-11-21 14:56")
11
+ })
12
+
13
+ it("should format a timestamp in milliseconds correctly", () => {
14
+ const timestamp = 1732200000000 // Nov 21, 2024 14:40:00 GMT
15
+ const formatted = formatDate(timestamp)
16
+
17
+ expect(formatted).toBe("2024-11-21 14:40")
18
+ })
19
+
20
+ it("should pad single-digit months and days with zeros", () => {
21
+ const date = new Date("2025-01-05T08:03:00Z")
22
+ const formatted = formatDate(date)
23
+
24
+ expect(formatted).toBe("2025-01-05 08:03")
25
+ })
26
+
27
+ it("should pad single-digit hours and minutes with zeros", () => {
28
+ const date = new Date("2025-11-21T08:03:00Z")
29
+ const formatted = formatDate(date)
30
+
31
+ expect(formatted).toBe("2025-11-21 08:03")
32
+ })
33
+
34
+ it("should handle midnight correctly", () => {
35
+ const date = new Date("2025-11-21T00:00:00Z")
36
+ const formatted = formatDate(date)
37
+
38
+ expect(formatted).toBe("2025-11-21 00:00")
39
+ })
40
+
41
+ it("should handle end of day correctly", () => {
42
+ const date = new Date("2025-11-21T23:59:00Z")
43
+ const formatted = formatDate(date)
44
+
45
+ expect(formatted).toBe("2025-11-21 23:59")
46
+ })
47
+
48
+ it("should handle year boundaries correctly", () => {
49
+ const date = new Date("2025-12-31T23:59:00Z")
50
+ const formatted = formatDate(date)
51
+
52
+ expect(formatted).toBe("2025-12-31 23:59")
53
+ })
54
+ })