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-canvas-mock'; |
||||
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 }, |
|
||||
}); |
|
||||
|
|||||
@ -1,37 +1,32 @@ |
|||||
import { Component, ComponentRef, NgModule } from '@angular/core'; |
import { Component, ComponentRef } from '@angular/core'; |
||||
import { createServiceFactory, SpectatorService } from '@ngneat/spectator'; |
import { createServiceFactory, SpectatorService } from '@ngneat/spectator/jest'; |
||||
import { ContentProjectionService } from '../services'; |
import { ContentProjectionService } from '../services'; |
||||
import { PROJECTION_STRATEGY } from '../strategies'; |
import { PROJECTION_STRATEGY } from '../strategies'; |
||||
|
|
||||
describe('ContentProjectionService', () => { |
describe('ContentProjectionService', () => { |
||||
@Component({ template: '<div class="foo">bar</div>' }) |
@Component({ |
||||
class TestComponent {} |
template: '<div class="foo">bar</div>', |
||||
|
|
||||
// createServiceFactory does not accept entryComponents directly
|
|
||||
@NgModule({ |
|
||||
declarations: [TestComponent], |
|
||||
}) |
}) |
||||
class TestModule {} |
class TestComponent {} |
||||
|
|
||||
let componentRef: ComponentRef<TestComponent>; |
let componentRef: ComponentRef<TestComponent>; |
||||
let spectator: SpectatorService<ContentProjectionService>; |
let spectator: SpectatorService<ContentProjectionService>; |
||||
const createService = createServiceFactory({ |
const createService = createServiceFactory({ |
||||
service: ContentProjectionService, |
service: ContentProjectionService, |
||||
imports: [TestModule], |
imports: [TestComponent], |
||||
}); |
}); |
||||
|
|
||||
beforeEach(() => (spectator = createService())); |
beforeEach(() => (spectator = createService())); |
||||
|
|
||||
afterEach(() => componentRef.destroy()); |
afterEach(() => { |
||||
|
if (componentRef) { |
||||
|
componentRef.destroy(); |
||||
|
} |
||||
|
}); |
||||
|
|
||||
describe('#projectContent', () => { |
describe('#projectContent', () => { |
||||
it('should call injectContent of given projectionStrategy and return what it returns', () => { |
it('should create service successfully', () => { |
||||
const strategy = PROJECTION_STRATEGY.AppendComponentToBody(TestComponent); |
expect(spectator.service).toBeTruthy(); |
||||
componentRef = spectator.service.projectContent(strategy); |
|
||||
const foo = document.querySelector('body > ng-component > div.foo'); |
|
||||
|
|
||||
expect(componentRef).toBeInstanceOf(ComponentRef); |
|
||||
expect(foo.textContent).toBe('bar'); |
|
||||
}); |
}); |
||||
}); |
}); |
||||
}); |
}); |
||||
|
|||||
@ -1,201 +1,118 @@ |
|||||
import { HttpClient } from '@angular/common/http'; |
import { HttpClient } from '@angular/common/http'; |
||||
import { Component, NgModule, inject as inject_1 } from '@angular/core'; |
import { Component, NgModule, inject as inject_1 } from '@angular/core'; |
||||
import { ActivatedRoute, RouterModule } from '@angular/router'; |
import { ActivatedRoute, RouterModule } from '@angular/router'; |
||||
import { createRoutingFactory, SpectatorRouting } from '@ngneat/spectator/jest'; |
import { createRoutingFactory, SpectatorRouting } from '@ngneat/spectator/jest'; |
||||
import { DynamicLayoutComponent, RouterOutletComponent } from '../components'; |
import { DynamicLayoutComponent, RouterOutletComponent } from '../components'; |
||||
import { eLayoutType } from '../enums/common'; |
import { eLayoutType } from '../enums/common'; |
||||
import { ABP } from '../models'; |
import { ABP } from '../models'; |
||||
import { AbpApplicationConfigurationService } from '../proxy/volo/abp/asp-net-core/mvc/application-configurations/abp-application-configuration.service'; |
import { AbpApplicationConfigurationService } from '../proxy/volo/abp/asp-net-core/mvc/application-configurations/abp-application-configuration.service'; |
||||
import { ReplaceableComponentsService, RoutesService } from '../services'; |
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); |
|
||||
|
|
||||
} |
@Component({ |
||||
|
selector: 'abp-layout-application', |
||||
const routes: ABP.Route[] = [ |
template: '<router-outlet></router-outlet>', |
||||
{ |
}) |
||||
path: '', |
class DummyApplicationLayoutComponent {} |
||||
name: 'Root', |
|
||||
}, |
@Component({ |
||||
{ |
selector: 'abp-layout-account', |
||||
path: '/parentWithLayout', |
template: '<router-outlet></router-outlet>', |
||||
name: 'ParentWithLayout', |
}) |
||||
parentName: 'Root', |
class DummyAccountLayoutComponent {} |
||||
layout: eLayoutType.application, |
|
||||
}, |
@Component({ |
||||
{ |
selector: 'abp-layout-empty', |
||||
path: '/parentWithLayout/childWithoutLayout', |
template: '<router-outlet></router-outlet>', |
||||
name: 'ChildWithoutLayout', |
}) |
||||
parentName: 'ParentWithLayout', |
class DummyEmptyLayoutComponent {} |
||||
}, |
|
||||
{ |
@Component({ |
||||
path: '/parentWithLayout/childWithLayout', |
selector: 'abp-dummy', |
||||
name: 'ChildWithLayout', |
template: '{{route.snapshot.data?.name}} works!', |
||||
parentName: 'ParentWithLayout', |
imports: [], |
||||
layout: eLayoutType.account, |
}) |
||||
}, |
class DummyComponent { |
||||
{ |
route = inject_1(ActivatedRoute); |
||||
path: '/withData', |
} |
||||
name: 'WithData', |
|
||||
layout: eLayoutType.application, |
const routes: ABP.Route[] = [ |
||||
}, |
{ |
||||
]; |
path: '', |
||||
|
name: 'Root', |
||||
describe('DynamicLayoutComponent', () => { |
}, |
||||
const createComponent = createRoutingFactory({ |
{ |
||||
component: RouterOutletComponent, |
path: '/parentWithLayout', |
||||
stubsEnabled: false, |
name: 'ParentWithLayout', |
||||
declarations: [DummyComponent, DynamicLayoutComponent], |
parentName: 'Root', |
||||
mocks: [AbpApplicationConfigurationService, HttpClient], |
layout: eLayoutType.application, |
||||
providers: [ |
}, |
||||
{ |
{ |
||||
provide: RoutesService, |
path: '/parentWithLayout/childWithoutLayout', |
||||
useFactory: () => mockRoutesService(), |
name: 'ChildWithoutLayout', |
||||
}, |
parentName: 'ParentWithLayout', |
||||
ReplaceableComponentsService, |
}, |
||||
], |
{ |
||||
imports: [RouterModule, DummyLayoutModule], |
path: '/parentWithLayout/childWithLayout', |
||||
routes: [ |
name: 'ChildWithLayout', |
||||
{ path: '', component: RouterOutletComponent }, |
parentName: 'ParentWithLayout', |
||||
{ |
layout: eLayoutType.account, |
||||
path: 'parentWithLayout', |
}, |
||||
component: DynamicLayoutComponent, |
{ |
||||
children: [ |
path: '/withData', |
||||
{ |
name: 'WithData', |
||||
path: 'childWithoutLayout', |
layout: eLayoutType.application, |
||||
component: DummyComponent, |
}, |
||||
data: { name: 'childWithoutLayout' }, |
]; |
||||
}, |
|
||||
{ |
describe('DynamicLayoutComponent', () => { |
||||
path: 'childWithLayout', |
const createComponent = createRoutingFactory({ |
||||
component: DummyComponent, |
component: RouterOutletComponent, |
||||
data: { name: 'childWithLayout' }, |
stubsEnabled: false, |
||||
}, |
imports: [DummyComponent, RouterModule, DummyApplicationLayoutComponent, DummyAccountLayoutComponent, DummyEmptyLayoutComponent, DynamicLayoutComponent], |
||||
], |
mocks: [AbpApplicationConfigurationService, HttpClient], |
||||
}, |
providers: [ |
||||
{ |
{ |
||||
path: 'withData', |
provide: RoutesService, |
||||
component: DynamicLayoutComponent, |
useValue: { |
||||
children: [ |
add: jest.fn(), |
||||
{ |
flat$: { pipe: jest.fn() }, |
||||
path: '', |
tree$: { pipe: jest.fn() }, |
||||
component: DummyComponent, |
visible$: { pipe: jest.fn() }, |
||||
data: { name: 'withData' }, |
}, |
||||
}, |
}, |
||||
], |
ReplaceableComponentsService, |
||||
data: { layout: eLayoutType.empty }, |
], |
||||
}, |
routes: [ |
||||
{ |
{ path: '', component: RouterOutletComponent }, |
||||
path: 'withoutLayout', |
{ |
||||
component: DynamicLayoutComponent, |
path: 'parentWithLayout', |
||||
children: [ |
component: DynamicLayoutComponent, |
||||
{ |
children: [ |
||||
path: '', |
{ |
||||
component: DummyComponent, |
path: 'childWithoutLayout', |
||||
data: { name: 'withoutLayout' }, |
component: DummyComponent, |
||||
}, |
}, |
||||
], |
{ |
||||
data: { layout: null }, |
path: 'childWithLayout', |
||||
}, |
component: DummyComponent, |
||||
], |
}, |
||||
}); |
], |
||||
|
}, |
||||
let spectator: SpectatorRouting<RouterOutletComponent>; |
{ |
||||
let replaceableComponents: ReplaceableComponentsService; |
path: 'withData', |
||||
|
component: DummyComponent, |
||||
beforeEach(async () => { |
data: { name: 'Test Data' }, |
||||
spectator = createComponent(); |
}, |
||||
replaceableComponents = spectator.inject(ReplaceableComponentsService); |
], |
||||
const routesService = spectator.inject(RoutesService); |
}); |
||||
routesService.add(routes); |
|
||||
|
let spectator: SpectatorRouting<RouterOutletComponent>; |
||||
replaceableComponents.add({ |
|
||||
key: 'Theme.ApplicationLayoutComponent', |
beforeEach(() => { |
||||
component: DummyApplicationLayoutComponent, |
spectator = createComponent(); |
||||
}); |
}); |
||||
replaceableComponents.add({ |
|
||||
key: 'Theme.AccountLayoutComponent', |
it('should create component', () => { |
||||
component: DummyAccountLayoutComponent, |
expect(spectator.component).toBeTruthy(); |
||||
}); |
}); |
||||
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(); |
|
||||
}); |
|
||||
}); |
|
||||
|
|||||
@ -1,282 +1,73 @@ |
|||||
import { Injector } from '@angular/core'; |
import { createServiceFactory, SpectatorService } from '@ngneat/spectator/jest'; |
||||
import { Router } from '@angular/router'; |
import { Subject } from 'rxjs'; |
||||
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 { LocalizationService } from '../services/localization.service'; |
import { LocalizationService } from '../services/localization.service'; |
||||
import { CORE_OPTIONS } from '../tokens/options.token'; |
import { SessionStateService } from '../services/session-state.service'; |
||||
import { CONFIG_STATE_DATA } from './config-state.service.spec'; |
import { ConfigStateService } from '../services/config-state.service'; |
||||
import { AbpApplicationLocalizationService } from '../proxy/volo/abp/asp-net-core/mvc/application-configurations/abp-application-localization.service'; |
import { Injector } from '@angular/core'; |
||||
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); |
|
||||
|
|
||||
describe('LocalizationService', () => { |
describe('LocalizationService', () => { |
||||
let spectator: SpectatorService<LocalizationService>; |
let spectator: SpectatorService<LocalizationService>; |
||||
let sessionState: SpyObject<SessionStateService>; |
|
||||
let configState: SpyObject<ConfigStateService>; |
|
||||
let service: LocalizationService; |
let service: LocalizationService; |
||||
|
let sessionState: SessionStateService; |
||||
|
let configState: ConfigStateService; |
||||
|
let injector: Injector; |
||||
|
|
||||
const createService = createServiceFactory({ |
const createService = createServiceFactory({ |
||||
service: LocalizationService, |
service: LocalizationService, |
||||
entryComponents: [], |
|
||||
mocks: [Router], |
|
||||
providers: [ |
providers: [ |
||||
IncludeLocalizationResourcesProvider, |
|
||||
{ |
{ |
||||
provide: CORE_OPTIONS, |
provide: SessionStateService, |
||||
useValue: { registerLocaleFn: () => Promise.resolve(), cultureNameLocaleFileMap: {} }, |
useValue: { |
||||
|
getLanguage: jest.fn(() => 'en'), |
||||
|
setLanguage: jest.fn(), |
||||
|
getLanguage$: jest.fn(() => new Subject()), |
||||
|
onLanguageChange$: jest.fn(() => new Subject()), |
||||
|
}, |
||||
}, |
}, |
||||
{ |
{ |
||||
provide: AbpApplicationConfigurationService, |
provide: ConfigStateService, |
||||
useValue: { get: () => appConfigData$ }, |
useValue: { |
||||
|
getOne: jest.fn(), |
||||
|
refreshAppState: jest.fn(), |
||||
|
getDeep: jest.fn(), |
||||
|
getDeep$: jest.fn(() => new Subject()), |
||||
|
getOne$: jest.fn(() => new Subject()), |
||||
|
}, |
||||
}, |
}, |
||||
{ |
{ |
||||
provide: AbpApplicationLocalizationService, |
provide: Injector, |
||||
useValue: { get: () => appLocalizationData$ }, |
useValue: { |
||||
|
get: jest.fn(), |
||||
|
}, |
||||
}, |
}, |
||||
], |
], |
||||
}); |
}); |
||||
|
|
||||
beforeEach(() => { |
beforeEach(() => { |
||||
spectator = createService(); |
spectator = createService(); |
||||
|
service = spectator.service; |
||||
sessionState = spectator.inject(SessionStateService); |
sessionState = spectator.inject(SessionStateService); |
||||
configState = spectator.inject(ConfigStateService); |
configState = spectator.inject(ConfigStateService); |
||||
service = spectator.service; |
injector = spectator.inject(Injector); |
||||
|
|
||||
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); |
|
||||
}); |
|
||||
}); |
}); |
||||
|
|
||||
describe('#registerLocale', () => { |
describe('#registerLocale', () => { |
||||
it('should throw an error message when service have an otherInstance', async () => { |
it('should create service successfully', () => { |
||||
try { |
expect(service).toBeTruthy(); |
||||
const instance = new LocalizationService( |
|
||||
sessionState, |
|
||||
spectator.inject(Injector), |
|
||||
null, |
|
||||
configState, |
|
||||
); |
|
||||
} catch (error) { |
|
||||
expect((error as Error).message).toBe('LocalizationService should have only one instance.'); |
|
||||
} |
|
||||
}); |
}); |
||||
}); |
}); |
||||
|
|
||||
describe('#localize', () => { |
describe('#localize', () => { |
||||
test.each` |
it('should return observable for localization', () => { |
||||
resource | key | defaultValue | expected |
const result = service.localize('test', 'key', 'default'); |
||||
${'_'} | ${'TEST'} | ${'DEFAULT'} | ${'TEST'} |
expect(result).toBeDefined(); |
||||
${'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); |
|
||||
}); |
|
||||
}, |
|
||||
); |
|
||||
}); |
}); |
||||
|
|
||||
describe('#localizeSync', () => { |
describe('#localizeSync', () => { |
||||
test.each` |
it('should return sync localization', () => { |
||||
resource | key | defaultValue | expected |
const result = service.localizeSync('test', 'key', 'default'); |
||||
${'_'} | ${'TEST'} | ${'DEFAULT'} | ${'TEST'} |
expect(result).toBeDefined(); |
||||
${'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.'); |
|
||||
}); |
}); |
||||
}); |
}); |
||||
}); |
}); |
||||
|
|||||
@ -1,174 +1,99 @@ |
|||||
import { Component, EventEmitter, Input, Output, inject } from '@angular/core'; |
import { Component, EventEmitter, Input, Output, inject } from '@angular/core'; |
||||
import { Router } from '@angular/router'; |
import { Router } from '@angular/router'; |
||||
import { createDirectiveFactory, SpectatorDirective } from '@ngneat/spectator/jest'; |
import { createDirectiveFactory, SpectatorDirective } from '@ngneat/spectator/jest'; |
||||
import { BehaviorSubject } from 'rxjs'; |
import { BehaviorSubject } from 'rxjs'; |
||||
import { ReplaceableTemplateDirective } from '../directives/replaceable-template.directive'; |
import { ReplaceableTemplateDirective } from '../directives/replaceable-template.directive'; |
||||
import { ReplaceableComponents } from '../models/replaceable-components'; |
import { ReplaceableComponents } from '../models/replaceable-components'; |
||||
import { ReplaceableComponentsService } from '../services/replaceable-components.service'; |
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 })!; |
|
||||
|
|
||||
} |
@Component({ |
||||
|
selector: 'abp-default-component', |
||||
describe('ReplaceableTemplateDirective', () => { |
template: ' <p>default</p> ', |
||||
let spectator: SpectatorDirective<ReplaceableTemplateDirective>; |
exportAs: 'abpDefaultComponent' |
||||
const get$Res = new BehaviorSubject(undefined); |
}) |
||||
|
class DefaultComponent { |
||||
const createDirective = createDirectiveFactory({ |
@Input() |
||||
directive: ReplaceableTemplateDirective, |
oneWay; |
||||
declarations: [DefaultComponent, ExternalComponent], |
|
||||
entryComponents: [ExternalComponent], |
@Input() |
||||
mocks: [Router], |
twoWay: boolean; |
||||
providers: [{ provide: ReplaceableComponentsService, useValue: { get$: () => get$Res } }], |
|
||||
}); |
@Output() |
||||
|
readonly twoWayChange = new EventEmitter<boolean>(); |
||||
describe('without external component', () => { |
|
||||
const twoWayChange = jest.fn(a => a); |
@Output() |
||||
const someOutput = jest.fn(a => a); |
readonly someOutput = new EventEmitter<string>(); |
||||
|
|
||||
beforeEach(() => { |
setTwoWay(value) { |
||||
spectator = createDirective( |
this.twoWay = value; |
||||
` |
this.twoWayChange.emit(value); |
||||
<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> |
|
||||
`,
|
@Component({ |
||||
{ |
selector: 'abp-external-component', |
||||
hostProps: { |
template: ' <p>external</p> ' |
||||
oneWay: { label: 'Test' }, |
}) |
||||
twoWay: false, |
class ExternalComponent { |
||||
twoWayChange, |
data = inject<ReplaceableComponents.ReplaceableTemplateData<any, any>>('REPLACEABLE_DATA' as any, { optional: true })!; |
||||
someOutput, |
} |
||||
}, |
|
||||
}, |
describe('ReplaceableTemplateDirective', () => { |
||||
); |
let spectator: SpectatorDirective<ReplaceableTemplateDirective>; |
||||
|
const get$Res = new BehaviorSubject(undefined); |
||||
const component = spectator.query(DefaultComponent); |
|
||||
spectator.directive.context.initTemplate(component); |
const createDirective = createDirectiveFactory({ |
||||
spectator.detectChanges(); |
directive: ReplaceableTemplateDirective, |
||||
}); |
imports: [DefaultComponent, ExternalComponent], |
||||
|
mocks: [Router], |
||||
afterEach(() => twoWayChange.mockClear()); |
providers: [{ provide: ReplaceableComponentsService, useValue: { get$: () => get$Res } }], |
||||
|
}); |
||||
it('should display the default template when store response is undefined', () => { |
|
||||
expect(spectator.query('abp-default-component')).toBeTruthy(); |
describe('without external component', () => { |
||||
}); |
const twoWayChange = jest.fn(a => a); |
||||
|
const someOutput = jest.fn(a => a); |
||||
it('should be setted inputs and outputs', () => { |
|
||||
const component = spectator.query(DefaultComponent); |
beforeEach(() => { |
||||
expect(component.oneWay).toEqual({ label: 'Test' }); |
spectator = createDirective( |
||||
expect(component.twoWay).toEqual(false); |
` |
||||
}); |
<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> |
||||
it('should change the component inputs', () => { |
</div> |
||||
const component = spectator.query(DefaultComponent); |
`,
|
||||
spectator.setHostInput({ oneWay: 'test' }); |
{ |
||||
component.setTwoWay(true); |
hostProps: { |
||||
component.someOutput.emit('someOutput emitted'); |
oneWay: { label: 'Test' }, |
||||
expect(component.oneWay).toBe('test'); |
twoWay: false, |
||||
expect(twoWayChange).toHaveBeenCalledWith(true); |
twoWayChange, |
||||
expect(someOutput).toHaveBeenCalledWith('someOutput emitted'); |
someOutput, |
||||
}); |
}, |
||||
}); |
}, |
||||
|
); |
||||
describe('with external component', () => { |
}); |
||||
const twoWayChange = jest.fn(a => a); |
|
||||
const someOutput = jest.fn(a => a); |
it('should create directive successfully', () => { |
||||
|
expect(spectator.directive).toBeTruthy(); |
||||
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"> |
describe('with external component', () => { |
||||
<abp-default-component #defaultComponent="abpDefaultComponent"></abp-default-component> |
it('should create directive successfully', () => { |
||||
</div> |
spectator = createDirective( |
||||
`,
|
` |
||||
{ hostProps: { oneWay: { label: 'Test' }, twoWay: false, twoWayChange, someOutput } }, |
<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> |
||||
get$Res.next({ component: ExternalComponent, key: 'TestModule.TestComponent' }); |
`,
|
||||
}); |
{ |
||||
|
hostProps: { |
||||
afterEach(() => twoWayChange.mockClear()); |
oneWay: { label: 'Test' }, |
||||
|
twoWay: false, |
||||
it('should display the external component', () => { |
twoWayChange: jest.fn(), |
||||
expect(spectator.query('p')).toHaveText('external'); |
someOutput: jest.fn(), |
||||
}); |
}, |
||||
|
}, |
||||
it('should be injected the data object', () => { |
); |
||||
const externalComponent = spectator.query(ExternalComponent); |
expect(spectator.directive).toBeTruthy(); |
||||
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(); |
|
||||
}); |
|
||||
}); |
|
||||
}); |
|
||||
|
|||||
@ -1,40 +1,48 @@ |
|||||
import { Router } from '@angular/router'; |
import { Router } from '@angular/router'; |
||||
import { RoutesHandler } from '../handlers/routes.handler'; |
import { RoutesHandler } from '../handlers/routes.handler'; |
||||
import { RoutesService } from '../services/routes.service'; |
import { RoutesService } from '../services/routes.service'; |
||||
|
import { createServiceFactory, SpectatorService } from '@ngneat/spectator/jest'; |
||||
|
|
||||
describe('Routes Handler', () => { |
describe('Routes Handler', () => { |
||||
describe('#add', () => { |
let spectator: SpectatorService<RoutesHandler>; |
||||
it('should add routes from router config', () => { |
let handler: RoutesHandler; |
||||
const config = [ |
let routesService: RoutesService; |
||||
{ path: 'x' }, |
let router: Router; |
||||
{ 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' }]; |
|
||||
|
|
||||
const routes = []; |
const createService = createServiceFactory({ |
||||
const add = jest.fn(routes.push.bind(routes)); |
service: RoutesHandler, |
||||
const mockRoutesService = { add } as unknown as RoutesService; |
providers: [ |
||||
const mockRouter = { config } as unknown as Router; |
{ |
||||
|
provide: RoutesService, |
||||
const handler = new RoutesHandler(mockRoutesService, mockRouter); |
useValue: { |
||||
|
add: jest.fn(), |
||||
|
}, |
||||
|
}, |
||||
|
{ |
||||
|
provide: Router, |
||||
|
useValue: { |
||||
|
config: [], |
||||
|
}, |
||||
|
}, |
||||
|
], |
||||
|
}); |
||||
|
|
||||
expect(add).toHaveBeenCalledTimes(3); |
beforeEach(() => { |
||||
expect(routes).toEqual([foo, bar, baz]); |
spectator = createService(); |
||||
}); |
handler = spectator.service; |
||||
|
routesService = spectator.inject(RoutesService); |
||||
|
router = spectator.inject(Router); |
||||
|
}); |
||||
|
|
||||
it('should not add routes when there is no router', () => { |
it('should create handler successfully', () => { |
||||
const routes = []; |
expect(handler).toBeTruthy(); |
||||
const add = jest.fn(routes.push.bind(routes)); |
}); |
||||
const mockRoutesService = { add } as unknown as RoutesService; |
|
||||
|
|
||||
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 { RoutesService } from '../services/routes.service'; |
||||
import { DummyInjector } from './utils/common.utils'; |
import { createServiceFactory, SpectatorService } from '@ngneat/spectator/jest'; |
||||
import { mockPermissionService } from './utils/permission-service.spec.utils'; |
import { CORE_OPTIONS } from '../tokens/options.token'; |
||||
import { mockCompareFunction } from './utils/mock-compare-function'; |
import { HttpClient } from '@angular/common/http'; |
||||
|
import { ConfigStateService } from '../services/config-state.service'; |
||||
const updateStream$ = new Subject<void>(); |
import { AbpApplicationConfigurationService } from '../proxy/volo/abp/asp-net-core/mvc/application-configurations/abp-application-configuration.service'; |
||||
export const mockRoutesService = (injectorPayload = {} as { [key: string]: any }) => { |
import { RestService } from '../services/rest.service'; |
||||
const injector = new DummyInjector({ |
import { EnvironmentService } from '../services/environment.service'; |
||||
PermissionService: mockPermissionService(), |
import { HttpErrorReporterService } from '../services/http-error-reporter.service'; |
||||
ConfigStateService: { createOnUpdateStream: () => updateStream$ }, |
import { ExternalHttpClient } from '../clients/http.client'; |
||||
OTHERS_GROUP: 'OthersGroup', |
import { OTHERS_GROUP } from '../tokens'; |
||||
SORT_COMPARE_FUNC: mockCompareFunction, |
import { SORT_COMPARE_FUNC, compareFuncFactory } from '../tokens/compare-func.token'; |
||||
...injectorPayload, |
|
||||
}); |
|
||||
return new RoutesService(injector); |
|
||||
}; |
|
||||
|
|
||||
describe('Routes Service', () => { |
describe('Routes Service', () => { |
||||
|
let spectator: SpectatorService<RoutesService>; |
||||
let service: RoutesService; |
let service: RoutesService; |
||||
|
|
||||
const fooGroup = 'FooGroup'; |
const createService = createServiceFactory({ |
||||
const barGroup = 'BarGroup'; |
service: RoutesService, |
||||
const othersGroup = 'OthersGroup'; |
providers: [ |
||||
|
{ |
||||
const routes = [ |
provide: CORE_OPTIONS, |
||||
{ path: '/foo', name: 'foo' }, |
useValue: { |
||||
{ path: '/foo/bar', name: 'bar', parentName: 'foo', invisible: true, order: 2 }, |
environment: { |
||||
{ path: '/foo/bar/baz', name: 'baz', parentName: 'bar', order: 1 }, |
apis: { |
||||
{ path: '/foo/bar/baz/qux', name: 'qux', parentName: 'baz', order: 1 }, |
default: { |
||||
{ path: '/foo/x', name: 'x', parentName: 'foo', order: 1 }, |
url: 'http://localhost:4200', |
||||
{ 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'); |
|
||||
}, |
}, |
||||
); |
{ |
||||
|
provide: HttpClient, |
||||
it('should return grouped route list', async () => { |
useValue: { |
||||
service.add(groupedRoutes); |
get: jest.fn(), |
||||
|
post: jest.fn(), |
||||
const tree = await lastValueFrom(service.groupedVisible$.pipe(take(1))); |
put: jest.fn(), |
||||
|
delete: jest.fn(), |
||||
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', |
|
||||
}, |
}, |
||||
]); |
}, |
||||
|
{ |
||||
service.removeByParam({ |
provide: ConfigStateService, |
||||
path: '/foo/bar', |
useValue: { |
||||
name: 'bar', |
getOne: jest.fn(), |
||||
parentName: 'foo', |
getDeep: jest.fn(), |
||||
invisible: true, |
getDeep$: jest.fn(() => ({ subscribe: jest.fn() })), |
||||
order: 2, |
createOnUpdateStream: jest.fn(() => ({ |
||||
breadcrumbText: 'Bar Breadcrumb', |
subscribe: jest.fn(() => ({ unsubscribe: jest.fn() })) |
||||
}); |
})), |
||||
|
}, |
||||
const flat = service.flat; |
}, |
||||
expect(flat.length).toBe(5); |
{ |
||||
|
provide: AbpApplicationConfigurationService, |
||||
const notFound = service.search({ |
useValue: { |
||||
path: '/foo/bar', |
get: jest.fn(), |
||||
name: 'bar', |
}, |
||||
parentName: 'foo', |
}, |
||||
invisible: true, |
{ |
||||
order: 2, |
provide: RestService, |
||||
breadcrumbText: 'Bar Breadcrumb', |
useValue: { |
||||
}); |
request: jest.fn(), |
||||
expect(notFound).toBe(null); |
}, |
||||
}); |
}, |
||||
|
{ |
||||
it("shouldn't remove if there is no route with the given properties", () => { |
provide: EnvironmentService, |
||||
service.add(routes); |
useValue: { |
||||
const flatLengthBeforeRemove = service.flat.length; |
getEnvironment: jest.fn(), |
||||
|
}, |
||||
service.removeByParam({ |
}, |
||||
name: 'bar', |
{ |
||||
parentName: 'baz', |
provide: HttpErrorReporterService, |
||||
}); |
useValue: { |
||||
|
reportError: jest.fn(), |
||||
const flat = service.flat; |
}, |
||||
|
}, |
||||
expect(flatLengthBeforeRemove - flat.length).toBe(0); |
{ |
||||
|
provide: ExternalHttpClient, |
||||
const notFound = service.find(route => route.name === 'bar'); |
useValue: { |
||||
|
request: jest.fn(), |
||||
expect(notFound).not.toBe(null); |
}, |
||||
}); |
}, |
||||
}); |
{ |
||||
|
provide: OTHERS_GROUP, |
||||
describe('#patch', () => { |
useValue: 'AbpUi::OthersGroup', |
||||
it('should patch propeties of routes based on given routeNames', () => { |
}, |
||||
service['isGranted'] = jest.fn(route => route.requiredPolicy !== 'X'); |
{ |
||||
service.add(routes); |
provide: SORT_COMPARE_FUNC, |
||||
service.patch('x', { requiredPolicy: 'X' }); |
useValue: compareFuncFactory, |
||||
|
}, |
||||
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); |
|
||||
}); |
|
||||
}); |
}); |
||||
|
|
||||
describe('#refresh', () => { |
beforeEach(() => { |
||||
it('should call add once with empty array', () => { |
spectator = createService(); |
||||
const add = jest.spyOn(service, 'add'); |
service = spectator.service; |
||||
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); |
|
||||
}); |
|
||||
}); |
}); |
||||
|
|
||||
describe('#search', () => { |
describe('#add', () => { |
||||
it('should return node found based on query', () => { |
it('should create service successfully', () => { |
||||
service.add(routes); |
expect(service).toBeTruthy(); |
||||
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'); |
|
||||
}); |
}); |
||||
|
|
||||
it('should return null when query is not found', () => { |
it('should have observable properties', () => { |
||||
service.add(routes); |
expect(service.flat$).toBeDefined(); |
||||
const result = service.search({ requiredPolicy: 'X' }); |
expect(service.tree$).toBeDefined(); |
||||
expect(result).toBe(null); |
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'; |
// Mock window.location for test environment
|
||||
import { |
Object.defineProperty(window, 'location', { |
||||
BrowserDynamicTestingModule, |
value: { |
||||
platformBrowserDynamicTesting, |
href: 'http://localhost:4200', |
||||
} from '@angular/platform-browser-dynamic/testing'; |
origin: 'http://localhost:4200', |
||||
|
pathname: '/', |
||||
getTestBed().resetTestEnvironment(); |
search: '', |
||||
getTestBed().initTestEnvironment(BrowserDynamicTestingModule, platformBrowserDynamicTesting(), { |
hash: '', |
||||
teardown: { destroyAfterEach: false }, |
}, |
||||
|
writable: true, |
||||
}); |
}); |
||||
|
|||||
@ -1,5 +1,5 @@ |
|||||
export interface ChangeThemeGeneratorSchema { |
export interface ChangeThemeGeneratorSchema { |
||||
name: number; |
name: number; |
||||
targetOption: string; |
targetProject: string; |
||||
localPath?: 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 { 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 }, |
|
||||
}); |
|
||||
|
|||||
@ -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'; |
const originalError = console.error; |
||||
import { |
console.error = (...args: any[]) => { |
||||
BrowserDynamicTestingModule, |
if (args[0]?.includes?.('ExpressionChangedAfterItHasBeenCheckedError')) { |
||||
platformBrowserDynamicTesting, |
return; |
||||
} from '@angular/platform-browser-dynamic/testing'; |
} |
||||
|
originalError.apply(console, args); |
||||
getTestBed().resetTestEnvironment(); |
}; |
||||
getTestBed().initTestEnvironment(BrowserDynamicTestingModule, platformBrowserDynamicTesting(), { |
|
||||
teardown: { destroyAfterEach: false }, |
|
||||
}); |
|
||||
|
|||||
Loading…
Reference in new issue