als-document 1.1.1 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/browser-tests/data/html1.js +2579 -0
- package/browser-tests/data/html2.js +1124 -0
- package/browser-tests/data/svg.js +66 -0
- package/{tests → browser-tests}/index.html +1 -1
- package/browser-tests/utils.js +37 -0
- package/build-readme.js +8 -0
- package/{src/build.js → build.js} +4 -4
- package/docs/#.md +52 -0
- package/docs/1.parseHTML.md +39 -0
- package/docs/2.node.md +25 -0
- package/docs/3.singleNode.md +3 -0
- package/docs/4.tTextNode.md +3 -0
- package/docs/5. Document.md +28 -0
- package/docs/6.Query.md +110 -0
- package/docs/7.cache.md +14 -0
- package/document.js +6 -4
- package/index.js +6 -4
- package/index.mjs +6 -4
- package/{src/node/root.js → lib/node/document.js} +3 -2
- package/{src → lib}/node/node.js +6 -3
- package/lib/node/root.js +6 -0
- package/{src → lib}/node/single-node.js +22 -4
- package/package.json +6 -4
- package/readme.md +21 -14
- package/tests/cache.test.js +23 -0
- package/tests/node.test.js +411 -0
- package/tests/parser.test.js +353 -0
- package/tests/query.test.js +65 -0
- package/tests/single-node.test.js +88 -0
- package/tests/utils.js +2 -0
- package/.vscode/settings.json +0 -3
- /package/{tests → browser-tests}/cache.js +0 -0
- /package/{tests → browser-tests}/node.js +0 -0
- /package/{tests → browser-tests}/parse-real.js +0 -0
- /package/{tests → browser-tests}/parser.js +0 -0
- /package/{tests → browser-tests}/query.js +0 -0
- /package/{tests/test.js → browser-tests/simple-test.js} +0 -0
- /package/{src → lib}/node/class-list.js +0 -0
- /package/{src → lib}/node/dataset.js +0 -0
- /package/{src → lib}/node/find.js +0 -0
- /package/{src → lib}/node/style.js +0 -0
- /package/{src → lib}/node/text-node.js +0 -0
- /package/{src → lib}/parse/cache.js +0 -0
- /package/{src → lib}/parse/parse-atts.js +0 -0
- /package/{src → lib}/parse/parser.js +0 -0
- /package/{src → lib}/parse/void-tags.js +0 -0
- /package/{src → lib}/query/check-element.js +0 -0
- /package/{src → lib}/query/query.js +0 -0
package/readme.md
CHANGED
|
@@ -3,11 +3,8 @@
|
|
|
3
3
|
|
|
4
4
|
## Overview
|
|
5
5
|
|
|
6
|
-
`als-document` is a powerful library for parsing HTML and manipulating
|
|
6
|
+
`als-document` is a powerful library for parsing HTML and XML, building and manipulating virtual DOM structure on backend and frontend. It provides a robust and intuitive API for querying and interacting with DOM elements using selectors, making it a valuable tool for web developers.
|
|
7
7
|
|
|
8
|
-
## Release notes
|
|
9
|
-
* als-document is still on alpha testing. All tested features works fine, but through the use, discovering some bugs or things that should work different. For example in this release, changed the way for storing attributes with empty value.
|
|
10
|
-
* Also, this release, has additional very powefull feature which is building cache for storing DOM tree as json and building back DOM from cache.
|
|
11
8
|
|
|
12
9
|
## Installation
|
|
13
10
|
|
|
@@ -25,13 +22,13 @@ The library provides three different files to cater to different module systems:
|
|
|
25
22
|
1. **index.js**: This file uses the CommonJS module system. It's suitable for projects using Node.js or bundlers like Browserify or Webpack. The entry point in `package.json` for this file is "main".
|
|
26
23
|
|
|
27
24
|
```javascript
|
|
28
|
-
const { parseHTML, Node, Query, TextNode, SingleNode,Root } = require('als-document');
|
|
25
|
+
const { parseHTML, Node, Query, TextNode, SingleNode, Root, Document } = require('als-document');
|
|
29
26
|
```
|
|
30
27
|
|
|
31
28
|
2. **index.mjs**: This file uses the ES Modules (ESM) system. It's suitable for modern JavaScript environments that support ESM. The entry point in `package.json` for this file is "module".
|
|
32
29
|
|
|
33
30
|
```js
|
|
34
|
-
import { parseHTML, Node, Query, TextNode, SingleNode, Root } from 'als-document';
|
|
31
|
+
import { parseHTML, Node, Query, TextNode, SingleNode, Root, Document } from 'als-document';
|
|
35
32
|
```
|
|
36
33
|
|
|
37
34
|
3. **document.js**: By including this file, a constant variable named `alsDocument` is created, which wraps all the exports.
|
|
@@ -39,10 +36,20 @@ import { parseHTML, Node, Query, TextNode, SingleNode, Root } from 'als-document
|
|
|
39
36
|
```html
|
|
40
37
|
<script src="/node_modules/als-document/document.js"></script>
|
|
41
38
|
<script>
|
|
42
|
-
const { parseHTML, Node, Query, TextNode, SingleNode, buildFromCache, cacheDoc, Root } = alsDocument
|
|
39
|
+
const { parseHTML, Node, Query, TextNode, SingleNode, buildFromCache, cacheDoc, Root, Document } = alsDocument
|
|
43
40
|
</script>
|
|
44
41
|
```
|
|
45
42
|
|
|
43
|
+
## Change log
|
|
44
|
+
New in 1.2.0:
|
|
45
|
+
* singleNode can insert(0) and insert(3)
|
|
46
|
+
* insertAdjacentElement replace element instead cloning it
|
|
47
|
+
* Document class
|
|
48
|
+
* has structure <!DOCTYPE html><html lang="en"><head><title></title></head><body></body></html>
|
|
49
|
+
* get title
|
|
50
|
+
* set title
|
|
51
|
+
* get body
|
|
52
|
+
* get head
|
|
46
53
|
## parseHTML
|
|
47
54
|
|
|
48
55
|
`parseHTML` is a function that takes an HTML string and constructs a DOM tree representation from it. It recognizes various HTML elements, such as comments, scripts, styles, and CDATA, and organizes them into nodes that can be manipulated and queried.
|
|
@@ -82,6 +89,7 @@ const parsedScript = parseHTML('<script>console.log("Hello, world!");</script>')
|
|
|
82
89
|
|
|
83
90
|
Remember, the actual tree structure will be more complex and detailed, but the provided examples give you a basic understanding of how to navigate through the parsed result.
|
|
84
91
|
|
|
92
|
+
|
|
85
93
|
## Node
|
|
86
94
|
|
|
87
95
|
`Node` is a fundamental class that represents an element node in the DOM tree. It provides functionality similar to the native DOM API in browsers, but with its own implementation.
|
|
@@ -107,17 +115,16 @@ Remember, the actual tree structure will be more complex and detailed, but the p
|
|
|
107
115
|
- **appendChild**: Add a child node to the element.
|
|
108
116
|
- **insert(place,element)**: place (0-3) or beforebegin,afterbegin,... eleemnt - raw html or element
|
|
109
117
|
|
|
110
|
-
|
|
118
|
+
|
|
111
119
|
### SingleNode
|
|
112
120
|
|
|
113
121
|
`SingleNode` extends from the `Node` class and represents elements that don't have closing tags (self-closing tags) in HTML. Examples include `<img>`, `<br>`, and `<!DOCTYPE>`. This class has restricted methods and properties since these elements can't have child nodes.
|
|
114
|
-
|
|
122
|
+
|
|
115
123
|
### TextNode
|
|
116
124
|
|
|
117
125
|
`TextNode` is a class that represents text content within the DOM. A TextNode holds raw text data and does not have child nodes.
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
### Root node (extends Node)
|
|
126
|
+
|
|
127
|
+
### Document node (extends Node)
|
|
121
128
|
|
|
122
129
|
Has additional getters and setters:
|
|
123
130
|
* getter root.title
|
|
@@ -145,7 +152,7 @@ const foundP = div.querySelector('p');
|
|
|
145
152
|
console.log(foundP.textContent); // Outputs: Hello, world!
|
|
146
153
|
```
|
|
147
154
|
|
|
148
|
-
|
|
155
|
+
|
|
149
156
|
## Query
|
|
150
157
|
|
|
151
158
|
The `Query` class is designed to parse CSS selector strings and transform them into a structured object format, providing detailed insights into each selector and its components.
|
|
@@ -256,7 +263,7 @@ if attribute has value, attrib object will contain check function with one param
|
|
|
256
263
|
let s = Query.get('[test^="some"]')[0]
|
|
257
264
|
console.log(s.attribs[0].check('some value test')) // true
|
|
258
265
|
```
|
|
259
|
-
|
|
266
|
+
|
|
260
267
|
## buildFromCache and cacheDoc
|
|
261
268
|
|
|
262
269
|
Building DOM from raw html, usually takes tens of milliseconds. But now, you can build DOM once and save it's cache as regular stringified JSON.
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
const { describe, it } = require('node:test')
|
|
2
|
+
const assert = require('node:assert')
|
|
3
|
+
const { parseHTML, cacheDoc, buildFromCache } = require('../index')
|
|
4
|
+
const html1 = require('./data/html1')
|
|
5
|
+
function mesureTime(fn) {
|
|
6
|
+
let time = performance.now()
|
|
7
|
+
const result = fn()
|
|
8
|
+
time = performance.now() - time
|
|
9
|
+
return { result, time }
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
describe('Cache and build from cache', () => {
|
|
13
|
+
const { result: root, time: rootTime } = mesureTime(() => parseHTML(html1))
|
|
14
|
+
const { result: cache, time: cacheTime } = mesureTime(() => cacheDoc(root))
|
|
15
|
+
const { result: root1, time: root1Time } = mesureTime(() => buildFromCache(cache))
|
|
16
|
+
|
|
17
|
+
it('HTML from cache and HTML from not cached same', () => {
|
|
18
|
+
// console.log({ rootTime, cacheTime, root1Time })
|
|
19
|
+
assert(cacheTime < 5, 'Build cache takes less then 5ms')
|
|
20
|
+
assert(root1Time < 5, 'Build DOM from cache takes less then 5ms')
|
|
21
|
+
assert(root.innerHTML === root1.innerHTML)
|
|
22
|
+
})
|
|
23
|
+
})
|
|
@@ -0,0 +1,411 @@
|
|
|
1
|
+
const { describe, it, beforeEach } = require('node:test')
|
|
2
|
+
const assert = require('node:assert')
|
|
3
|
+
const { Node, Root, TextNode, Document } = require('../index')
|
|
4
|
+
|
|
5
|
+
describe('Constructor and Basic Properties', () => {
|
|
6
|
+
|
|
7
|
+
it('Creates an element', () => {
|
|
8
|
+
const node = new Node('div');
|
|
9
|
+
assert(node instanceof Node, "Element was not created correctly");
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
it('Checks the setting of element properties: tagName, attributes, parent, etc.', () => {
|
|
13
|
+
const rootNode = new Node('ROOT');
|
|
14
|
+
const divNode = new Node('div', { class: 'container' }, rootNode);
|
|
15
|
+
|
|
16
|
+
assert(divNode.tagName === 'div', "Tag name is correct");
|
|
17
|
+
assert(divNode.attributes.class === 'container', "Attributes are correct");
|
|
18
|
+
assert(divNode.parent === rootNode, "Parent node is correct");
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it('Adds a new element to the parent\'s childNodes', () => {
|
|
22
|
+
const rootNode = new Node('ROOT');
|
|
23
|
+
const childNode = new Node('child', {}, rootNode);
|
|
24
|
+
|
|
25
|
+
assert(rootNode.childNodes.includes(childNode), "New element was added to parent's childNodes");
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
describe('DOM Search and Navigation', () => {
|
|
31
|
+
|
|
32
|
+
it('Gets the previous and next element', () => {
|
|
33
|
+
const rootNode = new Node('ROOT');
|
|
34
|
+
const div1 = new Node('div', {}, rootNode);
|
|
35
|
+
const div2 = new Node('div', {}, rootNode);
|
|
36
|
+
|
|
37
|
+
assert(div2.previousElementSibling === div1, "Previous sibling is correct");
|
|
38
|
+
assert(div2.nextElementSibling === null, "Next sibling is correct");
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it('Gets the list of ancestors', () => {
|
|
42
|
+
const rootNode = new Node('ROOT');
|
|
43
|
+
const firstLevel = new Node('first', {}, rootNode);
|
|
44
|
+
const secondLevel = new Node('second', {}, firstLevel);
|
|
45
|
+
const thirdLevel = new Node('third', {}, secondLevel);
|
|
46
|
+
|
|
47
|
+
const ancestors = thirdLevel.ancestors;
|
|
48
|
+
|
|
49
|
+
assert(ancestors.length === 2, "The number of ancestors is correct");
|
|
50
|
+
assert(ancestors[0] === firstLevel, "First ancestor is correct");
|
|
51
|
+
assert(ancestors[1] === secondLevel, "Second ancestor is correct");
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it('Searches for elements using different methods', () => {
|
|
55
|
+
const rootNode = new Node('ROOT');
|
|
56
|
+
const div1 = new Node('div', { class: 'test', id: 'first' }, rootNode);
|
|
57
|
+
const div2 = new Node('div', { class: 'test' }, rootNode);
|
|
58
|
+
const p = new Node('p', {}, rootNode);
|
|
59
|
+
|
|
60
|
+
const divs = rootNode.getElementsByTagName('div');
|
|
61
|
+
assert(divs.length === 2, "getElementsByTagName did find correct number of divs");
|
|
62
|
+
|
|
63
|
+
const testClassNodes = rootNode.getElementsByClassName('test');
|
|
64
|
+
assert(testClassNodes.length === 2, "getElementsByClassName did find correct number of .test elements");
|
|
65
|
+
|
|
66
|
+
const firstDiv = rootNode.getElementById('first');
|
|
67
|
+
assert(firstDiv === div1, "getElementById did find the correct element");
|
|
68
|
+
|
|
69
|
+
const firstQuery = rootNode.querySelector('.test');
|
|
70
|
+
assert(firstQuery === div1, "querySelector did return the first matching element");
|
|
71
|
+
|
|
72
|
+
const allQuery = rootNode.querySelectorAll('.test');
|
|
73
|
+
assert(allQuery.length === 2 && allQuery[0] === div1 && allQuery[1] === div2, "querySelectorAll did not find all matching elements");
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
describe('Attribute Operations', () => {
|
|
78
|
+
|
|
79
|
+
it('Gets, sets, and removes attributes', () => {
|
|
80
|
+
const attrNode = new Node('div');
|
|
81
|
+
attrNode.setAttribute('data-test', 'testValue');
|
|
82
|
+
|
|
83
|
+
assert(attrNode.getAttribute('data-test') === 'testValue', "getAttribute/setAttribute is correct");
|
|
84
|
+
|
|
85
|
+
attrNode.removeAttribute('data-test');
|
|
86
|
+
assert(!attrNode.getAttribute('data-test'), "removeAttribute is correct");
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it('Handles classList', () => {
|
|
90
|
+
const nodeWithClasses = new Node('div', { class: 'class1 class2' });
|
|
91
|
+
// Assertion to check if classList contains the right classes
|
|
92
|
+
assert(nodeWithClasses.classList.contains('class1'), "The classList contain 'class1'");
|
|
93
|
+
assert(nodeWithClasses.classList.contains('class2'), "The classList contain 'class2'");
|
|
94
|
+
|
|
95
|
+
// Modifying classList and checking
|
|
96
|
+
nodeWithClasses.classList.add('class3');
|
|
97
|
+
assert(nodeWithClasses.classList.contains('class3'), "The classList contain 'class3' after addition");
|
|
98
|
+
|
|
99
|
+
nodeWithClasses.classList.remove('class1');
|
|
100
|
+
assert(!nodeWithClasses.classList.contains('class1'), "The classList doesn't contains 'class1' after removal");
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
it('Handles style', () => {
|
|
104
|
+
const nodeWithStyle = new Node('div', { style: 'color: red; font-size: 16px;' });
|
|
105
|
+
// Assertions to check if style is correctly set
|
|
106
|
+
assert(nodeWithStyle.style.color === 'red', "The style property color is correctly set or retrieved");
|
|
107
|
+
assert(nodeWithStyle.style.fontSize === '16px', "The style property fontSize is correctly set or retrieved");
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
describe('Content Manipulation', () => {
|
|
113
|
+
|
|
114
|
+
it('Adds and removes child elements', () => {
|
|
115
|
+
const rootNode = new Node('ROOT');
|
|
116
|
+
const childNode = new Node('child');
|
|
117
|
+
rootNode.appendChild(childNode);
|
|
118
|
+
|
|
119
|
+
assert(rootNode.childNodes.includes(childNode), "Child node was added");
|
|
120
|
+
|
|
121
|
+
childNode.remove();
|
|
122
|
+
assert(!rootNode.childNodes.includes(childNode), "Child node was removed");
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
it('Inserts content adjacent to an element', () => {
|
|
127
|
+
const rootNode = new Node('div');
|
|
128
|
+
const childNode = new Node('p', {}, rootNode);
|
|
129
|
+
|
|
130
|
+
childNode.insertAdjacentElement('beforebegin', new Node('span'));
|
|
131
|
+
assert(rootNode.childNodes[0].tagName === 'span');
|
|
132
|
+
|
|
133
|
+
childNode.insertAdjacentElement('afterend', new Node('a'));
|
|
134
|
+
assert(rootNode.childNodes[2].tagName === 'a');
|
|
135
|
+
|
|
136
|
+
childNode.insertAdjacentHTML('beforebegin', '<strong></strong>');
|
|
137
|
+
assert(rootNode.childNodes[1].tagName === 'strong');
|
|
138
|
+
|
|
139
|
+
childNode.insertAdjacentText('afterend', 'Some text');
|
|
140
|
+
assert(typeof rootNode.childNodes[3].nodeValue === 'string');
|
|
141
|
+
assert(rootNode.childNodes[3].nodeValue === 'Some text');
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
// it('Gets and sets innerHTML and outerHTML', () => {
|
|
145
|
+
// const rootNode = new Node('div');
|
|
146
|
+
// new Node('p', {id: 'test'}, rootNode);
|
|
147
|
+
|
|
148
|
+
// assert(rootNode.innerHTML === '<p id="test"></p>');
|
|
149
|
+
|
|
150
|
+
// const newNode = new Node('span');
|
|
151
|
+
// newNode.innerHTML = '<a href="#">Link</a>';
|
|
152
|
+
// assert(newNode.childNodes[0].tagName === 'a');
|
|
153
|
+
|
|
154
|
+
// newNode.childNodes[0].outerHTML = '<div><strong>New content</strong></div>';
|
|
155
|
+
// assert(newNode.childNodes[0].childNodes[0].tagName === 'strong');
|
|
156
|
+
// });
|
|
157
|
+
|
|
158
|
+
// it('Works with text content', () => {
|
|
159
|
+
// const rootNode = new Node('div');
|
|
160
|
+
// new Node('p', {}, rootNode).textContent = 'Hello World';
|
|
161
|
+
|
|
162
|
+
// assert(rootNode.textContent === 'Hello World');
|
|
163
|
+
// rootNode.textContent = 'Changed Text';
|
|
164
|
+
// assert(rootNode.textContent === 'Changed Text');
|
|
165
|
+
// assert(rootNode.childNodes.leng === 1);
|
|
166
|
+
// assert(rootNode.childNodes[0] === 'Changed Text');
|
|
167
|
+
// });
|
|
168
|
+
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
describe('Other Methods and Properties', () => {
|
|
172
|
+
|
|
173
|
+
it('Gets the list of child elements', () => {
|
|
174
|
+
const rootNode = new Node('div');
|
|
175
|
+
|
|
176
|
+
// Добавление дочерних элементов
|
|
177
|
+
new Node('p', {}, rootNode);
|
|
178
|
+
new Node('span', {}, rootNode);
|
|
179
|
+
new Node('a', {}, rootNode);
|
|
180
|
+
|
|
181
|
+
rootNode.appendChild('Some text'); // Добавляем текстовый узел
|
|
182
|
+
|
|
183
|
+
// Проверяем, что у корневого элемента теперь 4 дочерних узла (3 элемента + 1 текстовый узел)
|
|
184
|
+
assert(rootNode.childNodes.length === 4);
|
|
185
|
+
|
|
186
|
+
// Проверяем свойство children
|
|
187
|
+
assert(rootNode.children.length === 3);
|
|
188
|
+
assert(rootNode.children[0].tagName === 'p');
|
|
189
|
+
assert(rootNode.children[1].tagName === 'span');
|
|
190
|
+
assert(rootNode.children[2].tagName === 'a');
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
it('Gets the parent element', () => {
|
|
194
|
+
const rootNode = new Node('ROOT');
|
|
195
|
+
const childNode = new Node('child', {}, rootNode);
|
|
196
|
+
|
|
197
|
+
assert(childNode.parentNode === rootNode, "Parent node is correct");
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
describe('Method and properties 2', () => {
|
|
203
|
+
let node
|
|
204
|
+
beforeEach(() => {node = new Node('div')})
|
|
205
|
+
|
|
206
|
+
it('set id', () => {
|
|
207
|
+
assert(node.getAttribute('id') === null)
|
|
208
|
+
assert(node.id === null)
|
|
209
|
+
node.id = 'test'
|
|
210
|
+
assert(node.getAttribute('id') === 'test')
|
|
211
|
+
assert(node.id === 'test')
|
|
212
|
+
})
|
|
213
|
+
|
|
214
|
+
it('get className', () => {
|
|
215
|
+
assert(node.classList.constructor.name === 'NodeClassList')
|
|
216
|
+
})
|
|
217
|
+
|
|
218
|
+
it('get outerHTML', () => {
|
|
219
|
+
assert(node.outerHTML === '<div></div>')
|
|
220
|
+
})
|
|
221
|
+
|
|
222
|
+
it('set innerHTML', () => {
|
|
223
|
+
assert(node.innerHTML === '')
|
|
224
|
+
node.innerHTML = 'some test'
|
|
225
|
+
assert(node.innerHTML === 'some test')
|
|
226
|
+
})
|
|
227
|
+
|
|
228
|
+
it('set outerHTML error no parent', () => {
|
|
229
|
+
assert.throws(() => {node.outerHTML = '<div>some test</div>'}) // element has no parent node
|
|
230
|
+
})
|
|
231
|
+
|
|
232
|
+
it('set outerHTML', () => {
|
|
233
|
+
const parent = new Node('div')
|
|
234
|
+
parent.insert(1,node)
|
|
235
|
+
assert(parent.children[0].outerHTML === '<div></div>')
|
|
236
|
+
const html = '<div>some test</div>'
|
|
237
|
+
node.outerHTML = html
|
|
238
|
+
assert(parent.children[0].outerHTML === html)
|
|
239
|
+
|
|
240
|
+
})
|
|
241
|
+
|
|
242
|
+
describe('set textContent', () => {
|
|
243
|
+
it('sets and gets textContent correctly', () => {
|
|
244
|
+
const node = new Node('div');
|
|
245
|
+
node.textContent = 'Hello World';
|
|
246
|
+
assert.strictEqual(node.textContent, 'Hello World', 'textContent should be "Hello World"');
|
|
247
|
+
});
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
describe('get innerHTML', () => {
|
|
251
|
+
it('gets correct innerHTML', () => {
|
|
252
|
+
const parent = new Node('div');
|
|
253
|
+
const child = new Node('span');
|
|
254
|
+
parent.appendChild(child);
|
|
255
|
+
assert.strictEqual(parent.innerHTML, '<span></span>', 'innerHTML should include child node markup');
|
|
256
|
+
});
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
describe('insertAdjacentHTML', () => {
|
|
260
|
+
it('inserts HTML correctly before begin', () => {
|
|
261
|
+
const node = new Node('div');
|
|
262
|
+
node.insertAdjacentHTML('afterbegin', '<span>test</span>');
|
|
263
|
+
assert(node.innerHTML === '<span>test</span>', 'Should insert <span>test</span> before the node');
|
|
264
|
+
});
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
describe('insertAdjacentText', () => {
|
|
268
|
+
it('inserts text correctly after end', () => {
|
|
269
|
+
const node = new Node('div');
|
|
270
|
+
const parent = new Node('div');
|
|
271
|
+
parent.appendChild(node);
|
|
272
|
+
node.insertAdjacentText('afterend', 'Hello');
|
|
273
|
+
assert.strictEqual(parent.childNodes[1].nodeValue, 'Hello', 'Should insert "Hello" after the node');
|
|
274
|
+
});
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
describe('appendChild', () => {
|
|
278
|
+
it('appends a child node correctly', () => {
|
|
279
|
+
const parentNode = new Node('div');
|
|
280
|
+
const childNode = new Node('span');
|
|
281
|
+
parentNode.appendChild(childNode);
|
|
282
|
+
assert(parentNode.childNodes.includes(childNode), 'Child node should be in parent\'s childNodes array');
|
|
283
|
+
});
|
|
284
|
+
});
|
|
285
|
+
|
|
286
|
+
describe('get textContent', () => {
|
|
287
|
+
it('returns correct text content', () => {
|
|
288
|
+
const node = new Node('div');
|
|
289
|
+
const child1 = new TextNode('Hello');
|
|
290
|
+
const child2 = new TextNode('World');
|
|
291
|
+
node.appendChild(child1);
|
|
292
|
+
node.appendChild(child2);
|
|
293
|
+
assert.strictEqual(node.textContent, 'HelloWorld', 'textContent should be "HelloWorld"');
|
|
294
|
+
});
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
describe('insertAdjacentElement', () => {
|
|
298
|
+
it('inserts an element adjacent to another', () => {
|
|
299
|
+
const root = new Root();
|
|
300
|
+
const refNode = new Node('div');
|
|
301
|
+
const newNode = new Node('span');
|
|
302
|
+
root.appendChild(refNode);
|
|
303
|
+
refNode.insertAdjacentElement('afterend', newNode);
|
|
304
|
+
assert.strictEqual(root.childNodes[1], newNode, 'New node should be inserted after the reference node');
|
|
305
|
+
});
|
|
306
|
+
});
|
|
307
|
+
|
|
308
|
+
})
|
|
309
|
+
|
|
310
|
+
describe('query', () => {
|
|
311
|
+
|
|
312
|
+
describe('$$', () => {
|
|
313
|
+
it('returns all elements matching the selector', () => {
|
|
314
|
+
const root = new Root();
|
|
315
|
+
const child1 = new Node('div', { class: 'test' });
|
|
316
|
+
const child2 = new Node('div', { class: 'test' });
|
|
317
|
+
root.appendChild(child1);
|
|
318
|
+
root.appendChild(child2);
|
|
319
|
+
assert.strictEqual(root.$$('.test').length, 2, 'Should find two elements with class "test"');
|
|
320
|
+
});
|
|
321
|
+
});
|
|
322
|
+
|
|
323
|
+
describe('$', () => {
|
|
324
|
+
it('returns the first element matching the selector', () => {
|
|
325
|
+
const root = new Root();
|
|
326
|
+
const child1 = new Node('div', { class: 'test' });
|
|
327
|
+
const child2 = new Node('div', { class: 'test' });
|
|
328
|
+
root.appendChild(child1);
|
|
329
|
+
root.appendChild(child2);
|
|
330
|
+
assert.strictEqual(root.$('.test'), child1, 'Should return the first element with class "test"');
|
|
331
|
+
});
|
|
332
|
+
});
|
|
333
|
+
|
|
334
|
+
describe('querySelectorAll', () => {
|
|
335
|
+
it('finds all elements matching a complex query', () => {
|
|
336
|
+
const root = new Root();
|
|
337
|
+
const child1 = new Node('div', { id: 'unique' });
|
|
338
|
+
const child2 = new Node('div', { class: 'test' });
|
|
339
|
+
root.appendChild(child1);
|
|
340
|
+
root.appendChild(child2);
|
|
341
|
+
assert.strictEqual(root.querySelectorAll('div').length, 2, 'Should find two div elements');
|
|
342
|
+
});
|
|
343
|
+
});
|
|
344
|
+
|
|
345
|
+
describe('querySelector', () => {
|
|
346
|
+
it('finds the first element matching a complex query', () => {
|
|
347
|
+
const root = new Root();
|
|
348
|
+
const child1 = new Node('div', { id: 'unique' });
|
|
349
|
+
const child2 = new Node('div', { class: 'test' });
|
|
350
|
+
root.appendChild(child1);
|
|
351
|
+
root.appendChild(child2);
|
|
352
|
+
assert.strictEqual(root.querySelector('#unique'), child1, 'Should find the first div with id "unique"');
|
|
353
|
+
});
|
|
354
|
+
});
|
|
355
|
+
|
|
356
|
+
describe('getElementsByClassName', () => {
|
|
357
|
+
it('finds elements by class name', () => {
|
|
358
|
+
const root = new Root();
|
|
359
|
+
const child1 = new Node('div', { class: 'test' });
|
|
360
|
+
const child2 = new Node('div', { class: 'test' });
|
|
361
|
+
root.appendChild(child1);
|
|
362
|
+
root.appendChild(child2);
|
|
363
|
+
assert.strictEqual(root.getElementsByClassName('test').length, 2, 'Should find two elements with class "test"');
|
|
364
|
+
});
|
|
365
|
+
});
|
|
366
|
+
|
|
367
|
+
describe('getElementsByTagName', () => {
|
|
368
|
+
it('finds elements by tag name', () => {
|
|
369
|
+
const root = new Root();
|
|
370
|
+
const child1 = new Node('div');
|
|
371
|
+
const child2 = new Node('span');
|
|
372
|
+
root.appendChild(child1);
|
|
373
|
+
root.appendChild(child2);
|
|
374
|
+
assert.strictEqual(root.getElementsByTagName('div').length, 1, 'Should find one div element');
|
|
375
|
+
});
|
|
376
|
+
});
|
|
377
|
+
|
|
378
|
+
describe('getElementById', () => {
|
|
379
|
+
it('finds an element by ID', () => {
|
|
380
|
+
const root = new Root();
|
|
381
|
+
const child = new Node('div', { id: 'unique' });
|
|
382
|
+
root.appendChild(child);
|
|
383
|
+
assert.strictEqual(root.getElementById('unique'), child, 'Should find the element with id "unique"');
|
|
384
|
+
});
|
|
385
|
+
});
|
|
386
|
+
|
|
387
|
+
})
|
|
388
|
+
|
|
389
|
+
describe('Document element', () => {
|
|
390
|
+
|
|
391
|
+
it('get body', () => {
|
|
392
|
+
const document = new Document();
|
|
393
|
+
assert(document.body !== undefined);
|
|
394
|
+
assert(document.body instanceof Node);
|
|
395
|
+
});
|
|
396
|
+
|
|
397
|
+
it('get head', () => {
|
|
398
|
+
const document = new Document();
|
|
399
|
+
assert(document.head !== undefined);
|
|
400
|
+
assert(document.head instanceof Node);
|
|
401
|
+
});
|
|
402
|
+
|
|
403
|
+
it('get and set title', () => {
|
|
404
|
+
const root = new Document();
|
|
405
|
+
root.title = 'Original Title';
|
|
406
|
+
assert.strictEqual(root.title.textContent, 'Original Title', 'Should get the title');
|
|
407
|
+
root.title = 'New Title';
|
|
408
|
+
assert.strictEqual(root.title.textContent, 'New Title', 'Should set the new title');
|
|
409
|
+
});
|
|
410
|
+
|
|
411
|
+
})
|