From b7481ff79f9af5001bab242f92188a53e4b581fe Mon Sep 17 00:00:00 2001 From: Cohen Erickson Date: Mon, 15 Jan 2024 15:52:07 -0600 Subject: [PATCH] Allow modification of existing attributes and text content. (#23) Moves the `setTextContent` and `setAttribute` methods to behave more like the DOM equivalents by replacing any existing content or attributes. --- src/attributes.ts | 2 ++ src/test/attributes_test.ts | 16 ++++++++++++++ src/test/text_test.ts | 44 ++++++++++++++++++++++++++++++++----- src/text.ts | 11 ++++------ 4 files changed, 61 insertions(+), 12 deletions(-) diff --git a/src/attributes.ts b/src/attributes.ts index 555f8a5..11abf4f 100644 --- a/src/attributes.ts +++ b/src/attributes.ts @@ -8,6 +8,8 @@ import type {Element} from './nodeTypes.js'; * @return {void} */ export function setAttribute(node: Element, name: string, value: string): void { + removeAttribute(node, name); + node.attrs.push({ name, value diff --git a/src/test/attributes_test.ts b/src/test/attributes_test.ts index c0f3a09..884b916 100644 --- a/src/test/attributes_test.ts +++ b/src/test/attributes_test.ts @@ -19,6 +19,22 @@ test('setAttribute', async (t) => { assert.deepStrictEqual(node.attrs, [{name: 'ping-pong', value: 'boing'}]); }); + + await t.test('modifies existing attribute on element', () => { + const node: Element = { + nodeName: 'div', + parentNode: null, + tagName: 'div', + attrs: [{name: 'foo', value: 'bar'}], + namespaceURI: html.NS.HTML, + childNodes: [] + }; + + main.setAttribute(node, 'foo', 'baz'); + + assert.strictEqual(node.attrs.length, 1); + assert.deepStrictEqual(node.attrs, [{name: 'foo', value: 'baz'}]); + }); }); test('getAttribute', async (t) => { diff --git a/src/test/text_test.ts b/src/test/text_test.ts index e8a94f7..b017b4d 100644 --- a/src/test/text_test.ts +++ b/src/test/text_test.ts @@ -35,16 +35,28 @@ test('getTextContent', async (t) => { assert.strictEqual(result, 'some text'); }); + await t.test('returns value of comment node', () => { + const node: CommentNode = { + nodeName: '#comment', + parentNode: null, + data: 'some comment' + }; + + const result = main.getTextContent(node); + + assert.strictEqual(result, 'some comment'); + }); + await t.test('concats all text-like children', () => { const child1: TextNode = { nodeName: '#text', parentNode: null, - value: 'text node' + value: 'text node 1' }; - const child2: CommentNode = { - nodeName: '#comment', + const child2: TextNode = { + nodeName: '#text', parentNode: null, - data: 'comment node' + value: 'text node 2' }; const node: DocumentFragment = { nodeName: '#document-fragment', @@ -53,7 +65,7 @@ test('getTextContent', async (t) => { const result = main.getTextContent(node); - assert.strictEqual(result, 'text nodecomment node'); + assert.strictEqual(result, 'text node 1text node 2'); }); await t.test('ignores non-text children', () => { @@ -164,6 +176,28 @@ test('setTextContent', async (t) => { }); }); + await t.test('sets text node for parent nodes', () => { + const node: DocumentFragment = { + nodeName: '#document-fragment', + childNodes: [] + }; + + node.childNodes.push({ + nodeName: '#text', + value: 'old text', + parentNode: node + }); + + main.setTextContent(node, 'new text'); + + assert.strictEqual(node.childNodes.length, 1); + assert.deepStrictEqual(node.childNodes[0], { + nodeName: '#text', + value: 'new text', + parentNode: node + }); + }); + await t.test('ignores document type nodes', () => { const node: DocumentType = { nodeName: '#documentType', diff --git a/src/text.ts b/src/text.ts index 9e59f1d..c4f5162 100644 --- a/src/text.ts +++ b/src/text.ts @@ -1,8 +1,8 @@ import type {Node} from './nodeTypes.js'; import {isCommentNode, isTextNode, isParentNode} from './typeGuards.js'; -import {appendChild} from './treeMutation.js'; import {createTextNode} from './creation.js'; import {queryAll} from './traversal.js'; +import {appendChild} from './treeMutation.js'; /** * Computes the text content of a given node using a similar @@ -21,10 +21,7 @@ export function getTextContent(node: Node): string { let content = ''; - const children = queryAll( - node, - (node) => isTextNode(node) || isCommentNode(node) - ); + const children = queryAll(node, (node) => isTextNode(node)); for (const child of children) { content += getTextContent(child); @@ -45,7 +42,7 @@ export function setTextContent(node: Node, text: string): void { } else if (isTextNode(node)) { node.value = text; } else if (isParentNode(node)) { - const textNode = createTextNode(text); - appendChild(node, textNode); + node.childNodes = []; + appendChild(node, createTextNode(text)); } }