Skip to content

Commit b421525

Browse files
committed
fix(material-experimental/mdc-snack-bar): add harness support for getting label and actions
of mdc-snack-bar with custom content
1 parent f4a54d6 commit b421525

File tree

8 files changed

+167
-46
lines changed

8 files changed

+167
-46
lines changed

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@ ng_test_library(
2626
":testing",
2727
"//src/material-experimental/mdc-snack-bar",
2828
"//src/material/snack-bar/testing:harness_tests_lib",
29+
"//src/cdk/testing",
30+
"//src/cdk/testing/testbed",
31+
"@npm//@angular/platform-browser",
2932
],
3033
)
3134

Lines changed: 82 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,88 @@
1-
import {MatSnackBarModule, MatSnackBar} from '@angular/material-experimental/mdc-snack-bar';
1+
import {
2+
MatSnackBar,
3+
MatSnackBarConfig,
4+
MatSnackBarModule
5+
} from '@angular/material-experimental/mdc-snack-bar';
26
import {runHarnessTests} from '@angular/material/snack-bar/testing/shared.spec';
37
import {MatSnackBarHarness} from './snack-bar-harness';
8+
import {ComponentFixture, TestBed} from '@angular/core/testing';
9+
import {HarnessLoader} from '@angular/cdk/testing';
10+
import {NoopAnimationsModule} from '@angular/platform-browser/animations';
11+
import {TestbedHarnessEnvironment} from '@angular/cdk/testing/testbed';
12+
import {Component, TemplateRef, ViewChild} from '@angular/core';
413

