update tests & add if condition to permissions directive

This commit is contained in:
Dan Percic 2022-10-17 19:27:40 +03:00
parent 53b8442167
commit 8b07e0d31b
2 changed files with 198 additions and 47 deletions

View File

@ -3,6 +3,7 @@ import { IqserPermissionsModule } from '.';
import { ComponentFixture, ComponentFixtureAutoDetect, fakeAsync, TestBed, tick } from '@angular/core/testing';
import { IqserPermissionsService } from './services/permissions.service';
import { IqserRolesService } from './services/roles.service';
import { BehaviorSubject } from 'rxjs';
class BaseTestComponent {
isAuthorized() {}
@ -184,16 +185,7 @@ describe('Permission directive with roles array', () => {
it('should show the component when key of role is the same', fakeAsync(() => {
permissionsService.add(awesomePermission);
rolesService.add({ ADMIN: [awesomePermission] });
tick();
const content = getFixtureContent();
expect(content).toBeTruthy();
expect(content.innerHTML).toEqual('123');
}));
it('should show the component when there is permission', fakeAsync(() => {
permissionsService.add(awesomePermission);
rolesService.add({ ADMIN: ['AWESOME'] });
rolesService.add({ GUEST: [awesomePermission] });
tick();
const content = getFixtureContent();
@ -204,6 +196,7 @@ describe('Permission directive with roles array', () => {
it('should hide the component when user deletes all roles', fakeAsync(() => {
permissionsService.add(awesomePermission);
rolesService.add({ ADMIN: [awesomePermission] });
rolesService.add({ GUEST: [awesomePermission] });
tick();
const content = getFixtureContent();
@ -216,9 +209,10 @@ describe('Permission directive with roles array', () => {
expect(getFixtureContent()).toEqual(null);
}));
it('should hide the component when user deletes one roles', fakeAsync(() => {
it('should hide the component when user deletes one role', fakeAsync(() => {
permissionsService.add(awesomePermission);
rolesService.add({ ADMIN: [awesomePermission] });
rolesService.add({ GUEST: [awesomePermission] });
tick();
const content = getFixtureContent();
@ -329,6 +323,7 @@ describe('Permission directive angular testing different async functions in role
expect(getFixtureContent()).toEqual(null);
rolesService.add({ ADMIN: () => Promise.resolve(true) });
rolesService.add({ GUEST: () => Promise.resolve(true) });
tick();
const content = getFixtureContent();
@ -340,6 +335,7 @@ describe('Permission directive angular testing different async functions in role
expect(getFixtureContent()).toEqual(null);
rolesService.add({ ADMIN: () => Promise.resolve(false) });
rolesService.add({ GUEST: () => Promise.resolve(true) });
tick();
expect(getFixtureContent()).toEqual(null);
@ -348,32 +344,29 @@ describe('Permission directive angular testing different async functions in role
it('should not show the component when promise rejects', fakeAsync(() => {
expect(getFixtureContent()).toEqual(null);
rolesService.add({ ADMIN: () => Promise.reject() });
rolesService.add({ ADMIN: () => Promise.resolve(true) });
rolesService.add({ GUEST: () => Promise.reject() });
tick();
expect(getFixtureContent()).toEqual(null);
}));
it('should show the component when one of the promises fulfills', fakeAsync(() => {
it('should hide the component when one of the promises fulfills', fakeAsync(() => {
expect(getFixtureContent()).toEqual(null);
rolesService.add({ ADMIN: () => Promise.reject(), GUEST: () => Promise.resolve(true) });
tick();
const content = getFixtureContent();
expect(content).toBeTruthy();
expect(content.innerHTML).toEqual('<div>123</div>');
expect(getFixtureContent()).toEqual(null);
}));
it('should show the component when one of the promises fulfills with true value', fakeAsync(() => {
it('should hide the component when one of the promises fulfills with true value', fakeAsync(() => {
expect(getFixtureContent()).toEqual(null);
rolesService.add({ ADMIN: () => Promise.reject(), GUEST: () => Promise.resolve(true) });
tick();
const content = getFixtureContent();
expect(content).toBeTruthy();
expect(content.innerHTML).toEqual('<div>123</div>');
expect(getFixtureContent()).toEqual(null);
}));
it('should not show the component when all promises fails', fakeAsync(() => {
@ -385,38 +378,23 @@ describe('Permission directive angular testing different async functions in role
expect(getFixtureContent()).toEqual(null);
}));
it('should show the component when one of promises returns true', fakeAsync(() => {
it('should hide the component when one of promises returns true', fakeAsync(() => {
expect(getFixtureContent()).toEqual(null);
rolesService.add({ GUEST: () => true, ADMIN: () => Promise.reject() });
tick();
const content = getFixtureContent();
expect(content).toBeTruthy();
expect(content.innerHTML).toEqual('<div>123</div>');
expect(getFixtureContent()).toEqual(null);
}));
it('should show the component when 1 passes second fails', fakeAsync(() => {
it('should hide the component when 1 passes second fails', fakeAsync(() => {
expect(getFixtureContent()).toEqual(null);
rolesService.add({ ADMIN: () => Promise.reject() });
rolesService.add({ ADMIN: ['AWESOME'] });
rolesService.add({ GUEST: ['AWESOME'] });
tick();
const content = getFixtureContent();
expect(content).toBeTruthy();
expect(content.innerHTML).toEqual('<div>123</div>');
}));
it('should show the component when one rejects but another one fulfils', fakeAsync(() => {
expect(getFixtureContent()).toEqual(null);
rolesService.add({ ADMIN: () => Promise.reject(), GUEST: ['AWESOME'] });
tick();
const content = getFixtureContent();
expect(content).toBeTruthy();
expect(content.innerHTML).toEqual('<div>123</div>');
}));
});
@ -806,3 +784,154 @@ describe('Permissions directive with an empty array', () => {
expect(content.innerHTML).toEqual(`123`);
});
});
describe('Permission directive with true if condition', () => {
@Component({
template: ` <ng-template [allow]="'ADMIN'" [allowIf]="true">
<div>123</div>
</ng-template>`,
})
class TestComponent extends BaseTestComponent {}
beforeEach(() => configureTestBed(TestComponent));
it('should show the component when permission is present', fakeAsync(() => {
permissionsService.add(ADMIN);
tick();
const content = getFixtureContent();
expect(content).toBeTruthy();
expect(content.innerHTML).toEqual('123');
}));
it('should hide the component when permission is absent', fakeAsync(() => {
expect(getFixtureContent()).toEqual(null);
}));
});
describe('Permission directive with true if condition', () => {
@Component({
template: ` <ng-template [allow]="'ADMIN'" [allowIf]="false">
<div>123</div>
</ng-template>`,
})
class TestComponent extends BaseTestComponent {}
beforeEach(() => configureTestBed(TestComponent));
it('should hide the component when permission is present', fakeAsync(() => {
permissionsService.add(ADMIN);
tick();
expect(getFixtureContent()).toEqual(null);
}));
it('should hide the component when permission is absent', fakeAsync(() => {
expect(getFixtureContent()).toEqual(null);
}));
});
describe('Permission directive with true promise if condition', () => {
@Component({
template: ` <ng-template [allow]="'ADMIN'" [allowIf]="condition">
<div>123</div>
</ng-template>`,
})
class TestComponent extends BaseTestComponent {
condition = Promise.resolve(true);
}
beforeEach(() => configureTestBed(TestComponent));
it('should show the component when permission is present', fakeAsync(() => {
permissionsService.add(ADMIN);
tick();
const content = getFixtureContent();
expect(content).toBeTruthy();
expect(content.innerHTML).toEqual('123');
}));
it('should hide the component when permission is absent', fakeAsync(() => {
expect(getFixtureContent()).toEqual(null);
}));
});
describe('Permission directive with false promise if condition', () => {
@Component({
template: ` <div *allow="'ADMIN'; if: condition">
<div>123</div>
</div>`,
})
class TestComponent extends BaseTestComponent {
condition = Promise.resolve(false);
}
beforeEach(() => configureTestBed(TestComponent));
it('should hide the component when permission is present', fakeAsync(() => {
permissionsService.add(ADMIN);
tick();
expect(getFixtureContent()).toEqual(null);
}));
it('should hide the component when permission is absent', fakeAsync(() => {
expect(getFixtureContent()).toEqual(null);
}));
});
describe('Permissions directive with then block and if condition', () => {
@Component({
template: `
<div *allow="['THEN_BLOCK']; if: condition; else: elseBlock; then: thenBlock">main</div>
<ng-template #elseBlock>
<div>elseBlockContent</div>
</ng-template>
<ng-template #thenBlock>
<div>thenBlockContent</div>
</ng-template>
`,
})
class TestComponent extends BaseTestComponent {
condition = new BehaviorSubject(true);
}
beforeEach(() => configureTestBed(TestComponent));
it('should show else block when permission missing', fakeAsync(() => {
const content = getFixtureContent();
expect(content).toBeTruthy();
expect(content.innerHTML).toEqual(`elseBlockContent`);
}));
it('should show then block when permission added', fakeAsync(() => {
permissionsService.add('THEN_BLOCK');
tick();
const content = getFixtureContent();
expect(content).toBeTruthy();
expect(content.innerHTML).toEqual(`thenBlockContent`);
}));
it('should show else block when permission added but condition is false', fakeAsync(() => {
permissionsService.add('THEN_BLOCK');
(fixture.componentInstance as TestComponent).condition.next(false);
tick();
const content = getFixtureContent();
expect(content).toBeTruthy();
expect(content.innerHTML).toEqual(`elseBlockContent`);
}));
it('should show else block when permission is missing and condition is false', fakeAsync(() => {
(fixture.componentInstance as TestComponent).condition.next(false);
tick();
const content = getFixtureContent();
expect(content).toBeTruthy();
expect(content.innerHTML).toEqual(`elseBlockContent`);
}));
});

View File

@ -12,12 +12,13 @@ import {
ɵstringify as stringify,
} from '@angular/core';
import { merge, Subject, Subscription, switchMap } from 'rxjs';
import { tap } from 'rxjs/operators';
import { BehaviorSubject, combineLatest, merge, Observable, of, Subject, Subscription, switchMap } from 'rxjs';
import { map, tap } from 'rxjs/operators';
import { IqserRolesService } from './services/roles.service';
import { IqserPermissionsService } from './services/permissions.service';
import { List } from '../utils';
import { isBoolean } from './utils';
@Directive({
selector: '[allow]',
@ -45,6 +46,7 @@ export class IqserPermissionsDirective implements OnDestroy, OnInit {
readonly #updateView = new Subject<void>();
readonly #subscription = new Subscription();
readonly #if = new BehaviorSubject<Promise<boolean> | Observable<boolean>>(of(true));
constructor(
private readonly _permissionsService: IqserPermissionsService,
@ -54,7 +56,17 @@ export class IqserPermissionsDirective implements OnDestroy, OnInit {
templateRef: TemplateRef<unknown>,
) {
this.#thenTemplateRef = templateRef;
this.#subscription = this.#updateView.pipe(switchMap(() => this.#waitForRolesAndPermissions())).subscribe();
const ifCondition$ = this.#if.pipe(
switchMap(condition => condition),
tap(console.log),
);
this.#subscription = combineLatest([ifCondition$, this.#updateView])
.pipe(
switchMap(([ifCondition]) => this.#waitForRolesAndPermissions().pipe(map(hasPermission => ifCondition && hasPermission))),
tap(isAllowed => (isAllowed ? this.#showThenBlock() : this.#showElseBlock())),
)
.subscribe();
}
@Input()
@ -79,6 +91,15 @@ export class IqserPermissionsDirective implements OnDestroy, OnInit {
this.#updateView.next();
}
@Input()
set allowIf(value: boolean | Promise<boolean> | Observable<boolean>) {
if (isBoolean(value)) {
this.#if.next(of(value));
} else {
this.#if.next(value);
}
}
/**
This assures that when the directive has an empty input (such as [allow]="") the view is updated
*/
@ -91,17 +112,18 @@ export class IqserPermissionsDirective implements OnDestroy, OnInit {
}
#waitForRolesAndPermissions() {
return merge(this._permissionsService.permissions$, this._rolesService.roles$).pipe(tap(() => this.#validate()));
return merge(this._permissionsService.permissions$, this._rolesService.roles$).pipe(switchMap(() => this.#validate()));
}
#validate(): void {
#validate() {
if (!this.#permissions) {
return this.#showThenBlock();
return Promise.resolve(true);
}
const promises = [this._permissionsService.has(this.#permissions), this._rolesService.has(this.#permissions)];
const result = Promise.all(promises).then(([hasPermission, hasRole]) => hasPermission || hasRole);
result.then(isAllowed => (isAllowed ? this.#showThenBlock() : this.#showElseBlock())).catch(() => this.#showElseBlock());
return Promise.all(promises)
.then(([hasPermission, hasRole]) => hasPermission || hasRole)
.catch(() => false);
}
#showElseBlock() {