Skip to content

Commit

Permalink
refactor(material-experimental/mdc-snack-bar): de-duplicate test harn…
Browse files Browse the repository at this point in the history
…ess logic (angular#21496)

Changes the MDC-based `MatSnackBarHarness` to extend the non-MDC one since
all the logic is the same, apart form a few selectors.
  • Loading branch information
crisbeto authored Jan 9, 2021
1 parent 390afca commit 7a8762a
Show file tree
Hide file tree
Showing 6 changed files with 34 additions and 138 deletions.
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

0 comments on commit 7a8762a

Please sign in to comment.