514
describe('MDC-based MatSnackBarHarness', () => {
615
runHarnessTests(MatSnackBarModule, MatSnackBar, MatSnackBarHarness as any);
716
});
17+
18+
describe('Non-MDC-based MatSnackBarHarness', () => {
19+
let fixture: ComponentFixture<SnackbarHarnessTest>;
20+
let loader: HarnessLoader;
21+
22+
beforeEach(async () => {
23+
await TestBed.configureTestingModule({
24+
imports: [MatSnackBarModule, NoopAnimationsModule],
25+
declarations: [SnackbarHarnessTest],
26+
}).compileComponents();
27+
28+
fixture = TestBed.createComponent(SnackbarHarnessTest);
29+
fixture.detectChanges();
30+
loader = TestbedHarnessEnvironment.documentRootLoader(fixture);
31+
});
32+
33+
it('should be able to get message of a snack-bar with custom content', async () => {
34+
fixture.componentInstance.openCustom();
35+
let snackBar = await loader.getHarness(MatSnackBarHarness);
36+
expect(await snackBar.getMessage()).toBe('My custom snack-bar.');
37+
38+
fixture.componentInstance.openCustomWithAction();
39+
snackBar = await loader.getHarness(MatSnackBarHarness);
40+
expect(await snackBar.getMessage()).toBe('My custom snack-bar with action.');
41+
});
42+
43+
it('should fail to get action description of a snack-bar with no action', async () => {
44+
fixture.componentInstance.openCustom();
45+
const snackBar = await loader.getHarness(MatSnackBarHarness);
46+
await expectAsync(snackBar.getActionDescription()).toBeRejectedWithError(/without an action/);
47+
});
48+
49+
it('should be able to get action description of a snack-bar with an action', async () => {
50+
fixture.componentInstance.openCustomWithAction();
51+
const snackBar = await loader.getHarness(MatSnackBarHarness);
52+
expect(await snackBar.getActionDescription()).toBe('Ok');
53+
});
54+
55+
it('should be able to check whether a snack-bar with custom content has an action', async () => {
56+
fixture.componentInstance.openCustom();
57+
let snackBar = await loader.getHarness(MatSnackBarHarness);
58+
expect(await snackBar.hasAction()).toBe(false);
59+
60+
fixture.componentInstance.openCustomWithAction();
61+
snackBar = await loader.getHarness(MatSnackBarHarness);
62+
expect(await snackBar.hasAction()).toBe(true);
63+
});
64+
});
65+
66+
@Component({
67+
template: `
68+
<ng-template #custom>My custom snack-bar.</ng-template>
69+
<ng-template #customWithAction>
70+
<span matSnackBarLabel>My custom snack-bar with action.</span>
71+
<div matSnackBarActions><button matSnackBarAction>Ok</button></div>
72+
</ng-template>
73+
`
74+
})
75+
class SnackbarHarnessTest {
76+
@ViewChild('custom') customTmpl: TemplateRef<any>;
77+
@ViewChild('customWithAction') customWithActionTmpl: TemplateRef<any>;
78+
79+
constructor(public snackBar: MatSnackBar) {}
80+
81+
openCustom(config?: MatSnackBarConfig) {
82+
return this.snackBar.openFromTemplate(this.customTmpl, config);
83+
}
84+
85+
openCustomWithAction(config?: MatSnackBarConfig) {
86+
return this.snackBar.openFromTemplate(this.customWithActionTmpl, config);
87+
}
88+
}

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

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,8 @@ export class MatSnackBarHarness extends BaseMatSnackBarHarness {
2121
// notified when it's done.
2222
/** The selector for the host element of a `MatSnackBar` instance. */
2323
static override hostSelector = '.mat-mdc-snack-bar-container:not([mat-exit])';
24-
protected override _messageSelector = '.mat-mdc-simple-snack-bar .mat-mdc-snack-bar-label';
25-
protected override _simpleSnackBarSelector = '.mat-mdc-simple-snack-bar';
26-
protected override _actionButtonSelector = '.mat-mdc-simple-snack-bar .mat-mdc-snack-bar-action';
24+
protected override _messageSelector = '.mdc-snackbar__label';
25+
protected override _actionButtonSelector = '.mat-mdc-snack-bar-action';
2726

2827
/**
2928
* Gets a `HarnessPredicate` that can be used to search for a `MatSnackBarHarness` that meets
@@ -35,4 +34,6 @@ export class MatSnackBarHarness extends BaseMatSnackBarHarness {
3534
options: SnackBarHarnessFilters = {}): HarnessPredicate<BaseMatSnackBarHarness> {
3635
return new HarnessPredicate<BaseMatSnackBarHarness>(MatSnackBarHarness, options);
3736
}
37+
38+
protected override async _assertContentAnnotated() {}
3839
}

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,9 @@ ng_test_library(
4343
":harness_tests_lib",
4444
":testing",
4545
"//src/material/snack-bar",
46+
"//src/cdk/testing",
47+
"//src/cdk/testing/testbed",
48+
"@npm//@angular/platform-browser",
4649
],
4750
)
4851

src/material/snack-bar/testing/shared.spec.ts

Lines changed: 1 addition & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -91,24 +91,12 @@ export function runHarnessTests(
9191
fixture.componentInstance.openSimple('Subscribed to newsletter.');
9292
let snackBar = await loader.getHarness(snackBarHarness);
9393
expect(await snackBar.getMessage()).toBe('Subscribed to newsletter.');
94-
95-
// For snack-bar's with custom template, the message cannot be
96-
// retrieved. We expect an error to be thrown.
97-
fixture.componentInstance.openCustom();
98-
snackBar = await loader.getHarness(snackBarHarness);
99-
await expectAsync(snackBar.getMessage()).toBeRejectedWithError(/custom content/);
10094
});
10195

10296
it('should be able to get action description of simple snack-bar', async () => {
10397
fixture.componentInstance.openSimple('Hello', 'Unsubscribe');
10498
let snackBar = await loader.getHarness(snackBarHarness);
10599
expect(await snackBar.getActionDescription()).toBe('Unsubscribe');
106-
107-
// For snack-bar's with custom template, the action description
108-
// cannot be retrieved. We expect an error to be thrown.
109-
fixture.componentInstance.openCustom();
110-
snackBar = await loader.getHarness(snackBarHarness);
111-
await expectAsync(snackBar.getActionDescription()).toBeRejectedWithError(/custom content/);
112100
});
113101

114102
it('should be able to check whether simple snack-bar has action', async () => {
@@ -119,12 +107,6 @@ export function runHarnessTests(
119107
fixture.componentInstance.openSimple('No action');
120108
snackBar = await loader.getHarness(snackBarHarness);
121109
expect(await snackBar.hasAction()).toBe(false);
122-
123-
// For snack-bar's with custom template, the action cannot
124-
// be found. We expect an error to be thrown.
125-
fixture.componentInstance.openCustom();
126-
snackBar = await loader.getHarness(snackBarHarness);
127-
await expectAsync(snackBar.hasAction()).toBeRejectedWithError(/custom content/);
128110
});
129111

130112
it('should be able to dismiss simple snack-bar with action', async () => {
@@ -143,7 +125,7 @@ export function runHarnessTests(
143125

144126
fixture.componentInstance.openSimple('No action');
145127
snackBar = await loader.getHarness(snackBarHarness);
146-
await expectAsync(snackBar.dismissWithAction()).toBeRejectedWithError(/without action/);
128+
await expectAsync(snackBar.dismissWithAction()).toBeRejectedWithError(/without an action/);
147129
});
148130

149131
@Component({
Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,59 @@
1-
import {MatSnackBarModule, MatSnackBar} from '@angular/material/snack-bar';
1+
import {MatSnackBar, MatSnackBarConfig, MatSnackBarModule} from '@angular/material/snack-bar';
22
import {runHarnessTests} from '@angular/material/snack-bar/testing/shared.spec';
33
import {MatSnackBarHarness} from './snack-bar-harness';
4+
import {ComponentFixture, TestBed} from '@angular/core/testing';
5+
import {HarnessLoader} from '@angular/cdk/testing';
6+
import {NoopAnimationsModule} from '@angular/platform-browser/animations';
7+
import {TestbedHarnessEnvironment} from '@angular/cdk/testing/testbed';
8+
import {Component, TemplateRef, ViewChild} from '@angular/core';
49

510
describe('Non-MDC-based MatSnackBarHarness', () => {
611
runHarnessTests(MatSnackBarModule, MatSnackBar, MatSnackBarHarness);
712
});
13+
14+
describe('Non-MDC-based MatSnackBarHarness', () => {
15+
let fixture: ComponentFixture<SnackbarHarnessTest>;
16+
let loader: HarnessLoader;
17+
18+
beforeEach(async () => {
19+
await TestBed.configureTestingModule({
20+
imports: [MatSnackBarModule, NoopAnimationsModule],
21+
declarations: [SnackbarHarnessTest],
22+
}).compileComponents();
23+
24+
fixture = TestBed.createComponent(SnackbarHarnessTest);
25+
fixture.detectChanges();
26+
loader = TestbedHarnessEnvironment.documentRootLoader(fixture);
27+
});
28+
29+
it('should fail to get message of a snack-bar with custom content', async () => {
30+
fixture.componentInstance.openCustom();
31+
const snackBar = await loader.getHarness(MatSnackBarHarness);
32+
await expectAsync(snackBar.getMessage()).toBeRejectedWithError(/custom content/);
33+
});
34+
35+
it('should fail to get action description of a snack-bar with custom content', async () => {
36+
fixture.componentInstance.openCustom();
37+
const snackBar = await loader.getHarness(MatSnackBarHarness);
38+
await expectAsync(snackBar.getActionDescription()).toBeRejectedWithError(/custom content/);
39+
});
40+
41+
it('should fail to check whether a snack-bar with custom content has an action', async () => {
42+
fixture.componentInstance.openCustom();
43+
const snackBar = await loader.getHarness(MatSnackBarHarness);
44+
await expectAsync(snackBar.hasAction()).toBeRejectedWithError(/custom content/);
45+
});
46+
});
47+
48+
@Component({
49+
template: `<ng-template>My custom snack-bar.</ng-template>`
50+
})
51+
class SnackbarHarnessTest {
52+
@ViewChild(TemplateRef) customTmpl: TemplateRef<any>;
53+
54+
constructor(public snackBar: MatSnackBar) {}
55+
56+
openCustom(config?: MatSnackBarConfig) {
57+
return this.snackBar.openFromTemplate(this.customTmpl, config);
58+
}
59+
}

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

Lines changed: 19 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,8 @@ export class MatSnackBarHarness extends ContentContainerComponentHarness<string>
1717
/** The selector for the host element of a `MatSnackBar` instance. */
1818
static hostSelector = '.mat-snack-bar-container';
1919
protected _messageSelector = '.mat-simple-snackbar > span';
20-
protected _simpleSnackBarSelector = '.mat-simple-snackbar';
2120
protected _actionButtonSelector = '.mat-simple-snackbar-action > button';
22-
private _simpleSnackBarLiveRegion = this.locatorFor('[aria-live]');
21+
private _snackBarLiveRegion = this.locatorFor('[aria-live]');
2322

2423
/**
2524
* Gets a `HarnessPredicate` that can be used to search for a `MatSnackBarHarness` that meets
@@ -46,25 +45,25 @@ export class MatSnackBarHarness extends ContentContainerComponentHarness<string>
4645
* determined based on the ARIA politeness specified in the snack-bar config.
4746
*/
4847
async getAriaLive(): Promise<AriaLivePoliteness> {
49-
return (await this._simpleSnackBarLiveRegion())
48+
return (await this._snackBarLiveRegion())
5049
.getAttribute('aria-live') as Promise<AriaLivePoliteness>;
5150
}
5251

5352
/**
5453
* Whether the snack-bar has an action. Method cannot be used for snack-bar's with custom content.
5554
*/
5655
async hasAction(): Promise<boolean> {
57-
await this._assertSimpleSnackBar();
58-
return (await this._getSimpleSnackBarActionButton()) !== null;
56+
await this._assertContentAnnotated();
57+
return (await this._getActionButton()) !== null;
5958
}
6059

6160
/**
6261
* Gets the description of the snack-bar. Method cannot be used for snack-bar's without action or
6362
* with custom content.
6463
*/
6564
async getActionDescription(): Promise<string> {
66-
await this._assertSimpleSnackBarWithAction();
67-
return (await this._getSimpleSnackBarActionButton())!.text();
65+
await this._assertHasAction();
66+
return (await this._getActionButton())!.text();
6867
}
6968

7069

@@ -73,15 +72,15 @@ export class MatSnackBarHarness extends ContentContainerComponentHarness<string>
7372
* without action or with custom content.
7473
*/
7574
async dismissWithAction(): Promise<void> {
76-
await this._assertSimpleSnackBarWithAction();
77-
await (await this._getSimpleSnackBarActionButton())!.click();
75+
await this._assertHasAction();
76+
await (await this._getActionButton())!.click();
7877
}
7978

8079
/**
8180
* Gets the message of the snack-bar. Method cannot be used for snack-bar's with custom content.
8281
*/
8382
async getMessage(): Promise<string> {
84-
await this._assertSimpleSnackBar();
83+
await this._assertContentAnnotated();
8584
return (await this.locatorFor(this._messageSelector)()).text();
8685
}
8786

@@ -102,33 +101,33 @@ export class MatSnackBarHarness extends ContentContainerComponentHarness<string>
102101
}
103102

