Skip to content

Commit

Permalink
fix: make sure tags with same URI are the same (#141)
Browse files Browse the repository at this point in the history
* fix: make sure tags with same URI are the same

* fix: use hash-based tag instead of time-based

* fix: avoid javascript injection

* test: new tests for register-component

* chore: code reformat of tests

* fix: test no longer throws segmentation fault

Co-authored-by: AbelVandenBriel <[email protected]>
Co-authored-by: Arne Vandoorslaer <[email protected]>
  • Loading branch information
3 people authored Aug 10, 2021
1 parent e693a67 commit 23ae680
Show file tree
Hide file tree
Showing 4 changed files with 102 additions and 16 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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', () => {
Expand Down Expand Up @@ -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');

});

Expand All @@ -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);

});

});
Original file line number Diff line number Diff line change
@@ -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 {
Expand All @@ -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}`);

}

Expand Down
5 changes: 5 additions & 0 deletions packages/semcom-sdk/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 4 additions & 3 deletions packages/semcom-sdk/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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": [
Expand All @@ -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
}
}
}
Expand Down

0 comments on commit 23ae680

Please sign in to comment.