Skip to content

Commit

Permalink
refactor: Make location info parser tests work (#440)
Browse files Browse the repository at this point in the history
  • Loading branch information
fb55 committed Mar 7, 2022
1 parent 1525329 commit a2c7240
Show file tree
Hide file tree
Showing 4 changed files with 49 additions and 44 deletions.
7 changes: 1 addition & 6 deletions packages/parse5-htmlparser2-tree-adapter/lib/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,12 +104,7 @@ export function getTemplateContent(templateElement: Element): Document {
return templateElement.children[0] as Document;
}

export function setDocumentType(
document: Document,
name: string | null,
publicId: string | null,
systemId: string | null
): void {
export function setDocumentType(document: Document, name: string, publicId: string, systemId: string): void {
const data = doctype.serializeContent(name, publicId, systemId);
let doctypeNode = document.children.find(
(node): node is ProcessingInstruction => isDirective(node) && node.name === '!doctype'
Expand Down
4 changes: 2 additions & 2 deletions packages/parse5/lib/common/doctype.ts
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ export function getDocumentMode(token: DoctypeToken): DOCUMENT_MODE {
return DOCUMENT_MODE.NO_QUIRKS;
}

export function serializeContent(name: string | null, publicId: string | null, systemId: string | null): string {
export function serializeContent(name: string, publicId: string, systemId: string): string {
let str = '!DOCTYPE ';

if (name) {
Expand All @@ -153,7 +153,7 @@ export function serializeContent(name: string | null, publicId: string | null, s
str += ' SYSTEM';
}

if (systemId !== null) {
if (systemId) {
str += ` ${enquoteDoctypeId(systemId)}`;
}

Expand Down
31 changes: 20 additions & 11 deletions packages/parse5/lib/serializer/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,11 +106,11 @@ export function serialize<T extends TreeAdapterTypeMap = DefaultTreeAdapter.Defa
* @param options Serialization options.
*/
export function serializeOuter<T extends TreeAdapterTypeMap = DefaultTreeAdapter.DefaultTreeAdapterMap>(
node: T['element'],
node: T['node'],
options?: SerializerOptions<T>
): string {
const opts = { ...defaultOpts, ...options };
return serializeElement(node, opts);
return serializeNode(node, opts);
}

function serializeChildNodes<T extends TreeAdapterTypeMap>(
Expand All @@ -129,21 +129,30 @@ function serializeChildNodes<T extends TreeAdapterTypeMap>(

if (childNodes) {
for (const currentNode of childNodes) {
if (options.treeAdapter.isElementNode(currentNode)) {
html += serializeElement(currentNode, options);
} else if (options.treeAdapter.isTextNode(currentNode)) {
html += serializeTextNode(currentNode, options);
} else if (options.treeAdapter.isCommentNode(currentNode)) {
html += serializeCommentNode(currentNode, options);
} else if (options.treeAdapter.isDocumentTypeNode(currentNode)) {
html += serializeDocumentTypeNode(currentNode, options);
}
html += serializeNode(currentNode, options);
}
}

return html;
}

function serializeNode<T extends TreeAdapterTypeMap>(node: T['node'], options: InternalOptions<T>): string {
if (options.treeAdapter.isElementNode(node)) {
return serializeElement(node, options);
}
if (options.treeAdapter.isTextNode(node)) {
return serializeTextNode(node, options);
}
if (options.treeAdapter.isCommentNode(node)) {
return serializeCommentNode(node, options);
}
if (options.treeAdapter.isDocumentTypeNode(node)) {
return serializeDocumentTypeNode(node, options);
}
// Return an empty string for unknown nodes
return '';
}

function serializeElement<T extends TreeAdapterTypeMap>(node: T['element'], options: InternalOptions<T>): string {
const tn = options.treeAdapter.getTagName(node);

Expand Down
51 changes: 26 additions & 25 deletions test/utils/generate-location-info-parser-tests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import { TreeAdapter, TreeAdapterTypeMap } from 'parse5/dist/tree-adapters/inter
import * as assert from 'node:assert';
import * as fs from 'node:fs';
import * as path from 'node:path';
import { escapeString } from 'parse5/dist/serializer/index.js';
import * as parse5 from 'parse5/dist/index.js';
import {
removeNewLines,
Expand All @@ -13,22 +12,19 @@ import {
normalizeNewLine,
generateTestsForEachTreeAdapter,
} from './common.js';
import * as doctype from 'parse5/dist/common/doctype.js';

function walkTree<T extends TreeAdapterTypeMap>(
document: T['document'],
parent: T['parentNode'],
treeAdapter: TreeAdapter<T>,
handler: (node: T['node']) => void
): void {
const stack = [...treeAdapter.getChildNodes(document)];
let node;
while ((node = stack.shift())) {
const children = treeAdapter.getChildNodes(node);
for (const node of treeAdapter.getChildNodes(parent)) {
if (treeAdapter.isElementNode(node)) {
walkTree(node, treeAdapter, handler);
}

handler(node);

if (children?.length) {
stack.unshift(...children);
}
}
}

Expand Down Expand Up @@ -75,7 +71,10 @@ function assertAttrsLocation(location: ElementLocation, serializedNode: string,
assert.ok(location.attrs, 'Expected attrs to be defined');

for (const attr of Object.values(location.attrs)) {
const expected = serializedNode.slice(attr.startOffset, attr.endOffset);
const expected = serializedNode.slice(
attr.startOffset - location.startOffset,
attr.endOffset - location.startOffset
);

assertLocation(attr, expected, html, lines);
}
Expand Down Expand Up @@ -113,42 +112,44 @@ export function generateLocationInfoParserTests(
//Then for each node in the tree we run the serializer and compare results with the substring
//obtained via the location info from the expected serialization results.
it(`Location info (Parser) - ${test.name}`, async () => {
const serializerOpts = { treeAdapter };
const html = escapeString(test.data);
const html = test.data;
const lines = html.split(/\r?\n/g);

const parserOpts = {
treeAdapter,
sourceCodeLocationInfo: true,
};

const parsingResult = await parse(html, parserOpts);
const parsingResult = parse(html, parserOpts);
const document = parsingResult.node;

walkTree(document, treeAdapter, (node) => {
const location = treeAdapter.getNodeSourceCodeLocation(node);

if (location) {
const fragment = treeAdapter.createDocumentFragment();
assert.ok(location);

treeAdapter.appendChild(fragment, node);
const serializedNode = treeAdapter.isDocumentTypeNode(node)
? `<${doctype.serializeContent(
treeAdapter.getDocumentTypeNodeName(node),
treeAdapter.getDocumentTypeNodePublicId(node),
treeAdapter.getDocumentTypeNodeSystemId(node)
)}>`
: parse5.serializeOuter(node, { treeAdapter });

const serializedNode = parse5.serialize(fragment, serializerOpts);
assertLocation(location, serializedNode, html, lines);

assertNodeLocation(location, serializedNode, html, lines);

// TODO: None of the cases below are ever matched.

if (location.startTag) {
assertStartTagLocation(location, serializedNode, html, lines);
}
if (treeAdapter.isElementNode(node)) {
assertStartTagLocation(location, serializedNode, html, lines);

if (location.endTag) {
assertEndTagLocation(location, serializedNode, html, lines);
}

if (location.attrs) {
assertAttrsLocation(location, serializedNode, html, lines);
} else {
// If we don't have `location.attrs`, we expect that the node has no attributes.
assert.strictEqual(treeAdapter.getAttrList(node).length, 0);
}
}
});
Expand Down

0 comments on commit a2c7240

Please sign in to comment.