diff options
Diffstat (limited to 'Timeline/ClientApp/src/app/user/internal-user-service')
-rw-r--r-- | Timeline/ClientApp/src/app/user/internal-user-service/internal-user.service.spec.ts | 149 | ||||
-rw-r--r-- | Timeline/ClientApp/src/app/user/internal-user-service/internal-user.service.ts | 35 |
2 files changed, 99 insertions, 85 deletions
diff --git a/Timeline/ClientApp/src/app/user/internal-user-service/internal-user.service.spec.ts b/Timeline/ClientApp/src/app/user/internal-user-service/internal-user.service.spec.ts index 613a8fa6..be6631eb 100644 --- a/Timeline/ClientApp/src/app/user/internal-user-service/internal-user.service.spec.ts +++ b/Timeline/ClientApp/src/app/user/internal-user-service/internal-user.service.spec.ts @@ -1,47 +1,95 @@ -import { TestBed } from '@angular/core/testing'; +import { TestBed, fakeAsync, tick } from '@angular/core/testing'; import { HttpRequest } from '@angular/common/http'; -import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; +import { HttpClientTestingModule, HttpTestingController, TestRequest } from '@angular/common/http/testing'; import { Router } from '@angular/router'; +import { MatSnackBar } from '@angular/material'; + +import { Mock } from 'src/app/test-utilities/mock'; +import { createMockStorage } from 'src/app/test-utilities/storage.mock'; +import { WINDOW } from '../window-inject-token'; import { UserInfo, UserCredentials } from '../entities'; import { createTokenUrl, validateTokenUrl, CreateTokenRequest, CreateTokenResponse, ValidateTokenRequest, ValidateTokenResponse } from './http-entities'; -import { InternalUserService, UserLoginState } from './internal-user.service'; +import { InternalUserService, SnackBarTextKey, snackBarText, TOKEN_STORAGE_KEY } from './internal-user.service'; +import { repeat } from 'src/app/utilities/language-untilities'; -describe('InternalUserService', () => { - const mockUserCredentials: UserCredentials = { - username: 'user', - password: 'user' - }; - beforeEach(() => TestBed.configureTestingModule({ - imports: [HttpClientTestingModule], - providers: [{ provide: Router, useValue: null }] - })); +describe('InternalUserService', () => { + let mockLocalStorage: Mock<Storage>; + let mockSnackBar: jasmine.SpyObj<MatSnackBar>; + + beforeEach(() => { + mockLocalStorage = createMockStorage(); + mockSnackBar = jasmine.createSpyObj('MatSnackBar', ['open']); + TestBed.configureTestingModule({ + imports: [HttpClientTestingModule], + providers: [ + { provide: WINDOW, useValue: { localStorage: mockLocalStorage } }, + { provide: Router, useValue: null }, + { provide: MatSnackBar, useValue: mockSnackBar } + ] + }); + }); it('should be created', () => { const service: InternalUserService = TestBed.get(InternalUserService); expect(service).toBeTruthy(); }); - it('should be nologin at first', () => { - const service: InternalUserService = TestBed.get(InternalUserService); - expect(service.currentUserInfo).toBe(null); - service.refreshAndGetUserState().subscribe(result => { - expect(result).toBe('nologin'); - }); + const mockUserInfo: UserInfo = { + username: 'user', + roles: ['user', 'other'] + }; + + const mockToken = 'mock-token'; + + describe('validate token', () => { + const validateTokenRequestMatcher = (req: HttpRequest<ValidateTokenRequest>): boolean => + req.url === validateTokenUrl && req.body !== null && req.body.token === mockToken; + + function createTest( + expectSnackBarTextKey: SnackBarTextKey, + setStorageToken: boolean, + setHttpController?: (controller: HttpTestingController) => void + ): () => void { + return fakeAsync(() => { + if (setStorageToken) { + mockLocalStorage.setItem(TOKEN_STORAGE_KEY, mockToken); + } + TestBed.get(InternalUserService); + const controller = TestBed.get(HttpTestingController) as HttpTestingController; + if (setHttpController) { + setHttpController(controller); + } + controller.verify(); + tick(); + expect(mockSnackBar.open).toHaveBeenCalledWith(snackBarText[expectSnackBarTextKey], jasmine.anything(), jasmine.anything()); + }); + } + + it('no login should work well', createTest('noLogin', false)); + it('already login should work well', createTest('alreadyLogin', true, + controller => controller.expectOne(validateTokenRequestMatcher).flush( + <ValidateTokenResponse>{ isValid: true, userInfo: mockUserInfo }))); + it('invalid login should work well', createTest('invalidLogin', true, + controller => controller.expectOne(validateTokenRequestMatcher).flush(<ValidateTokenResponse>{ isValid: false }))); + it('check fail should work well', createTest('checkFail', true, + controller => repeat(4, () => { + controller.expectOne(validateTokenRequestMatcher).error(new ErrorEvent('Network error', { message: 'simulated network error' })); + }))); }); it('login should work well', () => { - const service: InternalUserService = TestBed.get(InternalUserService); - - const mockUserInfo: UserInfo = { + const mockUserCredentials: UserCredentials = { username: 'user', - roles: ['user', 'other'] + password: 'user' }; + const service: InternalUserService = TestBed.get(InternalUserService); + service.tryLogin(mockUserCredentials).subscribe(result => { expect(result).toEqual(mockUserInfo); }); @@ -50,66 +98,17 @@ describe('InternalUserService', () => { httpController.expectOne((request: HttpRequest<CreateTokenRequest>) => request.url === createTokenUrl && request.body !== null && - request.body.username === 'user' && - request.body.password === 'user').flush(<CreateTokenResponse>{ - token: 'test-token', + request.body.username === mockUserCredentials.username && + request.body.password === mockUserCredentials.password).flush(<CreateTokenResponse>{ + token: mockToken, userInfo: mockUserInfo }); expect(service.currentUserInfo).toEqual(mockUserInfo); httpController.verify(); - }); - - describe('validateUserLoginState', () => { - let service: InternalUserService; - let httpController: HttpTestingController; - - const mockUserInfo: UserInfo = { - username: 'user', - roles: ['user', 'other'] - }; - - const mockToken = 'mock-token'; - - const tokenValidateRequestMatcher = (req: HttpRequest<ValidateTokenRequest>) => { - return req.url === validateTokenUrl && req.body !== null && req.body.token === mockToken; - }; - - beforeEach(() => { - service = TestBed.get(InternalUserService); - httpController = TestBed.get(HttpTestingController); - - service.tryLogin(mockUserCredentials).subscribe(); // subscribe to activate login - - httpController.expectOne(createTokenUrl).flush(<CreateTokenResponse>{ - token: mockToken, - userInfo: mockUserInfo - }); - }); - it('success should work well', () => { - service.refreshAndGetUserState().subscribe((result: UserLoginState) => { - expect(result).toEqual(<UserLoginState>'success'); - }); - - httpController.expectOne(tokenValidateRequestMatcher).flush(<ValidateTokenResponse>{ - isValid: true, - userInfo: mockUserInfo - }); - - httpController.verify(); - }); - - it('invalid should work well', () => { - service.refreshAndGetUserState().subscribe((result: UserLoginState) => { - expect(result).toEqual(<UserLoginState>'invalidlogin'); - }); - - httpController.expectOne(tokenValidateRequestMatcher).flush(<ValidateTokenResponse>{ isValid: false }); - - httpController.verify(); - }); + expect(mockLocalStorage.getItem(TOKEN_STORAGE_KEY)).toBe(mockToken); }); // TODO: test on error situations. diff --git a/Timeline/ClientApp/src/app/user/internal-user-service/internal-user.service.ts b/Timeline/ClientApp/src/app/user/internal-user-service/internal-user.service.ts index 604393f4..2098391e 100644 --- a/Timeline/ClientApp/src/app/user/internal-user-service/internal-user.service.ts +++ b/Timeline/ClientApp/src/app/user/internal-user-service/internal-user.service.ts @@ -1,4 +1,4 @@ -import { Injectable } from '@angular/core'; +import { Injectable, Inject } from '@angular/core'; import { HttpClient, HttpErrorResponse } from '@angular/common/http'; import { Router } from '@angular/router'; @@ -12,7 +12,19 @@ import { } from './http-entities'; import { UserCredentials, UserInfo } from '../entities'; import { MatSnackBar } from '@angular/material'; +import { WINDOW } from '../window-inject-token'; +export const snackBarText = { + checkFail: 'Failed to check last login', + noLogin: 'No login before!', + alreadyLogin: 'You have login already!', + invalidLogin: 'Last login is no longer invalid!', + ok: 'ok' +}; + +export type SnackBarTextKey = Exclude<keyof typeof snackBarText, 'ok'>; + +export const TOKEN_STORAGE_KEY = 'token'; /** * This service is only used internal in user module. @@ -33,25 +45,28 @@ export class InternalUserService { return this.userInfoSubject; } - constructor(private httpClient: HttpClient, private router: Router, private snackBar: MatSnackBar) { - const savedToken = window.localStorage.getItem('token'); + private openSnackBar(snackBar: MatSnackBar, textKey: SnackBarTextKey) { + setTimeout(() => snackBar.open(snackBarText[textKey], snackBarText.ok, { duration: 2000 }), 0); + } + + constructor(@Inject(WINDOW) private window: Window, private httpClient: HttpClient, private router: Router, snackBar: MatSnackBar) { + const savedToken = this.window.localStorage.getItem(TOKEN_STORAGE_KEY); if (savedToken === null) { - setTimeout(() => snackBar.open('No login before!', 'ok', { duration: 2000 }), 0); + this.openSnackBar(snackBar, 'noLogin'); } else { this.validateToken(savedToken).subscribe(result => { if (result === null) { - window.localStorage.removeItem('token'); - setTimeout(() => snackBar.open('Last login is no longer invalid!', 'ok', { duration: 2000 }), 0); + this.window.localStorage.removeItem(TOKEN_STORAGE_KEY); + this.openSnackBar(snackBar, 'invalidLogin'); } else { this.token = savedToken; this.userInfoSubject.next(result); - setTimeout(() => snackBar.open('You have login already!', 'ok', { duration: 2000 }), 0); + this.openSnackBar(snackBar, 'alreadyLogin'); } }, _ => { - setTimeout(() => snackBar.open('Failed to check last login', 'ok', { duration: 2000 }), 0); + this.openSnackBar(snackBar, 'checkFail'); }); } - } private validateToken(token: string): Observable<UserInfo | null> { @@ -107,7 +122,7 @@ export class InternalUserService { map(result => { this.token = result.token; if (options.remember) { - window.localStorage.setItem('token', result.token); + this.window.localStorage.setItem(TOKEN_STORAGE_KEY, result.token); } this.userInfoSubject.next(result.userInfo); return result.userInfo; |