aboutsummaryrefslogtreecommitdiff
path: root/test/bun.js/html-rewriter.test.js
diff options
context:
space:
mode:
Diffstat (limited to 'test/bun.js/html-rewriter.test.js')
-rw-r--r--test/bun.js/html-rewriter.test.js293
1 files changed, 293 insertions, 0 deletions
diff --git a/test/bun.js/html-rewriter.test.js b/test/bun.js/html-rewriter.test.js
new file mode 100644
index 000000000..29b765c2f
--- /dev/null
+++ b/test/bun.js/html-rewriter.test.js
@@ -0,0 +1,293 @@
+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>",
+ "&lt;span&gt;prepend&lt;/span&gt;",
+ "test",
+ "&lt;span&gt;append&lt;/span&gt;",
+ "<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>&lt;span&gt;replace&lt;/span&gt;</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>",
+ "&lt;span&gt;before&lt;/span&gt;",
+ "<span>before html</span>",
+ "<!--test-->",
+ "<span>after html</span>",
+ "&lt;span&gt;after&lt;/span&gt;",
+ "</p>",
+ ].join(""),
+ replace: "<p>&lt;span&gt;replace&lt;/span&gt;</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>"
+ );
+ });
+});