Skip to content

Commit 8808001

Browse files
Merge pull request #91 from chrisreddington/port-vitest
refactor: migrate from Jest to Vitest for testing framework
2 parents 4b1e4b9 + f34249a commit 8808001

9 files changed

Lines changed: 2731 additions & 5502 deletions

File tree

.github/linters/eslint.config.mjs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import jest from 'eslint-plugin-jest'
1+
import vitest from 'eslint-plugin-vitest'
22
import typescriptEslint from '@typescript-eslint/eslint-plugin'
33
import globals from 'globals'
44
import tsParser from '@typescript-eslint/parser'
@@ -27,24 +27,24 @@ export default [
2727
'dist/**',
2828
'coverage/**',
2929
'test/**',
30-
'**/.github/linters/**'
30+
'**/.github/linters/**',
31+
'vitest.config.mts'
3132
]
3233
},
3334
...compat.extends(
3435
'eslint:recommended',
35-
'plugin:@typescript-eslint/recommended-type-checked',
36-
'plugin:jest/recommended'
36+
'plugin:@typescript-eslint/recommended-type-checked'
3737
),
38+
vitest.configs.recommended,
3839
{
3940
plugins: {
40-
jest,
41+
vitest,
4142
'@typescript-eslint': typescriptEslint
4243
},
4344

4445
languageOptions: {
4546
globals: {
4647
...globals.node,
47-
...globals.jest,
4848
Atomics: 'readonly',
4949
SharedArrayBuffer: 'readonly'
5050
},

__tests__/index.test.ts

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,25 @@
1-
import { run } from '../src/main'
1+
/**
2+
* Unit tests for the action's entrypoint (src/index.ts)
3+
*/
4+
5+
import * as main from '../src/main'
6+
import { vi, describe, it, expect } from 'vitest'
27

3-
jest.mock('../src/main', () => ({
4-
run: jest.fn()
5-
}))
8+
/**
9+
* Mock implementation of the main run function
10+
*/
11+
const mockRun = vi.spyOn(main, 'run').mockImplementation()
612

713
/**
8-
* Test suite for the GitHub Action's entry point.
9-
* Validates that the action is properly initialized and the main run function is called.
14+
* Test suite for the action's entry point
1015
*/
1116
describe('index', () => {
17+
/**
18+
* Test case: Verifies that the run function is called when the index is imported
19+
*/
1220
it('calls run when imported', async () => {
1321
await import('../src/index')
14-
expect(run).toHaveBeenCalled()
22+
23+
expect(mockRun).toHaveBeenCalled()
1524
})
1625
})

__tests__/main.test.ts

Lines changed: 45 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,29 @@
11
import * as core from '@actions/core'
22
import * as fs from 'fs'
33
import { run } from '../src/main'
4+
import {
5+
vi,
6+
describe,
7+
test,
8+
expect,
9+
beforeEach,
10+
type MockInstance
11+
} from 'vitest'
412

5-
jest.mock('@actions/core')
6-
jest.mock('fs', () => ({
13+
vi.mock('@actions/core')
14+
vi.mock('fs', () => ({
715
promises: {
8-
access: jest.fn(),
9-
readFile: jest.fn()
16+
access: vi.fn(),
17+
readFile: vi.fn()
1018
},
1119
constants: {
1220
O_RDONLY: 0
1321
}
1422
}))
1523

24+
// Mock types
25+
let mockGetInput: MockInstance<typeof core.getInput>
26+
1627
/**
1728
* Test suite for the GitHub Action's main functionality.
1829
* Tests the complete validation workflow including:
@@ -23,7 +34,9 @@ jest.mock('fs', () => ({
2334
*/
2435
describe('run', () => {
2536
beforeEach(() => {
26-
jest.clearAllMocks()
37+
vi.clearAllMocks()
38+
mockGetInput = vi.spyOn(core, 'getInput')
39+
vi.spyOn(core, 'setFailed').mockImplementation(() => {})
2740
})
2841

2942
/**
@@ -45,7 +58,7 @@ describe('run', () => {
4558
}
4659

4760
// Mock inputs and file operations
48-
jest.spyOn(core, 'getInput').mockImplementation(name => {
61+
mockGetInput.mockImplementation(name => {
4962
switch (name) {
5063
case 'required-extensions':
5164
return 'required-ext'
@@ -55,8 +68,8 @@ describe('run', () => {
5568
return ''
5669
}
5770
})
58-
;(fs.promises.access as jest.Mock).mockResolvedValue(undefined)
59-
;(fs.promises.readFile as jest.Mock).mockResolvedValue(
71+
vi.spyOn(fs.promises, 'access').mockResolvedValue(undefined)
72+
vi.spyOn(fs.promises, 'readFile').mockResolvedValue(
6073
JSON.stringify(mockContent)
6174
)
6275

@@ -70,11 +83,11 @@ describe('run', () => {
7083
*/
7184
describe('file handling errors', () => {
7285
test('should throw error when devcontainer.json is not found', async () => {
73-
jest.spyOn(core, 'getInput').mockImplementation(name => {
86+
mockGetInput.mockImplementation(name => {
7487
if (name === 'required-extensions') return 'ext1'
7588
return ''
7689
})
77-
;(fs.promises.access as jest.Mock).mockRejectedValue(
90+
vi.spyOn(fs.promises, 'access').mockRejectedValue(
7891
new Error('File not found')
7992
)
8093

@@ -85,9 +98,9 @@ describe('run', () => {
8598
})
8699

87100
test('should throw error when JSON is invalid', async () => {
88-
jest.spyOn(core, 'getInput').mockReturnValue('ext1')
89-
;(fs.promises.access as jest.Mock).mockResolvedValue(undefined)
90-
;(fs.promises.readFile as jest.Mock).mockResolvedValue('invalid json')
101+
mockGetInput.mockReturnValue('ext1')
102+
vi.spyOn(fs.promises, 'access').mockResolvedValue(undefined)
103+
vi.spyOn(fs.promises, 'readFile').mockResolvedValue('invalid json')
91104

92105
await run()
93106
expect(core.setFailed).toHaveBeenCalledWith(
@@ -109,9 +122,9 @@ describe('run', () => {
109122
}
110123
}
111124

112-
jest.spyOn(core, 'getInput').mockReturnValue('ext1')
113-
;(fs.promises.access as jest.Mock).mockResolvedValue(undefined)
114-
;(fs.promises.readFile as jest.Mock).mockResolvedValue(
125+
mockGetInput.mockReturnValue('ext1')
126+
vi.spyOn(fs.promises, 'access').mockResolvedValue(undefined)
127+
vi.spyOn(fs.promises, 'readFile').mockResolvedValue(
115128
JSON.stringify(invalidContent)
116129
)
117130

@@ -130,12 +143,12 @@ describe('run', () => {
130143
}
131144
}
132145

133-
jest.spyOn(core, 'getInput').mockImplementation(name => {
146+
mockGetInput.mockImplementation(name => {
134147
if (name === 'required-extensions') return 'ext1,ext2'
135148
return ''
136149
})
137-
;(fs.promises.access as jest.Mock).mockResolvedValue(undefined)
138-
;(fs.promises.readFile as jest.Mock).mockResolvedValue(
150+
vi.spyOn(fs.promises, 'access').mockResolvedValue(undefined)
151+
vi.spyOn(fs.promises, 'readFile').mockResolvedValue(
139152
JSON.stringify(content)
140153
)
141154

@@ -162,13 +175,13 @@ describe('run', () => {
162175
}
163176
}
164177

165-
jest.spyOn(core, 'getInput').mockImplementation(name => {
178+
mockGetInput.mockImplementation(name => {
166179
if (name === 'required-extensions') return 'ext1'
167180
if (name === 'required-features') return 'feature1,feature2'
168181
return ''
169182
})
170-
;(fs.promises.access as jest.Mock).mockResolvedValue(undefined)
171-
;(fs.promises.readFile as jest.Mock).mockResolvedValue(
183+
vi.spyOn(fs.promises, 'access').mockResolvedValue(undefined)
184+
vi.spyOn(fs.promises, 'readFile').mockResolvedValue(
172185
JSON.stringify(content)
173186
)
174187

@@ -187,13 +200,13 @@ describe('run', () => {
187200
}
188201
}
189202

190-
jest.spyOn(core, 'getInput').mockImplementation(name => {
203+
mockGetInput.mockImplementation(name => {
191204
if (name === 'required-extensions') return 'ext1'
192205
if (name === 'required-features') return ''
193206
return ''
194207
})
195-
;(fs.promises.access as jest.Mock).mockResolvedValue(undefined)
196-
;(fs.promises.readFile as jest.Mock).mockResolvedValue(
208+
vi.spyOn(fs.promises, 'access').mockResolvedValue(undefined)
209+
vi.spyOn(fs.promises, 'readFile').mockResolvedValue(
197210
JSON.stringify(content)
198211
)
199212

@@ -219,13 +232,13 @@ describe('run', () => {
219232
}
220233
}
221234

222-
jest.spyOn(core, 'getInput').mockImplementation(name => {
235+
mockGetInput.mockImplementation(name => {
223236
if (name === 'required-extensions') return 'ext1'
224237
if (name === 'validate-tasks') return 'true'
225238
return ''
226239
})
227-
;(fs.promises.access as jest.Mock).mockResolvedValue(undefined)
228-
;(fs.promises.readFile as jest.Mock).mockResolvedValue(
240+
vi.spyOn(fs.promises, 'access').mockResolvedValue(undefined)
241+
vi.spyOn(fs.promises, 'readFile').mockResolvedValue(
229242
JSON.stringify(content)
230243
)
231244

@@ -250,7 +263,7 @@ describe('run', () => {
250263
}
251264
} as Error
252265

253-
jest.spyOn(core, 'getInput').mockImplementation(() => {
266+
mockGetInput.mockImplementation(() => {
254267
throw errorLike
255268
})
256269

@@ -259,9 +272,9 @@ describe('run', () => {
259272
})
260273

261274
test('should handle string errors correctly', async () => {
262-
jest.spyOn(core, 'getInput').mockReturnValue('ext1')
263-
;(fs.promises.access as jest.Mock).mockResolvedValue(undefined)
264-
;(fs.promises.readFile as jest.Mock).mockRejectedValue(
275+
mockGetInput.mockReturnValue('ext1')
276+
vi.spyOn(fs.promises, 'access').mockResolvedValue(undefined)
277+
vi.spyOn(fs.promises, 'readFile').mockRejectedValue(
265278
'String error message'
266279
)
267280

__tests__/utils.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { describe, test, expect } from 'vitest'
12
import { isDevcontainerContent, stripJsonComments } from '../src/utils'
23

34
/**

__tests__/validators.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { describe, test, expect } from 'vitest'
12
import {
23
validateExtensions,
34
validateTasks,

0 commit comments

Comments
 (0)