Browse Source

fix theme-shared testing errors

pull/9837/head
mehmet-erim 5 years ago
parent
commit
47eddc660f
  1. 3
      npm/ng-packs/nx/ng-packs/package.json
  2. 37
      npm/ng-packs/nx/ng-packs/packages/theme-shared/extensions/src/tests/enum.util.spec.ts
  3. 31
      npm/ng-packs/nx/ng-packs/packages/theme-shared/extensions/src/tests/state.util.spec.ts
  4. 18
      npm/ng-packs/nx/ng-packs/packages/theme-shared/src/lib/components/http-error-wrapper/http-error-wrapper.component.ts
  5. 7
      npm/ng-packs/nx/ng-packs/packages/theme-shared/src/lib/components/toast/toast.component.ts
  6. 75
      npm/ng-packs/nx/ng-packs/packages/theme-shared/src/lib/handlers/error.handler.ts
  7. 21
      npm/ng-packs/nx/ng-packs/packages/theme-shared/src/lib/services/toaster.service.ts
  8. 65
      npm/ng-packs/nx/ng-packs/packages/theme-shared/src/lib/tests/chart.component.spec.ts
  9. 74
      npm/ng-packs/nx/ng-packs/packages/theme-shared/src/lib/tests/confirmation.service.spec.ts
  10. 21
      npm/ng-packs/nx/ng-packs/packages/theme-shared/src/lib/tests/error.component.spec.ts
  11. 35
      npm/ng-packs/nx/ng-packs/packages/theme-shared/src/lib/tests/error.handler.spec.ts
  12. 28
      npm/ng-packs/nx/ng-packs/packages/theme-shared/src/lib/tests/modal.component.spec.ts
  13. 32
      npm/ng-packs/nx/ng-packs/packages/theme-shared/src/lib/tests/table-sort.directive.spec.ts
  14. 75
      npm/ng-packs/nx/ng-packs/packages/theme-shared/src/lib/tests/table.component.spec.ts
  15. 15
      npm/ng-packs/nx/ng-packs/packages/theme-shared/src/lib/tests/validation-utils.spec.ts
  16. 1
      npm/ng-packs/nx/ng-packs/packages/theme-shared/src/test-setup.ts
  17. 22
      npm/ng-packs/nx/ng-packs/yarn.lock

3
npm/ng-packs/nx/ng-packs/package.json

@ -31,9 +31,9 @@
},
"private": true,
"devDependencies": {
"@abp/ng.core": "~4.4.0",
"@abp/ng.account": "~4.4.0",
"@abp/ng.account.core": "~4.4.0",
"@abp/ng.core": "~4.4.0",
"@abp/ng.feature-management": "~4.4.0",
"@abp/ng.identity": "~4.4.0",
"@abp/ng.permission-management": "~4.4.0",
@ -90,6 +90,7 @@
"eslint-plugin-cypress": "^2.10.3",
"got": "^11.5.2",
"jest": "27.0.3",
"jest-canvas-mock": "^2.3.1",
"jest-preset-angular": "9.0.4",
"jsonc-parser": "^2.3.0",
"just-clone": "^3.1.0",

37
npm/ng-packs/nx/ng-packs/packages/theme-shared/extensions/src/tests/enum.util.spec.ts

@ -2,7 +2,11 @@ import { ConfigStateService, LocalizationService } from '@abp/ng.core';
import { BehaviorSubject } from 'rxjs';
import { take } from 'rxjs/operators';
import { PropData } from '../lib/models/props';
import { createEnum, createEnumOptions, createEnumValueResolver } from '../lib/utils/enum.util';
import {
createEnum,
createEnumOptions,
createEnumValueResolver,
} from '../lib/utils/enum.util';
const mockSessionState = {
languageChange$: new BehaviorSubject('tr'),
@ -49,9 +53,12 @@ describe('Enum Utils', () => {
${1} | ${'foo'}
${2} | ${'bar'}
${3} | ${'baz'}
`('should create an enum that returns $expected when $key is accessed', ({ key, expected }) => {
expect(enumFromFields[key]).toBe(expected);
});
`(
'should create an enum that returns $expected when $key is accessed',
({ key, expected }) => {
expect(enumFromFields[key]).toBe(expected);
}
);
});
describe('#createEnumValueResolver', () => {
@ -71,15 +78,19 @@ describe('Enum Utils', () => {
localizationResource: null,
transformed: createEnum(fields),
},
'EnumProp',
'EnumProp'
);
const propData = new MockPropData({ extraProperties: { EnumProp: value } });
const propData = new MockPropData({
extraProperties: { EnumProp: value },
});
propData.getInjected = () => service as any;
const resolved = await valueResolver(propData).pipe(take(1)).toPromise();
const resolved = await valueResolver(propData)
.pipe(take(1))
.toPromise();
expect(resolved).toBe(expected);
},
}
);
});
@ -107,8 +118,14 @@ describe('Enum Utils', () => {
});
function createMockLocalizationService() {
const configState = new ConfigStateService();
const configState = new ConfigStateService(null);
configState.setState({ localization: mockL10n } as any);
return new LocalizationService(mockSessionState, null, null, configState, null);
return new LocalizationService(
mockSessionState,
null,
null,
configState,
null
);
}

31
npm/ng-packs/nx/ng-packs/packages/theme-shared/extensions/src/tests/state.util.spec.ts

