From 4c0d23c791527850e76cf9c78c3422ffcceb458f Mon Sep 17 00:00:00 2001 From: Timothee Guerin Date: Fri, 25 May 2018 10:10:27 -0700 Subject: [PATCH 1/7] Validate paths --- .../add/file-or-directory-picker.component.ts | 67 ++++++++++++++++--- .../action/add/file-or-directory-picker.html | 3 + 2 files changed, 60 insertions(+), 10 deletions(-) diff --git a/app/components/data/action/add/file-or-directory-picker.component.ts b/app/components/data/action/add/file-or-directory-picker.component.ts index 1c85db5821..a820b5c5bc 100644 --- a/app/components/data/action/add/file-or-directory-picker.component.ts +++ b/app/components/data/action/add/file-or-directory-picker.component.ts @@ -1,27 +1,44 @@ -import { Component, HostListener, Input, OnDestroy, forwardRef } from "@angular/core"; import { - ControlValueAccessor, FormBuilder, FormControl, NG_VALIDATORS, NG_VALUE_ACCESSOR, + ChangeDetectionStrategy, + ChangeDetectorRef, + Component, + HostListener, + Input, + OnDestroy, + forwardRef, +} from "@angular/core"; +import { + AsyncValidator, + ControlValueAccessor, + FormBuilder, + FormControl, + NG_ASYNC_VALIDATORS, + NG_VALIDATORS, + NG_VALUE_ACCESSOR, + Validator, } from "@angular/forms"; -import { Subscription } from "rxjs"; +import { Observable, Subscription } from "rxjs"; import { FileOrDirectoryDto } from "app/models/dtos"; import { DragUtils } from "app/utils"; +import { autobind } from "@batch-flask/core"; +import { FileSystemService } from "app/services"; import "./file-or-directory-picker.scss"; -// tslint:disable:no-forward-ref @Component({ selector: "bl-file-or-directory-picker", templateUrl: "file-or-directory-picker.html", providers: [ { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => FileOrDirectoryPickerComponent), multi: true }, - { provide: NG_VALIDATORS, useExisting: forwardRef(() => FileOrDirectoryPickerComponent), multi: true }, + { provide: NG_ASYNC_VALIDATORS, useExisting: forwardRef(() => FileOrDirectoryPickerComponent), multi: true }, ], + changeDetection: ChangeDetectionStrategy.OnPush, }) -export class FileOrDirectoryPickerComponent implements ControlValueAccessor, OnDestroy { - @Input() - public dragMessage: string = "Drag and drop files or folders here"; +export class FileOrDirectoryPickerComponent implements AsyncValidator, ControlValueAccessor, OnDestroy { + @Input() public dragMessage: string = "Drag and drop files or folders here"; + public invalidPath: string; public isDraging = 0; public paths: FormControl; @@ -29,9 +46,11 @@ export class FileOrDirectoryPickerComponent implements ControlValueAccessor, OnD private _sub: Subscription; constructor( + private fs: FileSystemService, + private changeDetector: ChangeDetectorRef, private formBuilder: FormBuilder) { - this.paths = this.formBuilder.control([]); + this.paths = this.formBuilder.control([], null, this._validatePaths); this._sub = this.paths.valueChanges.subscribe((paths) => { if (this._propagateChange) { this._propagateChange(paths); @@ -58,7 +77,7 @@ export class FileOrDirectoryPickerComponent implements ControlValueAccessor, OnD } public validate(c: FormControl) { - return null; + return this._validatePaths(c); } @HostListener("dragover", ["$event"]) @@ -72,12 +91,14 @@ export class FileOrDirectoryPickerComponent implements ControlValueAccessor, OnD if (!this._canDrop(event.dataTransfer)) { return; } event.stopPropagation(); this.isDraging++; + this.changeDetector.markForCheck(); } @HostListener("dragleave", ["$event"]) public dragLeave(event: DragEvent) { if (!this._canDrop(event.dataTransfer)) { return; } this.isDraging--; + this.changeDetector.markForCheck(); } @HostListener("drop", ["$event"]) @@ -102,4 +123,30 @@ export class FileOrDirectoryPickerComponent implements ControlValueAccessor, OnD private _hasFile(dataTransfer: DataTransfer) { return dataTransfer.types.includes("Files"); } + + @autobind() + private _validatePaths(control: FormControl) { + return Observable.fromPromise(this._validatePathsAsync(control.value)).map((path) => { + if (path) { + return { + invalidFileOrPath: false, + }; + } + return null; + }); + } + + private async _validatePathsAsync(paths: Array<{ path: string }>) { + for (const { path } of paths) { + const exists = await this.fs.exists(path); + if (!exists) { + this.invalidPath = path; + this.changeDetector.markForCheck(); + return path; + } + } + this.invalidPath = null; + this.changeDetector.markForCheck(); + return null; + } } diff --git a/app/components/data/action/add/file-or-directory-picker.html b/app/components/data/action/add/file-or-directory-picker.html index 8cd42c8216..0320d0bc5c 100644 --- a/app/components/data/action/add/file-or-directory-picker.html +++ b/app/components/data/action/add/file-or-directory-picker.html @@ -1,6 +1,9 @@ File or directory path +
+ Path "{{invalidPath}}" is not found on this computer. +
{{dragMessage}}
From 674a7da0251c795a1165306b3ee6ba9f8c3e7429 Mon Sep 17 00:00:00 2001 From: Timothee Guerin Date: Fri, 25 May 2018 10:15:00 -0700 Subject: [PATCH 2/7] Move directory picker --- app/components/data/action/add/index.ts | 1 - app/components/data/data.module.ts | 14 +++++--- .../data/shared/data.shared.module.ts | 5 ++- ...file-or-directory-picker.component.spec.ts | 33 +++++++++++++++++++ .../file-or-directory-picker.component.ts | 2 -- .../file-or-directory-picker.html | 0 .../file-or-directory-picker.scss | 0 .../shared/file-or-directory-picker/index.ts | 1 + 8 files changed, 47 insertions(+), 9 deletions(-) create mode 100644 app/components/data/shared/file-or-directory-picker/file-or-directory-picker.component.spec.ts rename app/components/data/{action/add => shared/file-or-directory-picker}/file-or-directory-picker.component.ts (99%) rename app/components/data/{action/add => shared/file-or-directory-picker}/file-or-directory-picker.html (100%) rename app/components/data/{action/add => shared/file-or-directory-picker}/file-or-directory-picker.scss (100%) create mode 100644 app/components/data/shared/file-or-directory-picker/index.ts diff --git a/app/components/data/action/add/index.ts b/app/components/data/action/add/index.ts index e3e13bd849..f6e8209a2a 100644 --- a/app/components/data/action/add/index.ts +++ b/app/components/data/action/add/index.ts @@ -1,3 +1,2 @@ export * from "./file-group-create-form.component"; export * from "./file-group-options-picker.component"; -export * from "./file-or-directory-picker.component"; diff --git a/app/components/data/data.module.ts b/app/components/data/data.module.ts index e66061ddbd..67ed3bdcfe 100644 --- a/app/components/data/data.module.ts +++ b/app/components/data/data.module.ts @@ -5,7 +5,6 @@ import { DeleteContainerDialogComponent, FileGroupCreateFormComponent, FileGroupOptionsPickerComponent, - FileOrDirectoryPickerComponent, } from "app/components/data/action"; import { DataSharedModule } from "app/components/data/shared"; import { FileBrowseModule } from "app/components/file/browse"; @@ -20,10 +19,15 @@ import { import { DataHomeComponent } from "./home"; const components = [ - DataContainerConfigurationComponent, DataContainerFilesComponent, DataHomeComponent, - DataDefaultComponent, DataDetailsComponent, FileGroupCreateFormComponent, DeleteContainerDialogComponent, - DataContainerListComponent, FileGroupOptionsPickerComponent, FileGroupPreviewComponent, - FileOrDirectoryPickerComponent, + DataContainerConfigurationComponent, + DataContainerFilesComponent, + DataHomeComponent, + DataDefaultComponent, DataDetailsComponent, + FileGroupCreateFormComponent, + DeleteContainerDialogComponent, + DataContainerListComponent, + FileGroupOptionsPickerComponent, + FileGroupPreviewComponent, ]; @NgModule({ diff --git a/app/components/data/shared/data.shared.module.ts b/app/components/data/shared/data.shared.module.ts index 0016170f9e..63d25d1ccc 100644 --- a/app/components/data/shared/data.shared.module.ts +++ b/app/components/data/shared/data.shared.module.ts @@ -8,13 +8,16 @@ import { StorageErrorDisplayComponent } from "./errors"; import { FileGroupPickerComponent } from "./file-group-picker"; import { FileGroupSasComponent } from "./file-group-sas"; import { FileGroupsPickerComponent } from "./file-groups-picker"; +import { FileOrDirectoryPickerComponent } from "./file-or-directory-picker"; import { StorageAccountPickerComponent } from "./storage-account-picker"; const components = [ FileGroupPickerComponent, FileGroupSasComponent, FileGroupsPickerComponent, CloudFilePickerComponent, CloudFilePickerDialogComponent, - StorageErrorDisplayComponent, BlobContainerPickerComponent, + StorageErrorDisplayComponent, + BlobContainerPickerComponent, StorageAccountPickerComponent, + FileOrDirectoryPickerComponent, ]; @NgModule({ diff --git a/app/components/data/shared/file-or-directory-picker/file-or-directory-picker.component.spec.ts b/app/components/data/shared/file-or-directory-picker/file-or-directory-picker.component.spec.ts new file mode 100644 index 0000000000..092d57f947 --- /dev/null +++ b/app/components/data/shared/file-or-directory-picker/file-or-directory-picker.component.spec.ts @@ -0,0 +1,33 @@ +import { Component, DebugElement } from "@angular/core"; +import { ComponentFixture, TestBed } from "@angular/core/testing"; +import { By } from "@angular/platform-browser"; + +import { FileOrDirectoryPickerComponent } from "./file-or-directory-picker.component"; + +@Component({ + template: ``, +}) +class TestComponent { +} + +fdescribe("FileOrDirectoryPickerComponent", () => { + let fixture: ComponentFixture; + let testComponent: TestComponent; + let component: FileOrDirectoryPickerComponent; + let de: DebugElement; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [], + declarations: [FileOrDirectoryPickerComponent, TestComponent], + }); + fixture = TestBed.createComponent(TestComponent); + testComponent = fixture.componentInstance; + de = fixture.debugElement.query(By.css("bl-selector")); + component = de.componentInstance; + fixture.detectChanges(); + }); + + it("", () => { + }); +}); diff --git a/app/components/data/action/add/file-or-directory-picker.component.ts b/app/components/data/shared/file-or-directory-picker/file-or-directory-picker.component.ts similarity index 99% rename from app/components/data/action/add/file-or-directory-picker.component.ts rename to app/components/data/shared/file-or-directory-picker/file-or-directory-picker.component.ts index a820b5c5bc..7a1614cf11 100644 --- a/app/components/data/action/add/file-or-directory-picker.component.ts +++ b/app/components/data/shared/file-or-directory-picker/file-or-directory-picker.component.ts @@ -13,9 +13,7 @@ import { FormBuilder, FormControl, NG_ASYNC_VALIDATORS, - NG_VALIDATORS, NG_VALUE_ACCESSOR, - Validator, } from "@angular/forms"; import { Observable, Subscription } from "rxjs"; diff --git a/app/components/data/action/add/file-or-directory-picker.html b/app/components/data/shared/file-or-directory-picker/file-or-directory-picker.html similarity index 100% rename from app/components/data/action/add/file-or-directory-picker.html rename to app/components/data/shared/file-or-directory-picker/file-or-directory-picker.html diff --git a/app/components/data/action/add/file-or-directory-picker.scss b/app/components/data/shared/file-or-directory-picker/file-or-directory-picker.scss similarity index 100% rename from app/components/data/action/add/file-or-directory-picker.scss rename to app/components/data/shared/file-or-directory-picker/file-or-directory-picker.scss diff --git a/app/components/data/shared/file-or-directory-picker/index.ts b/app/components/data/shared/file-or-directory-picker/index.ts new file mode 100644 index 0000000000..80037cfc28 --- /dev/null +++ b/app/components/data/shared/file-or-directory-picker/index.ts @@ -0,0 +1 @@ +export * from "./file-or-directory-picker.component"; From 50c17aa9e2d1f69d77082e08eaea67a8d958089f Mon Sep 17 00:00:00 2001 From: Timothee Guerin Date: Fri, 25 May 2018 10:54:13 -0700 Subject: [PATCH 3/7] Startup --- ...file-or-directory-picker.component.spec.ts | 68 ++++++++++++++++--- karma.conf.js | 2 + src/client/startup.ts | 4 +- 3 files changed, 64 insertions(+), 10 deletions(-) diff --git a/app/components/data/shared/file-or-directory-picker/file-or-directory-picker.component.spec.ts b/app/components/data/shared/file-or-directory-picker/file-or-directory-picker.component.spec.ts index 092d57f947..1e32cc22a8 100644 --- a/app/components/data/shared/file-or-directory-picker/file-or-directory-picker.component.spec.ts +++ b/app/components/data/shared/file-or-directory-picker/file-or-directory-picker.component.spec.ts @@ -1,13 +1,17 @@ -import { Component, DebugElement } from "@angular/core"; -import { ComponentFixture, TestBed } from "@angular/core/testing"; -import { By } from "@angular/platform-browser"; +import { Component, DebugElement, NO_ERRORS_SCHEMA } from "@angular/core"; +import { ComponentFixture, TestBed, fakeAsync, tick } from "@angular/core/testing"; +import { FormControl, FormsModule, ReactiveFormsModule } from "@angular/forms"; +import { BrowserModule, By } from "@angular/platform-browser"; +import { EditableTableColumnComponent, EditableTableComponent } from "@batch-flask/ui/form/editable-table"; +import { FileSystemService } from "app/services"; import { FileOrDirectoryPickerComponent } from "./file-or-directory-picker.component"; @Component({ - template: ``, + template: ``, }) class TestComponent { + public paths = new FormControl([]); } fdescribe("FileOrDirectoryPickerComponent", () => { @@ -15,19 +19,65 @@ fdescribe("FileOrDirectoryPickerComponent", () => { let testComponent: TestComponent; let component: FileOrDirectoryPickerComponent; let de: DebugElement; + let fsSpy; beforeEach(() => { + fsSpy = { + exists: jasmine.createSpy("fs.exists").and.callFake((path) => { + return Promise.resolve(path === "/Users/test/files"); + }), + }; TestBed.configureTestingModule({ - imports: [], - declarations: [FileOrDirectoryPickerComponent, TestComponent], + imports: [ReactiveFormsModule, BrowserModule], + declarations: [ + FileOrDirectoryPickerComponent, + TestComponent, + EditableTableComponent, + EditableTableColumnComponent, + ], + providers: [ + { provide: FileSystemService, useValue: fsSpy }, + ], + schemas: [NO_ERRORS_SCHEMA], }); fixture = TestBed.createComponent(TestComponent); testComponent = fixture.componentInstance; - de = fixture.debugElement.query(By.css("bl-selector")); + de = fixture.debugElement.query(By.css("bl-file-or-directory-picker")); component = de.componentInstance; fixture.detectChanges(); }); - it("", () => { - }); + it("Validate valid paths", fakeAsync(() => { + testComponent.paths.setValue([ + { path: "/Users/test/files" }, + ]); + fixture.detectChanges(); + tick(); + + expect(testComponent.paths.valid).toBe(true); + expect(testComponent.paths.status).toBe("VALID"); + })); + + it("Validate invalid paths", fakeAsync(() => { + testComponent.paths.setValue([ + { path: "/invalid" }, + ]); + fixture.detectChanges(); + tick(); + + expect(testComponent.paths.valid).toBe(false); + expect(testComponent.paths.status).toBe("INVALID"); + })); + + it("Validate at least one invalid paths", fakeAsync(() => { + testComponent.paths.setValue([ + { path: "/Users/test/files" }, + { path: "/invalid" }, + ]); + fixture.detectChanges(); + tick(); + + expect(testComponent.paths.valid).toBe(false); + expect(testComponent.paths.status).toBe("INVALID"); + })); }); diff --git a/karma.conf.js b/karma.conf.js index 06d9cc11d3..5e6081f526 100644 --- a/karma.conf.js +++ b/karma.conf.js @@ -1,5 +1,7 @@ const webpackConfig = require("./config/webpack.config.test"); +process.env.ELECTRON_DISABLE_SECURITY_WARNINGS = "true"; + // Only enable coverage if env is defined(So we don't enable it in watch mode as it duplicate logs) const coverageReporters = process.env.COVERAGE ? ["coverage", "remap-coverage"] : []; // Karma config for testing the code running the browser environemnt. diff --git a/src/client/startup.ts b/src/client/startup.ts index d02f61bbd1..a56e7d5052 100644 --- a/src/client/startup.ts +++ b/src/client/startup.ts @@ -50,7 +50,9 @@ async function startApplication(batchLabsApp: BatchLabsApplication) { } export async function startBatchLabs() { - process.env["ELECTRON_DISABLE_SECURITY_WARNINGS"] = "true"; + // Those warnings are electron complaining we are loading remote data + // But this is a false positive when using dev server has it doesn't seem to ignore localhost + process.env.ELECTRON_DISABLE_SECURITY_WARNINGS = "true"; localStorage.load(); const batchLabsApp = new BatchLabsApplication(autoUpdater); setupSingleInstance(batchLabsApp); From ed96d094cf69d8cbccef392822c064f3153782a3 Mon Sep 17 00:00:00 2001 From: Timothee Guerin Date: Fri, 25 May 2018 10:57:48 -0700 Subject: [PATCH 4/7] more tests --- .../file-or-directory-picker.component.spec.ts | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/app/components/data/shared/file-or-directory-picker/file-or-directory-picker.component.spec.ts b/app/components/data/shared/file-or-directory-picker/file-or-directory-picker.component.spec.ts index 1e32cc22a8..4dee388f1e 100644 --- a/app/components/data/shared/file-or-directory-picker/file-or-directory-picker.component.spec.ts +++ b/app/components/data/shared/file-or-directory-picker/file-or-directory-picker.component.spec.ts @@ -1,6 +1,6 @@ import { Component, DebugElement, NO_ERRORS_SCHEMA } from "@angular/core"; import { ComponentFixture, TestBed, fakeAsync, tick } from "@angular/core/testing"; -import { FormControl, FormsModule, ReactiveFormsModule } from "@angular/forms"; +import { FormControl, ReactiveFormsModule } from "@angular/forms"; import { BrowserModule, By } from "@angular/platform-browser"; import { EditableTableColumnComponent, EditableTableComponent } from "@batch-flask/ui/form/editable-table"; @@ -17,7 +17,6 @@ class TestComponent { fdescribe("FileOrDirectoryPickerComponent", () => { let fixture: ComponentFixture; let testComponent: TestComponent; - let component: FileOrDirectoryPickerComponent; let de: DebugElement; let fsSpy; @@ -43,7 +42,6 @@ fdescribe("FileOrDirectoryPickerComponent", () => { fixture = TestBed.createComponent(TestComponent); testComponent = fixture.componentInstance; de = fixture.debugElement.query(By.css("bl-file-or-directory-picker")); - component = de.componentInstance; fixture.detectChanges(); }); @@ -56,6 +54,8 @@ fdescribe("FileOrDirectoryPickerComponent", () => { expect(testComponent.paths.valid).toBe(true); expect(testComponent.paths.status).toBe("VALID"); + + expect(de.nativeElement.textContent).not.toContain(`Path "/Users/test/files" is not found on this computer.`); })); it("Validate invalid paths", fakeAsync(() => { @@ -67,6 +67,9 @@ fdescribe("FileOrDirectoryPickerComponent", () => { expect(testComponent.paths.valid).toBe(false); expect(testComponent.paths.status).toBe("INVALID"); + + expect(de.query(By.css(".danger")).nativeElement.textContent) + .toContain(`Path "/invalid" is not found on this computer.`); })); it("Validate at least one invalid paths", fakeAsync(() => { @@ -79,5 +82,8 @@ fdescribe("FileOrDirectoryPickerComponent", () => { expect(testComponent.paths.valid).toBe(false); expect(testComponent.paths.status).toBe("INVALID"); + + expect(de.nativeElement.textContent).not.toContain(`Path "/Users/test/files" is not found on this computer.`); + expect(de.nativeElement.textContent).toContain(`Path "/invalid" is not found on this computer.`); })); }); From 5320b9f07ba74b1be554d3e1b281f383b353e6ce Mon Sep 17 00:00:00 2001 From: Timothee Guerin Date: Fri, 25 May 2018 10:58:05 -0700 Subject: [PATCH 5/7] more tests --- .../file-or-directory-picker.component.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/components/data/shared/file-or-directory-picker/file-or-directory-picker.component.spec.ts b/app/components/data/shared/file-or-directory-picker/file-or-directory-picker.component.spec.ts index 4dee388f1e..d0e1a1d21d 100644 --- a/app/components/data/shared/file-or-directory-picker/file-or-directory-picker.component.spec.ts +++ b/app/components/data/shared/file-or-directory-picker/file-or-directory-picker.component.spec.ts @@ -14,7 +14,7 @@ class TestComponent { public paths = new FormControl([]); } -fdescribe("FileOrDirectoryPickerComponent", () => { +describe("FileOrDirectoryPickerComponent", () => { let fixture: ComponentFixture; let testComponent: TestComponent; let de: DebugElement; From 769f40bf0bc1a0460c4fdd7636c0a6afada3b32a Mon Sep 17 00:00:00 2001 From: Timothee Guerin Date: Fri, 25 May 2018 11:23:40 -0700 Subject: [PATCH 6/7] Fix tests --- .../file-or-directory-picker.component.spec.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/app/components/data/shared/file-or-directory-picker/file-or-directory-picker.component.spec.ts b/app/components/data/shared/file-or-directory-picker/file-or-directory-picker.component.spec.ts index d0e1a1d21d..a07c18f15a 100644 --- a/app/components/data/shared/file-or-directory-picker/file-or-directory-picker.component.spec.ts +++ b/app/components/data/shared/file-or-directory-picker/file-or-directory-picker.component.spec.ts @@ -51,6 +51,7 @@ describe("FileOrDirectoryPickerComponent", () => { ]); fixture.detectChanges(); tick(); + fixture.detectChanges(); expect(testComponent.paths.valid).toBe(true); expect(testComponent.paths.status).toBe("VALID"); @@ -64,12 +65,11 @@ describe("FileOrDirectoryPickerComponent", () => { ]); fixture.detectChanges(); tick(); + fixture.detectChanges(); expect(testComponent.paths.valid).toBe(false); expect(testComponent.paths.status).toBe("INVALID"); - - expect(de.query(By.css(".danger")).nativeElement.textContent) - .toContain(`Path "/invalid" is not found on this computer.`); + expect(de.nativeElement.textContent).toContain(`Path "/invalid" is not found on this computer.`); })); it("Validate at least one invalid paths", fakeAsync(() => { @@ -79,6 +79,7 @@ describe("FileOrDirectoryPickerComponent", () => { ]); fixture.detectChanges(); tick(); + fixture.detectChanges(); expect(testComponent.paths.valid).toBe(false); expect(testComponent.paths.status).toBe("INVALID"); From 2d715fd9a7ea29425a761e54bb5f78dbba9e7fa5 Mon Sep 17 00:00:00 2001 From: Timothee Guerin Date: Fri, 25 May 2018 11:24:22 -0700 Subject: [PATCH 7/7] more --- test/app/components/base/tabs/tabs.spec.ts | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/test/app/components/base/tabs/tabs.spec.ts b/test/app/components/base/tabs/tabs.spec.ts index 7664500eb5..717e5a91c4 100644 --- a/test/app/components/base/tabs/tabs.spec.ts +++ b/test/app/components/base/tabs/tabs.spec.ts @@ -62,14 +62,13 @@ describe("Tabs", () => { expect(fixture.nativeElement.textContent).not.toContain("Content 2"); }); - it("changing the route should update the tab", async(() => { + it("changing the route should update the tab", async () => { activeRouteSpy.queryParams.next({ tab: "second" }); fixture.detectChanges(); - fixture.whenStable().then(() => { - expect(fixture.nativeElement.textContent).toContain("Content 2"); - expect(routerSpy.navigate).not.toHaveBeenCalled(); - }); - })); + await fixture.whenStable(); + expect(fixture.nativeElement.textContent).toContain("Content 2"); + expect(routerSpy.navigate).not.toHaveBeenCalled(); + }); it("clicking on a tab label should update the route", async(() => { const labels = fixture.debugElement.queryAll(By.css(".mat-tab-label"));