Browse Source

Merge pull request #1918 from abpframework/test/core

test(core): add profile.state.spec
pull/1930/head
Yasin Aydın 7 years ago
committed by GitHub
parent
commit
2346d0b589
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 3
      npm/ng-packs/package.json
  2. 10
      npm/ng-packs/packages/core/src/lib/directives/for.directive.ts
  3. 12
      npm/ng-packs/packages/core/src/lib/directives/visibility.directive.ts
  4. 2
      npm/ng-packs/packages/core/src/lib/states/session.state.ts
  5. 2
      npm/ng-packs/packages/core/src/lib/tests/config.state.spec.ts
  6. 191
      npm/ng-packs/packages/core/src/lib/tests/for.directive.spec.ts
  7. 74
      npm/ng-packs/packages/core/src/lib/tests/profile.state.spec.ts
  8. 70
      npm/ng-packs/packages/core/src/lib/tests/session.state.spec.ts
  9. 128
      npm/ng-packs/packages/core/src/lib/tests/visibility.directive.spec.ts
  10. 41
      npm/ng-packs/packages/theme-shared/src/lib/components/button/button.component.ts
  11. 39
      npm/ng-packs/packages/theme-shared/src/lib/tests/button.component.spec.ts
  12. 2
      npm/ng-packs/scripts/build.js

3
npm/ng-packs/package.json

@ -92,6 +92,7 @@
"prettier --write",
"tslint --fix",
"git add"
]
],
"dist/*": []
}
}

10
npm/ng-packs/packages/core/src/lib/directives/for.directive.ts

