|
1 | 1 | import { |
| 2 | + isLoadedSymbol, |
2 | 3 | loadAssets, |
3 | 4 | loadJsonAssets, |
4 | 5 | loadScriptAssets, |
@@ -222,6 +223,30 @@ describe('Merkur component', () => { |
222 | 223 | fakeAssetListeners = {}; |
223 | 224 | assetLoadingDeferreds = {}; |
224 | 225 |
|
| 226 | + // Add module script test assets |
| 227 | + assetsDictionary['module.js'] = { |
| 228 | + name: 'module.js', |
| 229 | + type: 'script', |
| 230 | + module: true, |
| 231 | + source: 'http://localhost:4444/static/es13/module.123456.js', |
| 232 | + }; |
| 233 | + assetsDictionary['inline-module.js'] = { |
| 234 | + name: 'inline-module.js', |
| 235 | + type: 'inlineScript', |
| 236 | + module: true, |
| 237 | + source: 'console.log("Hello from module!");', |
| 238 | + }; |
| 239 | + assetsDictionary['module-with-attr.js'] = { |
| 240 | + name: 'module-with-attr.js', |
| 241 | + type: 'script', |
| 242 | + module: true, |
| 243 | + source: 'http://localhost:4444/static/es13/module-attr.123456.js', |
| 244 | + attr: { |
| 245 | + crossorigin: 'anonymous', |
| 246 | + defer: true, // This should be overridden |
| 247 | + }, |
| 248 | + }; |
| 249 | + |
225 | 250 | jest.spyOn(console, 'warn').mockImplementation(() => {}); |
226 | 251 | jest |
227 | 252 | .spyOn(document, 'createElement') |
@@ -558,6 +583,96 @@ describe('Merkur component', () => { |
558 | 583 | }), |
559 | 584 | ]); |
560 | 585 | }); |
| 586 | + |
| 587 | + it('should create module script elements with type="module"', async () => { |
| 588 | + const scriptsPromise = loadScriptAssets([assetsDictionary['module.js']]); |
| 589 | + |
| 590 | + expect(scriptsPromise).toBeInstanceOf(Promise); |
| 591 | + expect(document.createElement).toHaveBeenCalledTimes(1); |
| 592 | + expect(document.head.appendChild).toHaveBeenCalledTimes(1); |
| 593 | + |
| 594 | + resolveFakeAssets(); |
| 595 | + const sources = await scriptsPromise; |
| 596 | + |
| 597 | + expect(fakeAssetObjects[0].type).toBe('module'); |
| 598 | + expect(fakeAssetObjects[0].defer).toBeUndefined(); // defer should not be set for modules |
| 599 | + expect(sources).toStrictEqual([ |
| 600 | + getAssetWithElement('module.js', fakeAssetObjects[0]), |
| 601 | + ]); |
| 602 | + }); |
| 603 | + |
| 604 | + it('should create inline module script elements with type="module"', async () => { |
| 605 | + const scriptsPromise = loadScriptAssets([ |
| 606 | + assetsDictionary['inline-module.js'], |
| 607 | + ]); |
| 608 | + |
| 609 | + expect(scriptsPromise).toBeInstanceOf(Promise); |
| 610 | + expect(document.createElement).toHaveBeenCalledTimes(1); |
| 611 | + expect(document.head.appendChild).toHaveBeenCalledTimes(1); |
| 612 | + |
| 613 | + const sources = await scriptsPromise; |
| 614 | + |
| 615 | + expect(fakeAssetObjects[0].type).toBe('module'); |
| 616 | + expect(fakeAssetObjects[0].textContent).toBe( |
| 617 | + 'console.log("Hello from module!");', |
| 618 | + ); |
| 619 | + expect(sources).toStrictEqual([ |
| 620 | + getAssetWithElement('inline-module.js', fakeAssetObjects[0]), |
| 621 | + ]); |
| 622 | + }); |
| 623 | + |
| 624 | + it('should handle module scripts with custom attributes correctly', async () => { |
| 625 | + const scriptsPromise = loadScriptAssets([ |
| 626 | + assetsDictionary['module-with-attr.js'], |
| 627 | + ]); |
| 628 | + |
| 629 | + expect(document.createElement).toHaveBeenCalledTimes(1); |
| 630 | + expect(document.head.appendChild).toHaveBeenCalledTimes(1); |
| 631 | + |
| 632 | + resolveFakeAssets(); |
| 633 | + const sources = await scriptsPromise; |
| 634 | + |
| 635 | + expect(fakeAssetObjects[0].type).toBe('module'); |
| 636 | + expect(fakeAssetObjects[0].defer).toBeUndefined(); // defer should be overridden for modules |
| 637 | + expect(fakeAssetObjects[0].crossorigin).toBe('anonymous'); // custom attributes should be preserved |
| 638 | + expect(sources).toStrictEqual([ |
| 639 | + getAssetWithElement('module-with-attr.js', fakeAssetObjects[0], { |
| 640 | + attr: { |
| 641 | + crossorigin: 'anonymous', |
| 642 | + defer: false, // defer should be overridden for modules |
| 643 | + }, |
| 644 | + }), |
| 645 | + ]); |
| 646 | + }); |
| 647 | + |
| 648 | + it('should return a promise that rejects when a module script fails to load', async () => { |
| 649 | + expect.assertions(3); |
| 650 | + const scriptsPromise = loadScriptAssets([assetsDictionary['module.js']]); |
| 651 | + |
| 652 | + expect(document.createElement).toHaveBeenCalledTimes(1); |
| 653 | + |
| 654 | + rejectFakeAssets(); |
| 655 | + |
| 656 | + try { |
| 657 | + await scriptsPromise; |
| 658 | + } catch (error) { |
| 659 | + expect(fakeAssetObjects[0].remove).toHaveBeenCalledTimes(1); |
| 660 | + expect(error).toBeInstanceOf(Error); |
| 661 | + } |
| 662 | + }); |
| 663 | + |
| 664 | + it('should resolve if a module script (not created by loadScriptAssets) is already present in the DOM', async () => { |
| 665 | + const script = fakeAssetObjectGenerator('script'); |
| 666 | + script.src = assetsDictionary['module.js'].source; |
| 667 | + script.type = 'module'; |
| 668 | + script[isLoadedSymbol] = true; // Mark as already loaded |
| 669 | + |
| 670 | + const scriptsPromise = loadScriptAssets([assetsDictionary['module.js']]); |
| 671 | + const sources = await scriptsPromise; |
| 672 | + |
| 673 | + expect(document.createElement).toHaveBeenCalledTimes(0); |
| 674 | + expect(sources).toStrictEqual([getAssetWithElement('module.js', script)]); |
| 675 | + }); |
561 | 676 | }); |
562 | 677 |
|
563 | 678 | describe('loadJsonAssets() function', () => { |
@@ -1004,5 +1119,40 @@ describe('Merkur component', () => { |
1004 | 1119 |
|
1005 | 1120 | expect(console.warn).not.toHaveBeenCalled(); |
1006 | 1121 | }); |
| 1122 | + |
| 1123 | + it('should load module scripts correctly', async () => { |
| 1124 | + const assetsPromise = loadAssets([ |
| 1125 | + assetsDictionary['module.js'], |
| 1126 | + assetsDictionary['inline-module.js'], |
| 1127 | + assetsDictionary['module-with-attr.js'], |
| 1128 | + ]); |
| 1129 | + |
| 1130 | + resolveFakeAssets(); |
| 1131 | + const sources = await assetsPromise; |
| 1132 | + |
| 1133 | + expect(document.createElement).toHaveBeenCalledTimes(3); |
| 1134 | + expect(sources).toStrictEqual([ |
| 1135 | + getAssetWithElement('module.js', fakeAssetObjects[0]), |
| 1136 | + getAssetWithElement('inline-module.js', fakeAssetObjects[1]), |
| 1137 | + getAssetWithElement('module-with-attr.js', fakeAssetObjects[2], { |
| 1138 | + attr: { |
| 1139 | + crossorigin: 'anonymous', |
| 1140 | + defer: false, // defer should be overridden for modules |
| 1141 | + }, |
| 1142 | + }), |
| 1143 | + ]); |
| 1144 | + |
| 1145 | + // Check that all scripts have the correct type |
| 1146 | + expect(fakeAssetObjects[0].type).toBe('module'); |
| 1147 | + expect(fakeAssetObjects[1].type).toBe('module'); |
| 1148 | + expect(fakeAssetObjects[2].type).toBe('module'); |
| 1149 | + |
| 1150 | + // Check that defer is not set for module scripts |
| 1151 | + expect(fakeAssetObjects[0].defer).toBeUndefined(); |
| 1152 | + expect(fakeAssetObjects[1].defer).toBeUndefined(); |
| 1153 | + expect(fakeAssetObjects[2].defer).toBeUndefined(); |
| 1154 | + |
| 1155 | + expect(console.warn).not.toHaveBeenCalled(); |
| 1156 | + }); |
1007 | 1157 | }); |
1008 | 1158 | }); |
0 commit comments