Skip to content

Commit 0b1e1fb

Browse files
committed
feat(mdc-experimental): add test harness for mat-tooltip
Sets up a test harness for the current Material tooltip.
1 parent 8e321ae commit 0b1e1fb

22 files changed

+401
-5
lines changed

.github/CODEOWNERS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@
9898
/src/material-experimental/mdc-radio/** @mmalerba
9999
/src/material-experimental/mdc-slide-toggle/** @crisbeto
100100
/src/material-experimental/mdc-tabs/** @crisbeto
101+
/src/material-experimental/mdc-tooltip/** @crisbeto
101102
/src/material-experimental/mdc-theming/** @mmalerba
102103
/src/material-experimental/mdc-typography/** @mmalerba
103104
/src/material-experimental/popover-edit/** @kseamon @andrewseguin

src/cdk-experimental/testing/protractor/protractor-element.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,12 @@ export class ProtractorElement implements TestElement {
3939
.perform();
4040
}
4141

42+
async movePointerAway(): Promise<void> {
43+
return browser.actions()
44+
.mouseMove(await this.element.getWebElement(), {x: -1, y: -1})
45+
.perform();
46+
}
47+
4248
async sendKeys(keys: string): Promise<void> {
4349
return this.element.sendKeys(keys);
4450
}

src/cdk-experimental/testing/test-element.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,9 @@ export interface TestElement {
2929
/** Hovers the mouse over the element. */
3030
hover(): Promise<void>;
3131

