diff --git a/src/app/configs/configs-routing.module.ts b/src/app/configs/configs-routing.module.ts index 2fcdf80e..3ff664e8 100644 --- a/src/app/configs/configs-routing.module.ts +++ b/src/app/configs/configs-routing.module.ts @@ -1,6 +1,6 @@ import { NgModule } from "@angular/core"; import { RouterModule, Routes } from "@angular/router"; -import { AuthGuardService } from "../shared"; +import { AuthGuardService } from "../shared/services/auth-guard.service"; import { ConfigsComponent } from "./configs.component"; const routes: Routes = [ diff --git a/src/app/configs/transport-providers/tranport-providers-list/transport-provider-list.component.html b/src/app/configs/transport-providers/tranport-providers-list/transport-provider-list.component.html index af2f591b..cd74e10e 100644 --- a/src/app/configs/transport-providers/tranport-providers-list/transport-provider-list.component.html +++ b/src/app/configs/transport-providers/tranport-providers-list/transport-provider-list.component.html @@ -1,3 +1,5 @@ - + (selectedRecord)="onRowSelect($event)" +> diff --git a/src/app/configs/transport-providers/tranport-providers-list/transport-provider-list.component.ts b/src/app/configs/transport-providers/tranport-providers-list/transport-provider-list.component.ts index 0ce3cf63..6ca4ca8e 100644 --- a/src/app/configs/transport-providers/tranport-providers-list/transport-provider-list.component.ts +++ b/src/app/configs/transport-providers/tranport-providers-list/transport-provider-list.component.ts @@ -1,6 +1,6 @@ import { Component, OnInit } from "@angular/core"; +import { Router } from "@angular/router"; import { Observable } from "rxjs"; -import { filter, first } from "rxjs/operators"; import { TransportProvider } from "src/app/online-orders/shared"; import { TransportProvidersService } from "src/app/online-orders/transport-providers.service"; @@ -13,17 +13,18 @@ export class TransportProviderListComponent implements OnInit { public transportProviders$: Observable; public excludeCols: string[] = ["availabilityRules"]; - constructor(private service: TransportProvidersService) {} + constructor( + private service: TransportProvidersService, + private router: Router + ) {} - handleRowSelect(selectedRecord: TransportProvider): void { - // navigate to single record - console.log(selectedRecord); + async onRowSelect(selectedRecord: TransportProvider): Promise { + await this.router.navigate([ + `configuration/transport-providers/${selectedRecord.id}`, + ]); } ngOnInit(): void { this.transportProviders$ = this.service.getTransportProviders(); - - // filter((x) => x.length !== 0 || !x) - // first() } } diff --git a/src/app/configs/transport-providers/transport-providers-container.component.ts b/src/app/configs/transport-providers/transport-providers-container.component.ts new file mode 100644 index 00000000..861c787b --- /dev/null +++ b/src/app/configs/transport-providers/transport-providers-container.component.ts @@ -0,0 +1,25 @@ +import { Component } from "@angular/core"; + +@Component({ + selector: "app-transport-providers-container", + template: ` + +
+ +
+ +
+
+ + `, + styles: [], +}) +export class TransportProvidersContainerComponent {} diff --git a/src/app/configs/transport-providers/transport-providers-edit/transport-providers-edit.component.css b/src/app/configs/transport-providers/transport-providers-edit/transport-providers-edit.component.css index e69de29b..c73c0466 100644 --- a/src/app/configs/transport-providers/transport-providers-edit/transport-providers-edit.component.css +++ b/src/app/configs/transport-providers/transport-providers-edit/transport-providers-edit.component.css @@ -0,0 +1,4 @@ +:host ::ng-deep .p-chip.custom-chip { + background: var(--primary-color); + color: var(--primary-color-text); +} diff --git a/src/app/configs/transport-providers/transport-providers-edit/transport-providers-edit.component.html b/src/app/configs/transport-providers/transport-providers-edit/transport-providers-edit.component.html index 90965192..36784441 100644 --- a/src/app/configs/transport-providers/transport-providers-edit/transport-providers-edit.component.html +++ b/src/app/configs/transport-providers/transport-providers-edit/transport-providers-edit.component.html @@ -1 +1,164 @@ -

transport-providers-edit works!

+ + + + + + + {{ r.text }} + + + + {{ tpHelperText }} + +
+
+
+
+ + + + + Key is required + No spaces or upper-case letters +
+ +
+ + + + + Text is required +
+
+
+
+ + +
+ + +
+ +
+ + +
+
+
+
+ +
+
+
+ Record Id +
+
+ + {{ r.id }} + +
+
+ + + +
+
+ Created By +
+ {{ + r.createdby + }} +
+ + + +
+
+ Date Created +
+ {{ + r.datecreated | date: "short" + }} +
+ + + +
+
+ Updated By +
+ + {{ r.updatedby }} +
+ + + +
+
+ Date Updated +
+ + {{ r.dateupdated | date: "short" }} + +
+
+
+
+
+ + +

+ Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium + doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo + inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo. + Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut + fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem + sequi nesciunt. Consectetur, adipisci velit, sed quia non numquam eius + modi. +

+
+
diff --git a/src/app/configs/transport-providers/transport-providers-edit/transport-providers-edit.component.spec.ts b/src/app/configs/transport-providers/transport-providers-edit/transport-providers-edit.component.spec.ts index a85fffc5..b500430b 100644 --- a/src/app/configs/transport-providers/transport-providers-edit/transport-providers-edit.component.spec.ts +++ b/src/app/configs/transport-providers/transport-providers-edit/transport-providers-edit.component.spec.ts @@ -1,14 +1,51 @@ +/* eslint-disable @typescript-eslint/no-unsafe-member-access */ +import { HttpClientTestingModule } from "@angular/common/http/testing"; import { ComponentFixture, TestBed } from "@angular/core/testing"; +import { FormsModule } from "@angular/forms"; +import { By } from "@angular/platform-browser"; +import { ActivatedRoute, Params } from "@angular/router"; +import { RouterTestingModule } from "@angular/router/testing"; +import { ConfirmationService } from "primeng/api"; +import { BehaviorSubject } from "rxjs"; +import { TransportProvidersService } from "src/app/online-orders/transport-providers.service"; +import { TransportProvidersServiceSpy } from "src/app/shared/testing"; import { TransportProvidersEditComponent } from "./transport-providers-edit.component"; describe("TransportProvidersEditComponent", () => { let component: TransportProvidersEditComponent; let fixture: ComponentFixture; + let confirmationSpy: jasmine.SpyObj; + const routeChangeSource: BehaviorSubject = + new BehaviorSubject({}); beforeEach(async () => { + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment + confirmationSpy = jasmine.createSpyObj("ConfirmationService", ["confirm"]); + await TestBed.configureTestingModule({ + imports: [ + HttpClientTestingModule, + RouterTestingModule.withRoutes([]), + FormsModule, + ], declarations: [TransportProvidersEditComponent], + providers: [ + { + provide: TransportProvidersService, + useClass: TransportProvidersServiceSpy, + }, + { + provide: ConfirmationService, + useValue: confirmationSpy, + }, + { + provide: ActivatedRoute, + useValue: { + params: routeChangeSource.asObservable(), + }, + }, + ], }).compileComponents(); }); @@ -21,4 +58,29 @@ describe("TransportProvidersEditComponent", () => { it("should create", () => { expect(component).toBeTruthy(); }); + + it("valid id route should load form with record", () => { + routeChangeSource.next({ id: "1" }); + fixture.detectChanges(); + fixture + .whenStable() + .then(() => { + const key = fixture.debugElement.query(By.css("#key")); + expect(key.nativeElement.value).toBe("one"); + expect(key.nativeElement.value).not.toBe(""); + }) + .catch(() => console.log("")); + }); + + it("NEW route should load empty form", () => { + routeChangeSource.next({ id: "new" }); + fixture.detectChanges(); + fixture + .whenStable() + .then(() => { + const key = fixture.debugElement.query(By.css("#key")); + expect(key.nativeElement.value).toBe(""); + }) + .catch(() => console.log("")); + }); }); diff --git a/src/app/configs/transport-providers/transport-providers-edit/transport-providers-edit.component.ts b/src/app/configs/transport-providers/transport-providers-edit/transport-providers-edit.component.ts index 29266287..5bf60ae6 100644 --- a/src/app/configs/transport-providers/transport-providers-edit/transport-providers-edit.component.ts +++ b/src/app/configs/transport-providers/transport-providers-edit/transport-providers-edit.component.ts @@ -1,8 +1,89 @@ import { Component, OnInit } from "@angular/core"; +import { ActivatedRoute, Router } from "@angular/router"; +import { merge, Observable, of, Subject } from "rxjs"; +import { pluck, switchMap, tap } from "rxjs/operators"; +import { TransportProvider } from "src/app/online-orders/shared"; +import { TransportProvidersService } from "src/app/online-orders/transport-providers.service"; + +import { ConfirmationService } from "primeng/api"; @Component({ selector: "app-transport-providers-edit", templateUrl: "./transport-providers-edit.component.html", styleUrls: ["./transport-providers-edit.component.css"], }) -export class TransportProvidersEditComponent {} +export class TransportProvidersEditComponent implements OnInit { + private routeRecordId: number; + + public record$: Observable; + public activeTab = "register"; + public tpHelperText: string; + private updatedRecord$: Subject = + new Subject(); + private initialData$ = this.activatedRoute.params.pipe( + pluck("id"), + switchMap((id: string) => { + if (id === "new") { + return of(new TransportProvider()); + } + const routeParam = parseInt(id); + this.routeRecordId = routeParam; + return this.tpService.getById(routeParam); + }), + tap(() => console.log("new subscription")) + ); + + constructor( + private activatedRoute: ActivatedRoute, + private router: Router, + private tpService: TransportProvidersService, + private primeConfirmService: ConfirmationService + ) {} + + ngOnInit(): void { + this.tpHelperText = `Transport providers are used as options for transportation methods. + Any availability rules you set will apply on the online hiring portal`; + + this.record$ = merge( + this.initialData$.pipe( + tap(() => console.log("on init: form set with initial")) + ), + this.updatedRecord$.pipe( + tap(() => + console.log("post update/create: form refresehed with API result") + ) + ) + ); + } + + public save(record: TransportProvider, isDelete?: boolean): void { + this.tpService + .save(record, isDelete) + .pipe(tap((tp: TransportProvider) => this.updatedRecord$.next(tp))) + .subscribe(); + } + + public async cancel(): Promise { + await this.router.navigate(["configuration/transport-providers"]); + } + + private deActivate(record: TransportProvider): void { + this.save({ ...record, active: false }, true); + } + + public confirm(event: Event, record: TransportProvider): void { + this.primeConfirmService.confirm({ + target: event.target, + message: + "Are you sure that you want to delete this provider. This cannot be undone", + icon: "pi pi-exclamation-triangle", + accept: async () => { + this.deActivate(record); + await this.cancel(); + }, + reject: () => { + //reject action + }, + }); + } +} diff --git a/src/app/configs/transport-providers/transport-providers-routing.module.ts b/src/app/configs/transport-providers/transport-providers-routing.module.ts index f62c7df6..afc74f86 100644 --- a/src/app/configs/transport-providers/transport-providers-routing.module.ts +++ b/src/app/configs/transport-providers/transport-providers-routing.module.ts @@ -2,22 +2,24 @@ import { NgModule } from "@angular/core"; import { RouterModule, Routes } from "@angular/router"; import { AuthGuardService } from "src/app/shared/services/auth-guard.service"; import { TransportProviderListComponent } from "./tranport-providers-list/transport-provider-list.component"; +import { TransportProvidersContainerComponent } from "./transport-providers-container.component"; import { TransportProvidersEditComponent } from "./transport-providers-edit/transport-providers-edit.component"; const routes: Routes = [ { path: "", - redirectTo: "list", - }, - { - path: "list", - component: TransportProviderListComponent, - canActivate: [AuthGuardService], - }, - { - path: "edit:id", - component: TransportProvidersEditComponent, + component: TransportProvidersContainerComponent, canActivate: [AuthGuardService], + children: [ + { + path: "", + component: TransportProviderListComponent, + }, + { + path: ":id", + component: TransportProvidersEditComponent, + }, + ], }, ]; diff --git a/src/app/configs/transport-providers/transport-providers.module.ts b/src/app/configs/transport-providers/transport-providers.module.ts index 4b2e45b7..d76f40b6 100644 --- a/src/app/configs/transport-providers/transport-providers.module.ts +++ b/src/app/configs/transport-providers/transport-providers.module.ts @@ -4,12 +4,40 @@ import { TransportProviderListComponent } from "./tranport-providers-list/transp import { RecordsTableModule } from "src/app/shared/components/records-table/records-table.module"; import { TransportProvidersRoutingModule } from "./transport-providers-routing.module"; import { TransportProvidersEditComponent } from "./transport-providers-edit/transport-providers-edit.component"; +import { TransportProvidersContainerComponent } from "./transport-providers-container.component"; +import { TabViewModule } from "primeng/tabview"; +import { FormsModule } from "@angular/forms"; +import { InputTextModule } from "primeng/inputtext"; +import { DividerModule } from "primeng/divider"; +import { FieldsetModule } from "primeng/fieldset"; +import { CardModule } from "primeng/card"; +import { ChipModule } from "primeng/chip"; +import { ButtonModule } from "primeng/button"; +import { ToolbarModule } from "primeng/toolbar"; +import { ConfirmPopupModule } from "primeng/confirmpopup"; +import { ConfirmationService } from "primeng/api"; @NgModule({ declarations: [ TransportProviderListComponent, TransportProvidersEditComponent, + TransportProvidersContainerComponent, ], - imports: [CommonModule, RecordsTableModule, TransportProvidersRoutingModule], + imports: [ + CommonModule, + FormsModule, + RecordsTableModule, + TransportProvidersRoutingModule, + TabViewModule, + InputTextModule, + DividerModule, + FieldsetModule, + CardModule, + ChipModule, + ButtonModule, + ToolbarModule, + ConfirmPopupModule, + ], + providers: [ConfirmationService], }) export class TransportProvidersModule {} diff --git a/src/app/menu/load-menu-rules.ts b/src/app/menu/load-menu-rules.ts index 6310ab42..87f53fe5 100644 --- a/src/app/menu/load-menu-rules.ts +++ b/src/app/menu/load-menu-rules.ts @@ -84,15 +84,6 @@ export function loadMenuRules(authList: string[]): Array { icon: "airport_shuttle", routerLink: ["configuration/transport-providers"], authorizedRoles: [LRole.ADMIN], - items: [ - new MenuRule({ - id: 14, - label: "List", - icon: "list", - routerLink: ["configuration/transport-providers/list"], - authorizedRoles: [LRole.ADMIN], - }), - ], }), ], }), diff --git a/src/app/online-orders/transport-providers.service.spec.ts b/src/app/online-orders/transport-providers.service.spec.ts index 9038db82..e49954b4 100644 --- a/src/app/online-orders/transport-providers.service.spec.ts +++ b/src/app/online-orders/transport-providers.service.spec.ts @@ -19,7 +19,7 @@ describe("TransportProviderService", () => { let httpClientSpy: jasmine.SpyObj; beforeEach(() => { - httpClientSpy = jasmine.createSpyObj("HttpClient", ["get"]); + httpClientSpy = jasmine.createSpyObj("HttpClient", ["get", "post", "put"]); TestBed.configureTestingModule({ providers: [ @@ -85,6 +85,48 @@ describe("TransportProviderService", () => { }); }); }); + + describe("upsert", () => { + const tp: TransportProvider = { + id: 0, + key: "fake", + text: "FAKE", + defaultAttribute: false, + active: true, + availabilityRules: [], + }; + beforeEach(() => { + spyOn(HttpClient.prototype, "post").calls.reset(); + httpClientSpy.post.and.returnValue( + of({ + data: [new TransportProvider()], + }) + ); + spyOn(HttpClient.prototype, "put").calls.reset(); + httpClientSpy.put.and.returnValue( + of({ + data: [new TransportProvider()], + }) + ); + }); + it("when no id, should CREATE", (done: DoneFn) => { + service.save(tp).subscribe(() => { + expect(httpClientSpy.post.calls.count()).toBe(1); + done(); + }); + }); + it("when id, should UPDATE", (done: DoneFn) => { + sessionStorage.setItem( + "tranportProviders", + JSON.stringify([{ ...tp, id: 10 }]) + ); + + service.save({ ...tp, id: 10 }).subscribe(() => { + expect(httpClientSpy.post.calls.count()).toBe(0); + done(); + }); + }); + }); }); describe("TransportProviderServiceHttp", () => { diff --git a/src/app/online-orders/transport-providers.service.ts b/src/app/online-orders/transport-providers.service.ts index cb320bc1..6bdd8611 100644 --- a/src/app/online-orders/transport-providers.service.ts +++ b/src/app/online-orders/transport-providers.service.ts @@ -1,6 +1,6 @@ import { Observable, throwError } from "rxjs"; -import { catchError, map, pluck, shareReplay, tap } from "rxjs/operators"; +import { catchError, map, pluck, tap } from "rxjs/operators"; import { Injectable, Optional, SkipSelf } from "@angular/core"; import { TransportProvider } from "./shared/"; import { of } from "rxjs"; @@ -65,4 +65,151 @@ export class TransportProvidersService { }) ); } + + getById(id: number): Observable { + if (this.isNotStale()) { + console.log( + `${TransportProvidersService.name}.${this.getById.name} returning cache:`, + this.providersAge + ); + const cache: TransportProvider[] = JSON.parse( + sessionStorage.getItem("tranportProviders") + ) as TransportProvider[]; + + return of({ ...cache.find((tp: TransportProvider) => tp.id === id) }); + } + + return this.http + .get(`${this.uriBase}/${id}`, { withCredentials: true }) + .pipe( + catchError((error) => { + // only expecting one type of error + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access + const errorAsText: string = error["statusText"] as string; + this.appMessages.showErrors({ + Error: `${errorAsText}: Contact Machete support.`, + }); + console.log(error); + return throwError(error); + }), + pluck("data"), + map((data) => data as TransportProvider), + tap((data: TransportProvider) => { + const cache: TransportProvider[] = JSON.parse( + sessionStorage.getItem("tranportProviders") + ) as TransportProvider[]; + const newCache: TransportProvider[] = cache.filter( + (tp: TransportProvider) => tp.id !== id + ); + sessionStorage.setItem( + "tranportProviders", + JSON.stringify([...newCache, data]) + ); + this.providersAge = Date.now(); + }) + ); + } + + save( + entity: TransportProvider, + isDelete?: boolean + ): Observable { + if (entity.id) { + return this.update(entity, isDelete); + } else { + return this.create(entity); + } + } + + private create( + transportProvider: TransportProvider + ): Observable { + const url = `${this.uriBase}`; + return this.http + .post( + url, + { ...transportProvider, active: true }, + { + withCredentials: true, + } + ) + .pipe( + catchError((error) => { + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access + const errorAsText: string = error["statusText"] as string; + this.appMessages.showErrors({ + Error: `${errorAsText}: Contact Machete support.`, + }); + console.log(error); + return throwError(error); + }), + pluck("data"), + map((data) => data as TransportProvider), + tap((data: TransportProvider) => { + this.appMessages.showSuccess({ + label: "Success", + message: "Record created", + }); + this.syncChacheWithMutation(data); + }) + ); + } + + private update( + tranportProvider: TransportProvider, + isDelete?: boolean + ): Observable { + const url = `${this.uriBase}/${tranportProvider.id.toString()}`; + return this.http + .put(url, tranportProvider, { + withCredentials: true, + }) + .pipe( + catchError((error) => { + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access + const errorAsText: string = error["statusText"] as string; + this.appMessages.showErrors({ + Error: `${errorAsText}: Contact Machete support.`, + }); + console.log(error); + return throwError(error); + }), + pluck("data"), + map((data) => data as TransportProvider), + tap((data: TransportProvider) => { + this.appMessages.showSuccess({ + label: "Success", + message: "Record updated", + }); + this.syncChacheWithMutation(data, isDelete); + }) + ); + } + + private syncChacheWithMutation( + mutated: TransportProvider, + isDelete?: boolean + ): void { + const cache: TransportProvider[] = JSON.parse( + sessionStorage.getItem("tranportProviders") + ) as TransportProvider[]; + + const newCache: TransportProvider[] = cache.filter( + (tp: TransportProvider) => tp.id !== mutated.id + ); + + if (isDelete) { + sessionStorage.setItem( + "tranportProviders", + JSON.stringify([...newCache]) + ); + } else { + sessionStorage.setItem( + "tranportProviders", + JSON.stringify([...newCache, mutated]) + ); + } + + this.providersAge = Date.now(); + } } diff --git a/src/app/shared/services/transport-providers-store.service.ts b/src/app/shared/services/transport-providers-store.service.ts deleted file mode 100644 index cdd415bf..00000000 --- a/src/app/shared/services/transport-providers-store.service.ts +++ /dev/null @@ -1,68 +0,0 @@ -import { HttpClient } from "@angular/common/http"; -import { Injectable, Optional, SkipSelf } from "@angular/core"; -import { Router } from "@angular/router"; -import { BehaviorSubject, Observable, throwError } from "rxjs"; -import { catchError, map, shareReplay, tap } from "rxjs/operators"; -import { TransportProvider } from "src/app/online-orders/shared"; -import { environment } from "src/environments/environment"; -import { MessagesService } from "../components/messages/messages.service"; - -/** - * A singleton RXJS BehaviorSubject Machete Data store. - * Returns the same list of records regardless of - * how many times the observable is consumned until the data is mutaded - * - * @class TransportProvidersStoreService - */ -@Injectable({ - providedIn: "root", -}) -export class TransportProvidersStoreService { - private transportProvidersSubject = new BehaviorSubject( - [] - ); - public transportProviders$: Observable = this - .transportProvidersSubject as Observable; - - constructor( - private http: HttpClient, - private appMessages: MessagesService, - private router: Router, - @Optional() @SkipSelf() parentModule?: TransportProvidersStoreService - ) { - // enforce app singleton pattern - if (parentModule) { - throw new Error( - "Machete dev error:TransportProvidersStoreService is already loaded. Additional imports not needed" - ); - } - this.getRecords(); - } - - private getRecords() { - const uriBase = environment.dataUrl + "/api/transportproviders"; - console.log("getReportList: ", uriBase); - return this.http - .get(uriBase, { withCredentials: true }) - .pipe( - map((o) => o["data"] as TransportProvider[]), - catchError((error) => { - // only expecting one type of error - // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access - const errorAsText: string = error["statusText"] as string; - this.appMessages.showErrors({ - Error: `${errorAsText}: Contact Machete support.`, - }); - console.log(error); - return throwError(error); - }), - tap((transportProviders) => - console.log("recordsList fetched", transportProviders) - ), - shareReplay(1) - ) - .subscribe((tProviders) => - this.transportProvidersSubject.next(tProviders) - ); - } -} diff --git a/src/app/shared/testing/services.spy.ts b/src/app/shared/testing/services.spy.ts index ec02474b..ecef5537 100644 --- a/src/app/shared/testing/services.spy.ts +++ b/src/app/shared/testing/services.spy.ts @@ -279,7 +279,6 @@ export class TransportProvidersServiceSpy { id: 32, text: "a text label", availabilityRules: new Array( - new TransportProviderAvailability({ day: 0, available: false }), new TransportProviderAvailability({ day: 1, available: true }), new TransportProviderAvailability({ day: 2, available: true }), new TransportProviderAvailability({ day: 3, available: true }), @@ -290,6 +289,19 @@ export class TransportProvidersServiceSpy { }), ]) ); + getById = jasmine.createSpy("getById").and.callFake(() => + observableOf( + new TransportProvider({ + id: 1, + text: "a text label", + availabilityRules: new Array( + new TransportProviderAvailability({ day: 1, available: true }), + new TransportProviderAvailability({ day: 2, available: true }) + ), + key: "one", + }) + ) + ); } export class WorkerServiceSpy { @@ -323,6 +335,7 @@ export class ReportsServiceSpy { export class MessagesServiceSpy { showErrors = jasmine.createSpy("showErrors").and.callThrough(); + showSuccess = jasmine.createSpy("showSuccess").and.callThrough(); } export class ReportsStoreServiceSpy { @@ -345,3 +358,7 @@ export class TransportProvidersStoreServiceSpy { new Array(new TransportProvider({ key: "test" })) ); } + +export class ConfirmationServiceSpy { + confirm = jasmine.createSpy("confirm").and.callThrough(); +}