Skip to content

Commit

Permalink
perf(stark-demo): set ChangeDetectionStrategy.OnPush to ExampleViewer…
Browse files Browse the repository at this point in the history
… component to prevent Angular from running unnecessary change detection cycles
  • Loading branch information
christophercr committed Jun 12, 2019
1 parent fe9a1e6 commit 709c261
Show file tree
Hide file tree
Showing 3 changed files with 47 additions and 29 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<mat-card-title>
<h3 translate>{{ exampleTitle }}</h3>
</mat-card-title>
<button color="white" mat-icon-button (click)="toggleSourceView()" matTooltip="View source">
<button color="white" mat-icon-button (click)="toggleSourceView()" matTooltip="View source" [disabled]="extensions.length === 0">
<mat-icon svgIcon="code-tags"></mat-icon>
</button>
</mat-card-header>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { NO_ERRORS_SCHEMA } from "@angular/core";
import { Component, NO_ERRORS_SCHEMA, ViewChild } from "@angular/core";
import { NoopAnimationsModule } from "@angular/platform-browser/animations";
import { async, ComponentFixture, TestBed } from "@angular/core/testing";
import { MatButtonModule } from "@angular/material/button";
Expand All @@ -17,14 +17,32 @@ import SpyObj = jasmine.SpyObj;
import Spy = jasmine.Spy;

describe("ExampleViewerComponent", () => {
@Component({
selector: "host-component",
template: `
<example-viewer [extensions]="extensions" [filesPath]="filesPath" [exampleTitle]="exampleTitle"></example-viewer>
`
})
class TestHostComponent {
@ViewChild(ExampleViewerComponent)
public exampleViewer!: ExampleViewerComponent;

public extensions: string[] = [];
public filesPath?: string;
public exampleTitle?: string;
}

// IMPORTANT: The official way to test components using ChangeDetectionStrategy.OnPush is to wrap it with a test host component
// see https://github.com/angular/angular/issues/12313#issuecomment-444623173
let hostFixture: ComponentFixture<TestHostComponent>;
let hostComponent: TestHostComponent;
let component: ExampleViewerComponent;
let fileService: SpyObj<FileService>;
let fixture: ComponentFixture<ExampleViewerComponent>;
let logger: SpyObj<StarkLoggingService>;

beforeEach(async(() => {
return TestBed.configureTestingModule({
declarations: [ExampleViewerComponent],
declarations: [ExampleViewerComponent, TestHostComponent],
imports: [NoopAnimationsModule, MatButtonModule, MatTabsModule, MatTooltipModule, StarkPrettyPrintModule],
providers: [
{ provide: STARK_LOGGING_SERVICE, useValue: new MockStarkLoggingService() },
Expand All @@ -47,29 +65,29 @@ describe("ExampleViewerComponent", () => {
fileService = TestBed.get(FileService);
fileService.fetchFile.and.callFake(() => of("initial dummy file content"));

fixture = TestBed.createComponent(ExampleViewerComponent);
component = fixture.componentInstance;
fixture.detectChanges(); // trigger initial data binding
hostFixture = TestBed.createComponent(TestHostComponent);
hostComponent = hostFixture.componentInstance;
component = hostComponent.exampleViewer;
hostFixture.detectChanges(); // trigger initial data binding
});

describe("@Input() exampleTitle", () => {
it("should change the exampleTitle according to the @Input", () => {
const h3: HTMLHeadingElement = fixture.nativeElement.querySelector("mat-card-header h3");
component.exampleTitle = "Test title";
fixture.detectChanges();
expect(h3.textContent).toContain(component.exampleTitle);
const h3: HTMLHeadingElement = hostFixture.nativeElement.querySelector("mat-card-header h3");
hostComponent.exampleTitle = "Test title";
hostFixture.detectChanges();
expect(h3.textContent).toContain(hostComponent.exampleTitle);
});
});

describe("@Input() extensions", () => {
it("should show the tabs when the file exist", (done: DoneFn) => {
component.exampleFiles = [];
component.extensions = ["CSS", "JS", "HTML", "SCSS", "TS"];
fixture.detectChanges();
hostComponent.extensions = ["CSS", "JS", "HTML", "SCSS", "TS"];
hostFixture.detectChanges();

let button: HTMLButtonElement = fixture.nativeElement.querySelector("mat-card-header button");
let button: HTMLButtonElement = hostFixture.nativeElement.querySelector("mat-card-header button");
button.click();
let tabs: any[] = fixture.nativeElement.querySelectorAll(".mat-tab-labels .mat-tab-label");
let tabs: any[] = hostFixture.nativeElement.querySelectorAll(".mat-tab-labels .mat-tab-label");
expect(tabs.length).toBe(0);

fileService.fetchFile.and.callFake(() => {
Expand All @@ -84,11 +102,12 @@ describe("ExampleViewerComponent", () => {
);

allFilesFetched.subscribe(() => {
fixture.detectChanges();
hostFixture.detectChanges();

button = fixture.nativeElement.querySelector("mat-card-header button");
button = hostFixture.nativeElement.querySelector("mat-card-header button");
button.click();
tabs = fixture.nativeElement.querySelectorAll(".mat-tab-labels .mat-tab-label");

tabs = hostFixture.nativeElement.querySelectorAll(".mat-tab-labels .mat-tab-label");
expect(tabs.length).toBe(component.extensions.length);
done();
});
Expand All @@ -102,6 +121,8 @@ describe("ExampleViewerComponent", () => {
spyOn(component, "addExampleFile");
fileService.fetchFile.calls.reset();
logger.error.calls.reset();

component.extensions = ["HTML", "TS", "CSS"];
});

it("should not do anything when the file doesn't exist", () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Component, Inject, Input, OnInit } from "@angular/core";
import { ChangeDetectionStrategy, Component, Inject, Input, OnInit } from "@angular/core";
import { HttpErrorResponse } from "@angular/common/http";
import { STARK_LOGGING_SERVICE, StarkErrorImpl, StarkLoggingService } from "@nationalbankbelgium/stark-core";
import { FileService } from "../../services";
Expand All @@ -12,27 +12,24 @@ export interface ExampleFile {
@Component({
selector: "example-viewer",
templateUrl: "./example-viewer.component.html",
styleUrls: ["./example-viewer.component.scss"]
styleUrls: ["./example-viewer.component.scss"],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class ExampleViewerComponent implements OnInit {
@Input()
public extensions: string[];
public extensions = ["HTML", "TS", "CSS"];
@Input()
public filesPath = "undefined";
@Input()
public exampleTitle = "undefined";

public appBaseHref: string;
public examplesFolder: string;
public exampleFiles: ExampleFile[];
public showSource: boolean;
public examplesFolder = "assets/examples/";
public exampleFiles: ExampleFile[] = [];
public showSource = false;

public constructor(private fileService: FileService, @Inject(STARK_LOGGING_SERVICE) public logger: StarkLoggingService) {
this.extensions = ["HTML", "TS", "CSS"];
this.exampleFiles = [];
this.showSource = false;
this.appBaseHref = this.getAppBaseHref();
this.examplesFolder = "assets/examples/";
}

public ngOnInit(): void {
Expand Down

0 comments on commit 709c261

Please sign in to comment.