Skip to content

Commit b21a43c

Browse files
committed
fix(material/button): align harness with new terminology
Updates the button harness to align with the terminology for button appearances. BREAKING CHANGE: * `ButtonVariant` which is returned by `MatButtonHarness.getVariant` no longer includes the appearance of the button. Use `MatButtonHarness.getAppearance` instead.
1 parent e89a2f7 commit b21a43c

File tree

4 files changed

+121
-63
lines changed

4 files changed

+121
-63
lines changed

src/material/button/testing/button-harness-filters.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,11 @@
88

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

11+
/** Possible button variants. */
12+
export type ButtonVariant = 'basic' | 'icon' | 'fab' | 'mini-fab';
13+
1114
/** Possible button appearances. */
12-
export type ButtonVariant = 'basic' | 'raised' | 'flat' | 'icon' | 'stroked' | 'fab' | 'mini-fab';
15+
export type ButtonAppearance = 'text' | 'filled' | 'elevated' | 'outlined';
1316

1417
/** A set of criteria that can be used to filter a list of button harness instances. */
1518
export interface ButtonHarnessFilters extends BaseHarnessFilters {
@@ -19,6 +22,9 @@ export interface ButtonHarnessFilters extends BaseHarnessFilters {
1922
/** Only find instances with a variant. */
2023
variant?: ButtonVariant;
2124

25+
/** Only find instances with a specific appearance. */
26+
appearance?: ButtonAppearance;
27+
2228
/** Only find instances which match the given disabled state. */
2329
disabled?: boolean;
2430
}

src/material/button/testing/button-harness.spec.ts

Lines changed: 69 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import {Component} from '@angular/core';
2-
import {ComponentFixture, inject, TestBed} from '@angular/core/testing';
3-
import {Platform, PlatformModule} from '@angular/cdk/platform';
2+
import {ComponentFixture, TestBed} from '@angular/core/testing';
3+
import {Platform} from '@angular/cdk/platform';
44
import {HarnessLoader, parallel} from '@angular/cdk/testing';
55
import {TestbedHarnessEnvironment} from '@angular/cdk/testing/testbed';
66
import {MatButtonModule} from '../module';
@@ -14,19 +14,12 @@ describe('MatButtonHarness', () => {
1414
let platform: Platform;
1515

1616
beforeEach(() => {
17-
TestBed.configureTestingModule({
18-
imports: [MatButtonModule, MatIconModule, PlatformModule, ButtonHarnessTest],
19-
});
20-
2117
fixture = TestBed.createComponent(ButtonHarnessTest);
2218
fixture.detectChanges();
2319
loader = TestbedHarnessEnvironment.loader(fixture);
20+
platform = TestBed.inject(Platform);
2421
});
2522

26-
beforeEach(inject([Platform], (p: Platform) => {
27-
platform = p;
28-
}));
29-
3023
it('should load all button harnesses', async () => {
3124
const buttons = await loader.getAllHarnesses(MatButtonHarness);
3225
expect(buttons.length).toBe(15);
@@ -53,24 +46,24 @@ describe('MatButtonHarness', () => {
5346
});
5447

5548
it('should get disabled state', async () => {
56-
// Grab each combination of [enabled, disabled] [button, anchor]
57-
const [disabledFlatButton, enabledFlatAnchor] = await loader.getAllHarnesses(
58-
MatButtonHarness.with({text: /flat/i}),
49+
// Grab each combination of [enabled, disabled] x [button, anchor]
50+
const [disabledFilledButton, enabledFilledAnchor] = await loader.getAllHarnesses(
51+
MatButtonHarness.with({text: /filled/i}),
5952
);
60-
const [enabledRaisedButton, disabledRaisedAnchor] = await loader.getAllHarnesses(
61-
MatButtonHarness.with({text: /raised/i}),
53+
const [enabledElevatedButton, disabledElevatedAnchor] = await loader.getAllHarnesses(
54+
MatButtonHarness.with({text: /elevated/i}),
6255
);
6356

64-
expect(await enabledFlatAnchor.isDisabled()).toBe(false);
65-
expect(await disabledFlatButton.isDisabled()).toBe(true);
66-
expect(await enabledRaisedButton.isDisabled()).toBe(false);
67-
expect(await disabledRaisedAnchor.isDisabled()).toBe(true);
57+
expect(await enabledFilledAnchor.isDisabled()).toBe(false);
58+
expect(await disabledFilledButton.isDisabled()).toBe(true);
59+
expect(await enabledElevatedButton.isDisabled()).toBe(false);
60+
expect(await disabledElevatedAnchor.isDisabled()).toBe(true);
6861
});
6962

7063
it('should get button text', async () => {
7164
const [firstButton, secondButton] = await loader.getAllHarnesses(MatButtonHarness);
7265
expect(await firstButton.getText()).toBe('Basic button');
73-
expect(await secondButton.getText()).toBe('Flat button');
66+
expect(await secondButton.getText()).toBe('Filled button');
7467
});
7568

7669
it('should focus and blur a button', async () => {
@@ -99,7 +92,7 @@ describe('MatButtonHarness', () => {
9992
return;
10093
}
10194

102-
const button = await loader.getHarness(MatButtonHarness.with({text: 'Flat button'}));
95+
const button = await loader.getHarness(MatButtonHarness.with({text: 'Filled button'}));
10396
await button.click();
10497

10598
expect(fixture.componentInstance.clicked).toBe(false);
@@ -116,65 +109,93 @@ describe('MatButtonHarness', () => {
116109
expect(await favIcon.getName()).toBe('favorite');
117110
});
118111

119-
it('should load all button harnesses', async () => {
112+
it('should be able to ge the type variant of the button', async () => {
120113
const buttons = await loader.getAllHarnesses(MatButtonHarness);
121114
const variants = await parallel(() => buttons.map(button => button.getVariant()));
122115

123116
expect(variants).toEqual([
124117
'basic',
125-
'flat',
126-
'raised',
127-
'stroked',
118+
'basic',
119+
'basic',
120+
'basic',
128121
'icon',
129122
'icon',
130123
'fab',
131124
'mini-fab',
132125
'basic',
133-
'flat',
134-
'raised',
135-
'stroked',
126+
'basic',
127+
'basic',
128+
'basic',
136129
'icon',
137130
'fab',
138131
'mini-fab',
139132
]);
140133
});
141134

135+
it('should be able to get the appearance of the button', async () => {
136+
const buttons = await loader.getAllHarnesses(MatButtonHarness);
137+
const appearances = await parallel(() => buttons.map(button => button.getAppearance()));
138+
139+
expect(appearances).toEqual([
140+
'text',
141+
'filled',
142+
'elevated',
143+
'outlined',
144+
null,
145+
null,
146+
null,
147+
null,
148+
'text',
149+
'filled',
150+
'elevated',
151+
'outlined',
152+
null,
153+
null,
154+
null,
155+
]);
156+
});
157+
142158
it('should be able to filter buttons based on their variant', async () => {
143-
const button = await loader.getHarness(MatButtonHarness.with({variant: 'flat'}));
144-
expect(await button.getText()).toBe('Flat button');
159+
const button = await loader.getHarness(MatButtonHarness.with({variant: 'fab'}));
160+
expect(await button.getText()).toBe('Fab button');
161+
});
162+
163+
it('should be able to filter buttons based on their appearance', async () => {
164+
const button = await loader.getHarness(MatButtonHarness.with({appearance: 'filled'}));
165+
expect(await button.getText()).toBe('Filled button');
145166
});
146167
});
147168

148169
@Component({
149170
// Include one of each type of button selector to ensure that they're all captured by
150171
// the harness's selector.
151172
template: `
152-
<button id="basic" type="button" mat-button (click)="clicked = true">
173+
<button id="basic" type="button" matButton (click)="clicked = true">
153174
Basic button
154175
</button>
155-
<button id="flat" type="button" mat-flat-button disabled (click)="clicked = true">
156-
Flat button
176+
<button id="flat" type="button" matButton="filled" disabled (click)="clicked = true">
177+
Filled button
157178
</button>
158-
<button id="raised" type="button" mat-raised-button>Raised button</button>
159-
<button id="stroked" type="button" mat-stroked-button>Stroked button</button>
160-
<button id="home-icon" type="button" mat-icon-button>
179+
<button id="raised" type="button" matButton="elevated">Elevated button</button>
180+
<button id="stroked" type="button" matButton="outlined">Outlined button</button>
181+
<button id="home-icon" type="button" matIconButton>
161182
<mat-icon>home</mat-icon>
162183
</button>
163-
<button id="favorite-icon" type="button" mat-icon-button>
184+
<button id="favorite-icon" type="button" matIconButton>
164185
<mat-icon>favorite</mat-icon>
165186
</button>
166-
<button id="fab" type="button" mat-fab>Fab button</button>
167-
<button id="mini-fab" type="button" mat-mini-fab>Mini Fab button</button>
168-
169-
<a id="anchor-basic" mat-button>Basic anchor</a>
170-
<a id="anchor-flat" mat-flat-button>Flat anchor</a>
171-
<a id="anchor-raised" mat-raised-button disabled>Raised anchor</a>
172-
<a id="anchor-stroked" mat-stroked-button>Stroked anchor</a>
173-
<a id="anchor-icon" mat-icon-button>Icon anchor</a>
174-
<a id="anchor-fab" mat-fab>Fab anchor</a>
175-
<a id="anchor-mini-fab" mat-mini-fab>Mini Fab anchor</a>
187+
<button id="fab" type="button" matFab>Fab button</button>
188+
<button id="mini-fab" type="button" matMiniFab>Mini Fab button</button>
189+
190+
<a id="anchor-basic" matButton>Basic anchor</a>
191+
<a id="anchor-flat" matButton="filled">Filled anchor</a>
192+
<a id="anchor-raised" matButton="elevated" disabled>Elevated anchor</a>
193+
<a id="anchor-stroked" matButton="outlined">Stroked anchor</a>
194+
<a id="anchor-icon" matIconButton>Icon anchor</a>
195+
<a id="anchor-fab" matFab>Fab anchor</a>
196+
<a id="anchor-mini-fab" matMiniFab>Mini Fab anchor</a>
176197
`,
177-
imports: [MatButtonModule, MatIconModule, PlatformModule],
198+
imports: [MatButtonModule, MatIconModule],
178199
})
179200
class ButtonHarnessTest {
180201
disabled = true;

src/material/button/testing/button-harness.ts

Lines changed: 39 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -12,20 +12,22 @@ import {
1212
ContentContainerComponentHarness,
1313
HarnessPredicate,
1414
} from '@angular/cdk/testing';
15-
import {ButtonHarnessFilters, ButtonVariant} from './button-harness-filters';
15+
import {ButtonAppearance, ButtonHarnessFilters, ButtonVariant} from './button-harness-filters';
1616

1717
/** Harness for interacting with a mat-button in tests. */
1818
export class MatButtonHarness extends ContentContainerComponentHarness {
1919
// TODO(jelbourn) use a single class, like `.mat-button-base`
20-
static hostSelector = `[mat-button], [mat-raised-button], [mat-flat-button],
21-
[mat-icon-button], [mat-stroked-button], [mat-fab], [mat-mini-fab]`;
20+
static hostSelector = `[matButton], [mat-button], [matIconButton], [matFab], [matMiniFab],
21+
[mat-raised-button], [mat-flat-button], [mat-icon-button], [mat-stroked-button], [mat-fab],
22+
[mat-mini-fab]`;
2223

2324
/**
2425
* Gets a `HarnessPredicate` that can be used to search for a button with specific attributes.
2526
* @param options Options for narrowing the search:
2627
* - `selector` finds a button whose host element matches the given selector.
2728
* - `text` finds a button with specific text content.
2829
* - `variant` finds buttons matching a specific variant.
30+
* - `appearance` finds buttons matching a specific appearance.
2931
* @return a `HarnessPredicate` configured with the given options.
3032
*/
3133
static with<T extends MatButtonHarness>(
@@ -39,6 +41,9 @@ export class MatButtonHarness extends ContentContainerComponentHarness {
3941
.addOption('variant', options.variant, (harness, variant) =>
4042
HarnessPredicate.stringMatches(harness.getVariant(), variant),
4143
)
44+
.addOption('appearance', options.appearance, (harness, appearance) =>
45+
HarnessPredicate.stringMatches(harness.getAppearance(), appearance),
46+
)
4247
.addOption('disabled', options.disabled, async (harness, disabled) => {
4348
return (await harness.isDisabled()) === disabled;
4449
});
@@ -91,20 +96,41 @@ export class MatButtonHarness extends ContentContainerComponentHarness {
9196
async getVariant(): Promise<ButtonVariant> {
9297
const host = await this.host();
9398

94-
if ((await host.getAttribute('mat-raised-button')) != null) {
95-
return 'raised';
96-
} else if ((await host.getAttribute('mat-flat-button')) != null) {
97-
return 'flat';
98-
} else if ((await host.getAttribute('mat-icon-button')) != null) {
99+
if (await host.hasClass('mat-mdc-icon-button')) {
99100
return 'icon';
100-
} else if ((await host.getAttribute('mat-stroked-button')) != null) {
101-
return 'stroked';
102-
} else if ((await host.getAttribute('mat-fab')) != null) {
103-
return 'fab';
104-
} else if ((await host.getAttribute('mat-mini-fab')) != null) {
101+
}
102+
103+
if (await host.hasClass('mat-mdc-mini-fab')) {
105104
return 'mini-fab';
106105
}
107106

107+
if (await host.hasClass('mat-mdc-fab')) {
108+
return 'fab';
109+
}
110+
108111
return 'basic';
109112
}
113+
114+
/** Gets the appearance of the button. */
115+
async getAppearance(): Promise<ButtonAppearance | null> {
116+
const host = await this.host();
117+
118+
if (await host.hasClass('mat-mdc-outlined-button')) {
119+
return 'outlined';
120+
}
121+
122+
if (await host.hasClass('mat-mdc-raised-button')) {
123+
return 'elevated';
124+
}
125+
126+
if (await host.hasClass('mat-mdc-unelevated-button')) {
127+
return 'filled';
128+
}
129+
130+
if (await host.hasClass('mat-mdc-button')) {
131+
return 'text';
132+
}
133+
134+
return null;
135+
}
110136
}

tools/public_api_guard/material/button-testing.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,19 @@ import { ComponentHarnessConstructor } from '@angular/cdk/testing';
99
import { ContentContainerComponentHarness } from '@angular/cdk/testing';
1010
import { HarnessPredicate } from '@angular/cdk/testing';
1111

12+
// @public
13+
export type ButtonAppearance = 'text' | 'filled' | 'elevated' | 'outlined';
14+
1215
// @public
1316
export interface ButtonHarnessFilters extends BaseHarnessFilters {
17+
appearance?: ButtonAppearance;
1418
disabled?: boolean;
1519
text?: string | RegExp;
1620
variant?: ButtonVariant;
1721
}
1822

1923
// @public
20-
export type ButtonVariant = 'basic' | 'raised' | 'flat' | 'icon' | 'stroked' | 'fab' | 'mini-fab';
24+
export type ButtonVariant = 'basic' | 'icon' | 'fab' | 'mini-fab';
2125

2226
// @public
2327
export class MatButtonHarness extends ContentContainerComponentHarness {
@@ -26,6 +30,7 @@ export class MatButtonHarness extends ContentContainerComponentHarness {
2630
click(location: 'center'): Promise<void>;
2731
click(): Promise<void>;
2832
focus(): Promise<void>;
33+
getAppearance(): Promise<ButtonAppearance | null>;
2934
getText(): Promise<string>;
3035
getVariant(): Promise<ButtonVariant>;
3136
// (undocumented)

0 commit comments

Comments
 (0)