@schukai/monster 4.153.0 → 4.154.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.
Files changed (52) hide show
  1. package/package.json +1 -1
  2. package/source/components/form/message-state-button.mjs +1 -1
  3. package/source/components/form/select-link.mjs +12 -1
  4. package/source/components/state/log.mjs +3 -7
  5. package/source/components/state/thread.mjs +3 -7
  6. package/source/components/time/day.mjs +0 -19
  7. package/source/components/time/month-calendar.mjs +5 -1
  8. package/source/data/buildmap.mjs +6 -5
  9. package/source/data/buildtree.mjs +0 -8
  10. package/source/data/datasource.mjs +3 -10
  11. package/source/data/diff.mjs +13 -12
  12. package/source/data/extend.mjs +23 -1
  13. package/source/data/pathfinder.mjs +228 -32
  14. package/source/data/transformer.mjs +2 -2
  15. package/source/dom/customcontrol.mjs +35 -9
  16. package/source/dom/customelement.mjs +293 -31
  17. package/source/dom/slotted.mjs +46 -31
  18. package/source/dom/template.mjs +4 -1
  19. package/source/dom/updater.mjs +57 -17
  20. package/source/types/base.mjs +7 -6
  21. package/source/types/basewithoptions.mjs +1 -8
  22. package/source/types/internal.mjs +4 -10
  23. package/source/types/node.mjs +1 -3
  24. package/source/types/observerlist.mjs +9 -17
  25. package/source/types/proxyobserver.mjs +51 -12
  26. package/source/types/tokenlist.mjs +13 -8
  27. package/source/util/clone.mjs +44 -14
  28. package/test/cases/browser-bundle.mjs +75 -0
  29. package/test/cases/components/time/month-calendar.mjs +115 -0
  30. package/test/cases/data/buildmap.mjs +14 -0
  31. package/test/cases/data/buildtree.mjs +49 -0
  32. package/test/cases/data/diff.mjs +86 -0
  33. package/test/cases/data/extend.mjs +52 -0
  34. package/test/cases/data/pathfinder-resolution-baseline.mjs +322 -0
  35. package/test/cases/data/pathfinder.mjs +163 -0
  36. package/test/cases/data/transformer.mjs +26 -0
  37. package/test/cases/dom/customcontrol.mjs +85 -0
  38. package/test/cases/dom/customelement-rebind-baseline.mjs +323 -0
  39. package/test/cases/dom/customelement-teardown.mjs +408 -0
  40. package/test/cases/dom/customelement.mjs +408 -0
  41. package/test/cases/dom/dimension.mjs +34 -10
  42. package/test/cases/dom/slotted-nodes.mjs +48 -1
  43. package/test/cases/dom/template.mjs +11 -0
  44. package/test/cases/dom/updater-remove.mjs +198 -0
  45. package/test/cases/dom/updater.mjs +118 -0
  46. package/test/cases/packaging.mjs +213 -0
  47. package/test/cases/types/base.mjs +50 -0
  48. package/test/cases/types/observerlist.mjs +103 -1
  49. package/test/cases/types/proxyobserver.mjs +189 -0
  50. package/test/cases/types/regex.mjs +4 -0
  51. package/test/cases/types/tokenlist.mjs +64 -1
  52. package/test/cases/util/clone.mjs +115 -0
@@ -587,6 +587,154 @@ describe('DOM', function () {
587
587
  linkedUpdater[internalSymbol].subject.detachObserver(observer);
588
588
  }
589
589
  });
