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
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ jobs:
node-version: ${{ matrix.node }}

- name: Test
run: pnpm exec vitest --coverage
run: pnpm test:all --coverage

- name: Submit coverage
uses: coverallsapp/github-action@master
Expand Down
10 changes: 3 additions & 7 deletions bin/read-package-json.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,13 @@
import { readFileSync } from 'node:fs';
import { createRequire } from 'node:module';

const require = createRequire(import.meta.url);

/**
* Read the package.json file of `concurrently`
*/
export function readPackageJson(): Record<string, unknown> {
let resolver;
try {
resolver = require.resolve;
} catch {
resolver = createRequire(import.meta.url).resolve;
}
const path = resolver('concurrently/package.json');
const path = require.resolve('concurrently/package.json');
const content = readFileSync(path, 'utf8');
return JSON.parse(content);
}
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@
"lint": "eslint",
"prepublishOnly": "safe-publish-latest && pnpm run build",
"test": "vitest --project unit",
"test:all": "vitest run",
"test:e2e": "vitest run --project e2e",
"test:smoke": "vitest run --project smoke",
"prepare": "husky"
},
Expand Down
File renamed without changes.
File renamed without changes.
34 changes: 4 additions & 30 deletions bin/index.spec.ts → tests/e2e/index.spec.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,16 @@
import { spawn } from 'node:child_process';
import fs from 'node:fs';
import os from 'node:os';
import path from 'node:path';
import readline from 'node:readline';

import { subscribeSpyTo } from '@hirez_io/observer-spy';
import { sendCtrlC, spawnWithWrapper } from 'ctrlc-wrapper';
import { build } from 'esbuild';
import Rx from 'rxjs';
import { map } from 'rxjs/operators';
import stringArgv from 'string-argv';
import { afterAll, beforeAll, describe, expect, it } from 'vitest';
import { describe, expect, it } from 'vitest';

import { escapeRegExp } from '../lib/utils.js';
import { escapeRegExp } from '../../lib/utils.js';

const isWindows = process.platform === 'win32';
const createKillMessage = (prefix: string, signal: 'SIGTERM' | 'SIGINT' | string) => {
Expand All @@ -24,37 +22,13 @@ const createKillMessage = (prefix: string, signal: 'SIGTERM' | 'SIGINT' | string
return new RegExp(`${escapeRegExp(prefix)} exited with code ${map[signal] ?? signal}`);
};

let tmpDir: string;

beforeAll(async () => {
// Build 'concurrently' and store it in a temporary directory
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'concurrently-'));
await build({
entryPoints: [path.join(__dirname, 'index.ts')],
platform: 'node',
bundle: true,
// it doesn't seem like esbuild is able to change a CJS module to ESM, so target CJS instead.
// https://github.com/evanw/esbuild/issues/1921
format: 'cjs',
outfile: path.join(tmpDir, 'concurrently.cjs'),
});
fs.copyFileSync(path.join(__dirname, '..', 'package.json'), path.join(tmpDir, 'package.json'));
}, 8000);

afterAll(() => {
// Remove the temporary directory where 'concurrently' was stored
if (tmpDir) {
fs.rmSync(tmpDir, { recursive: true });
}
});

/**
* Creates a child process running 'concurrently' with the given args.
* Returns observables for its combined stdout + stderr output, close events, pid, and stdin stream.
*/
const run = (args: string, ctrlcWrapper?: boolean) => {
const spawnFn = ctrlcWrapper ? spawnWithWrapper : spawn;
const child = spawnFn('node', [path.join(tmpDir, 'concurrently.cjs'), ...stringArgv(args)], {
const child = spawnFn('node', ['../../dist/bin/index.js', ...stringArgv(args)], {
cwd: __dirname,
env: {
...process.env,
Expand Down Expand Up @@ -126,7 +100,7 @@ it('prints help when no arguments are passed', async () => {
});

describe('has version command', () => {
const pkg = fs.readFileSync(path.join(__dirname, '..', 'package.json'), 'utf-8');
const pkg = fs.readFileSync(path.join(__dirname, '..', '..', 'package.json'), 'utf-8');
const { version } = JSON.parse(pkg);

it.each(['--version', '-V', '-v'])('%s', async (arg) => {
Expand Down
4 changes: 1 addition & 3 deletions tests/package.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
{
"type": "module",
"dependencies": {
"concurrently": "workspace:*"
},
"scripts": {
"test": "pnpm --workspace-root test:smoke"
}
}
36 changes: 36 additions & 0 deletions tests/setup.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { exec as originalExec } from 'node:child_process';
import util from 'node:util';

import type { TestProject, TestSpecification } from 'vitest/node';

const exec = util.promisify(originalExec);

function buildProject() {
return exec('pnpm run build');
}

function isBuildRequired(testFiles: TestSpecification[]) {
for (const file of testFiles) {
if (file.project.name === 'e2e' || file.project.name === 'smoke') {
return true;
}
}
return false;
}

export default async function setup(project: TestProject) {
// @ts-expect-error not typed
const pattern: string[] | undefined = project.vitest.filenamePattern;

const testFiles = await project.vitest.getRelevantTestSpecifications(pattern);

if (isBuildRequired(testFiles)) {
await buildProject();
}

project.onTestsRerun(async (testFiles) => {
if (isBuildRequired(testFiles)) {
await buildProject();
}
});
}
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
10 changes: 1 addition & 9 deletions tests/smoke-tests.spec.ts → tests/smoke/index.spec.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,10 @@
import { exec as originalExec } from 'node:child_process';
import util from 'node:util';

import { beforeAll, expect, it } from 'vitest';
import { expect, it } from 'vitest';

const exec = util.promisify(originalExec);

beforeAll(async () => {
await exec('pnpm run build');
}, 20_000);

it('spawns binary', async () => {
await expect(exec('node dist/bin/index.js "echo test"')).resolves.toBeDefined();
});

it.each(['cjs-import', 'cjs-require', 'esm'])('loads library in %s context', async (project) => {
// Use as separate execs as tsc outputs to stdout, instead of stderr, and so its text isn't shown
await exec(`tsc -p ${project}`, { cwd: __dirname }).catch((err) => Promise.reject(err.stdout));
Expand Down
13 changes: 9 additions & 4 deletions vitest.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,24 @@ export default defineConfig({
// lcov is used for coveralls
reporter: ['text', 'html', 'lcov'],
},
globalSetup: 'tests/setup.ts',
projects: [
{
extends: true,
test: {
name: 'unit',
include: ['{bin,lib}/**/*.spec.ts'],
include: ['lib/**/*.spec.ts'],
},
},
{
test: {
name: 'e2e',
include: ['tests/e2e/*.spec.ts'],
},
},
{
extends: true,
test: {
name: 'smoke',
include: ['tests/**/*.spec.ts'],
include: ['tests/smoke/*.spec.ts'],
},
},
],
Expand Down