Skip to content

Commit

Permalink
feat(paginator): add test harness (#17969)
Browse files Browse the repository at this point in the history
Sets up a test harness for `mat-paginator`.
  • Loading branch information
crisbeto authored and andrewseguin committed Dec 19, 2019
1 parent 8796d7c commit 5a6cdaf
Show file tree
Hide file tree
Showing 11 changed files with 360 additions and 1 deletion.
1 change: 1 addition & 0 deletions src/material/config.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ entryPoints = [
"menu",
"menu/testing",
"paginator",
"paginator/testing",
"progress-bar",
"progress-bar/testing",
"progress-spinner",
Expand Down
4 changes: 3 additions & 1 deletion src/material/paginator/paginator.html
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@
</mat-select>
</mat-form-field>

<div *ngIf="_displayedPageSizeOptions.length <= 1">{{pageSize}}</div>
<div
class="mat-paginator-page-size-value"
*ngIf="_displayedPageSizeOptions.length <= 1">{{pageSize}}</div>
</div>

<div class="mat-paginator-range-actions">
Expand Down
53 changes: 53 additions & 0 deletions src/material/paginator/testing/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package(default_visibility = ["//visibility:public"])

load("//tools:defaults.bzl", "ng_test_library", "ng_web_test_suite", "ts_library")

ts_library(
name = "testing",
srcs = glob(
["**/*.ts"],
exclude = ["**/*.spec.ts"],
),
module_name = "@angular/material/paginator/testing",
deps = [
"//src/cdk/coercion",
"//src/cdk/testing",
"//src/material/select/testing",
],
)

filegroup(
name = "source-files",
srcs = glob(["**/*.ts"]),
)

ng_test_library(
name = "harness_tests_lib",
srcs = ["shared.spec.ts"],
deps = [
":testing",
"//src/cdk/testing",
"//src/cdk/testing/private",
"//src/cdk/testing/testbed",
"//src/material/paginator",
"@npm//@angular/platform-browser",
],
)

ng_test_library(
name = "unit_tests_lib",
srcs = glob(
["**/*.spec.ts"],
exclude = ["shared.spec.ts"],
),
deps = [
":harness_tests_lib",
":testing",
"//src/material/paginator",
],
)

ng_web_test_suite(
name = "unit_tests",
deps = [":unit_tests_lib"],
)
9 changes: 9 additions & 0 deletions src/material/paginator/testing/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/

export * from './public-api';
13 changes: 13 additions & 0 deletions src/material/paginator/testing/paginator-harness-filters.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/

import {BaseHarnessFilters} from '@angular/cdk/testing';

/** A set of criteria that can be used to filter a list of `MatPaginatorHarness` instances. */
export interface PaginatorHarnessFilters extends BaseHarnessFilters {
}
7 changes: 7 additions & 0 deletions src/material/paginator/testing/paginator-harness.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import {MatPaginatorModule} from '@angular/material/paginator';
import {runHarnessTests} from './shared.spec';
import {MatPaginatorHarness} from './paginator-harness';

describe('Non-MDC-based MatPaginatorHarness', () => {
runHarnessTests(MatPaginatorModule, MatPaginatorHarness);
});
103 changes: 103 additions & 0 deletions src/material/paginator/testing/paginator-harness.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/

import {ComponentHarness, HarnessPredicate} from '@angular/cdk/testing';
import {MatSelectHarness} from '@angular/material/select/testing';
import {coerceNumberProperty} from '@angular/cdk/coercion';
import {PaginatorHarnessFilters} from './paginator-harness-filters';


/** Harness for interacting with a standard mat-paginator in tests. */
export class MatPaginatorHarness extends ComponentHarness {
/** Selector used to find paginator instances. */
static hostSelector = '.mat-paginator';
private _nextButton = this.locatorFor('.mat-paginator-navigation-next');
private _previousButton = this.locatorFor('.mat-paginator-navigation-previous');
private _firstPageButton = this.locatorForOptional('.mat-paginator-navigation-first');
private _lastPageButton = this.locatorForOptional('.mat-paginator-navigation-last');
private _select = this.locatorForOptional(MatSelectHarness.with({
ancestor: '.mat-paginator-page-size'
}));
private _pageSizeFallback = this.locatorFor('.mat-paginator-page-size-value');
private _rangeLabel = this.locatorFor('.mat-paginator-range-label');

/**
* Gets a `HarnessPredicate` that can be used to search for a `MatPaginatorHarness` that meets
* certain criteria.
* @param options Options for filtering which paginator instances are considered a match.
* @return a `HarnessPredicate` configured with the given options.
*/
static with(options: PaginatorHarnessFilters = {}): HarnessPredicate<MatPaginatorHarness> {
return new HarnessPredicate(MatPaginatorHarness, options);
}

/** Goes to the next page in the paginator. */
async goToNextPage(): Promise<void> {
return (await this._nextButton()).click();
}

/** Goes to the previous page in the paginator. */
async goToPreviousPage(): Promise<void> {
return (await this._previousButton()).click();
}

/** Goes to the first page in the paginator. */
async goToFirstPage(): Promise<void> {
const button = await this._firstPageButton();

// The first page button isn't enabled by default so we need to check for it.
if (!button) {
throw Error('Could not find first page button inside paginator. ' +
'Make sure that `showFirstLastButtons` is enabled.');
}

return button.click();
}

/** Goes to the last page in the paginator. */
async goToLastPage(): Promise<void> {
const button = await this._lastPageButton();

// The last page button isn't enabled by default so we need to check for it.
if (!button) {
throw Error('Could not find last page button inside paginator. ' +
'Make sure that `showFirstLastButtons` is enabled.');
}

return button.click();
}

/**
* Sets the page size of the paginator.
* @param size Page size that should be select.
*/
async setPageSize(size: number): Promise<void> {
const select = await this._select();

// The select is only available if the `pageSizeOptions` are
// set to an array with more than one item.
if (!select) {
throw Error('Cannot find page size selector in paginator. ' +
'Make sure that the `pageSizeOptions` have been configured.');
}

return select.clickOptions({text: `${size}`});
}

/** Gets the page size of the paginator. */
async getPageSize(): Promise<number> {
const select = await this._select();
const value = select ? select.getValueText() : (await this._pageSizeFallback()).text();
return coerceNumberProperty(await value);
}

/** Gets the text of the range labe of the paginator. */
async getRangeLabel(): Promise<string> {
return (await this._rangeLabel()).text();
}
}
10 changes: 10 additions & 0 deletions src/material/paginator/testing/public-api.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/

export * from './paginator-harness';
export * from './paginator-harness-filters';
145 changes: 145 additions & 0 deletions src/material/paginator/testing/shared.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
import {HarnessLoader} from '@angular/cdk/testing';
import {TestbedHarnessEnvironment} from '@angular/cdk/testing/testbed';
import {Component, ViewChild} from '@angular/core';
import {NoopAnimationsModule} from '@angular/platform-browser/animations';
import {ComponentFixture, TestBed} from '@angular/core/testing';
import {MatPaginatorModule, PageEvent, MatPaginator} from '@angular/material/paginator';
import {MatPaginatorHarness} from './paginator-harness';
import {expectAsyncError} from '@angular/cdk/testing/private';

/** Shared tests to run on both the original and MDC-based paginator. */
export function runHarnessTests(
paginatorModule: typeof MatPaginatorModule, paginatorHarness: typeof MatPaginatorHarness) {
let fixture: ComponentFixture<PaginatorHarnessTest>;
let loader: HarnessLoader;
let instance: PaginatorHarnessTest;

beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [paginatorModule, NoopAnimationsModule],
declarations: [PaginatorHarnessTest],
}).compileComponents();

fixture = TestBed.createComponent(PaginatorHarnessTest);
fixture.detectChanges();
loader = TestbedHarnessEnvironment.loader(fixture);
instance = fixture.componentInstance;
});

