Skip to content

Commit 0926b75

Browse files
JKRTclaude
authored andcommitted
Update @actions/core to v3
This updates @actions/core from v2 to v3, which is an ESM-only package. Changes required for ESM compatibility: - Add jest.config.mjs with ts-jest ESM preset - Update test script to use --experimental-vm-modules - Update test files to use jest.unstable_mockModule() for ESM mocking - Import jest from @jest/globals instead of using globals - Use dynamic imports after setting up mocks - Fix __dirname usage in src/collect.ts using import.meta.url Fixes #66 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent b682178 commit 0926b75

File tree

12 files changed

+36749
-6374
lines changed

12 files changed

+36749
-6374
lines changed

__fixtures__/core.ts

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
/**
2+
* Mock implementations for @actions/core
3+
*/
4+
5+
import * as fs from 'fs'
6+
import type * as core from '@actions/core'
7+
import { jest } from '@jest/globals'
8+
9+
export const debug = jest
10+
.fn<typeof core.debug>()
11+
.mockImplementation(msg => console.log(`::debug::${msg}`))
12+
13+
export const info = jest
14+
.fn<typeof core.info>()
15+
.mockImplementation(msg => console.log(`::info::${msg}`))
16+
17+
export const warning = jest.fn<typeof core.warning>()
18+
19+
export const error = jest
20+
.fn<typeof core.error>()
21+
.mockImplementation(msg => console.log(`::error::${msg}`))
22+
23+
export const getInput = jest.fn<typeof core.getInput>()
24+
25+
export const setOutput = jest.fn<typeof core.setOutput>()
26+
27+
export const setFailed = jest.fn<typeof core.setFailed>()
28+
29+
// Summary mock that writes to GITHUB_STEP_SUMMARY file
30+
let summaryBuffer = ''
31+
32+
export const summary = {
33+
addRaw: jest.fn((text: string) => {
34+
summaryBuffer += text
35+
return summary
36+
}),
37+
write: jest.fn(async () => {
38+
const summaryFile = process.env.GITHUB_STEP_SUMMARY
39+
if (summaryFile) {
40+
fs.appendFileSync(summaryFile, summaryBuffer)
41+
}
42+
summaryBuffer = ''
43+
})
44+
}
45+
46+
/**
47+
* Reset the summary buffer. Call this in beforeEach.
48+
*/
49+
export function resetSummaryBuffer(): void {
50+
summaryBuffer = ''
51+
}

__tests__/collect.test.ts

Lines changed: 30 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,40 @@
44

55
import * as fs from 'fs'
66
import * as path from 'path'
7-
import * as artifact from '@actions/artifact'
8-
import { expect } from '@jest/globals'
9-
import { copyHtmlFilesSync, uploadArtifacts } from '../src/collect'
7+
import {
8+
expect,
9+
jest,
10+
describe,
11+
beforeEach,
12+
afterEach,
13+
it
14+
} from '@jest/globals'
1015

1116
const tempTestDir = path.join('__tests__', 'tmp-collect')
1217

1318
// Mock @actions/artifact
14-
jest.mock('@actions/artifact')
15-
jest.mock('@actions/github')
19+
const uploadArtifactMock = jest
20+
.fn<() => Promise<{ size: number; id: number }>>()
21+
.mockResolvedValue({ size: 0, id: 0 })
22+
23+
const DefaultArtifactClientMock = jest.fn().mockImplementation(() => ({
24+
uploadArtifact: uploadArtifactMock
25+
}))
26+
27+
jest.unstable_mockModule('@actions/artifact', () => ({
28+
DefaultArtifactClient: DefaultArtifactClientMock
29+
}))
30+
31+
jest.unstable_mockModule('@actions/github', () => ({
32+
context: {
33+
repo: { owner: 'test', repo: 'test' },
34+
runId: 123,
35+
job: 'test-job'
36+
}
37+
}))
38+
39+
// Dynamic imports after mocking
40+
const { copyHtmlFilesSync, uploadArtifacts } = await import('../src/collect')
1641

