mirror of
https://github.com/oven-sh/bun
synced 2026-02-09 18:38:55 +00:00
* Add bun-types to packages * Improve typing * Fix types in tests * Fix dts tests * Run formatter * Fix all type errors * Add strict mode, fix type errors * Add ffi changes * Move workflows to root * Add workflows * Remove labeler * Add child_process types * Fix synthetic defaults issue * Remove docs * Move scripts * Run prettier * Include examples in typechecking * captureStackTrace types * moved captureStackTrace types to globals * Address reviews Co-authored-by: Colin McDonnell <colinmcd@alum.mit.edu> Co-authored-by: Dylan Conway <dylan.conway567@gmail.com>
294 lines
9.2 KiB
JavaScript
294 lines
9.2 KiB
JavaScript
import { describe, it, expect } from "bun:test";
|
|
import { gcTick } from "./gc";
|
|
|
|
var setTimeoutAsync = (fn, delay) => {
|
|
return new Promise((resolve, reject) => {
|
|
setTimeout(() => {
|
|
try {
|
|
resolve(fn());
|
|
} catch (e) {
|
|
reject(e);
|
|
}
|
|
}, delay);
|
|
});
|
|
};
|
|
|
|
describe("HTMLRewriter", () => {
|
|
it("HTMLRewriter: async replacement", async () => {
|
|
await gcTick();
|
|
const res = new HTMLRewriter()
|
|
.on("div", {
|
|
async element(element) {
|
|
await setTimeoutAsync(() => {
|
|
element.setInnerContent("<span>replace</span>", { html: true });
|
|
}, 5);
|
|
},
|
|
})
|
|
.transform(new Response("<div>example.com</div>"));
|
|
await gcTick();
|
|
expect(await res.text()).toBe("<div><span>replace</span></div>");
|
|
await gcTick();
|
|
});
|
|
|
|
it("supports element handlers", async () => {
|
|
var rewriter = new HTMLRewriter();
|
|
rewriter.on("div", {
|
|
element(element) {
|
|
element.setInnerContent("<blink>it worked!</blink>", { html: true });
|
|
},
|
|
});
|
|
var input = new Response("<div>hello</div>");
|
|
var output = rewriter.transform(input);
|
|
expect(await output.text()).toBe("<div><blink>it worked!</blink></div>");
|
|
});
|
|
|
|
it("(from file) supports element handlers", async () => {
|
|
var rewriter = new HTMLRewriter();
|
|
rewriter.on("div", {
|
|
element(element) {
|
|
element.setInnerContent("<blink>it worked!</blink>", { html: true });
|
|
},
|
|
});
|
|
await Bun.write("/tmp/html-rewriter.txt.js", "<div>hello</div>");
|
|
var input = new Response(Bun.file("/tmp/html-rewriter.txt.js"));
|
|
var output = rewriter.transform(input);
|
|
expect(await output.text()).toBe("<div><blink>it worked!</blink></div>");
|
|
});
|
|
|
|
it("supports attribute iterator", async () => {
|
|
var rewriter = new HTMLRewriter();
|
|
var expected = [
|
|
["first", ""],
|
|
["second", "alrihgt"],
|
|
["third", "123"],
|
|
["fourth", "5"],
|
|
["fifth", "helloooo"],
|
|
];
|
|
rewriter.on("div", {
|
|
element(element2) {
|
|
for (let attr of element2.attributes) {
|
|
const stack = expected.shift();
|
|
expect(stack[0]).toBe(attr[0]);
|
|
expect(stack[1]).toBe(attr[1]);
|
|
}
|
|
},
|
|
});
|
|
var input = new Response(
|
|
'<div first second="alrihgt" third="123" fourth=5 fifth=helloooo>hello</div>',
|
|
);
|
|
var output = rewriter.transform(input);
|
|
expect(await output.text()).toBe(
|
|
'<div first second="alrihgt" third="123" fourth=5 fifth=helloooo>hello</div>',
|
|
);
|
|
expect(expected.length).toBe(0);
|
|
});
|
|
|
|
it("handles element specific mutations", async () => {
|
|
// prepend/append
|
|
let res = new HTMLRewriter()
|
|
.on("p", {
|
|
element(element) {
|
|
element.prepend("<span>prepend</span>");
|
|
element.prepend("<span>prepend html</span>", { html: true });
|
|
element.append("<span>append</span>");
|
|
element.append("<span>append html</span>", { html: true });
|
|
},
|
|
})
|
|
.transform(new Response("<p>test</p>"));
|
|
expect(await res.text()).toBe(
|
|
[
|
|
"<p>",
|
|
"<span>prepend html</span>",
|
|
"<span>prepend</span>",
|
|
"test",
|
|
"<span>append</span>",
|
|
"<span>append html</span>",
|
|
"</p>",
|
|
].join(""),
|
|
);
|
|
|
|
// setInnerContent
|
|
res = new HTMLRewriter()
|
|
.on("p", {
|
|
element(element) {
|
|
element.setInnerContent("<span>replace</span>");
|
|
},
|
|
})
|
|
.transform(new Response("<p>test</p>"));
|
|
expect(await res.text()).toBe("<p><span>replace</span></p>");
|
|
res = new HTMLRewriter()
|
|
.on("p", {
|
|
element(element) {
|
|
element.setInnerContent("<span>replace</span>", { html: true });
|
|
},
|
|
})
|
|
.transform(new Response("<p>test</p>"));
|
|
expect(await res.text()).toBe("<p><span>replace</span></p>");
|
|
|
|
// removeAndKeepContent
|
|
res = new HTMLRewriter()
|
|
.on("p", {
|
|
element(element) {
|
|
element.removeAndKeepContent();
|
|
},
|
|
})
|
|
.transform(new Response("<p>test</p>"));
|
|
expect(await res.text()).toBe("test");
|
|
});
|
|
|
|
it("handles element class properties", async () => {
|
|
class Handler {
|
|
constructor(content) {
|
|
this.content = content;
|
|
}
|
|
|
|
// noinspection JSUnusedGlobalSymbols
|
|
element(element) {
|
|
element.setInnerContent(this.content);
|
|
}
|
|
}
|
|
const res = new HTMLRewriter()
|
|
.on("p", new Handler("new"))
|
|
.transform(new Response("<p>test</p>"));
|
|
expect(await res.text()).toBe("<p>new</p>");
|
|
});
|
|
|
|
const commentsMutationsInput = "<p><!--test--></p>";
|
|
const commentsMutationsExpected = {
|
|
beforeAfter: [
|
|
"<p>",
|
|
"<span>before</span>",
|
|
"<span>before html</span>",
|
|
"<!--test-->",
|
|
"<span>after html</span>",
|
|
"<span>after</span>",
|
|
"</p>",
|
|
].join(""),
|
|
replace: "<p><span>replace</span></p>",
|
|
replaceHtml: "<p><span>replace</span></p>",
|
|
remove: "<p></p>",
|
|
};
|
|
|
|
const commentPropertiesMacro = async (func) => {
|
|
const res = func(new HTMLRewriter(), (comment) => {
|
|
expect(comment.removed).toBe(false);
|
|
expect(comment.text).toBe("test");
|
|
comment.text = "new";
|
|
expect(comment.text).toBe("new");
|
|
}).transform(new Response("<p><!--test--></p>"));
|
|
expect(await res.text()).toBe("<p><!--new--></p>");
|
|
};
|
|
|
|
it("HTMLRewriter: handles comment properties", () =>
|
|
commentPropertiesMacro((rw, comments) => {
|
|
rw.on("p", { comments });
|
|
return rw;
|
|
}));
|
|
|
|
it("selector tests", async () => {
|
|
const checkSelector = async (selector, input, expected) => {
|
|
const res = new HTMLRewriter()
|
|
.on(selector, {
|
|
element(element) {
|
|
element.setInnerContent("new");
|
|
},
|
|
})
|
|
.transform(new Response(input));
|
|
expect(await res.text()).toBe(expected);
|
|
};
|
|
|
|
await checkSelector("*", "<h1>1</h1><p>2</p>", "<h1>new</h1><p>new</p>");
|
|
await checkSelector("p", "<h1>1</h1><p>2</p>", "<h1>1</h1><p>new</p>");
|
|
await checkSelector(
|
|
"p:nth-child(2)",
|
|
"<div><p>1</p><p>2</p><p>3</p></div>",
|
|
"<div><p>1</p><p>new</p><p>3</p></div>",
|
|
);
|
|
await checkSelector(
|
|
"p:first-child",
|
|
"<div><p>1</p><p>2</p><p>3</p></div>",
|
|
"<div><p>new</p><p>2</p><p>3</p></div>",
|
|
);
|
|
await checkSelector(
|
|
"p:nth-of-type(2)",
|
|
"<div><p>1</p><h1>2</h1><p>3</p><h1>4</h1><p>5</p></div>",
|
|
"<div><p>1</p><h1>2</h1><p>new</p><h1>4</h1><p>5</p></div>",
|
|
);
|
|
await checkSelector(
|
|
"p:first-of-type",
|
|
"<div><h1>1</h1><p>2</p><p>3</p></div>",
|
|
"<div><h1>1</h1><p>new</p><p>3</p></div>",
|
|
);
|
|
await checkSelector(
|
|
"p:not(:first-child)",
|
|
"<div><p>1</p><p>2</p><p>3</p></div>",
|
|
"<div><p>1</p><p>new</p><p>new</p></div>",
|
|
);
|
|
await checkSelector(
|
|
"p.red",
|
|
'<p class="red">1</p><p>2</p>',
|
|
'<p class="red">new</p><p>2</p>',
|
|
);
|
|
await checkSelector(
|
|
"h1#header",
|
|
'<h1 id="header">1</h1><h1>2</h1>',
|
|
'<h1 id="header">new</h1><h1>2</h1>',
|
|
);
|
|
await checkSelector(
|
|
"p[data-test]",
|
|
"<p data-test>1</p><p>2</p>",
|
|
"<p data-test>new</p><p>2</p>",
|
|
);
|
|
await checkSelector(
|
|
'p[data-test="one"]',
|
|
'<p data-test="one">1</p><p data-test="two">2</p>',
|
|
'<p data-test="one">new</p><p data-test="two">2</p>',
|
|
);
|
|
await checkSelector(
|
|
'p[data-test="one" i]',
|
|
'<p data-test="one">1</p><p data-test="OnE">2</p><p data-test="two">3</p>',
|
|
'<p data-test="one">new</p><p data-test="OnE">new</p><p data-test="two">3</p>',
|
|
);
|
|
await checkSelector(
|
|
'p[data-test="one" s]',
|
|
'<p data-test="one">1</p><p data-test="OnE">2</p><p data-test="two">3</p>',
|
|
'<p data-test="one">new</p><p data-test="OnE">2</p><p data-test="two">3</p>',
|
|
);
|
|
await checkSelector(
|
|
'p[data-test~="two"]',
|
|
'<p data-test="one two three">1</p><p data-test="one two">2</p><p data-test="one">3</p>',
|
|
'<p data-test="one two three">new</p><p data-test="one two">new</p><p data-test="one">3</p>',
|
|
);
|
|
await checkSelector(
|
|
'p[data-test^="a"]',
|
|
'<p data-test="a1">1</p><p data-test="a2">2</p><p data-test="b1">3</p>',
|
|
'<p data-test="a1">new</p><p data-test="a2">new</p><p data-test="b1">3</p>',
|
|
);
|
|
await checkSelector(
|
|
'p[data-test$="1"]',
|
|
'<p data-test="a1">1</p><p data-test="a2">2</p><p data-test="b1">3</p>',
|
|
'<p data-test="a1">new</p><p data-test="a2">2</p><p data-test="b1">new</p>',
|
|
);
|
|
await checkSelector(
|
|
'p[data-test*="b"]',
|
|
'<p data-test="abc">1</p><p data-test="ab">2</p><p data-test="a">3</p>',
|
|
'<p data-test="abc">new</p><p data-test="ab">new</p><p data-test="a">3</p>',
|
|
);
|
|
await checkSelector(
|
|
'p[data-test|="a"]',
|
|
'<p data-test="a">1</p><p data-test="a-1">2</p><p data-test="a2">3</p>',
|
|
'<p data-test="a">new</p><p data-test="a-1">new</p><p data-test="a2">3</p>',
|
|
);
|
|
await checkSelector(
|
|
"div span",
|
|
"<div><h1><span>1</span></h1><span>2</span><b>3</b></div>",
|
|
"<div><h1><span>new</span></h1><span>new</span><b>3</b></div>",
|
|
);
|
|
await checkSelector(
|
|
"div > span",
|
|
"<div><h1><span>1</span></h1><span>2</span><b>3</b></div>",
|
|
"<div><h1><span>1</span></h1><span>new</span><b>3</b></div>",
|
|
);
|
|
});
|
|
});
|