Skip to content

Commit

Permalink
Fix issues in type definitions and add node type test functions
Browse files Browse the repository at this point in the history
  • Loading branch information
cjbarth authored Jul 13, 2023
1 parent 07e9211 commit 38688e0
Show file tree
Hide file tree
Showing 5 changed files with 314 additions and 51 deletions.
14 changes: 7 additions & 7 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
}
],
"devDependencies": {
"@xmldom/xmldom": "^0.8.8",
"@xmldom/xmldom": "^0.8.9",
"es-check": "^7.1.1",
"mocha": "^9.0.2"
},
Expand Down
132 changes: 132 additions & 0 deletions test.js
Original file line number Diff line number Diff line change
Expand Up @@ -1051,4 +1051,136 @@ describe('xpath', () => {
assert.strictEqual(xpath.select1('local-name(/book/characters)', doc), 'characters');
});
});

describe('Node type tests', () => {
it('should correctly identify a Node of type Element', () => {
var doc = parseXml('<book />');
var element = doc.createElement('characters');

assert.ok(xpath.isNodeLike(element));
assert.ok(xpath.isElement(element));
assert.ok(!xpath.isAttribute(doc));
});

it('should correctly identify a Node of type Attribute', () => {
var doc = parseXml('<book />');
var attribute = doc.createAttribute('name');

assert.ok(xpath.isNodeLike(attribute));
assert.ok(xpath.isAttribute(attribute));
assert.ok(!xpath.isTextNode(attribute));
});

it('should correctly identify a Node of type Text', () => {
var doc = parseXml('<book />');
var text = doc.createTextNode('Harry Potter');

assert.ok(xpath.isNodeLike(text));
assert.ok(xpath.isTextNode(text));
assert.ok(!xpath.isCDATASection(text));
});

it('should correctly identify a Node of type CDATASection', () => {
var doc = parseXml('<book />');
var cdata = doc.createCDATASection('Harry Potter');

assert.ok(xpath.isNodeLike(cdata));
assert.ok(xpath.isCDATASection(cdata));
assert.ok(!xpath.isProcessingInstruction(cdata));
});

it('should correctly identify a Node of type ProcessingInstruction', () => {
var doc = parseXml('<book />');
var pi = doc.createProcessingInstruction('xml-stylesheet', 'href="mycss.css" type="text/css"');

// This test fails due to a bug in @xmldom/xmldom@0.8.8
// assert.ok(xpath.isNodeLike(pi));
assert.ok(xpath.isProcessingInstruction(pi));
assert.ok(!xpath.isComment(pi));
});

it('should correctly identify a Node of type Comment', () => {
var doc = parseXml('<book />');
var comment = doc.createComment('Harry Potter');

assert.ok(xpath.isNodeLike(comment));
assert.ok(xpath.isComment(comment));
assert.ok(!xpath.isDocumentNode(comment));
});

it('should correctly identify a Node of type Document', () => {
var doc = parseXml('<book />');

assert.ok(xpath.isNodeLike(doc));
assert.ok(xpath.isDocumentNode(doc));
assert.ok(!xpath.isDocumentTypeNode(doc));
});

it('should correctly identify a Node of type DocumentType', () => {
var doc = parseXml('<book />');
var doctype = doc.implementation.createDocumentType('book', null, null);

assert.ok(xpath.isNodeLike(doctype));
assert.ok(xpath.isDocumentTypeNode(doctype));
assert.ok(!xpath.isDocumentFragment(doctype));
});

it('should correctly identify a Node of type DocumentFragment', () => {
var doc = parseXml('<book />');
var fragment = doc.createDocumentFragment();

assert.ok(xpath.isNodeLike(fragment));
assert.ok(xpath.isDocumentFragment(fragment));
assert.ok(!xpath.isElement(fragment));
});

it('should not identify a string as a Node', () => {
assert.ok(!xpath.isNodeLike('Harry Potter'));
});

it('should not identify a number as a Node', () => {
assert.ok(!xpath.isNodeLike(45));
});

it('should not identify a boolean as a Node', () => {
assert.ok(!xpath.isNodeLike(true));
});

it('should not identify null as a Node', () => {
assert.ok(!xpath.isNodeLike(null));
});

it('should not identify undefined as a Node', () => {
assert.ok(!xpath.isNodeLike(undefined));
});

it('should not identify an array as a Node', () => {
assert.ok(!xpath.isNodeLike([]));
});

it('should identify an array of Nodes as such', () => {
var doc = parseXml('<book />');
var fragment = doc.createDocumentFragment();
var nodes = [doc, fragment];

assert.ok(xpath.isArrayOfNodes(nodes));
assert.ok(!xpath.isNodeLike(nodes));
});

it('should not identify an array of non-Nodes as an array of Nodes', () => {
var nodes = ['Harry Potter', 45];

assert.ok(!xpath.isArrayOfNodes(nodes));
assert.ok(!xpath.isNodeLike(nodes));
});

it('should not identify an array of mixed Nodes and non-Nodes as an array of Nodes', () => {
var doc = parseXml('<book />');
var fragment = doc.createDocumentFragment();
var nodes = [doc, fragment, 'Harry Potter'];

assert.ok(!xpath.isArrayOfNodes(nodes));
assert.ok(!xpath.isNodeLike(nodes));
});
});
});
56 changes: 48 additions & 8 deletions xpath.d.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,51 @@
/// <reference lib="dom" />

