Syntax previewClick to edit
12345
export default async function streamToText(stream, options = {}) {
// TODO: read stream with a reader + TextDecoder
throw new Error('Not implemented');
}
Syntax previewClick to edit
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354
import streamToText from './streamToText';
function makeStream(chunks) {
let i = 0;
let cancelled = false;
return {
getReader() {
return {
read() {
if (cancelled) return Promise.resolve({ done: true });
if (i >= chunks.length) return Promise.resolve({ done: true, value: undefined });
return Promise.resolve({ done: false, value: chunks[i++] });
},
cancel() {
cancelled = true;
}
};
}
};
}
describe('streamToText', () => {
test('concatenates chunks', async () => {
const enc = new TextEncoder();
const stream = makeStream([enc.encode('Hello '), enc.encode('World')]);
await expect(streamToText(stream)).resolves.toBe('Hello World');
});
test('handles multibyte characters across chunks', async () => {
const enc = new TextEncoder();
const bytes = enc.encode('Hi 🌍');
const part1 = bytes.slice(0, 4);
const part2 = bytes.slice(4);
const stream = makeStream([part1, part2]);
await expect(streamToText(stream)).resolves.toBe('Hi 🌍');
});
test('rejects on abort', async () => {
const enc = new TextEncoder();
const stream = makeStream([enc.encode('data')]);
const ac = new AbortController();
ac.abort(new Error('Stop'));
await expect(streamToText(stream, { signal: ac.signal })).rejects.toMatchObject({
name: 'AbortError'
});
});
test('rejects for invalid stream input', async () => {
// @ts-expect-error runtime guard
await expect(streamToText(null)).rejects.toThrow(TypeError);
});
});
Run tests to see results.