-
Notifications
You must be signed in to change notification settings - Fork 1
/
test.js
96 lines (82 loc) · 3.7 KB
/
test.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
import test from 'node:test';
import assert from 'node:assert';
import vm from 'node:vm';
import fs from 'node:fs';
import {injectRSCPayload} from './server.js';
const client = fs.readFileSync('client.js', 'utf8').replace('export let ', 'window.');
function testStream(chunks, ref) {
let encoder = new TextEncoder();
return new ReadableStream({
async pull(controller) {
while (chunks.length) {
let chunk = chunks.shift();
if (typeof chunk === 'function') {
await chunk();
} else if (typeof chunk === 'string') {
controller.enqueue(encoder.encode(chunk));
} else {
controller.enqueue(chunk);
}
}
controller.close();
}
});
}
function runScripts(html) {
let scripts = html.matchAll(/<script>(.*?)<\/script>/g);
let window = {};
let ctx = vm.createContext({self: window, window, TextEncoder, ReadableStream, atob});
for (let script of scripts) {
vm.runInContext(script[1], ctx);
}
vm.runInContext(client, ctx);
return window.rscStream;
}
async function streamToString(stream) {
let result = '';
for await (let chunk of stream.pipeThrough(new TextDecoderStream())) {
result += chunk;
}
return result;
}
test('should handle text data', async () => {
let html = testStream(['<html><body><h1>Test</h1>', '<p>Hello world</p></body></html>']);
let rscStream = testStream(['foo bar', 'baz qux', 'abcdef']);
let injected = html.pipeThrough(injectRSCPayload(rscStream));
let result = await streamToString(injected);
assert.equal(result, '<html><body><h1>Test</h1><p>Hello world</p><script>(self.__FLIGHT_DATA||=[]).push("foo bar")</script><script>(self.__FLIGHT_DATA||=[]).push("baz qux")</script><script>(self.__FLIGHT_DATA||=[]).push("abcdef")</script></body></html>');
let clientStream = runScripts(result);
let decoded = await streamToString(clientStream);
assert.equal(decoded, 'foo barbaz quxabcdef');
});
test('should handle binary data', async () => {
let html = testStream(['<html><body><h1>Test</h1>', '<p>Hello world</p></body></html>']);
let rscStream = testStream(['foo bar', new Uint8Array([1, 2, 3, 4, 5, 0xe2, 0x28, 0xa1])]);
let injected = html.pipeThrough(injectRSCPayload(rscStream));
let result = await streamToString(injected);
assert.equal(result, '<html><body><h1>Test</h1><p>Hello world</p><script>(self.__FLIGHT_DATA||=[]).push("foo bar")</script><script>(self.__FLIGHT_DATA||=[]).push(Uint8Array.from(atob("AQIDBAXiKKE="), m => m.codePointAt(0)))</script></body></html>');
let clientStream = runScripts(result);
let data = new Uint8Array(0);
for await (let chunk of clientStream) {
let resized = new Uint8Array(data.length + chunk.length);
resized.set(data);
resized.set(chunk, data.length);
data = resized;
}
assert.deepEqual(data, new Uint8Array([
102, 111, 111, 32, 98, 97,
114, 1, 2, 3, 4, 5,
226, 40, 161
]));
});
test('should handle chunked data', async () => {
let resolve;
let html = testStream(['<html><body><h1>Test</h1>', () => new Promise(r => setTimeout(r, 3)), '<p>Hello', () => resolve(), ' world</p></body></html>']);
let rscStream = testStream(['foo bar', () => new Promise(r => resolve = r), 'baz qux', 'abcdef']);
let injected = html.pipeThrough(injectRSCPayload(rscStream));
let result = await streamToString(injected);
assert.equal(result, '<html><body><h1>Test</h1><script>(self.__FLIGHT_DATA||=[]).push("foo bar")</script><script>(self.__FLIGHT_DATA||=[]).push("baz qux")</script><script>(self.__FLIGHT_DATA||=[]).push("abcdef")</script><p>Hello world</p></body></html>');
let clientStream = runScripts(result);
let decoded = await streamToString(clientStream);
assert.equal(decoded, 'foo barbaz quxabcdef');
});