Skip to content

Commit 54c2d16

Browse files
authored
feat(card/testing): Add a test harness for MatCard (#19833)
* card harness scaffolding * implement harness * add tests * CardHarness implements HarenssLoader * remove redundant harness loader getter methods * fix lint
1 parent 0012121 commit 54c2d16

File tree

13 files changed

+411
-0
lines changed

13 files changed

+411
-0
lines changed

src/cdk/testing/component-harness.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,9 @@ export interface LocatorFactory {
202202
locatorForAll<T extends (HarnessQuery<any> | string)[]>(...queries: T):
203203
AsyncFactoryFn<LocatorFnResult<T>[]>;
204204

205+
/** @return A `HarnessLoader` rooted at the root element of this `LocatorFactory`. */
206+
rootHarnessLoader(): Promise<HarnessLoader>;
207+
205208
/**
206209
* Gets a `HarnessLoader` instance for an element under the root of this `LocatorFactory`.
207210
* @param selector The selector for the root element.

src/cdk/testing/harness-environment.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,11 @@ export abstract class HarnessEnvironment<E> implements HarnessLoader, LocatorFac
7575
return () => this._getAllHarnessesAndTestElements(queries);
7676
}
7777

78+
// Implemented as part of the `LocatorFactory` interface.
79+
async rootHarnessLoader(): Promise<HarnessLoader> {
80+
return this;
81+
}
82+
7883
// Implemented as part of the `LocatorFactory` interface.
7984
async harnessLoaderFor(selector: string): Promise<HarnessLoader> {
8085
return this.createEnvironment(await _assertResultFound(this.getAllRawElements(selector),

src/material/card/testing/BUILD.bazel

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
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/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 = "harness_tests_lib",
25+
srcs = ["shared.spec.ts"],
26+
deps = [
27+
":testing",
28+
"//src/cdk/testing",
29+
"//src/cdk/testing/testbed",
30+
"//src/material/card",
31+
"@npm//@angular/platform-browser",
32+
],
33+
)
34+
35+
ng_test_library(
36+
name = "unit_tests_lib",
37+
srcs = glob(
38+
["**/*.spec.ts"],
39+
exclude = [
40+
"**/*.e2e.spec.ts",
41+
"shared.spec.ts",
42+
],
43+
),
44+
deps = [
45+
":harness_tests_lib",
46+
":testing",
47+
"//src/material/card",
48+
],
49+
)
50+
51+
ng_web_test_suite(
52+
name = "unit_tests",
53+
deps = [":unit_tests_lib"],
54+
)
55+
56+
ng_e2e_test_library(
57+
name = "e2e_test_sources",
58+
srcs = glob(["**/*.e2e.spec.ts"]),
59+
deps = [
60+
"//src/cdk/testing",
61+
"//src/cdk/testing/private/e2e",
62+
"//src/cdk/testing/protractor",
63+
"//src/material/card/testing",
64+
],
65+
)
66+
67+
e2e_test_suite(
68+
name = "e2e_tests",
69+
deps = [
70+
":e2e_test_sources",
71+
"//src/cdk/testing/private/e2e",
72+
],
73+
)
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/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('/cards'));
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: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import {MatCardModule} from '@angular/material/card';
2+
import {runHarnessTests} from '@angular/material/card/testing/shared.spec';
3+
import {MatCardHarness} from './card-harness';
4+
5+
describe('Non-MDC-based MatCardHarness', () => {
6+
runHarnessTests(MatCardModule, MatCardHarness);
7+
});
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
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 {
10+
ComponentHarness,
11+
HarnessLoader,
12+
HarnessPredicate,
13+
HarnessQuery
14+
} from '@angular/cdk/testing';
15+
import {CardHarnessFilters} from './card-harness-filters';
16+
17+
/** Selectors for different sections of the mat-card that can container user content. */
18+
export const enum MatCardSection {
19+
HEADER = '.mat-card-header',
20+
CONTENT = '.mat-card-content',
21+
ACTIONS = '.mat-card-actions',
22+
FOOTER = '.mat-card-footer'
23+
}
24+
25+
/** Harness for interacting with a standard mat-card in tests. */
26+
export class MatCardHarness extends ComponentHarness implements HarnessLoader {
27+
/** The selector for the host element of a `MatCard` instance. */
28+
static hostSelector = 'mat-card';
29+
30+
/**
31+
* Gets a `HarnessPredicate` that can be used to search for a `MatCardHarness` that meets
32+
* certain criteria.
33+
* @param options Options for filtering which card instances are considered a match.
34+
* @return a `HarnessPredicate` configured with the given options.
35+
*/
36+
static with(options: CardHarnessFilters = {}): HarnessPredicate<MatCardHarness> {
37+
return new HarnessPredicate(MatCardHarness, options)
38+
.addOption('text', options.text,
39+
(harness, text) => HarnessPredicate.stringMatches(harness.getText(), text))
40+
.addOption('title', options.title,
41+
(harness, title) => HarnessPredicate.stringMatches(harness.getTitleText(), title))
42+
.addOption('subtitle', options.subtitle,
43+
(harness, subtitle) =>
44+
HarnessPredicate.stringMatches(harness.getSubtitleText(), subtitle));
45+
}
46+
47+
private _title = this.locatorForOptional('.mat-card-title');
48+
private _subtitle = this.locatorForOptional('.mat-card-subtitle');
49+
50+
/** Gets all of the card's content as text. */
51+
async getText(): Promise<string> {
52+
return (await this.host()).text();
53+
}
54+
55+
/** Gets the cards's title text. */
56+
async getTitleText(): Promise<string> {
57+
return (await this._title())?.text() ?? '';
58+
}
59+
60+
/** Gets the cards's subtitle text. */
61+
async getSubtitleText(): Promise<string> {
62+
return (await this._subtitle())?.text() ?? '';
63+
}
64+
65+
async getChildLoader(selector: string): Promise<HarnessLoader> {
66+
return (await this.locatorFactory.rootHarnessLoader()).getChildLoader(selector);
67+
}
68+
69+
async getAllChildLoaders(selector: string): Promise<HarnessLoader[]> {
70+
return (await this.locatorFactory.rootHarnessLoader()).getAllChildLoaders(selector);
71+
}
72+
73+
async getHarness<T extends ComponentHarness>(query: HarnessQuery<T>): Promise<T> {
74+
return (await this.locatorFactory.rootHarnessLoader()).getHarness(query);
75+
}
76+
77+
async getAllHarnesses<T extends ComponentHarness>(query: HarnessQuery<T>): Promise<T[]> {
78+
return (await this.locatorFactory.rootHarnessLoader()).getAllHarnesses(query);
79+
}
80+
}

src/material/card/testing/index.ts

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';

0 commit comments

Comments
 (0)