Skip to content

Commit e3ccecb

Browse files
committed
feat(material-experimental): add button toggle test harness
Adds a test harness for `mat-button-toggle`.
1 parent c2562fb commit e3ccecb

14 files changed

+491
-0
lines changed

.github/CODEOWNERS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,7 @@
108108
/src/material-experimental/mdc-slider/** @devversion
109109
/src/material-experimental/mdc-tabs/** @crisbeto
110110
/src/material-experimental/mdc-sidenav/** @crisbeto
111+
/src/material-experimental/mdc-button-toggle/** @crisbeto
111112
/src/material-experimental/mdc-theming/** @mmalerba
112113
/src/material-experimental/mdc-typography/** @mmalerba
113114
/src/material-experimental/popover-edit/** @kseamon @andrewseguin
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
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-button-toggle",
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-button-toggle",
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/coercion",
34+
],
35+
)
36+
37+
sass_library(
38+
name = "mdc_button_toggle_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 = "button_toggle_scss",
49+
src = "button-toggle.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 = "button_toggle_tests_lib",
62+
srcs = [
63+
"harness/button-toggle-harness.spec.ts",
64+
],
65+
deps = [
66+
":harness",
67+
":mdc-button-toggle",
68+
"//src/cdk-experimental/testing",
69+
"//src/cdk-experimental/testing/testbed",
70+
"//src/cdk/platform",
71+
"//src/cdk/testing",
72+
"//src/material/button-toggle",
73+
"@npm//@angular/platform-browser",
74+
],
75+
)
76+
77+
ng_web_test_suite(
78+
name = "unit_tests",
79+
deps = [
80+
":button_toggle_tests_lib",
81+
"//src/material-experimental:mdc_require_config.js",
82+
],
83+
)
84+
85+
ng_e2e_test_library(
86+
name = "e2e_test_sources",
87+
srcs = glob(["**/*.e2e.spec.ts"]),
88+
deps = [
89+
"//src/cdk/testing/e2e",
90+
],
91+
)
92+
93+
e2e_test_suite(
94+
name = "e2e_tests",
95+
deps = [
96+
":e2e_test_sources",
97+
"//src/cdk/testing/e2e",
98+
],
99+
)
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-button-toggle-theme-mdc($theme) {
4+
@include mat-using-mdc-theme($theme) {
5+
// TODO: implement MDC-based button toggle.
6+
}
7+
}
8+
9+
@mixin mat-buttont-toggle-typography-mdc($config) {
10+
@include mat-using-mdc-typography($config) {
11+
// TODO: implement MDC-based button toggle.
12+
}
13+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
// TODO: copy tests from existing mat-button-toggle, update as necessary to fix.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
// TODO: implement MDC-based button toggle
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
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 ButtonToggleHarnessFilters = {
10+
label?: string | RegExp,
11+
name?: string,
12+
};
Lines changed: 187 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,187 @@
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, inject} from '@angular/core/testing';
5+
import {MatButtonToggleModule} from '@angular/material/button-toggle';
6+
import {Platform, PlatformModule} from '@angular/cdk/platform';
7+
import {MatButtonToggleModule as MatMdcButtonToggleModule} from '../index';
8+
import {MatButtonToggleHarness} from './button-toggle-harness';
9+
import {MatButtonToggleHarness as MatMdcButtonToggleHarness} from './mdc-button-toggle-harness';
10+
11+
let fixture: ComponentFixture<ButtonToggleHarnessTest>;
12+
let loader: HarnessLoader;
13+
let harness: typeof MatButtonToggleHarness;
14+
15+
describe('MatButtonToggleHarness', () => {
16+
describe('non-MDC-based', () => {
17+
beforeEach(async () => {
18+
await TestBed.configureTestingModule({
19+
imports: [MatButtonToggleModule, PlatformModule],
20+
declarations: [ButtonToggleHarnessTest],
21+
}).compileComponents();
22+
23+
fixture = TestBed.createComponent(ButtonToggleHarnessTest);
24+
fixture.detectChanges();
25+
loader = TestbedHarnessEnvironment.loader(fixture);
26+
harness = MatButtonToggleHarness;
27+
});
28+
29+
runTests();
30+
});
31+
32+
describe('MDC-based', () => {
33+
beforeEach(async () => {
34+
await TestBed.configureTestingModule({
35+
imports: [MatMdcButtonToggleModule, PlatformModule],
36+
declarations: [ButtonToggleHarnessTest],
37+
}).compileComponents();
38+
39+
fixture = TestBed.createComponent(ButtonToggleHarnessTest);
40+
fixture.detectChanges();
41+
loader = TestbedHarnessEnvironment.loader(fixture);
42+
// Public APIs are the same as MatButtonToggleHarness, but cast is
43+
// necessary because of different private fields.
44+
harness = MatMdcButtonToggleHarness as any;
45+
});
46+
47+
// TODO: enable when the MDC-based harness is done.
48+
// runTests();
49+
});
50+
});
51+
52+
/** Shared tests to run on both the original and MDC-based toggles. */
53+
function runTests() {
54+
let platform: Platform;
55+
56+
beforeEach(inject([Platform], (p: Platform) => {
57+
platform = p;
58+
}));
59+
60+
it('should load all button toggle harnesses', async () => {
61+
const toggles = await loader.getAllHarnesses(harness);
62+
expect(toggles.length).toBe(2);
63+
});
64+
65+
it('should load a button toggle with exact label', async () => {
66+
const toggles = await loader.getAllHarnesses(harness.with({label: 'First'}));
67+
expect(toggles.length).toBe(1);
68+
expect(await toggles[0].getText()).toBe('First');
69+
});
70+
71+
it('should load a button toggle with regex label match', async () => {
72+
const toggles = await loader.getAllHarnesses(harness.with({label: /^s/i}));
73+
expect(toggles.length).toBe(1);
74+
expect(await toggles[0].getText()).toBe('Second');
75+
});
76+
77+
it('should get checked state', async () => {
78+
const [checkedToggle, uncheckedToggle] = await loader.getAllHarnesses(harness);
79+
expect(await checkedToggle.isChecked()).toBe(true);
80+
expect(await uncheckedToggle.isChecked()).toBe(false);
81+
});
82+
83+
it('should get disabled state', async () => {
84+
const [enabledToggle, disabledToggle] = await loader.getAllHarnesses(harness);
85+
expect(await enabledToggle.isDisabled()).toBe(false);
86+
expect(await disabledToggle.isDisabled()).toBe(true);
87+
});
88+
89+
it('should get name', async () => {
90+
const toggle = await loader.getHarness(harness.with({label: 'First'}));
91+
expect(await toggle.getName()).toBe('first-name');
92+
});
93+
94+
it('should get aria-label', async () => {
95+
const toggle = await loader.getHarness(harness.with({label: 'First'}));
96+
expect(await toggle.getAriaLabel()).toBe('First toggle');
97+
});
98+
99+
it('should get aria-labelledby', async () => {
100+
const toggle = await loader.getHarness(harness.with({label: 'Second'}));
101+
expect(await toggle.getAriaLabelledby()).toBe('second-label');
102+
});
103+
104+
it('should get label text', async () => {
105+
const [firstToggle, secondToggle] = await loader.getAllHarnesses(harness);
106+
expect(await firstToggle.getText()).toBe('First');
107+
expect(await secondToggle.getText()).toBe('Second');
108+
});
109+
110+
it('should focus button', async () => {
111+
const toggle = await loader.getHarness(harness.with({label: 'First'}));
112+
expect(getActiveElementTagName()).not.toBe('button');
113+
await toggle.focus();
114+
expect(getActiveElementTagName()).toBe('button');
115+
});
116+
117+
it('should blur button', async () => {
118+
const toggle = await loader.getHarness(harness.with({label: 'First'}));
119+
await toggle.focus();
120+
expect(getActiveElementTagName()).toBe('button');
121+
await toggle.blur();
122+
expect(getActiveElementTagName()).not.toBe('button');
123+
});
124+
125+
it('should toggle', async () => {
126+
fixture.componentInstance.disabled = false;
127+
const [checkedToggle, uncheckedToggle] = await loader.getAllHarnesses(harness);
128+
await checkedToggle.toggle();
129+
await uncheckedToggle.toggle();
130+
expect(await checkedToggle.isChecked()).toBe(false);
131+
expect(await uncheckedToggle.isChecked()).toBe(true);
132+
});
133+
134+
it('should check toggle', async () => {
135+
fixture.componentInstance.disabled = false;
136+
const [checkedToggle, uncheckedToggle] = await loader.getAllHarnesses(harness);
137+
await checkedToggle.check();
138+
await uncheckedToggle.check();
139+
expect(await checkedToggle.isChecked()).toBe(true);
140+
expect(await uncheckedToggle.isChecked()).toBe(true);
141+
});
142+
143+
it('should uncheck toggle', async () => {
144+
fixture.componentInstance.disabled = false;
145+
const [checkedToggle, uncheckedToggle] = await loader.getAllHarnesses(harness);
146+
await checkedToggle.uncheck();
147+
await uncheckedToggle.uncheck();
148+
expect(await checkedToggle.isChecked()).toBe(false);
149+
expect(await uncheckedToggle.isChecked()).toBe(false);
150+
});
151+
152+
it('should not change disabled toggle', async () => {
153+
// Older versions of Edge have a bug where `disabled` buttons are still clickable if
154+
// they contain child elements. We skip this check on Edge.
155+
// See https://stackoverflow.com/questions/32377026/disabled-button-is-clickable-on-edge-browser
156+
if (platform.EDGE) {
157+
return;
158+
}
159+
160+
const disabledToggle = await loader.getHarness(harness.with({label: 'Second'}));
161+
expect(await disabledToggle.isChecked()).toBe(false);
162+
await disabledToggle.toggle();
163+
expect(await disabledToggle.isChecked()).toBe(false);
164+
});
165+
}
166+
167+
function getActiveElementTagName() {
168+
return document.activeElement ? document.activeElement.tagName.toLowerCase() : '';
169+
}
170+
171+
@Component({
172+
template: `
173+
<mat-button-toggle
174+
name="first-name"
175+
value="first-value"
176+
aria-label="First toggle"
177+
checked>First</mat-button-toggle>
178+
<mat-button-toggle
179+
[disabled]="disabled"
180+
aria-labelledby="second-label">Second</mat-button-toggle>
181+
<span id="second-label">Second toggle</span>
182+
`
183+
})
184+
class ButtonToggleHarnessTest {
185+
disabled = true;
186+
}
187+

0 commit comments

Comments
 (0)