Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: Make location info parser tests work #440

Merged
merged 5 commits into from
Mar 7, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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);
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the escapeString call that invalidated the test.

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.
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the TODO that started this PR.


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