Skip to content

Commit c9177bb

Browse files
Copilotpelikhan
andauthored
Convert test suite from Jest to Vitest (#3)
* Initial plan * Initial exploration - understanding current test setup Co-authored-by: pelikhan <[email protected]> * Convert tests from Jest to Vitest Co-authored-by: pelikhan <[email protected]> * Update documentation to reflect inline mock usage Co-authored-by: pelikhan <[email protected]> * Fix formatting in vitest.config.ts Co-authored-by: pelikhan <[email protected]> --------- Co-authored-by: copilot-swe-agent[bot] <[email protected]> Co-authored-by: pelikhan <[email protected]> Co-authored-by: Peli de Halleux <[email protected]>
1 parent 6cbf90f commit c9177bb

File tree

11 files changed

+4101
-9342
lines changed

11 files changed

+4101
-9342
lines changed

.github/prompts/unit-test.prompt.md

Lines changed: 21 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,9 @@ Ensure you adhere to the following guidelines when creating unit tests:
1313
- Use descriptive test names that clearly convey their purpose
1414
- Ensure tests cover both the main path of success and edge cases
1515
- Use proper assertions to validate the expected outcomes
16-
- Use `jest` for writing and running tests
16+
- Use `vitest` for writing and running tests
1717
- Place unit tests in the `__tests__` directory
18-
- Use fixtures for any necessary test data, placed in the `__fixtures__`
19-
directory
18+
- Use inline mocks with `vi.mock()` for mocking dependencies
2019

2120
## Example
2221

@@ -26,13 +25,19 @@ Use the following as an example of how to structure your unit tests:
2625
/**
2726
* Unit tests for the action's main functionality, src/main.ts
2827
*/
29-
import { jest } from '@jest/globals'
30-
import * as core from '../__fixtures__/core.js'
31-
import { wait } from '../__fixtures__/wait.js'
28+
import { vi, describe, beforeEach, afterEach, it, expect } from 'vitest'
3229

33-
// Mocks should be declared before the module being tested is imported.
34-
jest.unstable_mockModule('@actions/core', () => core)
35-
jest.unstable_mockModule('../src/wait.js', () => ({ wait }))
30+
// Mock modules
31+
const mockCore = {
32+
getInput: vi.fn(),
33+
setOutput: vi.fn(),
34+
setFailed: vi.fn()
35+
}
36+
37+
const mockWait = vi.fn()
38+
39+
vi.mock('@actions/core', () => mockCore)
40+
vi.mock('../src/wait.js', () => ({ wait: mockWait }))
3641

3742
// The module being tested should be imported dynamically. This ensures that the
3843
// mocks are used in place of any actual dependencies.
@@ -41,21 +46,21 @@ const { run } = await import('../src/main.js')
4146
describe('main.ts', () => {
4247
beforeEach(() => {
4348
// Set the action's inputs as return values from core.getInput().
44-
core.getInput.mockImplementation(() => '500')
49+
mockCore.getInput.mockImplementation(() => '500')
4550

4651
// Mock the wait function so that it does not actually wait.
47-
wait.mockImplementation(() => Promise.resolve('done!'))
52+
mockWait.mockImplementation(() => Promise.resolve('done!'))
4853
})
4954

5055
afterEach(() => {
51-
jest.resetAllMocks()
56+
vi.resetAllMocks()
5257
})
5358

5459
it('Sets the time output', async () => {
5560
await run()
5661

5762
// Verify the time output was set.
58-
expect(core.setOutput).toHaveBeenNthCalledWith(
63+
expect(mockCore.setOutput).toHaveBeenNthCalledWith(
5964
1,
6065
'time',
6166
// Simple regex to match a time string in the format HH:MM:SS.
@@ -65,17 +70,17 @@ describe('main.ts', () => {
6570

6671
it('Sets a failed status', async () => {
6772
// Clear the getInput mock and return an invalid value.
68-
core.getInput.mockClear().mockReturnValueOnce('this is not a number')
73+
mockCore.getInput.mockClear().mockReturnValueOnce('this is not a number')
6974

7075
// Clear the wait mock and return a rejected promise.
71-
wait
76+
mockWait
7277
.mockClear()
7378
.mockRejectedValueOnce(new Error('milliseconds is not a number'))
7479

7580
await run()
7681

7782
// Verify that the action was marked as failed.
78-
expect(core.setFailed).toHaveBeenNthCalledWith(
83+
expect(mockCore.setFailed).toHaveBeenNthCalledWith(
7984
1,
8085
'milliseconds is not a number'
8186
)

AGENTS.md

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ it is generated from.
1313

1414
| Path | Description |
1515
| -------------------- | -------------------------------------------------------- |
16-
| `__fixtures__/` | Unit Test Fixtures |
1716
| `__tests__/` | Unit Tests |
1817
| `.devcontainer/` | Development Container Configuration |
1918
| `.github/` | GitHub Configuration |
@@ -31,7 +30,7 @@ it is generated from.
3130
| `action.yml` | GitHub Action Metadata |
3231
| `CODEOWNERS` | Code Owners File |
3332
| `eslint.config.mjs` | ESLint Configuration |
34-
| `jest.config.js` | Jest Configuration |
33+
| `vitest.config.ts` | Vitest Configuration |
3534
| `LICENSE` | License File |
3635
| `package.json` | NPM Package Configuration |
3736
| `README.md` | Project Documentation |
@@ -55,7 +54,7 @@ npm run test
5554
```
5655

5756
Unit tests should exist in the `__tests__` directory. They are powered by
58-
`jest`. Fixtures should be placed in the `__fixtures__` directory.
57+
`vitest`.
5958

6059
## Bundling
6160

__fixtures__/core.ts

Lines changed: 0 additions & 10 deletions
This file was deleted.

__fixtures__/wait.ts

Lines changed: 0 additions & 3 deletions
This file was deleted.

__tests__/main.test.ts

Lines changed: 71 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,62 +1,97 @@
11
/**
22
* Unit tests for the action's main functionality, src/main.ts
3-
*
4-
* To mock dependencies in ESM, you can create fixtures that export mock
5-
* functions and objects. For example, the core module is mocked in this test,
6-
* so that the actual '@actions/core' module is not imported.
73
*/
8-
import { jest } from '@jest/globals'
9-
import * as core from '../__fixtures__/core.js'
10-
import { wait } from '../__fixtures__/wait.js'
4+
import { vi, describe, beforeEach, afterEach, it, expect } from 'vitest'
115

12-
// Mocks should be declared before the module being tested is imported.
13-
jest.unstable_mockModule('@actions/core', () => core)
14-
jest.unstable_mockModule('../src/wait.js', () => ({ wait }))
6+
// Mock modules
7+
const mockCore = {
8+
getInput: vi.fn(),
9+
setOutput: vi.fn(),
10+
setFailed: vi.fn(),
11+
debug: vi.fn(),
12+
error: vi.fn(),
13+
info: vi.fn(),
14+
warning: vi.fn()
15+
}
1516

16-
// The module being tested should be imported dynamically. This ensures that the
17-
// mocks are used in place of any actual dependencies.
17+
const mockStartProxy = vi.fn()
18+
19+
vi.mock('@actions/core', () => mockCore)
20+
vi.mock('../src/server.js', () => ({
21+
startProxy: mockStartProxy
22+
}))
23+
24+
// Import the module being tested
1825
const { run } = await import('../src/main.js')
1926

2027
describe('main.ts', () => {
2128
beforeEach(() => {
22-
// Set the action's inputs as return values from core.getInput().
23-
core.getInput.mockImplementation(() => '500')
24-
25-
// Mock the wait function so that it does not actually wait.
26-
wait.mockImplementation(() => Promise.resolve('done!'))
29+
vi.clearAllMocks()
2730
})
2831

2932
afterEach(() => {
30-
jest.resetAllMocks()
33+
vi.resetAllMocks()
3134
})
3235

33-
it('Sets the time output', async () => {
36+
it('Sets the outputs on success', async () => {
37+
// Setup mocks
38+
mockCore.getInput.mockReturnValue('http://example.com')
39+
mockStartProxy.mockResolvedValue({
40+
url: 'http://localhost:3000',
41+
port: 3000,
42+
apiKey: 'test-api-key',
43+
containerId: 'test-container-id'
44+
})
45+
3446
await run()
3547

36-
// Verify the time output was set.
37-
expect(core.setOutput).toHaveBeenNthCalledWith(
38-
1,
39-
'time',
40-
// Simple regex to match a time string in the format HH:MM:SS.
41-
expect.stringMatching(/^\d{2}:\d{2}:\d{2}/)
48+
// Verify outputs were set
49+
expect(mockCore.setOutput).toHaveBeenCalledWith(
50+
'url',
51+
'http://localhost:3000'
52+
)
53+
expect(mockCore.setOutput).toHaveBeenCalledWith('port', 3000)
54+
expect(mockCore.setOutput).toHaveBeenCalledWith('api-key', 'test-api-key')
55+
expect(mockCore.setOutput).toHaveBeenCalledWith(
56+
'container-id',
57+
'test-container-id'
4258
)
59+
expect(mockCore.setFailed).not.toHaveBeenCalled()
4360
})
4461

45-
it('Sets a failed status', async () => {
46-
// Clear the getInput mock and return an invalid value.
47-
core.getInput.mockClear().mockReturnValueOnce('this is not a number')
62+
it('Sets a failed status on error', async () => {
63+
// Setup mocks
64+
mockCore.getInput.mockReturnValue('http://example.com')
65+
mockStartProxy.mockRejectedValue(new Error('Connection failed'))
4866

49-
// Clear the wait mock and return a rejected promise.
50-
wait
51-
.mockClear()
52-
.mockRejectedValueOnce(new Error('milliseconds is not a number'))
67+
await run()
68+
69+
// Verify that the action was marked as failed
70+
expect(mockCore.setFailed).toHaveBeenCalledWith('Connection failed')
71+
})
72+
73+
it('Does not set container-id output when not present', async () => {
74+
// Setup mocks
75+
mockCore.getInput.mockReturnValue('http://example.com')
76+
mockStartProxy.mockResolvedValue({
77+
url: 'http://localhost:3000',
78+
port: 3000,
79+
apiKey: 'test-api-key'
80+
// No containerId
81+
})
5382

5483
await run()
5584

56-
// Verify that the action was marked as failed.
57-
expect(core.setFailed).toHaveBeenNthCalledWith(
58-
1,
59-
'milliseconds is not a number'
85+
// Verify container-id was not set
86+
expect(mockCore.setOutput).toHaveBeenCalledWith(
87+
'url',
88+
'http://localhost:3000'
89+
)
90+
expect(mockCore.setOutput).toHaveBeenCalledWith('port', 3000)
91+
expect(mockCore.setOutput).toHaveBeenCalledWith('api-key', 'test-api-key')
92+
expect(mockCore.setOutput).not.toHaveBeenCalledWith(
93+
'container-id',
94+
expect.anything()
6095
)
6196
})
6297
})

__tests__/wait.test.ts

Lines changed: 0 additions & 24 deletions
This file was deleted.

eslint.config.mjs

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import js from '@eslint/js'
66
import typescriptEslint from '@typescript-eslint/eslint-plugin'
77
import tsParser from '@typescript-eslint/parser'
88
import _import from 'eslint-plugin-import'
9-
import jest from 'eslint-plugin-jest'
109
import prettier from 'eslint-plugin-prettier'
1110
import globals from 'globals'
1211

@@ -24,21 +23,18 @@ export default [
2423
'eslint:recommended',
2524
'plugin:@typescript-eslint/eslint-recommended',
2625
'plugin:@typescript-eslint/recommended',
27-
'plugin:jest/recommended',
2826
'plugin:prettier/recommended'
2927
),
3028
{
3129
plugins: {
3230
import: fixupPluginRules(_import),
33-
jest,
3431
prettier,
3532
'@typescript-eslint': typescriptEslint
3633
},
3734

3835
languageOptions: {
3936
globals: {
4037
...globals.node,
41-
...globals.jest,
4238
Atomics: 'readonly',
4339
SharedArrayBuffer: 'readonly'
4440
},
@@ -53,7 +49,7 @@ export default [
5349
'__fixtures__/*.ts',
5450
'__tests__/*.ts',
5551
'eslint.config.mjs',
56-
'jest.config.js',
52+
'vitest.config.ts',
5753
'rollup.config.ts'
5854
]
5955
},

jest.config.js

Lines changed: 0 additions & 40 deletions
This file was deleted.

0 commit comments

Comments
 (0)