-
Notifications
You must be signed in to change notification settings - Fork 17
/
Copy pathmisc.spec.ts
180 lines (160 loc) · 4.7 KB
/
misc.spec.ts
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
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
import { TextEncoder, TextDecoder } from "util";
import vm from "vm";
import test from "ava";
import { HTMLRewriter as RawHTMLRewriter, ElementHandlers } from "..";
import { HTMLRewriter, wait } from ".";
test("handles multiple element handlers", async (t) => {
const res = await new HTMLRewriter()
.on("h1", {
element(element) {
element.setInnerContent("new h1");
},
})
.on("h2", {
element(element) {
element.setInnerContent("new h2");
},
})
.on("p", {
element(element) {
element.setInnerContent("new p");
},
})
.transform("<h1>old h1</h1><h2>old h2</h2><p>old p</p>");
t.is(res, "<h1>new h1</h1><h2>new h2</h2><p>new p</p>");
});
test("handles streaming", async (t) => {
t.plan(8); // 6 for text handler + 2 at the end
const expectedTextChunks = ["te", "st", ""];
const outputChunks: string[] = [];
const decoder = new TextDecoder();
const rewriter = new RawHTMLRewriter((chunk) =>
outputChunks.push(decoder.decode(chunk))
).on("p", {
text(text) {
t.is(text.text, expectedTextChunks.shift());
t.is(text.lastInTextNode, text.text === "");
},
});
const inputChunks = [
'<html lang="en">',
"<bo",
"dy>",
"<p>",
"te",
"st",
"</p></body>",
"</html>",
];
const encoder = new TextEncoder();
for (const chunk of inputChunks) {
await rewriter.write(encoder.encode(chunk));
await wait(50);
}
await rewriter.end();
t.true(outputChunks.length >= 2);
t.is(
outputChunks.join(""),
'<html lang="en"><body><p>test</p></body></html>'
);
});
test("handles empty chunk", async (t) => {
const res = await new HTMLRewriter().transform("");
t.is(res, "");
});
test("rethrows error thrown in handler", async (t) => {
const rewriter = new RawHTMLRewriter(() => {}).on("p", {
element() {
throw new Error("Whoops!");
},
});
const promise = rewriter.write(new TextEncoder().encode("<p>test</p>"));
await t.throwsAsync(promise, { message: "Whoops!" });
});
test("rethrows error thrown in async handler", async (t) => {
const rewriter = new RawHTMLRewriter(() => {}).on("p", {
async element() {
throw new Error("Whoops!");
},
});
const promise = rewriter.write(new TextEncoder().encode("<p>test</p>"));
await t.throwsAsync(promise, { message: "Whoops!" });
});
test.serial("handles concurrent rewriters with async handlers", async (t) => {
// Note this test requires the "safe" HTMLRewriter, see comments in
// src/modules/rewriter.ts for more details
const rewriter = (i: number) =>
new HTMLRewriter()
.on("p", {
async element(element) {
await wait(50);
element.setInnerContent(`new ${i}`);
},
})
.transform(`<p>old ${i}</p>`);
const res1 = rewriter(1);
const res2 = rewriter(2);
t.is(await res1, "<p>new 1</p>");
t.is(await res2, "<p>new 2</p>");
const res3 = rewriter(3);
const res4 = rewriter(4);
const texts = await Promise.all([res3, res4]);
t.deepEqual(texts, ["<p>new 3</p>", "<p>new 4</p>"]);
});
test.serial("handles many async handlers for single chunk write", async (t) => {
const rewriter = new HTMLRewriter();
rewriter.on("h1", {
async element(element) {
await wait(50);
element.setInnerContent("new h1");
},
});
rewriter.on("p", {
async element(element) {
await wait(50);
element.setInnerContent("new p");
},
});
const res = await rewriter.transform("<h1>old h1</h1><p>old p</p>");
t.is(res, "<h1>new h1</h1><p>new p</p>");
});
test("rewriter allows chaining", (t) => {
const rewriter = new RawHTMLRewriter(() => {});
t.is(rewriter.on("p", {}), rewriter);
t.is(rewriter.onDocument({}), rewriter);
});
test.serial("handles async handler in different realm", async (t) => {
const context = vm.createContext({ HTMLRewriter, wait });
const res = await vm.runInContext(
`
const rewriter = new HTMLRewriter();
rewriter.on("p", {
async element(element) {
await wait(50);
element.setInnerContent("new");
},
});
rewriter.transform("<p>old</p>");
`,
context
);
t.is(res, "<p>new</p>");
});
test("treats esi tags as void tags if option enabled", async (t) => {
const handlers: ElementHandlers = {
element(element) {
element.replace("replacement");
},
};
const input = '<span><esi:include src="a" /> text<span>';
// Check with option disabled
let res = await new HTMLRewriter()
.on("esi\\:include", handlers)
.transform(input);
t.is(res, "<span>replacement");
// Check with option enabled
res = await new HTMLRewriter({ enableEsiTags: true })
.on("esi\\:include", handlers)
.transform(input);
t.is(res, "<span>replacement text<span>");
});