diff --git a/fetch/api/body/textstream.tentative.any.js b/fetch/api/body/textstream.tentative.any.js new file mode 100644 index 00000000000000..42f11893991c0a --- /dev/null +++ b/fetch/api/body/textstream.tentative.any.js @@ -0,0 +1,161 @@ +// META: global=window,worker + +async function readAllChunks(stream) { + const reader = stream.getReader(); + const chunks = []; + while (true) { + const {done, value} = await reader.read(); + if (done) { + break; + } + chunks.push(value); + } + return chunks; +} + +test(() => { + assert_true('textStream' in Response.prototype, "textStream exists on Response.prototype"); + assert_true('textStream' in Request.prototype, "textStream exists on Request.prototype"); + assert_equals(typeof Response.prototype.textStream, "function", "Response.prototype.textStream is a function"); + assert_equals(typeof Request.prototype.textStream, "function", "Request.prototype.textStream is a function"); +}, "textStream method existence"); + +promise_test(async () => { + const response = new Response("hello world"); + assert_false(response.bodyUsed, "bodyUsed is false initially"); + const stream = response.textStream(); + assert_true(stream instanceof ReadableStream, "textStream() returns a ReadableStream"); + assert_true(response.bodyUsed, "bodyUsed becomes true immediately after calling textStream()"); + + const chunks = await readAllChunks(stream); + assert_greater_than(chunks.length, 0); + for (const chunk of chunks) { + assert_equals(typeof chunk, "string", "each chunk should be a string"); + } + assert_equals(chunks.join(""), "hello world", "concatenated chunks match the body content"); +}, "Response.textStream() basic functionality"); + +promise_test(async () => { + const request = new Request("https://example.com", { + method: "POST", + body: "hello world" + }); + assert_false(request.bodyUsed, "bodyUsed is false initially"); + const stream = request.textStream(); + assert_true(stream instanceof ReadableStream, "textStream() returns a ReadableStream"); + assert_true(request.bodyUsed, "bodyUsed becomes true immediately after calling textStream()"); + + const chunks = await readAllChunks(stream); + assert_greater_than(chunks.length, 0); + for (const chunk of chunks) { + assert_equals(typeof chunk, "string", "each chunk should be a string"); + } + assert_equals(chunks.join(""), "hello world", "concatenated chunks match the body content"); +}, "Request.textStream() basic functionality"); + +promise_test(async () => { + const stream = new ReadableStream({ + start(controller) { + controller.enqueue(new TextEncoder().encode("hello ")); + controller.enqueue(new TextEncoder().encode("world")); + controller.close(); + } + }); + const response = new Response(stream); + const textStream = response.textStream(); + const chunks = await readAllChunks(textStream); + assert_greater_than(chunks.length, 0); + for (const chunk of chunks) { + assert_equals(typeof chunk, "string"); + } + assert_equals(chunks.join(""), "hello world"); +}, "textStream() handles chunked byte stream input"); + +test(() => { + const response = new Response("hello"); + response.text(); // consumes body + assert_throws_js(TypeError, () => response.textStream()); +}, "Response.textStream() on consumed body throws TypeError"); + +test(() => { + const request = new Request("https://example.com", { + method: "POST", + body: "hello" + }); + request.text(); // consumes body + assert_throws_js(TypeError, () => request.textStream()); +}, "Request.textStream() on consumed body throws TypeError"); + +test(() => { + const response = new Response("hello"); + const reader = response.body.getReader(); // locks body + assert_throws_js(TypeError, () => response.textStream()); +}, "Response.textStream() on locked body throws TypeError"); + +test(() => { + const request = new Request("https://example.com", { + method: "POST", + body: "hello" + }); + const reader = request.body.getReader(); // locks body + assert_throws_js(TypeError, () => request.textStream()); +}, "Request.textStream() on locked body throws TypeError"); + +promise_test(async () => { + const response = new Response(); + assert_equals(response.body, null); + const stream = response.textStream(); + assert_true(stream instanceof ReadableStream); + const chunks = await readAllChunks(stream); + assert_equals(chunks.length, 0, "no chunks should be read from null body textStream"); +}, "Response.textStream() with null body"); + +promise_test(async () => { + const request = new Request("https://example.com"); + assert_equals(request.body, null); + const stream = request.textStream(); + assert_true(stream instanceof ReadableStream); + const chunks = await readAllChunks(stream); + assert_equals(chunks.length, 0, "no chunks should be read from null body textStream"); +}, "Request.textStream() with null body"); + +promise_test(async () => { + const response = new Response(""); + assert_false(response.bodyUsed); + const stream = response.textStream(); + assert_true(stream instanceof ReadableStream); + assert_true(response.bodyUsed); + const chunks = await readAllChunks(stream); + assert_equals(chunks.length, 0, "no chunks should be read from empty stream"); +}, "Response.textStream() with empty body"); + +promise_test(async () => { + const buffer = new Uint8Array([0x68, 0x00, 0x65, 0x00, 0x6c, 0x00, 0x6c, 0x00, 0x6f, 0x00]); // "hello" in UTF-16LE + const response = new Response(buffer, { + headers: { "Content-Type": "text/plain; charset=utf-16le" } + }); + const stream = response.textStream(); + const chunks = await readAllChunks(stream); + assert_equals(chunks.join(""), "h\0e\0l\0l\0o\0", "ignores charset=utf-16le and decodes as UTF-8"); +}, "Response.textStream() ignores Content-Type charset (UTF-16LE)"); + +promise_test(async () => { + const buffer = new Uint8Array([0x68, 0x00, 0x65, 0x00, 0x6c, 0x00, 0x6c, 0x00, 0x6f, 0x00]); // "hello" in UTF-16LE + const request = new Request("https://example.com", { + method: "POST", + body: buffer, + headers: { "Content-Type": "text/plain; charset=utf-16le" } + }); + const stream = request.textStream(); + const chunks = await readAllChunks(stream); + assert_equals(chunks.join(""), "h\0e\0l\0l\0o\0", "ignores charset=utf-16le and decodes as UTF-8"); +}, "Request.textStream() ignores Content-Type charset (UTF-16LE)"); + +promise_test(async () => { + const response = new Response(new TextEncoder().encode("hello"), { + headers: { "Content-Type": "text/plain; charset=invalid-charset" } + }); + const stream = response.textStream(); + const chunks = await readAllChunks(stream); + assert_equals(chunks.join(""), "hello", "ignores invalid-charset and decodes as UTF-8"); +}, "Response.textStream() ignores invalid Content-Type charset (invalid-charset)");