590
+
591
+ it('should not re-render unrelated NaN option paths on every change issue #571', async function () {
592
+ const htmlTAG = 'monster-testclass-nan-identity-571';
593
+
594
+ if (!customElements.get(htmlTAG)) {
595
+ class NanIdentityComponent extends CustomElement {
596
+ static getTag() {
597
+ return htmlTAG;
598
+ }
599
+
600
+ get defaults() {
601
+ return Object.assign({}, super.defaults, {
602
+ measured: NaN,
603
+ unrelated: 0,
604
+ templates: {
605
+ main: '<span id="nan-measured" data-monster-replace="path:measured | call:renderMeasured"></span><span id="nan-unrelated" data-monster-replace="path:unrelated"></span>',
606
+ },
607
+ });
608
+ }
609
+
610
+ [updaterTransformerMethodsSymbol]() {
611
+ return {
612
+ renderMeasured: (value) => {
613
+ this.renderCalls = (this.renderCalls || 0) + 1;
614
+ return Number.isNaN(value) ? 'n/a' : String(value);
615
+ },
616
+ };
617
+ }
618
+ }
619
+
620
+ registerCustomElement(NanIdentityComponent);
621
+ }
622
+
623
+ const element = document.createElement(htmlTAG);
624
+ document.getElementById('test1').appendChild(element);
625
+ await new Promise((resolve) => setTimeout(resolve, 20));
626
+
627
+ expect(element.shadowRoot.querySelector('#nan-measured').textContent).to.equal('n/a');
628
+ const afterAssembly = element.renderCalls;
629
+ expect(afterAssembly).to.be.greaterThan(0);
630
+
631
+ element.setOption('unrelated', 1);
632
+ await new Promise((resolve) => setTimeout(resolve, 30));
633
+
634
+ expect(element.shadowRoot.querySelector('#nan-unrelated').textContent).to.equal('1');
635
+ expect(element.renderCalls).to.equal(afterAssembly);
636
+ });
637
+
638
+ it('should not accumulate render work for a NaN option across repeated changes issue #571', async function () {
639
+ const htmlTAG = 'monster-testclass-nan-identity-repeat-571';
640
+
641
+ if (!customElements.get(htmlTAG)) {
642
+ class NanRepeatComponent extends CustomElement {
643
+ static getTag() {
644
+ return htmlTAG;
645
+ }
646
+
647
+ get defaults() {
648
+ return Object.assign({}, super.defaults, {
649
+ measured: NaN,
650
+ counter: 0,
651
+ templates: {
652
+ main: '<span id="nan-repeat-measured" data-monster-replace="path:measured | call:renderMeasured"></span><span id="nan-repeat-counter" data-monster-replace="path:counter"></span>',
653
+ },
654
+ });
655
+ }
656
+
657
+ [updaterTransformerMethodsSymbol]() {
658
+ return {
659
+ renderMeasured: (value) => {
660
+ this.renderCalls = (this.renderCalls || 0) + 1;
661
+ return Number.isNaN(value) ? 'n/a' : String(value);
662
+ },
663
+ };
664
+ }
665
+ }
666
+
667
+ registerCustomElement(NanRepeatComponent);
668
+ }
669
+
670
+ const element = document.createElement(htmlTAG);
671
+ document.getElementById('test1').appendChild(element);
672
+ await new Promise((resolve) => setTimeout(resolve, 20));
673
+
674
+ const afterAssembly = element.renderCalls;
675
+
676
+ for (let i = 1; i <= 5; i++) {
677
+ element.setOption('counter', i);
678
+ await new Promise((resolve) => setTimeout(resolve, 20));
679
+ }
680
+
681
+ expect(element.shadowRoot.querySelector('#nan-repeat-counter').textContent).to.equal('5');
682
+ expect(element.renderCalls).to.equal(afterAssembly);
683
+ });
684
+
685
+ it('should remove stale updater keys after replacing an option object issue #565', async function () {
686
+ const htmlTAG = 'monster-testclass-object-sync-565';
687
+
688
+ if (!customElements.get(htmlTAG)) {
689
+ class ObjectSyncComponent extends CustomElement {
690
+ static getTag() {
691
+ return htmlTAG;
692
+ }
693
+
694
+ get defaults() {
695
+ return Object.assign({}, super.defaults, {
696
+ settings: {a: 'initial', b: 'retained'},
697
+ templates: {
698
+ main: '<span data-monster-replace="path:settings.a"></span>',
699
+ },
700
+ });
701
+ }
702
+ }
703
+
704
+ registerCustomElement(ObjectSyncComponent);
705
+ }
706
+
707
+ const element = document.createElement(htmlTAG);
708
+ document.getElementById('test1').appendChild(element);
709
+ await new Promise((resolve) => setTimeout(resolve, 20));
710
+
711
+ let linkedUpdater;
712
+ for (const group of element[customElementUpdaterLinkSymbol]) {
713
+ for (const updater of group) {
714
+ linkedUpdater = updater;
715
+ break;
716
+ }
717
+ if (linkedUpdater) break;
718
+ }
719
+ expect(linkedUpdater).to.exist;
720
+
721
+ element.setOptions({settings: {a: 'merged'}});
722
+ await new Promise((resolve) => setTimeout(resolve, 20));
723
+ expect(linkedUpdater.getSubject().settings).to.deep.include({
724
+ a: 'merged',
725
+ b: 'retained',
726
+ });
727
+
728
+ const settingsProxy = linkedUpdater.getSubject().settings;
729
+ element.setOption('settings', {a: 'replacement'});
730
+ await new Promise((resolve) => setTimeout(resolve, 20));
731
+
732
+ expect(linkedUpdater.getSubject().settings).to.equal(settingsProxy);
733
+ expect(linkedUpdater.getSubject().settings).to.deep.equal({
734
+ a: 'replacement',
735
+ });
736
+ expect(element.shadowRoot.textContent).to.equal('replacement');
737
+ });
590
738
  })
