sass-references/angular-material/material/list/testing/list-item-harness-base.ts

199 lines
6.6 KiB
TypeScript

/**
* @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.dev/license
*/
import {
ComponentHarness,
ComponentHarnessConstructor,
ContentContainerComponentHarness,
HarnessPredicate,
parallel,
} from '@angular/cdk/testing';
import {BaseListItemHarnessFilters, SubheaderHarnessFilters} from './list-harness-filters';
const iconSelector = '.mat-mdc-list-item-icon';
const avatarSelector = '.mat-mdc-list-item-avatar';
/**
* Gets a `HarnessPredicate` that applies the given `BaseListItemHarnessFilters` to the given
* list item harness.
* @template H The type of list item harness to create a predicate for.
* @param harnessType A constructor for a list item harness.
* @param options An instance of `BaseListItemHarnessFilters` to apply.
* @return A `HarnessPredicate` for the given harness type with the given options applied.
*/
export function getListItemPredicate<H extends MatListItemHarnessBase>(
harnessType: ComponentHarnessConstructor<H>,
options: BaseListItemHarnessFilters,
): HarnessPredicate<H> {
return new HarnessPredicate(harnessType, options)
.addOption('text', options.text, (harness, text) =>
HarnessPredicate.stringMatches(harness.getText(), text),
)
.addOption('fullText', options.fullText, (harness, fullText) =>
HarnessPredicate.stringMatches(harness.getFullText(), fullText),
)
.addOption('title', options.title, (harness, title) =>
HarnessPredicate.stringMatches(harness.getTitle(), title),
)
.addOption('secondaryText', options.secondaryText, (harness, secondaryText) =>
HarnessPredicate.stringMatches(harness.getSecondaryText(), secondaryText),
)
.addOption('tertiaryText', options.tertiaryText, (harness, tertiaryText) =>
HarnessPredicate.stringMatches(harness.getTertiaryText(), tertiaryText),
);
}
/** Harness for interacting with a list subheader. */
export class MatSubheaderHarness extends ComponentHarness {
static hostSelector = '.mat-mdc-subheader';
static with(options: SubheaderHarnessFilters = {}): HarnessPredicate<MatSubheaderHarness> {
return new HarnessPredicate(MatSubheaderHarness, options).addOption(
'text',
options.text,
(harness, text) => HarnessPredicate.stringMatches(harness.getText(), text),
);
}
/** Gets the full text content of the list item (including text from any font icons). */
async getText(): Promise<string> {
return (await this.host()).text();
}
}
/** Selectors for the various list item sections that may contain user content. */
export enum MatListItemSection {
CONTENT = '.mdc-list-item__content',
}
/** Enum describing the possible variants of a list item. */
export enum MatListItemType {
ONE_LINE_ITEM,
TWO_LINE_ITEM,
THREE_LINE_ITEM,
}
/**
* Shared behavior among the harnesses for the various `MatListItem` flavors.
* @docs-private
*/
export abstract class MatListItemHarnessBase extends ContentContainerComponentHarness<MatListItemSection> {
private _lines = this.locatorForAll('.mat-mdc-list-item-line');
private _primaryText = this.locatorFor('.mdc-list-item__primary-text');
private _avatar = this.locatorForOptional('.mat-mdc-list-item-avatar');
private _icon = this.locatorForOptional('.mat-mdc-list-item-icon');
private _unscopedTextContent = this.locatorFor('.mat-mdc-list-item-unscoped-content');
/** Gets the type of the list item, currently describing how many lines there are. */
async getType(): Promise<MatListItemType> {
const host = await this.host();
const [isOneLine, isTwoLine] = await parallel(() => [
host.hasClass('mdc-list-item--with-one-line'),
host.hasClass('mdc-list-item--with-two-lines'),
]);
if (isOneLine) {
return MatListItemType.ONE_LINE_ITEM;
} else if (isTwoLine) {
return MatListItemType.TWO_LINE_ITEM;
} else {
return MatListItemType.THREE_LINE_ITEM;
}
}
/**
* Gets the full text content of the list item, excluding text
* from icons and avatars.
*
* @deprecated Use the `getFullText` method instead.
* @breaking-change 16.0.0
*/
async getText(): Promise<string> {
return this.getFullText();
}
/**
* Gets the full text content of the list item, excluding text
* from icons and avatars.
*/
async getFullText(): Promise<string> {
return (await this.host()).text({exclude: `${iconSelector}, ${avatarSelector}`});
}
/** Gets the title of the list item. */
async getTitle(): Promise<string> {
return (await this._primaryText()).text();
}
/** Whether the list item is disabled. */
async isDisabled(): Promise<boolean> {
return (await this.host()).hasClass('mdc-list-item--disabled');
}
/**
* Gets the secondary line text of the list item. Null if the list item
* does not have a secondary line.
*/
async getSecondaryText(): Promise<string | null> {
const type = await this.getType();
if (type === MatListItemType.ONE_LINE_ITEM) {
return null;
}
const [lines, unscopedTextContent] = await parallel(() => [
this._lines(),
this._unscopedTextContent(),
]);
// If there is no explicit line for the secondary text, the unscoped text content
// is rendered as the secondary text (with potential text wrapping enabled).
if (lines.length >= 1) {
return lines[0].text();
} else {
return unscopedTextContent.text();
}
}
/**
* Gets the tertiary line text of the list item. Null if the list item
* does not have a tertiary line.
*/
async getTertiaryText(): Promise<string | null> {
const type = await this.getType();
if (type !== MatListItemType.THREE_LINE_ITEM) {
return null;
}
const [lines, unscopedTextContent] = await parallel(() => [
this._lines(),
this._unscopedTextContent(),
]);
// First we check if there is an explicit line for the tertiary text. If so, we return it.
// If there is at least an explicit secondary line though, then we know that the unscoped
// text content corresponds to the tertiary line. If there are no explicit lines at all,
// we know that the unscoped text content from the secondary text just wraps into the third
// line, but there *no* actual dedicated tertiary text.
if (lines.length === 2) {
return lines[1].text();
} else if (lines.length === 1) {
return unscopedTextContent.text();
}
return null;
}
/** Whether this list item has an avatar. */
async hasAvatar(): Promise<boolean> {
return !!(await this._avatar());
}
/** Whether this list item has an icon. */
async hasIcon(): Promise<boolean> {
return !!(await this._icon());
}
}