Skip to content

Commit 88bc13d

Browse files
committed
fix(material/button): harness not picking up buttons with dynamic appearance (#31327)
Fixes that the button harness wasn't finding buttons with a dynamic appearance, because then Angular won't set an attribute to the DOM. These changes switch to using a class selector instead. (cherry picked from commit 5f4fa62)
1 parent 675d1cd commit 88bc13d

File tree

2 files changed

+31
-16
lines changed

2 files changed

+31
-16
lines changed

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

Lines changed: 23 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import {Component} from '@angular/core';
1+
import {Component, signal} from '@angular/core';
22
import {ComponentFixture, TestBed} from '@angular/core/testing';
33
import {Platform} from '@angular/cdk/platform';
44
import {HarnessLoader, parallel} from '@angular/cdk/testing';
@@ -7,22 +7,21 @@ import {MatButtonModule} from '../module';
77
import {MatIconModule} from '../../icon';
88
import {MatIconHarness} from '../../icon/testing';
99
import {MatButtonHarness} from './button-harness';
10+
import {MatButtonAppearance} from '../button-base';
1011

1112
describe('MatButtonHarness', () => {
1213
let fixture: ComponentFixture<ButtonHarnessTest>;
1314
let loader: HarnessLoader;
14-
let platform: Platform;
1515

1616
beforeEach(() => {
1717
fixture = TestBed.createComponent(ButtonHarnessTest);
1818
fixture.detectChanges();
1919
loader = TestbedHarnessEnvironment.loader(fixture);
20-
platform = TestBed.inject(Platform);
2120
});
2221

2322
it('should load all button harnesses', async () => {
2423
const buttons = await loader.getAllHarnesses(MatButtonHarness);
25-
expect(buttons.length).toBe(16);
24+
expect(buttons.length).toBe(17);
2625
});
2726

2827
it('should load button with exact text', async () => {
@@ -41,7 +40,7 @@ describe('MatButtonHarness', () => {
4140
it('should filter by whether a button is disabled', async () => {
4241
const enabledButtons = await loader.getAllHarnesses(MatButtonHarness.with({disabled: false}));
4342
const disabledButtons = await loader.getAllHarnesses(MatButtonHarness.with({disabled: true}));
44-
expect(enabledButtons.length).toBe(14);
43+
expect(enabledButtons.length).toBe(15);
4544
expect(disabledButtons.length).toBe(2);
4645
});
4746

@@ -79,7 +78,7 @@ describe('MatButtonHarness', () => {
7978
const button = await loader.getHarness(MatButtonHarness.with({text: 'Basic button'}));
8079
await button.click();
8180

82-
expect(fixture.componentInstance.clicked).toBe(true);
81+
expect(fixture.componentInstance.clicked()).toBe(true);
8382
});
8483

8584
it('should not click a disabled button', async () => {
@@ -88,14 +87,14 @@ describe('MatButtonHarness', () => {
8887
// cancel dispatched click events on disabled buttons. We skip this check on Edge and Firefox.
8988
// See: https://bugzilla.mozilla.org/show_bug.cgi?id=1582570 and:
9089
// https://stackoverflow.com/questions/32377026/disabled-button-is-clickable-on-edge-browser
91-
if (platform.FIREFOX) {
90+
if (TestBed.inject(Platform).FIREFOX) {
9291
return;
9392
}
9493

9594
const button = await loader.getHarness(MatButtonHarness.with({text: 'Filled button'}));
9695
await button.click();
9796

98-
expect(fixture.componentInstance.clicked).toBe(false);
97+
expect(fixture.componentInstance.clicked()).toBe(false);
9998
});
10099

101100
it('should be able to handle nested harnesses', async () => {
@@ -127,6 +126,7 @@ describe('MatButtonHarness', () => {
127126
'basic',
128127
'basic',
129128
'basic',
129+
'basic',
130130
'icon',
131131
'fab',
132132
'mini-fab',
@@ -151,6 +151,7 @@ describe('MatButtonHarness', () => {
151151
'filled',
152152
'elevated',
153153
'outlined',
154+
'tonal',
154155
null,
155156
null,
156157
null,
@@ -166,16 +167,25 @@ describe('MatButtonHarness', () => {
166167
const button = await loader.getHarness(MatButtonHarness.with({appearance: 'filled'}));
167168
expect(await button.getText()).toBe('Filled button');
168169
});
170+
171+
it('should get the appearance of a button with a dynamic appearance', async () => {
172+
const button = await loader.getHarness(
173+
MatButtonHarness.with({selector: '#dynamic-appearance'}),
174+
);
175+
expect(await button.getAppearance()).toBe('tonal');
176+
fixture.componentInstance.dynamicAppearance.set('filled');
177+
expect(await button.getAppearance()).toBe('filled');
178+
});
169179
});
170180

171181
@Component({
172182
// Include one of each type of button selector to ensure that they're all captured by
173183
// the harness's selector.
174184
template: `
175-
<button id="basic" type="button" matButton (click)="clicked = true">
185+
<button id="basic" type="button" matButton (click)="clicked.set(true)">
176186
Basic button
177187
</button>
178-
<button id="flat" type="button" matButton="filled" disabled (click)="clicked = true">
188+
<button id="flat" type="button" matButton="filled" disabled (click)="clicked.set(true)">
179189
Filled button
180190
</button>
181191
<button id="raised" type="button" matButton="elevated">Elevated button</button>
@@ -194,13 +204,14 @@ describe('MatButtonHarness', () => {
194204
<a id="anchor-flat" matButton="filled">Filled anchor</a>
195205
<a id="anchor-raised" matButton="elevated" disabled>Elevated anchor</a>
196206
<a id="anchor-stroked" matButton="outlined">Stroked anchor</a>
207+
<a id="dynamic-appearance" [matButton]="dynamicAppearance()">Stroked anchor</a>
197208
<a id="anchor-icon" matIconButton>Icon anchor</a>
198209
<a id="anchor-fab" matFab>Fab anchor</a>
199210
<a id="anchor-mini-fab" matMiniFab>Mini Fab anchor</a>
200211
`,
201212
imports: [MatButtonModule, MatIconModule],
202213
})
203214
class ButtonHarnessTest {
204-
disabled = true;
205-
clicked = false;
215+
clicked = signal(false);
216+
dynamicAppearance = signal<MatButtonAppearance>('tonal');
206217
}

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

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,14 @@ import {ButtonAppearance, ButtonHarnessFilters, ButtonVariant} from './button-ha
1616

1717
/** Harness for interacting with a mat-button in tests. */
1818
export class MatButtonHarness extends ContentContainerComponentHarness {
19-
// TODO(jelbourn) use a single class, like `.mat-button-base`
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]`;
19+
// Note: `.mat-mdc-button-base` should be enough for all buttons, however some apps are using
20+
// the harness without actually having an applied button. Keep the attributes for backwards
21+
// compatibility.
22+
23+
/** Selector for the harness. */
24+
static hostSelector = `.mat-mdc-button-base, [matButton], [mat-button], [matIconButton],
25+
[matFab], [matMiniFab], [mat-raised-button], [mat-flat-button], [mat-icon-button],
26+
[mat-stroked-button], [mat-fab], [mat-mini-fab]`;
2327

2428
/**
2529
* Gets a `HarnessPredicate` that can be used to search for a button with specific attributes.

0 commit comments

Comments
 (0)