591
739
 
592
740
  describe('setOptions()', function () {
@@ -932,10 +1080,270 @@ describe('DOM', function () {
932
1080
  expect(d.hasNode(t1)).to.be.true;
933
1081
 
934
1082
  })
1083
+ // net: containment that already works and must keep working
1084
+ it("hasNode should stay false for detached and foreign nodes issue #574", function () {
1085
+ let d = document.createElement('monster-testclass');
1086
+ let other = document.createElement('monster-testclass');
1087
+
1088
+ let own = document.createElement('div');
1089
+ let foreign = document.createElement('div');
1090
+ let foreignChild = document.createElement('span');
1091
+ foreign.appendChild(foreignChild);
1092
+
1093
+ d.appendChild(own);
1094
+ other.appendChild(foreign);
1095
+
1096
+ let div = document.getElementById('test1');
1097
+ div.append(d);
1098
+ div.append(other);
1099
+
1100
+ let detached = document.createElement('p');
1101
+
1102
+ expect(d.hasNode(own)).to.be.true;
1103
+ expect(d.hasNode(detached)).to.be.false;
1104
+ expect(d.hasNode(foreign)).to.be.false;
1105
+ expect(d.hasNode(foreignChild)).to.be.false;
1106
+ })
1107
+
1108
+ // regression: a node inside the shadow root of a child custom element
1109
+ it("hasNode should find a node inside the shadow root of a child element issue #574", async function () {
1110
+ const parentTAG = 'monster-testclass-hasnode-574';
1111
+ const childTAG = 'monster-testclass-hasnode-child-574';
1112
+
1113
+ if (!customElements.get(childTAG)) {
1114
+ class HasNodeChild extends CustomElement {
1115
+ static getTag() {
1116
+ return childTAG;
1117
+ }
1118
+
1119
+ get defaults() {
1120
+ return Object.assign({}, super.defaults, {
1121
+ templates: {
1122
+ main: '<span id="deep-node">deep</span>',
1123
+ },
1124
+ });
1125
+ }
1126
+ }
1127
+
1128
+ registerCustomElement(HasNodeChild);
1129
+ }
1130
+
1131
+ if (!customElements.get(parentTAG)) {
1132
+ class HasNodeParent extends CustomElement {
1133
+ static getTag() {
1134
+ return parentTAG;
1135
+ }
1136
+
1137
+ get defaults() {
1138
+ return Object.assign({}, super.defaults, {
1139
+ templates: {
1140
+ main: '<div id="host-slot"></div>',
1141
+ },
1142
+ });
1143
+ }
1144
+ }
1145
+
1146
+ registerCustomElement(HasNodeParent);
1147
+ }
1148
+
1149
+ const parent = document.createElement(parentTAG);
1150
+ document.getElementById('test1').appendChild(parent);
1151
+ await new Promise((resolve) => setTimeout(resolve, 20));
1152
+
1153
+ const child = document.createElement(childTAG);
1154
+ parent.shadowRoot.getElementById('host-slot').appendChild(child);
1155
+ await new Promise((resolve) => setTimeout(resolve, 20));
1156
+
1157
+ const deep = child.shadowRoot.getElementById('deep-node');
1158
+ expect(deep).to.not.equal(null);
1159
+
1160
+ // the child itself lives in the parent shadow root and is found today
1161
+ expect(parent.hasNode(child)).to.be.true;
1162
+ // the node inside the child shadow root is the gap being closed
1163
+ expect(parent.hasNode(deep)).to.be.true;
1164
+ })
1165
+
1166
+ it("hasNode should find a node nested two shadow roots deep issue #574", async function () {
1167
+ const outerTAG = 'monster-testclass-hasnode-outer-574';
1168
+ const middleTAG = 'monster-testclass-hasnode-middle-574';
1169
+
1170
+ if (!customElements.get(middleTAG)) {
1171
+ class HasNodeMiddle extends CustomElement {
1172
+ static getTag() {
1173
+ return middleTAG;
1174
+ }
1175
+
1176
+ get defaults() {
1177
+ return Object.assign({}, super.defaults, {
1178
+ templates: {
1179
+ main: '<div id="middle-host"></div>',
1180
+ },
1181
+ });
1182
+ }
1183
+ }
1184
+
1185
+ registerCustomElement(HasNodeMiddle);
1186
+ }
1187
+
1188
+ if (!customElements.get(outerTAG)) {
1189
+ class HasNodeOuter extends CustomElement {
1190
+ static getTag() {
1191
+ return outerTAG;
1192
+ }
1193
+
1194
+ get defaults() {
1195
+ return Object.assign({}, super.defaults, {
1196
+ templates: {
1197
+ main: '<div id="outer-host"></div>',
1198
+ },
1199
+ });
1200
+ }
1201
+ }
1202
+
1203
+ registerCustomElement(HasNodeOuter);
1204
+ }
1205
+
1206
+ const outer = document.createElement(outerTAG);
1207
+ document.getElementById('test1').appendChild(outer);
1208
+ await new Promise((resolve) => setTimeout(resolve, 20));
1209
+
1210
+ const middle = document.createElement(middleTAG);
1211
+ outer.shadowRoot.getElementById('outer-host').appendChild(middle);
1212
+ await new Promise((resolve) => setTimeout(resolve, 20));
1213
+
1214
+ const leaf = document.createElement('b');
1215
+ middle.shadowRoot.getElementById('middle-host').appendChild(leaf);
1216
+
1217
+ expect(outer.hasNode(leaf)).to.be.true;
1218
+
1219
+ const unrelated = document.createElement('b');
1220
+ expect(outer.hasNode(unrelated)).to.be.false;
1221
+ })
1222
+
935
1223
 
936
1224
 
937
1225
  })
