diff options
Diffstat (limited to 'test/bun.js/html-rewriter.test.js')
-rw-r--r-- | test/bun.js/html-rewriter.test.js | 293 |
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>", + "<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>" + ); + }); +}); |