diff --git a/packages/semcom-sdk/lib/component/services/register-component.service.spec.ts b/packages/semcom-sdk/lib/component/services/register-component.service.spec.ts index 539cc33a..094e1120 100644 --- a/packages/semcom-sdk/lib/component/services/register-component.service.spec.ts +++ b/packages/semcom-sdk/lib/component/services/register-component.service.spec.ts @@ -3,12 +3,36 @@ import { RegisterComponentService } from './register-component.service'; describe('RegisterComponentService', () => { + const testComponent = { + uri: 'https://www.google.com/test.js', + tag: 'component', + } as ComponentMetadata; + + let testComponent2: ComponentMetadata; + let service: RegisterComponentService; + const addedUris = new Set(); + beforeEach(() => { service = new RegisterComponentService(); + testComponent2 = { + uri: 'https://www.google.com/test2.js', + tag: 'component2', + } as ComponentMetadata; + + global.eval = jest.fn(async (url) => ({ 'default': {} })); + customElements.define = jest.fn((tag, html) => { addedUris.add(tag); }); + customElements.get = jest.fn((tag) => addedUris.has(tag) ? {} as CustomElementConstructor : undefined); + + }); + + afterEach(() => { + + addedUris.clear(); + }); it('should be correctly instantiated', () => { @@ -66,12 +90,14 @@ describe('RegisterComponentService', () => { it('should throw error when componentMetadata.uri is not found', async () => { + global.eval = jest.fn(async (url) => { throw new Error('Not found'); }); + const mockComponent = { uri: './../../mock/non-working-component.ts', tag: 'app-component-4', } as ComponentMetadata; - await expect(service.register(mockComponent)).rejects.toThrow(); + await expect(service.register(mockComponent)).rejects.toThrow('Failed to import or register componentMetadata: Error: Not found'); }); @@ -96,4 +122,59 @@ describe('RegisterComponentService', () => { }); + it('should generate a correct tag', async () => { + + const tag = await service.register(testComponent); + expect(tag.startsWith('semcom-component-')).toBe(true); + + }); + + it('should eval and define a component', async () => { + + await service.register(testComponent); + expect(global.eval).toBeCalledTimes(1); + expect(service.isRegistered(testComponent)).toBeTruthy(); + + }); + + it('should not reimport an existing component if it was already defined', async () => { + + await service.register(testComponent); + await service.register(testComponent); + + expect(global.eval).toBeCalledTimes(1); + expect(customElements.define).toBeCalledTimes(1); + + }); + + it('should only define a tag once, if two are imported at the same time', async () => { + + await Promise.all([ service.register(testComponent), service.register(testComponent) ]); + expect(customElements.define).toBeCalledTimes(1); + + }); + + it('should return different html tags for components with different tags', async () => { + + const tag = await service.register(testComponent); + await expect(service.register(testComponent2)).resolves.not.toBe(tag); + + }); + + it('should return a different html Tag for components with the same tag but a different URI', async () => { + + testComponent2.tag = 'component'; + + const tag = await service.register(testComponent); + await expect(service.register(testComponent2)).resolves.not.toBe(tag); + + }); + + it('should return the same tag if a component is imported twice', async () => { + + const tag = await service.register(testComponent); + await expect(service.register(testComponent)).resolves.toBe(tag); + + }); + }); diff --git a/packages/semcom-sdk/lib/component/services/register-component.service.ts b/packages/semcom-sdk/lib/component/services/register-component.service.ts index 9e2f1a37..90d5d615 100644 --- a/packages/semcom-sdk/lib/component/services/register-component.service.ts +++ b/packages/semcom-sdk/lib/component/services/register-component.service.ts @@ -1,4 +1,5 @@ /* eslint-disable no-eval -- webpack can't handle dynamic imports */ +import jsSHA from 'jssha'; import { AbstractRegisterComponentService, ComponentMetadata } from '@digita-ai/semcom-core'; export class RegisterComponentService extends AbstractRegisterComponentService { @@ -23,31 +24,29 @@ export class RegisterComponentService extends AbstractRegisterComponentService { } - const tag = `semcom-${ componentMetadata.tag }-${ btoa(Date.now().toString()).replace('==', '').toLowerCase() }`; + const shaObj = new jsSHA('SHAKE256', 'TEXT'); + shaObj.update(componentMetadata.uri); + const hash = shaObj.getHash('B64', { outputLen: 64 }).replace(/[+=/]/g, '').toLowerCase(); - let elementComponent; + const tag = `semcom-${ componentMetadata.tag }-${ hash }`; try { - elementComponent = await eval(`import("${componentMetadata.uri}")`); - - } catch (error) { + if (!customElements.get(tag)) { - throw new Error('Something went wrong during import'); + const elementComponent = await eval(`import("${encodeURI(componentMetadata.uri)}")`); - } + if (!customElements.get(tag)) { - try { - - if (!customElements.get(tag)) { + customElements.define(tag, elementComponent.default); - customElements.define(tag, elementComponent.default); + } } } catch (error) { - throw Error(`Failed to register componentMetadata: ${error}`); + throw Error(`Failed to import or register componentMetadata: ${error}`); } diff --git a/packages/semcom-sdk/package-lock.json b/packages/semcom-sdk/package-lock.json index 36874f32..4927e62f 100644 --- a/packages/semcom-sdk/package-lock.json +++ b/packages/semcom-sdk/package-lock.json @@ -4204,6 +4204,11 @@ "integrity": "sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA=", "dev": true }, + "jssha": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/jssha/-/jssha-3.2.0.tgz", + "integrity": "sha512-QuruyBENDWdN4tZwJbQq7/eAK85FqrI4oDbXjy5IBhYD+2pTJyBUWZe8ctWaCkrV0gy6AaelgOZZBMeswEa/6Q==" + }, "kind-of": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", diff --git a/packages/semcom-sdk/package.json b/packages/semcom-sdk/package.json index 38adaf8d..aa9d44a1 100644 --- a/packages/semcom-sdk/package.json +++ b/packages/semcom-sdk/package.json @@ -74,6 +74,7 @@ "dependencies": { "@digita-ai/semcom-core": "0.4.1", "buffer": "6.0.3", + "jssha": "^3.2.0", "n3": "1.10.0" }, "eslintIgnore": [ @@ -95,10 +96,10 @@ ], "coverageThreshold": { "global": { - "branches": 78.57, + "branches": 86.67, "functions": 50, - "lines": 70.69, - "statements": 67.74 + "lines": 80.33, + "statements": 76.92 } } }