Adds fake sign-in and sign-out components
This commit is contained in:
parent
4cf28994b6
commit
f9a47e36f7
|
@ -0,0 +1,4 @@
|
|||
// tslint:disable-next-line:max-line-length
|
||||
export const EMAIL_REGEX = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/i;
|
||||
|
||||
export default EMAIL_REGEX;
|
|
@ -0,0 +1,36 @@
|
|||
import {FormControl} from '@angular/forms';
|
||||
import {ValidateEmail} from './email.validator';
|
||||
|
||||
describe('ValidateEmail', () => {
|
||||
const control = new FormControl();
|
||||
|
||||
it('should return an error for an invalid email address', () => {
|
||||
const emailsToTest = [
|
||||
'124535',
|
||||
'not an email address',
|
||||
'@gmail.com',
|
||||
'incomplete@domain',
|
||||
'tooshort@tld.c',
|
||||
];
|
||||
|
||||
for (const email of emailsToTest) {
|
||||
control.setValue(email);
|
||||
expect(ValidateEmail(control)).toEqual({email: true});
|
||||
}
|
||||
});
|
||||
|
||||
it('should not return an error for a valid email address', () => {
|
||||
const emailsToTest = [
|
||||
'short@tld.co',
|
||||
'simple@email.edu',
|
||||
'more+complicated@www.email-server.mail',
|
||||
'this.is.a.valid.email+address@some-random.email-server.com',
|
||||
];
|
||||
|
||||
for (const email of emailsToTest) {
|
||||
control.setValue(email);
|
||||
expect(ValidateEmail(control)).toBeUndefined();
|
||||
}
|
||||
});
|
||||
|
||||
});
|
|
@ -0,0 +1,9 @@
|
|||
import { AbstractControl, ValidationErrors } from '@angular/forms';
|
||||
import EMAIL_REGEX from './email.regex';
|
||||
|
||||
export function ValidateEmail(control: AbstractControl): ValidationErrors {
|
||||
if (!EMAIL_REGEX.test(control.value) && control.value && control.value !== '') {
|
||||
const error: ValidationErrors = { email: true };
|
||||
return error;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,142 @@
|
|||
import {FormControl} from '@angular/forms';
|
||||
import {FormlyFieldConfig} from '@ngx-formly/core';
|
||||
import {FieldType} from '@ngx-formly/material';
|
||||
import * as Validators from './formly.validator';
|
||||
|
||||
describe('Formly Validators', () => {
|
||||
let control: FormControl;
|
||||
let err: Error;
|
||||
let field: FormlyFieldConfig;
|
||||
|
||||
beforeEach(() => {
|
||||
control = new FormControl();
|
||||
err = new Error('some error');
|
||||
field = {
|
||||
type: 'email',
|
||||
templateOptions: {},
|
||||
formControl: control,
|
||||
};
|
||||
});
|
||||
|
||||
it('should validate emails', () => {
|
||||
control.setValue('');
|
||||
expect(Validators.EmailValidator(control)).toBeNull();
|
||||
|
||||
control.setValue('a@b');
|
||||
expect(Validators.EmailValidator(control)).toEqual({email: true});
|
||||
|
||||
control.setValue('a@b.c');
|
||||
expect(Validators.EmailValidator(control)).toEqual({email: true});
|
||||
|
||||
control.setValue('a@b.com');
|
||||
expect(Validators.EmailValidator(control)).toBeNull();
|
||||
});
|
||||
|
||||
it('should email validator message', () => {
|
||||
control.setValue('bad_email');
|
||||
expect(Validators.EmailValidatorMessage(err, field)).toContain('bad_email');
|
||||
});
|
||||
|
||||
it('should validate URL strings', () => {
|
||||
const badUrls = [
|
||||
'bad_url',
|
||||
'http://',
|
||||
'http://bad',
|
||||
'bad.com',
|
||||
];
|
||||
badUrls.forEach(url => {
|
||||
control.setValue(url);
|
||||
expect(Validators.UrlValidator(control)).toEqual({url: true});
|
||||
});
|
||||
|
||||
const goodUrls = [
|
||||
'http://good.url.com',
|
||||
'ftp://who-uses.ftp-these-days.com',
|
||||
'https://this.is.actually:5432/a/valid/url?doncha=know#omg',
|
||||
];
|
||||
goodUrls.forEach(url => {
|
||||
control.setValue(url);
|
||||
expect(Validators.UrlValidator(control)).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
it('should return a url validator message', () => {
|
||||
control.setValue('bad_url');
|
||||
expect(Validators.UrlValidatorMessage(err, field)).toContain('bad_url');
|
||||
});
|
||||
|
||||
it('should validate phone numbers', () => {
|
||||
const badPhones = [
|
||||
'not a phone number',
|
||||
'54321',
|
||||
'3.1415926535897932384...',
|
||||
'(800) CALL-BRAD',
|
||||
'(540) 155-5555',
|
||||
'(999) 654-3210',
|
||||
'1234567890',
|
||||
];
|
||||
badPhones.forEach(phone => {
|
||||
control.setValue(phone);
|
||||
expect(Validators.PhoneValidator(control)).toEqual({phone: true});
|
||||
});
|
||||
|
||||
const goodPhones = [
|
||||
'12345678909',
|
||||
'1-800-555-5555',
|
||||
'(987) 654-3210',
|
||||
'(540) 555-1212 x321',
|
||||
'(540) 555-1212 ext 321',
|
||||
'(540) 555-1212 Ext. 321',
|
||||
'540.555.5555',
|
||||
'540.555.5555extension321',
|
||||
];
|
||||
goodPhones.forEach(phone => {
|
||||
control.setValue(phone);
|
||||
expect(Validators.PhoneValidator(control)).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
it('should return phone validator message', () => {
|
||||
control.setValue('bad_number');
|
||||
expect(Validators.PhoneValidatorMessage(err, field)).toContain('bad_number');
|
||||
});
|
||||
|
||||
it('should return error if at least one checkbox in multicheckbox is not checked', () => {
|
||||
control.setValue({});
|
||||
expect(Validators.MulticheckboxValidator(control)).toEqual({required: true});
|
||||
|
||||
control.setValue({
|
||||
checkbox_a: true,
|
||||
checkbox_b: false,
|
||||
checkbox_c: false,
|
||||
});
|
||||
expect(Validators.MulticheckboxValidator(control)).toBeNull();
|
||||
});
|
||||
|
||||
it('should return multicheckbox validator message', () => {
|
||||
control.setValue({});
|
||||
expect(Validators.MulticheckboxValidatorMessage(err, field))
|
||||
.toEqual('At least one of these checkboxes must be selected.');
|
||||
});
|
||||
|
||||
it('should min validation message', () => {
|
||||
field.templateOptions.min = 42;
|
||||
expect(Validators.MinValidationMessage(err, field)).toContain('42');
|
||||
});
|
||||
|
||||
it('should max validation message', () => {
|
||||
field.templateOptions.max = 42;
|
||||
expect(Validators.MaxValidationMessage(err, field)).toContain('42');
|
||||
});
|
||||
|
||||
it('should show error', () => {
|
||||
expect(Validators.ShowError(field as FieldType)).toBeFalsy();
|
||||
|
||||
control.setErrors({url: true});
|
||||
control.markAsDirty();
|
||||
expect(Validators.ShowError(field as FieldType)).toBeTruthy();
|
||||
|
||||
field.formControl = undefined;
|
||||
expect(Validators.ShowError(field as FieldType)).toBeFalsy();
|
||||
});
|
||||
});
|
|
@ -0,0 +1,62 @@
|
|||
import {FormControl, ValidationErrors} from '@angular/forms';
|
||||
import {FieldType, FormlyFieldConfig} from '@ngx-formly/core';
|
||||
import EMAIL_REGEX from './email.regex';
|
||||
import PHONE_REGEX from './phone.regex';
|
||||
import URL_REGEX from './url.regex';
|
||||
|
||||
export function EmailValidator(control: FormControl): ValidationErrors {
|
||||
return !control.value || EMAIL_REGEX.test(control.value) ? null : {email: true};
|
||||
}
|
||||
|
||||
export function EmailValidatorMessage(err, field: FormlyFieldConfig) {
|
||||
return `"${field.formControl.value}" is not a valid email address`;
|
||||
}
|
||||
|
||||
export function UrlValidator(control: FormControl): ValidationErrors {
|
||||
return !control.value || URL_REGEX.test(control.value) ? null : {url: true};
|
||||
}
|
||||
|
||||
export function UrlValidatorMessage(err, field: FormlyFieldConfig) {
|
||||
return `We cannot save "${field.formControl.value}". Please provide the full path, including http:// or https://`;
|
||||
}
|
||||
|
||||
export function PhoneValidator(control: FormControl): ValidationErrors {
|
||||
return !control.value || PHONE_REGEX.test(control.value) ? null : {phone: true};
|
||||
}
|
||||
|
||||
export function PhoneValidatorMessage(err, field: FormlyFieldConfig) {
|
||||
return `"${field.formControl.value}" is not a valid phone number`;
|
||||
}
|
||||
|
||||
export function MulticheckboxValidator(control: FormControl): ValidationErrors {
|
||||
if (control.value) {
|
||||
for (const key in control.value) {
|
||||
if (control.value[key] === true) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
return {required: true};
|
||||
}
|
||||
|
||||
export function MulticheckboxValidatorMessage(err, field: FormlyFieldConfig) {
|
||||
return 'At least one of these checkboxes must be selected.';
|
||||
}
|
||||
|
||||
export function MinValidationMessage(err, field) {
|
||||
return `This value should be more than ${field.templateOptions.min}`;
|
||||
}
|
||||
|
||||
export function MaxValidationMessage(err, field) {
|
||||
return `This value should be less than ${field.templateOptions.max}`;
|
||||
}
|
||||
|
||||
export function ShowError(field: FieldType) {
|
||||
return field.formControl &&
|
||||
field.formControl.invalid &&
|
||||
(
|
||||
field.formControl.dirty ||
|
||||
(field.options && field.options.parentForm && field.options.parentForm.submitted) ||
|
||||
(field.field && field.field.validation && field.field.validation.show)
|
||||
);
|
||||
}
|
|
@ -1,7 +1,9 @@
|
|||
import {NgModule} from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import {RouterModule, Routes} from '@angular/router';
|
||||
import {SessionRedirectComponent} from 'sartography-workflow-lib';
|
||||
import {ModelerComponent} from './modeler/modeler.component';
|
||||
import {SignInComponent} from './sign-in/sign-in.component';
|
||||
import {SignOutComponent} from './sign-out/sign-out.component';
|
||||
import {WorkflowSpecListComponent} from './workflow-spec-list/workflow-spec-list.component';
|
||||
|
||||
|
||||
|
@ -9,6 +11,18 @@ const appRoutes: Routes = [
|
|||
{path: 'modeler/:workflowSpecId', component: ModelerComponent},
|
||||
{path: 'modeler/:workflowSpecId/:fileMetaId', component: ModelerComponent},
|
||||
{path: '', component: WorkflowSpecListComponent},
|
||||
{
|
||||
path: 'sign-in',
|
||||
component: SignInComponent
|
||||
},
|
||||
{
|
||||
path: 'sign-out',
|
||||
component: SignOutComponent
|
||||
},
|
||||
{
|
||||
path: 'session/:token',
|
||||
component: SessionRedirectComponent
|
||||
}
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
|
@ -18,4 +32,5 @@ const appRoutes: Routes = [
|
|||
],
|
||||
exports: [RouterModule]
|
||||
})
|
||||
export class AppRoutingModule { }
|
||||
export class AppRoutingModule {
|
||||
}
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
import {HttpClientModule} from '@angular/common/http';
|
||||
import {NgModule} from '@angular/core';
|
||||
import {HTTP_INTERCEPTORS, HttpClientModule} from '@angular/common/http';
|
||||
import {Injectable, NgModule} from '@angular/core';
|
||||
import {FlexLayoutModule} from '@angular/flex-layout';
|
||||
import {FormsModule, ReactiveFormsModule} from '@angular/forms';
|
||||
import {MatButtonModule} from '@angular/material/button';
|
||||
import {MatCardModule} from '@angular/material/card';
|
||||
import {MatDialogModule} from '@angular/material/dialog';
|
||||
import {MatDividerModule} from '@angular/material/divider';
|
||||
import {MAT_FORM_FIELD_DEFAULT_OPTIONS} from '@angular/material/form-field';
|
||||
import {MatIconModule} from '@angular/material/icon';
|
||||
import {MatInputModule} from '@angular/material/input';
|
||||
import {MatListModule} from '@angular/material/list';
|
||||
|
@ -18,7 +19,7 @@ import {BrowserModule} from '@angular/platform-browser';
|
|||
import {BrowserAnimationsModule} from '@angular/platform-browser/animations';
|
||||
import {FormlyModule} from '@ngx-formly/core';
|
||||
import {FormlyMaterialModule} from '@ngx-formly/material';
|
||||
import {AppEnvironment} from 'sartography-workflow-lib';
|
||||
import {AppEnvironment, AuthInterceptor} from 'sartography-workflow-lib';
|
||||
import {environment} from '../environments/environment';
|
||||
import {DeleteFileDialogComponent} from './_dialogs/delete-file-dialog/delete-file-dialog.component';
|
||||
import {DeleteWorkflowSpecDialogComponent} from './_dialogs/delete-workflow-spec-dialog/delete-workflow-spec-dialog.component';
|
||||
|
@ -26,12 +27,15 @@ import {FileMetaDialogComponent} from './_dialogs/file-meta-dialog/file-meta-dia
|
|||
import {NewFileDialogComponent} from './_dialogs/new-file-dialog/new-file-dialog.component';
|
||||
import {OpenFileDialogComponent} from './_dialogs/open-file-dialog/open-file-dialog.component';
|
||||
import {WorkflowSpecDialogComponent} from './_dialogs/workflow-spec-dialog/workflow-spec-dialog.component';
|
||||
import {EmailValidator, EmailValidatorMessage, ShowError} from './_forms/validators/formly.validator';
|
||||
import {GetIconCodePipe} from './_pipes/get-icon-code.pipe';
|
||||
import {AppRoutingModule} from './app-routing.module';
|
||||
import {AppComponent} from './app.component';
|
||||
import {DiagramComponent} from './diagram/diagram.component';
|
||||
import {FileListComponent} from './file-list/file-list.component';
|
||||
import {ModelerComponent} from './modeler/modeler.component';
|
||||
import {SignInComponent} from './sign-in/sign-in.component';
|
||||
import {SignOutComponent} from './sign-out/sign-out.component';
|
||||
import {WorkflowSpecListComponent} from './workflow-spec-list/workflow-spec-list.component';
|
||||
|
||||
export class ThisEnvironment implements AppEnvironment {
|
||||
|
@ -41,6 +45,22 @@ export class ThisEnvironment implements AppEnvironment {
|
|||
irbUrl = environment.irbUrl;
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class AppFormlyConfig {
|
||||
public static config = {
|
||||
extras: {
|
||||
showError: ShowError,
|
||||
},
|
||||
validators: [
|
||||
{name: 'email', validation: EmailValidator},
|
||||
],
|
||||
validationMessages: [
|
||||
{name: 'email', message: EmailValidatorMessage},
|
||||
{name: 'required', message: 'This field is required.'},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
AppComponent,
|
||||
|
@ -53,6 +73,8 @@ export class ThisEnvironment implements AppEnvironment {
|
|||
ModelerComponent,
|
||||
NewFileDialogComponent,
|
||||
OpenFileDialogComponent,
|
||||
SignInComponent,
|
||||
SignOutComponent,
|
||||
WorkflowSpecDialogComponent,
|
||||
WorkflowSpecListComponent,
|
||||
],
|
||||
|
@ -61,11 +83,7 @@ export class ThisEnvironment implements AppEnvironment {
|
|||
BrowserModule,
|
||||
FlexLayoutModule,
|
||||
FormlyMaterialModule,
|
||||
FormlyModule.forRoot({
|
||||
validationMessages: [
|
||||
{name: 'required', message: 'This field is required'},
|
||||
],
|
||||
}),
|
||||
FormlyModule.forRoot(AppFormlyConfig.config),
|
||||
FormsModule,
|
||||
HttpClientModule,
|
||||
MatButtonModule,
|
||||
|
@ -92,7 +110,15 @@ export class ThisEnvironment implements AppEnvironment {
|
|||
OpenFileDialogComponent,
|
||||
WorkflowSpecDialogComponent,
|
||||
],
|
||||
providers: [{provide: 'APP_ENVIRONMENT', useClass: ThisEnvironment}]
|
||||
providers: [
|
||||
{provide: MAT_FORM_FIELD_DEFAULT_OPTIONS, useValue: {appearance: 'fill'}},
|
||||
{provide: 'APP_ENVIRONMENT', useClass: ThisEnvironment},
|
||||
{
|
||||
provide: HTTP_INTERCEPTORS,
|
||||
useClass: AuthInterceptor,
|
||||
multi: true
|
||||
},
|
||||
]
|
||||
})
|
||||
export class AppModule {
|
||||
}
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
<div class="full-height" fxLayout="column" fxLayoutAlign="center center">
|
||||
<h1>Fake UVA NetBadge Sign In (for testing only)</h1>
|
||||
<formly-form [fields]="fields" [form]="form" [model]="model">
|
||||
<mat-error *ngIf="error">{{error}}</mat-error>
|
||||
<button
|
||||
(click)="signIn()"
|
||||
[disabled]="form.invalid"
|
||||
color="primary"
|
||||
id="sign_in"
|
||||
mat-flat-button
|
||||
>
|
||||
Sign in
|
||||
</button>
|
||||
</formly-form>
|
||||
</div>
|
|
@ -0,0 +1,9 @@
|
|||
form {
|
||||
min-width: 150px;
|
||||
max-width: 500px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.full-width {
|
||||
width: 100%;
|
||||
}
|
|
@ -0,0 +1,100 @@
|
|||
import {HttpClientTestingModule, HttpTestingController} from '@angular/common/http/testing';
|
||||
import {async, ComponentFixture, TestBed} from '@angular/core/testing';
|
||||
import {FormsModule} from '@angular/forms';
|
||||
import {MatFormFieldModule} from '@angular/material/form-field';
|
||||
import {MatInputModule} from '@angular/material/input';
|
||||
import {BrowserAnimationsModule, NoopAnimationsModule} from '@angular/platform-browser/animations';
|
||||
import {ActivatedRoute, convertToParamMap, Router} from '@angular/router';
|
||||
import {RouterTestingModule} from '@angular/router/testing';
|
||||
import {FormlyModule} from '@ngx-formly/core';
|
||||
import {FormlyMaterialModule} from '@ngx-formly/material';
|
||||
import {of} from 'rxjs';
|
||||
import {ApiService, MockEnvironment, mockUser} from 'sartography-workflow-lib';
|
||||
import {EmailValidator, EmailValidatorMessage} from '../_forms/validators/formly.validator';
|
||||
import {SignInComponent} from './sign-in.component';
|
||||
|
||||
describe('SignInComponent', () => {
|
||||
let component: SignInComponent;
|
||||
let fixture: ComponentFixture<SignInComponent>;
|
||||
let httpMock: HttpTestingController;
|
||||
const mockRouter = {navigate: jasmine.createSpy('navigate')};
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [SignInComponent],
|
||||
imports: [
|
||||
BrowserAnimationsModule,
|
||||
FormlyModule.forRoot({
|
||||
validators: [
|
||||
{name: 'email', validation: EmailValidator},
|
||||
],
|
||||
validationMessages: [
|
||||
{name: 'email', message: EmailValidatorMessage},
|
||||
],
|
||||
}),
|
||||
FormlyMaterialModule,
|
||||
HttpClientTestingModule,
|
||||
FormsModule,
|
||||
MatFormFieldModule,
|
||||
MatInputModule,
|
||||
NoopAnimationsModule,
|
||||
RouterTestingModule,
|
||||
],
|
||||
providers: [
|
||||
ApiService,
|
||||
{
|
||||
provide: ActivatedRoute,
|
||||
useValue: {paramMap: of(convertToParamMap({study_id: '0', workflow_id: '0', task_id: '0'}))}
|
||||
},
|
||||
{
|
||||
provide: Router,
|
||||
useValue: mockRouter
|
||||
},
|
||||
{provide: 'APP_ENVIRONMENT', useClass: MockEnvironment},
|
||||
],
|
||||
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
httpMock = TestBed.get(HttpTestingController);
|
||||
fixture = TestBed.createComponent(SignInComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
httpMock.verify();
|
||||
fixture.destroy();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should fake sign in during testing', () => {
|
||||
const openSessionSpy = spyOn((component as any).api, 'openSession').and.stub();
|
||||
(component as any).environment.production = false;
|
||||
component.model = mockUser;
|
||||
component.signIn();
|
||||
expect(openSessionSpy).toHaveBeenCalledWith(mockUser);
|
||||
expect(component.error).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should display an error if sign in is called on production', () => {
|
||||
const openSessionSpy = spyOn((component as any).api, 'openSession').and.stub();
|
||||
(component as any).environment.production = true;
|
||||
component.signIn();
|
||||
expect(openSessionSpy).not.toHaveBeenCalled();
|
||||
expect(component.error).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should verify the user and redirect to home page on production', () => {
|
||||
const getUserSpy = spyOn((component as any).api, 'getUser').and.returnValue(of(mockUser));
|
||||
(component as any).environment.production = true;
|
||||
(component as any)._redirectOnProduction();
|
||||
expect(getUserSpy).toHaveBeenCalled();
|
||||
expect(mockRouter.navigate).toHaveBeenCalledWith(['/']);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,93 @@
|
|||
import {Component, Inject, OnInit} from '@angular/core';
|
||||
import {FormGroup} from '@angular/forms';
|
||||
import {Router} from '@angular/router';
|
||||
import {FormlyFieldConfig} from '@ngx-formly/core';
|
||||
import {ApiService, AppEnvironment, User} from 'sartography-workflow-lib';
|
||||
|
||||
@Component({
|
||||
selector: 'app-sign-in',
|
||||
templateUrl: './sign-in.component.html',
|
||||
styleUrls: ['./sign-in.component.scss']
|
||||
})
|
||||
export class SignInComponent implements OnInit {
|
||||
form = new FormGroup({});
|
||||
model: any = {};
|
||||
fields: FormlyFieldConfig[] = [
|
||||
{
|
||||
key: 'uid',
|
||||
type: 'input',
|
||||
defaultValue: 'czn1z',
|
||||
templateOptions: {
|
||||
required: true,
|
||||
label: 'UVA Computing ID',
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'email_address',
|
||||
type: 'input',
|
||||
defaultValue: 'czn1z@virginia.edu',
|
||||
templateOptions: {
|
||||
required: true,
|
||||
type: 'email',
|
||||
label: 'UVA Email Address',
|
||||
},
|
||||
validators: {validation: ['email']},
|
||||
},
|
||||
{
|
||||
key: 'first_name',
|
||||
type: 'input',
|
||||
defaultValue: 'Cordi',
|
||||
templateOptions: {
|
||||
label: 'First Name',
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'last_name',
|
||||
type: 'input',
|
||||
defaultValue: 'Nator',
|
||||
templateOptions: {
|
||||
label: 'First Name',
|
||||
},
|
||||
},
|
||||
];
|
||||
error: Error;
|
||||
|
||||
|
||||
constructor(
|
||||
@Inject('APP_ENVIRONMENT') private environment: AppEnvironment,
|
||||
private router: Router,
|
||||
private api: ApiService
|
||||
) {
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this._redirectOnProduction();
|
||||
}
|
||||
|
||||
signIn() {
|
||||
this.error = undefined;
|
||||
localStorage.removeItem('token');
|
||||
|
||||
// For testing purposes, create a user to simulate login.
|
||||
if (!this.environment.production) {
|
||||
this.api.openSession(this.model);
|
||||
} else {
|
||||
this.error = new Error('This feature does not work in production.');
|
||||
}
|
||||
}
|
||||
|
||||
// If this is production, verify the user and redirect to home page.
|
||||
private _redirectOnProduction() {
|
||||
if (this.environment.production) {
|
||||
this.api.getUser().subscribe((user: User) => {
|
||||
this.router.navigate(['/']);
|
||||
}, e => {
|
||||
this.error = e;
|
||||
localStorage.removeItem('token');
|
||||
this.router.navigate(['/']);
|
||||
});
|
||||
} else {
|
||||
localStorage.removeItem('token');
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
<div class="full-height" fxLayout="column" fxLayoutAlign="center center">
|
||||
<h1>You have been signed out.</h1>
|
||||
<button
|
||||
mat-flat-button
|
||||
color="accent"
|
||||
(click)="goHome()">Ok</button>
|
||||
</div>
|
|
@ -0,0 +1,49 @@
|
|||
import {HttpClientTestingModule, HttpTestingController} from '@angular/common/http/testing';
|
||||
import {async, ComponentFixture, TestBed} from '@angular/core/testing';
|
||||
import {RouterTestingModule} from '@angular/router/testing';
|
||||
import {ApiService, MockEnvironment} from 'sartography-workflow-lib';
|
||||
|
||||
import {SignOutComponent} from './sign-out.component';
|
||||
|
||||
describe('SignOutComponent', () => {
|
||||
let component: SignOutComponent;
|
||||
let fixture: ComponentFixture<SignOutComponent>;
|
||||
let httpMock: HttpTestingController;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [SignOutComponent],
|
||||
imports: [
|
||||
HttpClientTestingModule,
|
||||
RouterTestingModule,
|
||||
],
|
||||
providers: [
|
||||
ApiService,
|
||||
{provide: 'APP_ENVIRONMENT', useClass: MockEnvironment},
|
||||
],
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
httpMock = TestBed.get(HttpTestingController);
|
||||
fixture = TestBed.createComponent(SignOutComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
httpMock.verify();
|
||||
fixture.destroy();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should go home', () => {
|
||||
const openUrlSpy = spyOn((component as any).api, 'openUrl').and.stub();
|
||||
component.goHome();
|
||||
expect(openUrlSpy).toHaveBeenCalledWith('/');
|
||||
});
|
||||
});
|
|
@ -0,0 +1,21 @@
|
|||
import {Component, OnInit} from '@angular/core';
|
||||
import {ApiService} from 'sartography-workflow-lib';
|
||||
|
||||
@Component({
|
||||
selector: 'app-sign-out',
|
||||
templateUrl: './sign-out.component.html',
|
||||
styleUrls: ['./sign-out.component.scss']
|
||||
})
|
||||
export class SignOutComponent implements OnInit {
|
||||
|
||||
constructor(private api: ApiService) {
|
||||
localStorage.removeItem('token');
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
}
|
||||
|
||||
goHome() {
|
||||
this.api.openUrl('/');
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue