slicejs-web-framework 4.0.1 → 4.0.2
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/.opencode/opencode.json +14 -14
- package/LICENSE +21 -21
- package/README.md +97 -97
- package/Slice/Components/Structural/ContextManager/ContextManager.js +422 -422
- package/Slice/Components/Structural/ContextManager/ContextManagerDebugger.js +470 -470
- package/Slice/Components/Structural/Controller/Controller.js +1406 -1406
- package/Slice/Components/Structural/Controller/allowedValuesValidation.js +52 -52
- package/Slice/Components/Structural/Debugger/Debugger.html +72 -72
- package/Slice/Components/Structural/Debugger/Debugger.js +1505 -1505
- package/Slice/Components/Structural/EventManager/EventManager.js +651 -651
- package/Slice/Components/Structural/EventManager/EventManagerDebugger.js +985 -985
- package/Slice/Components/Structural/LeakInspector/LeakInspector.js +296 -296
- package/Slice/Components/Structural/Logger/Log.js +10 -10
- package/Slice/Components/Structural/Logger/LogViewer/LogViewer.js +662 -662
- package/Slice/Components/Structural/Logger/Logger.js +225 -225
- package/Slice/Components/Structural/Router/Router.js +810 -793
- package/Slice/Components/Structural/StylesManager/StylesManager.js +82 -82
- package/Slice/Components/Structural/StylesManager/ThemeManager/ThemeManager.js +175 -162
- package/Slice/Slice.js +708 -685
- package/Slice/tests/build-bundled-component-without-category.test.js +103 -103
- package/Slice/tests/build-js-only-visual-components.test.js +144 -144
- package/Slice/tests/build-singleton.test.js +244 -244
- package/Slice/tests/bundle-v2-runtime-contract.test.js +747 -738
- package/Slice/tests/context-patch-use.test.js +95 -95
- package/Slice/tests/destroy-cascade.test.js +215 -215
- package/Slice/tests/event-emitters.test.js +79 -79
- package/Slice/tests/event-registry.test.js +141 -141
- package/Slice/tests/fixtures/real-runtime-loader.mjs +21 -21
- package/Slice/tests/leak-orphans.test.js +131 -131
- package/Slice/tests/props-allowed-values-validation.test.js +119 -119
- package/Slice/tests/public-env-runtime-accessors.test.js +44 -44
- package/Slice/tests/public-env-typed-accessors.test.js +66 -66
- package/Slice/tests/router-loading-finally.test.js +68 -68
- package/api/framework/devDepsOptimizer.js +335 -335
- package/api/framework/express.js +3 -3
- package/api/framework/precompressedStatic.js +93 -93
- package/api/framework/publicEnvResolver.js +123 -123
- package/api/framework/securityMiddleware.js +197 -197
- package/api/framework/server.js +411 -411
- package/api/index.js +7 -7
- package/api/tests/dev-deps-optimizer.test.js +137 -137
- package/api/tests/dev-server-external.test.js +56 -56
- package/api/tests/precompressed-static.test.js +119 -119
- package/api/tests/public-env-resolver.test.js +193 -193
- package/package.json +49 -49
- package/sliceConfig.schema.json +122 -122
- package/src/App/index.html +69 -69
- package/src/App/index.js +20 -20
- package/src/App/style.css +11 -11
- package/src/Components/AppComponents/AboutSection/AboutSection.css +9 -9
- package/src/Components/AppComponents/AboutSection/AboutSection.html +8 -8
- package/src/Components/AppComponents/AboutSection/AboutSection.js +12 -12
- package/src/Components/AppComponents/AppShell/AppShell.css +10 -10
- package/src/Components/AppComponents/AppShell/AppShell.html +4 -4
- package/src/Components/AppComponents/AppShell/AppShell.js +36 -36
- package/src/Components/AppComponents/HomeSection/HomeSection.css +20 -20
- package/src/Components/AppComponents/HomeSection/HomeSection.html +10 -10
- package/src/Components/AppComponents/HomeSection/HomeSection.js +19 -19
- package/src/Components/components.js +8 -8
- package/src/public/Styles/sliceStyles.css +34 -34
- package/src/public/Themes/Dark.css +42 -42
- package/src/public/Themes/Light.css +31 -31
- package/src/public/Themes/Slice.css +47 -47
- package/src/public/manifest.json +15 -15
- package/src/public/robots.txt +4 -4
- package/src/public/sitemap.xml +8 -8
- package/src/routes.js +9 -9
- package/src/sliceConfig.json +72 -72
- package/tests/router-build-failure.test.js +125 -0
- package/tests/router-emit-ordering.test.js +63 -63
- package/types/index.d.ts +219 -219
|
@@ -1,131 +1,131 @@
|
|
|
1
|
-
// Real-Controller tests for the dev-mode leak detector (findOrphans + criteria).
|
|
2
|
-
// Loads the REAL Controller via the resolve hook (same pattern as
|
|
3
|
-
// destroy-cascade.test.js) and drives it with a minimal fake DOM. Fake nodes are
|
|
4
|
-
// made `instanceof HTMLElement` because findOrphans filters on that.
|
|
5
|
-
import { register } from 'node:module';
|
|
6
|
-
import test from 'node:test';
|
|
7
|
-
import assert from 'node:assert/strict';
|
|
8
|
-
|
|
9
|
-
globalThis.alert = () => {};
|
|
10
|
-
register(new URL('./fixtures/real-runtime-loader.mjs', import.meta.url));
|
|
11
|
-
|
|
12
|
-
globalThis.HTMLElement = class HTMLElement {};
|
|
13
|
-
globalThis.slice = {
|
|
14
|
-
logger: { logError() {}, logWarning() {}, logInfo() {} },
|
|
15
|
-
events: null,
|
|
16
|
-
};
|
|
17
|
-
|
|
18
|
-
const { default: Controller } = await import('../Components/Structural/Controller/Controller.js');
|
|
19
|
-
|
|
20
|
-
function mkEl(sliceId, children = []) {
|
|
21
|
-
return Object.assign(Object.create(HTMLElement.prototype), {
|
|
22
|
-
sliceId,
|
|
23
|
-
tagName: 'SLICE-X',
|
|
24
|
-
isConnected: true,
|
|
25
|
-
parentComponent: null,
|
|
26
|
-
_children: children,
|
|
27
|
-
querySelectorAll() {
|
|
28
|
-
const out = [];
|
|
29
|
-
const walk = (n) => { for (const c of n._children) { out.push(c); walk(c); } };
|
|
30
|
-
walk(this);
|
|
31
|
-
return out;
|
|
32
|
-
},
|
|
33
|
-
setAttribute() {},
|
|
34
|
-
remove() { this.isConnected = false; },
|
|
35
|
-
});
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
function flatten(root) {
|
|
39
|
-
const out = [root];
|
|
40
|
-
for (const c of root._children) out.push(...flatten(c));
|
|
41
|
-
return out;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
function buildTree(controller, root) {
|
|
45
|
-
for (const node of flatten(root)) controller.registerComponent(node);
|
|
46
|
-
controller.registerComponentsRecursively(root);
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
test('a built-then-detached component (no destroy) IS flagged', () => {
|
|
50
|
-
const c = new Controller();
|
|
51
|
-
const comp = mkEl('leaky');
|
|
52
|
-
buildTree(c, comp);
|
|
53
|
-
comp.isConnected = false; // simulate innerHTML='' / remove() without destroyComponent
|
|
54
|
-
|
|
55
|
-
const orphans = c.findOrphans();
|
|
56
|
-
assert.equal(orphans.length, 1, 'one orphan');
|
|
57
|
-
assert.equal(orphans[0].sliceId, 'leaky');
|
|
58
|
-
assert.ok(orphans[0].retainPath.includes('Object(leaky)') || orphans[0].retainPath.length >= 1);
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
test('a connected component is NOT flagged', () => {
|
|
62
|
-
const c = new Controller();
|
|
63
|
-
const comp = mkEl('alive');
|
|
64
|
-
buildTree(c, comp);
|
|
65
|
-
assert.equal(c.findOrphans().length, 0);
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
test('__sliceCached components are excluded (Route/MultiRoute cache)', () => {
|
|
69
|
-
const c = new Controller();
|
|
70
|
-
const comp = mkEl('cachedView');
|
|
71
|
-
buildTree(c, comp);
|
|
72
|
-
comp.isConnected = false;
|
|
73
|
-
comp.__sliceCached = true;
|
|
74
|
-
assert.equal(c.findOrphans().length, 0);
|
|
75
|
-
});
|
|
76
|
-
|
|
77
|
-
test('router-managed route-* sliceIds are excluded', () => {
|
|
78
|
-
const c = new Controller();
|
|
79
|
-
const comp = mkEl('route-Home');
|
|
80
|
-
buildTree(c, comp);
|
|
81
|
-
comp.isConnected = false;
|
|
82
|
-
assert.equal(c.findOrphans().length, 0);
|
|
83
|
-
});
|
|
84
|
-
|
|
85
|
-
test('ancestor de-dup: only the detached root is reported', () => {
|
|
86
|
-
const c = new Controller();
|
|
87
|
-
const child = mkEl('child');
|
|
88
|
-
const parent = mkEl('parent', [child]);
|
|
89
|
-
buildTree(c, parent);
|
|
90
|
-
parent.isConnected = false;
|
|
91
|
-
child.isConnected = false;
|
|
92
|
-
|
|
93
|
-
const orphans = c.findOrphans();
|
|
94
|
-
assert.equal(orphans.length, 1, 'only the root, not the descendant');
|
|
95
|
-
assert.equal(orphans[0].sliceId, 'parent');
|
|
96
|
-
});
|
|
97
|
-
|
|
98
|
-
test('descendants of a cached subtree are NOT flagged', () => {
|
|
99
|
-
const c = new Controller();
|
|
100
|
-
const child = mkEl('child');
|
|
101
|
-
const parent = mkEl('parent', [child]);
|
|
102
|
-
buildTree(c, parent);
|
|
103
|
-
parent.__sliceCached = true; // cached route root
|
|
104
|
-
parent.isConnected = false;
|
|
105
|
-
child.isConnected = false;
|
|
106
|
-
assert.equal(c.findOrphans().length, 0);
|
|
107
|
-
});
|
|
108
|
-
|
|
109
|
-
test('app-registered exclusion predicate is honored', () => {
|
|
110
|
-
const c = new Controller();
|
|
111
|
-
const comp = mkEl('special');
|
|
112
|
-
buildTree(c, comp);
|
|
113
|
-
comp.isConnected = false;
|
|
114
|
-
c.registerLeakExclusion((node) => node.sliceId === 'special');
|
|
115
|
-
assert.equal(c.findOrphans().length, 0);
|
|
116
|
-
});
|
|
117
|
-
|
|
118
|
-
test('growth heuristic trips on monotonic increase and resets on shrink', () => {
|
|
119
|
-
const c = new Controller();
|
|
120
|
-
assert.equal(c.isGrowthMonotonic(), false, 'needs >= 5 samples');
|
|
121
|
-
|
|
122
|
-
for (let i = 0; i < 5; i++) {
|
|
123
|
-
c.registerComponent(mkEl(`g${i}`));
|
|
124
|
-
c.sampleComponentGrowth();
|
|
125
|
-
}
|
|
126
|
-
assert.equal(c.isGrowthMonotonic(), true, 'sizes 1..5 are monotonic growth');
|
|
127
|
-
|
|
128
|
-
c.destroyComponent('g4');
|
|
129
|
-
c.sampleComponentGrowth(); // size dips
|
|
130
|
-
assert.equal(c.isGrowthMonotonic(), false, 'a shrink breaks the monotonic signal');
|
|
131
|
-
});
|
|
1
|
+
// Real-Controller tests for the dev-mode leak detector (findOrphans + criteria).
|
|
2
|
+
// Loads the REAL Controller via the resolve hook (same pattern as
|
|
3
|
+
// destroy-cascade.test.js) and drives it with a minimal fake DOM. Fake nodes are
|
|
4
|
+
// made `instanceof HTMLElement` because findOrphans filters on that.
|
|
5
|
+
import { register } from 'node:module';
|
|
6
|
+
import test from 'node:test';
|
|
7
|
+
import assert from 'node:assert/strict';
|
|
8
|
+
|
|
9
|
+
globalThis.alert = () => {};
|
|
10
|
+
register(new URL('./fixtures/real-runtime-loader.mjs', import.meta.url));
|
|
11
|
+
|
|
12
|
+
globalThis.HTMLElement = class HTMLElement {};
|
|
13
|
+
globalThis.slice = {
|
|
14
|
+
logger: { logError() {}, logWarning() {}, logInfo() {} },
|
|
15
|
+
events: null,
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
const { default: Controller } = await import('../Components/Structural/Controller/Controller.js');
|
|
19
|
+
|
|
20
|
+
function mkEl(sliceId, children = []) {
|
|
21
|
+
return Object.assign(Object.create(HTMLElement.prototype), {
|
|
22
|
+
sliceId,
|
|
23
|
+
tagName: 'SLICE-X',
|
|
24
|
+
isConnected: true,
|
|
25
|
+
parentComponent: null,
|
|
26
|
+
_children: children,
|
|
27
|
+
querySelectorAll() {
|
|
28
|
+
const out = [];
|
|
29
|
+
const walk = (n) => { for (const c of n._children) { out.push(c); walk(c); } };
|
|
30
|
+
walk(this);
|
|
31
|
+
return out;
|
|
32
|
+
},
|
|
33
|
+
setAttribute() {},
|
|
34
|
+
remove() { this.isConnected = false; },
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function flatten(root) {
|
|
39
|
+
const out = [root];
|
|
40
|
+
for (const c of root._children) out.push(...flatten(c));
|
|
41
|
+
return out;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function buildTree(controller, root) {
|
|
45
|
+
for (const node of flatten(root)) controller.registerComponent(node);
|
|
46
|
+
controller.registerComponentsRecursively(root);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
test('a built-then-detached component (no destroy) IS flagged', () => {
|
|
50
|
+
const c = new Controller();
|
|
51
|
+
const comp = mkEl('leaky');
|
|
52
|
+
buildTree(c, comp);
|
|
53
|
+
comp.isConnected = false; // simulate innerHTML='' / remove() without destroyComponent
|
|
54
|
+
|
|
55
|
+
const orphans = c.findOrphans();
|
|
56
|
+
assert.equal(orphans.length, 1, 'one orphan');
|
|
57
|
+
assert.equal(orphans[0].sliceId, 'leaky');
|
|
58
|
+
assert.ok(orphans[0].retainPath.includes('Object(leaky)') || orphans[0].retainPath.length >= 1);
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
test('a connected component is NOT flagged', () => {
|
|
62
|
+
const c = new Controller();
|
|
63
|
+
const comp = mkEl('alive');
|
|
64
|
+
buildTree(c, comp);
|
|
65
|
+
assert.equal(c.findOrphans().length, 0);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
test('__sliceCached components are excluded (Route/MultiRoute cache)', () => {
|
|
69
|
+
const c = new Controller();
|
|
70
|
+
const comp = mkEl('cachedView');
|
|
71
|
+
buildTree(c, comp);
|
|
72
|
+
comp.isConnected = false;
|
|
73
|
+
comp.__sliceCached = true;
|
|
74
|
+
assert.equal(c.findOrphans().length, 0);
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
test('router-managed route-* sliceIds are excluded', () => {
|
|
78
|
+
const c = new Controller();
|
|
79
|
+
const comp = mkEl('route-Home');
|
|
80
|
+
buildTree(c, comp);
|
|
81
|
+
comp.isConnected = false;
|
|
82
|
+
assert.equal(c.findOrphans().length, 0);
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
test('ancestor de-dup: only the detached root is reported', () => {
|
|
86
|
+
const c = new Controller();
|
|
87
|
+
const child = mkEl('child');
|
|
88
|
+
const parent = mkEl('parent', [child]);
|
|
89
|
+
buildTree(c, parent);
|
|
90
|
+
parent.isConnected = false;
|
|
91
|
+
child.isConnected = false;
|
|
92
|
+
|
|
93
|
+
const orphans = c.findOrphans();
|
|
94
|
+
assert.equal(orphans.length, 1, 'only the root, not the descendant');
|
|
95
|
+
assert.equal(orphans[0].sliceId, 'parent');
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
test('descendants of a cached subtree are NOT flagged', () => {
|
|
99
|
+
const c = new Controller();
|
|
100
|
+
const child = mkEl('child');
|
|
101
|
+
const parent = mkEl('parent', [child]);
|
|
102
|
+
buildTree(c, parent);
|
|
103
|
+
parent.__sliceCached = true; // cached route root
|
|
104
|
+
parent.isConnected = false;
|
|
105
|
+
child.isConnected = false;
|
|
106
|
+
assert.equal(c.findOrphans().length, 0);
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
test('app-registered exclusion predicate is honored', () => {
|
|
110
|
+
const c = new Controller();
|
|
111
|
+
const comp = mkEl('special');
|
|
112
|
+
buildTree(c, comp);
|
|
113
|
+
comp.isConnected = false;
|
|
114
|
+
c.registerLeakExclusion((node) => node.sliceId === 'special');
|
|
115
|
+
assert.equal(c.findOrphans().length, 0);
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
test('growth heuristic trips on monotonic increase and resets on shrink', () => {
|
|
119
|
+
const c = new Controller();
|
|
120
|
+
assert.equal(c.isGrowthMonotonic(), false, 'needs >= 5 samples');
|
|
121
|
+
|
|
122
|
+
for (let i = 0; i < 5; i++) {
|
|
123
|
+
c.registerComponent(mkEl(`g${i}`));
|
|
124
|
+
c.sampleComponentGrowth();
|
|
125
|
+
}
|
|
126
|
+
assert.equal(c.isGrowthMonotonic(), true, 'sizes 1..5 are monotonic growth');
|
|
127
|
+
|
|
128
|
+
c.destroyComponent('g4');
|
|
129
|
+
c.sampleComponentGrowth(); // size dips
|
|
130
|
+
assert.equal(c.isGrowthMonotonic(), false, 'a shrink breaks the monotonic signal');
|
|
131
|
+
});
|
|
@@ -1,119 +1,119 @@
|
|
|
1
|
-
import test from 'node:test';
|
|
2
|
-
import assert from 'node:assert/strict';
|
|
3
|
-
|
|
4
|
-
import { collectInvalidAllowedValueProps } from '../Components/Structural/Controller/allowedValuesValidation.js';
|
|
5
|
-
|
|
6
|
-
test('collectInvalidAllowedValueProps returns empty when value is allowed', () => {
|
|
7
|
-
const result = collectInvalidAllowedValueProps(
|
|
8
|
-
{
|
|
9
|
-
variant: {
|
|
10
|
-
type: 'string',
|
|
11
|
-
allowedValues: ['primary', 'secondary']
|
|
12
|
-
}
|
|
13
|
-
},
|
|
14
|
-
{
|
|
15
|
-
variant: 'primary'
|
|
16
|
-
}
|
|
17
|
-
);
|
|
18
|
-
|
|
19
|
-
assert.deepEqual(result, []);
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
test('collectInvalidAllowedValueProps reports invalid provided values', () => {
|
|
23
|
-
const result = collectInvalidAllowedValueProps(
|
|
24
|
-
{
|
|
25
|
-
variant: {
|
|
26
|
-
type: 'string',
|
|
27
|
-
allowedValues: ['primary', 'secondary']
|
|
28
|
-
},
|
|
29
|
-
size: {
|
|
30
|
-
type: 'number',
|
|
31
|
-
allowedValues: [12, 16]
|
|
32
|
-
}
|
|
33
|
-
},
|
|
34
|
-
{
|
|
35
|
-
variant: 'danger',
|
|
36
|
-
size: 14
|
|
37
|
-
}
|
|
38
|
-
);
|
|
39
|
-
|
|
40
|
-
assert.deepEqual(result, [
|
|
41
|
-
{
|
|
42
|
-
propName: 'variant',
|
|
43
|
-
value: 'danger',
|
|
44
|
-
allowedValues: ['primary', 'secondary']
|
|
45
|
-
},
|
|
46
|
-
{
|
|
47
|
-
propName: 'size',
|
|
48
|
-
value: 14,
|
|
49
|
-
allowedValues: [12, 16]
|
|
50
|
-
}
|
|
51
|
-
]);
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
test('collectInvalidAllowedValueProps ignores props not provided', () => {
|
|
55
|
-
const result = collectInvalidAllowedValueProps(
|
|
56
|
-
{
|
|
57
|
-
variant: {
|
|
58
|
-
type: 'string',
|
|
59
|
-
allowedValues: ['primary', 'secondary']
|
|
60
|
-
},
|
|
61
|
-
disabled: {
|
|
62
|
-
type: 'boolean'
|
|
63
|
-
}
|
|
64
|
-
},
|
|
65
|
-
{
|
|
66
|
-
disabled: true
|
|
67
|
-
}
|
|
68
|
-
);
|
|
69
|
-
|
|
70
|
-
assert.deepEqual(result, []);
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
test('collectInvalidAllowedValueProps uses strict equality', () => {
|
|
74
|
-
const result = collectInvalidAllowedValueProps(
|
|
75
|
-
{
|
|
76
|
-
amount: {
|
|
77
|
-
type: 'number',
|
|
78
|
-
allowedValues: [1]
|
|
79
|
-
},
|
|
80
|
-
active: {
|
|
81
|
-
type: 'boolean',
|
|
82
|
-
allowedValues: [true]
|
|
83
|
-
}
|
|
84
|
-
},
|
|
85
|
-
{
|
|
86
|
-
amount: '1',
|
|
87
|
-
active: 1
|
|
88
|
-
}
|
|
89
|
-
);
|
|
90
|
-
|
|
91
|
-
assert.deepEqual(result, [
|
|
92
|
-
{
|
|
93
|
-
propName: 'amount',
|
|
94
|
-
value: '1',
|
|
95
|
-
allowedValues: [1]
|
|
96
|
-
},
|
|
97
|
-
{
|
|
98
|
-
propName: 'active',
|
|
99
|
-
value: 1,
|
|
100
|
-
allowedValues: [true]
|
|
101
|
-
}
|
|
102
|
-
]);
|
|
103
|
-
});
|
|
104
|
-
|
|
105
|
-
test('collectInvalidAllowedValueProps ignores empty allowedValues arrays', () => {
|
|
106
|
-
const result = collectInvalidAllowedValueProps(
|
|
107
|
-
{
|
|
108
|
-
variant: {
|
|
109
|
-
type: 'string',
|
|
110
|
-
allowedValues: []
|
|
111
|
-
}
|
|
112
|
-
},
|
|
113
|
-
{
|
|
114
|
-
variant: 'unexpected'
|
|
115
|
-
}
|
|
116
|
-
);
|
|
117
|
-
|
|
118
|
-
assert.deepEqual(result, []);
|
|
119
|
-
});
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
|
|
4
|
+
import { collectInvalidAllowedValueProps } from '../Components/Structural/Controller/allowedValuesValidation.js';
|
|
5
|
+
|
|
6
|
+
test('collectInvalidAllowedValueProps returns empty when value is allowed', () => {
|
|
7
|
+
const result = collectInvalidAllowedValueProps(
|
|
8
|
+
{
|
|
9
|
+
variant: {
|
|
10
|
+
type: 'string',
|
|
11
|
+
allowedValues: ['primary', 'secondary']
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
variant: 'primary'
|
|
16
|
+
}
|
|
17
|
+
);
|
|
18
|
+
|
|
19
|
+
assert.deepEqual(result, []);
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
test('collectInvalidAllowedValueProps reports invalid provided values', () => {
|
|
23
|
+
const result = collectInvalidAllowedValueProps(
|
|
24
|
+
{
|
|
25
|
+
variant: {
|
|
26
|
+
type: 'string',
|
|
27
|
+
allowedValues: ['primary', 'secondary']
|
|
28
|
+
},
|
|
29
|
+
size: {
|
|
30
|
+
type: 'number',
|
|
31
|
+
allowedValues: [12, 16]
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
variant: 'danger',
|
|
36
|
+
size: 14
|
|
37
|
+
}
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
assert.deepEqual(result, [
|
|
41
|
+
{
|
|
42
|
+
propName: 'variant',
|
|
43
|
+
value: 'danger',
|
|
44
|
+
allowedValues: ['primary', 'secondary']
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
propName: 'size',
|
|
48
|
+
value: 14,
|
|
49
|
+
allowedValues: [12, 16]
|
|
50
|
+
}
|
|
51
|
+
]);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
test('collectInvalidAllowedValueProps ignores props not provided', () => {
|
|
55
|
+
const result = collectInvalidAllowedValueProps(
|
|
56
|
+
{
|
|
57
|
+
variant: {
|
|
58
|
+
type: 'string',
|
|
59
|
+
allowedValues: ['primary', 'secondary']
|
|
60
|
+
},
|
|
61
|
+
disabled: {
|
|
62
|
+
type: 'boolean'
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
disabled: true
|
|
67
|
+
}
|
|
68
|
+
);
|
|
69
|
+
|
|
70
|
+
assert.deepEqual(result, []);
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
test('collectInvalidAllowedValueProps uses strict equality', () => {
|
|
74
|
+
const result = collectInvalidAllowedValueProps(
|
|
75
|
+
{
|
|
76
|
+
amount: {
|
|
77
|
+
type: 'number',
|
|
78
|
+
allowedValues: [1]
|
|
79
|
+
},
|
|
80
|
+
active: {
|
|
81
|
+
type: 'boolean',
|
|
82
|
+
allowedValues: [true]
|
|
83
|
+
}
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
amount: '1',
|
|
87
|
+
active: 1
|
|
88
|
+
}
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
assert.deepEqual(result, [
|
|
92
|
+
{
|
|
93
|
+
propName: 'amount',
|
|
94
|
+
value: '1',
|
|
95
|
+
allowedValues: [1]
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
propName: 'active',
|
|
99
|
+
value: 1,
|
|
100
|
+
allowedValues: [true]
|
|
101
|
+
}
|
|
102
|
+
]);
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
test('collectInvalidAllowedValueProps ignores empty allowedValues arrays', () => {
|
|
106
|
+
const result = collectInvalidAllowedValueProps(
|
|
107
|
+
{
|
|
108
|
+
variant: {
|
|
109
|
+
type: 'string',
|
|
110
|
+
allowedValues: []
|
|
111
|
+
}
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
variant: 'unexpected'
|
|
115
|
+
}
|
|
116
|
+
);
|
|
117
|
+
|
|
118
|
+
assert.deepEqual(result, []);
|
|
119
|
+
});
|
|
@@ -1,44 +1,44 @@
|
|
|
1
|
-
import test from 'node:test';
|
|
2
|
-
import assert from 'node:assert/strict';
|
|
3
|
-
|
|
4
|
-
globalThis.alert = () => {};
|
|
5
|
-
|
|
6
|
-
const { default: Slice } = await import('../Slice.js');
|
|
7
|
-
|
|
8
|
-
function createSliceInstance() {
|
|
9
|
-
return new Slice({
|
|
10
|
-
paths: {},
|
|
11
|
-
themeManager: {},
|
|
12
|
-
stylesManager: {},
|
|
13
|
-
logger: {},
|
|
14
|
-
debugger: {},
|
|
15
|
-
loading: {},
|
|
16
|
-
events: {}
|
|
17
|
-
});
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
test('getEnv returns fallback for missing key and stored value for known key', () => {
|
|
21
|
-
const sliceInstance = createSliceInstance();
|
|
22
|
-
|
|
23
|
-
assert.equal(typeof sliceInstance.getEnv, 'function');
|
|
24
|
-
assert.equal(typeof sliceInstance.setPublicEnv, 'function');
|
|
25
|
-
assert.equal(sliceInstance.getEnv('SLICE_PUBLIC_MISSING', 'fallback'), 'fallback');
|
|
26
|
-
|
|
27
|
-
sliceInstance.setPublicEnv({ SLICE_PUBLIC_API_URL: 'https://api.example.com' });
|
|
28
|
-
assert.equal(sliceInstance.getEnv('SLICE_PUBLIC_API_URL'), 'https://api.example.com');
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
test('getPublicEnv returns a copy and only includes SLICE_PUBLIC_ keys', () => {
|
|
32
|
-
const sliceInstance = createSliceInstance();
|
|
33
|
-
|
|
34
|
-
sliceInstance.setPublicEnv({
|
|
35
|
-
SLICE_PUBLIC_FLAG: 'true',
|
|
36
|
-
INTERNAL_SECRET: 'hidden'
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
const snapshot = sliceInstance.getPublicEnv();
|
|
40
|
-
assert.deepEqual(snapshot, { SLICE_PUBLIC_FLAG: 'true' });
|
|
41
|
-
|
|
42
|
-
snapshot.SLICE_PUBLIC_FLAG = 'mutated';
|
|
43
|
-
assert.equal(sliceInstance.getEnv('SLICE_PUBLIC_FLAG'), 'true');
|
|
44
|
-
});
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
|
|
4
|
+
globalThis.alert = () => {};
|
|
5
|
+
|
|
6
|
+
const { default: Slice } = await import('../Slice.js');
|
|
7
|
+
|
|
8
|
+
function createSliceInstance() {
|
|
9
|
+
return new Slice({
|
|
10
|
+
paths: {},
|
|
11
|
+
themeManager: {},
|
|
12
|
+
stylesManager: {},
|
|
13
|
+
logger: {},
|
|
14
|
+
debugger: {},
|
|
15
|
+
loading: {},
|
|
16
|
+
events: {}
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
test('getEnv returns fallback for missing key and stored value for known key', () => {
|
|
21
|
+
const sliceInstance = createSliceInstance();
|
|
22
|
+
|
|
23
|
+
assert.equal(typeof sliceInstance.getEnv, 'function');
|
|
24
|
+
assert.equal(typeof sliceInstance.setPublicEnv, 'function');
|
|
25
|
+
assert.equal(sliceInstance.getEnv('SLICE_PUBLIC_MISSING', 'fallback'), 'fallback');
|
|
26
|
+
|
|
27
|
+
sliceInstance.setPublicEnv({ SLICE_PUBLIC_API_URL: 'https://api.example.com' });
|
|
28
|
+
assert.equal(sliceInstance.getEnv('SLICE_PUBLIC_API_URL'), 'https://api.example.com');
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
test('getPublicEnv returns a copy and only includes SLICE_PUBLIC_ keys', () => {
|
|
32
|
+
const sliceInstance = createSliceInstance();
|
|
33
|
+
|
|
34
|
+
sliceInstance.setPublicEnv({
|
|
35
|
+
SLICE_PUBLIC_FLAG: 'true',
|
|
36
|
+
INTERNAL_SECRET: 'hidden'
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
const snapshot = sliceInstance.getPublicEnv();
|
|
40
|
+
assert.deepEqual(snapshot, { SLICE_PUBLIC_FLAG: 'true' });
|
|
41
|
+
|
|
42
|
+
snapshot.SLICE_PUBLIC_FLAG = 'mutated';
|
|
43
|
+
assert.equal(sliceInstance.getEnv('SLICE_PUBLIC_FLAG'), 'true');
|
|
44
|
+
});
|