type SelectedValue = Node | Attr | string | number | boolean;
interface XPathSelect {
(expression: string, node?: Node): Array<SelectedValue>;
(expression: string, node: Node, single: true): SelectedValue | undefined;
export type SelectedValue = Node | string | number | boolean | null;

export type SelectReturnType = Array<Node> | SelectedValue;
export type SelectSingleReturnType = SelectedValue;

export interface XPathSelect {
(expression: string, node: Node): SelectReturnType;
(expression: string, node: Node, single: false): SelectReturnType;
(expression: string, node: Node, single: true): SelectSingleReturnType;
}
export var select: XPathSelect;
export function select1(expression: string, node?: Node): SelectedValue | undefined;
export function evaluate(expression: string, contextNode: Node, resolver: XPathNSResolver | null, type: number, result: XPathResult | null): XPathResult;
export function useNamespaces(namespaceMap: { [name: string]: string }): XPathSelect;

/**
* Evaluate an XPath expression against a DOM node.
*/
export function select(expression: string, node: Node): SelectReturnType;
export function select(expression: string, node: Node, single: false): SelectReturnType;
export function select(expression: string, node: Node, single: true): SelectSingleReturnType;

/**
* Evaluate an xpath expression against a DOM node, returning the first result only.
*/
export function select1(expression: string, node: Node): SelectSingleReturnType;

/**
* Evaluate an XPath expression against a DOM node using a given namespace resolver.
*/
export function selectWithResolver(expression: string, node: Node, resolver?: XPathNSResolver | null): SelectReturnType;
export function selectWithResolver(expression: string, node: Node, resolver: XPathNSResolver | null, single: false): SelectReturnType;
export function selectWithResolver(expression: string, node: Node, resolver: XPathNSResolver | null, single: true): SelectSingleReturnType;

/**
* Creates a `select` function that uses the given namespace prefix to URI mappings when evaluating queries.
* @param namespaceMap an object mapping namespace prefixes to namespace URIs. Each key is a prefix; each value is a URI.
* @return a function with the same signature as `xpath.select`
*/
export function useNamespaces(namespaceMap: Record<string, string>): XPathSelect;

// Type guards to narrow down the type of the selected type of a returned Node object
export function isNodeLike(value: SelectedValue): value is Node;
export function isArrayOfNodes(value: SelectedValue): value is Node[];
export function isElement(value: SelectedValue): value is Element;
export function isAttribute(value: SelectedValue): value is Attr;
export function isTextNode(value: SelectedValue): value is Text;
export function isCDATASection(value: SelectedValue): value is CDATASection;
export function isProcessingInstruction(value: SelectedValue): value is ProcessingInstruction;
export function isComment(value: SelectedValue): value is Comment;
export function isDocumentNode(value: SelectedValue): value is Document;
export function isDocumentTypeNode(value: SelectedValue): value is DocumentType;
export function isDocumentFragment(value: SelectedValue): value is DocumentFragment;
Loading

0 comments on commit 38688e0

Please sign in to comment.