Skip to content

Commit

Permalink
feat(core): add test harnesses for mat-option and mat-optgroup (#17940)
Browse files Browse the repository at this point in the history
Combines the option and option group harnesses from `select/testing` and `autocomplete/testing`. Also expands the harnesses to cover all of the supported uses of the components.
  • Loading branch information
crisbeto authored and mmalerba committed Dec 13, 2019
1 parent 887d063 commit 0ec499d
Show file tree
Hide file tree
Showing 23 changed files with 508 additions and 176 deletions.
1 change: 1 addition & 0 deletions .github/CODEOWNERS
Validating CODEOWNERS rules …
Expand Up @@ -40,6 +40,7 @@

# Angular Material core
/src/material/core/* @jelbourn
/src/material/core/testing/** @crisbeto
/src/material/core/animation/** @jelbourn
/src/material/core/common-behaviors/** @jelbourn @devversion
/src/material/core/datetime/** @mmalerba
Expand Down
1 change: 1 addition & 0 deletions src/material/autocomplete/testing/BUILD.bazel
Expand Up @@ -12,6 +12,7 @@ ts_library(
deps = [
"//src/cdk/coercion",
"//src/cdk/testing",
"//src/material/core/testing",
],
)

Expand Down
30 changes: 18 additions & 12 deletions src/material/autocomplete/testing/autocomplete-harness.ts
Expand Up @@ -8,13 +8,13 @@

import {coerceBooleanProperty} from '@angular/cdk/coercion';
import {ComponentHarness, HarnessPredicate} from '@angular/cdk/testing';
import {AutocompleteHarnessFilters} from './autocomplete-harness-filters';
import {
MatAutocompleteOptionGroupHarness,
MatAutocompleteOptionHarness,
OptionGroupHarnessFilters,
MatOptgroupHarness,
MatOptionHarness,
OptgroupHarnessFilters,
OptionHarnessFilters
} from './option-harness';
} from '@angular/material/core/testing';
import {AutocompleteHarnessFilters} from './autocomplete-harness-filters';

/** Selector for the autocomplete panel. */
const PANEL_SELECTOR = '.mat-autocomplete-panel';
Expand Down Expand Up @@ -66,15 +66,21 @@ export class MatAutocompleteHarness extends ComponentHarness {
}

/** Gets the options inside the autocomplete panel. */
async getOptions(filters: OptionHarnessFilters = {}): Promise<MatAutocompleteOptionHarness[]> {
return this._documentRootLocator.locatorForAll(MatAutocompleteOptionHarness.with(filters))();
async getOptions(filters: Omit<OptionHarnessFilters, 'ancestor'> = {}):
Promise<MatOptionHarness[]> {
return this._documentRootLocator.locatorForAll(MatOptionHarness.with({
...filters,
ancestor: PANEL_SELECTOR
}))();
}

/** Gets the option groups inside the autocomplete panel. */
async getOptionGroups(filters: OptionGroupHarnessFilters = {}):
Promise<MatAutocompleteOptionGroupHarness[]> {
return this._documentRootLocator.locatorForAll(
MatAutocompleteOptionGroupHarness.with(filters))();
async getOptionGroups(filters: Omit<OptgroupHarnessFilters, 'ancestor'> = {}):
Promise<MatOptgroupHarness[]> {
return this._documentRootLocator.locatorForAll(MatOptgroupHarness.with({
...filters,
ancestor: PANEL_SELECTOR
}))();
}

/** Selects the first option matching the given filters. */
Expand All @@ -84,7 +90,7 @@ export class MatAutocompleteHarness extends ComponentHarness {
if (!options.length) {
throw Error(`Could not find a mat-option matching ${JSON.stringify(filters)}`);
}
await options[0].select();
await options[0].click();
}

/** Whether the autocomplete is open. */
Expand Down
82 changes: 0 additions & 82 deletions src/material/autocomplete/testing/option-harness.ts

This file was deleted.

1 change: 1 addition & 0 deletions src/material/config.bzl
Expand Up @@ -13,6 +13,7 @@ entryPoints = [
"checkbox/testing",
"chips",
"core",
"core/testing",
"datepicker",
"dialog",
"dialog/testing",
Expand Down
55 changes: 55 additions & 0 deletions src/material/core/testing/BUILD.bazel
@@ -0,0 +1,55 @@
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/core/testing",
deps = [
"//src/cdk/testing",
],
)

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

ng_test_library(
name = "harness_tests_lib",
srcs = [
"optgroup-shared.spec.ts",
"option-shared.spec.ts",
],
deps = [
":testing",
"//src/cdk/testing",
"//src/cdk/testing/testbed",
"//src/material/core",
],
)

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

ng_web_test_suite(
name = "unit_tests",
deps = [":unit_tests_lib"],
)
9 changes: 9 additions & 0 deletions src/material/core/testing/index.ts
@@ -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/core/testing/optgroup-harness-filters.ts
@@ -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';

export interface OptgroupHarnessFilters extends BaseHarnessFilters {
labelText?: string | RegExp;
}
7 changes: 7 additions & 0 deletions src/material/core/testing/optgroup-harness.spec.ts
@@ -0,0 +1,7 @@
import {MatOptionModule} from '@angular/material/core';
import {runHarnessTests} from './optgroup-shared.spec';
import {MatOptgroupHarness} from './optgroup-harness';

describe('Non-MDC-based MatOptgroupHarness', () => {
runHarnessTests(MatOptionModule, MatOptgroupHarness);
});
51 changes: 51 additions & 0 deletions src/material/core/testing/optgroup-harness.ts
@@ -0,0 +1,51 @@
/**
* @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 {OptgroupHarnessFilters} from './optgroup-harness-filters';
import {MatOptionHarness} from './option-harness';
import {OptionHarnessFilters} from './option-harness-filters';

/** Harness for interacting with a `mat-optgroup` in tests. */
export class MatOptgroupHarness extends ComponentHarness {
/** Selector used to locate option group instances. */
static hostSelector = '.mat-optgroup';
private _label = this.locatorFor('.mat-optgroup-label');

/**
* Gets a `HarnessPredicate` that can be used to search for a `MatOptgroupHarness` that meets
* certain criteria.
* @param options Options for filtering which option instances are considered a match.
* @return a `HarnessPredicate` configured with the given options.
*/
static with(options: OptgroupHarnessFilters = {}) {
return new HarnessPredicate(MatOptgroupHarness, options)
.addOption('labelText', options.labelText,
async (harness, title) =>
HarnessPredicate.stringMatches(await harness.getLabelText(), title));
}

/** Gets the option group's label text. */
async getLabelText(): Promise<string> {
return (await this._label()).text();
}

/** Gets whether the option group is disabled. */
async isDisabled(): Promise<boolean> {
return (await this.host()).hasClass('mat-optgroup-disabled');
}

/**
* Gets the options that are inside the group.
* @param filter Optionally filters which options are included.
*/
async getOptions(filter: OptionHarnessFilters = {}): Promise<MatOptionHarness[]> {
return this.locatorForAll(MatOptionHarness.with(filter))();
}
}

80 changes: 80 additions & 0 deletions src/material/core/testing/optgroup-shared.spec.ts
@@ -0,0 +1,80 @@
import {HarnessLoader} from '@angular/cdk/testing';
import {TestbedHarnessEnvironment} from '@angular/cdk/testing/testbed';
import {Component} from '@angular/core';
import {ComponentFixture, TestBed} from '@angular/core/testing';
import {MatOptionModule} from '@angular/material/core';
import {MatOptgroupHarness} from './optgroup-harness';

/** Shared tests to run on both the original and MDC-based option groups. */
export function runHarnessTests(
optionModule: typeof MatOptionModule, optionGroupHarness: typeof MatOptgroupHarness) {
let fixture: ComponentFixture<OptgroupHarnessTest>;
let loader: HarnessLoader;

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

fixture = TestBed.createComponent(OptgroupHarnessTest);
fixture.detectChanges();
loader = TestbedHarnessEnvironment.loader(fixture);
});

it('should load all option group harnesses', async () => {
const groups = await loader.getAllHarnesses(optionGroupHarness);
expect(groups.length).toBe(2);
});

it('should filter groups based on their text', async () => {
const groups = await loader.getAllHarnesses(optionGroupHarness.with({
labelText: 'Disabled group'
}));

expect(groups.length).toBe(1);
});

it('should filter group label text by a pattern', async () => {
const groups = await loader.getAllHarnesses(optionGroupHarness.with({labelText: /group/}));
expect(groups.length).toBe(2);
});

it('should get the group label text', async () => {
const groups = await loader.getAllHarnesses(optionGroupHarness);
const texts = await Promise.all(groups.map(group => group.getLabelText()));
expect(texts).toEqual(['Plain group', 'Disabled group']);
});

it('should get the group disabled state', async () => {
const groups = await loader.getAllHarnesses(optionGroupHarness);
const disabledStates = await Promise.all(groups.map(group => group.isDisabled()));
expect(disabledStates).toEqual([false, true]);
});

it('should get the options inside the groups', async () => {
const group = await loader.getHarness(optionGroupHarness);
const optionTexts = await group.getOptions().then(async options => {
return await Promise.all(options.map(option => option.getText()));
});

expect(optionTexts).toEqual(['Option 1', 'Option 2']);
});
}


@Component({
template: `
<mat-optgroup label="Plain group">
<mat-option>Option 1</mat-option>
<mat-option>Option 2</mat-option>
</mat-optgroup>
<mat-optgroup label="Disabled group" disabled>
<mat-option>Disabled option 1</mat-option>
</mat-optgroup>
`
})
class OptgroupHarnessTest {
}

14 changes: 14 additions & 0 deletions src/material/core/testing/option-harness-filters.ts
@@ -0,0 +1,14 @@
/**
* @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';

export interface OptionHarnessFilters extends BaseHarnessFilters {
text?: string | RegExp;
isSelected?: boolean;
}

0 comments on commit 0ec499d

Please sign in to comment.