104103
/**
105-
* Asserts that the current snack-bar does not use custom content. Promise rejects if
106-
* custom content is used.
104+
* Asserts that the current snack-bar has annotated content. Promise reject
105+
* if content is not annotated.
107106
*/
108-
private async _assertSimpleSnackBar(): Promise<void> {
107+
protected async _assertContentAnnotated(): Promise<void> {
109108
if (!await this._isSimpleSnackBar()) {
110109
throw Error('Method cannot be used for snack-bar with custom content.');
111110
}
112111
}
113112

114113
/**
115-
* Asserts that the current snack-bar does not use custom content and has
116-
* an action defined. Otherwise the promise will reject.
114+
* Asserts that the current snack-bar has an action defined. Otherwise the
115+
* promise will reject.
117116
*/
118-
private async _assertSimpleSnackBarWithAction(): Promise<void> {
119-
await this._assertSimpleSnackBar();
117+
protected async _assertHasAction(): Promise<void> {
118+
await this._assertContentAnnotated();
120119
if (!await this.hasAction()) {
121-
throw Error('Method cannot be used for standard snack-bar without action.');
120+
throw Error('Method cannot be used for a snack-bar without an action.');
122121
}
123122
}
124123

125124
/** Whether the snack-bar is using the default content template. */
126125
private async _isSimpleSnackBar(): Promise<boolean> {
127-
return await this.locatorForOptional(this._simpleSnackBarSelector)() !== null;
126+
return await this.locatorForOptional('.mat-simple-snackbar')() !== null;
128127
}
129128

130129
/** Gets the simple snack bar action button. */
131-
private async _getSimpleSnackBarActionButton() {
130+
private async _getActionButton() {
132131
return this.locatorForOptional(this._actionButtonSelector)();
133132
}
134133
}

tools/public_api_guard/material/snack-bar-testing.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ import { HarnessPredicate } from '@angular/cdk/testing';
1313
export class MatSnackBarHarness extends ContentContainerComponentHarness<string> {
1414
// (undocumented)
1515
protected _actionButtonSelector: string;
16+
protected _assertContentAnnotated(): Promise<void>;
17+
protected _assertHasAction(): Promise<void>;
1618
dismissWithAction(): Promise<void>;
1719
getActionDescription(): Promise<string>;
1820
getAriaLive(): Promise<AriaLivePoliteness>;
@@ -24,8 +26,6 @@ export class MatSnackBarHarness extends ContentContainerComponentHarness<string>
2426
isDismissed(): Promise<boolean>;
2527
// (undocumented)
2628
protected _messageSelector: string;
27-
// (undocumented)
28-
protected _simpleSnackBarSelector: string;
2929
static with(options?: SnackBarHarnessFilters): HarnessPredicate<MatSnackBarHarness>;
3030
}
3131

0 commit comments

Comments
 (0)