32+
/** Moves the pointer away from the element. */
33+
movePointerAway(): Promise<void>;
34+
3235
/**
3336
* Sends the given string to the input as a series of key presses. Also fires input events
3437
* and attempts to add the string to the Element's value.

src/cdk-experimental/testing/testbed/testbed-harness-environment.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ export class TestbedHarnessEnvironment extends HarnessEnvironment<Element> {
3737
}
3838

3939
protected getDocumentRoot(): Element {
40-
return this._fixture.nativeElement;
40+
return document.body;
4141
}
4242

4343
protected createTestElement(element: Element): TestElement {

src/cdk-experimental/testing/testbed/unit-test-element.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,12 @@ export class UnitTestElement implements TestElement {
6666
await this._stabilize();
6767
}
6868

69+
async movePointerAway(): Promise<void> {
70+
await this._stabilize();
71+
dispatchMouseEvent(this.element, 'mouseleave');
72+
await this._stabilize();
73+
}
74+
6975
async sendKeys(keys: string): Promise<void> {
7076
await this._stabilize();
7177
triggerFocus(this.element as HTMLElement);

src/cdk-experimental/testing/tests/protractor.e2e.spec.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,18 @@ describe('ProtractorHarnessEnvironment', () => {
205205
expect(classAttr).toContain('hovering');
206206
});
207207

208+
it('should be able to stop hovering', async () => {
209+
const host = await harness.host();
210+
let classAttr = await host.getAttribute('class');
211+
expect(classAttr).not.toContain('hovering');
212+
await host.hover();
213+
classAttr = await host.getAttribute('class');
214+
expect(classAttr).toContain('hovering');
215+
await host.movePointerAway();
216+
classAttr = await host.getAttribute('class');
217+
expect(classAttr).not.toContain('hovering');
218+
});
219+
208220
it('should be able to getAttribute', async () => {
209221
const memoStr = `
210222
This is an example that shows how to use component harness

src/cdk-experimental/testing/tests/test-main-component.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@ import {
1919
templateUrl: 'test-main-component.html',
2020
host: {
2121
'[class.hovering]': '_isHovering',
22-
'(mouseenter)': 'onMouseOver()',
23-
'(mouseout)': 'onMouseOut()',
22+
'(mouseenter)': 'onMouseEnter()',
23+
'(mouseleave)': 'onMouseLeave()',
2424
},
2525
encapsulation: ViewEncapsulation.None,
2626
changeDetection: ChangeDetectionStrategy.OnPush,
@@ -36,11 +36,11 @@ export class TestMainComponent {
3636
testMethods: string[];
3737
_isHovering: boolean;
3838

39-
onMouseOver() {
39+
onMouseEnter() {
4040
this._isHovering = true;
4141
}
4242

43-
onMouseOut() {
43+
onMouseLeave() {
4444
this._isHovering = false;
4545
}
4646

src/cdk-experimental/testing/tests/testbed.spec.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,18 @@ describe('TestbedHarnessEnvironment', () => {
224224
expect(classAttr).toContain('hovering');
225225
});
226226

227+
it('should be able to stop hovering', async () => {
228+
const host = await harness.host();
229+
let classAttr = await host.getAttribute('class');
230+
expect(classAttr).not.toContain('hovering');
231+
await host.hover();
232+
classAttr = await host.getAttribute('class');
233+
expect(classAttr).toContain('hovering');
234+
await host.movePointerAway();
235+
classAttr = await host.getAttribute('class');
236+
expect(classAttr).not.toContain('hovering');
237+
});
238+
227239
it('should be able to getAttribute', async () => {
228240
const memoStr = `
229241
This is an example that shows how to use component harness
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
package(default_visibility = ["//visibility:public"])
2+
3+
load("@io_bazel_rules_sass//:defs.bzl", "sass_binary", "sass_library")
4+
load("//tools:defaults.bzl", "ng_e2e_test_library", "ng_module", "ng_test_library", "ng_web_test_suite", "ts_library")
5+
load("//src/e2e-app:test_suite.bzl", "e2e_test_suite")
6+
7+
ng_module(
8+
name = "mdc-tooltip",
9+
srcs = glob(
10+
["**/*.ts"],
11+
exclude = [
12+
"**/*.spec.ts",
13+
"harness/**",
14+
],
15+
),
16+
assets = [
17+
# TODO: include scss assets
18+
] + glob(["**/*.html"]),
19+
module_name = "@angular/material-experimental/mdc-tooltip",
20+
deps = [
21+
"//src/material/core",
22+
],
23+
)
24+
25+
ts_library(
26+
name = "harness",
27+
srcs = glob(
28+
["harness/**/*.ts"],
29+
exclude = ["**/*.spec.ts"],
30+
),
31+
deps = [
32+
"//src/cdk-experimental/testing",
33+
"//src/cdk/private/testing",
34+
],
35+
)
36+
37+
sass_library(
38+
name = "mdc_tooltip_scss_lib",
39+
srcs = glob(["**/_*.scss"]),
40+
deps = [
41+
"//src/material-experimental/mdc-helpers:mdc_helpers_scss_lib",
42+
"//src/material-experimental/mdc-helpers:mdc_scss_deps_lib",
43+
"//src/material/core:core_scss_lib",
44+
],
45+
)
46+
47+
sass_binary(
48+
name = "tooltip_scss",
49+
src = "tooltip.scss",
50+
include_paths = [
51+
"external/npm/node_modules",
52+
],
53+
deps = [
54+
"//src/material-experimental/mdc-helpers:mdc_helpers_scss_lib",
55+
"//src/material-experimental/mdc-helpers:mdc_scss_deps_lib",
56+
"//src/material/core:all_themes",
57+
],
58+
)
59+
60+
ng_test_library(
61+
name = "tooltip_tests_lib",
62+
srcs = [
63+
"harness/tooltip-harness.spec.ts",
64+
],
65+
deps = [
66+
":harness",
67+
":mdc-tooltip",
68+
"//src/cdk-experimental/testing",
69+
"//src/cdk-experimental/testing/testbed",
70+
"//src/cdk/testing",
71+
"//src/material/tooltip",
72+
"@npm//@angular/platform-browser",
73+
],
74+
)
75+
76+
ng_web_test_suite(
77+
name = "unit_tests",
78+
deps = [
79+
":tooltip_tests_lib",
80+
"//src/material-experimental:mdc_require_config.js",
81+
],
82+
)
83+
84+
ng_e2e_test_library(
85+
name = "e2e_test_sources",
86+
srcs = glob(["**/*.e2e.spec.ts"]),
87+
deps = [
88+
"//src/cdk/private/testing/e2e",
89+
],
90+
)
91+
92+
e2e_test_suite(
93+
name = "e2e_tests",
94+
deps = [
95+
":e2e_test_sources",
96+
"//src/cdk/private/testing/e2e",
97+
],
98+
)
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
<!-- TODO -->
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
@import '../mdc-helpers/mdc-helpers';
2+
3+
@mixin mat-tooltip-theme-mdc($theme) {
4+
@include mat-using-mdc-theme($theme) {
5+
// TODO: implement MDC-based tooltip.
6+
}
7+
}
8+
9+
@mixin mat-tooltip-typography-mdc($config) {
10+
@include mat-using-mdc-typography($config) {
11+
// TODO: implement MDC-based tooltip.
12+
}
13+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
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 {ComponentHarness} from '@angular/cdk-experimental/testing';
10+
11+
12+
/**
13+
* Harness for interacting with a MDC-based mat-tooltip in tests.
14+
* @dynamic
15+
*/
16+
export class MatTooltipHarness extends ComponentHarness {
17+
// TODO: implement once MDC tooltip is done.
18+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
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 type TooltipHarnessFilters = {
10+
id?: string;
11+
};
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
import {HarnessLoader} from '@angular/cdk-experimental/testing';
2+
import {TestbedHarnessEnvironment} from '@angular/cdk-experimental/testing/testbed';
3+
import {Component} from '@angular/core';
4+
import {ComponentFixture, TestBed} from '@angular/core/testing';
5+
import {MatTooltipModule} from '@angular/material/tooltip';
6+
import {NoopAnimationsModule} from '@angular/platform-browser/animations';
7+
import {MatTooltipModule as MatMdcTooltipModule} from '../index';
8+
import {MatTooltipHarness} from './tooltip-harness';
9+
import {MatTooltipHarness as MatMdcTooltipHarness} from './mdc-tooltip-harness';
10+
11+
let fixture: ComponentFixture<TooltipHarnessTest>;
12+
let loader: HarnessLoader;
13+
let harness: typeof MatTooltipHarness;
14+
15+
describe('MatTooltipHarness', () => {
16+
describe('non-MDC-based', () => {
17+
beforeEach(async () => {
18+
await TestBed.configureTestingModule({
19+
imports: [MatTooltipModule, NoopAnimationsModule],
20+
declarations: [TooltipHarnessTest],
21+
}).compileComponents();
22+
23+
fixture = TestBed.createComponent(TooltipHarnessTest);
24+
fixture.detectChanges();
25+
loader = TestbedHarnessEnvironment.loader(fixture);
26+
harness = MatTooltipHarness;
27+
});
28+
29+
runTests();
30+
});
31+
32+
describe('MDC-based', () => {
33+
beforeEach(async () => {
34+
await TestBed.configureTestingModule({
35+
imports: [MatMdcTooltipModule, NoopAnimationsModule],
36+
declarations: [TooltipHarnessTest],
37+
}).compileComponents();
38+
39+
fixture = TestBed.createComponent(TooltipHarnessTest);
40+
fixture.detectChanges();
41+
loader = TestbedHarnessEnvironment.loader(fixture);
42+
// Public APIs are the same as MatTooltipHarness, but cast
43+
// is necessary because of different private fields.
44+
harness = MatMdcTooltipHarness as any;
45+
});
46+
47+
// TODO: enable after MDC tooltip is implemented
48+
// runTests();
49+
});
50+
});
51+
52+
/** Shared tests to run on both the original and MDC-based tooltip. */
53+
function runTests() {
54+
it('should load all tooltip harnesses', async () => {
55+
const tooltips = await loader.getAllHarnesses(harness);
56+
expect(tooltips.length).toBe(2);
57+
});
58+
59+
it('should be able to open a tooltip', async () => {
60+
const tooltip = await loader.getHarness(harness.with({id: 'one'}));
61+
expect(await tooltip.isOpen()).toBe(false);
62+
await tooltip.open();
63+
expect(await tooltip.isOpen()).toBe(true);
64+
});
65+
66+
// TODO(crisbeto): this fails because we have a setTimeout inside the tooltip which
67+
// can't be flushed. Talk to Miles about how we should approach this in harnesses.
68+
// it('should be able to close a tooltip', async () => {
69+
// const tooltip = await loader.getHarness(harness.with({id: 'one'}));
70+
// expect(await tooltip.isOpen()).toBe(false);
71+
// await tooltip.open();
72+
// expect(await tooltip.isOpen()).toBe(true);
73+
// await tooltip.close();
74+
// expect(await tooltip.isOpen()).toBe(false);
75+
// });
76+
77+
it('should be able to get the text of a tooltip', async () => {
78+
const tooltip = await loader.getHarness(harness.with({id: 'one'}));
79+
await tooltip.open();
80+
expect(await tooltip.getTooltipText()).toBe('Tooltip message');
81+
});
82+
83+
it('should return empty when getting the tooltip text while closed', async () => {
84+
const tooltip = await loader.getHarness(harness.with({id: 'one'}));
85+
expect(await tooltip.getTooltipText()).toBe('');
86+
});
87+
}
88+
89+
@Component({
90+
template: `
91+
<button [matTooltip]="message" id="one">Trigger 1</button>
92+
<button matTooltip="Static message" id="two">Trigger 2</button>
93+
`
94+
})
95+
class TooltipHarnessTest {
96+
message = 'Tooltip message';
97+
}
98+

0 commit comments

Comments
 (0)