1742
function mockFileStructure(
1843
libraryName: string,
@@ -107,13 +132,6 @@ describe('collect.ts', () => {
107132
const targetDir = path.join(tempTestDir, 'html')
108133
mockFileStructure(libraryName, libraryVersion, modelPrefix, omLibTestingDir)
109134

110-
const DefaultArtifactClientMock = jest
111-
.spyOn(artifact, 'DefaultArtifactClient')
112-
.mockImplementation()
113-
const uploadArtifactMock = jest
114-
.spyOn(artifact.DefaultArtifactClient.prototype, 'uploadArtifact')
115-
.mockImplementation()
116-
117135
copyHtmlFilesSync(
118136
libraryName,
119137
libraryVersion,

__tests__/index.test.ts

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,19 @@
22
* Unit tests for the action's entrypoint, src/index.ts
33
*/
44

5-
import * as main from '../src/main'
5+
import { expect, jest, describe, it } from '@jest/globals'
66

7-
// Mock the action's entrypoint
8-
const runMock = jest.spyOn(main, 'run').mockImplementation()
7+
// Mock the main module before importing
8+
const runMock = jest.fn<() => Promise<void>>().mockResolvedValue(undefined)
9+
10+
jest.unstable_mockModule('../src/main', () => ({
11+
run: runMock
12+
}))
913

1014
describe('index', () => {
1115
it('calls run when imported', async () => {
12-
// eslint-disable-next-line @typescript-eslint/no-require-imports
13-
require('../src/index')
16+
// Dynamic import after mocking
17+
await import('../src/index')
1418

1519
expect(runMock).toHaveBeenCalled()
1620
})

__tests__/inputs.test.ts

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,24 +3,28 @@
33
*/
44

55
import * as path from 'path'
6-
import * as core from '@actions/core'
7-
import { expect } from '@jest/globals'
8-
import { ActionInputs, ActionInputsInterface } from '../src/inputs'
6+
import { expect, jest, describe, beforeEach, it } from '@jest/globals'
7+
8+
// Import mocks from fixtures
9+
import * as core from '../__fixtures__/core.js'
910

1011
const modelicaFile = path.resolve('examples/MyLibrary/package.mo')
1112
const referenceFilesDir = path.resolve('examples/ReferenceFiles')
1213

13-
// Mock the GitHub Actions core library
14-
let getInputMock: jest.SpyInstance
14+
// Mock @actions/core before importing modules that use it
15+
jest.unstable_mockModule('@actions/core', () => core)
16+
17+
// Dynamic import after mocking
18+
const { ActionInputs } = await import('../src/inputs')
19+
import type { ActionInputsInterface } from '../src/inputs'
1520

1621
describe('inputs.ts', () => {
1722
beforeEach(() => {
1823
jest.clearAllMocks()
19-
getInputMock = jest.spyOn(core, 'getInput').mockImplementation()
2024
})
2125

2226
it('Read inputs', async () => {
23-
getInputMock.mockImplementation((name: string): string => {
27+
core.getInput.mockImplementation((name: string): string => {
2428
switch (name) {
2529
case 'library':
2630
return 'MyLibrary'

__tests__/main.test.ts

Lines changed: 53 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -9,33 +9,45 @@
99
import * as fs from 'fs'
1010
import * as os from 'os'
1111
import * as path from 'path'
12-
import * as core from '@actions/core'
13-
import * as main from '../src/main'
14-
import { getMSYS } from '../src/get-msys'
12+
import { expect, jest, describe, beforeEach, afterAll, it } from '@jest/globals'
13+
14+
// Import mocks from fixtures
15+
import * as core from '../__fixtures__/core.js'
1516

1617
// Some expected string
1718
const mdCoverageTable = `| Total | Frontend | Backend | SimCode | Templates | Compilation | Simulation | Verification |
1819
| --- | --- | --- | --- | --- | --- | --- | --- |
1920
| 2 | 2 | 2 | 2 | 2 | 2 | 2 | 1 |`
2021

21-
// Mock the action's main function
22-
const runMock = jest.spyOn(main, 'run')
2322
const modelicaFile = path.resolve(
2423
path.join('examples', 'MyLibrary', 'package.mo')
2524
)
2625
const referenceFilesDir = path.resolve(path.join('examples', 'ReferenceFiles'))
2726

2827
// Mock the GitHub Actions core library
29-
let debugMock: jest.SpyInstance
30-
let errorMock: jest.SpyInstance
31-
let infoMock: jest.SpyInstance
32-
let getInputMock: jest.SpyInstance
33-
let setFailedMock: jest.SpyInstance
34-
let setOutputMock: jest.SpyInstance
28+
jest.unstable_mockModule('@actions/core', () => core)
3529

3630
// Mock @actions/artifact and @actions/github
37-
jest.mock('@actions/artifact')
38-
jest.mock('@actions/github')
31+
const uploadArtifactMock = jest
32+
.fn<() => Promise<{ size: number; id: number }>>()
33+
.mockResolvedValue({ size: 0, id: 0 })
34+
35+
jest.unstable_mockModule('@actions/artifact', () => ({
36+
DefaultArtifactClient: jest.fn().mockImplementation(() => ({
37+
uploadArtifact: uploadArtifactMock
38+
}))
39+
}))
40+
41+
jest.unstable_mockModule('@actions/github', () => ({
42+
context: {
43+
repo: { owner: 'test', repo: 'test' },
44+
runId: 123
45+
}
46+
}))
47+
48+
// Dynamic imports after mocking
49+
const main = await import('../src/main')
50+
const { getMSYS } = await import('../src/get-msys')
3951

4052
// Set GitHub summary file
4153
const gitHubStepSummaryFile = path.resolve(
@@ -56,26 +68,19 @@ describe('action', () => {
5668
fs.writeFileSync(gitHubStepSummaryFile, '', { flag: 'w' })
5769

5870
jest.clearAllMocks()
71+
// Reset summary buffer
72+
core.resetSummaryBuffer()
5973

60-
debugMock = jest
61-
.spyOn(core, 'debug')
62-
.mockImplementation(msg => console.log(`::debug::${msg}`))
63-
infoMock = jest
64-
.spyOn(core, 'info')
65-
.mockImplementation(msg => console.log(`::info::${msg}`))
66-
errorMock = jest
67-
.spyOn(core, 'error')
68-
.mockImplementation(msg => console.log(`::error::${msg}`))
69-
getInputMock = jest.spyOn(core, 'getInput').mockImplementation()
70-
setFailedMock = jest.spyOn(core, 'setFailed').mockImplementation()
71-
setOutputMock = jest.spyOn(core, 'setOutput').mockImplementation()
74+
core.debug.mockImplementation(msg => console.log(`::debug::${msg}`))
75+
core.info.mockImplementation(msg => console.log(`::info::${msg}`))
76+
core.error.mockImplementation(msg => console.log(`::error::${msg}`))
7277
})
7378

7479
it(
7580
'Run action',
7681
async () => {
7782
// Set the action's inputs as return values from core.getInput()
78-
getInputMock.mockImplementation((name: string): string => {
83+
core.getInput.mockImplementation((name: string): string => {
7984
switch (name) {
8085
case 'library':
8186
return 'MyLibrary'
@@ -101,68 +106,67 @@ describe('action', () => {
101106
})
102107

103108
await main.run()
104-
expect(runMock).toHaveReturned()
105109

106110
// Verify that all of the core library functions were called correctly
107-
expect(debugMock).toHaveBeenNthCalledWith(1, 'Get inputs')
108-
expect(debugMock).toHaveBeenNthCalledWith(
111+
expect(core.debug).toHaveBeenNthCalledWith(1, 'Get inputs')
112+
expect(core.debug).toHaveBeenNthCalledWith(
109113
2,
110114
'clone OpenModelicaLibraryTesting'
111115
)
112-
expect(debugMock).toHaveBeenNthCalledWith(4, 'Generating configuration')
113-
expect(debugMock).toHaveBeenNthCalledWith(
116+
expect(core.debug).toHaveBeenNthCalledWith(4, 'Generating configuration')
117+
expect(core.debug).toHaveBeenNthCalledWith(
114118
5,
115119
'Running python test.py --verbose --branch=master --noclean' +
116120
` ${os.platform() === 'win32' ? `--msysEnvironment=${getMSYS()}` : ''}` +
117121
` ${path.join('configs', 'conf-MyLibrary.json')}`
118122
)
119-
expect(debugMock).toHaveBeenNthCalledWith(
123+
expect(core.debug).toHaveBeenNthCalledWith(
120124
7,
121125
'Running python report.py --branch=master' +
122126
` ${path.join('configs', 'conf-MyLibrary.json')}`
123127
)
124-
expect(debugMock).toHaveBeenNthCalledWith(9, 'Write summary')
125-
expect(debugMock).toHaveBeenNthCalledWith(10, 'Set outputs')
126-
expect(debugMock).toHaveBeenNthCalledWith(11, 'Collect HTML outputs')
127-
expect(debugMock).toHaveBeenNthCalledWith(12, 'Upload artifacts')
128-
expect(debugMock).toHaveBeenCalledTimes(12)
128+
expect(core.debug).toHaveBeenNthCalledWith(9, 'Write summary')
129+
expect(core.debug).toHaveBeenNthCalledWith(10, 'Set outputs')
130+
expect(core.debug).toHaveBeenNthCalledWith(11, 'Collect HTML outputs')
131+
expect(core.debug).toHaveBeenNthCalledWith(12, 'Upload artifacts')
132+
expect(core.debug).toHaveBeenCalledTimes(12)
129133

130-
expect(setOutputMock).toHaveBeenNthCalledWith(
134+
expect(core.setOutput).toHaveBeenNthCalledWith(
131135
1,
132136
'simulation-tests-passing',
133137
true
134138
)
135-
expect(setOutputMock).toHaveBeenNthCalledWith(
139+
expect(core.setOutput).toHaveBeenNthCalledWith(
136140
2,
137141
'n-simulation-passing',
138142
2
139143
)
140-
expect(setOutputMock).toHaveBeenNthCalledWith(
144+
expect(core.setOutput).toHaveBeenNthCalledWith(
141145
3,
142146
'verification-tests-passing',
143147
false
144148
)
145-
expect(setOutputMock).toHaveBeenNthCalledWith(
149+
expect(core.setOutput).toHaveBeenNthCalledWith(
146150
4,
147151
'n-verification-passing',
148152
1
149153
)
150-
expect(setOutputMock).toHaveBeenCalledTimes(4)
154+
expect(core.setOutput).toHaveBeenCalledTimes(4)
151155

152-
expect(infoMock).toHaveBeenNthCalledWith(
156+
expect(core.info).toHaveBeenNthCalledWith(
153157
2,
154158
`simulation-tests-passing: true`
155159
)
156-
expect(infoMock).toHaveBeenNthCalledWith(3, `n-simulation-passing: 2`)
157-
expect(infoMock).toHaveBeenNthCalledWith(
160+
expect(core.info).toHaveBeenNthCalledWith(3, `n-simulation-passing: 2`)
161+
expect(core.info).toHaveBeenNthCalledWith(
158162
4,
159163
`verification-tests-passing: false`
160164
)
161-
expect(infoMock).toHaveBeenNthCalledWith(5, `n-verification-passing: 1`)
162-
expect(infoMock).toHaveBeenCalledTimes(5)
165+
expect(core.info).toHaveBeenNthCalledWith(5, `n-verification-passing: 1`)
166+
expect(core.info).toHaveBeenCalledTimes(5)
163167

164-
expect(errorMock).not.toHaveBeenCalled()
165-
expect(setFailedMock).not.toHaveBeenCalled()
168+
expect(core.error).not.toHaveBeenCalled()
169+
expect(core.setFailed).not.toHaveBeenCalled()
166170

167171
// Verify summary file
168172
const summaryContent = fs.readFileSync(gitHubStepSummaryFile, 'utf-8')

0 commit comments

Comments
 (0)