mirror of
https://github.com/angular/angular.git
synced 2026-09-14 13:54:52 +08:00
feat(material-experimental): add test harness for button (#16556)
This commit is contained in:
@@ -1,14 +1,17 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load("@io_bazel_rules_sass//:defs.bzl", "sass_binary", "sass_library")
|
||||
load("//tools:defaults.bzl", "ng_e2e_test_library", "ng_module")
|
||||
load("//tools:defaults.bzl", "ng_e2e_test_library", "ng_module", "ng_test_library", "ng_web_test_suite", "ts_library")
|
||||
load("//src/e2e-app:test_suite.bzl", "e2e_test_suite")
|
||||
|
||||
ng_module(
|
||||
name = "mdc-button",
|
||||
srcs = glob(
|
||||
["**/*.ts"],
|
||||
exclude = ["**/*.spec.ts"],
|
||||
exclude = [
|
||||
"**/*.spec.ts",
|
||||
"harness/**",
|
||||
],
|
||||
),
|
||||
assets = [
|
||||
":button_scss",
|
||||
@@ -22,6 +25,18 @@ ng_module(
|
||||
],
|
||||
)
|
||||
|
||||
ts_library(
|
||||
name = "harness",
|
||||
srcs = glob(
|
||||
["harness/**/*.ts"],
|
||||
exclude = ["**/*.spec.ts"],
|
||||
),
|
||||
deps = [
|
||||
"//src/cdk-experimental/testing",
|
||||
"//src/cdk/coercion",
|
||||
],
|
||||
)
|
||||
|
||||
sass_library(
|
||||
name = "mdc_button_scss_lib",
|
||||
srcs = glob(["**/_*.scss"]),
|
||||
@@ -71,6 +86,31 @@ sass_binary(
|
||||
],
|
||||
)
|
||||
|
||||
ng_test_library(
|
||||
name = "button_tests_lib",
|
||||
srcs = [
|
||||
"harness/button-harness.spec.ts",
|
||||
],
|
||||
deps = [
|
||||
":harness",
|
||||
":mdc-button",
|
||||
"//src/cdk-experimental/testing",
|
||||
"//src/cdk-experimental/testing/testbed",
|
||||
"//src/cdk/platform",
|
||||
"//src/cdk/testing",
|
||||
"//src/material/button",
|
||||
"@npm//@angular/platform-browser",
|
||||
],
|
||||
)
|
||||
|
||||
ng_web_test_suite(
|
||||
name = "unit_tests",
|
||||
deps = [
|
||||
":button_tests_lib",
|
||||
"//src/material-experimental:mdc_require_config.js",
|
||||
],
|
||||
)
|
||||
|
||||
ng_e2e_test_library(
|
||||
name = "e2e_test_sources",
|
||||
srcs = glob(["**/*.e2e.spec.ts"]),
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
// TODO: copy tests from existing mat-button, update as necessary to fix.
|
||||
@@ -0,0 +1,11 @@
|
||||
/**
|
||||
* @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 type ButtonHarnessFilters = {
|
||||
text?: string | RegExp
|
||||
};
|
||||
@@ -0,0 +1,159 @@
|
||||
import {HarnessLoader} from '@angular/cdk-experimental/testing';
|
||||
import {TestbedHarnessEnvironment} from '@angular/cdk-experimental/testing/testbed';
|
||||
import {Platform, PlatformModule} from '@angular/cdk/platform';
|
||||
import {Component} from '@angular/core';
|
||||
import {ComponentFixture, inject, TestBed} from '@angular/core/testing';
|
||||
import {MatButtonModule} from '@angular/material/button';
|
||||
import {MatButtonModule as MatMdcButtonModule} from '../index';
|
||||
import {MatButtonHarness} from './button-harness';
|
||||
import {MatButtonHarness as MatMdcButtonHarness} from './mdc-button-harness';
|
||||
|
||||
let fixture: ComponentFixture<ButtonHarnessTest>;
|
||||
let loader: HarnessLoader;
|
||||
let buttonHarness: typeof MatButtonHarness;
|
||||
let platform: Platform;
|
||||
|
||||
describe('MatButtonHarness', () => {
|
||||
describe('non-MDC-based', () => {
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [MatButtonModule, PlatformModule],
|
||||
declarations: [ButtonHarnessTest],
|
||||
}).compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(ButtonHarnessTest);
|
||||
fixture.detectChanges();
|
||||
loader = TestbedHarnessEnvironment.loader(fixture);
|
||||
buttonHarness = MatButtonHarness;
|
||||
});
|
||||
|
||||
runTests();
|
||||
});
|
||||
|
||||
describe('MDC-based', () => {
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [MatMdcButtonModule],
|
||||
declarations: [ButtonHarnessTest],
|
||||
}).compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(ButtonHarnessTest);
|
||||
fixture.detectChanges();
|
||||
loader = TestbedHarnessEnvironment.loader(fixture);
|
||||
// Public APIs are the same as MatButtonHarness, but cast is necessary because of different
|
||||
// private fields.
|
||||
buttonHarness = MatMdcButtonHarness as any;
|
||||
});
|
||||
|
||||
runTests();
|
||||
});
|
||||
});
|
||||
|
||||
/** Shared tests to run on both the original and MDC-based buttons. */
|
||||
function runTests() {
|
||||
beforeEach(inject([Platform], (p: Platform) => {
|
||||
platform = p;
|
||||
}));
|
||||
|
||||
it('should load all button harnesses', async () => {
|
||||
const buttons = await loader.getAllHarnesses(buttonHarness);
|
||||
expect(buttons.length).toBe(14);
|
||||
});
|
||||
|
||||
it('should load button with exact text', async () => {
|
||||
const buttons = await loader.getAllHarnesses(buttonHarness.with({text: 'Basic button'}));
|
||||
expect(buttons.length).toBe(1);
|
||||
expect(await buttons[0].getText()).toBe('Basic button');
|
||||
});
|
||||
|
||||
it('should load button with regex label match', async () => {
|
||||
const buttons = await loader.getAllHarnesses(buttonHarness.with({text: /basic/i}));
|
||||
expect(buttons.length).toBe(2);
|
||||
expect(await buttons[0].getText()).toBe('Basic button');
|
||||
expect(await buttons[1].getText()).toBe('Basic anchor');
|
||||
});
|
||||
|
||||
it('should get disabled state', async () => {
|
||||
// Grab each combination of [enabled, disabled] ⨯ [button, anchor]
|
||||
const [disabledFlatButton, enabledFlatAnchor] =
|
||||
await loader.getAllHarnesses(buttonHarness.with({text: /flat/i}));
|
||||
const [enabledRaisedButton, disabledRaisedAnchor] =
|
||||
await loader.getAllHarnesses(buttonHarness.with({text: /raised/i}));
|
||||
|
||||
expect(await enabledFlatAnchor.isDisabled()).toBe(false);
|
||||
expect(await disabledFlatButton.isDisabled()).toBe(true);
|
||||
expect(await enabledRaisedButton.isDisabled()).toBe(false);
|
||||
expect(await disabledRaisedAnchor.isDisabled()).toBe(true);
|
||||
});
|
||||
|
||||
it('should get button text', async () => {
|
||||
const [firstButton, secondButton] = await loader.getAllHarnesses(buttonHarness);
|
||||
expect(await firstButton.getText()).toBe('Basic button');
|
||||
expect(await secondButton.getText()).toBe('Flat button');
|
||||
});
|
||||
|
||||
it('should focus and blur a button', async () => {
|
||||
const button = await loader.getHarness(buttonHarness.with({text: 'Basic button'}));
|
||||
expect(getActiveElementId()).not.toBe('basic');
|
||||
await button.foucs();
|
||||
expect(getActiveElementId()).toBe('basic');
|
||||
await button.blur();
|
||||
expect(getActiveElementId()).not.toBe('basic');
|
||||
});
|
||||
|
||||
it('should click a button', async () => {
|
||||
const button = await loader.getHarness(buttonHarness.with({text: 'Basic button'}));
|
||||
await button.click();
|
||||
|
||||
expect(fixture.componentInstance.clicked).toBe(true);
|
||||
});
|
||||
|
||||
it('should not click a disabled button', async () => {
|
||||
// Older versions of Edge have a bug where `disabled` buttons are still clickable if
|
||||
// they contain child elements. We skip this check on Edge.
|
||||
// See https://stackoverflow.com/questions/32377026/disabled-button-is-clickable-on-edge-browser
|
||||
if (platform.EDGE) {
|
||||
return;
|
||||
}
|
||||
|
||||
const button = await loader.getHarness(buttonHarness.with({text: 'Flat button'}));
|
||||
await button.click();
|
||||
|
||||
expect(fixture.componentInstance.clicked).toBe(false);
|
||||
});
|
||||
}
|
||||
|
||||
function getActiveElementId() {
|
||||
return document.activeElement ? document.activeElement.id : '';
|
||||
}
|
||||
|
||||
@Component({
|
||||
// Include one of each type of button selector to ensure that they're all captured by
|
||||
// the harness's selector.
|
||||
template: `
|
||||
<button id="basic" type="button" mat-button (click)="clicked = true">
|
||||
Basic button
|
||||
</button>
|
||||
<button id="flat" type="button" mat-flat-button disabled (click)="clicked = true">
|
||||
Flat button
|
||||
</button>
|
||||
<button id="raised" type="button" mat-raised-button>Raised button</button>
|
||||
<button id="stroked" type="button" mat-stroked-button>Stroked button</button>
|
||||
<button id="icon" type="button" mat-icon-button>Icon button</button>
|
||||
<button id="fab" type="button" mat-fab>Fab button</button>
|
||||
<button id="mini-fab" type="button" mat-mini-fab>Mini Fab button</button>
|
||||
|
||||
<a id="anchor-basic" mat-button>Basic anchor</a>
|
||||
<a id="anchor-flat" mat-flat-button>Flat anchor</a>
|
||||
<a id="anchor-raised" mat-raised-button disabled>Raised anchor</a>
|
||||
<a id="anchor-stroked" mat-stroked-button>Stroked anchor</a>
|
||||
<a id="anchor-icon" mat-icon-button>Icon anchor</a>
|
||||
<a id="anchor-fab" mat-fab>Fab anchor</a>
|
||||
<a id="anchor-mini-fab" mat-mini-fab>Mini Fab anchor</a>
|
||||
`
|
||||
})
|
||||
class ButtonHarnessTest {
|
||||
disabled = true;
|
||||
clicked = false;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
/**
|
||||
* @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-experimental/testing';
|
||||
import {coerceBooleanProperty} from '@angular/cdk/coercion';
|
||||
import {ButtonHarnessFilters} from './button-harness-filters';
|
||||
|
||||
|
||||
/**
|
||||
* Harness for interacting with a standard mat-button in tests.
|
||||
* @dynamic
|
||||
*/
|
||||
export class MatButtonHarness extends ComponentHarness {
|
||||
// TODO(jelbourn) use a single class, like `.mat-button-base`
|
||||
static hostSelector = [
|
||||
'[mat-button]',
|
||||
'[mat-raised-button]',
|
||||
'[mat-flat-button]',
|
||||
'[mat-icon-button]',
|
||||
'[mat-stroked-button]',
|
||||
'[mat-fab]',
|
||||
'[mat-mini-fab]',
|
||||
].join(',');
|
||||
|
||||
/**
|
||||
* Gets a `HarnessPredicate` that can be used to search for a button with specific attributes.
|
||||
* @param options Options for narrowing the search:
|
||||
* - `text` finds a button with specific text content.
|
||||
* @return a `HarnessPredicate` configured with the given options.
|
||||
*/
|
||||
static with(options: ButtonHarnessFilters = {}): HarnessPredicate<MatButtonHarness> {
|
||||
return new HarnessPredicate(MatButtonHarness)
|
||||
.addOption('text', options.text,
|
||||
(harness, text) => HarnessPredicate.stringMatches(harness.getText(), text));
|
||||
}
|
||||
|
||||
/** Clicks the button. */
|
||||
async click(): Promise<void> {
|
||||
return (await this.host()).click();
|
||||
}
|
||||
|
||||
/** Gets a boolean promise indicating if the button is disabled. */
|
||||
async isDisabled(): Promise<boolean> {
|
||||
const disabled = (await this.host()).getAttribute('disabled');
|
||||
return coerceBooleanProperty(await disabled);
|
||||
}
|
||||
|
||||
/** Gets a promise for the button's label text. */
|
||||
async getText(): Promise<string> {
|
||||
return (await this.host()).text();
|
||||
}
|
||||
|
||||
/** Focuses the button and returns a void promise that indicates when the action is complete. */
|
||||
async foucs(): Promise<void> {
|
||||
return (await this.host()).focus();
|
||||
}
|
||||
|
||||
/** Blurs the button and returns a void promise that indicates when the action is complete. */
|
||||
async blur(): Promise<void> {
|
||||
return (await this.host()).blur();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
/**
|
||||
* @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-experimental/testing';
|
||||
import {coerceBooleanProperty} from '@angular/cdk/coercion';
|
||||
import {ButtonHarnessFilters} from './button-harness-filters';
|
||||
|
||||
|
||||
/**
|
||||
* Harness for interacting with a MDC-based mat-button in tests.
|
||||
* @dynamic
|
||||
*/
|
||||
export class MatButtonHarness extends ComponentHarness {
|
||||
// TODO(jelbourn) use a single class, like `.mat-button-base`
|
||||
static hostSelector = [
|
||||
'[mat-button]',
|
||||
'[mat-raised-button]',
|
||||
'[mat-flat-button]',
|
||||
'[mat-icon-button]',
|
||||
'[mat-stroked-button]',
|
||||
'[mat-fab]',
|
||||
'[mat-mini-fab]',
|
||||
].join(',');
|
||||
|
||||
/**
|
||||
* Gets a `HarnessPredicate` that can be used to search for a button with specific attributes.
|
||||
* @param options Options for narrowing the search:
|
||||
* - `text` finds a button with specific text content.
|
||||
* @return a `HarnessPredicate` configured with the given options.
|
||||
*/
|
||||
static with(options: ButtonHarnessFilters = {}): HarnessPredicate<MatButtonHarness> {
|
||||
return new HarnessPredicate(MatButtonHarness)
|
||||
.addOption('text', options.text,
|
||||
(harness, text) => HarnessPredicate.stringMatches(harness.getText(), text));
|
||||
}
|
||||
|
||||
/** Clicks the button. */
|
||||
async click(): Promise<void> {
|
||||
return (await this.host()).click();
|
||||
}
|
||||
|
||||
/** Gets a boolean promise indicating if the button is disabled. */
|
||||
async isDisabled(): Promise<boolean> {
|
||||
const disabled = (await this.host()).getAttribute('disabled');
|
||||
return coerceBooleanProperty(await disabled);
|
||||
}
|
||||
|
||||
/** Gets a promise for the button's label text. */
|
||||
async getText(): Promise<string> {
|
||||
return (await this.host()).text();
|
||||
}
|
||||
|
||||
/** Focuses the button and returns a void promise that indicates when the action is complete. */
|
||||
async foucs(): Promise<void> {
|
||||
return (await this.host()).focus();
|
||||
}
|
||||
|
||||
/** Blurs the button and returns a void promise that indicates when the action is complete. */
|
||||
async blur(): Promise<void> {
|
||||
return (await this.host()).blur();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user