Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor(material-experimental/mdc-snack-bar): de-duplicate test harness logic #21496

Merged
merged 1 commit into from
Jan 9, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ ts_library(
),
module_name = "@angular/material-experimental/mdc-snack-bar/testing",
deps = [
"//src/cdk/a11y",
"//src/cdk/testing",
"//src/material/snack-bar/testing",
],
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,4 @@
*/

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

This file was deleted.

130 changes: 16 additions & 114 deletions src/material-experimental/mdc-snack-bar/testing/snack-bar-harness.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,130 +6,32 @@
* found in the LICENSE file at https://angular.io/license
*/

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

/** 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.
export class MatSnackBarHarness extends BaseMatSnackBarHarness {
// 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 _simpleSnackBarLiveRegion = this.locatorFor('[aria-live]');
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');
protected _messageSelector = '.mat-mdc-simple-snack-bar .mat-mdc-snack-bar-label';
protected _simpleSnackBarSelector = '.mat-mdc-simple-snack-bar';
protected _actionButtonSelector = '.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.
* @deprecated Use `getAriaLive` instead.
* @breaking-change 13.0.0
*/
async getRole(): Promise<'alert'|'status'|null> {
return (await this.host()).getAttribute('role') as Promise<'alert'|'status'|null>;
}

/**
* Gets the aria-live of the snack-bar's live region. The aria-live of a snack-bar is
* determined based on the ARIA politeness specified in the snack-bar config.
*/
async getAriaLive(): Promise<AriaLivePoliteness> {
return (await this._simpleSnackBarLiveRegion())
.getAttribute('aria-live') as Promise<AriaLivePoliteness>;
}

/**
* 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 parallel(() => [
// 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;
static with(options: SnackBarHarnessFilters = {}): HarnessPredicate<BaseMatSnackBarHarness> {
return new HarnessPredicate<BaseMatSnackBarHarness>(MatSnackBarHarness, options);
}
}
23 changes: 13 additions & 10 deletions src/material/snack-bar/testing/snack-bar-harness.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,10 @@ export class MatSnackBarHarness extends ContentContainerComponentHarness<string>
// snackbar. The canonical snack-bar parent is the "MatSnackBarContainer".
/** The selector for the host element of a `MatSnackBar` instance. */
static hostSelector = '.mat-snack-bar-container';

private _simpleSnackBar = this.locatorForOptional('.mat-simple-snackbar');
protected _messageSelector = '.mat-simple-snackbar > span';
protected _simpleSnackBarSelector = '.mat-simple-snackbar';
protected _actionButtonSelector = '.mat-simple-snackbar-action > button';
private _simpleSnackBarLiveRegion = this.locatorFor('[aria-live]');
private _simpleSnackBarMessage = this.locatorFor('.mat-simple-snackbar > span');
private _simpleSnackBarActionButton =
this.locatorForOptional('.mat-simple-snackbar-action > button');

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

/**
Expand All @@ -66,7 +64,7 @@ export class MatSnackBarHarness extends ContentContainerComponentHarness<string>
*/
async getActionDescription(): Promise<string> {
await this._assertSimpleSnackBarWithAction();
return (await this._simpleSnackBarActionButton())!.text();
return (await this._getSimpleSnackBarActionButton())!.text();
}


Expand All @@ -76,15 +74,15 @@ export class MatSnackBarHarness extends ContentContainerComponentHarness<string>
*/
async dismissWithAction(): Promise<void> {
await this._assertSimpleSnackBarWithAction();
await (await this._simpleSnackBarActionButton())!.click();
await (await this._getSimpleSnackBarActionButton())!.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();
return (await this.locatorFor(this._messageSelector)()).text();
}

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

/** Whether the snack-bar is using the default content template. */
private async _isSimpleSnackBar(): Promise<boolean> {
return await this._simpleSnackBar() !== null;
return await this.locatorForOptional(this._simpleSnackBarSelector)() !== null;
}

/** Gets the simple snack bar action button. */
private async _getSimpleSnackBarActionButton() {
return this.locatorForOptional(this._actionButtonSelector)();
}
}
3 changes: 3 additions & 0 deletions tools/public_api_guard/material/snack-bar/testing.d.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
export declare class MatSnackBarHarness extends ContentContainerComponentHarness<string> {
protected _actionButtonSelector: string;
protected _messageSelector: string;
protected _simpleSnackBarSelector: string;
dismissWithAction(): Promise<void>;
getActionDescription(): Promise<string>;
getAriaLive(): Promise<AriaLivePoliteness>;
Expand Down