From d6831cf801a0c9e1e3f60d9c8182636cc6567d3a Mon Sep 17 00:00:00 2001 From: Jarred Sumner Date: Sat, 12 Mar 2022 01:14:45 -0800 Subject: [Bun.js] Implement `HTMLRewriter` https://developers.cloudflare.com/workers/runtime-apis/html-rewriter --- .../bunjs-only-snippets/html-rewriter.test.js | 228 ++++++++++++++++++++- 1 file changed, 226 insertions(+), 2 deletions(-) (limited to 'integration/bunjs-only-snippets') diff --git a/integration/bunjs-only-snippets/html-rewriter.test.js b/integration/bunjs-only-snippets/html-rewriter.test.js index cd4fad746..77c14e479 100644 --- a/integration/bunjs-only-snippets/html-rewriter.test.js +++ b/integration/bunjs-only-snippets/html-rewriter.test.js @@ -1,8 +1,232 @@ import { describe, it, expect } from "bun:test"; describe("HTMLRewriter", () => { - it("exists globally", () => { + it("exists globally", async () => { expect(typeof HTMLRewriter).toBe("function"); - console.log(HTMLRewriter.name); + expect(typeof HTMLRewriter.constructor).toBe("function"); + }); + it("supports element handlers", async () => { + var rewriter = new HTMLRewriter(); + rewriter.on("div", { + element(element) { + element.setInnerContent("it worked!", { html: true }); + }, + }); + var input = new Response("
hello
"); + var output = rewriter.transform(input); + expect(await output.text()).toBe("
it worked!
"); + }); + + it("handles element specific mutations", async () => { + // prepend/append + let res = new HTMLRewriter() + .on("p", { + element(element) { + element.prepend("prepend"); + element.prepend("prepend html", { html: true }); + element.append("append"); + element.append("append html", { html: true }); + }, + }) + .transform(new Response("

test

")); + expect(await res.text()).toBe( + [ + "

", + "prepend html", + "<span>prepend</span>", + "test", + "<span>append</span>", + "append html", + "

", + ].join("") + ); + + // setInnerContent + res = new HTMLRewriter() + .on("p", { + element(element) { + element.setInnerContent("replace"); + }, + }) + .transform(new Response("

test

")); + expect(await res.text()).toBe("

<span>replace</span>

"); + res = new HTMLRewriter() + .on("p", { + element(element) { + element.setInnerContent("replace", { html: true }); + }, + }) + .transform(new Response("

test

")); + expect(await res.text()).toBe("

replace

"); + + // removeAndKeepContent + res = new HTMLRewriter() + .on("p", { + element(element) { + element.removeAndKeepContent(); + }, + }) + .transform(new Response("

test

")); + 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("

test

")); + expect(await res.text()).toBe("

new

"); + }); + + const commentsMutationsInput = "

"; + const commentsMutationsExpected = { + beforeAfter: [ + "

", + "<span>before</span>", + "before html", + "", + "after html", + "<span>after</span>", + "

", + ].join(""), + replace: "

<span>replace</span>

", + replaceHtml: "

replace

", + remove: "

", + }; + + const commentPropertiesMacro = async (t, func) => { + const res = func(new HTMLRewriter(), (comment) => { + expect(comment.removed).toBe(false); + expect(comment.text).toBe("test"); + comment.text = "new"; + }).transform(new Response("

")); + t.is(await res.text(), "

"); + }; + test( + "HTMLRewriter: handles comment properties", + commentPropertiesMacro, + (rw, comments) => rw.on("p", { comments }) + ); + test( + "HTMLRewriter: handles comment mutations", + mutationsMacro, + (rw, comments) => rw.on("p", { comments }), + commentsMutationsInput, + commentsMutationsExpected + ); + + 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("*", "

1

2

", "

new

new

"); + await checkSelector("p", "

1

2

", "

1

new

"); + await checkSelector( + "p:nth-child(2)", + "

1

2

3

", + "

1

new

3

" + ); + await checkSelector( + "p:first-child", + "

1

2

3

", + "

new

2

3

" + ); + await checkSelector( + "p:nth-of-type(2)", + "

1

2

3

4

5

", + "

1

2

new

4

5

" + ); + await checkSelector( + "p:first-of-type", + "

1

2

3

", + "

1

new

3

" + ); + await checkSelector( + "p:not(:first-child)", + "

1

2

3

", + "

1

new

new

" + ); + await checkSelector( + "p.red", + '

1

2

', + '

new

2

' + ); + await checkSelector( + "h1#header", + '

1

2

', + '

new

2

' + ); + await checkSelector( + "p[data-test]", + "

1

2

", + "

new

2

" + ); + await checkSelector( + 'p[data-test="one"]', + '

1

2

', + '

new

2

' + ); + await checkSelector( + 'p[data-test="one" i]', + '

1

2

3

', + '

new

new

3

' + ); + await checkSelector( + 'p[data-test="one" s]', + '

1

2

3

', + '

new

2

3

' + ); + await checkSelector( + 'p[data-test~="two"]', + '

1

2

3

', + '

new

new

3

' + ); + await checkSelector( + 'p[data-test^="a"]', + '

1

2

3

', + '

new

new

3

' + ); + await checkSelector( + 'p[data-test$="1"]', + '

1

2

3

', + '

new

2

new

' + ); + await checkSelector( + 'p[data-test*="b"]', + '

1

2

3

', + '

new

new

3

' + ); + await checkSelector( + 'p[data-test|="a"]', + '

1

2

3

', + '

new

new

3

' + ); + await checkSelector( + "div span", + "

1

23
", + "

new

new3
" + ); + await checkSelector( + "div > span", + "

1

23
", + "

1

new3
" + ); }); }); -- cgit v1.2.3