Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 88631b9

Browse files
feat(material-experimental): add test harness for button (#16556)
1 parent 2a04ccf commit 88631b9

File tree

6 files changed

+346
-3
lines changed

6 files changed

+346
-3
lines changed

‎src/material-experimental/mdc-button/BUILD.bazel

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,17 @@
11
package(default_visibility = ["//visibility:public"])
22

33
load("@io_bazel_rules_sass//:defs.bzl", "sass_binary", "sass_library")
4-
load("//tools:defaults.bzl", "ng_e2e_test_library", "ng_module")
4+
load("//tools:defaults.bzl", "ng_e2e_test_library", "ng_module", "ng_test_library", "ng_web_test_suite", "ts_library")
55
load("//src/e2e-app:test_suite.bzl", "e2e_test_suite")
66

77
ng_module(
88
name = "mdc-button",
99
srcs = glob(
1010
["**/*.ts"],
11-
exclude = ["**/*.spec.ts"],
11+
exclude = [
12+
"**/*.spec.ts",
13+
"harness/**",
14+
],
1215
),
1316
assets = [
1417
":button_scss",
@@ -22,6 +25,18 @@ ng_module(
2225
],
2326
)
2427

28+
ts_library(
29+
name = "harness",
30+
srcs = glob(
31+
["harness/**/*.ts"],
32+
exclude = ["**/*.spec.ts"],
33+
),
34+
deps = [
35+
"//src/cdk-experimental/testing",
36+
"//src/cdk/coercion",
37+
],
38+
)
39+
2540
sass_library(
2641
name = "mdc_button_scss_lib",
2742
srcs = glob(["**/_*.scss"]),
@@ -71,6 +86,31 @@ sass_binary(
7186
],
7287
)
7388

89+
ng_test_library(
90+
name = "button_tests_lib",
91+
srcs = [
92+
"harness/button-harness.spec.ts",
93+
],
94+
deps = [
95+
":harness",
96+
":mdc-button",
97+
"//src/cdk-experimental/testing",
98+
"//src/cdk-experimental/testing/testbed",
99+
"//src/cdk/platform",
100+
"//src/cdk/testing",
101+
"//src/material/button",
102+
"@npm//@angular/platform-browser",
103+
],
104+
)
105+
106+
ng_web_test_suite(
107+
name = "unit_tests",
108+
deps = [
109+
":button_tests_lib",
110+
"//src/material-experimental:mdc_require_config.js",
111+
],
112+
)
113+
74114
ng_e2e_test_library(
75115
name = "e2e_test_sources",
76116
srcs = glob(["**/*.e2e.spec.ts"]),

‎src/material-experimental/mdc-button/button.spec.ts

Lines changed: 0 additions & 1 deletion
This file was deleted.
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
/**
2+
* @license
3+
* Copyright Google LLC All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.io/license
7+
*/
8+
9+
export type ButtonHarnessFilters = {
10+
text?: string | RegExp
11+
};
Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
import {HarnessLoader} from '@angular/cdk-experimental/testing';
2+
import {TestbedHarnessEnvironment} from '@angular/cdk-experimental/testing/testbed';
3+
import {Platform, PlatformModule} from '@angular/cdk/platform';
4+
import {Component} from '@angular/core';
5+
import {ComponentFixture, inject, TestBed} from '@angular/core/testing';
6+
import {MatButtonModule} from '@angular/material/button';
7+
import {MatButtonModule as MatMdcButtonModule} from '../index';
8+
import {MatButtonHarness} from './button-harness';
9+
import {MatButtonHarness as MatMdcButtonHarness} from './mdc-button-harness';
10+
11+
let fixture: ComponentFixture<ButtonHarnessTest>;
12+
let loader: HarnessLoader;
13+
let buttonHarness: typeof MatButtonHarness;
14+
let platform: Platform;
15+
16+
describe('MatButtonHarness', () => {
17+
describe('non-MDC-based', () => {
18+
beforeEach(async () => {
19+
await TestBed.configureTestingModule({
20+
imports: [MatButtonModule, PlatformModule],
21+
declarations: [ButtonHarnessTest],
22+
}).compileComponents();
23+
24+
fixture = TestBed.createComponent(ButtonHarnessTest);
25+
fixture.detectChanges();
26+
loader = TestbedHarnessEnvironment.loader(fixture);
27+
buttonHarness = MatButtonHarness;
28+
});
29+
30+
runTests();
31+
});
32+
33+
describe('MDC-based', () => {
34+
beforeEach(async () => {
35+
await TestBed.configureTestingModule({
36+
imports: [MatMdcButtonModule],
37+
declarations: [ButtonHarnessTest],
38+
}).compileComponents();
39+
40+
fixture = TestBed.createComponent(ButtonHarnessTest);
41+
fixture.detectChanges();
42+
loader = TestbedHarnessEnvironment.loader(fixture);
43+
// Public APIs are the same as MatButtonHarness, but cast is necessary because of different
44+
// private fields.
45+
buttonHarness = MatMdcButtonHarness as any;
46+
});
47+
48+
runTests();
49+
});
50+
});
51+
52+
/** Shared tests to run on both the original and MDC-based buttons. */
53+
function runTests() {
54+
beforeEach(inject([Platform], (p: Platform) => {
55+
platform = p;
56+
}));
57+
58+
it('should load all button harnesses', async () => {
59+
const buttons = await loader.getAllHarnesses(buttonHarness);
60+
expect(buttons.length).toBe(14);
61+
});
62+
63+
it('should load button with exact text', async () => {
64+
const buttons = await loader.getAllHarnesses(buttonHarness.with({text: 'Basic button'}));
65+
expect(buttons.length).toBe(1);
66+
expect(await buttons[0].getText()).toBe('Basic button');
67+
});
68+
69+
it('should load button with regex label match', async () => {
70+
const buttons = await loader.getAllHarnesses(buttonHarness.with({text: /basic/i}));
71+
expect(buttons.length).toBe(2);
72+
expect(await buttons[0].getText()).toBe('Basic button');
73+
expect(await buttons[1].getText()).toBe('Basic anchor');
74+
});
75+
76+
it('should get disabled state', async () => {
77+
// Grab each combination of [enabled, disabled] ⨯ [button, anchor]
78+
const [disabledFlatButton, enabledFlatAnchor] =
79+
await loader.getAllHarnesses(buttonHarness.with({text: /flat/i}));
80+
const [enabledRaisedButton, disabledRaisedAnchor] =
81+
await loader.getAllHarnesses(buttonHarness.with({text: /raised/i}));
82+
83+
expect(await enabledFlatAnchor.isDisabled()).toBe(false);
84+
expect(await disabledFlatButton.isDisabled()).toBe(true);
85+
expect(await enabledRaisedButton.isDisabled()).toBe(false);
86+
expect(await disabledRaisedAnchor.isDisabled()).toBe(true);
87+
});
88+
89+
it('should get button text', async () => {
90+
const [firstButton, secondButton] = await loader.getAllHarnesses(buttonHarness);
91+
expect(await firstButton.getText()).toBe('Basic button');
92+
expect(await secondButton.getText()).toBe('Flat button');
93+
});
94+
95+
it('should focus and blur a button', async () => {
96+
const button = await loader.getHarness(buttonHarness.with({text: 'Basic button'}));
97+
expect(getActiveElementId()).not.toBe('basic');
98+
await button.foucs();
99+
expect(getActiveElementId()).toBe('basic');
100+
await button.blur();
101+
expect(getActiveElementId()).not.toBe('basic');
102+
});
103+
104+
it('should click a button', async () => {
105+
const button = await loader.getHarness(buttonHarness.with({text: 'Basic button'}));
106+
await button.click();
107+
108+
expect(fixture.componentInstance.clicked).toBe(true);
109+
});
110+
111+
it('should not click a disabled button', async () => {
112+
// Older versions of Edge have a bug where `disabled` buttons are still clickable if
113+
// they contain child elements. We skip this check on Edge.
114+
// See https://stackoverflow.com/questions/32377026/disabled-button-is-clickable-on-edge-browser
115+
if (platform.EDGE) {
116+
return;
117+
}
118+
119+
const button = await loader.getHarness(buttonHarness.with({text: 'Flat button'}));
120+
await button.click();
121+
122+
expect(fixture.componentInstance.clicked).toBe(false);
123+
});
124+
}
125+
126+
function getActiveElementId() {
127+
return document.activeElement ? document.activeElement.id : '';
128+
}
129+
130+
@Component({
131+
// Include one of each type of button selector to ensure that they're all captured by
132+
// the harness's selector.
133+
template: `
134+
<button id="basic" type="button" mat-button (click)="clicked = true">
135+
Basic button
136+
</button>
137+
<button id="flat" type="button" mat-flat-button disabled (click)="clicked = true">
138+
Flat button
139+
</button>
140+
<button id="raised" type="button" mat-raised-button>Raised button</button>
141+
<button id="stroked" type="button" mat-stroked-button>Stroked button</button>
142+
<button id="icon" type="button" mat-icon-button>Icon button</button>
143+
<button id="fab" type="button" mat-fab>Fab button</button>
144+
<button id="mini-fab" type="button" mat-mini-fab>Mini Fab button</button>
145+
146+
<a id="anchor-basic" mat-button>Basic anchor</a>
147+
<a id="anchor-flat" mat-flat-button>Flat anchor</a>
148+
<a id="anchor-raised" mat-raised-button disabled>Raised anchor</a>
149+
<a id="anchor-stroked" mat-stroked-button>Stroked anchor</a>
150+
<a id="anchor-icon" mat-icon-button>Icon anchor</a>
151+
<a id="anchor-fab" mat-fab>Fab anchor</a>
152+
<a id="anchor-mini-fab" mat-mini-fab>Mini Fab anchor</a>
153+
`
154+
})
155+
class ButtonHarnessTest {
156+
disabled = true;
157+
clicked = false;
158+
}
159+
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
/**
2+
* @license
3+
* Copyright Google LLC All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.io/license
7+
*/
8+
9+
import {ComponentHarness, HarnessPredicate} from '@angular/cdk-experimental/testing';
10+
import {coerceBooleanProperty} from '@angular/cdk/coercion';
11+
import {ButtonHarnessFilters} from './button-harness-filters';
12+
13+
14+
/**
15+
* Harness for interacting with a standard mat-button in tests.
16+
* @dynamic
17+
*/
18+
export class MatButtonHarness extends ComponentHarness {
19+
// TODO(jelbourn) use a single class, like `.mat-button-base`
20+
static hostSelector = [
21+
'[mat-button]',
22+
'[mat-raised-button]',
23+
'[mat-flat-button]',
24+
'[mat-icon-button]',
25+
'[mat-stroked-button]',
26+
'[mat-fab]',
27+
'[mat-mini-fab]',
28+
].join(',');
29+
30+
/**
31+
* Gets a `HarnessPredicate` that can be used to search for a button with specific attributes.
32+
* @param options Options for narrowing the search:
33+
* - `text` finds a button with specific text content.
34+
* @return a `HarnessPredicate` configured with the given options.
35+
*/
36+
static with(options: ButtonHarnessFilters = {}): HarnessPredicate<MatButtonHarness> {
37+
return new HarnessPredicate(MatButtonHarness)
38+
.addOption('text', options.text,
39+
(harness, text) => HarnessPredicate.stringMatches(harness.getText(), text));
40+
}
41+
42+
/** Clicks the button. */
43+
async click(): Promise<void> {
44+
return (await this.host()).click();
45+
}
46+
47+
/** Gets a boolean promise indicating if the button is disabled. */
48+
async isDisabled(): Promise<boolean> {
49+
const disabled = (await this.host()).getAttribute('disabled');
50+
return coerceBooleanProperty(await disabled);
51+
}
52+
53+
/** Gets a promise for the button's label text. */
54+
async getText(): Promise<string> {
55+
return (await this.host()).text();
56+
}
57+
58+
/** Focuses the button and returns a void promise that indicates when the action is complete. */
59+
async foucs(): Promise<void> {
60+
return (await this.host()).focus();
61+
}
62+
63+
/** Blurs the button and returns a void promise that indicates when the action is complete. */
64+
async blur(): Promise<void> {
65+
return (await this.host()).blur();
66+
}
67+
}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
/**
2+
* @license
3+
* Copyright Google LLC All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.io/license
7+
*/
8+
9+
import {ComponentHarness, HarnessPredicate} from '@angular/cdk-experimental/testing';
10+
import {coerceBooleanProperty} from '@angular/cdk/coercion';
11+
import {ButtonHarnessFilters} from './button-harness-filters';
12+
13+
14+
/**
15+
* Harness for interacting with a MDC-based mat-button in tests.
16+
* @dynamic
17+
*/
18+
export class MatButtonHarness extends ComponentHarness {
19+
// TODO(jelbourn) use a single class, like `.mat-button-base`
20+
static hostSelector = [
21+
'[mat-button]',
22+
'[mat-raised-button]',
23+
'[mat-flat-button]',
24+
'[mat-icon-button]',
25+
'[mat-stroked-button]',
26+
'[mat-fab]',
27+
'[mat-mini-fab]',
28+
].join(',');
29+
30+
/**
31+
* Gets a `HarnessPredicate` that can be used to search for a button with specific attributes.
32+
* @param options Options for narrowing the search:
33+
* - `text` finds a button with specific text content.
34+
* @return a `HarnessPredicate` configured with the given options.
35+
*/
36+
static with(options: ButtonHarnessFilters = {}): HarnessPredicate<MatButtonHarness> {
37+
return new HarnessPredicate(MatButtonHarness)
38+
.addOption('text', options.text,
39+
(harness, text) => HarnessPredicate.stringMatches(harness.getText(), text));
40+
}
41+
42+
/** Clicks the button. */
43+
async click(): Promise<void> {
44+
return (await this.host()).click();
45+
}
46+
47+
/** Gets a boolean promise indicating if the button is disabled. */
48+
async isDisabled(): Promise<boolean> {
49+
const disabled = (await this.host()).getAttribute('disabled');
50+
return coerceBooleanProperty(await disabled);
51+
}
52+
53+
/** Gets a promise for the button's label text. */
54+
async getText(): Promise<string> {
55+
return (await this.host()).text();
56+
}
57+
58+
/** Focuses the button and returns a void promise that indicates when the action is complete. */
59+
async foucs(): Promise<void> {
60+
return (await this.host()).focus();
61+
}
62+
63+
/** Blurs the button and returns a void promise that indicates when the action is complete. */
64+
async blur(): Promise<void> {
65+
return (await this.host()).blur();
66+
}
67+
}

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /