Skip to content

Commit

Permalink
feat(material-experimental/mdc-card): add test harness (#20298)
Browse files Browse the repository at this point in the history
Sets up a test harness for the MDC-based `mat-card`. Also makes some changes to the shared tests since they were hardcoded to the non-MDC selectors.
  • Loading branch information
crisbeto committed Aug 14, 2020
1 parent fd2976f commit b69a75b
Show file tree
Hide file tree
Showing 10 changed files with 222 additions and 7 deletions.
1 change: 1 addition & 0 deletions src/material-experimental/config.bzl
Expand Up @@ -4,6 +4,7 @@ entryPoints = [
"mdc-button",
"mdc-button/testing",
"mdc-card",
"mdc-card/testing",
"mdc-checkbox",
"mdc-checkbox/testing",
"mdc-chips",
Expand Down
61 changes: 61 additions & 0 deletions src/material-experimental/mdc-card/testing/BUILD.bazel
@@ -0,0 +1,61 @@
load("//src/e2e-app:test_suite.bzl", "e2e_test_suite")
load("//tools:defaults.bzl", "ng_e2e_test_library", "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-card/testing",
deps = [
"//src/cdk/testing",
],
)

filegroup(
name = "source-files",
srcs = glob(["**/*.ts"]),
)

ng_test_library(
name = "unit_tests_lib",
srcs = glob(
["**/*.spec.ts"],
exclude = [
"**/*.e2e.spec.ts",
"shared.spec.ts",
],
),
deps = [
":testing",
"//src/material-experimental/mdc-card",
"//src/material/card/testing:harness_tests_lib",
],
)

ng_web_test_suite(
name = "unit_tests",
deps = [":unit_tests_lib"],
)

ng_e2e_test_library(
name = "e2e_test_sources",
srcs = glob(["**/*.e2e.spec.ts"]),
deps = [
"//src/cdk/testing",
"//src/cdk/testing/private/e2e",
"//src/cdk/testing/protractor",
"//src/material-experimental/mdc-card/testing",
],
)

e2e_test_suite(
name = "e2e_tests",
deps = [
":e2e_test_sources",
"//src/cdk/testing/private/e2e",
],
)
19 changes: 19 additions & 0 deletions src/material-experimental/mdc-card/testing/card-harness-filters.ts
@@ -0,0 +1,19 @@
/**
* @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 `MatCardHarness` instances. */
export interface CardHarnessFilters extends BaseHarnessFilters {
/** Only find instances whose text matches the given value. */
text?: string | RegExp;
/** Only find instances whose title matches the given value. */
title?: string | RegExp;
/** Only find instances whose subtitle matches the given value. */
subtitle?: string | RegExp;
}
@@ -0,0 +1,37 @@
import {HarnessLoader} from '@angular/cdk/testing';
import {ProtractorHarnessEnvironment} from '@angular/cdk/testing/protractor';
import {MatCardHarness} from '@angular/material-experimental/mdc-card/testing/card-harness';
import {browser} from 'protractor';

describe('card harness', () => {
let loader: HarnessLoader;

beforeEach(async () => await browser.get('/mdc-card'));

beforeEach(() => {
loader = ProtractorHarnessEnvironment.loader();
});

it('should get card text', async () => {
const card = await loader.getHarness(MatCardHarness);
expect(await card.getText()).toBe([
'Shiba Inu',
'Dog Breed',
'The Shiba Inu is the smallest of the six original and distinct spitz breeds of dog from' +
' Japan. A small, agile dog that copes very well with mountainous terrain, the Shiba Inu' +
' was originally bred for hunting.',
'LIKE',
'SHARE'
].join('\n'));
});

it('should get title text', async () => {
const card = await loader.getHarness(MatCardHarness);
expect(await card.getTitleText()).toBe('Shiba Inu');
});

it('should get subtitle text', async () => {
const card = await loader.getHarness(MatCardHarness);
expect(await card.getSubtitleText()).toBe('Dog Breed');
});
});
12 changes: 12 additions & 0 deletions src/material-experimental/mdc-card/testing/card-harness.spec.ts
@@ -0,0 +1,12 @@
import {MatCardModule} from '@angular/material-experimental/mdc-card';
import {runHarnessTests} from '@angular/material/card/testing/shared.spec';
import {MatCardHarness, MatCardSection} from './card-harness';

describe('MDC-based MatCardHarness', () => {
runHarnessTests(MatCardModule, MatCardHarness as any, {
header: MatCardSection.HEADER,
content: MatCardSection.CONTENT,
actions: MatCardSection.ACTIONS,
footer: MatCardSection.FOOTER
});
});
59 changes: 59 additions & 0 deletions src/material-experimental/mdc-card/testing/card-harness.ts
@@ -0,0 +1,59 @@
/**
* @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 {HarnessPredicate, ContentContainerComponentHarness} from '@angular/cdk/testing';
import {CardHarnessFilters} from './card-harness-filters';

/** Selectors for different sections of the mat-card that can container user content. */
export const enum MatCardSection {
HEADER = '.mat-mdc-card-header',
CONTENT = '.mat-mdc-card-content',
ACTIONS = '.mat-mdc-card-actions',
FOOTER = '.mat-mdc-card-footer'
}

/** Harness for interacting with an MDC-based mat-card in tests. */
export class MatCardHarness extends ContentContainerComponentHarness<MatCardSection> {
/** The selector for the host element of a `MatCard` instance. */
static hostSelector = '.mat-mdc-card';

/**
* Gets a `HarnessPredicate` that can be used to search for a `MatCardHarness` that meets
* certain criteria.
* @param options Options for filtering which card instances are considered a match.
* @return a `HarnessPredicate` configured with the given options.
*/
static with(options: CardHarnessFilters = {}): HarnessPredicate<MatCardHarness> {
return new HarnessPredicate(MatCardHarness, options)
.addOption('text', options.text,
(harness, text) => HarnessPredicate.stringMatches(harness.getText(), text))
.addOption('title', options.title,
(harness, title) => HarnessPredicate.stringMatches(harness.getTitleText(), title))
.addOption('subtitle', options.subtitle,
(harness, subtitle) =>
HarnessPredicate.stringMatches(harness.getSubtitleText(), subtitle));
}

private _title = this.locatorForOptional('.mat-mdc-card-title');
private _subtitle = this.locatorForOptional('.mat-mdc-card-subtitle');

/** Gets all of the card's content as text. */
async getText(): Promise<string> {
return (await this.host()).text();
}

/** Gets the cards's title text. */
async getTitleText(): Promise<string> {
return (await this._title())?.text() ?? '';
}

/** Gets the cards's subtitle text. */
async getSubtitleText(): Promise<string> {
return (await this._subtitle())?.text() ?? '';
}
}
9 changes: 9 additions & 0 deletions src/material-experimental/mdc-card/testing/index.ts
@@ -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 src/material-experimental/mdc-card/testing/public-api.ts
@@ -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 './card-harness';
export * from './card-harness-filters';
9 changes: 7 additions & 2 deletions src/material/card/testing/card-harness.spec.ts
@@ -1,7 +1,12 @@
import {MatCardModule} from '@angular/material/card';
import {runHarnessTests} from '@angular/material/card/testing/shared.spec';
import {MatCardHarness} from './card-harness';
import {MatCardHarness, MatCardSection} from './card-harness';

describe('Non-MDC-based MatCardHarness', () => {
runHarnessTests(MatCardModule, MatCardHarness);
runHarnessTests(MatCardModule, MatCardHarness, {
header: MatCardSection.HEADER,
content: MatCardSection.CONTENT,
actions: MatCardSection.ACTIONS,
footer: MatCardSection.FOOTER
});
});
12 changes: 7 additions & 5 deletions src/material/card/testing/shared.spec.ts
Expand Up @@ -7,7 +7,9 @@ import {MatCardHarness, MatCardSection} from '@angular/material/card/testing/car

/** Shared tests to run on both the original and MDC-based cards. */
export function runHarnessTests(
cardModule: typeof MatCardModule, cardHarness: typeof MatCardHarness) {
cardModule: typeof MatCardModule,
cardHarness: typeof MatCardHarness,
contentSelectors: {header: string, content: string, actions: string, footer: string}) {
let fixture: ComponentFixture<CardHarnessTest>;
let loader: HarnessLoader;

Expand Down Expand Up @@ -73,28 +75,28 @@ export function runHarnessTests(

it('should get a harness loader for the card header', async () => {
const card = await loader.getHarness(cardHarness.with({title: 'Shiba Inu'}));
const headerLoader = await card.getChildLoader(MatCardSection.HEADER);
const headerLoader = await card.getChildLoader(contentSelectors.header as MatCardSection);
const headerSubcomponents = await headerLoader?.getAllHarnesses(DummyHarness) ?? [];
expect(headerSubcomponents.length).toBe(2);
});

it('should get a harness loader for the card content', async () => {
const card = await loader.getHarness(cardHarness.with({title: 'Shiba Inu'}));
const contentLoader = await card.getChildLoader(MatCardSection.CONTENT);
const contentLoader = await card.getChildLoader(contentSelectors.content as MatCardSection);
const contentSubcomponents = await contentLoader?.getAllHarnesses(DummyHarness) ?? [];
expect(contentSubcomponents.length).toBe(1);
});

it('should get a harness loader for the card actions', async () => {
const card = await loader.getHarness(cardHarness.with({title: 'Shiba Inu'}));
const actionLoader = await card.getChildLoader(MatCardSection.ACTIONS);
const actionLoader = await card.getChildLoader(contentSelectors.actions as MatCardSection);
const actionSubcomponents = await actionLoader?.getAllHarnesses(DummyHarness) ?? [];
expect(actionSubcomponents.length).toBe(2);
});

it('should get a harness loader for the card footer', async () => {
const card = await loader.getHarness(cardHarness.with({title: 'Shiba Inu'}));
const footerLoader = await card.getChildLoader(MatCardSection.FOOTER);
const footerLoader = await card.getChildLoader(contentSelectors.footer as MatCardSection);
const footerSubcomponents = await footerLoader?.getAllHarnesses(DummyHarness) ?? [];
expect(footerSubcomponents.length).toBe(1);
});
Expand Down

0 comments on commit b69a75b

Please sign in to comment.