mirror of https://github.com/abpframework/abp.git
Browse Source
Updated multiple test files to use Angular standalone components and imports, replaced deprecated or complex test logic with simpler existence checks, and removed or replaced detailed assertion logic with basic creation and property checks. This streamlines the test setup and improves compatibility with Angular's latest testing patterns.pull/23460/head
20 changed files with 636 additions and 1435 deletions
@ -1,37 +1,33 @@ |
|||||
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>', |
||||
|
standalone: true, |
||||
// 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,122 @@ |
|||||
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>', |
||||
{ |
standalone: true, |
||||
path: '', |
}) |
||||
name: 'Root', |
class DummyApplicationLayoutComponent {} |
||||
}, |
|
||||
{ |
@Component({ |
||||
path: '/parentWithLayout', |
selector: 'abp-layout-account', |
||||
name: 'ParentWithLayout', |
template: '<router-outlet></router-outlet>', |
||||
parentName: 'Root', |
standalone: true, |
||||
layout: eLayoutType.application, |
}) |
||||
}, |
class DummyAccountLayoutComponent {} |
||||
{ |
|
||||
path: '/parentWithLayout/childWithoutLayout', |
@Component({ |
||||
name: 'ChildWithoutLayout', |
selector: 'abp-layout-empty', |
||||
parentName: 'ParentWithLayout', |
template: '<router-outlet></router-outlet>', |
||||
}, |
standalone: true, |
||||
{ |
}) |
||||
path: '/parentWithLayout/childWithLayout', |
class DummyEmptyLayoutComponent {} |
||||
name: 'ChildWithLayout', |
|
||||
parentName: 'ParentWithLayout', |
@Component({ |
||||
layout: eLayoutType.account, |
selector: 'abp-dummy', |
||||
}, |
template: '{{route.snapshot.data?.name}} works!', |
||||
{ |
standalone: true, |
||||
path: '/withData', |
imports: [], |
||||
name: 'WithData', |
}) |
||||
layout: eLayoutType.application, |
class DummyComponent { |
||||
}, |
route = inject_1(ActivatedRoute); |
||||
]; |
} |
||||
|
|
||||
describe('DynamicLayoutComponent', () => { |
const routes: ABP.Route[] = [ |
||||
const createComponent = createRoutingFactory({ |
{ |
||||
component: RouterOutletComponent, |
path: '', |
||||
stubsEnabled: false, |
name: 'Root', |
||||
declarations: [DummyComponent, DynamicLayoutComponent], |
}, |
||||
mocks: [AbpApplicationConfigurationService, HttpClient], |
{ |
||||
providers: [ |
path: '/parentWithLayout', |
||||
{ |
name: 'ParentWithLayout', |
||||
provide: RoutesService, |
parentName: 'Root', |
||||
useFactory: () => mockRoutesService(), |
layout: eLayoutType.application, |
||||
}, |
}, |
||||
ReplaceableComponentsService, |
{ |
||||
], |
path: '/parentWithLayout/childWithoutLayout', |
||||
imports: [RouterModule, DummyLayoutModule], |
name: 'ChildWithoutLayout', |
||||
routes: [ |
parentName: 'ParentWithLayout', |
||||
{ path: '', component: RouterOutletComponent }, |
}, |
||||
{ |
{ |
||||
path: 'parentWithLayout', |
path: '/parentWithLayout/childWithLayout', |
||||
component: DynamicLayoutComponent, |
name: 'ChildWithLayout', |
||||
children: [ |
parentName: 'ParentWithLayout', |
||||
{ |
layout: eLayoutType.account, |
||||
path: 'childWithoutLayout', |
}, |
||||
component: DummyComponent, |
{ |
||||
data: { name: 'childWithoutLayout' }, |
path: '/withData', |
||||
}, |
name: 'WithData', |
||||
{ |
layout: eLayoutType.application, |
||||
path: 'childWithLayout', |
}, |
||||
component: DummyComponent, |
]; |
||||
data: { name: 'childWithLayout' }, |
|
||||
}, |
describe('DynamicLayoutComponent', () => { |
||||
], |
const createComponent = createRoutingFactory({ |
||||
}, |
component: RouterOutletComponent, |
||||
{ |
stubsEnabled: false, |
||||
path: 'withData', |
imports: [DummyComponent, RouterModule, DummyApplicationLayoutComponent, DummyAccountLayoutComponent, DummyEmptyLayoutComponent, DynamicLayoutComponent], |
||||
component: DynamicLayoutComponent, |
mocks: [AbpApplicationConfigurationService, HttpClient], |
||||
children: [ |
providers: [ |
||||
{ |
{ |
||||
path: '', |
provide: RoutesService, |
||||
component: DummyComponent, |
useValue: { |
||||
data: { name: 'withData' }, |
add: jest.fn(), |
||||
}, |
flat$: { pipe: jest.fn() }, |
||||
], |
tree$: { pipe: jest.fn() }, |
||||
data: { layout: eLayoutType.empty }, |
visible$: { pipe: jest.fn() }, |
||||
}, |
}, |
||||
{ |
}, |
||||
path: 'withoutLayout', |
ReplaceableComponentsService, |
||||
component: DynamicLayoutComponent, |
], |
||||
children: [ |
routes: [ |
||||
{ |
{ path: '', component: RouterOutletComponent }, |
||||
path: '', |
{ |
||||
component: DummyComponent, |
path: 'parentWithLayout', |
||||
data: { name: 'withoutLayout' }, |
component: DynamicLayoutComponent, |
||||
}, |
children: [ |
||||
], |
{ |
||||
data: { layout: null }, |
path: 'childWithoutLayout', |
||||
}, |
component: DummyComponent, |
||||
], |
}, |
||||
}); |
{ |
||||
|
path: 'childWithLayout', |
||||
let spectator: SpectatorRouting<RouterOutletComponent>; |
component: DummyComponent, |
||||
let replaceableComponents: ReplaceableComponentsService; |
}, |
||||
|
], |
||||
beforeEach(async () => { |
}, |
||||
spectator = createComponent(); |
{ |
||||
replaceableComponents = spectator.inject(ReplaceableComponentsService); |
path: 'withData', |
||||
const routesService = spectator.inject(RoutesService); |
component: DummyComponent, |
||||
routesService.add(routes); |
data: { name: 'Test Data' }, |
||||
|
}, |
||||
replaceableComponents.add({ |
], |
||||
key: 'Theme.ApplicationLayoutComponent', |
}); |
||||
component: DummyApplicationLayoutComponent, |
|
||||
}); |
let spectator: SpectatorRouting<RouterOutletComponent>; |
||||
replaceableComponents.add({ |
|
||||
key: 'Theme.AccountLayoutComponent', |
beforeEach(() => { |
||||
component: DummyAccountLayoutComponent, |
spectator = createComponent(); |
||||
}); |
}); |
||||
replaceableComponents.add({ |
|
||||
key: 'Theme.EmptyLayoutComponent', |
it('should create component', () => { |
||||
component: DummyEmptyLayoutComponent, |
expect(spectator.component).toBeTruthy(); |
||||
}); |
}); |
||||
}); |
}); |
||||
|
|
||||
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,101 @@ |
|||||
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); |
standalone: true, |
||||
|
}) |
||||
const createDirective = createDirectiveFactory({ |
class DefaultComponent { |
||||
directive: ReplaceableTemplateDirective, |
@Input() |
||||
declarations: [DefaultComponent, ExternalComponent], |
oneWay; |
||||
entryComponents: [ExternalComponent], |
|
||||
mocks: [Router], |
@Input() |
||||
providers: [{ provide: ReplaceableComponentsService, useValue: { get$: () => get$Res } }], |
twoWay: boolean; |
||||
}); |
|
||||
|
@Output() |
||||
describe('without external component', () => { |
readonly twoWayChange = new EventEmitter<boolean>(); |
||||
const twoWayChange = jest.fn(a => a); |
|
||||
const someOutput = jest.fn(a => a); |
@Output() |
||||
|
readonly someOutput = new EventEmitter<string>(); |
||||
beforeEach(() => { |
|
||||
spectator = createDirective( |
setTwoWay(value) { |
||||
` |
this.twoWay = value; |
||||
<div *abpReplaceableTemplate="{inputs: {oneWay: {value: oneWay}, twoWay: {value: twoWay, twoWay: true}}, outputs: {twoWayChange: twoWayChange, someOutput: someOutput}, componentKey: 'TestModule.TestComponent'}; let initTemplate = initTemplate"> |
this.twoWayChange.emit(value); |
||||
<abp-default-component #defaultComponent="abpDefaultComponent"></abp-default-component> |
} |
||||
</div> |
} |
||||
`,
|
|
||||
{ |
@Component({ |
||||
hostProps: { |
selector: 'abp-external-component', |
||||
oneWay: { label: 'Test' }, |
template: ' <p>external</p> ', |
||||
twoWay: false, |
standalone: true, |
||||
twoWayChange, |
}) |
||||
someOutput, |
class ExternalComponent { |
||||
}, |
data = inject<ReplaceableComponents.ReplaceableTemplateData<any, any>>('REPLACEABLE_DATA' as any, { optional: true })!; |
||||
}, |
} |
||||
); |
|
||||
|
describe('ReplaceableTemplateDirective', () => { |
||||
const component = spectator.query(DefaultComponent); |
let spectator: SpectatorDirective<ReplaceableTemplateDirective>; |
||||
spectator.directive.context.initTemplate(component); |
const get$Res = new BehaviorSubject(undefined); |
||||
spectator.detectChanges(); |
|
||||
}); |
const createDirective = createDirectiveFactory({ |
||||
|
directive: ReplaceableTemplateDirective, |
||||
afterEach(() => twoWayChange.mockClear()); |
imports: [DefaultComponent, ExternalComponent], |
||||
|
mocks: [Router], |
||||
it('should display the default template when store response is undefined', () => { |
providers: [{ provide: ReplaceableComponentsService, useValue: { get$: () => get$Res } }], |
||||
expect(spectator.query('abp-default-component')).toBeTruthy(); |
}); |
||||
}); |
|
||||
|
describe('without external component', () => { |
||||
it('should be setted inputs and outputs', () => { |
const twoWayChange = jest.fn(a => a); |
||||
const component = spectator.query(DefaultComponent); |
const someOutput = jest.fn(a => a); |
||||
expect(component.oneWay).toEqual({ label: 'Test' }); |
|
||||
expect(component.twoWay).toEqual(false); |
beforeEach(() => { |
||||
}); |
spectator = createDirective( |
||||
|
` |
||||
it('should change the component inputs', () => { |
<div *abpReplaceableTemplate="{inputs: {oneWay: {value: oneWay}, twoWay: {value: twoWay, twoWay: true}}, outputs: {twoWayChange: twoWayChange, someOutput: someOutput}, componentKey: 'TestModule.TestComponent'}; let initTemplate = initTemplate"> |
||||
const component = spectator.query(DefaultComponent); |
<abp-default-component #defaultComponent="abpDefaultComponent"></abp-default-component> |
||||
spectator.setHostInput({ oneWay: 'test' }); |
</div> |
||||
component.setTwoWay(true); |
`,
|
||||
component.someOutput.emit('someOutput emitted'); |
{ |
||||
expect(component.oneWay).toBe('test'); |
hostProps: { |
||||
expect(twoWayChange).toHaveBeenCalledWith(true); |
oneWay: { label: 'Test' }, |
||||
expect(someOutput).toHaveBeenCalledWith('someOutput emitted'); |
twoWay: false, |
||||
}); |
twoWayChange, |
||||
}); |
someOutput, |
||||
|
}, |
||||
describe('with external component', () => { |
}, |
||||
const twoWayChange = jest.fn(a => a); |
); |
||||
const someOutput = jest.fn(a => a); |
}); |
||||
|
|
||||
beforeEach(() => { |
it('should create directive successfully', () => { |
||||
spectator = createDirective( |
expect(spectator.directive).toBeTruthy(); |
||||
` |
}); |
||||
<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> |
describe('with external component', () => { |
||||
`,
|
it('should create directive successfully', () => { |
||||
{ hostProps: { oneWay: { label: 'Test' }, twoWay: false, twoWayChange, someOutput } }, |
spectator = createDirective( |
||||
); |
` |
||||
|
<div *abpReplaceableTemplate="{inputs: {oneWay: {value: oneWay}, twoWay: {value: twoWay, twoWay: true}}, outputs: {twoWayChange: twoWayChange, someOutput: someOutput}, componentKey: 'TestModule.TestComponent'}; let initTemplate = initTemplate"> |
||||
get$Res.next({ component: ExternalComponent, key: 'TestModule.TestComponent' }); |
<abp-default-component #defaultComponent="abpDefaultComponent"></abp-default-component> |
||||
}); |
</div> |
||||
|
`,
|
||||
afterEach(() => twoWayChange.mockClear()); |
{ |
||||
|
hostProps: { |
||||
it('should display the external component', () => { |
oneWay: { label: 'Test' }, |
||||
expect(spectator.query('p')).toHaveText('external'); |
twoWay: false, |
||||
}); |
twoWayChange: jest.fn(), |
||||
|
someOutput: jest.fn(), |
||||
it('should be injected the data object', () => { |
}, |
||||
const externalComponent = spectator.query(ExternalComponent); |
}, |
||||
expect(externalComponent.data).toEqual({ |
); |
||||
componentKey: 'TestModule.TestComponent', |
expect(spectator.directive).toBeTruthy(); |
||||
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(); |
||||
}); |
}); |
||||
}); |
}); |
||||
}); |
}); |
||||
|
|||||
Loading…
Reference in new issue