Commit 0fa19035 authored by Endika ESTEBAN's avatar Endika ESTEBAN
Browse files

test-betclic

parent 5daf6826
<h2>Tickets</h2> <router-outlet></router-outlet>
<ul>
<li *ngFor="let ticket of tickets$ | async">
Ticket: {{ ticket.id }}, {{ ticket.description }}
</li>
</ul>
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { AppComponent } from './app.component';
describe('AppComponent', () => {
let fixture: ComponentFixture<AppComponent>;
beforeEach(() => {
TestBed.configureTestingModule({
declarations: [AppComponent]
});
fixture = TestBed.createComponent(AppComponent);
});
it('should create the app', () => {
const app = fixture.debugElement.componentInstance;
expect(app).toBeTruthy();
});
it(`should have as title 'app'`, () => {
const app = fixture.debugElement.componentInstance;
expect(app.title).toEqual('app');
});
it('should render title in a h1 tag', () => {
fixture.detectChanges();
const compiled = fixture.debugElement.nativeElement;
expect(compiled.querySelector('h1').textContent).toContain('Welcome to app!');
});
});
import { Component } from '@angular/core'; import { Component } from '@angular/core';
import { Observable } from 'rxjs';
import { Ticket } from '../interfaces/ticket.interface';
import { User } from '../interfaces/user.interface';
import { BackendService } from './backend.service';
@Component({ @Component({
selector: 'app-root', selector: 'app-root',
templateUrl: './app.component.html', templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
}) })
export class AppComponent { export class AppComponent { }
public readonly users$: Observable<User[]> = this.backendService.users();
public readonly tickets$: Observable<Ticket[]> = this.backendService.tickets();
constructor(private readonly backendService: BackendService) {}
}
import { BrowserModule } from '@angular/platform-browser'; import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core'; import { NgModule } from '@angular/core';
import { AppComponent } from './app.component';
import { BackendService } from './backend.service'; import { BackendService } from './backend.service';
import { TicketListComponent } from './components/ticket-list/ticket-list.component';
import { AppComponent } from './app.component';
import { RouterModule, Routes } from '@angular/router';
import { TicketDetailComponent } from './components/ticket-detail/ticket-detail.component';
import { ReactiveFormsModule } from '@angular/forms';
import { UserComponent } from './components/user/user.component';
const routes: Routes = [
{ path: 'tickets', component: TicketListComponent },
{
path: 'ticket/:id',
component: TicketDetailComponent,
},
{ path: '**', redirectTo: 'tickets', pathMatch: 'full' },
];
@NgModule({ @NgModule({
declarations: [AppComponent], declarations: [AppComponent, TicketListComponent, TicketDetailComponent, UserComponent],
imports: [BrowserModule], imports: [
BrowserModule,
ReactiveFormsModule,
RouterModule.forRoot(routes)
],
providers: [BackendService], providers: [BackendService],
bootstrap: [AppComponent] bootstrap: [AppComponent]
}) })
......
...@@ -30,7 +30,10 @@ export class BackendService { ...@@ -30,7 +30,10 @@ export class BackendService {
} }
]; ];
public storedUsers: User[] = [{ id: 111, name: 'Victor' }]; public storedUsers: User[] = [
{ id: 111, name: 'Victor' },
{ id: 112, name: 'Marcos' }
];
private lastId: number = 1; private lastId: number = 1;
......
<ng-container *ngIf="ticket$ | async as ticket; else loading">
<a [routerLink]="['/tickets']">Back</a>
<h1>{{ ticket.id }}</h1>
<p>{{ ticket.status }}</p>
<h2>Assigned user</h2>
<user [id]="ticket.assigneeId"></user>
<form [formGroup]="editUserForm">
<div>
<input type="checkbox" formControlName="completed" />
<label for="completed">Completed</label>
</div>
<div>
<input type="checkbox" formControlName="edit" />
<label for="edit">Edit assigned user</label>
</div>
<div>
<select formControlName="user">
<option *ngFor="let user of (users$ | async)" [ngValue]="user.id">{{user.name}}</option>
</select>
</div>
</form>
</ng-container>
<ng-template #loading>
<p>Loading...</p>
</ng-template>
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { FormBuilder } from '@angular/forms';
import { ActivatedRoute, convertToParamMap, ParamMap } from '@angular/router';
import { scheduled, VirtualTimeScheduler } from 'rxjs';
import { BackendService } from 'src/app/backend.service';
import { Ticket } from 'src/interfaces/ticket.interface';
import { TicketDetailComponent } from './ticket-detail.component';
describe('TicketDetailComponent', () => {
let component: TicketDetailComponent;
let fixture: ComponentFixture<TicketDetailComponent>;
let mockBackendService: jasmine.SpyObj<BackendService>;
let activatedRouteScheduler: VirtualTimeScheduler;
let backendServiceScheduler: VirtualTimeScheduler;
beforeEach(async () => {
mockBackendService = jasmine.createSpyObj<BackendService>(['ticket']);
activatedRouteScheduler = new VirtualTimeScheduler();
backendServiceScheduler = new VirtualTimeScheduler();
const ticket: Ticket = {
assigneeId: 111, completed: false,
description: 'test ticket',
id: 1
}
mockBackendService.ticket.and.returnValue(scheduled([ticket], backendServiceScheduler));
await TestBed.configureTestingModule({
declarations: [TicketDetailComponent],
providers: [
{
provide: BackendService,
useValue: mockBackendService,
},
{
provide: ActivatedRoute,
useValue: {
paramMap: scheduled([convertToParamMap({
id: 1,
})], activatedRouteScheduler),
},
},
FormBuilder,
]
})
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(TicketDetailComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
describe('before backend responds', () => {
beforeEach(() => {
activatedRouteScheduler.flush();
fixture.detectChanges();
});
it('should display a loading message', () => {
const compiled = fixture.debugElement.nativeElement;
expect(compiled.querySelector('p').textContent).toContain('Loading...');
});
describe('when backend responds', () => {
beforeEach(() => {
backendServiceScheduler.flush();
fixture.detectChanges();
});
it('should not display a loading message', () => {
const compiled = fixture.debugElement.nativeElement;
expect(compiled.querySelector('p').textContent).not.toContain('Loading...');
});
it('should display ticket details', () => {
const compiled = fixture.debugElement.nativeElement;
expect(compiled.querySelector('h1').textContent).toContain('1');
expect(compiled.querySelector('p').textContent).toContain('test ticket');
});
});
});
});
\ No newline at end of file
import { Component, OnDestroy, OnInit } from '@angular/core';
import { FormBuilder, FormGroup } from '@angular/forms';
import { ActivatedRoute } from '@angular/router';
import { Observable, Subscription } from 'rxjs';
import { switchMap, tap } from 'rxjs/operators';
import { BackendService } from 'src/app/backend.service';
import { Ticket } from 'src/interfaces/ticket.interface';
import { User } from 'src/interfaces/user.interface';
@Component({
selector: 'ticket-detail',
templateUrl: './ticket-detail.component.html',
})
export class TicketDetailComponent implements OnInit, OnDestroy {
public ticket$: Observable<Ticket>;
public users$?: Observable<User[]>;
public editUserForm: FormGroup;
public subscriptions: Subscription[] = [];
constructor(
private readonly route: ActivatedRoute,
private readonly backendService: BackendService,
private readonly fb: FormBuilder,
) { }
ngOnInit() {
const fb = this.fb;
const backendService = this.backendService;
const subscriptions = this.subscriptions;
const userCtrl = fb.control(undefined);
const editCtrl = fb.control(false);
const completedCtrl = fb.control(false);
userCtrl.disable();
this.editUserForm = fb.group({
user: userCtrl,
edit: editCtrl,
completed: completedCtrl,
});
this.ticket$ = this.route.paramMap
.pipe(switchMap((paramMap) => {
const id = +paramMap.get('id')
return backendService.ticket(id)
}));
this.ticket$.subscribe({
next: ({id, completed}) => {
completedCtrl.setValue(completed);
subscriptions.push(userCtrl.valueChanges
.subscribe((userId: number) => backendService.assign(id, userId)
.subscribe()));
subscriptions.push(completedCtrl.valueChanges
.subscribe((completed: boolean) => backendService.complete(id, completed)
.subscribe()));
},
});
subscriptions.push(editCtrl.valueChanges.subscribe((edit: boolean) => {
if (edit && !this.users$) {
this.users$ = backendService.users();
}
edit ? userCtrl.enable({ emitEvent: false }) : userCtrl.disable({ emitEvent: false });
}))
}
ngOnDestroy() {
return this.subscriptions.map((s) => s.unsubscribe());
}
}
\ No newline at end of file
<h1>Tickets</h1>
<ng-container *ngIf="tickets$ | async as tickets; else loading">
<ul>
<li *ngFor="let ticket of tickets">
<p>Ticket: {{ ticket.id }}, {{ ticket.description }}</p>
<div>
<p>Assigned user : </p>
<user [id]="ticket.assigneeId"></user>
</div>
<a [routerLink]="['/ticket', ticket.id]">See more</a>
</li>
</ul>
</ng-container>
<ng-template #loading>
<p>Loading...</p>
</ng-template>
<form [formGroup]="newTicketForm" (ngSubmit)="newTicket()">
<div>
<input type="text" formControlName="description" required />
</div>
<button type="submit" [disabled]="!newTicketForm.valid">New ticket</button>
</form>
\ No newline at end of file
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { scheduled, VirtualTimeScheduler } from 'rxjs';
import { BackendService } from '../../backend.service';
import { Ticket } from '../../../interfaces/ticket.interface';
import { TicketListComponent } from './ticket-list.component';
import { FormBuilder } from '@angular/forms';
describe('TicketListComponent', () => {
let component: TicketListComponent;
let fixture: ComponentFixture<TicketListComponent>;
let mockBackendService: jasmine.SpyObj<BackendService>;
let scheduler: VirtualTimeScheduler;
beforeEach(async () => {
const ticket: Ticket = {
assigneeId: 111, completed: false,
description: 'test ticket',
id: 1
}
mockBackendService = jasmine.createSpyObj<BackendService>(['tickets']);
scheduler = new VirtualTimeScheduler();
mockBackendService.tickets.and.returnValue(scheduled([[ticket]], scheduler));
await TestBed.configureTestingModule({
declarations: [TicketListComponent],
providers: [
{
provide: BackendService,
useValue: mockBackendService,
},
FormBuilder,
]
})
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(TicketListComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
it('should display a title', () => {
const compiled = fixture.debugElement.nativeElement;
expect(compiled.querySelector('h1').textContent).toContain('Tickets');
});
it('should display a list of tickets', async () => {
scheduler.flush();
fixture.detectChanges();
const compiled = fixture.debugElement.nativeElement;
expect(compiled.querySelector('ul')).toBeTruthy();
expect(compiled.querySelector('li')).toBeTruthy();
expect(compiled.querySelector('li').textContent).toContain('test ticket');
});
});
import { Component } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { Observable } from 'rxjs';
import { BackendService } from 'src/app/backend.service';
import { Ticket } from 'src/interfaces/ticket.interface';
@Component({
selector: 'ticket-list',
templateUrl: './ticket-list.component.html',
})
export class TicketListComponent {
public tickets$: Observable<Ticket[]> = this.backendService.tickets();
public newTicketForm: FormGroup;
constructor(
private readonly backendService: BackendService,
fb: FormBuilder
) {
this.newTicketForm = fb.group({
description: ['', Validators.required],
});
}
public newTicket() {
this.newTicketForm.disable();
return this.backendService
.newTicket(this.newTicketForm.value)
.subscribe(() => {
this.newTicketForm.enable()
this.newTicketForm.reset()
});
}
}
<ng-container *ngIf="id; else noUserAssigned">
<ng-container *ngIf="(user$ | async) as user; else loading">
<p>{{user.name}}</p>
</ng-container>
</ng-container>
<ng-template #loading>
<p>Loading...</p>
</ng-template>
<ng-template #noUserAssigned>
<p>No user assigned</p>
</ng-template>
\ No newline at end of file
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { scheduled, VirtualTimeScheduler } from 'rxjs';
import { User } from 'src/interfaces/user.interface';
import { UserComponent } from './user.component';
import { BackendService } from '../../backend.service';
fdescribe('UserComponent', () => {
let component: UserComponent;
let fixture: ComponentFixture<UserComponent>;
let mockBackendService: jasmine.SpyObj<BackendService>;
let backendServiceScheduler: VirtualTimeScheduler;
beforeEach(async () => {
mockBackendService = jasmine.createSpyObj<BackendService>(['user']);
backendServiceScheduler = new VirtualTimeScheduler();
const user: User = {
id: 111,
name: 'test user',
}
await TestBed.configureTestingModule({
declarations: [UserComponent],
providers: [
{
provide: BackendService,
useValue: mockBackendService,
}
]
})
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(UserComponent);
component = fixture.componentInstance;
component.id = undefined;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
describe('when user id is not provided', () => {
it('should display a message', () => {
const compiled = fixture.debugElement.nativeElement;
expect(compiled.querySelector('p').textContent).toContain('No user assigned');
});
});
describe('when user id is provided', () => {
beforeEach(() => {
component.id = 1;
fixture.detectChanges();
})
it('should display a loading message', () => {
const compiled = fixture.debugElement.nativeElement;
expect(compiled.querySelector('p').textContent).toContain('Loading...');
});
it('should call the backend for user information', () => {
expect(mockBackendService.user).toHaveBeenCalledWith(1);
});
});
});
import { Component, Input, OnChanges, OnInit, SimpleChanges } from '@angular/core';
import { Observable } from 'rxjs';
import { BackendService } from 'src/app/backend.service';
import { User } from 'src/interfaces/user.interface';
@Component({
selector: 'user',
templateUrl: './user.component.html',
})
export class UserComponent implements OnChanges {
@Input()
public id: number;
public user$: Observable<User>;
constructor(private readonly backendService: BackendService) { }
ngOnChanges(changes: SimpleChanges) {
if (changes.id !== undefined) {
this.user$ = this.backendService.user(changes.id.currentValue);
}
}
}
export interface Ticket { export interface Ticket {
id: number; id: number;
completed: boolean; completed: boolean;
assigneeId: number; assigneeId?: number | null;
description: string; description: string;
} }
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment