diff --git a/npm/ng-packs/packages/core/src/lib/tests/environment-utils.spec.ts b/npm/ng-packs/packages/core/src/lib/tests/environment-utils.spec.ts index ab7cc0df0c..5599b61782 100644 --- a/npm/ng-packs/packages/core/src/lib/tests/environment-utils.spec.ts +++ b/npm/ng-packs/packages/core/src/lib/tests/environment-utils.spec.ts @@ -5,29 +5,8 @@ import { Store } from '@ngxs/store'; import { BehaviorSubject, of } from 'rxjs'; import { getRemoteEnv } from '../utils/environment-utils'; import { SetEnvironment } from '../actions/config.actions'; - -const environment = { - production: false, - hmr: false, - application: { - baseUrl: 'https://volosoft.com', - name: 'MyProjectName', - logoUrl: '', - }, - oAuthConfig: { - issuer: 'https://api.volosoft.com', - clientId: 'MyProjectName_App', - dummyClientSecret: '1q2w3e*', - scope: 'MyProjectName', - oidc: false, - requireHttps: true, - }, - apis: { - default: { - url: 'https://api.volosoft.com', - }, - }, -}; +import { Config } from '../models/config'; +import { deepMerge } from '../utils/object-utils'; @Component({ selector: 'abp-dummy', @@ -44,8 +23,58 @@ describe('EnvironmentUtils', () => { beforeEach(() => (spectator = createComponent())); - describe('#getRemoteEnv', async () => { - test('should call the remoteEnv URL and dispatch the SetEnvironment action ', async () => { + describe('#getRemoteEnv', () => { + const environment: Config.Environment = { + production: false, + hmr: false, + application: { + baseUrl: 'https://volosoft.com', + name: 'MyProjectName', + logoUrl: '', + }, + remoteEnv: { url: '/assets/appsettings.json', mergeStrategy: 'deepmerge' }, + oAuthConfig: { + issuer: 'https://api.volosoft.com', + clientId: 'MyProjectName_App', + dummyClientSecret: '1q2w3e*', + scope: 'MyProjectName', + oidc: false, + requireHttps: true, + }, + apis: { + default: { + url: 'https://api.volosoft.com', + }, + }, + }; + + const customEnv = { + application: { + baseUrl: 'https://custom-volosoft.com', + name: 'Custom-MyProjectName', + logoUrl: 'https://logourl/', + }, + apis: { + default: { + url: 'https://test-api.volosoft.com', + }, + }, + }; + + it('should call the remoteEnv URL and dispatch the SetEnvironment action for overwrite', () => { + setupTestAndRun({ mergeStrategy: 'deepmerge' }, deepMerge(environment, customEnv)); + }); + + it('should call the remoteEnv URL and dispatch the SetEnvironment action for deepmerge', () => { + setupTestAndRun({ mergeStrategy: 'overwrite' }, customEnv); + }); + + it('should call the remoteEnv URL and dispatch the SetEnvironment action for customFn', () => { + const someEnv = { apiUrl: 'https://some-api-url' } as any; + setupTestAndRun({ mergeStrategy: (_, __) => someEnv }, someEnv); + }); + + function setupTestAndRun(strategy: Pick, expectedValue) { const injector = spectator.inject(Injector); const injectorSpy = jest.spyOn(injector, 'get'); const store = spectator.inject(Store); @@ -56,16 +85,14 @@ describe('EnvironmentUtils', () => { injectorSpy.mockReturnValueOnce(http); injectorSpy.mockReturnValueOnce(store); - requestSpy.mockReturnValue(new BehaviorSubject(environment)); + requestSpy.mockReturnValue(new BehaviorSubject(customEnv)); dispatchSpy.mockReturnValue(of(true)); - const partialEnv = { remoteEnv: { url: '/assets/appsettings.json' } }; - getRemoteEnv(injector, partialEnv); + environment.remoteEnv.mergeStrategy = strategy.mergeStrategy; + getRemoteEnv(injector, environment); expect(requestSpy).toHaveBeenCalledWith('GET', '/assets/appsettings.json', { headers: {} }); - expect(dispatchSpy).toHaveBeenCalledWith( - new SetEnvironment({ ...environment, ...partialEnv }), - ); - }); + expect(dispatchSpy).toHaveBeenCalledWith(new SetEnvironment(expectedValue)); + } }); }); diff --git a/npm/ng-packs/packages/core/src/lib/tests/object-utils.spec.ts b/npm/ng-packs/packages/core/src/lib/tests/object-utils.spec.ts new file mode 100644 index 0000000000..efa1e73e19 --- /dev/null +++ b/npm/ng-packs/packages/core/src/lib/tests/object-utils.spec.ts @@ -0,0 +1,150 @@ +import { deepMerge } from '../utils/object-utils'; + +describe('DeepMerge', () => { + it('should return empty object when both inputs are null or undefined', () => { + expect(deepMerge(undefined, undefined)).toEqual({}); + expect(deepMerge(undefined, null)).toEqual({}); + expect(deepMerge(null, undefined)).toEqual({}); + expect(deepMerge(null, null)).toEqual({}); + }); + + it('should correctly return when any of the inputs is null or undefined', () => { + const differentTestValues = [10, false, '', 'test-string', { a: 1 }, [1, 2, 3], {}]; + differentTestValues.forEach(val => { + expect(deepMerge(undefined, val)).toEqual(val); + expect(deepMerge(null, val)).toEqual(val); + expect(deepMerge(val, undefined)).toEqual(val); + expect(deepMerge(val, null)).toEqual(val); + }); + }); + + it('should correctly return source if one of them is primitive', () => { + const differentTestValues = [ + { + target: 10, + source: false, + }, + { + target: false, + source: 20, + }, + { + target: 'string', + source: { a: 5 }, + }, + { + target: { b: 10 }, + source: 50, + }, + { + target: [1, 2, 3], + source: 40, + }, + { + target: { k: 60 }, + source: [4, 5, 6], + }, + ]; + + differentTestValues.forEach(val => + expect(deepMerge(val.target, val.source)).toEqual(val.source), + ); + }); + + it('should correctly return when both inputs are objects with different fields', () => { + const target = { a: 1 }; + const source = { b: 2 }; + const expected = { a: 1, b: 2 }; + expect(deepMerge(target, source)).toEqual(expected); + expect(deepMerge(source, target)).toEqual(expected); + }); + + it('should correctly return when both inputs are object with same fields but different values', () => { + const target = { a: 1 }; + const source = { a: 5 }; + expect(deepMerge(target, source)).toEqual(source); + expect(deepMerge(source, target)).toEqual(target); + }); + + it('should correctly merge shallow objects with different fields as well as some shared ones', () => { + const target = { a: 1, b: 2, c: 3 }; + const source = { a: 4, d: 5, e: 6 }; + expect(deepMerge(target, source)).toEqual({ a: 4, b: 2, c: 3, d: 5, e: 6 }); + }); + + it('should merge arrays', () => { + const firstArray = [1, 2, 3]; + const secondArray = [3, 4, 5, 6]; + expect(deepMerge(firstArray, secondArray)).toEqual(secondArray); + const target = { a: firstArray }; + const source = { a: secondArray }; + expect(deepMerge(target, source)).toEqual({ a: secondArray }); + }); + + it('should correctly merge nested objects', () => { + const target = { + a: { + b: { + c: { + d: 1, + g: 10, + q: undefined, + t: false, + }, + e: { + f: [1, 2, 3], + p: 'other-string', + }, + }, + }, + x: { + q: 'some-string', + }, + }; + const source = { + a: { + b: { + c: { + h: 30, + q: 45, + t: null, + }, + m: 20, + e: { + f: [20, 30, 40], + }, + }, + e: { k: [5, 6] }, + }, + z: { + y: true, + }, + }; + const expected = { + a: { + b: { + c: { + d: 1, + g: 10, + h: 30, + q: 45, + t: false, + }, + e: { + f: [20, 30, 40], + p: 'other-string', + }, + m: 20, + }, + e: { k: [5, 6] }, + }, + x: { + q: 'some-string', + }, + z: { + y: true, + }, + }; + expect(deepMerge(target, source)).toEqual(expected); + }); +});