Skip to content

Commit

Permalink
test: initial set of tests (#6)
Browse files Browse the repository at this point in the history
Adds the initial set of tests for our utils.
  • Loading branch information
43081j authored Jun 14, 2024
1 parent ce57d86 commit 7809a84
Show file tree
Hide file tree
Showing 6 changed files with 158 additions and 5 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@
/node_modules
/lib
/dist
/coverage
*.swp
9 changes: 9 additions & 0 deletions eslint.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,14 @@ export default [
rules: {
'@typescript-eslint/no-unused-vars': 'off'
}
},
{
files: [
'src/test/**/*.ts'
],
rules: {
'@typescript-eslint/no-dynamic-delete': 'off',
'@typescript-eslint/no-non-null-assertion': 'off'
}
}
];
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@
"!dist/esm/test"
],
"scripts": {
"build": "tsc --noEmit",
"test": "npm run prepare && echo \"no tests yet\"",
"build": "tsc",
"test": "npm run build && c8 node --test",
"lint": "eslint src",
"format": "prettier --write src",
"format:check": "prettier --check src",
Expand Down
5 changes: 2 additions & 3 deletions src/env.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import {type SpawnOptions} from 'node:child_process';
import {
delimiter as pathDelimiter,
resolve as resolvePath,
dirname
} from 'node:path';

export type EnvLike = SpawnOptions['env'];
export type EnvLike = (typeof process)['env'];

export interface EnvPathInfo {
key: string;
Expand Down Expand Up @@ -50,7 +49,7 @@ function addNodeBinToPath(cwd: string, path: EnvPathInfo): EnvPathInfo {
return {key: path.key, value: parts.join(pathDelimiter)};
}

export function computeEnv(cwd: string, env: EnvLike): EnvLike {
export function computeEnv(cwd: string, env?: EnvLike): EnvLike {
const envWithDefault = {
...process.env,
...env
Expand Down
70 changes: 70 additions & 0 deletions src/test/env_test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import {computeEnv} from '../env.js';
import * as assert from 'node:assert/strict';
import {test} from 'node:test';
import process from 'node:process';

test('computeEnv', async (t) => {
await t.test('adds node binaries to path', () => {
const env = computeEnv(process.cwd());
const path = env['PATH']!;

assert.ok(path.includes('node_modules/.bin'));
});

await t.test('extends process env', () => {
const env = computeEnv(process.cwd(), {
foo: 'bar'
});

for (const key in process.env) {
if (key !== 'PATH') {
assert.equal(env[key], process.env[key]);
}
}

assert.equal(env.foo, 'bar');
});

await t.test('supports case-insensitive path keys', () => {
const originalPath = process.env['PATH'];
try {
delete process.env['PATH'];
const env = computeEnv(process.cwd(), {
Path: '/'
});
const keys = [...Object.keys(env)];

assert.ok(keys.includes('Path'));
assert.ok(!keys.includes('PATH'));
} finally {
process.env['PATH'] = originalPath;
}
});

await t.test('uses default key if empty path found', () => {
const originalPath = process.env['PATH'];
try {
delete process.env['PATH'];
const env = computeEnv(process.cwd(), {
Path: undefined
});

assert.ok(typeof env['PATH'] === 'string');
assert.equal(env['Path'], undefined);
} finally {
process.env['PATH'] = originalPath;
}
});

await t.test('uses default key if no path found', () => {
const originalPath = process.env['PATH'];
try {
delete process.env['PATH'];
const env = computeEnv(process.cwd());

assert.ok(typeof env['PATH'] === 'string');
} finally {
process.env['PATH'] = originalPath;
}
});
});
74 changes: 74 additions & 0 deletions src/test/stream_test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import {combineStreams, waitForEvent, readStreamAsString} from '../stream.js';
import * as assert from 'node:assert/strict';
import {test} from 'node:test';
import {EventEmitter} from 'node:events';
import {Readable} from 'node:stream';

test('waitForEvent', async (t) => {
await t.test('waits for event to fire', async () => {
const emitter = new EventEmitter();
const waiter = waitForEvent(emitter, 'foo');
emitter.emit('foo');
await waiter;
});
});

test('readStreamAsString', async (t) => {
await t.test('rejects on error', async () => {
const streamError = new Error('fudge');
const stream = new Readable({
read() {
this.destroy(streamError);
}
});
try {
await readStreamAsString(stream);
assert.fail('expected to throw');
} catch (err) {
assert.equal(err, streamError);
}
});

await t.test('resolves to concatenated data', async () => {
const stream = Readable.from(['foo', 'bar']);
const result = await readStreamAsString(stream);
assert.equal(result, 'foobar');
});

await t.test('handles buffer data', async () => {
const stream = new Readable({
read() {
this.push(Buffer.from('foo'));
this.push(Buffer.from('bar'));
this.push(null);
}
});
const result = await readStreamAsString(stream);
assert.equal(result, 'foobar');
});
});

test('combineStreams', async (t) => {
await t.test('works with a single stream', async () => {
const stream = Readable.from(['foo', 'bar']);
const combined = combineStreams([stream]);
const chunks: string[] = [];
combined.on('data', (chunk: Buffer) => {
chunks.push(chunk.toString());
});
await waitForEvent(combined, 'end');
assert.deepEqual(chunks, ['foo', 'bar']);
});

await t.test('works with multiple streams', async () => {
const stream0 = Readable.from(['foo']);
const stream1 = Readable.from(['bar', 'baz']);
const combined = combineStreams([stream0, stream1]);
const chunks: string[] = [];
combined.on('data', (chunk: Buffer) => {
chunks.push(chunk.toString());
});
await waitForEvent(combined, 'end');
assert.deepEqual(chunks, ['foo', 'bar', 'baz']);
});
});

0 comments on commit 7809a84

Please sign in to comment.