Skip to content

Commit

Permalink
converted tests to typescript with jest
Browse files Browse the repository at this point in the history
  • Loading branch information
gil-fireblocks committed Mar 28, 2022
1 parent 2fb0f37 commit 2dac2ee
Show file tree
Hide file tree
Showing 20 changed files with 3,136 additions and 808 deletions.
7 changes: 7 additions & 0 deletions jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
/** @type {import('ts-jest/dist/types').InitialOptionsTsJest} */
module.exports = {
preset: 'ts-jest',
testEnvironment: 'node',
testMatch: ["**/src/test/*.test.ts"],
testTimeout: 30000,
};
3,391 changes: 2,840 additions & 551 deletions package-lock.json

Large diffs are not rendered by default.

15 changes: 7 additions & 8 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"description": "The TypeScript compiler with onSuccess command",
"scripts": {
"clean": "rimraf dist && rimraf tmp",
"test": "npm run build && mocha --timeout 10000 src/test/**/*.js",
"test": "npm run build && jest --verbose --runInBand",
"build": "npm run clean && npm run build-lib && npm run build-client",
"build-lib": "tsc -p tsconfig.json",
"build-client": "tsc -p tsconfig-client.json"
Expand Down Expand Up @@ -41,22 +41,21 @@
"cross-spawn": "^7.0.3",
"node-cleanup": "^2.1.2",
"ps-tree": "^1.2.0",
"string-argv": "^0.3.1",
"strip-ansi": "^0.1.0"
"string-argv": "^0.3.1"
},
"peerDependencies": {
"typescript": "*"
},
"devDependencies": {
"@types/cross-spawn": "^6.0.2",
"@types/jest": "^27.4.1",
"@types/node": "^12.12.0",
"@types/node-cleanup": "^2.1.2",
"@types/ps-tree": "^1.1.2",
"chai": "^4.3.6",
"fs-extra": "^10.0.0",
"mocha": "^9.2.2",
"fs-extra": "^10.0.1",
"jest": "^27.5.1",
"rimraf": "^3.0.2",
"sinon": "^13.0.1",
"typescript": "^4.5.4"
"ts-jest": "^27.1.4",
"typescript": "^4.6.3"
}
}
11 changes: 7 additions & 4 deletions src/client/client.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
import { fork, ChildProcess } from 'child_process';
import { ChildProcess, fork, ForkOptions } from 'child_process';
import { EventEmitter } from 'events';
import { join } from 'path';

