aboutsummaryrefslogtreecommitdiff
path: root/Timeline/ClientApp
diff options
context:
space:
mode:
Diffstat (limited to 'Timeline/ClientApp')
-rw-r--r--Timeline/ClientApp/src/app/user/internal-user-service/internal-user.service.spec.ts39
-rw-r--r--Timeline/ClientApp/src/app/user/internal-user-service/internal-user.service.ts10
-rw-r--r--Timeline/ClientApp/src/app/user/user-login/user-login.component.html7
-rw-r--r--Timeline/ClientApp/src/app/user/user-login/user-login.component.spec.ts21
-rw-r--r--Timeline/ClientApp/src/app/user/user-login/user-login.component.ts3
-rw-r--r--Timeline/ClientApp/src/app/user/user.module.ts4
6 files changed, 51 insertions, 33 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 be6631eb..8d081402 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
@@ -82,33 +82,40 @@ describe('InternalUserService', () => {
})));
});
- it('login should work well', () => {
+ describe('login should work well', () => {
const mockUserCredentials: UserCredentials = {
username: 'user',
password: 'user'
};
- const service: InternalUserService = TestBed.get(InternalUserService);
+ function createTest(rememberMe: boolean) {
+ return () => {
+ const service: InternalUserService = TestBed.get(InternalUserService);
- service.tryLogin(mockUserCredentials).subscribe(result => {
- expect(result).toEqual(mockUserInfo);
- });
+ service.tryLogin({ ...mockUserCredentials, rememberMe: rememberMe }).subscribe(result => {
+ expect(result).toEqual(mockUserInfo);
+ });
- const httpController = TestBed.get(HttpTestingController) as HttpTestingController;
+ const httpController = TestBed.get(HttpTestingController) as HttpTestingController;
- httpController.expectOne((request: HttpRequest<CreateTokenRequest>) =>
- request.url === createTokenUrl && request.body !== null &&
- request.body.username === mockUserCredentials.username &&
- request.body.password === mockUserCredentials.password).flush(<CreateTokenResponse>{
- token: mockToken,
- userInfo: mockUserInfo
- });
+ httpController.expectOne((request: HttpRequest<CreateTokenRequest>) =>
+ request.url === createTokenUrl && request.body !== null &&
+ request.body.username === mockUserCredentials.username &&
+ request.body.password === mockUserCredentials.password).flush(<CreateTokenResponse>{
+ token: mockToken,
+ userInfo: mockUserInfo
+ });
- expect(service.currentUserInfo).toEqual(mockUserInfo);
+ expect(service.currentUserInfo).toEqual(mockUserInfo);
- httpController.verify();
+ httpController.verify();
+
+ expect(mockLocalStorage.getItem(TOKEN_STORAGE_KEY)).toBe(rememberMe ? mockToken : null);
+ }
+ }
- expect(mockLocalStorage.getItem(TOKEN_STORAGE_KEY)).toBe(mockToken);
+ it('remember me should work well', createTest(true));
+ it('not remember me should work well', createTest(false));
});
// 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 3f6147af..6de355f2 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
@@ -26,6 +26,10 @@ export type SnackBarTextKey = Exclude<keyof typeof snackBarText, 'ok'>;
export const TOKEN_STORAGE_KEY = 'token';
+export interface LoginInfo extends UserCredentials {
+ rememberMe: boolean;
+}
+
/**
* This service is only used internal in user module.
*/
@@ -103,12 +107,12 @@ export class InternalUserService {
}]);
}
- tryLogin(credentials: UserCredentials, options: { remember: boolean } = { remember: true }): Observable<UserInfo> {
+ tryLogin(info: LoginInfo): Observable<UserInfo> {
if (this.token) {
return throwError(new AlreadyLoginError());
}
- return this.httpClient.post<CreateTokenResponse>(createTokenUrl, <CreateTokenRequest>credentials).pipe(
+ return this.httpClient.post<CreateTokenResponse>(createTokenUrl, <CreateTokenRequest>info).pipe(
catchError((error: HttpErrorResponse) => {
if (error.error instanceof ErrorEvent) {
console.error('An error occurred when login: ' + error.error.message);
@@ -123,7 +127,7 @@ export class InternalUserService {
}),
map(result => {
this.token = result.token;
- if (options.remember) {
+ if (info.rememberMe) {
this.window.localStorage.setItem(TOKEN_STORAGE_KEY, result.token);
}
this.userInfoSubject.next(result.userInfo);
diff --git a/Timeline/ClientApp/src/app/user/user-login/user-login.component.html b/Timeline/ClientApp/src/app/user/user-login/user-login.component.html
index b1dd289d..7398ece7 100644
--- a/Timeline/ClientApp/src/app/user/user-login/user-login.component.html
+++ b/Timeline/ClientApp/src/app/user/user-login/user-login.component.html
@@ -1,8 +1,8 @@
<form [formGroup]="form">
<ng-container *ngIf="message" [ngSwitch]="message">
- <p *ngSwitchCase="'nologin'" class="mat-body no-login-message">You haven't login.</p>
- <p *ngSwitchCase="'invalidlogin'" class="mat-body invalid-login-message">Your login is no longer valid.</p>
- <p *ngSwitchDefault class="mat-body error-message">{{ message }}</p>
+ <p *ngSwitchCase="'nologin'" class="mat-h3 no-login-message">You haven't login.</p>
+ <p *ngSwitchCase="'invalidlogin'" class="mat-h3 invalid-login-message">Your login is no longer valid.</p>
+ <p *ngSwitchDefault class="mat-h3 error-message">{{ message }}</p>
</ng-container>
<mat-form-field>
<mat-label>Username</mat-label>
@@ -13,6 +13,7 @@
<mat-label>Password</mat-label>
<input formControlName="password" matInput type="password" />
</mat-form-field>
+ <mat-checkbox formControlName="rememberMe">Remember me!</mat-checkbox>
<div class="w-100"></div>
<button mat-flat-button class="login-button" (appDebounceClick)="onLoginButtonClick()">Login</button>
</form>
diff --git a/Timeline/ClientApp/src/app/user/user-login/user-login.component.spec.ts b/Timeline/ClientApp/src/app/user/user-login/user-login.component.spec.ts
index 693d5b6e..f010e4b7 100644
--- a/Timeline/ClientApp/src/app/user/user-login/user-login.component.spec.ts
+++ b/Timeline/ClientApp/src/app/user/user-login/user-login.component.spec.ts
@@ -2,7 +2,6 @@ import { NO_ERRORS_SCHEMA } from '@angular/core';
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { ReactiveFormsModule } from '@angular/forms';
import { By } from '@angular/platform-browser';
-import { ActivatedRoute } from '@angular/router';
import { of, throwError } from 'rxjs';
@@ -10,6 +9,7 @@ import { createMockInternalUserService } from '../internal-user-service/internal
import { UserLoginComponent } from './user-login.component';
import { InternalUserService } from '../internal-user-service/internal-user.service';
import { UserInfo } from '../entities';
+import { MatCheckboxModule } from '@angular/material';
describe('UserLoginComponent', () => {
let component: UserLoginComponent;
@@ -27,7 +27,7 @@ describe('UserLoginComponent', () => {
providers: [
{ provide: InternalUserService, useValue: mockInternalUserService }
],
- imports: [ReactiveFormsModule],
+ imports: [ReactiveFormsModule, MatCheckboxModule],
schemas: [NO_ERRORS_SCHEMA]
})
.compileComponents();
@@ -48,17 +48,20 @@ describe('UserLoginComponent', () => {
const usernameInput = fixture.debugElement.query(By.css('input[type=text]')).nativeElement as HTMLInputElement;
const passwordInput = fixture.debugElement.query(By.css('input[type=password]')).nativeElement as HTMLInputElement;
+ const rememberMeCheckbox = fixture.debugElement.query(By.css('input[type=checkbox]')).nativeElement as HTMLInputElement;
usernameInput.value = 'user';
usernameInput.dispatchEvent(new Event('input'));
passwordInput.value = 'user';
passwordInput.dispatchEvent(new Event('input'));
+ rememberMeCheckbox.dispatchEvent(new MouseEvent('click'));
fixture.detectChanges();
expect(component.form.value).toEqual({
username: 'user',
- password: 'user'
+ password: 'user',
+ rememberMe: true
});
});
@@ -67,7 +70,8 @@ describe('UserLoginComponent', () => {
const mockValue = {
username: 'user',
- password: 'user'
+ password: 'user',
+ rememberMe: true
};
mockInternalUserService.tryLogin.withArgs(mockValue).and.returnValue(of(<UserInfo>{ username: 'user', roles: ['user'] }));
@@ -84,7 +88,7 @@ describe('UserLoginComponent', () => {
fixture.detectChanges();
component.message = 'nologin';
fixture.detectChanges();
- expect((fixture.debugElement.query(By.css('p.mat-body')).nativeElement as
+ expect((fixture.debugElement.query(By.css('p')).nativeElement as
HTMLParagraphElement).textContent).toBe('You haven\'t login.');
});
@@ -92,7 +96,7 @@ describe('UserLoginComponent', () => {
fixture.detectChanges();
component.message = 'invalidlogin';
fixture.detectChanges();
- expect((fixture.debugElement.query(By.css('p.mat-body')).nativeElement as
+ expect((fixture.debugElement.query(By.css('p')).nativeElement as
HTMLParagraphElement).textContent).toBe('Your login is no longer valid.');
});
@@ -103,7 +107,8 @@ describe('UserLoginComponent', () => {
const mockValue = {
username: 'user',
- password: 'user'
+ password: 'user',
+ rememberMe: false
};
mockInternalUserService.tryLogin.withArgs(mockValue).and.returnValue(throwError(new Error(customMessage)));
component.form.setValue(mockValue);
@@ -111,7 +116,7 @@ describe('UserLoginComponent', () => {
fixture.detectChanges();
expect(component.message).toBe(customMessage);
- expect((fixture.debugElement.query(By.css('p.mat-body')).nativeElement as
+ expect((fixture.debugElement.query(By.css('p')).nativeElement as
HTMLParagraphElement).textContent).toBe(customMessage);
});
});
diff --git a/Timeline/ClientApp/src/app/user/user-login/user-login.component.ts b/Timeline/ClientApp/src/app/user/user-login/user-login.component.ts
index 836202de..4395c5cf 100644
--- a/Timeline/ClientApp/src/app/user/user-login/user-login.component.ts
+++ b/Timeline/ClientApp/src/app/user/user-login/user-login.component.ts
@@ -19,7 +19,8 @@ export class UserLoginComponent implements OnInit {
form = new FormGroup({
username: new FormControl(''),
- password: new FormControl('')
+ password: new FormControl(''),
+ rememberMe: new FormControl(false)
});
ngOnInit() {
diff --git a/Timeline/ClientApp/src/app/user/user.module.ts b/Timeline/ClientApp/src/app/user/user.module.ts
index 50c59662..59193380 100644
--- a/Timeline/ClientApp/src/app/user/user.module.ts
+++ b/Timeline/ClientApp/src/app/user/user.module.ts
@@ -6,7 +6,7 @@ import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { RouterModule } from '@angular/router';
import {
MatFormFieldModule, MatProgressSpinnerModule,
- MatDialogModule, MatInputModule, MatButtonModule, MatSnackBarModule
+ MatDialogModule, MatInputModule, MatButtonModule, MatSnackBarModule, MatCheckboxModule
} from '@angular/material';
import { RequireNoLoginGuard, RequireLoginGuard } from './auth.guard';
@@ -28,7 +28,7 @@ import { UserLogoutComponent } from './user-logout/user-logout.component';
{ path: '**', component: RedirectComponent, outlet: 'user' }
]),
CommonModule, HttpClientModule, ReactiveFormsModule, BrowserAnimationsModule,
- MatFormFieldModule, MatProgressSpinnerModule, MatDialogModule, MatInputModule, MatButtonModule, MatSnackBarModule,
+ MatFormFieldModule, MatProgressSpinnerModule, MatDialogModule, MatInputModule, MatButtonModule, MatCheckboxModule, MatSnackBarModule,
UtilityModule
],
providers: [{ provide: WINDOW, useValue: window }],