Skip to content

Commit

Permalink
test(start): covers most scenarios
Browse files Browse the repository at this point in the history
  • Loading branch information
ArnaudBuchholz committed Jan 20, 2025
1 parent 3b08258 commit e746d54
Show file tree
Hide file tree
Showing 2 changed files with 115 additions and 18 deletions.
22 changes: 13 additions & 9 deletions src/start.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,18 @@ module.exports = async function start (job) {

// check if existing NPM script
const packagePath = join(job.cwd, 'package.json')
const packageStat = await stat(packagePath)
if (packageStat.isFile()) {
output.debug('start', 'Found package.json in cwd')
const packageFile = JSON.parse(await readFile(packagePath, 'utf-8'))
if (packageFile.scripts[command]) {
output.debug('start', 'Found matching start script in package.json')
start = `npm run ${start}`
try {
const packageStat = await stat(packagePath)
if (packageStat.isFile()) {
output.debug('start', 'Found package.json in cwd')
const packageFile = JSON.parse(await readFile(packagePath, 'utf-8'))
if (packageFile.scripts[command]) {
output.debug('start', 'Found matching start script in package.json')
start = `npm run ${start}`
}
}
} catch (e) {
output.debug('start', 'Missing or invalid package.json in cwd', e)
}

let childProcessExited = false
Expand All @@ -33,7 +37,7 @@ module.exports = async function start (job) {
cwd: job.cwd,
windowsHide: true
})
childProcess.on('exit', () => {
childProcess.on('close', () => {
output.debug('start', 'start command process exited')
childProcessExited = true
})
Expand All @@ -46,7 +50,7 @@ module.exports = async function start (job) {

const begin = Date.now()
// eslint-disable-next-line no-unmodified-loop-condition
while (!childProcessExited && Date.now() - begin < job.startTimeout) {
while (!childProcessExited && Date.now() - begin <= job.startTimeout) {
try {
const response = await fetch(url)
output.debug('start', url, response.status)
Expand Down
111 changes: 102 additions & 9 deletions src/start.spec.js
Original file line number Diff line number Diff line change
@@ -1,26 +1,119 @@
const { mock: mockChildProcess } = require('child_process')
const { start } = require('./start')
const { join } = require('path')
const start = require('./start')

jest.mock('ps-tree', () => (_, cb) => cb(null, [{
PID: 1
}, {
PID: 2
}]))
jest.spyOn(process, 'kill')

describe('src/start', () => {
let job
let returnUrlAnswerAfter // ms
const VALID_URL = 'http://localhost/valid'
const INVALID_URL = 'http://localhost/invalid'
let startExecuted = false

beforeEach(() => {
job = {}
process.kill.mockClear()
returnUrlAnswerAfter = 500 // ms
job = {
cwd: __dirname,
startTimeout: 5000,
start: 'start',
url: [VALID_URL]
}
mockChildProcess({
api: 'exec',
scriptPath: 'start',
exec: () => { startExecuted = true },
close: false
})
let firstFetch
global.fetch = async (url) => {
if (firstFetch === undefined) {
firstFetch = Date.now()
throw new Error('E_CONNECT failed')
} else if (Date.now() - firstFetch < returnUrlAnswerAfter) {
throw new Error('E_CONNECT failed')
}
if (url === VALID_URL) {
return { status: 200 }
}
if (url === INVALID_URL) {
return { status: 404 }
}
}
})

it('detects equivalent script cwd\'s package.json', () => {})
it('runs command', async () => {
await start(job)
expect(startExecuted).toStrictEqual(true)
})

it('runs command despite cwd\'s package.json', () => {})
it('detects equivalent script cwd\'s package.json', async () => {
job.cwd = join(__dirname, '..')
job.start = 'test'
let executed = false
mockChildProcess({
api: 'exec',
scriptPath: 'npm',
args: ['run', 'test'],
exec: () => { executed = true },
close: false
})
await start(job)
expect(executed).toStrictEqual(true)
})

it('runs command', () => {})
it('runs command despite cwd\'s package.json', async () => {
job.cwd = join(__dirname, '..')
job.start = 'test2'
let executed = false
mockChildProcess({
api: 'exec',
scriptPath: 'test2',
exec: () => { executed = true },
close: false
})
await start(job)
expect(executed).toStrictEqual(true)
})

describe('waiting for URL to be available', () => {
it('detects command termination and fails', () => {})
it('detects command termination and fails', async () => {
job.start = 'close'
mockChildProcess({
api: 'exec',
scriptPath: 'close',
exec: () => {},
close: true
})
returnUrlAnswerAfter = 5000
job.startTimeout = 5000
await expect(start(job)).rejects.toThrowError(/Start command failed with exit code/)
})

it('times out after expected limit and fails', () => {})
it('times out after expected limit and fails', async () => {
returnUrlAnswerAfter = 5000
job.startTimeout = 500
await expect(start(job)).rejects.toThrowError(/Timeout while waiting for/)
})

it('succeeds when URL is available', () => {})
it('times out after expected limit and fails (wrong URL)', async () => {
job.startTimeout = 1000
job.url = [INVALID_URL]
await expect(start(job)).rejects.toThrowError(/Timeout while waiting for/)
})
})

it('stops the command by killing all children processes', () => {})
it('stops the command by killing all children processes', async () => {
const started = await start(job)
await started.stop()
expect(process.kill).toHaveBeenCalledTimes(2)
expect(process.kill).toHaveBeenCalledWith(1, 'SIGKILL')
expect(process.kill).toHaveBeenCalledWith(2, 'SIGKILL')
})
})

0 comments on commit e746d54

Please sign in to comment.