From 202fa9995e10256d39ab1c5152473eff03b3dc6f Mon Sep 17 00:00:00 2001 From: mehmet-erim Date: Fri, 3 Apr 2020 17:08:12 +0300 Subject: [PATCH 01/13] feat(core): add generateHash utility fn --- .../core/src/lib/tests/generator-utils.spec.ts | 10 ++++++++++ .../core/src/lib/utils/generator-utils.ts | 17 +++++++++++++++-- 2 files changed, 25 insertions(+), 2 deletions(-) create mode 100644 npm/ng-packs/packages/core/src/lib/tests/generator-utils.spec.ts diff --git a/npm/ng-packs/packages/core/src/lib/tests/generator-utils.spec.ts b/npm/ng-packs/packages/core/src/lib/tests/generator-utils.spec.ts new file mode 100644 index 0000000000..e09be3fb4f --- /dev/null +++ b/npm/ng-packs/packages/core/src/lib/tests/generator-utils.spec.ts @@ -0,0 +1,10 @@ +import { generateHash } from '../utils'; + +describe('GeneratorUtils', () => { + describe('#generateHash', () => { + test('should generate a hash', async () => { + const hash = generateHash('some content \n with second line'); + expect(hash).toBe(1112440527); + }); + }); +}); diff --git a/npm/ng-packs/packages/core/src/lib/utils/generator-utils.ts b/npm/ng-packs/packages/core/src/lib/utils/generator-utils.ts index 4256489efa..cc88e216d6 100644 --- a/npm/ng-packs/packages/core/src/lib/utils/generator-utils.ts +++ b/npm/ng-packs/packages/core/src/lib/utils/generator-utils.ts @@ -1,6 +1,19 @@ +// tslint:disable: no-bitwise + export function uuid(a?: any): string { return a - ? // tslint:disable-next-line: no-bitwise - (a ^ ((Math.random() * 16) >> (a / 4))).toString(16) + ? (a ^ ((Math.random() * 16) >> (a / 4))).toString(16) : ('' + 1e7 + -1e3 + -4e3 + -8e3 + -1e11).replace(/[018]/g, uuid); } + +export function generateHash(value: string): number { + let hashed = 0; + let charCode: number; + + for (let i = 0; i < value.length; i++) { + charCode = value.charCodeAt(i); + hashed = (hashed << 5) - hashed + charCode; + hashed |= 0; + } + return hashed; +} From e5c62a100b77c5611d607dbcb534935fd4089dc6 Mon Sep 17 00:00:00 2001 From: mehmet-erim Date: Fri, 3 Apr 2020 17:08:38 +0300 Subject: [PATCH 02/13] chore: deprecate old load method --- .../packages/core/src/lib/services/lazy-load.service.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/npm/ng-packs/packages/core/src/lib/services/lazy-load.service.ts b/npm/ng-packs/packages/core/src/lib/services/lazy-load.service.ts index 5e605d8c5d..cec9d4a6cf 100644 --- a/npm/ng-packs/packages/core/src/lib/services/lazy-load.service.ts +++ b/npm/ng-packs/packages/core/src/lib/services/lazy-load.service.ts @@ -13,6 +13,10 @@ export class LazyLoadService { loadedLibraries: { [url: string]: ReplaySubject } = {}; load(strategy: LoadingStrategy, retryTimes?: number, retryDelay?: number): Observable; + /** + * + * @deprecated Use other overload that requires a strategy as first param + */ load( urlOrUrls: string | string[], type: 'script' | 'style', From dd5f3572d60ba6ca9ebd1fd7c133080aa5feef2f Mon Sep 17 00:00:00 2001 From: mehmet-erim Date: Fri, 3 Apr 2020 17:16:23 +0300 Subject: [PATCH 03/13] feat(core): create DOMInsertionService --- .../src/lib/services/dom-insertion.service.ts | 17 +++++++++++++++++ .../packages/core/src/lib/services/index.ts | 1 + 2 files changed, 18 insertions(+) create mode 100644 npm/ng-packs/packages/core/src/lib/services/dom-insertion.service.ts diff --git a/npm/ng-packs/packages/core/src/lib/services/dom-insertion.service.ts b/npm/ng-packs/packages/core/src/lib/services/dom-insertion.service.ts new file mode 100644 index 0000000000..99e6ee4afe --- /dev/null +++ b/npm/ng-packs/packages/core/src/lib/services/dom-insertion.service.ts @@ -0,0 +1,17 @@ +import { Injectable } from '@angular/core'; +import { ContentStrategy } from '../strategies/content.strategy'; +import { generateHash } from '../utils'; + +@Injectable({ providedIn: 'root' }) +export class DOMInsertionService { + readonly inserted = new Set(); + + insertElement(contentStrategy: ContentStrategy) { + const hash = generateHash(contentStrategy.content); + + if (this.inserted.has(hash)) return; + + contentStrategy.insertElement(); + this.inserted.add(hash); + } +} diff --git a/npm/ng-packs/packages/core/src/lib/services/index.ts b/npm/ng-packs/packages/core/src/lib/services/index.ts index ad23b74fae..a64e721c67 100644 --- a/npm/ng-packs/packages/core/src/lib/services/index.ts +++ b/npm/ng-packs/packages/core/src/lib/services/index.ts @@ -1,6 +1,7 @@ export * from './application-configuration.service'; export * from './auth.service'; export * from './config-state.service'; +export * from './dom-insertion.service'; export * from './lazy-load.service'; export * from './localization.service'; export * from './profile-state.service'; From ecbf966d01fd78325b852e7e180f1a01cf28efef Mon Sep 17 00:00:00 2001 From: mehmet-erim Date: Fri, 3 Apr 2020 17:17:04 +0300 Subject: [PATCH 04/13] feat(core): create content strategy --- .../src/lib/strategies/content.strategy.ts | 52 +++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 npm/ng-packs/packages/core/src/lib/strategies/content.strategy.ts diff --git a/npm/ng-packs/packages/core/src/lib/strategies/content.strategy.ts b/npm/ng-packs/packages/core/src/lib/strategies/content.strategy.ts new file mode 100644 index 0000000000..1d58f6bb3d --- /dev/null +++ b/npm/ng-packs/packages/core/src/lib/strategies/content.strategy.ts @@ -0,0 +1,52 @@ +import { ContentSecurityStrategy, CONTENT_SECURITY_STRATEGY } from './content-security.strategy'; +import { DomStrategy, DOM_STRATEGY } from './dom.strategy'; + +export abstract class ContentStrategy { + constructor( + public content: string, + protected domStrategy: DomStrategy = DOM_STRATEGY.AppendToHead(), + protected contentSecurityStrategy: ContentSecurityStrategy = CONTENT_SECURITY_STRATEGY.None(), + ) {} + + abstract createElement(): T; + + insertElement() { + const element = this.createElement(); + + this.contentSecurityStrategy.applyCSP(element); + this.domStrategy.insertElement(element); + } +} + +export class StyleContentStrategy extends ContentStrategy { + createElement(): HTMLStyleElement { + const element = document.createElement('style'); + element.textContent = this.content; + + return element; + } +} + +export class ScriptContentStrategy extends ContentStrategy { + createElement(): HTMLScriptElement { + const element = document.createElement('script'); + element.textContent = this.content; + + return element; + } +} + +export const CONTENT_STRATEGY = { + AppendScriptToBody(content: string) { + return new ScriptContentStrategy(content, DOM_STRATEGY.AppendToBody()); + }, + AppendScriptToHead(content: string) { + return new ScriptContentStrategy(content, DOM_STRATEGY.AppendToHead()); + }, + AppendStyleToHead(content: string) { + return new StyleContentStrategy(content, DOM_STRATEGY.AppendToHead()); + }, + PrependStyleToHead(content: string) { + return new StyleContentStrategy(content, DOM_STRATEGY.PrependToHead()); + }, +}; From 1f74235cb7a7ac919623b77d28ac210fe57c5b3f Mon Sep 17 00:00:00 2001 From: mehmet-erim Date: Fri, 3 Apr 2020 17:17:47 +0300 Subject: [PATCH 05/13] refactor( core): rename StrictContentSecurityStrategy to NoContentSecurityStrategy --- .../src/lib/strategies/content-security.strategy.ts | 6 +++--- npm/ng-packs/packages/core/src/lib/strategies/index.ts | 1 + .../src/lib/tests/content-security.strategy.spec.ts | 10 +++++----- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/npm/ng-packs/packages/core/src/lib/strategies/content-security.strategy.ts b/npm/ng-packs/packages/core/src/lib/strategies/content-security.strategy.ts index 2875916ef2..a848feabe3 100644 --- a/npm/ng-packs/packages/core/src/lib/strategies/content-security.strategy.ts +++ b/npm/ng-packs/packages/core/src/lib/strategies/content-security.strategy.ts @@ -14,7 +14,7 @@ export class LooseContentSecurityStrategy extends ContentSecurityStrategy { } } -export class StrictContentSecurityStrategy extends ContentSecurityStrategy { +export class NoContentSecurityStrategy extends ContentSecurityStrategy { constructor() { super(); } @@ -26,7 +26,7 @@ export const CONTENT_SECURITY_STRATEGY = { Loose(nonce: string) { return new LooseContentSecurityStrategy(nonce); }, - Strict() { - return new StrictContentSecurityStrategy(); + None() { + return new NoContentSecurityStrategy(); }, }; diff --git a/npm/ng-packs/packages/core/src/lib/strategies/index.ts b/npm/ng-packs/packages/core/src/lib/strategies/index.ts index 93904af549..2d6be484dd 100644 --- a/npm/ng-packs/packages/core/src/lib/strategies/index.ts +++ b/npm/ng-packs/packages/core/src/lib/strategies/index.ts @@ -1,4 +1,5 @@ export * from './content-security.strategy'; +export * from './content.strategy'; export * from './cross-origin.strategy'; export * from './dom.strategy'; export * from './loading.strategy'; diff --git a/npm/ng-packs/packages/core/src/lib/tests/content-security.strategy.spec.ts b/npm/ng-packs/packages/core/src/lib/tests/content-security.strategy.spec.ts index c617e35893..1eaaf480f9 100644 --- a/npm/ng-packs/packages/core/src/lib/tests/content-security.strategy.spec.ts +++ b/npm/ng-packs/packages/core/src/lib/tests/content-security.strategy.spec.ts @@ -1,7 +1,7 @@ import { CONTENT_SECURITY_STRATEGY, LooseContentSecurityStrategy, - StrictContentSecurityStrategy, + NoContentSecurityStrategy, } from '../strategies'; import { uuid } from '../utils'; @@ -21,7 +21,7 @@ describe('LooseContentSecurityStrategy', () => { describe('StrictContentSecurityStrategy', () => { describe('#applyCSP', () => { it('should not set nonce attribute', () => { - const strategy = new StrictContentSecurityStrategy(); + const strategy = new NoContentSecurityStrategy(); const element = document.createElement('link'); strategy.applyCSP(element); @@ -32,9 +32,9 @@ describe('StrictContentSecurityStrategy', () => { describe('CONTENT_SECURITY_STRATEGY', () => { test.each` - name | Strategy | nonce - ${'Loose'} | ${LooseContentSecurityStrategy} | ${uuid()} - ${'Strict'} | ${StrictContentSecurityStrategy} | ${undefined} + name | Strategy | nonce + ${'Loose'} | ${LooseContentSecurityStrategy} | ${uuid()} + ${'Strict'} | ${NoContentSecurityStrategy} | ${undefined} `('should successfully map $name to $Strategy.name', ({ name, Strategy, nonce }) => { expect(CONTENT_SECURITY_STRATEGY[name](nonce)).toEqual(new Strategy(nonce)); }); From 310a56166143a44226390bf2667174b2f6714f5b Mon Sep 17 00:00:00 2001 From: mehmet-erim Date: Fri, 3 Apr 2020 17:18:04 +0300 Subject: [PATCH 06/13] feat(theme-shared): create append-content.token --- .../theme-shared/src/lib/theme-shared.module.ts | 12 +++++++++--- .../src/lib/tokens/append-content.token.ts | 15 +++++++++++++++ 2 files changed, 24 insertions(+), 3 deletions(-) create mode 100644 npm/ng-packs/packages/theme-shared/src/lib/tokens/append-content.token.ts diff --git a/npm/ng-packs/packages/theme-shared/src/lib/theme-shared.module.ts b/npm/ng-packs/packages/theme-shared/src/lib/theme-shared.module.ts index 205e2a488c..15ba212944 100644 --- a/npm/ng-packs/packages/theme-shared/src/lib/theme-shared.module.ts +++ b/npm/ng-packs/packages/theme-shared/src/lib/theme-shared.module.ts @@ -1,4 +1,4 @@ -import { CoreModule, LazyLoadService } from '@abp/ng.core'; +import { CoreModule, noop, LazyLoadService } from '@abp/ng.core'; import { DatePipe } from '@angular/common'; import { APP_INITIALIZER, Injector, ModuleWithProviders, NgModule } from '@angular/core'; import { NgbDateParserFormatter, NgbPaginationModule } from '@ng-bootstrap/ng-bootstrap'; @@ -25,7 +25,13 @@ import { chartJsLoaded$ } from './utils/widget-utils'; import { PaginationComponent } from './components/pagination/pagination.component'; import { LoadingComponent } from './components/loading/loading.component'; import { LoadingDirective } from './directives/loading.directive'; +import { THEME_SHARED_APPEND_CONTENT } from './tokens/append-content.token'; +/** + * + * @deprecated To be deleted in v2.6 + * + */ export function appendScript(injector: Injector) { const fn = () => { import('chart.js').then(() => chartJsLoaded$.next(true)); @@ -87,8 +93,8 @@ export class ThemeSharedModule { { provide: APP_INITIALIZER, multi: true, - deps: [Injector], - useFactory: appendScript, + deps: [THEME_SHARED_APPEND_CONTENT], + useFactory: noop, }, { provide: HTTP_ERROR_CONFIG, useValue: options.httpErrorConfig }, { diff --git a/npm/ng-packs/packages/theme-shared/src/lib/tokens/append-content.token.ts b/npm/ng-packs/packages/theme-shared/src/lib/tokens/append-content.token.ts new file mode 100644 index 0000000000..7ff6e2e7c6 --- /dev/null +++ b/npm/ng-packs/packages/theme-shared/src/lib/tokens/append-content.token.ts @@ -0,0 +1,15 @@ +import { CONTENT_STRATEGY, DOMInsertionService } from '@abp/ng.core'; +import { inject, InjectionToken } from '@angular/core'; +import styles from '../constants/styles'; +import { chartJsLoaded$ } from '../utils/widget-utils'; + +export const THEME_SHARED_APPEND_CONTENT = new InjectionToken('THEME_SHARED_APPEND_CONTENT', { + providedIn: 'root', + factory: () => { + const domInsertion: DOMInsertionService = inject(DOMInsertionService); + + domInsertion.insertElement(CONTENT_STRATEGY.AppendStyleToHead(styles)); + + import('chart.js').then(() => chartJsLoaded$.next(true)); + }, +}); From fc525170d4ede6b639de2190427ad20f3c0f27e0 Mon Sep 17 00:00:00 2001 From: mehmet-erim Date: Sun, 5 Apr 2020 07:57:44 +0300 Subject: [PATCH 07/13] refactor(core): rename dom-insertion.service class name --- .../packages/core/src/lib/services/dom-insertion.service.ts | 2 +- .../theme-shared/src/lib/tokens/append-content.token.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/npm/ng-packs/packages/core/src/lib/services/dom-insertion.service.ts b/npm/ng-packs/packages/core/src/lib/services/dom-insertion.service.ts index 99e6ee4afe..e460063d1a 100644 --- a/npm/ng-packs/packages/core/src/lib/services/dom-insertion.service.ts +++ b/npm/ng-packs/packages/core/src/lib/services/dom-insertion.service.ts @@ -3,7 +3,7 @@ import { ContentStrategy } from '../strategies/content.strategy'; import { generateHash } from '../utils'; @Injectable({ providedIn: 'root' }) -export class DOMInsertionService { +export class DomInsertionService { readonly inserted = new Set(); insertElement(contentStrategy: ContentStrategy) { diff --git a/npm/ng-packs/packages/theme-shared/src/lib/tokens/append-content.token.ts b/npm/ng-packs/packages/theme-shared/src/lib/tokens/append-content.token.ts index 7ff6e2e7c6..a950c0435a 100644 --- a/npm/ng-packs/packages/theme-shared/src/lib/tokens/append-content.token.ts +++ b/npm/ng-packs/packages/theme-shared/src/lib/tokens/append-content.token.ts @@ -1,4 +1,4 @@ -import { CONTENT_STRATEGY, DOMInsertionService } from '@abp/ng.core'; +import { CONTENT_STRATEGY, DomInsertionService } from '@abp/ng.core'; import { inject, InjectionToken } from '@angular/core'; import styles from '../constants/styles'; import { chartJsLoaded$ } from '../utils/widget-utils'; @@ -6,7 +6,7 @@ import { chartJsLoaded$ } from '../utils/widget-utils'; export const THEME_SHARED_APPEND_CONTENT = new InjectionToken('THEME_SHARED_APPEND_CONTENT', { providedIn: 'root', factory: () => { - const domInsertion: DOMInsertionService = inject(DOMInsertionService); + const domInsertion: DomInsertionService = inject(DomInsertionService); domInsertion.insertElement(CONTENT_STRATEGY.AppendStyleToHead(styles)); From aef5cedd9193cef6d51b93d5b3b03e27f30660c0 Mon Sep 17 00:00:00 2001 From: mehmet-erim Date: Sun, 5 Apr 2020 07:57:58 +0300 Subject: [PATCH 08/13] test(theme-shared): add append-content.token.spec.ts --- .../lib/tests/append-content.token.spec.ts | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 npm/ng-packs/packages/theme-shared/src/lib/tests/append-content.token.spec.ts diff --git a/npm/ng-packs/packages/theme-shared/src/lib/tests/append-content.token.spec.ts b/npm/ng-packs/packages/theme-shared/src/lib/tests/append-content.token.spec.ts new file mode 100644 index 0000000000..44bf398a31 --- /dev/null +++ b/npm/ng-packs/packages/theme-shared/src/lib/tests/append-content.token.spec.ts @@ -0,0 +1,27 @@ +import { Component } from '@angular/core'; +import { createComponentFactory, Spectator } from '@ngneat/spectator'; +import { THEME_SHARED_APPEND_CONTENT } from '../tokens/append-content.token'; +import { DomInsertionService } from '@abp/ng.core'; +import { chartJsLoaded$ } from '../utils'; + +@Component({ selector: 'app-dummy', template: '' }) +class DummyComponent {} + +describe('AppendContentToken', () => { + let spectator: Spectator; + const createComponent = createComponentFactory(DummyComponent); + + beforeEach(() => (spectator = createComponent())); + + it('should insert a style element to the DOM', () => { + spectator.get(THEME_SHARED_APPEND_CONTENT); + expect(spectator.get(DomInsertionService).inserted.size).toBe(1); + }); + + it('should be loaded the chart.js', done => { + chartJsLoaded$.subscribe(loaded => { + expect(loaded).toBe(true); + done(); + }); + }); +}); From 3b1fa3ba3d391895f5531a4f22fd1b79a51623be Mon Sep 17 00:00:00 2001 From: mehmet-erim Date: Sun, 5 Apr 2020 08:04:15 +0300 Subject: [PATCH 09/13] test(core): add dom-insertion.service.spec --- .../lib/tests/content-security.strategy.spec.ts | 8 ++++---- .../src/lib/tests/dom-insertion.service.spec.ts | 15 +++++++++++++++ .../src/lib/tests/append-content.token.spec.ts | 2 ++ 3 files changed, 21 insertions(+), 4 deletions(-) create mode 100644 npm/ng-packs/packages/core/src/lib/tests/dom-insertion.service.spec.ts diff --git a/npm/ng-packs/packages/core/src/lib/tests/content-security.strategy.spec.ts b/npm/ng-packs/packages/core/src/lib/tests/content-security.strategy.spec.ts index 1eaaf480f9..701da5b756 100644 --- a/npm/ng-packs/packages/core/src/lib/tests/content-security.strategy.spec.ts +++ b/npm/ng-packs/packages/core/src/lib/tests/content-security.strategy.spec.ts @@ -18,7 +18,7 @@ describe('LooseContentSecurityStrategy', () => { }); }); -describe('StrictContentSecurityStrategy', () => { +describe('NoContentSecurityStrategy', () => { describe('#applyCSP', () => { it('should not set nonce attribute', () => { const strategy = new NoContentSecurityStrategy(); @@ -32,9 +32,9 @@ describe('StrictContentSecurityStrategy', () => { describe('CONTENT_SECURITY_STRATEGY', () => { test.each` - name | Strategy | nonce - ${'Loose'} | ${LooseContentSecurityStrategy} | ${uuid()} - ${'Strict'} | ${NoContentSecurityStrategy} | ${undefined} + name | Strategy | nonce + ${'Loose'} | ${LooseContentSecurityStrategy} | ${uuid()} + ${'None'} | ${NoContentSecurityStrategy} | ${undefined} `('should successfully map $name to $Strategy.name', ({ name, Strategy, nonce }) => { expect(CONTENT_SECURITY_STRATEGY[name](nonce)).toEqual(new Strategy(nonce)); }); diff --git a/npm/ng-packs/packages/core/src/lib/tests/dom-insertion.service.spec.ts b/npm/ng-packs/packages/core/src/lib/tests/dom-insertion.service.spec.ts new file mode 100644 index 0000000000..4153f2de08 --- /dev/null +++ b/npm/ng-packs/packages/core/src/lib/tests/dom-insertion.service.spec.ts @@ -0,0 +1,15 @@ +import { createServiceFactory, SpectatorService } from '@ngneat/spectator'; +import { DomInsertionService } from '../services'; +import { CONTENT_STRATEGY } from '../strategies'; + +describe('DomInsertionService', () => { + let spectator: SpectatorService; + const createService = createServiceFactory(DomInsertionService); + + beforeEach(() => (spectator = createService())); + + it('should be insert an element', () => { + spectator.service.insertElement(CONTENT_STRATEGY.AppendStyleToHead('.test {}')); + expect(spectator.service.inserted.has(1437348290)).toBe(true); + }); +}); diff --git a/npm/ng-packs/packages/theme-shared/src/lib/tests/append-content.token.spec.ts b/npm/ng-packs/packages/theme-shared/src/lib/tests/append-content.token.spec.ts index 44bf398a31..d959ce3217 100644 --- a/npm/ng-packs/packages/theme-shared/src/lib/tests/append-content.token.spec.ts +++ b/npm/ng-packs/packages/theme-shared/src/lib/tests/append-content.token.spec.ts @@ -23,5 +23,7 @@ describe('AppendContentToken', () => { expect(loaded).toBe(true); done(); }); + + spectator.get(THEME_SHARED_APPEND_CONTENT); }); }); From 9ad386d962a60f2847c2b861dec780040adeccca Mon Sep 17 00:00:00 2001 From: mehmet-erim Date: Sun, 5 Apr 2020 12:21:21 +0300 Subject: [PATCH 10/13] test(core): add content.strategy.spec --- .../src/lib/tests/content.strategy.spec.ts | 92 +++++++++++++++++++ 1 file changed, 92 insertions(+) create mode 100644 npm/ng-packs/packages/core/src/lib/tests/content.strategy.spec.ts diff --git a/npm/ng-packs/packages/core/src/lib/tests/content.strategy.spec.ts b/npm/ng-packs/packages/core/src/lib/tests/content.strategy.spec.ts new file mode 100644 index 0000000000..06042c0aee --- /dev/null +++ b/npm/ng-packs/packages/core/src/lib/tests/content.strategy.spec.ts @@ -0,0 +1,92 @@ +import { + CONTENT_STRATEGY, + StyleContentStrategy, + ScriptContentStrategy, + DOM_STRATEGY, + CONTENT_SECURITY_STRATEGY, +} from '../strategies'; +import { uuid } from '../utils'; + +describe('StyleContentStrategy', () => { + describe('#createElement', () => { + it('should create a style element', () => { + const strategy = new StyleContentStrategy(''); + const element = strategy.createElement(); + + expect(element.tagName).toBe('STYLE'); + }); + }); + + describe('#insertElement', () => { + it('should use given dom and content security strategies', () => { + const domStrategy = DOM_STRATEGY.PrependToHead(); + const contentSecurityStrategy = CONTENT_SECURITY_STRATEGY.None(); + + contentSecurityStrategy.applyCSP = jest.fn((el: HTMLScriptElement) => {}); + domStrategy.insertElement = jest.fn((el: HTMLScriptElement) => {}) as any; + + const strategy = new StyleContentStrategy('', domStrategy, contentSecurityStrategy); + const element = strategy.createElement(); + strategy.insertElement(); + + expect(contentSecurityStrategy.applyCSP).toHaveBeenCalledWith(element); + expect(domStrategy.insertElement).toHaveBeenCalledWith(element); + }); + }); +}); + +describe('ScriptContentStrategy', () => { + describe('#createElement', () => { + it('should create a style element', () => { + const nonce = uuid(); + const strategy = new ScriptContentStrategy(''); + const element = strategy.createElement(); + + expect(element.tagName).toBe('SCRIPT'); + }); + }); + + describe('#insertElement', () => { + it('should use given dom and content security strategies', () => { + const nonce = uuid(); + + const domStrategy = DOM_STRATEGY.PrependToHead(); + const contentSecurityStrategy = CONTENT_SECURITY_STRATEGY.Loose(nonce); + + contentSecurityStrategy.applyCSP = jest.fn((el: HTMLScriptElement) => {}); + domStrategy.insertElement = jest.fn((el: HTMLScriptElement) => {}) as any; + + const strategy = new ScriptContentStrategy('', domStrategy, contentSecurityStrategy); + const element = strategy.createElement(); + strategy.insertElement(); + + expect(contentSecurityStrategy.applyCSP).toHaveBeenCalledWith(element); + expect(domStrategy.insertElement).toHaveBeenCalledWith(element); + }); + }); +}); + +// describe('CONTENT_SECURITY_STRATEGY', () => { +// test.each` +// name | Strategy | nonce +// ${'Loose'} | ${LooseContentSecurityStrategy} | ${uuid()} +// ${'None'} | ${NoContentSecurityStrategy} | ${undefined} +// `('should successfully map $name to $Strategy.name', ({ name, Strategy, nonce }) => { +// expect(CONTENT_SECURITY_STRATEGY[name](nonce)).toEqual(new Strategy(nonce)); +// }); +// }); + +describe('CONTENT_STRATEGY', () => { + test.each` + name | Strategy | domStrategy + ${'AppendScriptToBody'} | ${ScriptContentStrategy} | ${'AppendToBody'} + ${'AppendScriptToHead'} | ${ScriptContentStrategy} | ${'AppendToHead'} + ${'AppendStyleToHead'} | ${StyleContentStrategy} | ${'AppendToHead'} + ${'PrependStyleToHead'} | ${StyleContentStrategy} | ${'PrependToHead'} + `( + 'should successfully map $name to $Strategy.name with $domStrategy dom strategy', + ({ name, Strategy, domStrategy }) => { + expect(CONTENT_STRATEGY[name]('')).toEqual(new Strategy('', DOM_STRATEGY[domStrategy]())); + }, + ); +}); From 065ea6cd8b82f70917b34e87956a507ac9af3e95 Mon Sep 17 00:00:00 2001 From: mehmet-erim Date: Sun, 5 Apr 2020 12:29:25 +0300 Subject: [PATCH 11/13] fix: fix lint error --- .../theme-shared/src/lib/tests/append-content.token.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/npm/ng-packs/packages/theme-shared/src/lib/tests/append-content.token.spec.ts b/npm/ng-packs/packages/theme-shared/src/lib/tests/append-content.token.spec.ts index d959ce3217..5bfbd169e8 100644 --- a/npm/ng-packs/packages/theme-shared/src/lib/tests/append-content.token.spec.ts +++ b/npm/ng-packs/packages/theme-shared/src/lib/tests/append-content.token.spec.ts @@ -4,7 +4,7 @@ import { THEME_SHARED_APPEND_CONTENT } from '../tokens/append-content.token'; import { DomInsertionService } from '@abp/ng.core'; import { chartJsLoaded$ } from '../utils'; -@Component({ selector: 'app-dummy', template: '' }) +@Component({ selector: 'abp-dummy', template: '' }) class DummyComponent {} describe('AppendContentToken', () => { From 0d44a7d5cef315e1172919010e10b8cbbc5e0b15 Mon Sep 17 00:00:00 2001 From: mehmet-erim Date: Sun, 5 Apr 2020 12:31:25 +0300 Subject: [PATCH 12/13] feat(theme-basic): implement dom insertion service to append style --- .../theme-basic/src/lib/services/initial.service.ts | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/npm/ng-packs/packages/theme-basic/src/lib/services/initial.service.ts b/npm/ng-packs/packages/theme-basic/src/lib/services/initial.service.ts index 21fb1756bc..027bfaed69 100644 --- a/npm/ng-packs/packages/theme-basic/src/lib/services/initial.service.ts +++ b/npm/ng-packs/packages/theme-basic/src/lib/services/initial.service.ts @@ -1,4 +1,4 @@ -import { LazyLoadService, AddReplaceableComponent } from '@abp/ng.core'; +import { DomInsertionService, AddReplaceableComponent, CONTENT_STRATEGY } from '@abp/ng.core'; import { Injectable } from '@angular/core'; import { Store } from '@ngxs/store'; import styles from '../constants/styles'; @@ -8,8 +8,9 @@ import { EmptyLayoutComponent } from '../components/empty-layout/empty-layout.co @Injectable({ providedIn: 'root' }) export class InitialService { - constructor(private lazyLoadService: LazyLoadService, private store: Store) { - this.appendStyle().subscribe(); + constructor(private domInsertion: DomInsertionService, private store: Store) { + this.appendStyle(); + this.store.dispatch([ new AddReplaceableComponent({ key: 'Theme.ApplicationLayoutComponent', @@ -27,6 +28,6 @@ export class InitialService { } appendStyle() { - return this.lazyLoadService.load(null, 'style', styles, 'head', 'beforeend'); + this.domInsertion.insertElement(CONTENT_STRATEGY.AppendStyleToHead(styles)); } } From e0c99283462640a331290b5bfbd5581428f78288 Mon Sep 17 00:00:00 2001 From: mehmet-erim Date: Sun, 5 Apr 2020 19:21:21 +0300 Subject: [PATCH 13/13] refactor: change DomInsertionsService's method name --- .../core/src/lib/services/dom-insertion.service.ts | 2 +- .../core/src/lib/tests/content.strategy.spec.ts | 10 ---------- .../core/src/lib/tests/dom-insertion.service.spec.ts | 2 +- .../theme-basic/src/lib/services/initial.service.ts | 2 +- .../src/lib/tokens/append-content.token.ts | 2 +- 5 files changed, 4 insertions(+), 14 deletions(-) diff --git a/npm/ng-packs/packages/core/src/lib/services/dom-insertion.service.ts b/npm/ng-packs/packages/core/src/lib/services/dom-insertion.service.ts index e460063d1a..f1a59e4e43 100644 --- a/npm/ng-packs/packages/core/src/lib/services/dom-insertion.service.ts +++ b/npm/ng-packs/packages/core/src/lib/services/dom-insertion.service.ts @@ -6,7 +6,7 @@ import { generateHash } from '../utils'; export class DomInsertionService { readonly inserted = new Set(); - insertElement(contentStrategy: ContentStrategy) { + insertContent(contentStrategy: ContentStrategy) { const hash = generateHash(contentStrategy.content); if (this.inserted.has(hash)) return; diff --git a/npm/ng-packs/packages/core/src/lib/tests/content.strategy.spec.ts b/npm/ng-packs/packages/core/src/lib/tests/content.strategy.spec.ts index 06042c0aee..e742313135 100644 --- a/npm/ng-packs/packages/core/src/lib/tests/content.strategy.spec.ts +++ b/npm/ng-packs/packages/core/src/lib/tests/content.strategy.spec.ts @@ -66,16 +66,6 @@ describe('ScriptContentStrategy', () => { }); }); -// describe('CONTENT_SECURITY_STRATEGY', () => { -// test.each` -// name | Strategy | nonce -// ${'Loose'} | ${LooseContentSecurityStrategy} | ${uuid()} -// ${'None'} | ${NoContentSecurityStrategy} | ${undefined} -// `('should successfully map $name to $Strategy.name', ({ name, Strategy, nonce }) => { -// expect(CONTENT_SECURITY_STRATEGY[name](nonce)).toEqual(new Strategy(nonce)); -// }); -// }); - describe('CONTENT_STRATEGY', () => { test.each` name | Strategy | domStrategy diff --git a/npm/ng-packs/packages/core/src/lib/tests/dom-insertion.service.spec.ts b/npm/ng-packs/packages/core/src/lib/tests/dom-insertion.service.spec.ts index 4153f2de08..2570636197 100644 --- a/npm/ng-packs/packages/core/src/lib/tests/dom-insertion.service.spec.ts +++ b/npm/ng-packs/packages/core/src/lib/tests/dom-insertion.service.spec.ts @@ -9,7 +9,7 @@ describe('DomInsertionService', () => { beforeEach(() => (spectator = createService())); it('should be insert an element', () => { - spectator.service.insertElement(CONTENT_STRATEGY.AppendStyleToHead('.test {}')); + spectator.service.insertContent(CONTENT_STRATEGY.AppendStyleToHead('.test {}')); expect(spectator.service.inserted.has(1437348290)).toBe(true); }); }); diff --git a/npm/ng-packs/packages/theme-basic/src/lib/services/initial.service.ts b/npm/ng-packs/packages/theme-basic/src/lib/services/initial.service.ts index 027bfaed69..2510b77a8d 100644 --- a/npm/ng-packs/packages/theme-basic/src/lib/services/initial.service.ts +++ b/npm/ng-packs/packages/theme-basic/src/lib/services/initial.service.ts @@ -28,6 +28,6 @@ export class InitialService { } appendStyle() { - this.domInsertion.insertElement(CONTENT_STRATEGY.AppendStyleToHead(styles)); + this.domInsertion.insertContent(CONTENT_STRATEGY.AppendStyleToHead(styles)); } } diff --git a/npm/ng-packs/packages/theme-shared/src/lib/tokens/append-content.token.ts b/npm/ng-packs/packages/theme-shared/src/lib/tokens/append-content.token.ts index a950c0435a..2ba7898480 100644 --- a/npm/ng-packs/packages/theme-shared/src/lib/tokens/append-content.token.ts +++ b/npm/ng-packs/packages/theme-shared/src/lib/tokens/append-content.token.ts @@ -8,7 +8,7 @@ export const THEME_SHARED_APPEND_CONTENT = new InjectionToken('THEME_SHARE factory: () => { const domInsertion: DomInsertionService = inject(DomInsertionService); - domInsertion.insertElement(CONTENT_STRATEGY.AppendStyleToHead(styles)); + domInsertion.insertContent(CONTENT_STRATEGY.AppendStyleToHead(styles)); import('chart.js').then(() => chartJsLoaded$.next(true)); },