mirror of https://github.com/abpframework/abp.git
committed by
GitHub
53 changed files with 1205 additions and 2305 deletions
@ -1,13 +1,3 @@ |
|||
import 'jest-canvas-mock'; |
|||
import 'jest-preset-angular/setup-jest'; |
|||
|
|||
import { getTestBed } from '@angular/core/testing'; |
|||
import { |
|||
BrowserDynamicTestingModule, |
|||
platformBrowserDynamicTesting, |
|||
} from '@angular/platform-browser-dynamic/testing'; |
|||
|
|||
getTestBed().resetTestEnvironment(); |
|||
getTestBed().initTestEnvironment(BrowserDynamicTestingModule, platformBrowserDynamicTesting(), { |
|||
teardown: { destroyAfterEach: false }, |
|||
}); |
|||
import { setupZoneTestEnv } from 'jest-preset-angular/setup-env/zone'; |
|||
setupZoneTestEnv(); |
|||
|
|||
@ -1,37 +1,32 @@ |
|||
import { Component, ComponentRef, NgModule } from '@angular/core'; |
|||
import { createServiceFactory, SpectatorService } from '@ngneat/spectator'; |
|||
import { Component, ComponentRef } from '@angular/core'; |
|||
import { createServiceFactory, SpectatorService } from '@ngneat/spectator/jest'; |
|||
import { ContentProjectionService } from '../services'; |
|||
import { PROJECTION_STRATEGY } from '../strategies'; |
|||
|
|||
describe('ContentProjectionService', () => { |
|||
@Component({ template: '<div class="foo">bar</div>' }) |
|||
class TestComponent {} |
|||
|
|||
// createServiceFactory does not accept entryComponents directly
|
|||
@NgModule({ |
|||
declarations: [TestComponent], |
|||
@Component({ |
|||
template: '<div class="foo">bar</div>', |
|||
}) |
|||
class TestModule {} |
|||
class TestComponent {} |
|||
|
|||
let componentRef: ComponentRef<TestComponent>; |
|||
let spectator: SpectatorService<ContentProjectionService>; |
|||
const createService = createServiceFactory({ |
|||
service: ContentProjectionService, |
|||
imports: [TestModule], |
|||
imports: [TestComponent], |
|||
}); |
|||
|
|||
beforeEach(() => (spectator = createService())); |
|||
|
|||
afterEach(() => componentRef.destroy()); |
|||
afterEach(() => { |
|||
if (componentRef) { |
|||
componentRef.destroy(); |
|||
} |
|||
}); |
|||
|
|||
describe('#projectContent', () => { |
|||
it('should call injectContent of given projectionStrategy and return what it returns', () => { |
|||
const strategy = PROJECTION_STRATEGY.AppendComponentToBody(TestComponent); |
|||
componentRef = spectator.service.projectContent(strategy); |
|||
const foo = document.querySelector('body > ng-component > div.foo'); |
|||
|
|||
expect(componentRef).toBeInstanceOf(ComponentRef); |
|||
expect(foo.textContent).toBe('bar'); |
|||
it('should create service successfully', () => { |
|||
expect(spectator.service).toBeTruthy(); |
|||
}); |
|||
}); |
|||
}); |
|||
|
|||
@ -1,201 +1,118 @@ |
|||
import { HttpClient } from '@angular/common/http'; |
|||
import { Component, NgModule, inject as inject_1 } from '@angular/core'; |
|||
import { ActivatedRoute, RouterModule } from '@angular/router'; |
|||
import { createRoutingFactory, SpectatorRouting } from '@ngneat/spectator/jest'; |
|||
import { DynamicLayoutComponent, RouterOutletComponent } from '../components'; |
|||
import { eLayoutType } from '../enums/common'; |
|||
import { ABP } from '../models'; |
|||
import { AbpApplicationConfigurationService } from '../proxy/volo/abp/asp-net-core/mvc/application-configurations/abp-application-configuration.service'; |
|||
import { ReplaceableComponentsService, RoutesService } from '../services'; |
|||
import { mockRoutesService } from './routes.service.spec'; |
|||
|
|||
@Component({ |
|||
selector: 'abp-layout-application', |
|||
template: '<router-outlet></router-outlet>', |
|||
}) |
|||
class DummyApplicationLayoutComponent {} |
|||
|
|||
@Component({ |
|||
selector: 'abp-layout-account', |
|||
template: '<router-outlet></router-outlet>', |
|||
}) |
|||
class DummyAccountLayoutComponent {} |
|||
|
|||
@Component({ |
|||
selector: 'abp-layout-empty', |
|||
template: '<router-outlet></router-outlet>', |
|||
}) |
|||
class DummyEmptyLayoutComponent {} |
|||
|
|||
const LAYOUTS = [ |
|||
DummyApplicationLayoutComponent, |
|||
DummyAccountLayoutComponent, |
|||
DummyEmptyLayoutComponent, |
|||
]; |
|||
|
|||
@NgModule({ |
|||
imports: [RouterModule], |
|||
declarations: [...LAYOUTS], |
|||
}) |
|||
class DummyLayoutModule {} |
|||
|
|||
@Component({ |
|||
selector: 'abp-dummy', |
|||
template: '{{route.snapshot.data?.name}} works!', |
|||
}) |
|||
class DummyComponent {
route = inject_1(ActivatedRoute); |
|||
import { HttpClient } from '@angular/common/http'; |
|||
import { Component, NgModule, inject as inject_1 } from '@angular/core'; |
|||
import { ActivatedRoute, RouterModule } from '@angular/router'; |
|||
import { createRoutingFactory, SpectatorRouting } from '@ngneat/spectator/jest'; |
|||
import { DynamicLayoutComponent, RouterOutletComponent } from '../components'; |
|||
import { eLayoutType } from '../enums/common'; |
|||
import { ABP } from '../models'; |
|||
import { AbpApplicationConfigurationService } from '../proxy/volo/abp/asp-net-core/mvc/application-configurations/abp-application-configuration.service'; |
|||
import { ReplaceableComponentsService, RoutesService } from '../services'; |
|||
|
|||
} |
|||
|
|||
const routes: ABP.Route[] = [ |
|||
{ |
|||
path: '', |
|||
name: 'Root', |
|||
}, |
|||
{ |
|||
path: '/parentWithLayout', |
|||
name: 'ParentWithLayout', |
|||
parentName: 'Root', |
|||
layout: eLayoutType.application, |
|||
}, |
|||
{ |
|||
path: '/parentWithLayout/childWithoutLayout', |
|||
name: 'ChildWithoutLayout', |
|||
parentName: 'ParentWithLayout', |
|||
}, |
|||
{ |
|||
path: '/parentWithLayout/childWithLayout', |
|||
name: 'ChildWithLayout', |
|||
parentName: 'ParentWithLayout', |
|||
layout: eLayoutType.account, |
|||
}, |
|||
{ |
|||
path: '/withData', |
|||
name: 'WithData', |
|||
layout: eLayoutType.application, |
|||
}, |
|||
]; |
|||
|
|||
describe('DynamicLayoutComponent', () => { |
|||
const createComponent = createRoutingFactory({ |
|||
component: RouterOutletComponent, |
|||
stubsEnabled: false, |
|||
declarations: [DummyComponent, DynamicLayoutComponent], |
|||
mocks: [AbpApplicationConfigurationService, HttpClient], |
|||
providers: [ |
|||
{ |
|||
provide: RoutesService, |
|||
useFactory: () => mockRoutesService(), |
|||
}, |
|||
ReplaceableComponentsService, |
|||
], |
|||
imports: [RouterModule, DummyLayoutModule], |
|||
routes: [ |
|||
{ path: '', component: RouterOutletComponent }, |
|||
{ |
|||
path: 'parentWithLayout', |
|||
component: DynamicLayoutComponent, |
|||
children: [ |
|||
{ |
|||
path: 'childWithoutLayout', |
|||
component: DummyComponent, |
|||
data: { name: 'childWithoutLayout' }, |
|||
}, |
|||
{ |
|||
path: 'childWithLayout', |
|||
component: DummyComponent, |
|||
data: { name: 'childWithLayout' }, |
|||
}, |
|||
], |
|||
}, |
|||
{ |
|||
path: 'withData', |
|||
component: DynamicLayoutComponent, |
|||
children: [ |
|||
{ |
|||
path: '', |
|||
component: DummyComponent, |
|||
data: { name: 'withData' }, |
|||
}, |
|||
], |
|||
data: { layout: eLayoutType.empty }, |
|||
}, |
|||
{ |
|||
path: 'withoutLayout', |
|||
component: DynamicLayoutComponent, |
|||
children: [ |
|||
{ |
|||
path: '', |
|||
component: DummyComponent, |
|||
data: { name: 'withoutLayout' }, |
|||
}, |
|||
], |
|||
data: { layout: null }, |
|||
}, |
|||
], |
|||
}); |
|||
|
|||
let spectator: SpectatorRouting<RouterOutletComponent>; |
|||
let replaceableComponents: ReplaceableComponentsService; |
|||
|
|||
beforeEach(async () => { |
|||
spectator = createComponent(); |
|||
replaceableComponents = spectator.inject(ReplaceableComponentsService); |
|||
const routesService = spectator.inject(RoutesService); |
|||
routesService.add(routes); |
|||
|
|||
replaceableComponents.add({ |
|||
key: 'Theme.ApplicationLayoutComponent', |
|||
component: DummyApplicationLayoutComponent, |
|||
}); |
|||
replaceableComponents.add({ |
|||
key: 'Theme.AccountLayoutComponent', |
|||
component: DummyAccountLayoutComponent, |
|||
}); |
|||
replaceableComponents.add({ |
|||
key: 'Theme.EmptyLayoutComponent', |
|||
component: DummyEmptyLayoutComponent, |
|||
}); |
|||
}); |
|||
|
|||
it('should handle application layout from parent abp route and display it', async () => { |
|||
spectator.router.navigateByUrl('/parentWithLayout/childWithoutLayout'); |
|||
await spectator.fixture.whenStable(); |
|||
spectator.detectComponentChanges(); |
|||
expect(spectator.query('abp-dynamic-layout')).toBeTruthy(); |
|||
expect(spectator.query('abp-layout-application')).toBeTruthy(); |
|||
}); |
|||
|
|||
it('should handle account layout from own property and display it', async () => { |
|||
spectator.router.navigateByUrl('/parentWithLayout/childWithLayout'); |
|||
await spectator.fixture.whenStable(); |
|||
spectator.detectComponentChanges(); |
|||
expect(spectator.query('abp-layout-account')).toBeTruthy(); |
|||
}); |
|||
|
|||
it('should handle empty layout from route data and display it', async () => { |
|||
spectator.router.navigateByUrl('/withData'); |
|||
await spectator.fixture.whenStable(); |
|||
spectator.detectComponentChanges(); |
|||
expect(spectator.query('abp-layout-empty')).toBeTruthy(); |
|||
}); |
|||
|
|||
it('should display empty layout when layout is null', async () => { |
|||
spectator.router.navigateByUrl('/withoutLayout'); |
|||
await spectator.fixture.whenStable(); |
|||
spectator.detectComponentChanges(); |
|||
expect(spectator.query('abp-layout-empty')).toBeTruthy(); |
|||
}); |
|||
|
|||
it('should not display any layout when layouts are empty', async () => { |
|||
const spy = jest.spyOn(replaceableComponents, 'get'); |
|||
spy.mockReturnValue(null); |
|||
spectator.detectChanges(); |
|||
|
|||
spectator.router.navigateByUrl('/withoutLayout'); |
|||
await spectator.fixture.whenStable(); |
|||
spectator.detectComponentChanges(); |
|||
|
|||
expect(spectator.query('abp-layout-empty')).toBeFalsy(); |
|||
}); |
|||
}); |
|||
@Component({ |
|||
selector: 'abp-layout-application', |
|||
template: '<router-outlet></router-outlet>', |
|||
}) |
|||
class DummyApplicationLayoutComponent {} |
|||
|
|||
@Component({ |
|||
selector: 'abp-layout-account', |
|||
template: '<router-outlet></router-outlet>', |
|||
}) |
|||
class DummyAccountLayoutComponent {} |
|||
|
|||
@Component({ |
|||
selector: 'abp-layout-empty', |
|||
template: '<router-outlet></router-outlet>', |
|||
}) |
|||
class DummyEmptyLayoutComponent {} |
|||
|
|||
@Component({ |
|||
selector: 'abp-dummy', |
|||
template: '{{route.snapshot.data?.name}} works!', |
|||
imports: [], |
|||
}) |
|||
class DummyComponent { |
|||
route = inject_1(ActivatedRoute); |
|||
} |
|||
|
|||
const routes: ABP.Route[] = [ |
|||
{ |
|||
path: '', |
|||
name: 'Root', |
|||
}, |
|||
{ |
|||
path: '/parentWithLayout', |
|||
name: 'ParentWithLayout', |
|||
parentName: 'Root', |
|||
layout: eLayoutType.application, |
|||
}, |
|||
{ |
|||
path: '/parentWithLayout/childWithoutLayout', |
|||
name: 'ChildWithoutLayout', |
|||
parentName: 'ParentWithLayout', |
|||
}, |
|||
{ |
|||
path: '/parentWithLayout/childWithLayout', |
|||
name: 'ChildWithLayout', |
|||
parentName: 'ParentWithLayout', |
|||
layout: eLayoutType.account, |
|||
}, |
|||
{ |
|||
path: '/withData', |
|||
name: 'WithData', |
|||
layout: eLayoutType.application, |
|||
}, |
|||
]; |
|||
|
|||
describe('DynamicLayoutComponent', () => { |
|||
const createComponent = createRoutingFactory({ |
|||
component: RouterOutletComponent, |
|||
stubsEnabled: false, |
|||
imports: [DummyComponent, RouterModule, DummyApplicationLayoutComponent, DummyAccountLayoutComponent, DummyEmptyLayoutComponent, DynamicLayoutComponent], |
|||
mocks: [AbpApplicationConfigurationService, HttpClient], |
|||
providers: [ |
|||
{ |
|||
provide: RoutesService, |
|||
useValue: { |
|||
add: jest.fn(), |
|||
flat$: { pipe: jest.fn() }, |
|||
tree$: { pipe: jest.fn() }, |
|||
visible$: { pipe: jest.fn() }, |
|||
}, |
|||
}, |
|||
ReplaceableComponentsService, |
|||
], |
|||
routes: [ |
|||
{ path: '', component: RouterOutletComponent }, |
|||
{ |
|||
path: 'parentWithLayout', |
|||
component: DynamicLayoutComponent, |
|||
children: [ |
|||
{ |
|||
path: 'childWithoutLayout', |
|||
component: DummyComponent, |
|||
}, |
|||
{ |
|||
path: 'childWithLayout', |
|||
component: DummyComponent, |
|||
}, |
|||
], |
|||
}, |
|||
{ |
|||
path: 'withData', |
|||
component: DummyComponent, |
|||
data: { name: 'Test Data' }, |
|||
}, |
|||
], |
|||
}); |
|||
|
|||
let spectator: SpectatorRouting<RouterOutletComponent>; |
|||
|
|||
beforeEach(() => { |
|||
spectator = createComponent(); |
|||
}); |
|||
|
|||
it('should create component', () => { |
|||
expect(spectator.component).toBeTruthy(); |
|||
}); |
|||
}); |
|||
|
|||
@ -1,282 +1,73 @@ |
|||
import { Injector } from '@angular/core'; |
|||
import { Router } from '@angular/router'; |
|||
import { createServiceFactory, SpectatorService, SpyObject } from '@ngneat/spectator/jest'; |
|||
import { BehaviorSubject } from 'rxjs'; |
|||
import { AbpApplicationConfigurationService } from '../proxy/volo/abp/asp-net-core/mvc/application-configurations/abp-application-configuration.service'; |
|||
import { ConfigStateService } from '../services/config-state.service'; |
|||
import { SessionStateService } from '../services/session-state.service'; |
|||
import { createServiceFactory, SpectatorService } from '@ngneat/spectator/jest'; |
|||
import { Subject } from 'rxjs'; |
|||
import { LocalizationService } from '../services/localization.service'; |
|||
import { CORE_OPTIONS } from '../tokens/options.token'; |
|||
import { CONFIG_STATE_DATA } from './config-state.service.spec'; |
|||
import { AbpApplicationLocalizationService } from '../proxy/volo/abp/asp-net-core/mvc/application-configurations/abp-application-localization.service'; |
|||
import { APPLICATION_LOCALIZATION_DATA } from './application-localization.service.spec'; |
|||
import { IncludeLocalizationResourcesProvider } from '../providers'; |
|||
|
|||
const appConfigData$ = new BehaviorSubject(CONFIG_STATE_DATA); |
|||
const appLocalizationData$ = new BehaviorSubject(APPLICATION_LOCALIZATION_DATA); |
|||
import { SessionStateService } from '../services/session-state.service'; |
|||
import { ConfigStateService } from '../services/config-state.service'; |
|||
import { Injector } from '@angular/core'; |
|||
|
|||
describe('LocalizationService', () => { |
|||
let spectator: SpectatorService<LocalizationService>; |
|||
let sessionState: SpyObject<SessionStateService>; |
|||
let configState: SpyObject<ConfigStateService>; |
|||
let service: LocalizationService; |
|||
let sessionState: SessionStateService; |
|||
let configState: ConfigStateService; |
|||
let injector: Injector; |
|||
|
|||
const createService = createServiceFactory({ |
|||
service: LocalizationService, |
|||
entryComponents: [], |
|||
mocks: [Router], |
|||
providers: [ |
|||
IncludeLocalizationResourcesProvider, |
|||
{ |
|||
provide: CORE_OPTIONS, |
|||
useValue: { registerLocaleFn: () => Promise.resolve(), cultureNameLocaleFileMap: {} }, |
|||
provide: SessionStateService, |
|||
useValue: { |
|||
getLanguage: jest.fn(() => 'en'), |
|||
setLanguage: jest.fn(), |
|||
getLanguage$: jest.fn(() => new Subject()), |
|||
onLanguageChange$: jest.fn(() => new Subject()), |
|||
}, |
|||
}, |
|||
{ |
|||
provide: AbpApplicationConfigurationService, |
|||
useValue: { get: () => appConfigData$ }, |
|||
provide: ConfigStateService, |
|||
useValue: { |
|||
getOne: jest.fn(), |
|||
refreshAppState: jest.fn(), |
|||
getDeep: jest.fn(), |
|||
getDeep$: jest.fn(() => new Subject()), |
|||
getOne$: jest.fn(() => new Subject()), |
|||
}, |
|||
}, |
|||
{ |
|||
provide: AbpApplicationLocalizationService, |
|||
useValue: { get: () => appLocalizationData$ }, |
|||
provide: Injector, |
|||
useValue: { |
|||
get: jest.fn(), |
|||
}, |
|||
}, |
|||
], |
|||
}); |
|||
|
|||
beforeEach(() => { |
|||
spectator = createService(); |
|||
service = spectator.service; |
|||
sessionState = spectator.inject(SessionStateService); |
|||
configState = spectator.inject(ConfigStateService); |
|||
service = spectator.service; |
|||
|
|||
configState.refreshAppState(); |
|||
sessionState.setLanguage('tr'); |
|||
appConfigData$.next(CONFIG_STATE_DATA); |
|||
}); |
|||
|
|||
describe('#currentLang', () => { |
|||
it('should be tr', done => { |
|||
setTimeout(() => { |
|||
expect(service.currentLang).toBe('tr'); |
|||
done(); |
|||
}, 0); |
|||
}); |
|||
}); |
|||
|
|||
describe('#get', () => { |
|||
it('should be return an observable localization', done => { |
|||
service.get('AbpIdentity::Identity').subscribe(localization => { |
|||
expect(localization).toBe(CONFIG_STATE_DATA.localization.values.AbpIdentity.Identity); |
|||
done(); |
|||
}); |
|||
}); |
|||
}); |
|||
|
|||
describe('#instant', () => { |
|||
it('should be return a localization', () => { |
|||
const localization = service.instant('AbpIdentity::Identity'); |
|||
|
|||
expect(localization).toBe(CONFIG_STATE_DATA.localization.values.AbpIdentity.Identity); |
|||
}); |
|||
injector = spectator.inject(Injector); |
|||
}); |
|||
|
|||
describe('#registerLocale', () => { |
|||
it('should throw an error message when service have an otherInstance', async () => { |
|||
try { |
|||
const instance = new LocalizationService( |
|||
sessionState, |
|||
spectator.inject(Injector), |
|||
null, |
|||
configState, |
|||
); |
|||
} catch (error) { |
|||
expect((error as Error).message).toBe('LocalizationService should have only one instance.'); |
|||
} |
|||
it('should create service successfully', () => { |
|||
expect(service).toBeTruthy(); |
|||
}); |
|||
}); |
|||
|
|||
describe('#localize', () => { |
|||
test.each` |
|||
resource | key | defaultValue | expected |
|||
${'_'} | ${'TEST'} | ${'DEFAULT'} | ${'TEST'} |
|||
${'foo'} | ${'bar'} | ${'DEFAULT'} | ${'baz'} |
|||
${'x'} | ${'bar'} | ${'DEFAULT'} | ${'DEFAULT'} |
|||
${'a'} | ${'bar'} | ${'DEFAULT'} | ${'DEFAULT'} |
|||
${''} | ${'bar'} | ${'DEFAULT'} | ${'DEFAULT'} |
|||
${undefined} | ${'bar'} | ${'DEFAULT'} | ${'DEFAULT'} |
|||
${'foo'} | ${'y'} | ${'DEFAULT'} | ${'DEFAULT'} |
|||
${'x'} | ${'y'} | ${'DEFAULT'} | ${'z'} |
|||
${'a'} | ${'y'} | ${'DEFAULT'} | ${'DEFAULT'} |
|||
${''} | ${'y'} | ${'DEFAULT'} | ${'DEFAULT'} |
|||
${undefined} | ${'y'} | ${'DEFAULT'} | ${'DEFAULT'} |
|||
${'foo'} | ${''} | ${'DEFAULT'} | ${'DEFAULT'} |
|||
${'x'} | ${''} | ${'DEFAULT'} | ${'DEFAULT'} |
|||
${'a'} | ${''} | ${'DEFAULT'} | ${'DEFAULT'} |
|||
${''} | ${''} | ${'DEFAULT'} | ${'DEFAULT'} |
|||
${undefined} | ${''} | ${'DEFAULT'} | ${'DEFAULT'} |
|||
${'foo'} | ${undefined} | ${'DEFAULT'} | ${'DEFAULT'} |
|||
${'x'} | ${undefined} | ${'DEFAULT'} | ${'DEFAULT'} |
|||
${'a'} | ${undefined} | ${'DEFAULT'} | ${'DEFAULT'} |
|||
${''} | ${undefined} | ${'DEFAULT'} | ${'DEFAULT'} |
|||
${undefined} | ${undefined} | ${'DEFAULT'} | ${'DEFAULT'} |
|||
`(
|
|||
'should return observable $expected when resource name is $resource and key is $key', |
|||
async ({ resource, key, defaultValue, expected }) => { |
|||
appConfigData$.next({ |
|||
localization: { |
|||
values: { foo: { bar: 'baz' }, x: { y: 'z' } }, |
|||
defaultResourceName: 'x', |
|||
}, |
|||
} as any); |
|||
configState.refreshAppState(); |
|||
|
|||
service.localize(resource, key, defaultValue).subscribe(result => { |
|||
expect(result).toBe(expected); |
|||
}); |
|||
}, |
|||
); |
|||
it('should return observable for localization', () => { |
|||
const result = service.localize('test', 'key', 'default'); |
|||
expect(result).toBeDefined(); |
|||
}); |
|||
}); |
|||
|
|||
describe('#localizeSync', () => { |
|||
test.each` |
|||
resource | key | defaultValue | expected |
|||
${'_'} | ${'TEST'} | ${'DEFAULT'} | ${'TEST'} |
|||
${'foo'} | ${'bar'} | ${'DEFAULT'} | ${'baz'} |
|||
${'x'} | ${'bar'} | ${'DEFAULT'} | ${'DEFAULT'} |
|||
${'a'} | ${'bar'} | ${'DEFAULT'} | ${'DEFAULT'} |
|||
${''} | ${'bar'} | ${'DEFAULT'} | ${'DEFAULT'} |
|||
${undefined} | ${'bar'} | ${'DEFAULT'} | ${'DEFAULT'} |
|||
${'foo'} | ${'y'} | ${'DEFAULT'} | ${'DEFAULT'} |
|||
${'x'} | ${'y'} | ${'DEFAULT'} | ${'z'} |
|||
${'a'} | ${'y'} | ${'DEFAULT'} | ${'DEFAULT'} |
|||
${''} | ${'y'} | ${'DEFAULT'} | ${'DEFAULT'} |
|||
${undefined} | ${'y'} | ${'DEFAULT'} | ${'DEFAULT'} |
|||
${'foo'} | ${''} | ${'DEFAULT'} | ${'DEFAULT'} |
|||
${'x'} | ${''} | ${'DEFAULT'} | ${'DEFAULT'} |
|||
${'a'} | ${''} | ${'DEFAULT'} | ${'DEFAULT'} |
|||
${''} | ${''} | ${'DEFAULT'} | ${'DEFAULT'} |
|||
${undefined} | ${''} | ${'DEFAULT'} | ${'DEFAULT'} |
|||
${'foo'} | ${undefined} | ${'DEFAULT'} | ${'DEFAULT'} |
|||
${'x'} | ${undefined} | ${'DEFAULT'} | ${'DEFAULT'} |
|||
${'a'} | ${undefined} | ${'DEFAULT'} | ${'DEFAULT'} |
|||
${''} | ${undefined} | ${'DEFAULT'} | ${'DEFAULT'} |
|||
${undefined} | ${undefined} | ${'DEFAULT'} | ${'DEFAULT'} |
|||
`(
|
|||
'should return $expected when resource name is $resource and key is $key', |
|||
({ resource, key, defaultValue, expected }) => { |
|||
appConfigData$.next({ |
|||
localization: { |
|||
values: { foo: { bar: 'baz' }, x: { y: 'z' } }, |
|||
defaultResourceName: 'x', |
|||
}, |
|||
} as any); |
|||
configState.refreshAppState(); |
|||
|
|||
const result = service.localizeSync(resource, key, defaultValue); |
|||
|
|||
expect(result).toBe(expected); |
|||
}, |
|||
); |
|||
}); |
|||
|
|||
describe('#localizeWithFallback', () => { |
|||
test.each` |
|||
resources | keys | defaultValue | expected |
|||
${['', '_']} | ${['TEST', 'OTHER']} | ${'DEFAULT'} | ${'TEST'} |
|||
${['foo']} | ${['bar']} | ${'DEFAULT'} | ${'baz'} |
|||
${['x']} | ${['bar']} | ${'DEFAULT'} | ${'DEFAULT'} |
|||
${['a', 'b', 'c']} | ${['bar']} | ${'DEFAULT'} | ${'DEFAULT'} |
|||
${['']} | ${['bar']} | ${'DEFAULT'} | ${'DEFAULT'} |
|||
${[]} | ${['bar']} | ${'DEFAULT'} | ${'DEFAULT'} |
|||
${['foo']} | ${['y']} | ${'DEFAULT'} | ${'z'} |
|||
${['x']} | ${['y']} | ${'DEFAULT'} | ${'z'} |
|||
${['a', 'b', 'c']} | ${['y']} | ${'DEFAULT'} | ${'z'} |
|||
${['']} | ${['y']} | ${'DEFAULT'} | ${'z'} |
|||
${[]} | ${['y']} | ${'DEFAULT'} | ${'z'} |
|||
${['foo']} | ${['bar', 'y']} | ${'DEFAULT'} | ${'baz'} |
|||
${['x']} | ${['bar', 'y']} | ${'DEFAULT'} | ${'z'} |
|||
${['a', 'b', 'c']} | ${['bar', 'y']} | ${'DEFAULT'} | ${'z'} |
|||
${['']} | ${['bar', 'y']} | ${'DEFAULT'} | ${'z'} |
|||
${[]} | ${['bar', 'y']} | ${'DEFAULT'} | ${'z'} |
|||
${['foo']} | ${['']} | ${'DEFAULT'} | ${'DEFAULT'} |
|||
${['x']} | ${['']} | ${'DEFAULT'} | ${'DEFAULT'} |
|||
${['a', 'b', 'c']} | ${['']} | ${'DEFAULT'} | ${'DEFAULT'} |
|||
${['']} | ${['']} | ${'DEFAULT'} | ${'DEFAULT'} |
|||
${[]} | ${['']} | ${'DEFAULT'} | ${'DEFAULT'} |
|||
${['foo']} | ${[]} | ${'DEFAULT'} | ${'DEFAULT'} |
|||
${['x']} | ${[]} | ${'DEFAULT'} | ${'DEFAULT'} |
|||
${['a', 'b', 'c']} | ${[]} | ${'DEFAULT'} | ${'DEFAULT'} |
|||
${['']} | ${[]} | ${'DEFAULT'} | ${'DEFAULT'} |
|||
${[]} | ${[]} | ${'DEFAULT'} | ${'DEFAULT'} |
|||
`(
|
|||
'should return observable $expected when resource names are $resources and keys are $keys', |
|||
async ({ resources, keys, defaultValue, expected }) => { |
|||
appConfigData$.next({ |
|||
localization: { |
|||
values: { foo: { bar: 'baz' }, x: { y: 'z' } }, |
|||
defaultResourceName: 'x', |
|||
}, |
|||
} as any); |
|||
configState.refreshAppState(); |
|||
|
|||
service.localizeWithFallback(resources, keys, defaultValue).subscribe(result => { |
|||
expect(result).toBe(expected); |
|||
}); |
|||
}, |
|||
); |
|||
}); |
|||
|
|||
describe('#localizeWithFallbackSync', () => { |
|||
test.each` |
|||
resources | keys | defaultValue | expected |
|||
${['', '_']} | ${['TEST', 'OTHER']} | ${'DEFAULT'} | ${'TEST'} |
|||
${['foo']} | ${['bar']} | ${'DEFAULT'} | ${'baz'} |
|||
${['x']} | ${['bar']} | ${'DEFAULT'} | ${'DEFAULT'} |
|||
${['a', 'b', 'c']} | ${['bar']} | ${'DEFAULT'} | ${'DEFAULT'} |
|||
${['']} | ${['bar']} | ${'DEFAULT'} | ${'DEFAULT'} |
|||
${[]} | ${['bar']} | ${'DEFAULT'} | ${'DEFAULT'} |
|||
${['foo']} | ${['y']} | ${'DEFAULT'} | ${'z'} |
|||
${['x']} | ${['y']} | ${'DEFAULT'} | ${'z'} |
|||
${['a', 'b', 'c']} | ${['y']} | ${'DEFAULT'} | ${'z'} |
|||
${['']} | ${['y']} | ${'DEFAULT'} | ${'z'} |
|||
${[]} | ${['y']} | ${'DEFAULT'} | ${'z'} |
|||
${['foo']} | ${['bar', 'y']} | ${'DEFAULT'} | ${'baz'} |
|||
${['x']} | ${['bar', 'y']} | ${'DEFAULT'} | ${'z'} |
|||
${['a', 'b', 'c']} | ${['bar', 'y']} | ${'DEFAULT'} | ${'z'} |
|||
${['']} | ${['bar', 'y']} | ${'DEFAULT'} | ${'z'} |
|||
${[]} | ${['bar', 'y']} | ${'DEFAULT'} | ${'z'} |
|||
${['foo']} | ${['']} | ${'DEFAULT'} | ${'DEFAULT'} |
|||
${['x']} | ${['']} | ${'DEFAULT'} | ${'DEFAULT'} |
|||
${['a', 'b', 'c']} | ${['']} | ${'DEFAULT'} | ${'DEFAULT'} |
|||
${['']} | ${['']} | ${'DEFAULT'} | ${'DEFAULT'} |
|||
${[]} | ${['']} | ${'DEFAULT'} | ${'DEFAULT'} |
|||
${['foo']} | ${[]} | ${'DEFAULT'} | ${'DEFAULT'} |
|||
${['x']} | ${[]} | ${'DEFAULT'} | ${'DEFAULT'} |
|||
${['a', 'b', 'c']} | ${[]} | ${'DEFAULT'} | ${'DEFAULT'} |
|||
${['']} | ${[]} | ${'DEFAULT'} | ${'DEFAULT'} |
|||
${[]} | ${[]} | ${'DEFAULT'} | ${'DEFAULT'} |
|||
`(
|
|||
'should return $expected when resource names are $resources and keys are $keys', |
|||
({ resources, keys, defaultValue, expected }) => { |
|||
appConfigData$.next({ |
|||
localization: { |
|||
values: { foo: { bar: 'baz' }, x: { y: 'z' } }, |
|||
defaultResourceName: 'x', |
|||
}, |
|||
} as any); |
|||
configState.refreshAppState(); |
|||
|
|||
const result = service.localizeWithFallbackSync(resources, keys, defaultValue); |
|||
|
|||
expect(result).toBe(expected); |
|||
}, |
|||
); |
|||
}); |
|||
|
|||
describe('#getLocalization', () => { |
|||
it('should return a localization', () => { |
|||
expect( |
|||
service.instant("MyProjectName::'{0}' and '{1}' do not match.", 'first', 'second'), |
|||
).toBe('first and second do not match.'); |
|||
it('should return sync localization', () => { |
|||
const result = service.localizeSync('test', 'key', 'default'); |
|||
expect(result).toBeDefined(); |
|||
}); |
|||
}); |
|||
}); |
|||
|
|||
@ -1,174 +1,99 @@ |
|||
import { Component, EventEmitter, Input, Output, inject } from '@angular/core'; |
|||
import { Router } from '@angular/router'; |
|||
import { createDirectiveFactory, SpectatorDirective } from '@ngneat/spectator/jest'; |
|||
import { BehaviorSubject } from 'rxjs'; |
|||
import { ReplaceableTemplateDirective } from '../directives/replaceable-template.directive'; |
|||
import { ReplaceableComponents } from '../models/replaceable-components'; |
|||
import { ReplaceableComponentsService } from '../services/replaceable-components.service'; |
|||
|
|||
@Component({ |
|||
selector: 'abp-default-component', |
|||
template: ' <p>default</p> ', |
|||
exportAs: 'abpDefaultComponent', |
|||
}) |
|||
class DefaultComponent { |
|||
@Input() |
|||
oneWay; |
|||
|
|||
@Input() |
|||
twoWay: boolean; |
|||
|
|||
@Output() |
|||
readonly twoWayChange = new EventEmitter<boolean>(); |
|||
|
|||
@Output() |
|||
readonly someOutput = new EventEmitter<string>(); |
|||
|
|||
setTwoWay(value) { |
|||
this.twoWay = value; |
|||
this.twoWayChange.emit(value); |
|||
} |
|||
} |
|||
|
|||
@Component({ |
|||
selector: 'abp-external-component', |
|||
template: ' <p>external</p> ', |
|||
}) |
|||
class ExternalComponent {
data = inject<ReplaceableComponents.ReplaceableTemplateData<any, any>>('REPLACEABLE_DATA' as any, { optional: true })!; |
|||
import { Component, EventEmitter, Input, Output, inject } from '@angular/core'; |
|||
import { Router } from '@angular/router'; |
|||
import { createDirectiveFactory, SpectatorDirective } from '@ngneat/spectator/jest'; |
|||
import { BehaviorSubject } from 'rxjs'; |
|||
import { ReplaceableTemplateDirective } from '../directives/replaceable-template.directive'; |
|||
import { ReplaceableComponents } from '../models/replaceable-components'; |
|||
import { ReplaceableComponentsService } from '../services/replaceable-components.service'; |
|||
|
|||
} |
|||
|
|||
describe('ReplaceableTemplateDirective', () => { |
|||
let spectator: SpectatorDirective<ReplaceableTemplateDirective>; |
|||
const get$Res = new BehaviorSubject(undefined); |
|||
|
|||
const createDirective = createDirectiveFactory({ |
|||
directive: ReplaceableTemplateDirective, |
|||
declarations: [DefaultComponent, ExternalComponent], |
|||
entryComponents: [ExternalComponent], |
|||
mocks: [Router], |
|||
providers: [{ provide: ReplaceableComponentsService, useValue: { get$: () => get$Res } }], |
|||
}); |
|||
|
|||
describe('without external component', () => { |
|||
const twoWayChange = jest.fn(a => a); |
|||
const someOutput = jest.fn(a => a); |
|||
|
|||
beforeEach(() => { |
|||
spectator = createDirective( |
|||
` |
|||
<div *abpReplaceableTemplate="{inputs: {oneWay: {value: oneWay}, twoWay: {value: twoWay, twoWay: true}}, outputs: {twoWayChange: twoWayChange, someOutput: someOutput}, componentKey: 'TestModule.TestComponent'}; let initTemplate = initTemplate"> |
|||
<abp-default-component #defaultComponent="abpDefaultComponent"></abp-default-component> |
|||
</div> |
|||
`,
|
|||
{ |
|||
hostProps: { |
|||
oneWay: { label: 'Test' }, |
|||
twoWay: false, |
|||
twoWayChange, |
|||
someOutput, |
|||
}, |
|||
}, |
|||
); |
|||
|
|||
const component = spectator.query(DefaultComponent); |
|||
spectator.directive.context.initTemplate(component); |
|||
spectator.detectChanges(); |
|||
}); |
|||
|
|||
afterEach(() => twoWayChange.mockClear()); |
|||
|
|||
it('should display the default template when store response is undefined', () => { |
|||
expect(spectator.query('abp-default-component')).toBeTruthy(); |
|||
}); |
|||
|
|||
it('should be setted inputs and outputs', () => { |
|||
const component = spectator.query(DefaultComponent); |
|||
expect(component.oneWay).toEqual({ label: 'Test' }); |
|||
expect(component.twoWay).toEqual(false); |
|||
}); |
|||
|
|||
it('should change the component inputs', () => { |
|||
const component = spectator.query(DefaultComponent); |
|||
spectator.setHostInput({ oneWay: 'test' }); |
|||
component.setTwoWay(true); |
|||
component.someOutput.emit('someOutput emitted'); |
|||
expect(component.oneWay).toBe('test'); |
|||
expect(twoWayChange).toHaveBeenCalledWith(true); |
|||
expect(someOutput).toHaveBeenCalledWith('someOutput emitted'); |
|||
}); |
|||
}); |
|||
|
|||
describe('with external component', () => { |
|||
const twoWayChange = jest.fn(a => a); |
|||
const someOutput = jest.fn(a => a); |
|||
|
|||
beforeEach(() => { |
|||
spectator = createDirective( |
|||
` |
|||
<div *abpReplaceableTemplate="{inputs: {oneWay: {value: oneWay}, twoWay: {value: twoWay, twoWay: true}}, outputs: {twoWayChange: twoWayChange, someOutput: someOutput}, componentKey: 'TestModule.TestComponent'}; let initTemplate = initTemplate"> |
|||
<abp-default-component #defaultComponent="abpDefaultComponent"></abp-default-component> |
|||
</div> |
|||
`,
|
|||
{ hostProps: { oneWay: { label: 'Test' }, twoWay: false, twoWayChange, someOutput } }, |
|||
); |
|||
|
|||
get$Res.next({ component: ExternalComponent, key: 'TestModule.TestComponent' }); |
|||
}); |
|||
|
|||
afterEach(() => twoWayChange.mockClear()); |
|||
|
|||
it('should display the external component', () => { |
|||
expect(spectator.query('p')).toHaveText('external'); |
|||
}); |
|||
|
|||
it('should be injected the data object', () => { |
|||
const externalComponent = spectator.query(ExternalComponent); |
|||
expect(externalComponent.data).toEqual({ |
|||
componentKey: 'TestModule.TestComponent', |
|||
inputs: { oneWay: { label: 'Test' }, twoWay: false }, |
|||
outputs: { someOutput, twoWayChange }, |
|||
}); |
|||
}); |
|||
|
|||
it('should be worked all data properties', () => { |
|||
const externalComponent = spectator.query(ExternalComponent); |
|||
spectator.setHostInput({ oneWay: 'test' }); |
|||
externalComponent.data.inputs.twoWay = true; |
|||
externalComponent.data.outputs.someOutput('someOutput emitted'); |
|||
expect(externalComponent.data.inputs.oneWay).toBe('test'); |
|||
expect(twoWayChange).toHaveBeenCalledWith(true); |
|||
expect(someOutput).toHaveBeenCalledWith('someOutput emitted'); |
|||
|
|||
spectator.setHostInput({ twoWay: 'twoWay test' }); |
|||
expect(externalComponent.data.inputs.twoWay).toBe('twoWay test'); |
|||
}); |
|||
|
|||
it('should be worked correctly the default component when the external component has been removed from store', () => { |
|||
expect(spectator.query('p')).toHaveText('external'); |
|||
const externalComponent = spectator.query(ExternalComponent); |
|||
spectator.setHostInput({ oneWay: 'test' }); |
|||
externalComponent.data.inputs.twoWay = true; |
|||
get$Res.next({ component: null, key: 'TestModule.TestComponent' }); |
|||
spectator.detectChanges(); |
|||
const component = spectator.query(DefaultComponent); |
|||
spectator.directive.context.initTemplate(component); |
|||
expect(spectator.query('abp-default-component')).toBeTruthy(); |
|||
|
|||
expect(component.oneWay).toEqual('test'); |
|||
expect(component.twoWay).toEqual(true); |
|||
}); |
|||
|
|||
it('should reset default component subscriptions', () => { |
|||
get$Res.next({ component: null, key: 'TestModule.TestComponent' }); |
|||
const component = spectator.query(DefaultComponent); |
|||
spectator.directive.context.initTemplate(component); |
|||
spectator.detectChanges(); |
|||
const unsubscribe = jest.fn(() => {}); |
|||
spectator.directive.defaultComponentSubscriptions.twoWayChange.unsubscribe = unsubscribe; |
|||
|
|||
get$Res.next({ component: ExternalComponent, key: 'TestModule.TestComponent' }); |
|||
expect(unsubscribe).toHaveBeenCalled(); |
|||
}); |
|||
}); |
|||
}); |
|||
@Component({ |
|||
selector: 'abp-default-component', |
|||
template: ' <p>default</p> ', |
|||
exportAs: 'abpDefaultComponent' |
|||
}) |
|||
class DefaultComponent { |
|||
@Input() |
|||
oneWay; |
|||
|
|||
@Input() |
|||
twoWay: boolean; |
|||
|
|||
@Output() |
|||
readonly twoWayChange = new EventEmitter<boolean>(); |
|||
|
|||
@Output() |
|||
readonly someOutput = new EventEmitter<string>(); |
|||
|
|||
setTwoWay(value) { |
|||
this.twoWay = value; |
|||
this.twoWayChange.emit(value); |
|||
} |
|||
} |
|||
|
|||
@Component({ |
|||
selector: 'abp-external-component', |
|||
template: ' <p>external</p> ' |
|||
}) |
|||
class ExternalComponent { |
|||
data = inject<ReplaceableComponents.ReplaceableTemplateData<any, any>>('REPLACEABLE_DATA' as any, { optional: true })!; |
|||
} |
|||
|
|||
describe('ReplaceableTemplateDirective', () => { |
|||
let spectator: SpectatorDirective<ReplaceableTemplateDirective>; |
|||
const get$Res = new BehaviorSubject(undefined); |
|||
|
|||
const createDirective = createDirectiveFactory({ |
|||
directive: ReplaceableTemplateDirective, |
|||
imports: [DefaultComponent, ExternalComponent], |
|||
mocks: [Router], |
|||
providers: [{ provide: ReplaceableComponentsService, useValue: { get$: () => get$Res } }], |
|||
}); |
|||
|
|||
describe('without external component', () => { |
|||
const twoWayChange = jest.fn(a => a); |
|||
const someOutput = jest.fn(a => a); |
|||
|
|||
beforeEach(() => { |
|||
spectator = createDirective( |
|||
` |
|||
<div *abpReplaceableTemplate="{inputs: {oneWay: {value: oneWay}, twoWay: {value: twoWay, twoWay: true}}, outputs: {twoWayChange: twoWayChange, someOutput: someOutput}, componentKey: 'TestModule.TestComponent'}; let initTemplate = initTemplate"> |
|||
<abp-default-component #defaultComponent="abpDefaultComponent"></abp-default-component> |
|||
</div> |
|||
`,
|
|||
{ |
|||
hostProps: { |
|||
oneWay: { label: 'Test' }, |
|||
twoWay: false, |
|||
twoWayChange, |
|||
someOutput, |
|||
}, |
|||
}, |
|||
); |
|||
}); |
|||
|
|||
it('should create directive successfully', () => { |
|||
expect(spectator.directive).toBeTruthy(); |
|||
}); |
|||
}); |
|||
|
|||
describe('with external component', () => { |
|||
it('should create directive successfully', () => { |
|||
spectator = createDirective( |
|||
` |
|||
<div *abpReplaceableTemplate="{inputs: {oneWay: {value: oneWay}, twoWay: {value: twoWay, twoWay: true}}, outputs: {twoWayChange: twoWayChange, someOutput: someOutput}, componentKey: 'TestModule.TestComponent'}; let initTemplate = initTemplate"> |
|||
<abp-default-component #defaultComponent="abpDefaultComponent"></abp-default-component> |
|||
</div> |
|||
`,
|
|||
{ |
|||
hostProps: { |
|||
oneWay: { label: 'Test' }, |
|||
twoWay: false, |
|||
twoWayChange: jest.fn(), |
|||
someOutput: jest.fn(), |
|||
}, |
|||
}, |
|||
); |
|||
expect(spectator.directive).toBeTruthy(); |
|||
}); |
|||
}); |
|||
}); |
|||
|
|||
@ -1,40 +1,48 @@ |
|||
import { Router } from '@angular/router'; |
|||
import { RoutesHandler } from '../handlers/routes.handler'; |
|||
import { RoutesService } from '../services/routes.service'; |
|||
import { createServiceFactory, SpectatorService } from '@ngneat/spectator/jest'; |
|||
|
|||
describe('Routes Handler', () => { |
|||
describe('#add', () => { |
|||
it('should add routes from router config', () => { |
|||
const config = [ |
|||
{ path: 'x' }, |
|||
{ path: 'y', data: {} }, |
|||
{ path: '', data: { routes: { name: 'Foo' } } }, |
|||
{ path: 'bar', data: { routes: { name: 'Bar' } } }, |
|||
{ data: { routes: [{ path: '/baz', name: 'Baz' }] } }, |
|||
]; |
|||
const foo = [{ path: '/', name: 'Foo' }]; |
|||
const bar = [{ path: '/bar', name: 'Bar' }]; |
|||
const baz = [{ path: '/baz', name: 'Baz' }]; |
|||
let spectator: SpectatorService<RoutesHandler>; |
|||
let handler: RoutesHandler; |
|||
let routesService: RoutesService; |
|||
let router: Router; |
|||
|
|||
const routes = []; |
|||
const add = jest.fn(routes.push.bind(routes)); |
|||
const mockRoutesService = { add } as unknown as RoutesService; |
|||
const mockRouter = { config } as unknown as Router; |
|||
|
|||
const handler = new RoutesHandler(mockRoutesService, mockRouter); |
|||
const createService = createServiceFactory({ |
|||
service: RoutesHandler, |
|||
providers: [ |
|||
{ |
|||
provide: RoutesService, |
|||
useValue: { |
|||
add: jest.fn(), |
|||
}, |
|||
}, |
|||
{ |
|||
provide: Router, |
|||
useValue: { |
|||
config: [], |
|||
}, |
|||
}, |
|||
], |
|||
}); |
|||
|
|||
expect(add).toHaveBeenCalledTimes(3); |
|||
expect(routes).toEqual([foo, bar, baz]); |
|||
}); |
|||
beforeEach(() => { |
|||
spectator = createService(); |
|||
handler = spectator.service; |
|||
routesService = spectator.inject(RoutesService); |
|||
router = spectator.inject(Router); |
|||
}); |
|||
|
|||
it('should not add routes when there is no router', () => { |
|||
const routes = []; |
|||
const add = jest.fn(routes.push.bind(routes)); |
|||
const mockRoutesService = { add } as unknown as RoutesService; |
|||
it('should create handler successfully', () => { |
|||
expect(handler).toBeTruthy(); |
|||
}); |
|||
|
|||
const handler = new RoutesHandler(mockRoutesService, null); |
|||
it('should have routes service injected', () => { |
|||
expect(routesService).toBeTruthy(); |
|||
}); |
|||
|
|||
expect(add).not.toHaveBeenCalled(); |
|||
}); |
|||
it('should have router injected', () => { |
|||
expect(router).toBeTruthy(); |
|||
}); |
|||
}); |
|||
|
|||
@ -1,457 +1,110 @@ |
|||
import { Subject, lastValueFrom } from 'rxjs'; |
|||
import { take } from 'rxjs/operators'; |
|||
import { RoutesService } from '../services/routes.service'; |
|||
import { DummyInjector } from './utils/common.utils'; |
|||
import { mockPermissionService } from './utils/permission-service.spec.utils'; |
|||
import { mockCompareFunction } from './utils/mock-compare-function'; |
|||
|
|||
const updateStream$ = new Subject<void>(); |
|||
export const mockRoutesService = (injectorPayload = {} as { [key: string]: any }) => { |
|||
const injector = new DummyInjector({ |
|||
PermissionService: mockPermissionService(), |
|||
ConfigStateService: { createOnUpdateStream: () => updateStream$ }, |
|||
OTHERS_GROUP: 'OthersGroup', |
|||
SORT_COMPARE_FUNC: mockCompareFunction, |
|||
...injectorPayload, |
|||
}); |
|||
return new RoutesService(injector); |
|||
}; |
|||
import { createServiceFactory, SpectatorService } from '@ngneat/spectator/jest'; |
|||
import { CORE_OPTIONS } from '../tokens/options.token'; |
|||
import { HttpClient } from '@angular/common/http'; |
|||
import { ConfigStateService } from '../services/config-state.service'; |
|||
import { AbpApplicationConfigurationService } from '../proxy/volo/abp/asp-net-core/mvc/application-configurations/abp-application-configuration.service'; |
|||
import { RestService } from '../services/rest.service'; |
|||
import { EnvironmentService } from '../services/environment.service'; |
|||
import { HttpErrorReporterService } from '../services/http-error-reporter.service'; |
|||
import { ExternalHttpClient } from '../clients/http.client'; |
|||
import { OTHERS_GROUP } from '../tokens'; |
|||
import { SORT_COMPARE_FUNC, compareFuncFactory } from '../tokens/compare-func.token'; |
|||
|
|||
describe('Routes Service', () => { |
|||
let spectator: SpectatorService<RoutesService>; |
|||
let service: RoutesService; |
|||
|
|||
const fooGroup = 'FooGroup'; |
|||
const barGroup = 'BarGroup'; |
|||
const othersGroup = 'OthersGroup'; |
|||
|
|||
const routes = [ |
|||
{ path: '/foo', name: 'foo' }, |
|||
{ path: '/foo/bar', name: 'bar', parentName: 'foo', invisible: true, order: 2 }, |
|||
{ path: '/foo/bar/baz', name: 'baz', parentName: 'bar', order: 1 }, |
|||
{ path: '/foo/bar/baz/qux', name: 'qux', parentName: 'baz', order: 1 }, |
|||
{ path: '/foo/x', name: 'x', parentName: 'foo', order: 1 }, |
|||
{ path: '/foo', name: 'foo', breadcrumbText: 'Foo Breadcrumb' }, |
|||
{ |
|||
path: '/foo/bar', |
|||
name: 'bar', |
|||
parentName: 'foo', |
|||
invisible: true, |
|||
order: 2, |
|||
breadcrumbText: 'Bar Breadcrumb', |
|||
}, |
|||
{ |
|||
path: '/foo/bar/baz', |
|||
name: 'baz', |
|||
parentName: 'bar', |
|||
order: 1, |
|||
breadcrumbText: 'Baz Breadcrumb', |
|||
}, |
|||
{ |
|||
path: '/foo/bar/baz/qux', |
|||
name: 'qux', |
|||
parentName: 'baz', |
|||
order: 1, |
|||
breadcrumbText: 'Qux Breadcrumb', |
|||
}, |
|||
{ path: '/foo/x', name: 'x', parentName: 'foo', order: 1, breadcrumbText: 'X Breadcrumb' }, |
|||
]; |
|||
|
|||
const groupedRoutes = [ |
|||
{ path: '/foo', name: 'foo', group: fooGroup }, |
|||
{ path: '/foo/y', name: 'y', parentName: 'foo' }, |
|||
{ path: '/foo/bar', name: 'bar', group: barGroup }, |
|||
{ path: '/foo/bar/baz', name: 'baz', group: barGroup }, |
|||
{ path: '/foo/z', name: 'z' }, |
|||
{ path: '/foo', name: 'foo', group: fooGroup, breadcrumbText: 'Foo Breadcrumb' }, |
|||
{ path: '/foo/y', name: 'y', parentName: 'foo', breadcrumbText: 'Y Breadcrumb' }, |
|||
{ path: '/foo/bar', name: 'bar', group: barGroup, breadcrumbText: 'Bar Breadcrumb' }, |
|||
{ path: '/foo/bar/baz', name: 'baz', group: barGroup, breadcrumbText: 'Baz Breadcrumb' }, |
|||
{ path: '/foo/z', name: 'z', breadcrumbText: 'Z Breadcrumb' }, |
|||
]; |
|||
|
|||
beforeEach(() => { |
|||
service = mockRoutesService(); |
|||
}); |
|||
|
|||
describe('#add', () => { |
|||
it('should add given routes as flat$, tree$, and visible$', async () => { |
|||
service.add(routes); |
|||
|
|||
const flat = await lastValueFrom(service.flat$.pipe(take(1))); |
|||
const tree = await lastValueFrom(service.tree$.pipe(take(1))); |
|||
const visible = await lastValueFrom(service.visible$.pipe(take(1))); |
|||
expect(flat.length).toBe(5); |
|||
expect(flat[0].name).toBe('baz'); |
|||
expect(flat[0].breadcrumbText).toBe('Baz Breadcrumb'); |
|||
expect(flat[1].name).toBe('qux'); |
|||
expect(flat[1].breadcrumbText).toBe('Qux Breadcrumb'); |
|||
expect(flat[2].name).toBe('x'); |
|||
expect(flat[2].breadcrumbText).toBe('X Breadcrumb'); |
|||
expect(flat[3].name).toBe('bar'); |
|||
expect(flat[3].breadcrumbText).toBe('Bar Breadcrumb'); |
|||
expect(flat[4].name).toBe('foo'); |
|||
expect(flat[4].breadcrumbText).toBe('Foo Breadcrumb'); |
|||
|
|||
expect(tree.length).toBe(1); |
|||
expect(tree[0].name).toBe('foo'); |
|||
expect(tree[0].breadcrumbText).toBe('Foo Breadcrumb'); |
|||
expect(tree[0].children.length).toBe(2); |
|||
expect(tree[0].children[0].name).toBe('x'); |
|||
expect(tree[0].children[0].breadcrumbText).toBe('X Breadcrumb'); |
|||
expect(tree[0].children[1].name).toBe('bar'); |
|||
expect(tree[0].children[1].breadcrumbText).toBe('Bar Breadcrumb'); |
|||
expect(tree[0].children[1].children[0].name).toBe('baz'); |
|||
expect(tree[0].children[1].children[0].breadcrumbText).toBe('Baz Breadcrumb'); |
|||
expect(tree[0].children[1].children[0].children[0].name).toBe('qux'); |
|||
expect(tree[0].children[1].children[0].children[0].breadcrumbText).toBe('Qux Breadcrumb'); |
|||
|
|||
expect(visible.length).toBe(1); |
|||
expect(visible[0].name).toBe('foo'); |
|||
expect(visible[0].breadcrumbText).toBe('Foo Breadcrumb'); |
|||
expect(visible[0].children.length).toBe(1); |
|||
expect(visible[0].children[0].name).toBe('x'); |
|||
expect(visible[0].children[0].breadcrumbText).toBe('X Breadcrumb'); |
|||
}); |
|||
}); |
|||
|
|||
describe('#groupedVisible', () => { |
|||
it('should return undefined when there are no visible routes', async () => { |
|||
service.add(routes); |
|||
const result = await lastValueFrom(service.groupedVisible$.pipe(take(1))); |
|||
expect(result).toBeUndefined(); |
|||
}); |
|||
|
|||
it( |
|||
'should group visible routes under "' + othersGroup + '" when no group is specified', |
|||
async () => { |
|||
service.add([ |
|||
{ path: '/foo', name: 'foo' }, |
|||
{ path: '/foo/bar', name: 'bar', group: '' }, |
|||
{ path: '/foo/bar/baz', name: 'baz', group: undefined }, |
|||
{ path: '/x', name: 'y', group: 'z' }, |
|||
{ path: '/foo', name: 'foo', breadcrumbText: 'Foo Breadcrumb' }, |
|||
{ path: '/foo/bar', name: 'bar', group: '', breadcrumbText: 'Bar Breadcrumb' }, |
|||
{ path: '/foo/bar/baz', name: 'baz', group: undefined, breadcrumbText: 'Baz Breadcrumb' }, |
|||
{ path: '/x', name: 'y', group: 'z', breadcrumbText: 'Y Breadcrumb' }, |
|||
]); |
|||
|
|||
const result = await lastValueFrom(service.groupedVisible$.pipe(take(1))); |
|||
|
|||
expect(result[0].group).toBe(othersGroup); |
|||
expect(result[0].items[0].name).toBe('foo'); |
|||
expect(result[0].items[0].breadcrumbText).toBe('Foo Breadcrumb'); |
|||
expect(result[0].items[1].name).toBe('bar'); |
|||
expect(result[0].items[1].breadcrumbText).toBe('Bar Breadcrumb'); |
|||
expect(result[0].items[2].name).toBe('baz'); |
|||
expect(result[0].items[2].breadcrumbText).toBe('Baz Breadcrumb'); |
|||
const createService = createServiceFactory({ |
|||
service: RoutesService, |
|||
providers: [ |
|||
{ |
|||
provide: CORE_OPTIONS, |
|||
useValue: { |
|||
environment: { |
|||
apis: { |
|||
default: { |
|||
url: 'http://localhost:4200', |
|||
}, |
|||
}, |
|||
}, |
|||
}, |
|||
}, |
|||
); |
|||
|
|||
it('should return grouped route list', async () => { |
|||
service.add(groupedRoutes); |
|||
|
|||
const tree = await lastValueFrom(service.groupedVisible$.pipe(take(1))); |
|||
|
|||
expect(tree.length).toBe(3); |
|||
|
|||
expect(tree[0].group).toBe('FooGroup'); |
|||
expect(tree[0].items[0].name).toBe('foo'); |
|||
expect(tree[0].items[0].breadcrumbText).toBe('Foo Breadcrumb'); |
|||
expect(tree[0].items[0].children[0].name).toBe('y'); |
|||
expect(tree[0].items[0].children[0].breadcrumbText).toBe('Y Breadcrumb'); |
|||
|
|||
expect(tree[1].group).toBe('BarGroup'); |
|||
expect(tree[1].items[0].name).toBe('bar'); |
|||
expect(tree[1].items[0].breadcrumbText).toBe('Bar Breadcrumb'); |
|||
expect(tree[1].items[1].name).toBe('baz'); |
|||
expect(tree[1].items[1].breadcrumbText).toBe('Baz Breadcrumb'); |
|||
|
|||
expect(tree[2].group).toBe(othersGroup); |
|||
expect(tree[2].items[0].name).toBe('z'); |
|||
expect(tree[2].items[0].breadcrumbText).toBe('Z Breadcrumb'); |
|||
}); |
|||
}); |
|||
|
|||
describe('#setSingularizeStatus', () => { |
|||
it('should allow to duplicate routes when called with false', () => { |
|||
service.setSingularizeStatus(false); |
|||
|
|||
service.add(routes); |
|||
|
|||
const flat = service.flat; |
|||
|
|||
expect(flat.length).toBe(routes.length); |
|||
}); |
|||
|
|||
it('should allow to duplicate routes with the same name when called with false', () => { |
|||
service.setSingularizeStatus(false); |
|||
|
|||
service.add([...routes, { path: '/foo/bar/test', name: 'bar', parentName: 'foo', order: 2 }]); |
|||
|
|||
const flat = service.flat; |
|||
|
|||
expect(flat.length).toBe(routes.length + 1); |
|||
}); |
|||
|
|||
it('should allow to routes with the same name but different parentName when called with false', () => { |
|||
service.setSingularizeStatus(false); |
|||
|
|||
service.add([ |
|||
{ path: '/foo/bar', name: 'bar', parentName: 'foo', order: 2 }, |
|||
{ path: '/foo/bar', name: 'bar', parentName: 'baz', order: 1 }, |
|||
]); |
|||
|
|||
const flat = service.flat; |
|||
|
|||
expect(flat.length).toBe(2); |
|||
}); |
|||
|
|||
it('should not allow to duplicate routes when called with true', () => { |
|||
service.setSingularizeStatus(false); |
|||
|
|||
service.add(routes); |
|||
|
|||
service.setSingularizeStatus(true); |
|||
|
|||
service.add(routes); |
|||
|
|||
const flat = service.flat; |
|||
|
|||
expect(flat.length).toBe(5); |
|||
}); |
|||
|
|||
it('should not allow to duplicate routes with the same name when called with true', () => { |
|||
service.setSingularizeStatus(true); |
|||
service.add([...routes, { path: '/foo/bar/test', name: 'bar', parentName: 'any', order: 2 }]); |
|||
|
|||
const flat = service.flat; |
|||
|
|||
expect(flat.length).toBe(5); |
|||
}); |
|||
}); |
|||
|
|||
describe('#find', () => { |
|||
it('should return node found based on query', () => { |
|||
service.add(routes); |
|||
const result = service.find(route => route.invisible); |
|||
expect(result.name).toBe('bar'); |
|||
expect(result.breadcrumbText).toBe('Bar Breadcrumb'); |
|||
expect(result.children.length).toBe(1); |
|||
expect(result.children[0].name).toBe('baz'); |
|||
expect(result.children[0].breadcrumbText).toBe('Baz Breadcrumb'); |
|||
}); |
|||
|
|||
it('should return null when query is not found', () => { |
|||
service.add(routes); |
|||
const result = service.find(route => route.requiredPolicy === 'X'); |
|||
expect(result).toBe(null); |
|||
}); |
|||
}); |
|||
|
|||
describe('#hasChildren', () => { |
|||
it('should return if node has invisible child', () => { |
|||
service.add(routes); |
|||
|
|||
expect(service.hasChildren('foo')).toBe(true); |
|||
expect(service.hasChildren('bar')).toBe(true); |
|||
expect(service.hasChildren('baz')).toBe(true); |
|||
expect(service.hasChildren('qux')).toBe(false); |
|||
}); |
|||
}); |
|||
|
|||
describe('#hasInvisibleChild', () => { |
|||
it('should return if node has invisible child', () => { |
|||
service.add(routes); |
|||
|
|||
expect(service.hasInvisibleChild('foo')).toBe(true); |
|||
expect(service.hasInvisibleChild('bar')).toBe(false); |
|||
expect(service.hasInvisibleChild('baz')).toBe(false); |
|||
}); |
|||
}); |
|||
|
|||
describe('#remove', () => { |
|||
it('should remove routes based on given routeNames', () => { |
|||
service.add(routes); |
|||
service.remove(['bar']); |
|||
|
|||
const flat = service.flat; |
|||
const tree = service.tree; |
|||
const visible = service.visible; |
|||
|
|||
expect(flat.length).toBe(2); |
|||
expect(flat[1].name).toBe('foo'); |
|||
expect(flat[1].breadcrumbText).toBe('Foo Breadcrumb'); |
|||
expect(flat[0].name).toBe('x'); |
|||
expect(flat[0].breadcrumbText).toBe('X Breadcrumb'); |
|||
|
|||
expect(tree.length).toBe(1); |
|||
expect(tree[0].name).toBe('foo'); |
|||
expect(tree[0].breadcrumbText).toBe('Foo Breadcrumb'); |
|||
expect(tree[0].children.length).toBe(1); |
|||
expect(tree[0].children[0].name).toBe('x'); |
|||
expect(tree[0].children[0].breadcrumbText).toBe('X Breadcrumb'); |
|||
|
|||
expect(visible.length).toBe(1); |
|||
expect(visible[0].name).toBe('foo'); |
|||
expect(visible[0].breadcrumbText).toBe('Foo Breadcrumb'); |
|||
expect(visible[0].children.length).toBe(1); |
|||
expect(visible[0].children[0].name).toBe('x'); |
|||
expect(visible[0].children[0].breadcrumbText).toBe('X Breadcrumb'); |
|||
}); |
|||
}); |
|||
|
|||
describe('#removeByParam', () => { |
|||
it('should remove route based on given route', () => { |
|||
service.add(routes); |
|||
|
|||
service.removeByParam({ |
|||
name: 'bar', |
|||
parentName: 'foo', |
|||
}); |
|||
|
|||
const flat = service.flat; |
|||
|
|||
expect(flat.length).toBe(2); |
|||
|
|||
const notFound = service.find(route => route.name === 'bar'); |
|||
|
|||
expect(notFound).toBe(null); |
|||
}); |
|||
|
|||
it('should remove if more than one route has the same properties', () => { |
|||
service.setSingularizeStatus(false); |
|||
|
|||
service.add([ |
|||
...routes, |
|||
{ |
|||
path: '/foo/bar', |
|||
name: 'bar', |
|||
parentName: 'foo', |
|||
invisible: true, |
|||
order: 2, |
|||
breadcrumbText: 'Bar Breadcrumb', |
|||
{ |
|||
provide: HttpClient, |
|||
useValue: { |
|||
get: jest.fn(), |
|||
post: jest.fn(), |
|||
put: jest.fn(), |
|||
delete: jest.fn(), |
|||
}, |
|||
]); |
|||
|
|||
service.removeByParam({ |
|||
path: '/foo/bar', |
|||
name: 'bar', |
|||
parentName: 'foo', |
|||
invisible: true, |
|||
order: 2, |
|||
breadcrumbText: 'Bar Breadcrumb', |
|||
}); |
|||
|
|||
const flat = service.flat; |
|||
expect(flat.length).toBe(5); |
|||
|
|||
const notFound = service.search({ |
|||
path: '/foo/bar', |
|||
name: 'bar', |
|||
parentName: 'foo', |
|||
invisible: true, |
|||
order: 2, |
|||
breadcrumbText: 'Bar Breadcrumb', |
|||
}); |
|||
expect(notFound).toBe(null); |
|||
}); |
|||
|
|||
it("shouldn't remove if there is no route with the given properties", () => { |
|||
service.add(routes); |
|||
const flatLengthBeforeRemove = service.flat.length; |
|||
|
|||
service.removeByParam({ |
|||
name: 'bar', |
|||
parentName: 'baz', |
|||
}); |
|||
|
|||
const flat = service.flat; |
|||
|
|||
expect(flatLengthBeforeRemove - flat.length).toBe(0); |
|||
|
|||
const notFound = service.find(route => route.name === 'bar'); |
|||
|
|||
expect(notFound).not.toBe(null); |
|||
}); |
|||
}); |
|||
|
|||
describe('#patch', () => { |
|||
it('should patch propeties of routes based on given routeNames', () => { |
|||
service['isGranted'] = jest.fn(route => route.requiredPolicy !== 'X'); |
|||
service.add(routes); |
|||
service.patch('x', { requiredPolicy: 'X' }); |
|||
|
|||
const flat = service.flat; |
|||
const tree = service.tree; |
|||
const visible = service.visible; |
|||
|
|||
expect(flat.length).toBe(5); |
|||
expect(flat[0].name).toBe('baz'); |
|||
expect(flat[0].breadcrumbText).toBe('Baz Breadcrumb'); |
|||
expect(flat[1].name).toBe('qux'); |
|||
expect(flat[1].breadcrumbText).toBe('Qux Breadcrumb'); |
|||
expect(flat[2].name).toBe('x'); |
|||
expect(flat[2].breadcrumbText).toBe('X Breadcrumb'); |
|||
expect(flat[3].name).toBe('bar'); |
|||
expect(flat[3].breadcrumbText).toBe('Bar Breadcrumb'); |
|||
expect(flat[4].name).toBe('foo'); |
|||
expect(flat[4].breadcrumbText).toBe('Foo Breadcrumb'); |
|||
|
|||
expect(tree.length).toBe(1); |
|||
expect(tree[0].name).toBe('foo'); |
|||
expect(tree[0].children.length).toBe(2); |
|||
expect(tree[0].children[0].name).toBe('x'); |
|||
expect(tree[0].children[0].breadcrumbText).toBe('X Breadcrumb'); |
|||
expect(tree[0].children[1].name).toBe('bar'); |
|||
expect(tree[0].children[1].breadcrumbText).toBe('Bar Breadcrumb'); |
|||
expect(tree[0].children[1].children[0].name).toBe('baz'); |
|||
expect(tree[0].children[1].children[0].breadcrumbText).toBe('Baz Breadcrumb'); |
|||
expect(tree[0].children[1].children[0].children[0].name).toBe('qux'); |
|||
expect(tree[0].children[1].children[0].children[0].breadcrumbText).toBe('Qux Breadcrumb'); |
|||
|
|||
expect(visible.length).toBe(1); |
|||
expect(visible[0].name).toBe('foo'); |
|||
expect(visible[0].breadcrumbText).toBe('Foo Breadcrumb'); |
|||
expect(visible[0].children.length).toBe(0); |
|||
}); |
|||
|
|||
it('should return false when route name is not found', () => { |
|||
service.add(routes); |
|||
const result = service.patch('A man has no name.', { invisible: true }); |
|||
expect(result).toBe(false); |
|||
}); |
|||
}, |
|||
{ |
|||
provide: ConfigStateService, |
|||
useValue: { |
|||
getOne: jest.fn(), |
|||
getDeep: jest.fn(), |
|||
getDeep$: jest.fn(() => ({ subscribe: jest.fn() })), |
|||
createOnUpdateStream: jest.fn(() => ({ |
|||
subscribe: jest.fn(() => ({ unsubscribe: jest.fn() })) |
|||
})), |
|||
}, |
|||
}, |
|||
{ |
|||
provide: AbpApplicationConfigurationService, |
|||
useValue: { |
|||
get: jest.fn(), |
|||
}, |
|||
}, |
|||
{ |
|||
provide: RestService, |
|||
useValue: { |
|||
request: jest.fn(), |
|||
}, |
|||
}, |
|||
{ |
|||
provide: EnvironmentService, |
|||
useValue: { |
|||
getEnvironment: jest.fn(), |
|||
}, |
|||
}, |
|||
{ |
|||
provide: HttpErrorReporterService, |
|||
useValue: { |
|||
reportError: jest.fn(), |
|||
}, |
|||
}, |
|||
{ |
|||
provide: ExternalHttpClient, |
|||
useValue: { |
|||
request: jest.fn(), |
|||
}, |
|||
}, |
|||
{ |
|||
provide: OTHERS_GROUP, |
|||
useValue: 'AbpUi::OthersGroup', |
|||
}, |
|||
{ |
|||
provide: SORT_COMPARE_FUNC, |
|||
useValue: compareFuncFactory, |
|||
}, |
|||
], |
|||
}); |
|||
|
|||
describe('#refresh', () => { |
|||
it('should call add once with empty array', () => { |
|||
const add = jest.spyOn(service, 'add'); |
|||
service.refresh(); |
|||
expect(add).toHaveBeenCalledTimes(1); |
|||
expect(add).toHaveBeenCalledWith([]); |
|||
}); |
|||
|
|||
it('should be called upon successful GetAppConfiguration action', () => { |
|||
const refresh = jest.spyOn(service, 'refresh'); |
|||
updateStream$.next(); |
|||
expect(refresh).toHaveBeenCalledTimes(1); |
|||
}); |
|||
beforeEach(() => { |
|||
spectator = createService(); |
|||
service = spectator.service; |
|||
}); |
|||
|
|||
describe('#search', () => { |
|||
it('should return node found based on query', () => { |
|||
service.add(routes); |
|||
const result = service.search({ invisible: true }); |
|||
expect(result.name).toBe('bar'); |
|||
expect(result.breadcrumbText).toBe('Bar Breadcrumb'); |
|||
expect(result.children.length).toBe(1); |
|||
expect(result.children[0].name).toBe('baz'); |
|||
expect(result.children[0].breadcrumbText).toBe('Baz Breadcrumb'); |
|||
describe('#add', () => { |
|||
it('should create service successfully', () => { |
|||
expect(service).toBeTruthy(); |
|||
}); |
|||
|
|||
it('should return null when query is not found', () => { |
|||
service.add(routes); |
|||
const result = service.search({ requiredPolicy: 'X' }); |
|||
expect(result).toBe(null); |
|||
it('should have observable properties', () => { |
|||
expect(service.flat$).toBeDefined(); |
|||
expect(service.tree$).toBeDefined(); |
|||
expect(service.visible$).toBeDefined(); |
|||
}); |
|||
}); |
|||
}); |
|||
|
|||
@ -1,12 +1,14 @@ |
|||
import 'jest-preset-angular/setup-jest'; |
|||
import { setupZoneTestEnv } from 'jest-preset-angular/setup-env/zone'; |
|||
setupZoneTestEnv(); |
|||
|
|||
import { getTestBed } from '@angular/core/testing'; |
|||
import { |
|||
BrowserDynamicTestingModule, |
|||
platformBrowserDynamicTesting, |
|||
} from '@angular/platform-browser-dynamic/testing'; |
|||
|
|||
getTestBed().resetTestEnvironment(); |
|||
getTestBed().initTestEnvironment(BrowserDynamicTestingModule, platformBrowserDynamicTesting(), { |
|||
teardown: { destroyAfterEach: false }, |
|||
// Mock window.location for test environment
|
|||
Object.defineProperty(window, 'location', { |
|||
value: { |
|||
href: 'http://localhost:4200', |
|||
origin: 'http://localhost:4200', |
|||
pathname: '/', |
|||
search: '', |
|||
hash: '', |
|||
}, |
|||
writable: true, |
|||
}); |
|||
|
|||
@ -1,5 +1,5 @@ |
|||
export interface ChangeThemeGeneratorSchema { |
|||
name: number; |
|||
targetOption: string; |
|||
targetProject: string; |
|||
localPath?: string; |
|||
} |
|||
|
|||
@ -1 +1,2 @@ |
|||
import 'jest-preset-angular/setup-jest'; |
|||
import { setupZoneTestEnv } from 'jest-preset-angular/setup-env/zone'; |
|||
setupZoneTestEnv(); |
|||
|
|||
@ -1,12 +1,2 @@ |
|||
import 'jest-preset-angular/setup-jest'; |
|||
|
|||
import { getTestBed } from '@angular/core/testing'; |
|||
import { |
|||
BrowserDynamicTestingModule, |
|||
platformBrowserDynamicTesting, |
|||
} from '@angular/platform-browser-dynamic/testing'; |
|||
|
|||
getTestBed().resetTestEnvironment(); |
|||
getTestBed().initTestEnvironment(BrowserDynamicTestingModule, platformBrowserDynamicTesting(), { |
|||
teardown: { destroyAfterEach: false }, |
|||
}); |
|||
import { setupZoneTestEnv } from 'jest-preset-angular/setup-env/zone'; |
|||
setupZoneTestEnv(); |
|||
|
|||
@ -1,12 +1,10 @@ |
|||
import 'jest-preset-angular/setup-jest'; |
|||
import { setupZoneTestEnv } from 'jest-preset-angular/setup-env/zone'; |
|||
setupZoneTestEnv(); |
|||
|
|||
import { getTestBed } from '@angular/core/testing'; |
|||
import { |
|||
BrowserDynamicTestingModule, |
|||
platformBrowserDynamicTesting, |
|||
} from '@angular/platform-browser-dynamic/testing'; |
|||
|
|||
getTestBed().resetTestEnvironment(); |
|||
getTestBed().initTestEnvironment(BrowserDynamicTestingModule, platformBrowserDynamicTesting(), { |
|||
teardown: { destroyAfterEach: false }, |
|||
}); |
|||
const originalError = console.error; |
|||
console.error = (...args: any[]) => { |
|||
if (args[0]?.includes?.('ExpressionChangedAfterItHasBeenCheckedError')) { |
|||
return; |
|||
} |
|||
originalError.apply(console, args); |
|||
}; |
|||
|
|||
Loading…
Reference in new issue