Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(material-experimental/mdc-snack-bar): add test harness (#20366)
Adds a test harness for the MDC-based snack bar. One gotcha here compared to the standard snack bar is that the harness excludes (via a CSS selector) instances that are in the process of being closed. We have to take this approach, because MDC's animations are run outside of Angular and we don't have a way of waiting for them to finish.
- Loading branch information
Showing
9 changed files
with
221 additions
and
19 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
41 changes: 41 additions & 0 deletions
41
src/material-experimental/mdc-snack-bar/testing/BUILD.bazel
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
load("//tools:defaults.bzl", "ng_test_library", "ng_web_test_suite", "ts_library") | ||
|
||
package(default_visibility = ["//visibility:public"]) | ||
|
||
ts_library( | ||
name = "testing", | ||
srcs = glob( | ||
["**/*.ts"], | ||
exclude = ["**/*.spec.ts"], | ||
), | ||
module_name = "@angular/material-experimental/mdc-snack-bar/testing", | ||
deps = [ | ||
"//src/cdk/testing", | ||
], | ||
) | ||
|
||
filegroup( | ||
name = "source-files", | ||
srcs = glob(["**/*.ts"]), | ||
) | ||
|
||
ng_test_library( | ||
name = "unit_tests_lib", | ||
srcs = glob(["**/*.spec.ts"]), | ||
deps = [ | ||
":testing", | ||
"//src/material-experimental/mdc-snack-bar", | ||
"//src/material/snack-bar/testing:harness_tests_lib", | ||
], | ||
) | ||
|
||
ng_web_test_suite( | ||
name = "unit_tests", | ||
static_files = [ | ||
"@npm//:node_modules/@material/snackbar/dist/mdc.snackbar.js", | ||
], | ||
deps = [ | ||
":unit_tests_lib", | ||
"//src/material-experimental:mdc_require_config.js", | ||
], | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
/** | ||
* @license | ||
* Copyright Google LLC All Rights Reserved. | ||
* | ||
* Use of this source code is governed by an MIT-style license that can be | ||
* found in the LICENSE file at https://angular.io/license | ||
*/ | ||
|
||
export * from './public-api'; |
10 changes: 10 additions & 0 deletions
10
src/material-experimental/mdc-snack-bar/testing/public-api.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
/** | ||
* @license | ||
* Copyright Google LLC All Rights Reserved. | ||
* | ||
* Use of this source code is governed by an MIT-style license that can be | ||
* found in the LICENSE file at https://angular.io/license | ||
*/ | ||
|
||
export * from './snack-bar-harness'; | ||
export * from './snack-bar-harness-filters'; |
12 changes: 12 additions & 0 deletions
12
src/material-experimental/mdc-snack-bar/testing/snack-bar-harness-filters.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
/** | ||
* @license | ||
* Copyright Google LLC All Rights Reserved. | ||
* | ||
* Use of this source code is governed by an MIT-style license that can be | ||
* found in the LICENSE file at https://angular.io/license | ||
*/ | ||
|
||
import {BaseHarnessFilters} from '@angular/cdk/testing'; | ||
|
||
/** A set of criteria that can be used to filter a list of `MatSnackBarHarness` instances. */ | ||
export interface SnackBarHarnessFilters extends BaseHarnessFilters {} |
7 changes: 7 additions & 0 deletions
7
src/material-experimental/mdc-snack-bar/testing/snack-bar-harness.spec.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
import {MatSnackBarModule, MatSnackBar} from '@angular/material-experimental/mdc-snack-bar'; | ||
import {runHarnessTests} from '@angular/material/snack-bar/testing/shared.spec'; | ||
import {MatSnackBarHarness} from './snack-bar-harness'; | ||
|
||
describe('MDC-based MatSnackBarHarness', () => { | ||
runHarnessTests(MatSnackBarModule, MatSnackBar, MatSnackBarHarness as any); | ||
}); |
122 changes: 122 additions & 0 deletions
122
src/material-experimental/mdc-snack-bar/testing/snack-bar-harness.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,122 @@ | ||
/** | ||
* @license | ||
* Copyright Google LLC All Rights Reserved. | ||
* | ||
* Use of this source code is governed by an MIT-style license that can be | ||
* found in the LICENSE file at https://angular.io/license | ||
*/ | ||
|
||
import {ComponentHarness, HarnessPredicate} from '@angular/cdk/testing'; | ||
import {SnackBarHarnessFilters} from './snack-bar-harness-filters'; | ||
|
||
/** Harness for interacting with an MDC-based mat-snack-bar in tests. */ | ||
export class MatSnackBarHarness extends ComponentHarness { | ||
// Developers can provide a custom component or template for the | ||
// snackbar. The canonical snack-bar parent is the "MatSnackBarContainer". | ||
// We use `:not([mat-exit])` to exclude snack bars that are in the process of being dismissed, | ||
// because the element only gets removed after the animation is finished and since it runs | ||
// outside of Angular, we don't have a way of being notified when it's done. | ||
/** The selector for the host element of a `MatSnackBar` instance. */ | ||
static hostSelector = '.mat-mdc-snack-bar-container:not([mat-exit])'; | ||
|
||
private _simpleSnackBar = this.locatorForOptional('.mat-mdc-simple-snack-bar'); | ||
private _simpleSnackBarMessage = | ||
this.locatorFor('.mat-mdc-simple-snack-bar .mat-mdc-snack-bar-label'); | ||
private _simpleSnackBarActionButton = | ||
this.locatorForOptional('.mat-mdc-simple-snack-bar .mat-mdc-snack-bar-action'); | ||
|
||
/** | ||
* Gets a `HarnessPredicate` that can be used to search for a `MatSnackBarHarness` that meets | ||
* certain criteria. | ||
* @param options Options for filtering which snack bar instances are considered a match. | ||
* @return a `HarnessPredicate` configured with the given options. | ||
*/ | ||
static with(options: SnackBarHarnessFilters = {}): HarnessPredicate<MatSnackBarHarness> { | ||
return new HarnessPredicate(MatSnackBarHarness, options); | ||
} | ||
|
||
/** | ||
* Gets the role of the snack-bar. The role of a snack-bar is determined based | ||
* on the ARIA politeness specified in the snack-bar config. | ||
*/ | ||
async getRole(): Promise<'alert'|'status'|null> { | ||
return (await this.host()).getAttribute('role') as Promise<'alert'|'status'|null>; | ||
} | ||
|
||
/** | ||
* Whether the snack-bar has an action. Method cannot be used for snack-bar's with custom content. | ||
*/ | ||
async hasAction(): Promise<boolean> { | ||
await this._assertSimpleSnackBar(); | ||
return (await this._simpleSnackBarActionButton()) !== null; | ||
} | ||
|
||
/** | ||
* Gets the description of the snack-bar. Method cannot be used for snack-bar's without action or | ||
* with custom content. | ||
*/ | ||
async getActionDescription(): Promise<string> { | ||
await this._assertSimpleSnackBarWithAction(); | ||
return (await this._simpleSnackBarActionButton())!.text(); | ||
} | ||
|
||
|
||
/** | ||
* Dismisses the snack-bar by clicking the action button. Method cannot be used for snack-bar's | ||
* without action or with custom content. | ||
*/ | ||
async dismissWithAction(): Promise<void> { | ||
await this._assertSimpleSnackBarWithAction(); | ||
await (await this._simpleSnackBarActionButton())!.click(); | ||
} | ||
|
||
/** | ||
* Gets the message of the snack-bar. Method cannot be used for snack-bar's with custom content. | ||
*/ | ||
async getMessage(): Promise<string> { | ||
await this._assertSimpleSnackBar(); | ||
return (await this._simpleSnackBarMessage()).text(); | ||
} | ||
|
||
/** Gets whether the snack-bar has been dismissed. */ | ||
async isDismissed(): Promise<boolean> { | ||
// We consider the snackbar dismissed if it's not in the DOM. We can assert that the | ||
// element isn't in the DOM by seeing that its width and height are zero. | ||
|
||
const host = await this.host(); | ||
const [exit, dimensions] = await Promise.all([ | ||
// The snackbar container is marked with the "exit" attribute after it has been dismissed | ||
// but before the animation has finished (after which it's removed from the DOM). | ||
host.getAttribute('mat-exit'), | ||
host.getDimensions(), | ||
]); | ||
|
||
return exit != null || (!!dimensions && dimensions.height === 0 && dimensions.width === 0); | ||
} | ||
|
||
/** | ||
* Asserts that the current snack-bar does not use custom content. Promise rejects if | ||
* custom content is used. | ||
*/ | ||
private async _assertSimpleSnackBar(): Promise<void> { | ||
if (!await this._isSimpleSnackBar()) { | ||
throw Error('Method cannot be used for snack-bar with custom content.'); | ||
} | ||
} | ||
|
||
/** | ||
* Asserts that the current snack-bar does not use custom content and has | ||
* an action defined. Otherwise the promise will reject. | ||
*/ | ||
private async _assertSimpleSnackBarWithAction(): Promise<void> { | ||
await this._assertSimpleSnackBar(); | ||
if (!await this.hasAction()) { | ||
throw Error('Method cannot be used for standard snack-bar without action.'); | ||
} | ||
} | ||
|
||
/** Whether the snack-bar is using the default content template. */ | ||
private async _isSimpleSnackBar(): Promise<boolean> { | ||
return await this._simpleSnackBar() !== null; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,7 @@ | ||
import {MatSnackBarModule} from '@angular/material/snack-bar'; | ||
import {MatSnackBarModule, MatSnackBar} from '@angular/material/snack-bar'; | ||
import {runHarnessTests} from '@angular/material/snack-bar/testing/shared.spec'; | ||
import {MatSnackBarHarness} from './snack-bar-harness'; | ||
|
||
describe('Non-MDC-based MatSnackBarHarness', () => { | ||
runHarnessTests(MatSnackBarModule, MatSnackBarHarness); | ||
runHarnessTests(MatSnackBarModule, MatSnackBar, MatSnackBarHarness); | ||
}); |