import { describe, it, expect, vi } from "vitest"; import { Parser, type ParserOptions } from "./Parser.js"; import * as helper from "./__fixtures__/testHelper.js"; /** * Write to the parser twice, once a bytes, once as a single blob. Then check * that we received the expected events. * * @internal * @param input Data to write. * @param options Parser options. * @returns Promise that resolves if the test passes. */ function runTest(input: string, options?: ParserOptions) { let firstResult: unknown[] | undefined; return new Promise((resolve, reject) => { const handler = helper.getEventCollector((error, actual) => { if (error) { return reject(error); } if (firstResult) { expect(actual).toEqual(firstResult); resolve(); } else { firstResult = actual; expect(actual).toMatchSnapshot(); } }); const parser = new Parser(handler, options); // First, try to run the test via chunks for (let index = 0; index < input.length; index++) { parser.write(input.charAt(index)); } parser.end(); // Then, parse everything parser.parseComplete(input); }); } describe("Events", () => { it("simple", () => runTest("

adsf

")); it("Template script tags", () => runTest( '

', )); it("Lowercase tags", () => runTest("

adsf

", { lowerCaseTags: true })); it("CDATA", () => runTest("<> fo]]>", { xmlMode: true, })); it("CDATA (inside special)", () => runTest( "", )); it("leading lt", () => runTest(">a>")); it("end slash: void element ending with />", () => runTest("

Hold the line.")); it("end slash: void element ending with >", () => runTest("


Hold the line.")); it("end slash: void element ending with >, xmlMode=true", () => runTest("


Hold the line.", { xmlMode: true })); it("end slash: non-void element ending with />", () => runTest("

Hold the line.")); it("end slash: non-void element ending with />, xmlMode=true", () => runTest("

Hold the line.", { xmlMode: true })); it("end slash: non-void element ending with />, recognizeSelfClosing=true", () => runTest("

Hold the line.", { recognizeSelfClosing: true })); it("end slash: as part of attrib value of void element", () => runTest("

Hold the line.")); it("end slash: as part of attrib value of non-void element", () => runTest("Foo

Hold the line.")); it("Implicit close tags", () => runTest( "

  1. TH

    Heading

    Div
    Div2
  2. Heading 2

Para

Heading 4

", )); it("attributes (no white space, no value, no quotes)", () => runTest( '', )); it("crazy attribute", () => runTest("

stuff

runTest("

")); it("Long comment ending", () => runTest("")); it("Long CDATA ending", () => runTest("", { xmlMode: true, })); it("Implicit open p and br tags", () => runTest("
Hallo

World


")); it("lt followed by whitespace", () => runTest("a < b")); it("double attribute", () => runTest("

")); it("numeric entities", () => runTest("abcdfg&#x;h")); it("legacy entities", () => runTest("&elíe&eer;s<er&sum")); it("named entities", () => runTest("&el<er∳foo&bar")); it("xml entities", () => runTest("&>&<üabcde", { xmlMode: true, })); it("entity in attribute", () => runTest( "", )); it("double brackets", () => runTest("<>testing")); it("legacy entities fail", () => runTest("M&M")); it("Special special tags", () => runTest( "<b>foo</b><title>", )); it("Empty tag name", () => runTest("< >")); it("Not quite closed", () => runTest("")); it("Entities in attributes", () => runTest("")); it("CDATA in HTML", () => runTest("")); it("Comment edge-cases", () => runTest("")); it("Scripts ending with <", () => runTest("")); it("CDATA more edge-cases", () => runTest("baz]]>", { recognizeCDATA: true })); it("tag names are not ASCII alpha", () => runTest("<12>text")); it("open-implies-close case of (non-br) void close tag in non-XML mode", () => runTest("", { lowerCaseAttributeNames: true })); it("entity in attribute (#276)", () => runTest( '?&image_uri=1&ℑ=2&image=3', )); it("entity in title (#592)", () => runTest("the "title"")); it("entity in title - decodeEntities=false (#592)", () => runTest("<title>the "title"", { decodeEntities: false })); it(" in ")); it("XML tags", () => runTest("<:foo><_bar>", { xmlMode: true })); it("Trailing legacy entity", () => runTest("⨱×bar")); it("Trailing numeric entity", () => runTest("55")); it("Multi-byte entity", () => runTest("≧̸")); it("Start & end indices from domhandler", () => runTest( " The Title Hello world

", )); it("Self-closing indices (#941)", () => runTest("
", { xmlMode: true })); it("Entity after <", () => runTest("<&")); it("Attribute in XML (see #1350)", () => runTest( '', { xmlMode: true }, )); }); describe("Helper", () => { it("should handle errors", () => { const eventCallback = vi.fn(); const parser = new Parser(helper.getEventCollector(eventCallback)); parser.end(); parser.write("foo"); expect(eventCallback).toHaveBeenCalledTimes(2); expect(eventCallback).toHaveBeenNthCalledWith(1, null, []); expect(eventCallback).toHaveBeenLastCalledWith( new Error(".write() after done!"), ); }); });