export class TscWatchClient extends EventEmitter {
private tsc: ChildProcess | undefined;

constructor(private tscWatchPath: string = require.resolve('tsc-watch')) {
super();
}

start(...args: string[]) {
const tscWatch = require.resolve(join(__dirname, 'dist', 'lib', 'tsc-watch'));
this.tsc = fork(tscWatch, args, { stdio: 'inherit' });
const options: ForkOptions = { stdio: 'inherit' };
this.tsc = fork(this.tscWatchPath, args, options);
this.tsc.on('message', (msg: string) => {
this.emit(...deserializeTscMessage(msg));
});
Expand Down
1 change: 0 additions & 1 deletion src/lib/@types/ansi-strip.d.ts

This file was deleted.

1 change: 1 addition & 0 deletions src/lib/@types/fs-extra.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
declare module 'fs-extra';
30 changes: 19 additions & 11 deletions src/lib/stdout-manipulator.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,19 @@
import stripAnsi from 'strip-ansi';

const ANSI_REGEX = new RegExp('[\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]+)*|[a-zA-Z\\d]+(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?\\u0007)|(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-nq-uy=><~]))', 'g');
const stripAnsi = (str: string) => str.replace(ANSI_REGEX, '');


const tscUsageSyntaxRegex = / -w, --watch.*Watch input files\./;
const typescriptPrettyErrorRegex = /:\d+:\d+ \- error TS\d+: /;
const typescriptErrorRegex = /\(\d+,\d+\): error TS\d+: /;
const typescriptEmittedFileRegex = /(TSFILE:)\s*(.*)/;

const compilationCompleteWithErrorRegex = / Found [^0][0-9]* error[s]?\. Watching for file changes\./;
const compilationCompleteWithErrorRegex =
/ Found [^0][0-9]* error[s]?\. Watching for file changes\./;
const compilationCompleteWithoutErrorRegex = / Found 0 errors\. Watching for file changes\./;
const compilationCompleteRegex = /( Compilation complete\. Watching for file changes\.| Found \d+ error[s]?\. Watching for file changes\.)/;
const compilationStartedRegex = /( Starting compilation in watch mode\.\.\.| File change detected\. Starting incremental compilation\.\.\.)/;
const compilationCompleteRegex =
/( Compilation complete\. Watching for file changes\.| Found \d+ error[s]?\. Watching for file changes\.)/;
const compilationStartedRegex =
/( Starting compilation in watch mode\.\.\.| File change detected\. Starting incremental compilation\.\.\.)/;

const newAdditionToSyntax = [
' -w, --watch Watch input files. [always on]',
Expand All @@ -24,20 +29,23 @@ const newAdditionToSyntax = [

function color(line: string, noClear: boolean = false): string {
// coloring errors:
line = line.replace(typescriptErrorRegex, m => `\u001B[36m${m}\u001B[39m`); // Cyan
line = line.replace(typescriptPrettyErrorRegex, m => `\u001B[36m${m}\u001B[39m`); // Cyan
line = line.replace(typescriptErrorRegex, (m) => `\u001B[36m${m}\u001B[39m`); // Cyan
line = line.replace(typescriptPrettyErrorRegex, (m) => `\u001B[36m${m}\u001B[39m`); // Cyan

// completed with error:
line = line.replace(compilationCompleteWithErrorRegex, m => `\u001B[31m${m}\u001B[39m`); // Red
line = line.replace(compilationCompleteWithErrorRegex, (m) => `\u001B[31m${m}\u001B[39m`); // Red

// completed without error:
line = line.replace(compilationCompleteWithoutErrorRegex, m => `\u001B[32m${m}\u001B[39m`); // Green
line = line.replace(compilationCompleteWithoutErrorRegex, (m) => `\u001B[32m${m}\u001B[39m`); // Green

// usage
line = line.replace(tscUsageSyntaxRegex, m => `\u001B[33m${m}\u001B[39m`); // Yellow
line = line.replace(tscUsageSyntaxRegex, (m) => `\u001B[33m${m}\u001B[39m`); // Yellow

// file emitted
line = line.replace(typescriptEmittedFileRegex, (_0, stdPrefix, file) => `\u001B[30m\u001B[4m${stdPrefix}\u001B[0m \u001B[30m${file}\u001B[0m`); // Grey underlined / Grey
line = line.replace(
typescriptEmittedFileRegex,
(_0, stdPrefix, file) => `\u001B[30m\u001B[4m${stdPrefix}\u001B[0m \u001B[30m${file}\u001B[0m`,
); // Grey underlined / Grey

if (noClear && compilationStartedRegex.test(line)) {
return '\n\n----------------------\n' + line;
Expand Down
59 changes: 29 additions & 30 deletions src/test/args-manager.it.js → src/test/args-manager.test.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
const { expect } = require('chai');
const { extractArgs } = require('../../dist/lib/args-manager');
import { extractArgs } from '../lib/args-manager';

describe('Args Manager', () => {
it('Should remove the runner args', () => {
const { args } = extractArgs(['node', 'tsc-watch.js', '-d', '1.ts']);
expect(args).to.deep.eq(['-d', '1.ts', '--watch']);
expect(args).toEqual(['-d', '1.ts', '--watch']);
});

it('Should remove custom args', () => {
Expand All @@ -24,12 +23,12 @@ describe('Args Manager', () => {
'-d',
'1.ts',
]);
expect(args).to.deep.eq(['-d', '1.ts', '--watch']);
expect(args).toEqual(['-d', '1.ts', '--watch']);
});

it('Should force watch', () => {
const { args } = extractArgs(['node', 'tsc-watch.js', '1.ts']);
expect(args.indexOf('--watch')).to.be.greaterThan(-1);
expect(args.indexOf('--watch')).toBeGreaterThan(-1);
});

it('Should not change the argv order options if watch was not specified (fixes --build option)', () => {
Expand All @@ -40,86 +39,86 @@ describe('Args Manager', () => {
'1.tsconfig.conf',
'2.tsconfig.conf',
]);
expect(args.indexOf('--build')).to.be.equal(0);
expect(args.indexOf('--watch')).to.be.equal(3);
expect(args.indexOf('--build')).toBe(0);
expect(args.indexOf('--watch')).toBe(3);
});

it('Should not re-add watch', () => {
expect(
extractArgs(['node', 'tsc-watch.js', '-w', '1.ts']).args.indexOf('-w'),
).to.be.greaterThan(-1);
).toBeGreaterThan(-1);
expect(
extractArgs(['node', 'tsc-watch.js', '--watch', '1.ts']).args.indexOf('--watch'),
).to.be.greaterThan(-1);
).toBeGreaterThan(-1);
expect(
extractArgs(['node', 'tsc-watch.js', '--watch', '1.ts']).args.indexOf('--watch'),
).to.be.greaterThan(-1);
).toBeGreaterThan(-1);
});

it('Should return the onFirstSuccessCommand', () => {
expect(extractArgs(['node', 'tsc-watch.js', '1.ts']).onFirstSuccessCommand).to.eq(null);
expect(extractArgs(['node', 'tsc-watch.js', '1.ts']).onFirstSuccessCommand).toBe(null);
expect(
extractArgs(['node', 'tsc-watch.js', '--onFirstSuccess', 'COMMAND_TO_RUN', '1.ts'])
.onFirstSuccessCommand,
).to.eq('COMMAND_TO_RUN');
).toBe('COMMAND_TO_RUN');
});

it('Should return the onSuccessCommand', () => {
expect(extractArgs(['node', 'tsc-watch.js', '1.ts']).onSuccessCommand).to.eq(null);
expect(extractArgs(['node', 'tsc-watch.js', '1.ts']).onSuccessCommand).toBe(null);
expect(
extractArgs(['node', 'tsc-watch.js', '--onSuccess', 'COMMAND_TO_RUN', '1.ts'])
.onSuccessCommand,
).to.eq('COMMAND_TO_RUN');
).toBe('COMMAND_TO_RUN');
});

it('Should return the onFailureCommand', () => {
expect(extractArgs(['node', 'tsc-watch.js', '1.ts']).onFailureCommand).to.eq(null);
expect(extractArgs(['node', 'tsc-watch.js', '1.ts']).onFailureCommand).toBe(null);
expect(
extractArgs(['node', 'tsc-watch.js', '--onFailure', 'COMMAND_TO_RUN', '1.ts'])
.onFailureCommand,
).to.eq('COMMAND_TO_RUN');
).toBe('COMMAND_TO_RUN');
});

it('Should return the onCompilationStarted', () => {
expect(extractArgs(['node', 'tsc-watch.js', '1.ts']).onCompilationStarted).to.eq(null);
expect(extractArgs(['node', 'tsc-watch.js', '1.ts']).onCompilationStarted).toBe(null);
expect(
extractArgs(['node', 'tsc-watch.js', '--onCompilationStarted', 'COMMAND_TO_RUN', '1.ts'])
.onCompilationStarted,
).to.eq('COMMAND_TO_RUN');
).toBe('COMMAND_TO_RUN');
});

it('Should return the onCompilationComplete', () => {
expect(extractArgs(['node', 'tsc-watch.js', '1.ts']).onCompilationComplete).to.eq(null);
expect(extractArgs(['node', 'tsc-watch.js', '1.ts']).onCompilationComplete).toBe(null);
expect(
extractArgs(['node', 'tsc-watch.js', '--onCompilationComplete', 'COMMAND_TO_RUN', '1.ts'])
.onCompilationComplete,
).to.eq('COMMAND_TO_RUN');
).toBe('COMMAND_TO_RUN');
});

