initial import
This commit is contained in:
+212
@@ -0,0 +1,212 @@
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import { dirname } from 'node:path';
|
||||
import { test } from 'node:test';
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = dirname(__filename);
|
||||
const indexPath = __filename.replace('/test/index.js', '/index.js');
|
||||
const indexUrl = `file://${indexPath}`;
|
||||
|
||||
let takeUntil;
|
||||
|
||||
test('setup', async () => {
|
||||
const mod = await import(indexUrl);
|
||||
takeUntil = mod.takeUntil || mod.default || mod;
|
||||
});
|
||||
|
||||
function createAsyncSource(values, delay = 5) {
|
||||
let index = 0;
|
||||
return {
|
||||
pipe(s) {
|
||||
this.sink = s;
|
||||
try { s.source = this; } catch (e) {}
|
||||
this.resume();
|
||||
return s;
|
||||
},
|
||||
resume() { this.writeNext(); },
|
||||
writeNext() {
|
||||
if (index >= values.length) {
|
||||
if (this.sink && this.sink.end) {
|
||||
this.sink.end();
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (!this.sink) return;
|
||||
this.sink.write(values[index++]);
|
||||
setTimeout(() => this.writeNext(), delay);
|
||||
},
|
||||
write() {},
|
||||
end() {},
|
||||
abort() {},
|
||||
get sink() { return this._sink; },
|
||||
set sink(s) { this._sink = s; },
|
||||
get paused() { return this._sink ? this._sink.paused : true; },
|
||||
get ended() { return false; }
|
||||
};
|
||||
}
|
||||
|
||||
function createSignalStream(delay) {
|
||||
return {
|
||||
pipe(s) {
|
||||
this.sink = s;
|
||||
try { s.source = this; } catch (e) {}
|
||||
if (delay === 0) {
|
||||
if (this.sink && this.sink.end) {
|
||||
this.sink.end();
|
||||
}
|
||||
} else {
|
||||
setTimeout(() => {
|
||||
if (this.sink && this.sink.end) {
|
||||
this.sink.end();
|
||||
}
|
||||
}, delay);
|
||||
}
|
||||
return s;
|
||||
},
|
||||
resume() {},
|
||||
write() {},
|
||||
end() {},
|
||||
abort() {},
|
||||
get sink() { return this._sink; },
|
||||
set sink(s) { this._sink = s; },
|
||||
get paused() { return this._sink ? this._sink.paused : true; },
|
||||
get ended() { return false; }
|
||||
};
|
||||
}
|
||||
|
||||
test('ends when signal emits', async () => {
|
||||
const results = [];
|
||||
let streamEnded = false;
|
||||
let endTime = 0;
|
||||
const startTime = Date.now();
|
||||
|
||||
const signalStream = createSignalStream(30);
|
||||
const { sink, pipe } = takeUntil(signalStream);
|
||||
|
||||
const finalSink = {
|
||||
write: (v) => results.push(v),
|
||||
end: (err) => { streamEnded = true; endTime = Date.now() - startTime; },
|
||||
get paused() { return false; }
|
||||
};
|
||||
|
||||
pipe(finalSink);
|
||||
createAsyncSource([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 5).pipe(sink);
|
||||
|
||||
let waited = 0;
|
||||
while (!streamEnded && waited < 200) {
|
||||
await new Promise(resolve => setTimeout(resolve, 10));
|
||||
waited++;
|
||||
}
|
||||
|
||||
if (streamEnded === false) {
|
||||
throw new Error('Stream should have ended');
|
||||
}
|
||||
if (endTime < 20) {
|
||||
throw new Error(`Stream ended too quickly (${endTime}ms) - signal may not be working`);
|
||||
}
|
||||
if (endTime > 100) {
|
||||
throw new Error(`Stream took too long to end (${endTime}ms)`);
|
||||
}
|
||||
if (results.length >= 10) {
|
||||
throw new Error(`Expected fewer than 10 values (signal should have stopped it), got ${results.length}`);
|
||||
}
|
||||
});
|
||||
|
||||
test('signal immediately ends', async () => {
|
||||
const results = [];
|
||||
let streamEnded = false;
|
||||
|
||||
const signalStream = createSignalStream(0);
|
||||
const { sink, pipe } = takeUntil(signalStream);
|
||||
|
||||
const finalSink = {
|
||||
write: (v) => results.push(v),
|
||||
end: () => { streamEnded = true; },
|
||||
get paused() { return false; }
|
||||
};
|
||||
|
||||
pipe(finalSink);
|
||||
createAsyncSource([1, 2, 3, 4, 5], 5).pipe(sink);
|
||||
|
||||
let waited = 0;
|
||||
while (!streamEnded && waited < 100) {
|
||||
await new Promise(resolve => setTimeout(resolve, 10));
|
||||
waited++;
|
||||
}
|
||||
|
||||
if (results.length !== 0) {
|
||||
throw new Error(`Expected 0 values (immediate signal), got ${results.length}`);
|
||||
}
|
||||
});
|
||||
|
||||
test('signal never emits', async () => {
|
||||
const results = [];
|
||||
let streamEnded = false;
|
||||
|
||||
const signalStream = {
|
||||
pipe(s) {
|
||||
this.sink = s;
|
||||
try { s.source = this; } catch (e) {}
|
||||
return s;
|
||||
},
|
||||
resume() {},
|
||||
write() {},
|
||||
end() {},
|
||||
abort() {},
|
||||
get sink() { return this._sink; },
|
||||
set sink(s) { this._sink = s; },
|
||||
get paused() { return this._sink ? this._sink.paused : true; },
|
||||
get ended() { return false; }
|
||||
};
|
||||
|
||||
const takeUntilStream = takeUntil(signalStream);
|
||||
|
||||
const finalSink = {
|
||||
write: (v) => results.push(v),
|
||||
end: () => { streamEnded = true; },
|
||||
get paused() { return false; }
|
||||
};
|
||||
|
||||
takeUntilStream.pipe(finalSink);
|
||||
createAsyncSource([1, 2, 3], 5).pipe(takeUntilStream);
|
||||
|
||||
let waited = 0;
|
||||
while (!streamEnded && waited < 200) {
|
||||
await new Promise(resolve => setTimeout(resolve, 10));
|
||||
waited++;
|
||||
}
|
||||
|
||||
if (results.length !== 3) {
|
||||
throw new Error(`Expected 3 values, got ${results.length}`);
|
||||
}
|
||||
});
|
||||
|
||||
test('passes through all values before signal', async () => {
|
||||
const results = [];
|
||||
let streamEnded = false;
|
||||
|
||||
const signalStream = createSignalStream(100);
|
||||
const takeUntilStream = takeUntil(signalStream);
|
||||
|
||||
const finalSink = {
|
||||
write: (v) => results.push(v),
|
||||
end: () => { streamEnded = true; },
|
||||
get paused() { return false; }
|
||||
};
|
||||
|
||||
takeUntilStream.pipe(finalSink);
|
||||
createAsyncSource([1, 2], 5).pipe(takeUntilStream);
|
||||
|
||||
let waited = 0;
|
||||
while (!streamEnded && waited < 200) {
|
||||
await new Promise(resolve => setTimeout(resolve, 10));
|
||||
waited++;
|
||||
}
|
||||
|
||||
if (results.length !== 2) {
|
||||
throw new Error(`Expected 2 values before signal, got ${results.length}`);
|
||||
}
|
||||
if (results[0] !== 1 || results[1] !== 2) {
|
||||
throw new Error(`Values wrong: ${JSON.stringify(results)}`);
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user