Skip to content
Open
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
5 changes: 5 additions & 0 deletions plugins/node-test/jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
const base = require('../../jest.config.base')

module.exports = {
...base.config
}
2 changes: 1 addition & 1 deletion plugins/node-test/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"version": "2.0.1",
"main": "lib",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
"test": "cd ../../ ; npx jest --silent --projects plugins/node-test"
},
"keywords": [],
"author": "FT.com Platforms Team <platforms-team.customer-products@ft.com>",
Expand Down
6 changes: 3 additions & 3 deletions plugins/node-test/src/tasks/node-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import { z } from 'zod'
// between Node.js 20 and 22. In future (when we drop Node.js 20 support) we will be able to remove
// this and rely on the built-in patterns.
// See https://nodejs.org/api/test.html#running-tests-from-the-command-line
const defaultFilePatterns = [
export const defaultFilePatterns = [
'**/*.test.?(c|m)js',
'**/*-test.?(c|m)js',
'**/*_test.?(c|m)js',
Expand All @@ -25,7 +25,7 @@ const defaultFilePatterns = [
]

// We don't want to run tests against files under "node_modules"
const defaultIgnorePatterns = ['**/node_modules/**']
export const defaultIgnorePatterns = ['**/node_modules/**']

// TODO:IM:20250407 This function has been copied wholesale from
// plugins/jest/src/tasks/jest.ts. There isn't a clear shared library to put it
Expand Down Expand Up @@ -113,7 +113,7 @@ export default class NodeTest extends Task<{ task: typeof NodeTestSchema }> {
if (concurrency === true && process.env.CIRCLECI) {
concurrency = (await guessCircleCiThreads()) - 1
}
const files = await glob(filePatterns, { cwd, ignore })
const files = await glob(filePatterns, { absolute: true, cwd, ignore })

let success = true
const testStream = run(Object.assign({ concurrency, files, forceExit, watch }, customOptions))
Expand Down
8 changes: 8 additions & 0 deletions plugins/node-test/test/files/failing-js/example.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
const assert = require('node:assert/strict')
const { describe, it } = require('node:test')

describe('failing test suite', () => {
it('has tests that fail', () => {
assert.ok(false)
})
})
2 changes: 2 additions & 0 deletions plugins/node-test/test/files/failing-js/throw.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
// This file does not match the default file patterns
throw new Error('Should not be run as part of the test suite');
8 changes: 8 additions & 0 deletions plugins/node-test/test/files/file-pattern/example.foo.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
const assert = require('node:assert/strict')
const { describe, it } = require('node:test')

describe('passing test suite', () => {
it('has tests that pass', () => {
assert.ok(true)
})
})
2 changes: 2 additions & 0 deletions plugins/node-test/test/files/file-pattern/example.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
// This file does not match the configured file patterns
throw new Error('Should not be run as part of the test suite');
8 changes: 8 additions & 0 deletions plugins/node-test/test/files/passing-js/example.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
const assert = require('node:assert/strict')
const { describe, it } = require('node:test')

describe('passing test suite', () => {
it('has tests that pass', () => {
assert.ok(true)
})
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
const assert = require('node:assert/strict')
const { describe, it } = require('node:test')

describe("passing test suite (CJS)", () => {
it('has tests that pass', () => {
assert.ok(true)
})
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import assert from 'node:assert/strict'
import { describe, it } from 'node:test'

describe("passing test suite (MJS)", () => {
it('has tests that pass', () => {
assert.ok(true)
})
})
8 changes: 8 additions & 0 deletions plugins/node-test/test/files/passing-js/test/example.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
const assert = require('node:assert/strict')
const { describe, it } = require('node:test')

describe("passing test suite (test folder)", () => {
it('has tests that pass', () => {
assert.ok(true)
})
})
2 changes: 2 additions & 0 deletions plugins/node-test/test/files/passing-js/throw.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
// This file does not match the default file patterns
throw new Error('Should not be run as part of the test suite');
10 changes: 10 additions & 0 deletions plugins/node-test/test/files/timeout/example.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
const assert = require('node:assert/strict')
const { describe, it } = require('node:test')
const { setTimeout } = require('node:timers/promises')

describe('passing test suite', () => {
it('has tests that pass', async () => {
await setTimeout(1000);
assert.ok(true)
})
})
79 changes: 79 additions & 0 deletions plugins/node-test/test/tasks/node-test.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import { describe, it, expect } from '@jest/globals'
import { join, resolve } from 'node:path'
import NodeTest, { defaultFilePatterns, defaultIgnorePatterns } from '../../src/tasks/node-test'
import winston, { Logger } from 'winston'
import { ToolKitError } from '@dotcom-tool-kit/error'
import { PassThrough } from 'node:stream'

const fixturesPath = resolve(__dirname, '..', 'files')

const defaultOptions = {
concurrency: false,
files: defaultFilePatterns,
forceExit: false,
ignore: defaultIgnorePatterns,
watch: false
}

describe('NodeTest', () => {
let logger: Logger
let log: jest.Mock

beforeEach(() => {
log = jest.fn()
const logStream = new PassThrough()
logStream.on('data', (data) => log(data.toString()))

logger = winston.createLogger({
transports: [new winston.transports.Stream({ stream: logStream })]
})
})

it('does not error when the tests pass', async () => {
const task = new NodeTest(logger, 'NodeTest', {}, defaultOptions)
await expect(task.run({ command: 'test:local', cwd: join(fixturesPath, 'passing-js') })).resolves.toBe(
undefined
)
expect(log).toHaveBeenCalledWith(expect.stringMatching(/passing test suite/))
expect(log).toHaveBeenCalledWith(expect.stringMatching(/pass 4/))
expect(log).toHaveBeenCalledWith(expect.stringMatching(/fail 0/))
})

it('errors when the tests fail', async () => {
const task = new NodeTest(logger, 'NodeTest', {}, defaultOptions)
await expect(task.run({ command: 'test:local', cwd: join(fixturesPath, 'failing-js') })).rejects.toThrow(
ToolKitError
)
expect(log).toHaveBeenCalledWith(expect.stringMatching(/failing test suite/))
expect(log).toHaveBeenCalledWith(expect.stringMatching(/pass 0/))
expect(log).toHaveBeenCalledWith(expect.stringMatching(/fail 1/))
})

it('finds tests based on the given file patterns', async () => {
const task = new NodeTest(logger, 'NodeTest', {}, { ...defaultOptions, files: ['**/*.foo.js'] })
await expect(task.run({ command: 'test:local', cwd: join(fixturesPath, 'file-pattern') })).resolves.toBe(
undefined
)
expect(log).toHaveBeenCalledWith(expect.stringMatching(/passing test suite/))
expect(log).toHaveBeenCalledWith(expect.stringMatching(/pass 1/))
expect(log).toHaveBeenCalledWith(expect.stringMatching(/fail 0/))
})

it('ignores tests based on the given file patterns', async () => {
const task = new NodeTest(logger, 'NodeTest', {}, { ...defaultOptions, ignore: ['subfolder/*'] })
await expect(task.run({ command: 'test:local', cwd: join(fixturesPath, 'passing-js') })).resolves.toBe(
undefined
)
expect(log).toHaveBeenCalledWith(expect.stringMatching(/passing test suite/))
expect(log).toHaveBeenCalledWith(expect.stringMatching(/pass 2/))
expect(log).toHaveBeenCalledWith(expect.stringMatching(/fail 0/))
})

it('can accept custom options (timeout)', async () => {
const task = new NodeTest(logger, 'NodeTest', {}, {...defaultOptions, customOptions: { timeout: 100 }})
await expect(task.run({ command: 'test:local', cwd: join(fixturesPath, 'timeout') })).rejects.toThrow(
ToolKitError
)
expect(log).toHaveBeenCalledWith(expect.stringMatching(/✖/))
})
})
Loading