it('Should return the maxNodeMem', () => {
expect(extractArgs(['node', 'tsc-watch.js', '1.ts']).maxNodeMem).to.eq(null);
expect(extractArgs(['node', 'tsc-watch.js', '--maxNodeMem', '1024']).maxNodeMem).to.eq('1024');
expect(extractArgs(['node', 'tsc-watch.js', '1.ts']).maxNodeMem).toBe(null);
expect(extractArgs(['node', 'tsc-watch.js', '--maxNodeMem', '1024']).maxNodeMem).toBe('1024');
});

it('Should return the noColors', () => {
expect(extractArgs(['node', 'tsc-watch.js', '1.ts']).noColors).to.eq(false);
expect(extractArgs(['node', 'tsc-watch.js', '--noColors', '1.ts']).noColors).to.eq(true);
expect(extractArgs(['node', 'tsc-watch.js', '1.ts']).noColors).toBe(false);
expect(extractArgs(['node', 'tsc-watch.js', '--noColors', '1.ts']).noColors).toBe(true);
});

it('Should return the noClear', () => {
expect(extractArgs(['node', 'tsc-watch.js', '1.ts']).noClear).to.eq(false);
expect(extractArgs(['node', 'tsc-watch.js', '--noClear', '1.ts']).noClear).to.eq(true);
expect(extractArgs(['node', 'tsc-watch.js', '1.ts']).noClear).toBe(false);
expect(extractArgs(['node', 'tsc-watch.js', '--noClear', '1.ts']).noClear).toBe(true);
});

