Skip to content

Commit

Permalink
test: deep watch files (#310)
Browse files Browse the repository at this point in the history
  • Loading branch information
privatenumber committed Sep 13, 2023
1 parent 84d722d commit f45e25d
Show file tree
Hide file tree
Showing 5 changed files with 118 additions and 119 deletions.
2 changes: 1 addition & 1 deletion .nvmrc
Original file line number Diff line number Diff line change
@@ -1 +1 @@
v18.13.0
v20.6.1
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@
"dependencies": {
"@esbuild-kit/cjs-loader": "^2.4.2",
"@esbuild-kit/core-utils": "^3.3.0",
"@esbuild-kit/esm-loader": "^2.6.1"
"@esbuild-kit/esm-loader": "^2.6.3"
},
"optionalDependencies": {
"fsevents": "~2.3.2"
Expand Down
8 changes: 4 additions & 4 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

224 changes: 111 additions & 113 deletions tests/specs/watch.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,32 @@
import { type Readable } from 'node:stream';
import path from 'path';
import { setTimeout } from 'timers/promises';
import { on } from 'events';
import { testSuite, expect } from 'manten';
import { createFixture } from 'fs-fixture';
import { tsx } from '../utils/tsx';

type MaybePromise<T> = T | Promise<T>;
const interact = async (
stdout: Readable,
actions: ((data: string) => MaybePromise<boolean | void>)[],
) => {
let currentAction = actions.shift();

const buffers: Buffer[] = [];
while (currentAction) {
for await (const [chunk] of on(stdout, 'data')) {
buffers.push(chunk);
if (await currentAction(chunk.toString())) {
currentAction = actions.shift();
break;
}
}
}

return Buffer.concat(buffers).toString();
};

export default testSuite(async ({ describe }, fixturePath: string) => {
describe('watch', ({ test, describe }) => {
test('require file path', async () => {
Expand All @@ -15,10 +38,17 @@ export default testSuite(async ({ describe }, fixturePath: string) => {
});

test('watch files for changes', async ({ onTestFinish }) => {
let initialValue = Date.now();
const fixture = await createFixture({
'index.js': 'console.log(1)',
'package.json': JSON.stringify({
type: 'module',
}),
'index.js': `
import { value } from './value.js';
console.log(value);
`,
'value.js': `export const value = ${initialValue};`,
});

onTestFinish(async () => await fixture.rm());

const tsxProcess = tsx({
Expand All @@ -28,21 +58,23 @@ export default testSuite(async ({ describe }, fixturePath: string) => {
],
});

await new Promise<void>((resolve) => {
async function onStdOut(data: Buffer) {
const chunkString = data.toString();
await interact(
tsxProcess.stdout!,
[
async (data) => {
if (data.includes(`${initialValue}\n`)) {
initialValue = Date.now();
await fixture.writeFile('value.js', `export const value = ${initialValue};`);
return true;
}
},
data => data.includes(`${initialValue}\n`),
],
);

if (chunkString.match('1\n')) {
await fixture.writeFile('index.js', 'console.log(2)');
} else if (chunkString.match('2\n')) {
tsxProcess.kill();
resolve();
}
}
tsxProcess.kill();

tsxProcess.stdout!.on('data', onStdOut);
tsxProcess.stderr!.on('data', onStdOut);
});
await tsxProcess;
}, 10_000);

test('suppresses warnings & clear screen', async () => {
Expand All @@ -53,32 +85,24 @@ export default testSuite(async ({ describe }, fixturePath: string) => {
],
});

const stdout = await new Promise<string>((resolve) => {
let aggregateStdout = '';
let hitEnter = false;

function onStdOut(data: Buffer) {
const chunkString = data.toString();
// console.log({ chunkString });

aggregateStdout += chunkString;

if (chunkString.match('log-argv.ts')) {
if (hitEnter) {
tsxProcess.kill();
resolve(aggregateStdout);
} else {
hitEnter = true;
await interact(
tsxProcess.stdout!,
[
(data) => {
if (data.includes('log-argv.ts')) {
tsxProcess.stdin?.write('enter');
return true;
}
}
}
tsxProcess.stdout!.on('data', onStdOut);
tsxProcess.stderr!.on('data', onStdOut);
});
},
data => data.includes('log-argv.ts'),
],
);

tsxProcess.kill();

expect(stdout).not.toMatch('Warning');
expect(stdout).toMatch('\u001Bc');
const { all } = await tsxProcess;
expect(all).not.toMatch('Warning');
expect(all).toMatch('\u001Bc');
}, 10_000);

test('passes flags', async () => {
Expand All @@ -90,20 +114,15 @@ export default testSuite(async ({ describe }, fixturePath: string) => {
],
});

const stdout = await new Promise<string>((resolve) => {
tsxProcess.stdout!.on('data', (chunk) => {
const chunkString = chunk.toString();
if (chunkString.startsWith('[')) {
resolve(chunkString);
}
});
});
await interact(
tsxProcess.stdout!,
[data => data.startsWith('["')],
);

tsxProcess.kill();

expect(stdout).toMatch('"--some-flag"');

await tsxProcess;
const { all } = await tsxProcess;
expect(all).toMatch('"--some-flag"');
}, 10_000);

test('wait for exit', async ({ onTestFinish }) => {
Expand All @@ -130,42 +149,23 @@ export default testSuite(async ({ describe }, fixturePath: string) => {
],
});

const stdout = await new Promise<string>((resolve) => {
const buffers: Buffer[] = [];
const waitingOn: [string, (() => void)][] = [
['start\n', () => {
tsxProcess.stdin?.write('enter');
}],
['end\n', () => {}],
];

let currentWaitingOn = waitingOn.shift();
async function onStdOut(data: Buffer) {
buffers.push(data);
const chunkString = data.toString();

if (currentWaitingOn) {
const [expected, callback] = currentWaitingOn!;

// eslint-disable-next-line unicorn/prefer-regexp-test
if (chunkString.match(expected)) {
callback();
currentWaitingOn = waitingOn.shift();
if (!currentWaitingOn) {
tsxProcess.kill();
resolve(Buffer.concat(buffers).toString());
}
await interact(
tsxProcess.stdout!,
[
(data) => {
if (data.includes('start\n')) {
tsxProcess.stdin?.write('enter');
return true;
}
}
}

tsxProcess.stdout!.on('data', onStdOut);
tsxProcess.stderr!.on('data', onStdOut);
});
},
data => data.includes('end\n'),
],
);

expect(stdout).toMatch(/start[\s\S]+end/);
tsxProcess.kill();

await tsxProcess;
const { all } = await tsxProcess;
expect(all).toMatch(/start[\s\S]+end/);
}, 10_000);

describe('help', ({ test }) => {
Expand All @@ -188,20 +188,15 @@ export default testSuite(async ({ describe }, fixturePath: string) => {
],
});

const stdout = await new Promise<string>((resolve) => {
tsxProcess.stdout!.on('data', (chunk) => {
const chunkString = chunk.toString();
if (chunkString.startsWith('[')) {
resolve(chunkString);
}
});
});
await interact(
tsxProcess.stdout!,
[data => data.startsWith('["')],
);

tsxProcess.kill();

expect(stdout).toMatch('"--help"');

await tsxProcess;
const { all } = await tsxProcess;
expect(all).toMatch('"--help"');
}, 5000);
});

Expand Down Expand Up @@ -235,28 +230,31 @@ export default testSuite(async ({ describe }, fixturePath: string) => {
],
});

tsxProcess.stdout!.on('data', async (data: Buffer) => {
const chunkString = data.toString();
if (chunkString === `${value} ${value}\n`) {
value = Date.now();
await Promise.all([
fixture.writeFile(fileA, `export default ${value}`),
fixture.writeFile(fileB, `export default ${value}`),
]);

await setTimeout(500);
await fixture.writeFile(entryFile, 'console.log("TERMINATE")');
}

if (chunkString === 'TERMINATE\n') {
tsxProcess.kill();
}
});
await interact(
tsxProcess.stdout!,
[
async (data) => {
if (data === `${value} ${value}\n`) {
value = Date.now();
await Promise.all([
fixture.writeFile(fileA, `export default ${value}`),
fixture.writeFile(fileB, `export default ${value}`),
]);

await setTimeout(500);
await fixture.writeFile(entryFile, 'console.log("TERMINATE")');
return true;
}
},
data => data === 'TERMINATE\n',
],
);

const tsxProcessResolved = await tsxProcess;
tsxProcess.kill();

expect(tsxProcessResolved.stdout).not.toMatch(`${value} ${value}`);
expect(tsxProcessResolved.stderr).toBe('');
const { all, stderr } = await tsxProcess;
expect(all).not.toMatch(`${value} ${value}`);
expect(stderr).toBe('');
}, 10_000);
});
});
Expand Down
1 change: 1 addition & 0 deletions tests/utils/tsx.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ export const tsx = (
nodeOptions: [],
cwd: options.cwd,
reject: false,
all: true,
},
);

Expand Down

0 comments on commit f45e25d

Please sign in to comment.