Skip to content

Commit 7a8762a

Browse files
authored
refactor(material-experimental/mdc-snack-bar): de-duplicate test harness logic (#21496)
Changes the MDC-based `MatSnackBarHarness` to extend the non-MDC one since all the logic is the same, apart form a few selectors.
1 parent 390afca commit 7a8762a

File tree

6 files changed

+34
-138
lines changed

6 files changed

+34
-138
lines changed

src/material-experimental/mdc-snack-bar/testing/BUILD.bazel

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@ ts_library(
1010
),
1111
module_name = "@angular/material-experimental/mdc-snack-bar/testing",
1212
deps = [
13-
"//src/cdk/a11y",
1413
"//src/cdk/testing",
14+
"//src/material/snack-bar/testing",
1515
],
1616
)
1717

src/material-experimental/mdc-snack-bar/testing/public-api.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,4 @@
77
*/
88

99
export * from './snack-bar-harness';
10-
export * from './snack-bar-harness-filters';
10+
export {SnackBarHarnessFilters} from '@angular/material/snack-bar/testing';

src/material-experimental/mdc-snack-bar/testing/snack-bar-harness-filters.ts

Lines changed: 0 additions & 12 deletions
This file was deleted.

src/material-experimental/mdc-snack-bar/testing/snack-bar-harness.ts

Lines changed: 16 additions & 114 deletions
Original file line numberDiff line numberDiff line change
@@ -6,130 +6,32 @@
66
* found in the LICENSE file at https://angular.io/license
77
*/
88

9-
import {AriaLivePoliteness} from '@angular/cdk/a11y';
10-
import {ComponentHarness, HarnessPredicate, parallel} from '@angular/cdk/testing';
11-
import {SnackBarHarnessFilters} from './snack-bar-harness-filters';
9+
import {HarnessPredicate} from '@angular/cdk/testing';
10+
import {
11+
MatSnackBarHarness as BaseMatSnackBarHarness,
12+
SnackBarHarnessFilters,
13+
} from '@angular/material/snack-bar/testing';
1214