it('Should return the silent', () => {
expect(extractArgs(['node', 'tsc-watch.js', '1.ts']).silent).to.eq(false);
expect(extractArgs(['node', 'tsc-watch.js', '--silent', '1.ts']).silent).to.eq(true);
expect(extractArgs(['node', 'tsc-watch.js', '1.ts']).silent).toBe(false);
expect(extractArgs(['node', 'tsc-watch.js', '--silent', '1.ts']).silent).toBe(true);
});

it('Should return the compiler', () => {
expect(extractArgs(['node', 'tsc-watch.js', '1.ts']).compiler).to.eq('typescript/bin/tsc');
expect(extractArgs(['node', 'tsc-watch.js', '1.ts']).compiler).toBe('typescript/bin/tsc');
expect(
extractArgs(['node', 'tsc-watch.js', '--compiler', 'typescript/lib/tsc', '1.ts']).compiler,
).to.eq(require.resolve('typescript/lib/tsc'));
).toBe(require.resolve('typescript/lib/tsc'));
});
});
57 changes: 0 additions & 57 deletions src/test/client.it.js

This file was deleted.

65 changes: 65 additions & 0 deletions src/test/client.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import { join } from 'path';
import { TscWatchClient } from '../client';
import {
copyFixtures,
FAILING_FILE,
OUTPUT_FILE,
PASSING_FILE,
removeFixtures,
waitFor,
} from './test-utils';

describe('Client Events', () => {
let watchClient: TscWatchClient;
let callback: jest.Mock;

beforeEach(() => {
const tscWatchPath = require.resolve(join('..', '..', 'dist', 'lib', 'tsc-watch'));
watchClient = new TscWatchClient(tscWatchPath);
callback = jest.fn();
copyFixtures();
});

afterEach(() => {
watchClient.kill();
removeFixtures();
});

describe('Events', () => {
it('Should emit "started" on compilation start', () => {
watchClient.on('started', callback);
watchClient.start('--noClear', '--out', OUTPUT_FILE, FAILING_FILE);
return waitFor(() => callback.mock.calls.length > 0);
});

it('Should emit "first_success" on first success', async () => {
watchClient.on('first_success', callback);
watchClient.start('--noClear', '--out', OUTPUT_FILE, PASSING_FILE);
return waitFor(() => callback.mock.calls.length > 0);
});

it('Should emit "success" on first success', async () => {
watchClient.on('success', callback);
watchClient.start('--noClear', '--out', OUTPUT_FILE, PASSING_FILE);
return waitFor(() => callback.mock.calls.length > 0);
});

it('Should deserialize and emit a "file_emitted" with the emitted file path', async function () {
watchClient.on('file_emitted', callback);
watchClient.start('--noClear', '--listEmittedFiles', '--out', OUTPUT_FILE, PASSING_FILE);
return waitFor(() => {
if (callback.mock.calls.length > 0) {
const firstCall = callback.mock.calls[0];
const callFirstArg = firstCall[0];
return callFirstArg === OUTPUT_FILE;
}
});
});

it('Should fire "compile_errors" on when tsc compile errors occur', async () => {
watchClient.on('compile_errors', callback);
watchClient.start('--noClear', '--out', OUTPUT_FILE, FAILING_FILE);
return waitFor(() => callback.mock.calls.length > 0);
});
});
});
Loading

0 comments on commit 2dac2ee

Please sign in to comment.