Skip to content

Commit b69a75b

Browse files
authored
feat(material-experimental/mdc-card): add test harness (#20298)
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.
1 parent fd2976f commit b69a75b

File tree

10 files changed

+222
-7
lines changed

10 files changed

+222
-7
lines changed

src/material-experimental/config.bzl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ entryPoints = [
44
"mdc-button",
55
"mdc-button/testing",
66
"mdc-card",
7+
"mdc-card/testing",
78
"mdc-checkbox",
89
"mdc-checkbox/testing",
910
"mdc-chips",
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
load("//src/e2e-app:test_suite.bzl", "e2e_test_suite")
2+
load("//tools:defaults.bzl", "ng_e2e_test_library", "ng_test_library", "ng_web_test_suite", "ts_library")
3+
4+
package(default_visibility = ["//visibility:public"])
5+
6+
ts_library(
7+
name = "testing",
8+
srcs = glob(
9+
["**/*.ts"],
10+
exclude = ["**/*.spec.ts"],
11+
),
12+
module_name = "@angular/material-experimental/mdc-card/testing",
13+
deps = [
14+
"//src/cdk/testing",
15+
],
16+
)
17+
18+
filegroup(
19+
name = "source-files",
20+
srcs = glob(["**/*.ts"]),
21+
)
22+
23+
ng_test_library(
24+
name = "unit_tests_lib",
25+
srcs = glob(
26+
["**/*.spec.ts"],
27+
exclude = [
28+
"**/*.e2e.spec.ts",
29+
"shared.spec.ts",
30+
],
31+
),
32+
deps = [
33+
":testing",
34+
"//src/material-experimental/mdc-card",
35+
"//src/material/card/testing:harness_tests_lib",
36+
],
37+
)
38+
39+
ng_web_test_suite(
40+
name = "unit_tests",
41+
deps = [":unit_tests_lib"],
42+
)
43+
44+
ng_e2e_test_library(
45+
name = "e2e_test_sources",
46+
srcs = glob(["**/*.e2e.spec.ts"]),
47+
deps = [
48+
"//src/cdk/testing",
49+
"//src/cdk/testing/private/e2e",
50+
"//src/cdk/testing/protractor",
51+
"//src/material-experimental/mdc-card/testing",
52+
],
53+
)
54+
55+
e2e_test_suite(
56+
name = "e2e_tests",
57+
deps = [
58+
":e2e_test_sources",
59+
"//src/cdk/testing/private/e2e",
60+
],
61+
)
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
/**
2+
* @license
3+
* Copyright Google LLC All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.io/license
7+
*/
8+
9+
import {BaseHarnessFilters} from '@angular/cdk/testing';
10+
11+
/** A set of criteria that can be used to filter a list of `MatCardHarness` instances. */
12+
export interface CardHarnessFilters extends BaseHarnessFilters {
13+
/** Only find instances whose text matches the given value. */
14+
text?: string | RegExp;
15+
/** Only find instances whose title matches the given value. */
16+
title?: string | RegExp;
17+
/** Only find instances whose subtitle matches the given value. */
18+
subtitle?: string | RegExp;
19+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import {HarnessLoader} from '@angular/cdk/testing';
2+
import {ProtractorHarnessEnvironment} from '@angular/cdk/testing/protractor';
3+
import {MatCardHarness} from '@angular/material-experimental/mdc-card/testing/card-harness';
4+
import {browser} from 'protractor';
5+
6+
describe('card harness', () => {
7+
let loader: HarnessLoader;
8+
9+
beforeEach(async () => await browser.get('/mdc-card'));
10+
11+
beforeEach(() => {
12+
loader = ProtractorHarnessEnvironment.loader();
13+
});
14+
15+
it('should get card text', async () => {
16+
const card = await loader.getHarness(MatCardHarness);
17+
expect(await card.getText()).toBe([
18+
'Shiba Inu',
19+
'Dog Breed',
20+
'The Shiba Inu is the smallest of the six original and distinct spitz breeds of dog from' +
21+
' Japan. A small, agile dog that copes very well with mountainous terrain, the Shiba Inu' +
22+
' was originally bred for hunting.',
23+
'LIKE',
24+
'SHARE'
25+
].join('\n'));
26+
});
27+
28+
it('should get title text', async () => {
29+
const card = await loader.getHarness(MatCardHarness);
30+
expect(await card.getTitleText()).toBe('Shiba Inu');
31+
});
32+
33+
it('should get subtitle text', async () => {
34+
const card = await loader.getHarness(MatCardHarness);
35+
expect(await card.getSubtitleText()).toBe('Dog Breed');
36+
});
37+
});
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import {MatCardModule} from '@angular/material-experimental/mdc-card';
2+
import {runHarnessTests} from '@angular/material/card/testing/shared.spec';
3+
import {MatCardHarness, MatCardSection} from './card-harness';
4+
5+
describe('MDC-based MatCardHarness', () => {
6+
runHarnessTests(MatCardModule, MatCardHarness as any, {
7+
header: MatCardSection.HEADER,
8+
content: MatCardSection.CONTENT,
9+
actions: MatCardSection.ACTIONS,
10+
footer: MatCardSection.FOOTER
11+
});
12+
});
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
/**
2+
* @license
3+
* Copyright Google LLC All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.io/license
7+
*/
8+
9+
import {HarnessPredicate, ContentContainerComponentHarness} from '@angular/cdk/testing';
10+
import {CardHarnessFilters} from './card-harness-filters';
11+
12+
/** Selectors for different sections of the mat-card that can container user content. */
13+
export const enum MatCardSection {
14+
HEADER = '.mat-mdc-card-header',
15+
CONTENT = '.mat-mdc-card-content',
16+
ACTIONS = '.mat-mdc-card-actions',
17+
FOOTER = '.mat-mdc-card-footer'
18+
}
19+
20+
/** Harness for interacting with an MDC-based mat-card in tests. */
21+
export class MatCardHarness extends ContentContainerComponentHarness<MatCardSection> {
22+
/** The selector for the host element of a `MatCard` instance. */
23+
static hostSelector = '.mat-mdc-card';
24+
25+
/**
26+
* Gets a `HarnessPredicate` that can be used to search for a `MatCardHarness` that meets
27+
* certain criteria.
28+
* @param options Options for filtering which card instances are considered a match.
29+
* @return a `HarnessPredicate` configured with the given options.
30+
*/
31+
static with(options: CardHarnessFilters = {}): HarnessPredicate<MatCardHarness> {
32+
return new HarnessPredicate(MatCardHarness, options)
33+
.addOption('text', options.text,
34+
(harness, text) => HarnessPredicate.stringMatches(harness.getText(), text))
35+
.addOption('title', options.title,
36+
(harness, title) => HarnessPredicate.stringMatches(harness.getTitleText(), title))
37+
.addOption('subtitle', options.subtitle,
38+
(harness, subtitle) =>
39+
HarnessPredicate.stringMatches(harness.getSubtitleText(), subtitle));
40+
}
41+
42+
private _title = this.locatorForOptional('.mat-mdc-card-title');
43+
private _subtitle = this.locatorForOptional('.mat-mdc-card-subtitle');
44+
45+
/** Gets all of the card's content as text. */
46+
async getText(): Promise<string> {
47+
return (await this.host()).text();
48+
}
49+
50+
/** Gets the cards's title text. */
51+
async getTitleText(): Promise<string> {
52+
return (await this._title())?.text() ?? '';
53+
}
54+
55+
/** Gets the cards's subtitle text. */
56+
async getSubtitleText(): Promise<string> {
57+
return (await this._subtitle())?.text() ?? '';
58+
}
59+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
/**
2+
* @license
3+
* Copyright Google LLC All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.io/license
7+
*/
8+
9+
export * from './public-api';
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
/**
2+
* @license
3+
* Copyright Google LLC All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.io/license
7+
*/
8+
9+
export * from './card-harness';
10+
export * from './card-harness-filters';
Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,12 @@
11
import {MatCardModule} from '@angular/material/card';
22
import {runHarnessTests} from '@angular/material/card/testing/shared.spec';
3-
import {MatCardHarness} from './card-harness';
3+
import {MatCardHarness, MatCardSection} from './card-harness';
44

55
describe('Non-MDC-based MatCardHarness', () => {
6-
runHarnessTests(MatCardModule, MatCardHarness);
6+
runHarnessTests(MatCardModule, MatCardHarness, {
7+
header: MatCardSection.HEADER,
8+
content: MatCardSection.CONTENT,
9+
actions: MatCardSection.ACTIONS,
10+
footer: MatCardSection.FOOTER
11+
});
712
});

src/material/card/testing/shared.spec.ts

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@ import {MatCardHarness, MatCardSection} from '@angular/material/card/testing/car
77

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

@@ -73,28 +75,28 @@ export function runHarnessTests(
7375

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

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

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

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

0 commit comments

Comments
 (0)