als-document 1.0.7-alpha → 1.0.8-alpha
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/.vscode/settings.json +3 -0
- package/document.js +8 -4
- package/index.js +8 -4
- package/index.mjs +8 -4
- package/package.json +2 -5
- package/readme.md +20 -1
- package/src/build.js +3 -3
- package/src/node/node.js +1 -1
- package/src/node/single-node.js +1 -1
- package/src/parse/cache.js +33 -0
- package/src/parse/parse-atts.js +1 -1
- package/tests/cache.js +19 -0
- package/tests/index.html +5 -4
- package/tests/test.js +169 -0
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
function buildFromCache(cached) {
|
|
2
|
+
function buildNode(cache,parent=null) {
|
|
3
|
+
if(typeof cache === 'string') return parent.childNodes.push(cache)
|
|
4
|
+
const {isSingle,tagName,attributes,childNodes,textContent} = cache
|
|
5
|
+
if(textContent) return parent.childNodes.push(new TextNode(textContent))
|
|
6
|
+
if(isSingle) return parent.childNodes.push(new SingleNode(tagName,attributes))
|
|
7
|
+
const newDoc = new Node(tagName,attributes,parent)
|
|
8
|
+
childNodes.forEach(childNode => {
|
|
9
|
+
buildNode(childNode,newDoc)
|
|
10
|
+
});
|
|
11
|
+
return newDoc
|
|
12
|
+
}
|
|
13
|
+
return buildNode(cached)
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
function cacheDoc(doc) {
|
|
18
|
+
const props = ['isSingle','tagName','attributes']
|
|
19
|
+
function addToCache(element,cache={}) {
|
|
20
|
+
if(typeof element === 'string') return element
|
|
21
|
+
if(element.nodeName === '#text') return {textContent:element.textContent}
|
|
22
|
+
props.forEach(prop => {
|
|
23
|
+
if(element[prop]) cache[prop] = element[prop]
|
|
24
|
+
});
|
|
25
|
+
if(!element.childNodes) return cache
|
|
26
|
+
cache.childNodes = []
|
|
27
|
+
element.childNodes.forEach(childNode => {
|
|
28
|
+
cache.childNodes.push(addToCache(childNode))
|
|
29
|
+
});
|
|
30
|
+
return cache
|
|
31
|
+
}
|
|
32
|
+
return addToCache(doc)
|
|
33
|
+
}
|
package/src/parse/parse-atts.js
CHANGED
|
@@ -31,6 +31,6 @@ function parseAttributes(str) {
|
|
|
31
31
|
else value += char;
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
-
if (key.trim() && !value) attrs[key.trim()] =
|
|
34
|
+
if (key.trim() && !value) attrs[key.trim()] = ''; // After the loop, check if there's a leftover key, which would be an attribute without a value
|
|
35
35
|
return attrs;
|
|
36
36
|
}
|
package/tests/cache.js
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
function mesureTime(fn) {
|
|
2
|
+
let time = performance.now()
|
|
3
|
+
const result = fn()
|
|
4
|
+
time = performance.now() - time
|
|
5
|
+
return {result,time}
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
describe('Cache and build from cache', () => {
|
|
9
|
+
const {result:root,time:rootTime} = mesureTime(() => parseHTML(html1))
|
|
10
|
+
const {result:cache,time:cacheTime} = mesureTime(() => cacheDoc(root))
|
|
11
|
+
const {result:root1,time:root1Time} = mesureTime(() => buildFromCache(cache))
|
|
12
|
+
|
|
13
|
+
it('HTML from cache and HTML from not cached same',() => {
|
|
14
|
+
console.log({rootTime,cacheTime,root1Time})
|
|
15
|
+
assert(cacheTime < 5,'Build cache takes less then 5ms')
|
|
16
|
+
assert(root1Time < 5,'Build DOM from cache takes less then 5ms')
|
|
17
|
+
assert(root.innerHTML === root1.innerHTML)
|
|
18
|
+
})
|
|
19
|
+
})
|
package/tests/index.html
CHANGED
|
@@ -4,13 +4,13 @@
|
|
|
4
4
|
<meta charset="UTF-8">
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
6
|
<title>Document</title>
|
|
7
|
-
<script src="
|
|
7
|
+
<script src="test.js"></script>
|
|
8
8
|
<script src="../document.js"></script>
|
|
9
|
-
|
|
10
|
-
<script src="./data/html2.js"></script>
|
|
9
|
+
<script src="./data/html1.js"></script>
|
|
10
|
+
<!-- <script src="./data/html2.js"></script> -->
|
|
11
11
|
<script src="./data/svg.js"></script>
|
|
12
12
|
<script>
|
|
13
|
-
const { parseHTML, Node, Query, TextNode, SingleNode } = alsDocument
|
|
13
|
+
const { parseHTML, Node, Query, TextNode, SingleNode, buildFromCache, cacheDoc } = alsDocument
|
|
14
14
|
let {describe,it,beforeEach,runTests,expect,delay,assert,beforeAll} = SimpleTest
|
|
15
15
|
SimpleTest.showFullError = true
|
|
16
16
|
</script>
|
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
<script src="./query.js"></script>
|
|
19
19
|
<script src="parser.js"></script>
|
|
20
20
|
<script src="node.js"></script>
|
|
21
|
+
<script src="./cache.js"></script>
|
|
21
22
|
</head>
|
|
22
23
|
<body>
|
|
23
24
|
<script src="parse-real.js"></script>
|
package/tests/test.js
ADDED
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
class SimpleTest {
|
|
2
|
+
static tests = [];
|
|
3
|
+
static beforeEachCallback;
|
|
4
|
+
static afterEachCallback;
|
|
5
|
+
static nestingLevel = 0;
|
|
6
|
+
static currentParent = [];
|
|
7
|
+
static space = ''
|
|
8
|
+
static results = {}
|
|
9
|
+
static testTitle = ''
|
|
10
|
+
static showFullError = false
|
|
11
|
+
static colors = {
|
|
12
|
+
bold:'\x1b[1m',
|
|
13
|
+
cblue:'\x1b[34m',
|
|
14
|
+
cred:'\x1b[31m',
|
|
15
|
+
cgreen:'\x1b[32m',
|
|
16
|
+
cgray:'\x1b[37m',
|
|
17
|
+
reset:'\x1b[0m'
|
|
18
|
+
}
|
|
19
|
+
static delay = (ms,toResolve) => new Promise((resolve) => {
|
|
20
|
+
setTimeout(() => {resolve(toResolve)}, ms);
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
static describe(title, callback,pause=false) {
|
|
24
|
+
if(pause) return
|
|
25
|
+
let { nestingLevel, currentParent } = SimpleTest
|
|
26
|
+
nestingLevel++;
|
|
27
|
+
currentParent.push(title);
|
|
28
|
+
callback();
|
|
29
|
+
|
|
30
|
+
currentParent.pop();
|
|
31
|
+
nestingLevel--;
|
|
32
|
+
if (nestingLevel === 0) {
|
|
33
|
+
SimpleTest.beforeEachCallback = null; // Clear beforeEachCallback after finishing the outermost describe block
|
|
34
|
+
SimpleTest.afterEachCallback = null; // Clear beforeEachCallback after finishing the outermost describe block
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
static beforeEach(callback) {SimpleTest.beforeEachCallback = callback;}
|
|
39
|
+
static afterEach(callback) {SimpleTest.afterEachCallback = callback;}
|
|
40
|
+
static beforeAll(callback) {SimpleTest.beforAllCallback = callback;}
|
|
41
|
+
static afterAll(callback) {SimpleTest.afterAllCallback = callback;}
|
|
42
|
+
|
|
43
|
+
static it(title, callback) {
|
|
44
|
+
SimpleTest.tests.push({
|
|
45
|
+
titles: [...SimpleTest.currentParent, title],
|
|
46
|
+
before: SimpleTest.beforeEachCallback,
|
|
47
|
+
after:SimpleTest.afterEachCallback,
|
|
48
|
+
test: callback,
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
static async runTests(consoleLog=true) {
|
|
53
|
+
const {bold,cblue,cred,reset} = SimpleTest.colors
|
|
54
|
+
SimpleTest.consoleLog = consoleLog
|
|
55
|
+
let { tests,results,showFullError,beforAllCallback,afterAllCallback } = SimpleTest;
|
|
56
|
+
let previousTitles = [];
|
|
57
|
+
if(typeof beforAllCallback == 'function') await beforAllCallback()
|
|
58
|
+
for(const test of tests) {
|
|
59
|
+
let curentResult = results
|
|
60
|
+
SimpleTest.space = test.titles.map(t => ' ').join('');
|
|
61
|
+
test.titles.forEach((title, index) => {
|
|
62
|
+
if(curentResult[title] == undefined) {
|
|
63
|
+
if(index == test.titles.length-1) curentResult[title] = []
|
|
64
|
+
else curentResult[title] = {}
|
|
65
|
+
}
|
|
66
|
+
curentResult = curentResult[title]
|
|
67
|
+
if(previousTitles[index] !== title) {
|
|
68
|
+
const indentation = ' '.repeat(index);
|
|
69
|
+
console.log(bold+cblue+'%s'+reset,`${indentation} ${title}`);
|
|
70
|
+
previousTitles[index] = title;
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
SimpleTest.curentResult = curentResult
|
|
74
|
+
try {
|
|
75
|
+
if (test.before) await test.before();
|
|
76
|
+
await test.test();
|
|
77
|
+
if (test.after) await test.after();
|
|
78
|
+
} catch (error) {
|
|
79
|
+
if(showFullError) console.log(error)
|
|
80
|
+
else console.log(`${SimpleTest.space}${cred}Error:${reset} ${error.message}`);
|
|
81
|
+
curentResult.push({error})
|
|
82
|
+
}
|
|
83
|
+
console.log('\n');
|
|
84
|
+
}
|
|
85
|
+
if(typeof afterAllCallback == 'function') await afterAllCallback()
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
static assert(value,testTitle) {
|
|
89
|
+
if(testTitle) SimpleTest.testTitle = testTitle
|
|
90
|
+
SimpleTest.logResult(value)
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
static expect(value1) {
|
|
94
|
+
let { logResult, isEqual } = SimpleTest
|
|
95
|
+
const methods = {
|
|
96
|
+
is: (text='') => {
|
|
97
|
+
SimpleTest.testTitle = text;
|
|
98
|
+
SimpleTest.not = false
|
|
99
|
+
return methods;
|
|
100
|
+
},
|
|
101
|
+
isNot: (text='') => {
|
|
102
|
+
SimpleTest.testTitle = text;
|
|
103
|
+
SimpleTest.not = true
|
|
104
|
+
return methods;
|
|
105
|
+
},
|
|
106
|
+
equalTo: (value2) => {logResult(value1 === value2);},
|
|
107
|
+
between(value2,value3) {logResult(value1 >= value2 && value1 <= value3);},
|
|
108
|
+
below(value2) {logResult(value1 < value2);},
|
|
109
|
+
above(value2) {logResult(value1 > value2);},
|
|
110
|
+
atLeast(value2) {logResult(value1 >= value2);},
|
|
111
|
+
atMost(value2) {logResult(value1 <= value2);},
|
|
112
|
+
sameAs: (value2) => {logResult(isEqual(value1, value2));},
|
|
113
|
+
defined: () => {logResult(value1 !== undefined);},
|
|
114
|
+
matchTo(pattern) {logResult(pattern.test(new RegExp(value1)));},
|
|
115
|
+
includes(value2) {logResult(value1.includes(value2));},
|
|
116
|
+
error: () => {
|
|
117
|
+
if(typeof value1 !== 'function') return console.log('Expected value has to be function')
|
|
118
|
+
try {
|
|
119
|
+
let result = value1()
|
|
120
|
+
if(result instanceof Error) logResult(true)
|
|
121
|
+
else logResult(false);
|
|
122
|
+
} catch (error) {logResult(true)}
|
|
123
|
+
},
|
|
124
|
+
hasProperty: (property) => {logResult(Object.prototype.hasOwnProperty.call(value1, property));},
|
|
125
|
+
instanceof: (classType) => {logResult(value1.constructor.name == classType.name)},
|
|
126
|
+
closeTo(value2, decimalPlaces = 2) {
|
|
127
|
+
const multiplier = 10 ** decimalPlaces;
|
|
128
|
+
const roundedActual = Math.round(value1 * multiplier);
|
|
129
|
+
const roundedExpected = Math.round(value2 * multiplier);
|
|
130
|
+
const diff = Math.abs(roundedActual - roundedExpected) / multiplier;
|
|
131
|
+
logResult(diff < 1 / multiplier);
|
|
132
|
+
}
|
|
133
|
+
};
|
|
134
|
+
return methods;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
static logResult(result) {
|
|
138
|
+
let {curentResult,testTitle,not,space,colors} = SimpleTest
|
|
139
|
+
let {cgreen,cred,cgray,reset} = colors
|
|
140
|
+
if(not) result = !result
|
|
141
|
+
curentResult.push({result:result ? true : false,testTitle,error:null})
|
|
142
|
+
const status = result ? 'Success' : 'Failed';
|
|
143
|
+
const color = result ? cgreen : cred;
|
|
144
|
+
if (testTitle == '') console.log(`${color}${space}${status}${reset}`);
|
|
145
|
+
else console.log(`${space}${color}${status}: ${testTitle ? `${cgray}${testTitle}` : ''}`+reset,);
|
|
146
|
+
SimpleTest.not = false
|
|
147
|
+
SimpleTest.testTitle = ''
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
static isEqual(obj1, obj2, visited = new WeakMap()) {
|
|
151
|
+
if (obj1 === obj2) return true;
|
|
152
|
+
if (visited.has(obj1) && visited.get(obj1) === obj2) return true;
|
|
153
|
+
const type1 = typeof obj1;
|
|
154
|
+
const type2 = typeof obj2;
|
|
155
|
+
|
|
156
|
+
if (type1 !== type2 || type1 !== 'object' || obj1 === null || obj2 === null) return false;
|
|
157
|
+
visited.set(obj1, obj2);
|
|
158
|
+
|
|
159
|
+
const keys1 = Object.keys(obj1);
|
|
160
|
+
const keys2 = Object.keys(obj2);
|
|
161
|
+
if(keys1.length !== keys2.length) return false;
|
|
162
|
+
|
|
163
|
+
for (let key of keys1) {
|
|
164
|
+
if (!keys2.includes(key) || !SimpleTest.isEqual(obj1[key], obj2[key], visited)) return false;
|
|
165
|
+
}
|
|
166
|
+
return true;
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
try {module.exports = SimpleTest} catch (error) {}
|