Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .github/linters/.eslintrc.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
env:
commonjs: true
es6: true
es2022: true
jest: true
node: true

Expand Down Expand Up @@ -41,7 +40,8 @@ rules:
'eslint-comments/no-use': 'off',
'eslint-comments/no-unused-disable': 'off',
'i18n-text/no-en': 'off',
'import/no-commonjs': 'off',
'import/extensions': ['error', 'ignorePackages'],
'import/no-commonjs': 'error',
'import/no-namespace': 'off',
'no-console': 'off',
'no-unused-vars': 'off',
Expand Down
16 changes: 10 additions & 6 deletions __tests__/index.test.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,22 @@
/**
* Unit tests for the action's entrypoint, src/index.js
*/
import { jest } from '@jest/globals'

const { run } = require('../src/main')
const mockRun = jest.fn()

// Mock the action's entrypoint
jest.mock('../src/main', () => ({
run: jest.fn()
await jest.unstable_mockModule('../src/main.js', () => ({
run: mockRun
}))

await jest.unstable_mockModule('@actions/core', () => ({
setFailed: jest.fn()
}))

describe('index', () => {
it('calls run when imported', async () => {
require('../src/index')
await import('../src/index.js')

expect(run).toHaveBeenCalled()
expect(mockRun).toHaveBeenCalled()
})
})
177 changes: 110 additions & 67 deletions __tests__/main.test.js
Original file line number Diff line number Diff line change
@@ -1,96 +1,139 @@
/**
* Unit tests for the action's main functionality, src/main.js
*/
const core = require('@actions/core')
const main = require('../src/main')
import { jest } from '@jest/globals'

// Mock the GitHub Actions core library
const debugMock = jest.spyOn(core, 'debug').mockImplementation()
const getInputMock = jest.spyOn(core, 'getInput').mockImplementation()
const setFailedMock = jest.spyOn(core, 'setFailed').mockImplementation()
const setOutputMock = jest.spyOn(core, 'setOutput').mockImplementation()
const mockGetInput = jest.fn()
const mockGetBooleanInput = jest.fn()
const mockSetFailed = jest.fn()
const mockInfo = jest.fn()
const mockExec = jest.fn()
const mockReaddirSync = jest.fn()
const mockExistsSync = jest.fn()

// Mock the action's main function
const runMock = jest.spyOn(main, 'run')
await jest.unstable_mockModule('@actions/core', () => ({
getInput: mockGetInput,
getBooleanInput: mockGetBooleanInput,
setFailed: mockSetFailed,
info: mockInfo
}))

// Other utilities
const timeRegex = /^\d{2}:\d{2}:\d{2}/
await jest.unstable_mockModule('@actions/exec', () => ({
exec: mockExec
}))

describe('action', () => {
await jest.unstable_mockModule('fs', () => ({
readdirSync: mockReaddirSync,
existsSync: mockExistsSync
}))

const { run } = await import('../src/main.js')

describe('run', () => {
beforeEach(() => {
jest.clearAllMocks()
mockGetBooleanInput.mockReturnValue(false)
mockGetInput.mockReturnValue('')
mockExec.mockResolvedValue(0)
})

it('sets the time output', async () => {
// Set the action's inputs as return values from core.getInput()
getInputMock.mockImplementation(name => {
switch (name) {
case 'milliseconds':
return '500'
default:
return ''
}
})
it('uses the provided path', async () => {
mockGetInput.mockImplementation(name =>
name === 'path' ? 'my.sdPlugin' : ''
)
mockExistsSync.mockReturnValue(true)

await main.run()
expect(runMock).toHaveReturned()
await run()

// Verify that all of the core library functions were called correctly
expect(debugMock).toHaveBeenNthCalledWith(1, 'Waiting 500 milliseconds ...')
expect(debugMock).toHaveBeenNthCalledWith(
2,
expect.stringMatching(timeRegex)
expect(mockExec).toHaveBeenCalledWith(
'npx',
expect.arrayContaining(['pack', 'my.sdPlugin'])
)
expect(debugMock).toHaveBeenNthCalledWith(
3,
expect.stringMatching(timeRegex)
})

it('auto-detects the sdPlugin directory', async () => {
mockGetInput.mockReturnValue('')
mockReaddirSync.mockReturnValue([
{ isDirectory: () => true, name: 'my.sdPlugin' }
])
mockExistsSync.mockReturnValue(true)

await run()

expect(mockExec).toHaveBeenCalledWith(
'npx',
expect.arrayContaining(['pack', 'my.sdPlugin'])
)
expect(setOutputMock).toHaveBeenNthCalledWith(
1,
'time',
expect.stringMatching(timeRegex)
})

it('fails when no sdPlugin directory is found', async () => {
mockGetInput.mockReturnValue('')
mockReaddirSync.mockReturnValue([])

await run()

expect(mockSetFailed).toHaveBeenCalledWith(
'path not specified and no .sdPlugin directory found in the current working directory.'
)
})

it('sets a failed status', async () => {
// Set the action's inputs as return values from core.getInput()
getInputMock.mockImplementation(name => {
switch (name) {
case 'milliseconds':
return 'this is not a number'
default:
return ''
}
})
it('fails when the provided path does not exist', async () => {
mockGetInput.mockImplementation(name =>
name === 'path' ? 'missing.sdPlugin' : ''
)
mockExistsSync.mockReturnValue(false)

await main.run()
expect(runMock).toHaveReturned()
await run()

// Verify that all of the core library functions were called correctly
expect(setFailedMock).toHaveBeenNthCalledWith(
1,
'milliseconds not a number'
expect(mockSetFailed).toHaveBeenCalledWith(
"Path 'missing.sdPlugin' does not exist."
)
})

it('fails if no input is provided', async () => {
// Set the action's inputs as return values from core.getInput()
getInputMock.mockImplementation(name => {
switch (name) {
case 'milliseconds':
throw new Error('Input required and not supplied: milliseconds')
default:
return ''
}
it('fails when both forceUpdateCheck and noUpdateCheck are true', async () => {
mockGetInput.mockImplementation(name =>
name === 'path' ? 'my.sdPlugin' : ''
)
mockGetBooleanInput.mockImplementation(
name => name === 'forceUpdateCheck' || name === 'noUpdateCheck'
)
mockExistsSync.mockReturnValue(true)

await run()

expect(mockSetFailed).toHaveBeenCalledWith(
'forceUpdateCheck and noUpdateCheck cannot be set to true at the same time'
)
})

it('appends --output when outputPath is provided', async () => {
mockGetInput.mockImplementation(name => {
if (name === 'path') return 'my.sdPlugin'
if (name === 'outputPath') return 'output/'
return ''
})
mockExistsSync.mockReturnValue(true)

await run()

expect(mockExec).toHaveBeenCalledWith(
'npx',
expect.arrayContaining(['--output', 'output/'])
)
})

it('appends --force when force is true', async () => {
mockGetInput.mockImplementation(name =>
name === 'path' ? 'my.sdPlugin' : ''
)
mockGetBooleanInput.mockImplementation(name => name === 'force')
mockExistsSync.mockReturnValue(true)

await main.run()
expect(runMock).toHaveReturned()
await run()

// Verify that all of the core library functions were called correctly
expect(setFailedMock).toHaveBeenNthCalledWith(
1,
'Input required and not supplied: milliseconds'
expect(mockExec).toHaveBeenCalledWith(
'npx',
expect.arrayContaining(['--force'])
)
})
})
Loading