938
1226
 
1227
+ describe('option resolution issue #576', function () {
1228
+
1229
+ let mocks;
1230
+ beforeEach(() => {
1231
+ mocks = document.getElementById('mocks');
1232
+ mocks.innerHTML = html1;
1233
+ })
1234
+
1235
+ // Pathfinder.exists() is load bearing as an input validator here:
1236
+ // the option name is undefined for every unknown
1237
+ // data-monster-option-* attribute, and the constructor plus every
1238
+ // attributeChangedCallback runs through it.
1239
+ it('should construct with an unknown data-monster-option attribute', async function () {
1240
+ const htmlTAG = 'monster-testclass-unknown-option-576';
1241
+
1242
+ if (!customElements.get(htmlTAG)) {
1243
+ class UnknownOptionComponent extends CustomElement {
1244
+ static getTag() {
1245
+ return htmlTAG;
1246
+ }
1247
+
1248
+ get defaults() {
1249
+ return Object.assign({}, super.defaults, {
1250
+ known: 'initial',
1251
+ templates: {
1252
+ main: '<span id="known-value" data-monster-replace="path:known"></span>',
1253
+ },
1254
+ });
1255
+ }
1256
+ }
1257
+
1258
+ registerCustomElement(UnknownOptionComponent);
1259
+ }
1260
+
1261
+ const element = document.createElement(htmlTAG);
1262
+ element.setAttribute('data-monster-option-foo', 'bar');
1263
+ element.setAttribute('data-monster-option-deeply-unknown-path', '1');
1264
+ document.getElementById('test1').appendChild(element);
1265
+ await new Promise((resolve) => setTimeout(resolve, 20));
1266
+
1267
+ expect(element.hasAttribute('data-monster-error')).to.be.false;
1268
+ expect(element.shadowRoot.querySelector('#known-value').textContent).to.equal('initial');
1269
+ expect(element.getOption('foo')).to.equal(undefined);
1270
+ });
1271
+
1272
+ it('should survive a runtime change of an unknown data-monster-option attribute', async function () {
1273
+ const htmlTAG = 'monster-testclass-unknown-option-runtime-576';
1274
+
1275
+ if (!customElements.get(htmlTAG)) {
1276
+ class UnknownOptionRuntimeComponent extends CustomElement {
1277
+ static getTag() {
1278
+ return htmlTAG;
1279
+ }
1280
+
1281
+ get defaults() {
1282
+ return Object.assign({}, super.defaults, {
1283
+ known: 'initial',
1284
+ templates: {
1285
+ main: '<span id="runtime-value" data-monster-replace="path:known"></span>',
1286
+ },
1287
+ });
1288
+ }
1289
+ }
1290
+
1291
+ registerCustomElement(UnknownOptionRuntimeComponent);
1292
+ }
1293
+
1294
+ const element = document.createElement(htmlTAG);
1295
+ document.getElementById('test1').appendChild(element);
1296
+ await new Promise((resolve) => setTimeout(resolve, 20));
1297
+
1298
+ element.setAttribute('data-monster-option-foo', 'bar');
1299
+ element.setAttribute('data-monster-option-known', 'changed');
1300
+ await new Promise((resolve) => setTimeout(resolve, 30));
1301
+
1302
+ expect(element.hasAttribute('data-monster-error')).to.be.false;
1303
+ expect(element.getOption('known')).to.equal('changed');
1304
+ expect(element.shadowRoot.querySelector('#runtime-value').textContent).to.equal('changed');
1305
+ });
1306
+
1307
+ it('should return declared defaults including falsy ones', async function () {
1308
+ const htmlTAG = 'monster-testclass-falsy-defaults-576';
1309
+
1310
+ if (!customElements.get(htmlTAG)) {
1311
+ class FalsyDefaultsComponent extends CustomElement {
1312
+ static getTag() {
1313
+ return htmlTAG;
1314
+ }
1315
+
1316
+ get defaults() {
1317
+ return Object.assign({}, super.defaults, {
1318
+ zero: 0,
1319
+ flag: false,
1320
+ text: '',
1321
+ nothing: null,
1322
+ absent: undefined,
1323
+ templates: {
1324
+ main: '<span></span>',
1325
+ },
1326
+ });
1327
+ }
1328
+ }
1329
+
1330
+ registerCustomElement(FalsyDefaultsComponent);
1331
+ }
1332
+
1333
+ const element = document.createElement(htmlTAG);
1334
+ document.getElementById('test1').appendChild(element);
1335
+ await new Promise((resolve) => setTimeout(resolve, 20));
1336
+
1337
+ expect(element.getOption('zero', 99)).to.equal(0);
1338
+ expect(element.getOption('flag', true)).to.equal(false);
1339
+ expect(element.getOption('text', 'fallback')).to.equal('');
1340
+ expect(element.getOption('nothing', 'fallback')).to.equal(null);
1341
+ expect(element.getOption('absent', 'fallback')).to.equal('fallback');
1342
+ expect(element.getOption('templates.main', 'fallback')).to.equal('<span></span>');
1343
+ expect(element.getOption('missing.deep.path', 'fallback')).to.equal('fallback');
1344
+ });
1345
+ });
1346
+
939
1347
  describe('mutation observer lifecycle', function () {
940
1348
  it('should coalesce child mutations without a refresh loop issue #517', async function () {
941
1349
  const htmlTAG = 'monster-testclass-mutation-loop-517';
@@ -6,6 +6,21 @@ import {getGlobal} from "../../../source/types/global.mjs";
6
6
  import {detectRuntimeEnvironment} from "../../../source/util/runtime.mjs";
7
7
 
8
8
 
9
+ /**
10
+ * Puts the real window back. Deleting the global instead would make
11
+ * initJSDOM() build a second jsdom for every file that runs later, while
12
+ * CustomElement still extends the HTMLElement of the first one, so every
13
+ * custom element defined afterwards fails to upgrade.
14
+ */
15
+ function restoreWindow(previousWindow) {
16
+ if (previousWindow === undefined) {
17
+ delete getGlobal().window;
18
+ return;
19
+ }
20
+
21
+ getGlobal().window = previousWindow;
22
+ }
23
+
9
24
  function getMockWindow(dpi) {
10
25
 
11
26
  if(detectRuntimeEnvironment() === 'browser') {
@@ -43,16 +58,19 @@ describe('dimension', () => {
43
58
  });
44
59
  })
45
60
 
61
+ let originalWindow;
62
+
46
63
  beforeEach(() => {
47
64
 
48
65
  const testDpi = 96;
49
66
  const testWindow = getMockWindow(testDpi);
67
+ originalWindow = getGlobal().window;
50
68
  getGlobal().window = testWindow;
51
69
 
52
70
  });
53
71
 
54
72
  afterEach(() => {
55
- delete getGlobal().window;
73
+ restoreWindow(originalWindow);
56
74
  });
57
75
 
58
76
  describe('convertToPixels', () => {
@@ -123,24 +141,30 @@ describe('dimension', () => {
123
141
  it('should return the correct device DPI', () => {
124
142
  const testDpi = 96;
125
143
  const testWindow = getMockWindow(testDpi);
144
+ const previousWindow = getGlobal().window;
126
145
  getGlobal().window = testWindow;
127
146
 
128
- const deviceDpi = getDeviceDPI();
129
- expect(deviceDpi).to.equal(testDpi * testWindow.devicePixelRatio);
130
-
131
- delete getGlobal().window;
147
+ try {
148
+ const deviceDpi = getDeviceDPI();
149
+ expect(deviceDpi).to.equal(testDpi * testWindow.devicePixelRatio);
150
+ } finally {
151
+ restoreWindow(previousWindow);
152
+ }
132
153
  });
133
154
 
134
155
  it('should cache the result and return the same value', () => {
135
156
  const testDpi = 96;
136
157
  const testWindow = getMockWindow(testDpi);
158
+ const previousWindow = getGlobal().window;
137
159
  getGlobal().window = testWindow;
138
160
 
139
- const deviceDpi1 = getDeviceDPI();
140
- const deviceDpi2 = getDeviceDPI();
141
- expect(deviceDpi1).to.equal(deviceDpi2);
142
-
143
- delete getGlobal().window;
161
+ try {
162
+ const deviceDpi1 = getDeviceDPI();
163
+ const deviceDpi2 = getDeviceDPI();
164
+ expect(deviceDpi1).to.equal(deviceDpi2);
165
+ } finally {
166
+ restoreWindow(previousWindow);
167
+ }
144
168
  });
145
169
  });
146
170
 
@@ -1,14 +1,61 @@
1
1
  import {expect} from 'chai';
2
2
  import {getSlottedNodes} from "../../../source/dom/slotted.mjs";
3
+ import {getSlottedElements} from "../../../source/dom/slotted.mjs";
4
+ import {initJSDOM} from "../../util/jsdom.mjs";
3
5
 
4
6
 
5
7
  describe('getSlottedNodes', () => {
6
8
 
9
+ before(async () => {
10
+ await initJSDOM();
11
+ });
12
+
7
13
 
8
14
  it('should return an empty Set if there is no ShadowRoot', () => {
9
15
  expect(getSlottedNodes).is.a('function');
10
16
  });
11
17
 
18
+ it('should ignore text nodes when applying an element query issue #567', () => {
19
+ const host = document.createElement('div');
20
+ host.append(' whitespace ');
21
+ const child = document.createElement('span');
22
+ child.className = 'match';
23
+ host.appendChild(child);
24
+ host.attachShadow({mode: 'open'}).innerHTML = '<slot></slot>';
25
+ document.body.appendChild(host);
26
+
27
+ expect([...getSlottedNodes.call(host, '.match')]).to.deep.equal([child]);
28
+ host.remove();
29
+ });
30
+
31
+ it('should match empty and spaced slot names without selector interpolation issue #567', () => {
32
+ const host = document.createElement('div');
33
+ const defaultChild = document.createElement('span');
34
+ const namedChild = document.createElement('span');
35
+ namedChild.slot = 'some slot';
36
+ host.append(defaultChild, namedChild);
37
+ host.attachShadow({mode: 'open'}).innerHTML =
38
+ '<slot name=""></slot><slot name="some slot"></slot>';
39
+ document.body.appendChild(host);
40
+
41
+ expect([...getSlottedElements.call(host, undefined, null)]).to.deep.equal([defaultChild]);
42
+ expect([...getSlottedElements.call(host, undefined, 'some slot')]).to.deep.equal([namedChild]);
43
+ host.remove();
44
+ });
45
+
46
+ it('should accept the internally managed closed shadow root issue #567', () => {
47
+ const host = document.createElement('div');
48
+ const child = document.createElement('span');
49
+ host.appendChild(child);
50
+ const root = host.attachShadow({mode: 'closed'});
51
+ root.innerHTML = '<slot></slot>';
52
+ document.body.appendChild(host);
53
+
54
+ expect(host.shadowRoot).to.equal(null);
55
+ expect([...getSlottedElements.call(host, undefined, undefined, root)])
56
+ .to.deep.equal([child]);
57
+ host.remove();
58
+ });
12
59
 
13
- });
14
60
 
61
+ });
@@ -81,6 +81,17 @@ describe('Template', function () {
81
81
  expect(t).is.instanceof(Template);
82
82
 
83
83
  })
84
+
85
+ it('should resolve from a text node context issue #567', function () {
86
+ const container = document.createElement('div');
87
+ const text = document.createTextNode('context');
88
+ container.appendChild(text);
89
+ document.body.appendChild(container);
90
+
91
+ const template = findDocumentTemplate('mytemplate', text);
92
+ expect(template).to.be.instanceof(Template);
93
+ container.remove();
94
+ });
84
95
  });
85
96
 
86
97
  describe('Thmened Template', function () {