1315
/** Harness for interacting with an MDC-based mat-snack-bar in tests. */
14-
export class MatSnackBarHarness extends ComponentHarness {
15-
// Developers can provide a custom component or template for the
16-
// snackbar. The canonical snack-bar parent is the "MatSnackBarContainer".
17-
// We use `:not([mat-exit])` to exclude snack bars that are in the process of being dismissed,
18-
// because the element only gets removed after the animation is finished and since it runs
19-
// outside of Angular, we don't have a way of being notified when it's done.
16+
export class MatSnackBarHarness extends BaseMatSnackBarHarness {
17+
// Developers can provide a custom component or template for the snackbar. The canonical snack-bar
18+
// parent is the "MatSnackBarContainer". We use `:not([mat-exit])` to exclude snack bars that
19+
// are in the process of being dismissed, because the element only gets removed after the
20+
// animation is finished and since it runs outside of Angular, we don't have a way of being
21+
// notified when it's done.
2022
/** The selector for the host element of a `MatSnackBar` instance. */
2123
static hostSelector = '.mat-mdc-snack-bar-container:not([mat-exit])';
22-
23-
private _simpleSnackBar = this.locatorForOptional('.mat-mdc-simple-snack-bar');
24-
private _simpleSnackBarLiveRegion = this.locatorFor('[aria-live]');
25-
private _simpleSnackBarMessage =
26-
this.locatorFor('.mat-mdc-simple-snack-bar .mat-mdc-snack-bar-label');
27-
private _simpleSnackBarActionButton =
28-
this.locatorForOptional('.mat-mdc-simple-snack-bar .mat-mdc-snack-bar-action');
24+
protected _messageSelector = '.mat-mdc-simple-snack-bar .mat-mdc-snack-bar-label';
25+
protected _simpleSnackBarSelector = '.mat-mdc-simple-snack-bar';
26+
protected _actionButtonSelector = '.mat-mdc-simple-snack-bar .mat-mdc-snack-bar-action';
2927

3028
/**
3129
* Gets a `HarnessPredicate` that can be used to search for a `MatSnackBarHarness` that meets
3230
* certain criteria.
3331
* @param options Options for filtering which snack bar instances are considered a match.
3432
* @return a `HarnessPredicate` configured with the given options.
3533
*/
36-
static with(options: SnackBarHarnessFilters = {}): HarnessPredicate<MatSnackBarHarness> {
37-
return new HarnessPredicate(MatSnackBarHarness, options);
38-
}
39-
40-
/**
41-
* Gets the role of the snack-bar. The role of a snack-bar is determined based
42-
* on the ARIA politeness specified in the snack-bar config.
43-
* @deprecated Use `getAriaLive` instead.
44-
* @breaking-change 13.0.0
45-
*/
46-
async getRole(): Promise<'alert'|'status'|null> {
47-
return (await this.host()).getAttribute('role') as Promise<'alert'|'status'|null>;
48-
}
49-
50-
/**
51-
* Gets the aria-live of the snack-bar's live region. The aria-live of a snack-bar is
52-
* determined based on the ARIA politeness specified in the snack-bar config.
53-
*/
54-
async getAriaLive(): Promise<AriaLivePoliteness> {
55-
return (await this._simpleSnackBarLiveRegion())
56-
.getAttribute('aria-live') as Promise<AriaLivePoliteness>;
57-
}
58-
59-
/**
60-
* Whether the snack-bar has an action. Method cannot be used for snack-bar's with custom content.
61-
*/
62-
async hasAction(): Promise<boolean> {
63-
await this._assertSimpleSnackBar();
64-
return (await this._simpleSnackBarActionButton()) !== null;
65-
}
66-
67-
/**
68-
* Gets the description of the snack-bar. Method cannot be used for snack-bar's without action or
69-
* with custom content.
70-
*/
71-
async getActionDescription(): Promise<string> {
72-
await this._assertSimpleSnackBarWithAction();
73-
return (await this._simpleSnackBarActionButton())!.text();
74-
}
75-
76-
77-
/**
78-
* Dismisses the snack-bar by clicking the action button. Method cannot be used for snack-bar's
79-
* without action or with custom content.
80-
*/
81-
async dismissWithAction(): Promise<void> {
82-
await this._assertSimpleSnackBarWithAction();
83-
await (await this._simpleSnackBarActionButton())!.click();
84-
}
85-
86-
/**
87-
* Gets the message of the snack-bar. Method cannot be used for snack-bar's with custom content.
88-
*/
89-
async getMessage(): Promise<string> {
90-
await this._assertSimpleSnackBar();
91-
return (await this._simpleSnackBarMessage()).text();
92-
}
93-
94-
/** Gets whether the snack-bar has been dismissed. */
95-
async isDismissed(): Promise<boolean> {
96-
// We consider the snackbar dismissed if it's not in the DOM. We can assert that the
97-
// element isn't in the DOM by seeing that its width and height are zero.
98-
99-
const host = await this.host();
100-
const [exit, dimensions] = await parallel(() => [
101-
// The snackbar container is marked with the "exit" attribute after it has been dismissed
102-
// but before the animation has finished (after which it's removed from the DOM).
103-
host.getAttribute('mat-exit'),
104-
host.getDimensions(),
105-
]);
106-
107-
return exit != null || (!!dimensions && dimensions.height === 0 && dimensions.width === 0);
108-
}
109-
110-
/**
111-
* Asserts that the current snack-bar does not use custom content. Promise rejects if
112-
* custom content is used.
113-
*/
114-
private async _assertSimpleSnackBar(): Promise<void> {
115-
if (!await this._isSimpleSnackBar()) {
116-
throw Error('Method cannot be used for snack-bar with custom content.');
117-
}
118-
}
119-
120-
/**
121-
* Asserts that the current snack-bar does not use custom content and has
122-
* an action defined. Otherwise the promise will reject.
123-
*/
124-
private async _assertSimpleSnackBarWithAction(): Promise<void> {
125-
await this._assertSimpleSnackBar();
126-
if (!await this.hasAction()) {
127-
throw Error('Method cannot be used for standard snack-bar without action.');
128-
}
129-
}
130-
131-
/** Whether the snack-bar is using the default content template. */
132-
private async _isSimpleSnackBar(): Promise<boolean> {
133-
return await this._simpleSnackBar() !== null;
34+
static with(options: SnackBarHarnessFilters = {}): HarnessPredicate<BaseMatSnackBarHarness> {
35+
return new HarnessPredicate<BaseMatSnackBarHarness>(MatSnackBarHarness, options);
13436
}
13537
}

src/material/snack-bar/testing/snack-bar-harness.ts

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,10 @@ export class MatSnackBarHarness extends ContentContainerComponentHarness<string>
1616
// snackbar. The canonical snack-bar parent is the "MatSnackBarContainer".
1717
/** The selector for the host element of a `MatSnackBar` instance. */
1818
static hostSelector = '.mat-snack-bar-container';
19-
20-
private _simpleSnackBar = this.locatorForOptional('.mat-simple-snackbar');
19+
protected _messageSelector = '.mat-simple-snackbar > span';
20+
protected _simpleSnackBarSelector = '.mat-simple-snackbar';
21+
protected _actionButtonSelector = '.mat-simple-snackbar-action > button';
2122
private _simpleSnackBarLiveRegion = this.locatorFor('[aria-live]');
22-
private _simpleSnackBarMessage = this.locatorFor('.mat-simple-snackbar > span');
23-
private _simpleSnackBarActionButton =
24-
this.locatorForOptional('.mat-simple-snackbar-action > button');
2523

2624
/**
2725
* Gets a `HarnessPredicate` that can be used to search for a `MatSnackBarHarness` that meets
@@ -57,7 +55,7 @@ export class MatSnackBarHarness extends ContentContainerComponentHarness<string>
5755
*/
5856
async hasAction(): Promise<boolean> {
5957
await this._assertSimpleSnackBar();
60-
return (await this._simpleSnackBarActionButton()) !== null;
58+
return (await this._getSimpleSnackBarActionButton()) !== null;
6159
}
6260

6361
/**
@@ -66,7 +64,7 @@ export class MatSnackBarHarness extends ContentContainerComponentHarness<string>
6664
*/
6765
async getActionDescription(): Promise<string> {
6866
await this._assertSimpleSnackBarWithAction();
69-
return (await this._simpleSnackBarActionButton())!.text();
67+
return (await this._getSimpleSnackBarActionButton())!.text();
7068
}
7169

7270

@@ -76,15 +74,15 @@ export class MatSnackBarHarness extends ContentContainerComponentHarness<string>
7674
*/
7775
async dismissWithAction(): Promise<void> {
7876
await this._assertSimpleSnackBarWithAction();
79-
await (await this._simpleSnackBarActionButton())!.click();
77+
await (await this._getSimpleSnackBarActionButton())!.click();
8078
}
8179

8280
/**
8381
* Gets the message of the snack-bar. Method cannot be used for snack-bar's with custom content.
8482
*/
8583
async getMessage(): Promise<string> {
8684
await this._assertSimpleSnackBar();
87-
return (await this._simpleSnackBarMessage()).text();
85+
return (await this.locatorFor(this._messageSelector)()).text();
8886
}
8987

9088
/** Gets whether the snack-bar has been dismissed. */
@@ -126,6 +124,11 @@ export class MatSnackBarHarness extends ContentContainerComponentHarness<string>
126124

127125
/** Whether the snack-bar is using the default content template. */
128126
private async _isSimpleSnackBar(): Promise<boolean> {
129-
return await this._simpleSnackBar() !== null;
127+
return await this.locatorForOptional(this._simpleSnackBarSelector)() !== null;
128+
}
129+
130+
/** Gets the simple snack bar action button. */
131+
private async _getSimpleSnackBarActionButton() {
132+
return this.locatorForOptional(this._actionButtonSelector)();
130133
}
131134
}

tools/public_api_guard/material/snack-bar/testing.d.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
export declare class MatSnackBarHarness extends ContentContainerComponentHarness<string> {
2+
protected _actionButtonSelector: string;
3+
protected _messageSelector: string;
4+
protected _simpleSnackBarSelector: string;
25
dismissWithAction(): Promise<void>;
36
getActionDescription(): Promise<string>;
47
getAriaLive(): Promise<AriaLivePoliteness>;

0 commit comments

Comments
 (0)