Skip to content

Commit

Permalink
feat: Make DOM serializable by Jest
Browse files Browse the repository at this point in the history
Jest now properly renders the DOM in snapshots.
  • Loading branch information
fb55 committed Sep 25, 2020
1 parent 1edb2ab commit 448dadb
Show file tree
Hide file tree
Showing 3 changed files with 87 additions and 25 deletions.
87 changes: 64 additions & 23 deletions src/index.spec.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { readdirSync } from "fs";
import { resolve } from "path";
import { Parser } from "htmlparser2";
import Handler, { Node } from ".";
import { Parser, ParserOptions } from "htmlparser2";
import Handler, { Node, DomHandlerOptions } from ".";

const basePath = resolve(__dirname, "__fixtures__");

Expand All @@ -12,33 +12,74 @@ describe("DomHandler", () => {
.map(require)
.forEach((fixture) =>
test(fixture.name, () => {
const { expected, html: data } = fixture;

const handler = new Handler(
(err: Error | null, actual: Node[]) => {
expect(err).toBeNull();
compare(actual, expected);
},
fixture.options
const result = parse(
fixture.html,
fixture.options,
fixture.streaming
);

const parser = new Parser(handler, fixture.options);

// First, try to run the fixture via chunks
if (fixture.streaming || fixture.streaming === undefined) {
for (let i = 0; i < data.length; i++) {
parser.write(data.charAt(i));
}

parser.done();
}

// Then parse everything
parser.parseComplete(data);
compare(result, fixture.expected);
})
);

it("Should serialize to Jest snapshot", () => {
const result = parse(
"<html><!-- A Comment --><title>The Title</title><body>Hello world<input disabled type=text></body></html>"
);
expect(result).toMatchInlineSnapshot(`
Array [
<html>
<!-- A Comment -->
<title>
The Title
</title>
<body>
Hello world
<input
disabled=""
type="text"
/>
</body>
</html>,
]
`);
});
});

function parse(
data: string,
options: DomHandlerOptions & ParserOptions = {},
streaming = true
): Node[] {
const results: Node[][] = [];

const handler = new Handler((err: Error | null, actual: Node[]) => {
expect(err).toBeNull();
results.push(actual);
}, options);

const parser = new Parser(handler, options);

// First, try to run the fixture via chunks
if (streaming) {
for (let i = 0; i < data.length; i++) {
parser.write(data.charAt(i));
}

parser.done();
}

// Then parse everything
parser.parseComplete(data);

if (streaming) {
// Ensure streaming doesn't change anything.
expect(results[0]).toEqual(results[1]);
}

return results[0];
}

function compare<T>(actual: T, expected: T) {
expect(typeof actual).toBe(typeof expected);
if (typeof expected !== "object" || expected === null) {
Expand Down
6 changes: 4 additions & 2 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import {
Node,
Element,
DataNode,
Text,
Comment,
NodeWithChildren,
ProcessingInstruction,
} from "./node";
Expand Down Expand Up @@ -172,7 +174,7 @@ export class DomHandler {
data = data.replace(reWhitespace, " ");
}

const node = new DataNode(ElementType.Text, data);
const node = new Text(data);
this.addNode(node);
this._lastNode = node;
}
Expand All @@ -184,7 +186,7 @@ export class DomHandler {
return;
}

const node = new DataNode(ElementType.Comment, data);
const node = new Comment(data);
this.addNode(node);
this._lastNode = node;
}
Expand Down
19 changes: 19 additions & 0 deletions src/node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,18 @@ export class DataNode extends Node {
}
}

export class Text extends DataNode {
constructor(data: string) {
super(ElementType.Text, data);
}
}

export class Comment extends DataNode {
constructor(data: string) {
super(ElementType.Comment, data);
}
}

export class ProcessingInstruction extends DataNode {
constructor(public name: string, data: string) {
super(ElementType.Directive, data);
Expand Down Expand Up @@ -161,4 +173,11 @@ export class Element extends NodeWithChildren {
set tagName(name: string) {
this.name = name;
}

get attributes(): { name: string; value: string }[] {
return Object.keys(this.attribs).map((name) => ({
name,
value: this.attribs[name],
}));
}
}

0 comments on commit 448dadb

Please sign in to comment.