@ -9,7 +9,7 @@ import {
OnChanges,
TemplateRef,
TrackByFunction,
ViewContainerRef
ViewContainerRef,
} from '@angular/core';
import compare from 'just-compare';
import clone from 'just-clone';
@ -25,7 +25,7 @@ class RecordView {
}
@Directive({
selector: '[abpFor]'
selector: '[abpFor]',
})
export class ForDirective implements OnChanges {
@Input('abpForOf')
@ -67,7 +67,7 @@ export class ForDirective implements OnChanges {
constructor(
private tempRef: TemplateRef<AbpForContext>,
private vcRef: ViewContainerRef,
private differs: IterableDiffers
private differs: IterableDiffers,
) {}
private iterateOverAppliedOperations(changes: IterableChanges<any>) {
@ -78,7 +78,7 @@ export class ForDirective implements OnChanges {
const view = this.vcRef.createEmbeddedView(
this.tempRef,
new AbpForContext(null, -1, -1, this.items),
currentIndex
currentIndex,
);
rw.push(new RecordView(record, view));
@ -155,7 +155,7 @@ export class ForDirective implements OnChanges {
const compareFn = this.compareFn;
if (typeof this.filterBy !== 'undefined') {
if (typeof this.filterBy !== 'undefined' && this.filterVal) {
items = items.filter(item => compareFn(item[this.filterBy], this.filterVal));
}

12
npm/ng-packs/packages/core/src/lib/directives/visibility.directive.ts

@ -3,7 +3,7 @@ import { Subject } from 'rxjs';
import snq from 'snq';
@Directive({
selector: '[abpVisibility]'
selector: '[abpVisibility]',
})
export class VisibilityDirective implements AfterViewInit {
@Input('abpVisibility')
@ -17,6 +17,10 @@ export class VisibilityDirective implements AfterViewInit {
constructor(@Optional() private elRef: ElementRef, private renderer: Renderer2) {}
ngAfterViewInit() {
if (!this.focusedElement && this.elRef) {
this.focusedElement = this.elRef.nativeElement;
}
let observer: MutationObserver;
if (this.mutationObserverEnabled) {
observer = new MutationObserver(mutations => {
@ -25,7 +29,7 @@ export class VisibilityDirective implements AfterViewInit {
const htmlNodes = snq(
() => Array.from(mutation.target.childNodes).filter(node => node instanceof HTMLElement),
[]
[],
);
if (!htmlNodes.length) {
@ -40,13 +44,13 @@ export class VisibilityDirective implements AfterViewInit {
});
observer.observe(this.focusedElement, {
childList: true
childList: true,
});
} else {
setTimeout(() => {
const htmlNodes = snq(
() => Array.from(this.focusedElement.childNodes).filter(node => node instanceof HTMLElement),
[]
[],
);
if (!htmlNodes.length) this.removeFromDOM();

2
npm/ng-packs/packages/core/src/lib/states/session.state.ts

@ -35,7 +35,7 @@ export class SessionState {
}
@Action(SetTenant)
setTenantId({ patchState }: StateContext<Session.State>, { payload }: SetTenant) {
setTenant({ patchState }: StateContext<Session.State>, { payload }: SetTenant) {
patchState({
tenant: payload,
});

2
npm/ng-packs/packages/core/src/lib/tests/config.state.spec.ts

@ -109,7 +109,7 @@ export const CONFIG_STATE_DATA = {
},
} as Config.State;
describe('ConfigService', () => {
describe('ConfigState', () => {
let spectator: SpectatorService<ConfigService>;
let store: SpyObject<Store>;
let service: ConfigService;

191
npm/ng-packs/packages/core/src/lib/tests/for.directive.spec.ts

@ -0,0 +1,191 @@
import { SpectatorDirective, createDirectiveFactory } from '@ngneat/spectator/jest';
import { ForDirective } from '../directives/for.directive';
import { uuid } from '../utils';
describe('ForDirective', () => {
let spectator: SpectatorDirective<ForDirective>;
let directive: ForDirective;
const items = [0, 1, 2, 3, 4, 5];
const createDirective = createDirectiveFactory({
directive: ForDirective,
});
describe('basic', () => {
beforeEach(() => {
spectator = createDirective('<ul><li *abpFor="let item of items">{{ item }}</li></ul>', {
hostProps: { items },
});
directive = spectator.directive;
});
test('should be created', () => {
expect(directive).toBeTruthy();
});
test('should be iterated', () => {
const elements = spectator.queryAll('li');
expect(elements[3]).toHaveText('3');
expect(elements).toHaveLength(6);
});
test('should sync the DOM when change items', () => {
(spectator.hostComponent as any).items = [10, 11, 12];
spectator.detectChanges();
const elements = spectator.queryAll('li');
expect(elements[1]).toHaveText('11');
expect(elements).toHaveLength(3);
});
test('should sync the DOM when add an item', () => {
(spectator.hostComponent as any).items = [...items, 6];
spectator.detectChanges();
const elements = spectator.queryAll('li');
expect(elements[6]).toHaveText('6');
expect(elements).toHaveLength(7);
});
});
describe('trackBy', () => {
const trackByFn = (_, item) => item;
beforeEach(() => {
spectator = createDirective('<ul><li *abpFor="let item of items; trackBy: trackByFn">{{ item }}</li></ul>', {
hostProps: { items, trackByFn },
});
directive = spectator.directive;
});
test('should be setted the trackBy', () => {
expect(directive.trackBy).toEqual(trackByFn);
});
});
describe('with basic order', () => {
beforeEach(() => {
spectator = createDirective(
`<ul>
<li
*abpFor="let item of [3,6,2];
orderDir: 'ASC'">
{{ item }}
</li>
</ul>`,
);
directive = spectator.directive;
});
test('should order by asc', () => {
const elements = spectator.queryAll('li');
expect(elements.map(el => el.textContent.trim())).toEqual(['2', '3', '6']);
});
});
describe('with order', () => {
beforeEach(() => {
spectator = createDirective(
`<ul>
<li
*abpFor="let item of [{value: 3}, {value: 6}, {value: 2}];
orderBy: 'value';
orderDir: orderDir">
{{ item.value }}
</li>
</ul>`,
{
hostProps: { orderDir: 'ASC' },
},
);
directive = spectator.directive;
});
test('should order by asc', () => {
const elements = spectator.queryAll('li');
expect(elements.map(el => el.textContent.trim())).toEqual(['2', '3', '6']);
});
test('should order by desc', () => {
(spectator.hostComponent as any).orderDir = 'DESC';
spectator.detectChanges();
const elements = spectator.queryAll('li');
expect(elements.map(el => el.textContent.trim())).toEqual(['6', '3', '2']);
});
});
describe('with filter', () => {
beforeEach(() => {
spectator = createDirective(
`<ul>
<li
*abpFor="let item of [{value: 'test'}, {value: 'abp'}, {value: 'volo'}];
filterBy: 'value';
filterVal: filterVal">
{{ item.value }}
</li>
</ul>`,
{
hostProps: { filterVal: '' },
},
);
directive = spectator.directive;
});
test('should not filter when filterVal is empty,', () => {
const elements = spectator.queryAll('li');
expect(elements.map(el => el.textContent.trim())).toEqual(['test', 'abp', 'volo']);
});
test('should be filtered', () => {
(spectator.hostComponent as any).filterVal = 'volo';
spectator.detectChanges();
expect(spectator.query('li')).toHaveText('volo');
});
test('should not show an element when filter value not match to any text', () => {
(spectator.hostComponent as any).filterVal = 'volos';
spectator.detectChanges();
const elements = spectator.queryAll('li');
expect(elements).toHaveLength(0);
});
});
describe('with empty ref', () => {
beforeEach(() => {
spectator = createDirective(
`<ul>
<li
*abpFor="let item of items;
emptyRef: empty">
{{ item.value }}
</li>
<ng-template #empty>No records found</ng-template>
</ul>`,
{
hostProps: { items: [] },
},
);
directive = spectator.directive;
});
test('should display the empty ref', () => {
expect(spectator.query('ul')).toHaveText('No records found');
expect(spectator.queryAll('li')).toHaveLength(0);
});
test('should not display the empty ref', () => {
expect(spectator.query('ul')).toHaveText('No records found');
expect(spectator.queryAll('li')).toHaveLength(0);
(spectator.hostComponent as any).items = [0];
spectator.detectChanges();
expect(spectator.query('ul')).not.toHaveText('No records found');
expect(spectator.queryAll('li')).toHaveLength(1);
});
});
});

74
npm/ng-packs/packages/core/src/lib/tests/profile.state.spec.ts

@ -0,0 +1,74 @@
import { createServiceFactory, SpectatorService, SpyObject } from '@ngneat/spectator/jest';
import { Session } from '../models/session';
import { ProfileService } from '../services';
import { ProfileState } from '../states';
import { GetAppConfiguration } from '../actions/config.actions';
import { of } from 'rxjs';
import { Profile } from '../models/profile';
export class DummyClass {}
export const PROFILE_STATE_DATA = {
profile: { userName: 'admin', email: 'info@abp.io', name: 'Admin' },
} as Profile.State;
describe('ProfileState', () => {
let spectator: SpectatorService<DummyClass>;
let state: ProfileState;
let profileService: SpyObject<ProfileService>;
let patchedData;
const patchState = jest.fn(data => (patchedData = data));
const createService = createServiceFactory({
service: DummyClass,
mocks: [ProfileService],
});
beforeEach(() => {
spectator = createService();
profileService = spectator.get(ProfileService);
state = new ProfileState(profileService);
});
describe('#getProfile', () => {
it('should return the current language', () => {
expect(ProfileState.getProfile(PROFILE_STATE_DATA)).toEqual(PROFILE_STATE_DATA.profile);
});
});
describe('#GetProfile', () => {
it('should call the profile service get method and update the state', () => {
const mockData = { userName: 'test', email: 'test@abp.io' };
const spy = jest.spyOn(profileService, 'get');
spy.mockReturnValue(of(mockData as any));
state.profileGet({ patchState } as any).subscribe();
expect(patchedData).toEqual({ profile: mockData });
});
});
describe('#UpdateProfile', () => {
it('should call the profile service update method and update the state', () => {
const mockData = { userName: 'test2', email: 'test@abp.io' };
const spy = jest.spyOn(profileService, 'update');
spy.mockReturnValue(of(mockData as any));
state.profileUpdate({ patchState } as any, { payload: mockData as any }).subscribe();
expect(patchedData).toEqual({ profile: mockData });
});
});
describe('#ChangePassword', () => {
it('should call the profile service changePassword method', () => {
const mockData = { currentPassword: 'test123', newPassword: 'test123' };
const spy = jest.spyOn(profileService, 'changePassword');
spy.mockReturnValue(of(null));
state.changePassword(null, { payload: mockData }).subscribe();
expect(spy).toHaveBeenCalledWith(mockData, true);
});
});
});

70
npm/ng-packs/packages/core/src/lib/tests/session.state.spec.ts

@ -0,0 +1,70 @@
import { createServiceFactory, SpectatorService } from '@ngneat/spectator/jest';
import { Session } from '../models/session';
import { LocalizationService } from '../services';
import { SessionState } from '../states';
import { GetAppConfiguration } from '../actions/config.actions';
import { of } from 'rxjs';
export class DummyClass {}
export const SESSION_STATE_DATA = {
language: 'tr',
tenant: { id: 'd5692aef-2ac6-49cd-9f3e-394c0bd4f8b3', name: 'Test' },
} as Session.State;
describe('SessionState', () => {
let spectator: SpectatorService<DummyClass>;
let state: SessionState;
const createService = createServiceFactory({
service: DummyClass,
mocks: [LocalizationService],
});
beforeEach(() => {
spectator = createService();
state = new SessionState(spectator.get(LocalizationService));
});
describe('#getLanguage', () => {
it('should return the current language', () => {
expect(SessionState.getLanguage(SESSION_STATE_DATA)).toEqual(SESSION_STATE_DATA.language);
});
});
describe('#getTenant', () => {
it('should return the tenant object', () => {
expect(SessionState.getTenant(SESSION_STATE_DATA)).toEqual(SESSION_STATE_DATA.tenant);
});
});
describe('#SetLanguage', () => {
it('should set the language and dispatch the GetAppConfiguration action', () => {
let patchedData;
let dispatchedData;
const patchState = jest.fn(data => (patchedData = data));
const dispatch = jest.fn(action => {
dispatchedData = action;
return of({});
});
const spy = jest.spyOn(spectator.get(LocalizationService), 'registerLocale');
state.setLanguage({ patchState, dispatch } as any, { payload: 'en' }).subscribe();
expect(patchedData).toEqual({ language: 'en' });
expect(dispatchedData instanceof GetAppConfiguration).toBeTruthy();
expect(spy).toHaveBeenCalledWith('en');
});
});
describe('#setTenantId', () => {
it('should set the tenant', () => {
let patchedData;
const patchState = jest.fn(data => (patchedData = data));
const testTenant = { id: '54ae02ba-9289-4c1b-8521-0ea437756288', name: 'Test Tenant' };
state.setTenant({ patchState } as any, { payload: testTenant });
expect(patchedData).toEqual({ tenant: testTenant });
});
});
});

128
npm/ng-packs/packages/core/src/lib/tests/visibility.directive.spec.ts

@ -0,0 +1,128 @@
import { SpectatorDirective, createDirectiveFactory } from '@ngneat/spectator/jest';
import { VisibilityDirective } from '../directives/visibility.directive';
describe('VisibilityDirective', () => {
let spectator: SpectatorDirective<VisibilityDirective>;
let directive: VisibilityDirective;
const createDirective = createDirectiveFactory({
directive: VisibilityDirective,
});
describe('without mutation observer and without content', () => {
beforeEach(() => {
spectator = createDirective('<div [abpVisibility] [mutationObserverEnabled]="false"></div>');
directive = spectator.directive;
});
it('should be created', () => {
expect(directive).toBeTruthy();
});
it('should be removed', done => {
setTimeout(() => {
expect(spectator.query('div')).toBeFalsy();
done();
}, 0);
});
});
describe('without mutation observer and with content', () => {
beforeEach(() => {
spectator = createDirective(
'<div [abpVisibility] [mutationObserverEnabled]="false"><p id="content">Content</p></div>',
);
directive = spectator.directive;
});
it('should not removed', done => {
setTimeout(() => {
expect(spectator.query('div')).toBeTruthy();
done();
}, 0);
});
});
describe('without mutation observer and with focused element', () => {
beforeEach(() => {
spectator = createDirective(
'<div id="main" [abpVisibility]="container" [mutationObserverEnabled]="false"></div><div #container><p id="content">Content</p></div>',
);
directive = spectator.directive;
});
it('should not removed', done => {
setTimeout(() => {
expect(spectator.query('#main')).toBeTruthy();
done();
}, 0);
});
});
describe('without content and with focused element', () => {
beforeEach(() => {
spectator = createDirective(
'<div id="main" [abpVisibility]="container" [mutationObserverEnabled]="false"></div><div #container></div>',
);
directive = spectator.directive;
});
it('should be removed', done => {
setTimeout(() => {
expect(spectator.query('#main')).toBeFalsy();
done();
}, 0);
});
});
describe('with mutation observer and with content', () => {
beforeEach(() => {
spectator = createDirective('<div [abpVisibility]><div id="content">Content</div></div>');
directive = spectator.directive;
});
it('should remove the main div element when content removed', done => {
spectator.query('#content').remove();
setTimeout(() => {
expect(spectator.query('div')).toBeFalsy();
done();
}, 0);
});
it('should not remove the main div element', done => {
spectator.query('div').appendChild(document.createElement('div'));
setTimeout(() => {
expect(spectator.query('div')).toBeTruthy();
done();
}, 100);
});
});
describe('with mutation observer and with focused element', () => {
beforeEach(() => {
spectator = createDirective(
'<div id="main" [abpVisibility]="container"></div><div #container><p id="content">Content</p></div>',
);
directive = spectator.directive;
});
it('should remove the main div element when content removed', done => {
spectator.query('#content').remove();
setTimeout(() => {
expect(spectator.query('#main')).toBeFalsy();
done();
}, 0);
});
it('should not remove the main div element', done => {
spectator.query('#content').appendChild(document.createElement('div'));
setTimeout(() => {
expect(spectator.query('#main')).toBeTruthy();
done();
}, 100);
});
});
});

41
npm/ng-packs/packages/theme-shared/src/lib/components/button/button.component.ts

@ -1,14 +1,24 @@
import { Component, Input } from '@angular/core';
import { Component, EventEmitter, Input, Output, ViewChild, ElementRef, Renderer2, OnInit } from '@angular/core';
import { ABP } from '@abp/ng.core';
@Component({
selector: 'abp-button',
// tslint:disable-next-line: component-max-inline-declarations
template: `
<button [attr.type]="type" [ngClass]="buttonClass" [disabled]="loading || disabled">
<button
#button
[attr.type]="type"
[ngClass]="buttonClass"
[disabled]="loading || disabled"
(click)="click.emit($event)"
(focus)="focus.emit($event)"
(blur)="blur.emit($event)"
>
<i [ngClass]="icon" class="mr-1"></i><ng-content></ng-content>
</button>
`,
})
export class ButtonComponent {
export class ButtonComponent implements OnInit {
@Input()
buttonClass = 'btn btn-primary';
@ -24,6 +34,21 @@ export class ButtonComponent {
@Input()
disabled = false;
@Input()
attributes: ABP.Dictionary<string>;
// tslint:disable-next-line: no-output-native
@Output() readonly click = new EventEmitter<MouseEvent>();
// tslint:disable-next-line: no-output-native
@Output() readonly focus = new EventEmitter<FocusEvent>();
// tslint:disable-next-line: no-output-native
@Output() readonly blur = new EventEmitter<FocusEvent>();
@ViewChild('button', { static: true })
buttonRef: ElementRef<HTMLButtonElement>;
/**
* @deprecated Use buttonType instead. To be deleted in v1
*/
@ -32,4 +57,14 @@ export class ButtonComponent {
get icon(): string {
return `${this.loading ? 'fa fa-pulse fa-spinner' : this.iconClass || 'd-none'}`;
}
constructor(private renderer: Renderer2) {}
ngOnInit() {
if (this.attributes) {
Object.keys(this.attributes).forEach(key => {
this.renderer.setAttribute(this.buttonRef.nativeElement, key, this.attributes[key]);
});
}
}
}

39
npm/ng-packs/packages/theme-shared/src/lib/tests/button.component.spec.ts

@ -2,46 +2,55 @@ import { createHostFactory, SpectatorHost } from '@ngneat/spectator/jest';
import { ButtonComponent } from '../components';
describe('ButtonComponent', () => {
let host: SpectatorHost<ButtonComponent>;
let spectator: SpectatorHost<ButtonComponent>;
const createHost = createHostFactory(ButtonComponent);
beforeEach(() => (host = createHost('<abp-button iconClass="fa fa-check">Button</abp-button>')));
beforeEach(
() =>
(spectator = createHost('<abp-button iconClass="fa fa-check" [attributes]="attributes">Button</abp-button>', {
hostProps: { attributes: { autofocus: '', name: 'abp-button' } },
})),
);
it('should display the button', () => {
expect(host.query('button')).toBeTruthy();
expect(spectator.query('button')).toBeTruthy();
});
it('should equal the default classes to btn btn-primary', () => {
expect(host.query('button')).toHaveClass('btn btn-primary');
expect(spectator.query('button')).toHaveClass('btn btn-primary');
});
it('should equal the default type to button', () => {
expect(host.query('button')).toHaveAttribute('type', 'button');
expect(spectator.query('button')).toHaveAttribute('type', 'button');
});
it('should enabled', () => {
expect(host.query('[disabled]')).toBeFalsy();
expect(spectator.query('[disabled]')).toBeFalsy();
});
it('should have the text content', () => {
expect(host.query('button')).toHaveText('Button');
expect(spectator.query('button')).toHaveText('Button');
});
it('should display the icon', () => {
expect(host.query('i.d-none')).toBeFalsy();
expect(host.query('i')).toHaveClass('fa');
expect(spectator.query('i.d-none')).toBeFalsy();
expect(spectator.query('i')).toHaveClass('fa');
});
it('should display the spinner icon', () => {
host.component.loading = true;
host.detectComponentChanges();
expect(host.query('i')).toHaveClass('fa-spinner');
spectator.component.loading = true;
spectator.detectComponentChanges();
expect(spectator.query('i')).toHaveClass('fa-spinner');
});
it('should disabled when the loading input is true', () => {
host.component.loading = true;
host.detectComponentChanges();
expect(host.query('[disabled]')).toBeDefined();
spectator.component.loading = true;
spectator.detectComponentChanges();
expect(spectator.query('[disabled]')).toBeTruthy();
});
it('should disabled when the loading input is true', () => {
expect(spectator.query('[autofocus][name="abp-button"]')).toBeTruthy();
});
});

2
npm/ng-packs/scripts/build.js

@ -37,7 +37,7 @@ import fse from 'fs-extra';
});
await execa('git', ['add', '../dist/*', '../package.json'], { stdout: 'inherit' });
await execa('git', ['commit', '-m', 'Build ng packages'], { stdout: 'inherit' });
await execa('git', ['commit', '--no-verify', '-m', 'Build ng packages'], { stdout: 'inherit' });
process.exit(0);
})();

Loading…
Cancel
Save