@ -10,7 +10,7 @@ import {
mapEntitiesToContributors,
} from '../lib/utils/state.util';
const configState = new ConfigStateService();
const configState = new ConfigStateService(null);
configState.setState(createMockState() as any);
describe('State Utils', () => {
@ -18,21 +18,27 @@ describe('State Utils', () => {
it('should return observable entities of an existing module', async () => {
const entities = await getObjectExtensionEntitiesFromStore(
configState,
'Identity',
'Identity'
).toPromise();
expect('Role' in entities).toBe(true);
});
it('should return observable empty object if module does not exist', async () => {
const entities = await getObjectExtensionEntitiesFromStore(configState, 'Saas').toPromise();
const entities = await getObjectExtensionEntitiesFromStore(
configState,
'Saas'
).toPromise();
expect(entities).toEqual({});
});
it('should not emit when object extensions do not exist', done => {
const emptyConfigState = new ConfigStateService();
it('should not emit when object extensions do not exist', (done) => {
const emptyConfigState = new ConfigStateService(null);
const emit = jest.fn();
getObjectExtensionEntitiesFromStore(emptyConfigState, 'Identity').subscribe(emit);
getObjectExtensionEntitiesFromStore(
emptyConfigState,
'Identity'
).subscribe(emit);
setTimeout(() => {
expect(emit).not.toHaveBeenCalled();
@ -48,7 +54,7 @@ describe('State Utils', () => {
.toPromise();
const propList = new EntityPropList();
contributors.prop.Role.forEach(callback => callback(propList));
contributors.prop.Role.forEach((callback) => callback(propList));
expect(propList.length).toBe(4);
expect(propList.head.value.name).toBe('Title');
@ -57,7 +63,9 @@ describe('State Utils', () => {
expect(propList.head.next.next.next.value.name).toBe('Foo_Text');
const createFormList = new FormPropList();
contributors.createForm.Role.forEach(callback => callback(createFormList));
contributors.createForm.Role.forEach((callback) =>
callback(createFormList)
);
expect(createFormList.length).toBe(4);
expect(createFormList.head.value.name).toBe('Title');
@ -66,7 +74,7 @@ describe('State Utils', () => {
expect(createFormList.head.next.next.next.value.name).toBe('Foo_Text');
const editFormList = new FormPropList();
contributors.editForm.Role.forEach(callback => callback(editFormList));
contributors.editForm.Role.forEach((callback) => callback(editFormList));
expect(editFormList.length).toBe(4);
expect(editFormList.head.value.name).toBe('Title');
@ -118,7 +126,10 @@ function createMockState() {
};
}
function createMockEntities(): Record<string, ObjectExtensions.EntityExtensionDto> {
function createMockEntities(): Record<
string,
ObjectExtensions.EntityExtensionDto
> {
return {
Role: {
properties: {

18
npm/ng-packs/nx/ng-packs/packages/theme-shared/src/lib/components/http-error-wrapper/http-error-wrapper.component.ts

@ -14,7 +14,6 @@ import {
} from '@angular/core';
import { fromEvent, Subject } from 'rxjs';
import { debounceTime, filter } from 'rxjs/operators';
import snq from 'snq';
@Component({
selector: 'abp-http-error-wrapper',
@ -22,7 +21,9 @@ import snq from 'snq';
styleUrls: ['http-error-wrapper.component.scss'],
providers: [SubscriptionService],
})
export class HttpErrorWrapperComponent implements AfterViewInit, OnDestroy, OnInit {
export class HttpErrorWrapperComponent
implements AfterViewInit, OnDestroy, OnInit
{
appRef: ApplicationRef;
cfRes: ComponentFactoryResolver;
@ -56,8 +57,9 @@ export class HttpErrorWrapperComponent implements AfterViewInit, OnDestroy, OnIn
ngOnInit() {
this.backgroundColor =
snq(() => window.getComputedStyle(document.body).getPropertyValue('background-color')) ||
'#fff';
window
.getComputedStyle(document.body)
?.getPropertyValue('background-color') || '#fff';
}
ngAfterViewInit() {
@ -69,19 +71,21 @@ export class HttpErrorWrapperComponent implements AfterViewInit, OnDestroy, OnIn
customComponentRef.instance.destroy$ = this.destroy$;
this.appRef.attachView(customComponentRef.hostView);
this.containerRef.nativeElement.appendChild(
(customComponentRef.hostView as EmbeddedViewRef<any>).rootNodes[0],
(customComponentRef.hostView as EmbeddedViewRef<any>).rootNodes[0]
);
customComponentRef.changeDetectorRef.detectChanges();
}
const keyup$ = fromEvent(document, 'keyup').pipe(
debounceTime(150),
filter((key: KeyboardEvent) => key && key.key === 'Escape'),
filter((key: KeyboardEvent) => key && key.key === 'Escape')
);
this.subscription.addOne(keyup$, () => this.destroy());
}
ngOnDestroy() {}
ngOnDestroy() {
this.destroy();
}
destroy() {
this.destroy$.next();

7
npm/ng-packs/nx/ng-packs/packages/theme-shared/src/lib/components/toast/toast.component.ts

@ -1,7 +1,6 @@
import { Component, Input, OnInit } from '@angular/core';
import { Toaster } from '../../models/toaster';
import { ToasterService } from '../../services/toaster.service';
import snq from 'snq';
@Component({
selector: 'abp-toast',
@ -35,8 +34,10 @@ export class ToastComponent implements OnInit {
constructor(private toasterService: ToasterService) {}
ngOnInit() {
if (snq(() => this.toast.options.sticky)) return;
const timeout = snq(() => this.toast.options.life) || 5000;
const { sticky, life } = this.toast.options || {};
if (sticky) return;
const timeout = life || 5000;
setTimeout(() => {
this.close();
}, timeout);

75
npm/ng-packs/nx/ng-packs/packages/theme-shared/src/lib/handlers/error.handler.ts

@ -1,4 +1,9 @@
import { AuthService, LocalizationParam, RestOccurError, RouterEvents } from '@abp/ng.core';
import {
AuthService,
LocalizationParam,
RestOccurError,
RouterEvents,
} from '@abp/ng.core';
import { HttpErrorResponse } from '@angular/common/http';
import {
ApplicationRef,
@ -14,7 +19,6 @@ import { NavigationError, ResolveEnd } from '@angular/router';
import { Actions, ofActionSuccessful } from '@ngxs/store';
import { Observable, of, Subject, throwError } from 'rxjs';
import { catchError, filter, map, switchMap } from 'rxjs/operators';
import snq from 'snq';
import { HttpErrorWrapperComponent } from '../components/http-error-wrapper/http-error-wrapper.component';
import { ErrorScreenErrorCodes, HttpErrorConfig } from '../models/common';
import { Confirmation } from '../models/confirmation';
@ -28,7 +32,8 @@ export const DEFAULT_ERROR_MESSAGES = {
},
defaultError401: {
title: 'You are not authenticated!',
details: 'You should be authenticated (sign in) in order to perform this operation.',
details:
'You should be authenticated (sign in) in order to perform this operation.',
},
defaultError403: {
title: 'You are not authorized!',
@ -71,8 +76,9 @@ export const DEFAULT_ERROR_LOCALIZATIONS = {
export class ErrorHandler {
componentRef: ComponentRef<HttpErrorWrapperComponent>;
protected httpErrorHandler = this.injector.get(HTTP_ERROR_HANDLER, (_, err: HttpErrorResponse) =>
throwError(err),
protected httpErrorHandler = this.injector.get(
HTTP_ERROR_HANDLER,
(_, err: HttpErrorResponse) => throwError(err)
);
constructor(
@ -82,7 +88,7 @@ export class ErrorHandler {
protected cfRes: ComponentFactoryResolver,
protected rendererFactory: RendererFactory2,
protected injector: Injector,
@Inject('HTTP_ERROR_CONFIG') protected httpErrorConfig: HttpErrorConfig,
@Inject('HTTP_ERROR_CONFIG') protected httpErrorConfig: HttpErrorConfig
) {
this.listenToRestError();
this.listenToRouterError();
@ -110,31 +116,34 @@ export class ErrorHandler {
this.actions
.pipe(
ofActionSuccessful(RestOccurError),
map(action => action.payload),
map((action) => action.payload),
filter(this.filterRestErrors),
switchMap(this.executeErrorHandler),
switchMap(this.executeErrorHandler)
)
.subscribe();
}
private executeErrorHandler = error => {
private executeErrorHandler = (error) => {
const returnValue = this.httpErrorHandler(this.injector, error);
return (returnValue instanceof Observable ? returnValue : of(null)).pipe(
catchError(err => {
catchError((err) => {
this.handleError(err);
return of(null);
}),
})
);
};
private handleError(err: any) {
const body = snq(() => err.error.error, {
const body = err?.error?.error || {
key: DEFAULT_ERROR_LOCALIZATIONS.defaultError.title,
defaultValue: DEFAULT_ERROR_MESSAGES.defaultError.title,
});
};
if (err instanceof HttpErrorResponse && err.headers.get('_AbpErrorFormat')) {
if (
err instanceof HttpErrorResponse &&
err.headers.get('_AbpErrorFormat')
) {
const confirmation$ = this.showError(null, null, body);
if (err.status === 401) {
@ -155,7 +164,7 @@ export class ErrorHandler {
{
key: DEFAULT_ERROR_LOCALIZATIONS.defaultError401.details,
defaultValue: DEFAULT_ERROR_MESSAGES.defaultError401.details,
},
}
).subscribe(() => this.navigateToLogin());
break;
case 403:
@ -182,7 +191,7 @@ export class ErrorHandler {
{
key: DEFAULT_ERROR_LOCALIZATIONS.defaultError404.title,
defaultValue: DEFAULT_ERROR_MESSAGES.defaultError404.title,
},
}
);
break;
case 500:
@ -219,7 +228,7 @@ export class ErrorHandler {
{
key: DEFAULT_ERROR_LOCALIZATIONS.defaultError.title,
defaultValue: DEFAULT_ERROR_MESSAGES.defaultError.title,
},
}
);
break;
}
@ -249,7 +258,7 @@ export class ErrorHandler {
protected showError(
message?: LocalizationParam,
title?: LocalizationParam,
body?: any,
body?: any
): Observable<Confirmation.Status> {
if (body) {
if (body.details) {
@ -294,18 +303,23 @@ export class ErrorHandler {
}
}
this.componentRef.instance.hideCloseIcon = this.httpErrorConfig.errorScreen.hideCloseIcon;
this.componentRef.instance.hideCloseIcon =
this.httpErrorConfig.errorScreen.hideCloseIcon;
const appRef = this.injector.get(ApplicationRef);
if (this.canCreateCustomError(instance.status as ErrorScreenErrorCodes)) {
this.componentRef.instance.cfRes = this.cfRes;
this.componentRef.instance.appRef = appRef;
this.componentRef.instance.injector = this.injector;
this.componentRef.instance.customComponent = this.httpErrorConfig.errorScreen.component;
this.componentRef.instance.customComponent =
this.httpErrorConfig.errorScreen.component;
}
appRef.attachView(this.componentRef.hostView);
renderer.appendChild(host, (this.componentRef.hostView as EmbeddedViewRef<any>).rootNodes[0]);
renderer.appendChild(
host,
(this.componentRef.hostView as EmbeddedViewRef<any>).rootNodes[0]
);
const destroy$ = new Subject<void>();
this.componentRef.instance.destroy$ = destroy$;
@ -316,23 +330,28 @@ export class ErrorHandler {
}
canCreateCustomError(status: ErrorScreenErrorCodes): boolean {
return snq(
() =>
this.httpErrorConfig.errorScreen.component &&
this.httpErrorConfig.errorScreen.forWhichErrors.indexOf(status) > -1,
return (
this.httpErrorConfig?.errorScreen?.component &&
this.httpErrorConfig?.errorScreen?.forWhichErrors?.indexOf(status) > -1
);
}
protected filterRestErrors = ({ status }: HttpErrorResponse): boolean => {
if (typeof status !== 'number') return false;
return this.httpErrorConfig.skipHandledErrorCodes.findIndex(code => code === status) < 0;
return (
this.httpErrorConfig.skipHandledErrorCodes.findIndex(
(code) => code === status
) < 0
);
};
protected filterRouteErrors = (navigationError: NavigationError): boolean => {
return (
snq(() => navigationError.error.message.indexOf('Cannot match') > -1) &&
this.httpErrorConfig.skipHandledErrorCodes.findIndex(code => code === 404) < 0
navigationError.error?.message?.indexOf('Cannot match') > -1 &&
this.httpErrorConfig.skipHandledErrorCodes.findIndex(
(code) => code === 404
) < 0
);
};
}

21
npm/ng-packs/nx/ng-packs/packages/theme-shared/src/lib/services/toaster.service.ts

@ -6,7 +6,6 @@ import {
} from '@abp/ng.core';
import { ComponentRef, Injectable } from '@angular/core';
import { ReplaySubject } from 'rxjs';
import snq from 'snq';
import { ToastContainerComponent } from '../components/toast-container/toast-container.component';
import { Toaster } from '../models';
@ -26,7 +25,9 @@ export class ToasterService implements ToasterContract {
private setContainer() {
this.containerComponentRef = this.contentProjectionService.projectContent(
PROJECTION_STRATEGY.AppendComponentToBody(ToastContainerComponent, { toasts$: this.toasts$ }),
PROJECTION_STRATEGY.AppendComponentToBody(ToastContainerComponent, {
toasts$: this.toasts$,
})
);
this.containerComponentRef.changeDetectorRef.detectChanges();
@ -41,7 +42,7 @@ export class ToasterService implements ToasterContract {
info(
message: LocalizationParam,
title?: LocalizationParam,
options?: Partial<Toaster.ToastOptions>,
options?: Partial<Toaster.ToastOptions>
): Toaster.ToasterId {
return this.show(message, title, 'info', options);
}
@ -55,7 +56,7 @@ export class ToasterService implements ToasterContract {
success(
message: LocalizationParam,
title?: LocalizationParam,
options?: Partial<Toaster.ToastOptions>,
options?: Partial<Toaster.ToastOptions>
): Toaster.ToasterId {
return this.show(message, title, 'success', options);
}
@ -69,7 +70,7 @@ export class ToasterService implements ToasterContract {
warn(
message: LocalizationParam,
title?: LocalizationParam,
options?: Partial<Toaster.ToastOptions>,
options?: Partial<Toaster.ToastOptions>
): Toaster.ToasterId {
return this.show(message, title, 'warning', options);
}
@ -83,7 +84,7 @@ export class ToasterService implements ToasterContract {
error(
message: LocalizationParam,
title?: LocalizationParam,
options?: Partial<Toaster.ToastOptions>,
options?: Partial<Toaster.ToastOptions>
): Toaster.ToasterId {
return this.show(message, title, 'error', options);
}
@ -100,7 +101,7 @@ export class ToasterService implements ToasterContract {
message: LocalizationParam,
title: LocalizationParam = null,
severity: Toaster.Severity = 'neutral',
options = {} as Partial<Toaster.ToastOptions>,
options = {} as Partial<Toaster.ToastOptions>
): Toaster.ToasterId {
if (!this.containerComponentRef) this.setContainer();
@ -120,7 +121,7 @@ export class ToasterService implements ToasterContract {
* @param id ID of the toast to be removed.
*/
remove(id: number): void {
this.toasts = this.toasts.filter(toast => snq(() => toast.options.id) !== id);
this.toasts = this.toasts.filter((toast) => toast.options?.id !== id);
this.toasts$.next(this.toasts);
}
@ -130,7 +131,9 @@ export class ToasterService implements ToasterContract {
clear(containerKey?: string): void {
this.toasts = !containerKey
? []
: this.toasts.filter(toast => snq(() => toast.options.containerKey) !== containerKey);
: this.toasts.filter(
(toast) => toast.options?.containerKey !== containerKey
);
this.toasts$.next(this.toasts);
}
}

65
npm/ng-packs/nx/ng-packs/packages/theme-shared/src/lib/tests/chart.component.spec.ts

@ -1,14 +1,14 @@
import { createHostFactory, SpectatorHost } from '@ngneat/spectator/jest';
import { ChartComponent } from '../components';
import { chartJsLoaded$ } from '../utils/widget-utils';
import { ReplaySubject } from 'rxjs';
import { ChartComponent } from '../components';
import * as widgetUtils from '../utils/widget-utils';
import { chartJsLoaded$ } from '../utils/widget-utils';
// import 'chart.js';
declare const Chart;
Object.defineProperty(window, 'getComputedStyle', {
value: () => ({
getPropertyValue: prop => {
getPropertyValue: (prop) => {
return '';
},
}),
@ -20,20 +20,23 @@ describe('ChartComponent', () => {
beforeEach(() => {
(widgetUtils as any).chartJsLoaded$ = new ReplaySubject(1);
spectator = createHost('<abp-chart [data]="data" type="polarArea"></abp-chart>', {
hostProps: {
data: {
datasets: [
{
data: [11],
backgroundColor: ['#FF6384'],
label: 'My dataset',
},
],
labels: ['Red'],
spectator = createHost(
'<abp-chart [data]="data" type="polarArea"></abp-chart>',
{
hostProps: {
data: {
datasets: [
{
data: [11],
backgroundColor: ['#FF6384'],
label: 'My dataset',
},
],
labels: ['Red'],
},
},
},
});
}
);
});
test('should throw error when chart.js is not loaded', () => {
@ -44,18 +47,18 @@ describe('ChartComponent', () => {
}
});
test('should have a success class by default', async done => {
await import('chart.js');
chartJsLoaded$.next();
setTimeout(() => {
expect(spectator.component.chart).toBeTruthy();
done();
}, 0);
test('should have a success class by default', (done) => {
import('chart.js').then(() => {
chartJsLoaded$.next();
setTimeout(() => {
expect(spectator.component.chart).toBeTruthy();
done();
}, 0);
});
});
describe('#reinit', () => {
it('should call the destroy method', done => {
it('should call the destroy method', (done) => {
chartJsLoaded$.next();
const spy = jest.spyOn(spectator.component.chart, 'destroy');
spectator.setHostInput({
@ -78,7 +81,7 @@ describe('ChartComponent', () => {
});
describe('#refresh', () => {
it('should call the update method', done => {
it('should call the update method', (done) => {
chartJsLoaded$.next();
const spy = jest.spyOn(spectator.component.chart, 'update');
spectator.component.refresh();
@ -90,7 +93,7 @@ describe('ChartComponent', () => {
});
describe('#generateLegend', () => {
it('should call the generateLegend method', done => {
it('should call the generateLegend method', (done) => {
chartJsLoaded$.next();
const spy = jest.spyOn(spectator.component.chart, 'generateLegend');
spectator.component.generateLegend();
@ -102,19 +105,21 @@ describe('ChartComponent', () => {
});
describe('#onCanvasClick', () => {
it('should emit the onDataSelect', done => {
it('should emit the onDataSelect', (done) => {
spectator.component.onDataSelect.subscribe(() => {
done();
});
chartJsLoaded$.next();
jest.spyOn(spectator.component.chart, 'getElementAtEvent').mockReturnValue([document.createElement('div')]);
jest
.spyOn(spectator.component.chart, 'getElementAtEvent')
.mockReturnValue([document.createElement('div')]);
spectator.click('canvas');
});
});
describe('#base64Image', () => {
it('should return the base64 image', done => {
it('should return the base64 image', (done) => {
chartJsLoaded$.next();
setTimeout(() => {

74
npm/ng-packs/nx/ng-packs/packages/theme-shared/src/lib/tests/confirmation.service.spec.ts

@ -4,7 +4,6 @@ import { fakeAsync, tick } from '@angular/core/testing';
import { createServiceFactory, SpectatorService } from '@ngneat/spectator/jest';
import { NgxsModule } from '@ngxs/store';
import { timer } from 'rxjs';
import { take } from 'rxjs/operators';
import { ConfirmationComponent } from '../components';
import { Confirmation } from '../models';
import { ConfirmationService } from '../services';
@ -51,7 +50,7 @@ describe('ConfirmationService', () => {
{
cancelText: '<span class="custom-cancel">CANCEL</span>',
yesText: '<span class="custom-yes">YES</span>',
},
}
);
tick();
@ -68,42 +67,47 @@ describe('ConfirmationService', () => {
${'success'} | ${'.success'} | ${'.fa-check-circle'}
${'warn'} | ${'.warning'} | ${'.fa-exclamation-triangle'}
${'error'} | ${'.error'} | ${'.fa-times-circle'}
`('should display $type confirmation popup', async ({ type, selector, icon }) => {
service[type]('MESSAGE', 'TITLE');
`(
'should display $type confirmation popup',
async ({ type, selector, icon }) => {
service[type]('MESSAGE', 'TITLE');
await timer(0).toPromise();
await timer(0).toPromise();
expect(selectConfirmationContent('.title')).toBe('TITLE');
expect(selectConfirmationContent('.message')).toBe('MESSAGE');
expect(selectConfirmationElement(selector)).toBeTruthy();
expect(selectConfirmationElement(icon)).toBeTruthy();
});
expect(selectConfirmationContent('.title')).toBe('TITLE');
expect(selectConfirmationContent('.message')).toBe('MESSAGE');
expect(selectConfirmationElement(selector)).toBeTruthy();
expect(selectConfirmationElement(icon)).toBeTruthy();
}
);
// test('should close with ESC key', (done) => {
// service
// .info('', '')
// .pipe(take(1))
// .subscribe((status) => {
// expect(status).toBe(Confirmation.Status.dismiss);
// done();
// });
test('should close with ESC key', done => {
// const escape = new KeyboardEvent('keyup', { key: 'Escape' });
// document.dispatchEvent(escape);
// });
test('should close when click cancel button', (done) => {
service
.info('', '')
.pipe(take(1))
.subscribe(status => {
expect(status).toBe(Confirmation.Status.dismiss);
.info('', '', { yesText: 'Sure', cancelText: 'Exit' })
.subscribe((status) => {
expect(status).toBe(Confirmation.Status.reject);
done();
});
const escape = new KeyboardEvent('keyup', { key: 'Escape' });
document.dispatchEvent(escape);
});
timer(0).subscribe(() => {
expect(selectConfirmationContent('button#cancel')).toBe('Exit');
expect(selectConfirmationContent('button#confirm')).toBe('Sure');
test('should close when click cancel button', async done => {
service.info('', '', { yesText: 'Sure', cancelText: 'Exit' }).subscribe(status => {
expect(status).toBe(Confirmation.Status.reject);
done();
(document.querySelector('button#cancel') as HTMLButtonElement).click();
});
await timer(0).toPromise();
expect(selectConfirmationContent('button#cancel')).toBe('Exit');
expect(selectConfirmationContent('button#confirm')).toBe('Sure');
selectConfirmationElement<HTMLButtonElement>('button#cancel').click();
});
test.each`
@ -113,23 +117,27 @@ describe('ConfirmationService', () => {
`(
'should call the listenToEscape method $count times when dismissible is $dismissible',
({ dismissible, count }) => {
const spy = spyOn(service as any, 'listenToEscape');
const spy = jest.spyOn(service as any, 'listenToEscape');
service.info('', '', { dismissible });
expect(spy).toHaveBeenCalledTimes(count);
},
}
);
});
function clearElements(selector = '.confirmation') {
document.querySelectorAll(selector).forEach(element => element.parentNode.removeChild(element));
document
.querySelectorAll(selector)
.forEach((element) => element.parentNode.removeChild(element));
}
function selectConfirmationContent(selector = '.confirmation'): string {
return selectConfirmationElement(selector).textContent.trim();
}
function selectConfirmationElement<T extends HTMLElement>(selector = '.confirmation'): T {
function selectConfirmationElement<T extends HTMLElement>(
selector = '.confirmation'
): T {
return document.querySelector(selector);
}

21
npm/ng-packs/nx/ng-packs/packages/theme-shared/src/lib/tests/error.component.spec.ts

@ -1,10 +1,10 @@
import { SpectatorHost, createHostFactory } from '@ngneat/spectator/jest';
import { HttpErrorWrapperComponent } from '../components/http-error-wrapper/http-error-wrapper.component';
import { CORE_OPTIONS, LocalizationPipe } from '@abp/ng.core';
import { HttpClient } from '@angular/common/http';
import { ElementRef, Renderer2 } from '@angular/core';
import { createHostFactory, SpectatorHost } from '@ngneat/spectator/jest';
import { Store } from '@ngxs/store';
import { Renderer2, ElementRef } from '@angular/core';
import { Subject } from 'rxjs';
import { HttpClient } from '@angular/common/http';
import { HttpErrorWrapperComponent } from '../components/http-error-wrapper/http-error-wrapper.component';
describe('ErrorComponent', () => {
let spectator: SpectatorHost<HttpErrorWrapperComponent>;
@ -15,7 +15,10 @@ describe('ErrorComponent', () => {
providers: [
{ provide: CORE_OPTIONS, useValue: {} },
{ provide: Renderer2, useValue: { removeChild: () => null } },
{ provide: ElementRef, useValue: { nativeElement: document.createElement('div') } },
{
provide: ElementRef,
useValue: { nativeElement: document.createElement('div') },
},
],
});
@ -25,16 +28,16 @@ describe('ErrorComponent', () => {
});
describe('#destroy', () => {
it('should be call when pressed the esc key', done => {
spectator.component.destroy$.subscribe(res => {
it('should be call when pressed the esc key', (done) => {
spectator.component.destroy$.subscribe(() => {
done();
});
spectator.keyboard.pressEscape();
});
it('should be call when clicked the close button', done => {
spectator.component.destroy$.subscribe(res => {
it('should be call when clicked the close button', (done) => {
spectator.component.destroy$.subscribe(() => {
done();
});

35
npm/ng-packs/nx/ng-packs/packages/theme-shared/src/lib/tests/error.handler.spec.ts

@ -8,7 +8,11 @@ import { NgxsModule, Store } from '@ngxs/store';
import { OAuthService } from 'angular-oauth2-oidc';
import { of } from 'rxjs';
import { HttpErrorWrapperComponent } from '../components/http-error-wrapper/http-error-wrapper.component';
import { DEFAULT_ERROR_LOCALIZATIONS, DEFAULT_ERROR_MESSAGES, ErrorHandler } from '../handlers';
import {
DEFAULT_ERROR_LOCALIZATIONS,
DEFAULT_ERROR_MESSAGES,
ErrorHandler,
} from '../handlers';
import { ConfirmationService } from '../services';
import { httpErrorConfigFactory } from '../tokens/http-error.token';
@ -31,7 +35,11 @@ const CONFIRMATION_BUTTONS = {
describe('ErrorHandler', () => {
const createService = createServiceFactory({
service: ErrorHandler,
imports: [NgxsModule.forRoot([]), CoreTestingModule.withConfig(), MockModule],
imports: [
NgxsModule.forRoot([]),
CoreTestingModule.withConfig(),
MockModule,
],
mocks: [OAuthService],
providers: [
{ provide: APP_BASE_HREF, useValue: '/' },
@ -106,7 +114,10 @@ describe('ErrorHandler', () => {
test('should display HttpErrorWrapperComponent when unknown error occurs', () => {
const createComponent = jest.spyOn(service, 'createErrorComponent');
const error = new HttpErrorResponse({ status: 0, statusText: 'Unknown Error' });
const error = new HttpErrorResponse({
status: 0,
statusText: 'Unknown Error',
});
const params = {
title: {
key: DEFAULT_ERROR_LOCALIZATIONS.defaultError.title,
@ -135,7 +146,7 @@ describe('ErrorHandler', () => {
key: DEFAULT_ERROR_LOCALIZATIONS.defaultError404.title,
defaultValue: DEFAULT_ERROR_MESSAGES.defaultError404.title,
},
CONFIRMATION_BUTTONS,
CONFIRMATION_BUTTONS
);
});
@ -151,7 +162,7 @@ describe('ErrorHandler', () => {
key: DEFAULT_ERROR_LOCALIZATIONS.defaultError.title,
defaultValue: DEFAULT_ERROR_MESSAGES.defaultError.title,
},
CONFIRMATION_BUTTONS,
CONFIRMATION_BUTTONS
);
});
@ -167,7 +178,7 @@ describe('ErrorHandler', () => {
key: DEFAULT_ERROR_LOCALIZATIONS.defaultError401.details,
defaultValue: DEFAULT_ERROR_MESSAGES.defaultError401.details,
},
CONFIRMATION_BUTTONS,
CONFIRMATION_BUTTONS
);
});
@ -175,7 +186,9 @@ describe('ErrorHandler', () => {
const headers: HttpHeaders = new HttpHeaders({
_AbpErrorFormat: '_AbpErrorFormat',
});
store.dispatch(new RestOccurError(new HttpErrorResponse({ status: 401, headers })));
store.dispatch(
new RestOccurError(new HttpErrorResponse({ status: 401, headers }))
);
expect(errorConfirmation).toHaveBeenCalledWith(
{
@ -183,7 +196,7 @@ describe('ErrorHandler', () => {
defaultValue: DEFAULT_ERROR_MESSAGES.defaultError.title,
},
null,
CONFIRMATION_BUTTONS,
CONFIRMATION_BUTTONS
);
});
@ -196,14 +209,14 @@ describe('ErrorHandler', () => {
error: { error: { message: 'test message', details: 'test detail' } },
status: 412,
headers,
}),
),
})
)
);
expect(errorConfirmation).toHaveBeenCalledWith(
'test detail',
'test message',
CONFIRMATION_BUTTONS,
CONFIRMATION_BUTTONS
);
});
});

28
npm/ng-packs/nx/ng-packs/packages/theme-shared/src/lib/tests/modal.component.spec.ts

@ -5,7 +5,11 @@ import { createHostFactory, SpectatorHost } from '@ngneat/spectator/jest';
import { Store } from '@ngxs/store';
import { fromEvent, Subject, timer } from 'rxjs';
import { delay, reduce, take } from 'rxjs/operators';
import { ButtonComponent, ConfirmationComponent, ModalComponent } from '../components';
import {
ButtonComponent,
ConfirmationComponent,
ModalComponent,
} from '../components';
import { Confirmation } from '../models';
import { ConfirmationService } from '../services';
@ -65,7 +69,7 @@ describe('ModalComponent', () => {
appearFn,
disappearFn,
},
},
}
);
await wait0ms();
@ -157,7 +161,11 @@ describe('ModalComponent', () => {
it('should close with esc key', async () => {
await wait0ms();
spectator.dispatchKeyboardEvent(spectator.component.modalWindowRef, 'keyup', 'Escape');
spectator.dispatchKeyboardEvent(
spectator.component.modalWindowRef,
'keyup',
'Escape'
);
await wait300ms();
@ -175,12 +183,12 @@ describe('ModalComponent', () => {
expect(disappearFn).not.toHaveBeenCalled();
});
xit('should not let window unload when form is dirty', async done => {
xit('should not let window unload when form is dirty', (done) => {
fromEvent(window, 'beforeunload')
.pipe(
take(2),
delay(0),
reduce<Event[]>((acc, v) => acc.concat(v), []),
reduce<Event[]>((acc, v) => acc.concat(v), [])
)
.subscribe(([event1, event2]) => {
expect(event1.returnValue).toBe(false);
@ -192,11 +200,11 @@ describe('ModalComponent', () => {
spectator.detectChanges();
spectator.dispatchFakeEvent(window, 'beforeunload');
await wait0ms();
spectator.hostComponent.ngDirty = false;
spectator.detectChanges();
spectator.dispatchFakeEvent(window, 'beforeunload');
wait0ms().then(() => {
spectator.hostComponent.ngDirty = false;
spectator.detectChanges();
spectator.dispatchFakeEvent(window, 'beforeunload');
});
});
});

32
npm/ng-packs/nx/ng-packs/packages/theme-shared/src/lib/tests/table-sort.directive.spec.ts

@ -1,32 +0,0 @@
import { SpectatorDirective, createDirectiveFactory } from '@ngneat/spectator/jest';
import { TableSortDirective } from '../directives/table-sort.directive';
import { TableComponent } from '../components/table/table.component';
import { DummyLocalizationPipe } from './table.component.spec';
import { NgbPaginationModule } from '@ng-bootstrap/ng-bootstrap';
describe('TableSortDirective', () => {
let spectator: SpectatorDirective<TableSortDirective>;
let directive: TableSortDirective;
const createDirective = createDirectiveFactory({
directive: TableSortDirective,
declarations: [TableComponent, DummyLocalizationPipe],
imports: [NgbPaginationModule],
});
beforeEach(() => {
spectator = createDirective(
`<abp-table [value]="[1,4,2]" [abpTableSort]="{ order: 'asc' }"></abp-table>`,
);
directive = spectator.directive;
});
test('should be created', () => {
expect(directive).toBeTruthy();
});
test('should change table value', () => {
expect(directive.value).toEqual([1, 4, 2]);
const table = spectator.query(TableComponent);
expect(table.value).toEqual([1, 2, 4]);
});
});

75
npm/ng-packs/nx/ng-packs/packages/theme-shared/src/lib/tests/table.component.spec.ts

@ -1,75 +0,0 @@
import { Pipe, PipeTransform } from '@angular/core';
import { NgbPaginationModule } from '@ng-bootstrap/ng-bootstrap';
import { createHostFactory, SpectatorHost } from '@ngneat/spectator/jest';
import { TableComponent } from '../components';
@Pipe({
name: 'abpLocalization',
})
export class DummyLocalizationPipe implements PipeTransform {
transform(value: any, ...args: any[]): any {
return value;
}
}
describe('TableComponent', () => {
let spectator: SpectatorHost<TableComponent>;
const createHost = createHostFactory({
component: TableComponent,
declarations: [DummyLocalizationPipe],
imports: [NgbPaginationModule],
});
describe('without value', () => {
beforeEach(() => {
spectator = createHost(
`<abp-table
[headerTemplate]="header"
[colgroupTemplate]="colgroup"
[value]="value">
</abp-table>
<ng-template #colgroup><colgroup><col /></colgroup></ng-template>
<ng-template #header><th>name</th></ng-template>`,
{
hostProps: {
value: [],
},
},
);
});
it('should display the empty message', () => {
expect(spectator.query('caption.ui-table-empty')).toHaveText(
'AbpAccount::NoDataAvailableInDatatable',
);
});
it('should display the header', () => {
expect(spectator.query('thead')).toBeTruthy();
expect(spectator.query('th')).toHaveText('name');
});
it('should place the colgroup template', () => {
expect(spectator.query('colgroup')).toBeTruthy();
expect(spectator.query('col')).toBeTruthy();
});
});
describe('with value', () => {
// TODO
beforeEach(() => {
spectator = createHost(
`<abp-table
[headerTemplate]="header"
[value]="value"></abp-table>
<ng-template #header><th>name</th></ng-template>
`,
{
hostProps: {
value: [],
},
},
);
});
});
});

15
npm/ng-packs/nx/ng-packs/packages/theme-shared/src/lib/tests/validation-utils.spec.ts

@ -1,13 +1,12 @@
import { ConfigState, ConfigStateService } from '@abp/ng.core';
import { Component, Injector } from '@angular/core';
import { createComponentFactory, Spectator } from '@ngneat/spectator';
import { NgxValidateCoreModule, validatePassword } from '@ngx-validate/core';
import { NgxsModule, Store } from '@ngxs/store';
import { ConfigStateService } from '@abp/ng.core';
import { CoreTestingModule } from '@abp/ng.core/testing';
import { HttpClient } from '@angular/common/http';
import { getPasswordValidators } from '../utils';
import { Component, Injector } from '@angular/core';
import { Validators } from '@angular/forms';
import { createComponentFactory, Spectator } from '@ngneat/spectator/jest';
import { NgxValidateCoreModule, validatePassword } from '@ngx-validate/core';
import { OAuthService } from 'angular-oauth2-oidc';
import { getPasswordValidators } from '../utils';
@Component({ template: '', selector: 'abp-dummy' })
class DummyComponent {}
@ -15,7 +14,7 @@ describe('ValidationUtils', () => {
let spectator: Spectator<DummyComponent>;
const createComponent = createComponentFactory({
component: DummyComponent,
imports: [NgxValidateCoreModule.forRoot()],
imports: [CoreTestingModule.withConfig(), NgxValidateCoreModule.forRoot()],
mocks: [HttpClient, OAuthService],
});

1
npm/ng-packs/nx/ng-packs/packages/theme-shared/src/test-setup.ts

@ -1 +1,2 @@
import 'jest-canvas-mock';
import 'jest-preset-angular/setup-jest';

22
npm/ng-packs/nx/ng-packs/yarn.lock

@ -5307,7 +5307,7 @@ color-name@1.1.3:
resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25"
integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=
color-name@^1.0.0, color-name@~1.1.4:
color-name@^1.0.0, color-name@^1.1.4, color-name@~1.1.4:
version "1.1.4"
resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2"
integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
@ -5902,6 +5902,11 @@ cssesc@^3.0.0:
resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee"
integrity sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==
cssfontparser@^1.2.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/cssfontparser/-/cssfontparser-1.2.1.tgz#f4022fc8f9700c68029d542084afbaf425a3f3e3"
integrity sha1-9AIvyPlwDGgCnVQghK+69CWj8+M=
cssnano-preset-default@^5.1.3:
version "5.1.3"
resolved "https://registry.yarnpkg.com/cssnano-preset-default/-/cssnano-preset-default-5.1.3.tgz#caa54183a8c8df03124a9e23f374ab89df5a9a99"
@ -8928,6 +8933,14 @@ jasminewd2@^2.1.0:
resolved "https://registry.yarnpkg.com/jasminewd2/-/jasminewd2-2.2.0.tgz#e37cf0b17f199cce23bea71b2039395246b4ec4e"
integrity sha1-43zwsX8ZnM4jvqcbIDk5Uka07E4=
jest-canvas-mock@^2.3.1:
version "2.3.1"
resolved "https://registry.yarnpkg.com/jest-canvas-mock/-/jest-canvas-mock-2.3.1.tgz#9535d14bc18ccf1493be36ac37dd349928387826"
integrity sha512-5FnSZPrX3Q2ZfsbYNE3wqKR3+XorN8qFzDzB5o0golWgt6EOX1+emBnpOc9IAQ+NXFj8Nzm3h7ZdE/9H0ylBcg==
dependencies:
cssfontparser "^1.2.1"
moo-color "^1.0.2"
jest-changed-files@^27.0.6:
version "27.0.6"
resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-27.0.6.tgz#bed6183fcdea8a285482e3b50a9a7712d49a7a8b"
@ -10453,6 +10466,13 @@ moment@^2.10.2:
resolved "https://registry.yarnpkg.com/moment/-/moment-2.29.1.tgz#b2be769fa31940be9eeea6469c075e35006fa3d3"
integrity sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ==
moo-color@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/moo-color/-/moo-color-1.0.2.tgz#837c40758d2d58763825d1359a84e330531eca64"
integrity sha512-5iXz5n9LWQzx/C2WesGFfpE6RLamzdHwsn3KpfzShwbfIqs7stnoEpaNErf/7+3mbxwZ4s8Foq7I0tPxw7BWHg==
dependencies:
color-name "^1.1.4"
move-concurrently@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/move-concurrently/-/move-concurrently-1.0.1.tgz#be2c005fda32e0b29af1f05d7c4b33214c701f92"

Loading…
Cancel
Save