it('should load all paginator harnesses', async () => {
const paginators = await loader.getAllHarnesses(paginatorHarness);
expect(paginators.length).toBe(1);
});

it('should be able to go to the next page', async () => {
const paginator = await loader.getHarness(paginatorHarness);

expect(instance.pageIndex).toBe(0);
await paginator.goToNextPage();
expect(instance.pageIndex).toBe(1);
});

it('should be able to go to the previous page', async () => {
const paginator = await loader.getHarness(paginatorHarness);

instance.pageIndex = 5;
fixture.detectChanges();

await paginator.goToPreviousPage();
expect(instance.pageIndex).toBe(4);
});

it('should be able to go to the first page', async () => {
const paginator = await loader.getHarness(paginatorHarness);

instance.pageIndex = 5;
fixture.detectChanges();

await paginator.goToFirstPage();
expect(instance.pageIndex).toBe(0);
});

it('should be able to go to the last page', async () => {
const paginator = await loader.getHarness(paginatorHarness);

expect(instance.pageIndex).toBe(0);
await paginator.goToLastPage();
expect(instance.pageIndex).toBe(49);
});

it('should be able to set the page size', async () => {
const paginator = await loader.getHarness(paginatorHarness);

expect(instance.pageSize).toBe(10);
await paginator.setPageSize(25);
expect(instance.pageSize).toBe(25);
});

