@webqit/oohtml 1.10.3 → 2.0.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/.gitignore +3 -3
- package/LICENSE +20 -20
- package/README.md +399 -396
- package/dist/context-api.js +2 -0
- package/dist/context-api.js.map +7 -0
- package/dist/html-imports.js +1 -2
- package/dist/html-imports.js.map +3 -3
- package/dist/html-modules.js +1 -2
- package/dist/html-modules.js.map +3 -3
- package/dist/main.js +26 -14
- package/dist/main.js.map +3 -3
- package/dist/namespaced-html.js +1 -2
- package/dist/namespaced-html.js.map +3 -3
- package/dist/scoped-js.js +27 -0
- package/dist/scoped-js.js.map +7 -0
- package/dist/state-api.js +1 -2
- package/dist/state-api.js.map +3 -3
- package/package.json +76 -76
- package/src/context-api/HTMLContext.js +158 -0
- package/src/context-api/HTMLContextManager.js +77 -0
- package/src/context-api/_ContextRequestEvent.js +26 -0
- package/src/context-api/index.js +53 -0
- package/src/{namespaced-html/browser-entry.js → context-api/targets.browser.js} +9 -9
- package/src/html-imports/_HTMLImportElement.js +216 -0
- package/src/html-imports/index.js +92 -557
- package/src/{browser-entry.js → html-imports/targets.browser.js} +10 -10
- package/src/html-modules/HTMLExportsManager.js +191 -0
- package/src/html-modules/_HTMLImportsContext.js +114 -0
- package/src/html-modules/index.js +133 -384
- package/src/{html-imports/browser-entry.js → html-modules/targets.browser.js} +9 -9
- package/src/index.js +34 -39
- package/src/namespaced-html/index.js +130 -144
- package/src/namespaced-html/targets.browser.js +10 -0
- package/src/scoped-js/index.js +382 -0
- package/src/scoped-js/targets.browser.js +10 -0
- package/src/state-api/index.js +55 -142
- package/src/state-api/targets.browser.js +10 -0
- package/src/{html-modules/browser-entry.js → targets.browser.js} +10 -10
- package/src/util.js +20 -180
- package/test/imports.test.js +194 -0
- package/test/index.js +119 -0
- package/test/modules.test.js +198 -0
- package/test/namespaced-html.test.js +50 -0
- package/test/scoped-js.js +57 -0
- package/test/state-api.test.js +34 -0
- package/test/test.html +69 -0
- package/dist/subscript.js +0 -15
- package/dist/subscript.js.map +0 -7
- package/src/state-api/browser-entry.js +0 -10
- package/src/subscript/Element.js +0 -103
- package/src/subscript/browser-entry.js +0 -10
- package/src/subscript/index.js +0 -70
- package/test/all.test.js +0 -0
package/src/util.js
CHANGED
|
@@ -1,180 +1,20 @@
|
|
|
1
|
-
|
|
2
|
-
/**
|
|
3
|
-
* @imports
|
|
4
|
-
*/
|
|
5
|
-
import
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
if (!WebQit.OOHTML) {
|
|
22
|
-
// For feature modules that will call outside of ./index.js module
|
|
23
|
-
WebQit.OOHTML = {};
|
|
24
|
-
}
|
|
25
|
-
if (!WebQit.OOHTML.meta) {
|
|
26
|
-
WebQit.OOHTML.meta = WebQit.DOM.meta('oohtml', true/* readWrite */);
|
|
27
|
-
}
|
|
28
|
-
WebQit.OOHTML.meta.defaults(_merge(3, defaults, overrides));
|
|
29
|
-
return WebQit.OOHTML.meta;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* Runs a "scope-query" against a context.
|
|
34
|
-
*
|
|
35
|
-
* @param Array contexts
|
|
36
|
-
* @param String query
|
|
37
|
-
* @param Function collectionCallback
|
|
38
|
-
* @param Function advancementCallback
|
|
39
|
-
*
|
|
40
|
-
* @return Array
|
|
41
|
-
*/
|
|
42
|
-
export function scopeQuery(contexts, query, collectionCallback, advancementCallback = null) {
|
|
43
|
-
var queryPath = query.split('#')[0].split('/').map(n => n.trim()).filter(n => n);
|
|
44
|
-
return execScopeQuery(contexts, queryPath, collectionCallback, advancementCallback);
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
* Parses a "scope-query" reference expression to seperate the "reference" and its "modifiers".
|
|
49
|
-
*
|
|
50
|
-
* @param String expr
|
|
51
|
-
*
|
|
52
|
-
* @return Array
|
|
53
|
-
*/
|
|
54
|
-
export function parseScopeReferenceExpr(reference) {
|
|
55
|
-
var split = Lexer.split(reference.trim(), [':']);
|
|
56
|
-
reference = split.shift();
|
|
57
|
-
var modifiers = split.reduce((_modifiers, _modifier) => {
|
|
58
|
-
var [ name, parentheses ] = Lexer.split(_modifier.trim(), []);
|
|
59
|
-
_modifiers[name] = _unwrap(parentheses, '(', ')');
|
|
60
|
-
return _modifiers;
|
|
61
|
-
}, {});
|
|
62
|
-
return [ reference, modifiers ];
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
/**
|
|
66
|
-
* Determines if a given path matches a "scope-query".
|
|
67
|
-
*
|
|
68
|
-
* @param String query
|
|
69
|
-
* @param String path
|
|
70
|
-
*
|
|
71
|
-
* @return Bool
|
|
72
|
-
*/
|
|
73
|
-
export function queryMatchPath(query, path) {
|
|
74
|
-
path = path.split('#')[0].split('/').map(n => n.trim()).filter(n => n);
|
|
75
|
-
query = query.split('#')[0].split('/').map(n => n.trim()).filter(n => n);
|
|
76
|
-
return !query.length ? false : query.reduce((prev, segment, i) => {
|
|
77
|
-
if (!prev) return false;
|
|
78
|
-
return Lexer.split(segment.trim(), ['|', '+']).reduce((_prev, _reference) => {
|
|
79
|
-
var [ _reference, modifiers ] = parseScopeReferenceExpr(_reference);
|
|
80
|
-
_reference = _reference.trim();
|
|
81
|
-
var sementIsMatch = _reference === path[i];
|
|
82
|
-
if (!sementIsMatch && (('deep' in modifiers) || ('deepest' in modifiers))) {
|
|
83
|
-
var _sementIsMatch = path.slice(i + 1).reduce((prev, s, i) => {
|
|
84
|
-
return prev > -1 && ('deep' in modifiers) ? prev : (s === _reference ? i : prev);
|
|
85
|
-
}, -1);
|
|
86
|
-
if (_sementIsMatch > -1) {
|
|
87
|
-
var e = path.splice(i, _sementIsMatch + 1);
|
|
88
|
-
sementIsMatch = true;
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
return _prev || sementIsMatch;
|
|
92
|
-
}, false);
|
|
93
|
-
}, true);
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
const evalAssertExpr = (segment, callback) => {
|
|
97
|
-
return Lexer.split(segment.trim(), ['|', '+'], { preserveDelims: true }).reduce((_result, _reference) => {
|
|
98
|
-
var operator;
|
|
99
|
-
if (_reference.startsWith('|') || _reference.startsWith('+')) {
|
|
100
|
-
operator = _reference.substr(0, 1);
|
|
101
|
-
_reference = _reference.substr(1).trim();
|
|
102
|
-
}
|
|
103
|
-
if (_result.theEnd || (operator === '|' && _result.length)) {
|
|
104
|
-
_result.theEnd = true;
|
|
105
|
-
return _result;
|
|
106
|
-
}
|
|
107
|
-
return _result.concat(callback(_reference.trim()));
|
|
108
|
-
}, []).filter(t => t);
|
|
109
|
-
};
|
|
110
|
-
|
|
111
|
-
const evalModuleExpr = (contexts, segment, collectionCallback) => {
|
|
112
|
-
const lookAhead = contexts => contexts.reduce((_list, _module) => _list.concat(...collectionCallback(_module).values()), []);
|
|
113
|
-
return evalAssertExpr(segment, _reference => {
|
|
114
|
-
var [ _reference, modifiers ] = parseScopeReferenceExpr(_reference);
|
|
115
|
-
// ------------
|
|
116
|
-
return contexts.reduce((list, context) => {
|
|
117
|
-
var collection = collectionCallback(context);
|
|
118
|
-
if (_reference === '*') {
|
|
119
|
-
_reference = '(' + collection.keys().join('+') + ')';
|
|
120
|
-
}
|
|
121
|
-
var itemArray = _wrapped(_reference, '(', ')') ? evalModuleExpr([context], _unwrap(_reference, '(', ')'), collectionCallback) : _arrFrom(collection.get(_reference), false);
|
|
122
|
-
// ------------
|
|
123
|
-
var appliedModifiers = [], reapplyAppliedModifiers = expr => `${expr}${appliedModifiers.map(m => `:${m}(${modifiers[m]})`).join('')}`;
|
|
124
|
-
Object.keys(modifiers).forEach(modifier => {
|
|
125
|
-
if (modifier === 'deep' || modifier === 'deepest') {
|
|
126
|
-
var nextLevel = [context];
|
|
127
|
-
while ((modifier === 'deepest' || !itemArray.length) && (nextLevel = lookAhead(nextLevel)).length) {
|
|
128
|
-
var _itemArray = evalModuleExpr(nextLevel, reapplyAppliedModifiers(_reference), collectionCallback);
|
|
129
|
-
if (_itemArray.length) {
|
|
130
|
-
itemArray = _itemArray;
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
} else {
|
|
134
|
-
if (modifier === 'having' || modifier === 'not-having') {
|
|
135
|
-
itemArray = itemArray.filter(module => {
|
|
136
|
-
var filterHavingsResult = evalAssertExpr(modifiers[modifier], _filterHavings => collectionCallback(module, _filterHavings));
|
|
137
|
-
return modifier === 'not-having' ? !filterHavingsResult.length : filterHavingsResult.length;
|
|
138
|
-
});
|
|
139
|
-
}
|
|
140
|
-
appliedModifiers.push(modifier);
|
|
141
|
-
}
|
|
142
|
-
});
|
|
143
|
-
return list.concat(itemArray);
|
|
144
|
-
}, []);
|
|
145
|
-
});
|
|
146
|
-
};
|
|
147
|
-
|
|
148
|
-
const execScopeQuery = function(contexts, path, collectionCallback, advancementCallback = null, level = 0) {
|
|
149
|
-
|
|
150
|
-
if (!path.length) {
|
|
151
|
-
return [];
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
let segment = path.shift(), isStopSegmentIfCount;
|
|
155
|
-
if (segment.endsWith('.')) {
|
|
156
|
-
isStopSegmentIfCount = true;
|
|
157
|
-
segment = segment.substr(0, segment.length - 1).trim();
|
|
158
|
-
}
|
|
159
|
-
// -----------
|
|
160
|
-
let modules = evalModuleExpr(contexts, segment, collectionCallback);
|
|
161
|
-
modules.forEach(context => {
|
|
162
|
-
if (_internals(context, 'oohtml').has('queryCallback')) {
|
|
163
|
-
_internals(context, 'oohtml').get('queryCallback')();
|
|
164
|
-
}
|
|
165
|
-
});
|
|
166
|
-
// -----------
|
|
167
|
-
if (modules.length && isStopSegmentIfCount) {
|
|
168
|
-
return modules;
|
|
169
|
-
}
|
|
170
|
-
// -----------
|
|
171
|
-
if (path.length) {
|
|
172
|
-
let submodules = execScopeQuery(modules, path.slice(), collectionCallback, advancementCallback, level + 1);
|
|
173
|
-
if (submodules === -1) {
|
|
174
|
-
return advancementCallback(modules, level, true);
|
|
175
|
-
}
|
|
176
|
-
return submodules;
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
return advancementCallback ? advancementCallback(modules, level) : modules;
|
|
180
|
-
};
|
|
1
|
+
|
|
2
|
+
/**
|
|
3
|
+
* @imports
|
|
4
|
+
*/
|
|
5
|
+
import { _internals } from '@webqit/util/js/index.js';
|
|
6
|
+
|
|
7
|
+
export const _ = ( node, ...args ) => _internals( node, 'oohtml', ...args );
|
|
8
|
+
|
|
9
|
+
export const _compare = ( a, b, depth = 1, objectSizing = false ) => {
|
|
10
|
+
if ( depth && typeof a === 'object' && a && typeof b === 'object' && b && ( !objectSizing || Object.keys( a ).length === Object.keys( b ).length ) ) {
|
|
11
|
+
for ( let key in a ) {
|
|
12
|
+
if ( !_compare( a[ key ], b[ key ], depth - 1, objectSizing ) ) return false;
|
|
13
|
+
}
|
|
14
|
+
return true;
|
|
15
|
+
}
|
|
16
|
+
if ( Array.isArray( a ) && Array.isArray( b ) && a.length === b.length ) {
|
|
17
|
+
return ( b = b.slice( 0 ).sort() ) && a.slice( 0 ).sort().every( ( valueA, i ) => valueA === b[ i ] );
|
|
18
|
+
}
|
|
19
|
+
return a === b;
|
|
20
|
+
};
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
|
|
2
|
+
/**
|
|
3
|
+
* @imports
|
|
4
|
+
*/
|
|
5
|
+
import { expect } from 'chai';
|
|
6
|
+
import { delay, createDocument, mockRemoteFetch, printDocument, _ } from './index.js';
|
|
7
|
+
|
|
8
|
+
describe(`HTML Imports`, function() {
|
|
9
|
+
|
|
10
|
+
describe( `Basic...`, function() {
|
|
11
|
+
|
|
12
|
+
const head = `
|
|
13
|
+
<template exportid="temp0">
|
|
14
|
+
<p>Hello world Export</p>
|
|
15
|
+
<p>Hellort</p>
|
|
16
|
+
</template>`;
|
|
17
|
+
const body = `
|
|
18
|
+
<import module="temp0"></import>`;
|
|
19
|
+
const { document } = createDocument( head, body );
|
|
20
|
+
|
|
21
|
+
it ( `The document object and <template> elements should expose a "modules" property`, async function() {
|
|
22
|
+
expect( document ).to.have.property( 'modules' );
|
|
23
|
+
expect( document.modules.temp0 ).to.have.property( 'modules' );
|
|
24
|
+
} );
|
|
25
|
+
|
|
26
|
+
it ( `<import> element be automatically resolved: import default export...`, async function() {
|
|
27
|
+
expect( document.body.children ).to.have.length( 2 );
|
|
28
|
+
const importElement = _( document.body.firstElementChild ).get( 'slot@imports' );
|
|
29
|
+
expect( importElement.nodeName ).to.eq( 'IMPORT' );
|
|
30
|
+
} );
|
|
31
|
+
|
|
32
|
+
it( `<import> element be resolved again: after having mutated an export right at its module.`, async function() {
|
|
33
|
+
const templateEl = document.querySelector( 'template' );
|
|
34
|
+
let added = document.createElement( 'div' );
|
|
35
|
+
templateEl.content.appendChild( added );
|
|
36
|
+
expect( document.body.children ).to.have.length( 3 );
|
|
37
|
+
} );
|
|
38
|
+
} );
|
|
39
|
+
|
|
40
|
+
describe( `Dynamic...`, function() {
|
|
41
|
+
|
|
42
|
+
const head = `
|
|
43
|
+
<template exportid="temp0">
|
|
44
|
+
<p>Hello world Export</p>
|
|
45
|
+
<p>Hellort</p>
|
|
46
|
+
<input exportid="input" />
|
|
47
|
+
<template exportid="temp1">
|
|
48
|
+
<textarea exportid="input"></textarea>
|
|
49
|
+
<template exportid="temp2">
|
|
50
|
+
<select exportid="input"></select>
|
|
51
|
+
</template>
|
|
52
|
+
</template>
|
|
53
|
+
</template>`;
|
|
54
|
+
const body = `
|
|
55
|
+
<import module="temp0/uuu"></import>`;
|
|
56
|
+
const { document } = createDocument( head, body );
|
|
57
|
+
const importEl = document.querySelector( 'import' );
|
|
58
|
+
|
|
59
|
+
it ( `<import> element should not be resolved: no match for given import ID...`, async function() {
|
|
60
|
+
expect( document.body.firstElementChild.nodeName ).to.eq( 'IMPORT' );
|
|
61
|
+
} );
|
|
62
|
+
|
|
63
|
+
it ( `<import> element should be automatically resolved: new import ID is set...`, async function() {
|
|
64
|
+
importEl.setAttribute( 'module', 'temp0#input' );
|
|
65
|
+
expect( document.body.firstElementChild.nodeName ).to.eq( 'INPUT' );
|
|
66
|
+
} );
|
|
67
|
+
|
|
68
|
+
it ( `<import> element should be automatically resolved: new moduleref is set - nested...`, async function() {
|
|
69
|
+
importEl.setAttribute( 'module', 'temp0/temp1#input' );
|
|
70
|
+
expect( document.body.firstElementChild.nodeName ).to.eq( 'TEXTAREA' );
|
|
71
|
+
} );
|
|
72
|
+
|
|
73
|
+
it ( `<import> element should be automatically resolved: moduleref is unset - should now be inherited from <body>...`, async function() {
|
|
74
|
+
importEl.setAttribute( 'module', '#input' );
|
|
75
|
+
expect( document.body.firstElementChild.nodeName ).to.eq( 'IMPORT' );
|
|
76
|
+
document.body.setAttribute( 'importscontext', 'temp0/temp1/temp2' );
|
|
77
|
+
expect( document.body.firstElementChild.nodeName ).to.eq( 'SELECT' );
|
|
78
|
+
} );
|
|
79
|
+
|
|
80
|
+
it ( `<import> element should be automatically resolved: moduleref at <body> is changed...`, async function() {
|
|
81
|
+
document.body.setAttribute( 'importscontext', 'temp0' );
|
|
82
|
+
expect( document.body.firstElementChild.nodeName ).to.eq( 'INPUT' );
|
|
83
|
+
} );
|
|
84
|
+
|
|
85
|
+
it ( `<import> element should be automatically RESTORED: slotted element is removed from DOM...`, async function() {
|
|
86
|
+
document.body.querySelector( 'input' ).remove();
|
|
87
|
+
expect( document.body.firstElementChild.nodeName ).to.eq( 'IMPORT' );
|
|
88
|
+
} );
|
|
89
|
+
|
|
90
|
+
} );
|
|
91
|
+
|
|
92
|
+
describe( `Remote...`, function() {
|
|
93
|
+
|
|
94
|
+
it( `<import> element from nested remote modules.`, async function() {
|
|
95
|
+
this.timeout( 10000 );
|
|
96
|
+
|
|
97
|
+
const head = ``, body = ``, timeout = 2000;
|
|
98
|
+
const window = createDocument( head, body, window => {
|
|
99
|
+
// Define a remote response
|
|
100
|
+
const temp0 = `
|
|
101
|
+
<template exportid="temp1" src="/temp1.html" loading="lazy"></template>`;
|
|
102
|
+
const temp1 = `
|
|
103
|
+
<p>Hello world Export</p>
|
|
104
|
+
<p>Hellort</p>
|
|
105
|
+
<template exportid="temp22">
|
|
106
|
+
</template>`;
|
|
107
|
+
mockRemoteFetch( window, { '/temp0.html': temp0, '/temp1.html': temp1 }, timeout );
|
|
108
|
+
} ), document = window.document;
|
|
109
|
+
|
|
110
|
+
// Add a remote module
|
|
111
|
+
const templateEl = document.createElement( 'template' );
|
|
112
|
+
templateEl.setAttribute( 'exportid', 'temp0' );
|
|
113
|
+
templateEl.setAttribute( 'src', '/temp0.html' );
|
|
114
|
+
document.head.appendChild( templateEl );
|
|
115
|
+
// Add the import element to with a view to waiting for the remote module
|
|
116
|
+
const importEl = document.createElement( 'import' );
|
|
117
|
+
importEl.setAttribute( 'module', 'temp0/temp1' );
|
|
118
|
+
document.body.appendChild( importEl );
|
|
119
|
+
// Should stil be waiting...
|
|
120
|
+
expect( document.body.firstElementChild.nodeName ).to.eq( 'IMPORT' );
|
|
121
|
+
// When remote request must have completed
|
|
122
|
+
await delay( ( timeout * 2 ) + 100 );
|
|
123
|
+
expect( document.body.firstElementChild.nodeName ).to.eq( 'P' );
|
|
124
|
+
expect( document.body.lastElementChild.nodeName ).to.eq( 'P' );
|
|
125
|
+
} );
|
|
126
|
+
} );
|
|
127
|
+
|
|
128
|
+
describe( `Hydration...`, function() {
|
|
129
|
+
|
|
130
|
+
it ( `Server-resolved <import> element should maintain relationship with slotted elements...`, async function() {
|
|
131
|
+
|
|
132
|
+
const head = `
|
|
133
|
+
<template exportid="temp0">
|
|
134
|
+
<input exportid="input" />
|
|
135
|
+
<template exportid="temp1">
|
|
136
|
+
<textarea exportid="input"></textarea>
|
|
137
|
+
<template exportid="temp2">
|
|
138
|
+
<select exportid="input"></select>
|
|
139
|
+
</template>
|
|
140
|
+
</template>
|
|
141
|
+
</template>`;
|
|
142
|
+
const body = `
|
|
143
|
+
<div importscontext="temp0/temp1">
|
|
144
|
+
<textarea exportid="input"></textarea>
|
|
145
|
+
<!--<import module="#input"></import>-->
|
|
146
|
+
</div>`;
|
|
147
|
+
const { document } = createDocument( head, body );
|
|
148
|
+
await true;
|
|
149
|
+
|
|
150
|
+
const routingElement = document.body.firstElementChild;
|
|
151
|
+
expect( routingElement.firstElementChild.nodeName ).to.eq( 'TEXTAREA' );
|
|
152
|
+
const temp1 = document.modules.temp0.modules.temp1;
|
|
153
|
+
const textarea = [ ...temp1.modules[ '#input' ] ][ 0 ];
|
|
154
|
+
textarea.remove();
|
|
155
|
+
expect( routingElement.firstElementChild.nodeName ).to.eq( 'IMPORT' );
|
|
156
|
+
temp1.content.prepend( textarea );
|
|
157
|
+
expect( routingElement.firstElementChild.nodeName ).to.eq( 'TEXTAREA' );
|
|
158
|
+
routingElement.firstElementChild.remove();
|
|
159
|
+
expect( routingElement.firstElementChild.nodeName ).to.eq( 'IMPORT' );
|
|
160
|
+
} );
|
|
161
|
+
|
|
162
|
+
it ( `Server-resolved <import> element should maintain relationship with slotted elements...`, async function() {
|
|
163
|
+
|
|
164
|
+
const head = `
|
|
165
|
+
<template exportid="temp0">
|
|
166
|
+
<input exportid="input" />
|
|
167
|
+
<template exportid="temp1">
|
|
168
|
+
<textarea exportid="input"></textarea>
|
|
169
|
+
<template exportid="temp2">
|
|
170
|
+
<select exportid="input"></select>
|
|
171
|
+
</template>
|
|
172
|
+
</template>
|
|
173
|
+
</template>`;
|
|
174
|
+
const body = `
|
|
175
|
+
<div importscontext="temp0/temp1">
|
|
176
|
+
<textarea exportid="input"></textarea>
|
|
177
|
+
<!--<import module="#input"></import>-->
|
|
178
|
+
</div>`;
|
|
179
|
+
const { document } = createDocument( head, body );
|
|
180
|
+
await true;
|
|
181
|
+
|
|
182
|
+
const routingElement = document.body.firstElementChild;
|
|
183
|
+
expect( routingElement.firstElementChild.nodeName ).to.eq( 'TEXTAREA' );
|
|
184
|
+
routingElement.setAttribute( 'importscontext', 'temp0/temp1/temp2' );
|
|
185
|
+
expect( routingElement.firstElementChild.nodeName ).to.eq( 'SELECT' );
|
|
186
|
+
routingElement.removeChild( routingElement.firstElementChild );
|
|
187
|
+
expect( routingElement.firstElementChild.nodeName ).to.eq( 'IMPORT' );
|
|
188
|
+
routingElement.setAttribute( 'importscontext', 'temp0' );
|
|
189
|
+
expect( routingElement.firstElementChild.nodeName ).to.eq( 'INPUT' );
|
|
190
|
+
} );
|
|
191
|
+
|
|
192
|
+
} );
|
|
193
|
+
|
|
194
|
+
} );
|
package/test/index.js
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
|
|
2
|
+
/**
|
|
3
|
+
* @imports
|
|
4
|
+
*/
|
|
5
|
+
import { _internals } from '@webqit/util/js/index.js';
|
|
6
|
+
import { createContext, compileFunction, runInContext } from 'vm';
|
|
7
|
+
import jsdom from 'jsdom';
|
|
8
|
+
import init, { Observer } from '../src/index.js';
|
|
9
|
+
import wqDom from '@webqit/dom';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* -------
|
|
13
|
+
* HELPERS
|
|
14
|
+
* -------
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
export { Observer };
|
|
18
|
+
|
|
19
|
+
export const _ = ( el, ...args ) => _internals( el, 'oohtml', ...args );
|
|
20
|
+
|
|
21
|
+
export function delay( duration, callback = undefined ) {
|
|
22
|
+
return new Promise( res => {
|
|
23
|
+
setTimeout( () => res( callback && callback() ), duration );
|
|
24
|
+
} );
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function createDocument( head = '', body = '', callback = null, ) {
|
|
28
|
+
// -------
|
|
29
|
+
const skeletonDoc = `
|
|
30
|
+
<!DOCTYPE html>
|
|
31
|
+
<html>
|
|
32
|
+
<head>${ head }</head>
|
|
33
|
+
<body>${ body }</body>
|
|
34
|
+
</html>`;
|
|
35
|
+
// --------
|
|
36
|
+
// TODO: Proper indentation for pretty-printing
|
|
37
|
+
const instance = new jsdom.JSDOM( skeletonDoc, {
|
|
38
|
+
url: 'http://localhost',
|
|
39
|
+
beforeParse( window ) {
|
|
40
|
+
init.call( window );
|
|
41
|
+
if ( callback ) callback( window, window.wq.dom );
|
|
42
|
+
}
|
|
43
|
+
} );
|
|
44
|
+
// --------
|
|
45
|
+
return instance.window;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function createDocumentForScopedJS( head = '', body = '', callback = null, params = {} ) {
|
|
49
|
+
// -------
|
|
50
|
+
const skeletonDoc = `
|
|
51
|
+
<!DOCTYPE html>
|
|
52
|
+
<html>
|
|
53
|
+
<head>${ head }</head>
|
|
54
|
+
<body>${ body }</body>
|
|
55
|
+
</html>`;
|
|
56
|
+
// --------
|
|
57
|
+
const instance = new jsdom.JSDOM( skeletonDoc, {
|
|
58
|
+
url: 'http://localhost',
|
|
59
|
+
...params,
|
|
60
|
+
beforeParse( window ) {
|
|
61
|
+
window.testRecords = [];
|
|
62
|
+
createContext( window );
|
|
63
|
+
// -----------------
|
|
64
|
+
// Running advanced scripts
|
|
65
|
+
const subscriptParams = {
|
|
66
|
+
runtimeParams: {
|
|
67
|
+
compileFunction: ( code, parameters ) => compileFunction( code, parameters, {
|
|
68
|
+
parsingContext: window,
|
|
69
|
+
} ),
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
init.call( window, { ScopedJS: subscriptParams } );
|
|
73
|
+
// -----------------
|
|
74
|
+
// Running basic scripts
|
|
75
|
+
const dom = wqDom.call( window );
|
|
76
|
+
if ( params.runScripts !== 'dangerously' ) {
|
|
77
|
+
dom.realtime( window.document ).observe( 'script', record => {
|
|
78
|
+
record.entrants.forEach( script => {
|
|
79
|
+
//console.log( '-----------------------------------------', script.textContent );
|
|
80
|
+
runInContext( script.textContent, window );
|
|
81
|
+
} );
|
|
82
|
+
}, { subtree: true } );
|
|
83
|
+
}
|
|
84
|
+
// -----------------
|
|
85
|
+
if ( callback ) callback( window, window.wq.dom );
|
|
86
|
+
}
|
|
87
|
+
} );
|
|
88
|
+
// --------
|
|
89
|
+
return instance.window;
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
export function mockRemoteFetch( window, contents, delay = 1000 ) {
|
|
94
|
+
window.fetch = url => {
|
|
95
|
+
console.info( 'Fetching .......... ', url );
|
|
96
|
+
const successResponse = () => ( { ok: true, text: () => Promise.resolve( contents[ url ] ), } );
|
|
97
|
+
return new Promise( ( res, rej ) => {
|
|
98
|
+
setTimeout( () => {
|
|
99
|
+
if ( contents[ url ] ) res( successResponse() )
|
|
100
|
+
else rej( { message: 'Not found.' } );
|
|
101
|
+
}, delay );
|
|
102
|
+
} );
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export function printDocument( document, desc = '' ) {
|
|
107
|
+
return; new Promise( res => {
|
|
108
|
+
setTimeout( () => {
|
|
109
|
+
console.log( '' );
|
|
110
|
+
console.log( '-------------' );
|
|
111
|
+
console.log( desc );
|
|
112
|
+
console.log( '-------------' );
|
|
113
|
+
console.log( document.documentElement.outerHTML );
|
|
114
|
+
console.log( '-------------' );
|
|
115
|
+
console.log( '' );
|
|
116
|
+
res();
|
|
117
|
+
}, 0 );
|
|
118
|
+
} );
|
|
119
|
+
}
|