it('should be able to get the page size', async () => {
const paginator = await loader.getHarness(paginatorHarness);
expect(await paginator.getPageSize()).toBe(10);
});

it('should be able to get the range label', async () => {
const paginator = await loader.getHarness(paginatorHarness);
expect(await paginator.getRangeLabel()).toBe('1 – 10 of 500');
});

it('should throw an error if the first page button is not available', async () => {
const paginator = await loader.getHarness(paginatorHarness);

instance.showFirstLastButtons = false;
fixture.detectChanges();

await expectAsyncError(() => paginator.goToFirstPage(),
/Error: Could not find first page button inside paginator/);
});

it('should throw an error if the last page button is not available', async () => {
const paginator = await loader.getHarness(paginatorHarness);

instance.showFirstLastButtons = false;
fixture.detectChanges();

await expectAsyncError(() => paginator.goToLastPage(),
/Error: Could not find last page button inside paginator/);
});

it('should throw an error if the page size selector is not available', async () => {
const paginator = await loader.getHarness(paginatorHarness);

instance.pageSizeOptions = [];
fixture.detectChanges();

await expectAsyncError(() => paginator.setPageSize(10),
/Error: Cannot find page size selector in paginator/);
});
}

@Component({
template: `
<mat-paginator
(page)="handlePageEvent($event)"
[length]="length"
[pageSize]="pageSize"
[showFirstLastButtons]="showFirstLastButtons"
[pageSizeOptions]="pageSizeOptions"
[pageIndex]="pageIndex">
</mat-paginator>
`
})
class PaginatorHarnessTest {
@ViewChild(MatPaginator) paginator: MatPaginator;
length = 500;
pageSize = 10;
pageIndex = 0;
pageSizeOptions = [5, 10, 25];
showFirstLastButtons = true;

handlePageEvent(event: PageEvent) {
this.length = event.length;
this.pageSize = event.pageSize;
this.pageIndex = event.pageIndex;
}
}

2 changes: 2 additions & 0 deletions test/karma-system-config.js
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,8 @@ System.config({
'@angular/material/menu/testing': 'dist/packages/material/menu/testing/index.js',
'@angular/material/menu/testing/shared.spec': 'dist/packages/material/menu/testing/shared.spec.js',
'@angular/material/paginator': 'dist/packages/material/paginator/index.js',
'@angular/material/paginator/testing': 'dist/packages/material/paginator/testing/index.js',
'@angular/material/paginator/testing/shared.spec': 'dist/packages/material/paginator/testing/shared.spec.js',
'@angular/material/progress-bar': 'dist/packages/material/progress-bar/index.js',
'@angular/material/progress-bar/testing': 'dist/packages/material/progress-bar/testing/index.js',
'@angular/material/progress-bar/testing/shared.spec': 'dist/packages/material/progress-bar/testing/shared.spec.js',
Expand Down
14 changes: 14 additions & 0 deletions tools/public_api_guard/material/paginator/testing.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
export declare class MatPaginatorHarness extends ComponentHarness {
getPageSize(): Promise<number>;
getRangeLabel(): Promise<string>;
goToFirstPage(): Promise<void>;
goToLastPage(): Promise<void>;
goToNextPage(): Promise<void>;
goToPreviousPage(): Promise<void>;
setPageSize(size: number): Promise<void>;
static hostSelector: string;
static with(options?: PaginatorHarnessFilters): HarnessPredicate<MatPaginatorHarness>;
}

export interface PaginatorHarnessFilters extends BaseHarnessFilters {
}

0 comments on commit 5a6cdaf

Please sign in to comment.