mirror of
https://github.com/angular/angular.git
synced 2026-09-14 13:54:52 +08:00
feat(material-experimental/checkbox): Add test harnesses for both versions of mat-checkbox (#16334)
This commit is contained in:
@@ -94,6 +94,8 @@
|
||||
/src/material-experimental/mdc-radio/** @mmalerba
|
||||
/src/material-experimental/mdc-slide-toggle/** @crisbeto
|
||||
/src/material-experimental/mdc-tabs/** @crisbeto
|
||||
/src/material-experimental/mdc-theming/** @mmalerba
|
||||
/src/material-experimental/mdc-typography/** @mmalerba
|
||||
/src/material-experimental/popover-edit/** @kseamon @andrewseguin
|
||||
|
||||
# CDK experimental package
|
||||
|
||||
@@ -9,15 +9,9 @@ ng_module(
|
||||
["**/*.ts"],
|
||||
exclude = [
|
||||
"**/*.spec.ts",
|
||||
"tests/**",
|
||||
],
|
||||
),
|
||||
module_name = "@angular/cdk-experimental/testing",
|
||||
deps = [
|
||||
"//src/cdk/testing",
|
||||
"@npm//@angular/core",
|
||||
"@npm//protractor",
|
||||
],
|
||||
)
|
||||
|
||||
ng_web_test_suite(
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load("//tools:defaults.bzl", "ts_library")
|
||||
|
||||
ts_library(
|
||||
name = "protractor",
|
||||
srcs = glob(
|
||||
["**/*.ts"],
|
||||
exclude = ["**/*.spec.ts"],
|
||||
),
|
||||
module_name = "@angular/cdk-experimental/testing/protractor",
|
||||
deps = [
|
||||
"//src/cdk-experimental/testing",
|
||||
"@npm//protractor",
|
||||
],
|
||||
)
|
||||
@@ -6,5 +6,4 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
export * from './protractor-element';
|
||||
export * from './protractor-harness-environment';
|
||||
export * from './public-api';
|
||||
|
||||
@@ -50,4 +50,9 @@ export class ProtractorElement implements TestElement {
|
||||
async getAttribute(name: string): Promise<string|null> {
|
||||
return this.element.getAttribute(name);
|
||||
}
|
||||
|
||||
async hasClass(name: string): Promise<boolean> {
|
||||
const classes = (await this.getAttribute('class')) || '';
|
||||
return new Set(classes.split(/\s+/).filter(c => c)).has(name);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 './protractor-element';
|
||||
export * from './protractor-harness-environment';
|
||||
@@ -8,6 +8,4 @@
|
||||
|
||||
export * from './component-harness';
|
||||
export * from './harness-environment';
|
||||
export * from './protractor';
|
||||
export * from './test-element';
|
||||
export * from './testbed';
|
||||
|
||||
@@ -43,4 +43,7 @@ export interface TestElement {
|
||||
* falls back to reading the property.
|
||||
*/
|
||||
getAttribute(name: string): Promise<string | null>;
|
||||
|
||||
/** Checks whether the element has the given class. */
|
||||
hasClass(name: string): Promise<boolean>;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load("//tools:defaults.bzl", "ts_library")
|
||||
|
||||
ts_library(
|
||||
name = "testbed",
|
||||
srcs = glob(
|
||||
["**/*.ts"],
|
||||
exclude = ["**/*.spec.ts"],
|
||||
),
|
||||
module_name = "@angular/cdk-experimental/testing/testbed",
|
||||
deps = [
|
||||
"//src/cdk-experimental/testing",
|
||||
"//src/cdk/testing",
|
||||
"@npm//@angular/core",
|
||||
],
|
||||
)
|
||||
@@ -6,5 +6,4 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
export * from './testbed-harness-environment';
|
||||
export * from './unit-test-element';
|
||||
export * from './public-api';
|
||||
|
||||
@@ -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 './testbed-harness-environment';
|
||||
export * from './unit-test-element';
|
||||
@@ -86,7 +86,7 @@ export class UnitTestElement implements TestElement {
|
||||
|
||||
async text(): Promise<string> {
|
||||
await this._stabilize();
|
||||
return this.element.textContent || '';
|
||||
return (this.element.textContent || '').trim();
|
||||
}
|
||||
|
||||
async getAttribute(name: string): Promise<string|null> {
|
||||
@@ -100,4 +100,9 @@ export class UnitTestElement implements TestElement {
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
async hasClass(name: string): Promise<boolean> {
|
||||
await this._stabilize();
|
||||
return this.element.classList.contains(name);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,6 +36,7 @@ ng_test_library(
|
||||
":test_components",
|
||||
":test_harnesses",
|
||||
"//src/cdk-experimental/testing",
|
||||
"//src/cdk-experimental/testing/testbed",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -45,5 +46,6 @@ ng_e2e_test_library(
|
||||
deps = [
|
||||
":test_harnesses",
|
||||
"//src/cdk-experimental/testing",
|
||||
"//src/cdk-experimental/testing/protractor",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import {HarnessLoader} from '@angular/cdk-experimental/testing';
|
||||
import {ProtractorHarnessEnvironment} from '@angular/cdk-experimental/testing/protractor';
|
||||
import {browser} from 'protractor';
|
||||
import {HarnessLoader} from '../component-harness';
|
||||
import {ProtractorHarnessEnvironment} from '../protractor';
|
||||
import {MainComponentHarness} from './harnesses/main-component-harness';
|
||||
import {SubComponentHarness} from './harnesses/sub-component-harness';
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import {HarnessLoader} from '@angular/cdk-experimental/testing';
|
||||
import {TestbedHarnessEnvironment} from '@angular/cdk-experimental/testing/testbed';
|
||||
import {ComponentFixture, TestBed} from '@angular/core/testing';
|
||||
import {HarnessLoader} from '../component-harness';
|
||||
import {TestbedHarnessEnvironment} from '../testbed/index';
|
||||
import {MainComponentHarness} from './harnesses/main-component-harness';
|
||||
import {SubComponentHarness} from './harnesses/sub-component-harness';
|
||||
import {TestComponentsModule} from './test-components-module';
|
||||
|
||||
@@ -2,13 +2,16 @@ package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load("@io_bazel_rules_sass//:defs.bzl", "sass_binary", "sass_library")
|
||||
load("@npm_angular_bazel//:index.bzl", "protractor_web_test_suite")
|
||||
load("//tools:defaults.bzl", "ng_e2e_test_library", "ng_module", "ng_test_library", "ng_web_test_suite")
|
||||
load("//tools:defaults.bzl", "ng_e2e_test_library", "ng_module", "ng_test_library", "ng_web_test_suite", "ts_library")
|
||||
|
||||
ng_module(
|
||||
name = "mdc-checkbox",
|
||||
srcs = glob(
|
||||
["**/*.ts"],
|
||||
exclude = ["**/*.spec.ts"],
|
||||
exclude = [
|
||||
"**/*.spec.ts",
|
||||
"harness/**",
|
||||
],
|
||||
),
|
||||
assets = [":checkbox_scss"] + glob(["**/*.html"]),
|
||||
module_name = "@angular/material-experimental/mdc-checkbox",
|
||||
@@ -25,6 +28,18 @@ ng_module(
|
||||
],
|
||||
)
|
||||
|
||||
ts_library(
|
||||
name = "harness",
|
||||
srcs = glob(
|
||||
["harness/**/*.ts"],
|
||||
exclude = ["**/*.spec.ts"],
|
||||
),
|
||||
deps = [
|
||||
"//src/cdk-experimental/testing",
|
||||
"//src/cdk/coercion",
|
||||
],
|
||||
)
|
||||
|
||||
sass_library(
|
||||
name = "mdc_checkbox_scss_lib",
|
||||
srcs = glob(["**/_*.scss"]),
|
||||
@@ -55,8 +70,12 @@ ng_test_library(
|
||||
exclude = ["**/*.e2e.spec.ts"],
|
||||
),
|
||||
deps = [
|
||||
":harness",
|
||||
":mdc-checkbox",
|
||||
"//src/cdk-experimental/testing",
|
||||
"//src/cdk-experimental/testing/testbed",
|
||||
"//src/cdk/testing",
|
||||
"//src/material/checkbox",
|
||||
"@npm//@angular/forms",
|
||||
"@npm//@angular/platform-browser",
|
||||
],
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
/**
|
||||
* @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 type CheckboxHarnessFilters = {
|
||||
label?: string | RegExp
|
||||
};
|
||||
@@ -0,0 +1,202 @@
|
||||
import {HarnessLoader} from '@angular/cdk-experimental/testing';
|
||||
import {TestbedHarnessEnvironment} from '@angular/cdk-experimental/testing/testbed';
|
||||
import {Component} from '@angular/core';
|
||||
import {ComponentFixture, TestBed} from '@angular/core/testing';
|
||||
import {FormControl, ReactiveFormsModule} from '@angular/forms';
|
||||
import {MatCheckboxModule} from '@angular/material/checkbox';
|
||||
import {MatCheckboxModule as MatMdcCheckboxModule} from '../index';
|
||||
import {MatCheckboxHarness} from './checkbox-harness';
|
||||
import {MatCheckboxHarness as MatMdcCheckboxHarness} from './mdc-checkbox-harness';
|
||||
|
||||
let fixture: ComponentFixture<CheckboxHarnessTest>;
|
||||
let loader: HarnessLoader;
|
||||
let checkboxHarness: typeof MatCheckboxHarness;
|
||||
|
||||
describe('MatCheckboxHarness', () => {
|
||||
describe('non-MDC-based', () => {
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [MatCheckboxModule, ReactiveFormsModule],
|
||||
declarations: [CheckboxHarnessTest],
|
||||
}).compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(CheckboxHarnessTest);
|
||||
fixture.detectChanges();
|
||||
loader = TestbedHarnessEnvironment.loader(fixture);
|
||||
checkboxHarness = MatCheckboxHarness;
|
||||
});
|
||||
|
||||
runTests();
|
||||
});
|
||||
|
||||
describe('MDC-based', () => {
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [MatMdcCheckboxModule, ReactiveFormsModule],
|
||||
declarations: [CheckboxHarnessTest],
|
||||
}).compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(CheckboxHarnessTest);
|
||||
fixture.detectChanges();
|
||||
loader = TestbedHarnessEnvironment.loader(fixture);
|
||||
// Public APIs are the same as MatCheckboxHarness, but cast is necessary because of different
|
||||
// private fields.
|
||||
checkboxHarness = MatMdcCheckboxHarness as any;
|
||||
});
|
||||
|
||||
runTests();
|
||||
});
|
||||
});
|
||||
|
||||
/** Shared tests to run on both the original and MDC-based checkboxes. */
|
||||
function runTests() {
|
||||
it('should load all checkbox harnesses', async () => {
|
||||
const checkboxes = await loader.getAllHarnesses(checkboxHarness);
|
||||
expect(checkboxes.length).toBe(2);
|
||||
});
|
||||
|
||||
it('should load checkbox with exact label', async () => {
|
||||
const checkboxes = await loader.getAllHarnesses(checkboxHarness.with({label: 'First'}));
|
||||
expect(checkboxes.length).toBe(1);
|
||||
expect(await checkboxes[0].getLabelText()).toBe('First');
|
||||
});
|
||||
|
||||
it('should load checkbox with regex label match', async () => {
|
||||
const checkboxes = await loader.getAllHarnesses(checkboxHarness.with({label: /^s/i}));
|
||||
expect(checkboxes.length).toBe(1);
|
||||
expect(await checkboxes[0].getLabelText()).toBe('Second');
|
||||
});
|
||||
|
||||
it('should get checked state', async () => {
|
||||
const [checkedCheckbox, uncheckedCheckbox] = await loader.getAllHarnesses(checkboxHarness);
|
||||
expect(await checkedCheckbox.isChecked()).toBe(true);
|
||||
expect(await uncheckedCheckbox.isChecked()).toBe(false);
|
||||
});
|
||||
|
||||
it('should get indeterminate state', async () => {
|
||||
const [checkedCheckbox, indeterminateCheckbox] = await loader.getAllHarnesses(checkboxHarness);
|
||||
expect(await checkedCheckbox.isIndeterminate()).toBe(false);
|
||||
expect(await indeterminateCheckbox.isIndeterminate()).toBe(true);
|
||||
});
|
||||
|
||||
it('should get disabled state', async () => {
|
||||
const [enabledCheckbox, disabledCheckbox] = await loader.getAllHarnesses(checkboxHarness);
|
||||
expect(await enabledCheckbox.isDisabled()).toBe(false);
|
||||
expect(await disabledCheckbox.isDisabled()).toBe(true);
|
||||
});
|
||||
|
||||
it('should get required state', async () => {
|
||||
const [requiredCheckbox, optionalCheckbox] = await loader.getAllHarnesses(checkboxHarness);
|
||||
expect(await requiredCheckbox.isRequired()).toBe(true);
|
||||
expect(await optionalCheckbox.isRequired()).toBe(false);
|
||||
});
|
||||
|
||||
it('should get valid state', async () => {
|
||||
const [requiredCheckbox, optionalCheckbox] = await loader.getAllHarnesses(checkboxHarness);
|
||||
expect(await optionalCheckbox.isValid()).toBe(true);
|
||||
expect(await requiredCheckbox.isValid()).toBe(true);
|
||||
await requiredCheckbox.uncheck();
|
||||
expect(await requiredCheckbox.isValid()).toBe(false);
|
||||
});
|
||||
|
||||
it('should get name', async () => {
|
||||
const checkbox = await loader.getHarness(checkboxHarness.with({label: 'First'}));
|
||||
expect(await checkbox.getName()).toBe('first-name');
|
||||
});
|
||||
|
||||
it('should get value', async () => {
|
||||
const checkbox = await loader.getHarness(checkboxHarness.with({label: 'First'}));
|
||||
expect(await checkbox.getValue()).toBe('first-value');
|
||||
});
|
||||
|
||||
it('should get aria-label', async () => {
|
||||
const checkbox = await loader.getHarness(checkboxHarness.with({label: 'First'}));
|
||||
expect(await checkbox.getAriaLabel()).toBe('First checkbox');
|
||||
});
|
||||
|
||||
it('should get aria-labelledby', async () => {
|
||||
const checkbox = await loader.getHarness(checkboxHarness.with({label: 'Second'}));
|
||||
expect(await checkbox.getAriaLabelledby()).toBe('second-label');
|
||||
});
|
||||
|
||||
it('should get label text', async () => {
|
||||
const [firstCheckbox, secondCheckbox] = await loader.getAllHarnesses(checkboxHarness);
|
||||
expect(await firstCheckbox.getLabelText()).toBe('First');
|
||||
expect(await secondCheckbox.getLabelText()).toBe('Second');
|
||||
});
|
||||
|
||||
it('should focus checkbox', async () => {
|
||||
const checkbox = await loader.getHarness(checkboxHarness.with({label: 'First'}));
|
||||
expect(getActiveElementTagName()).not.toBe('input');
|
||||
await checkbox.foucs();
|
||||
expect(getActiveElementTagName()).toBe('input');
|
||||
});
|
||||
|
||||
it('should blur checkbox', async () => {
|
||||
const checkbox = await loader.getHarness(checkboxHarness.with({label: 'First'}));
|
||||
await checkbox.foucs();
|
||||
expect(getActiveElementTagName()).toBe('input');
|
||||
await checkbox.blur();
|
||||
expect(getActiveElementTagName()).not.toBe('input');
|
||||
});
|
||||
|
||||
it('should toggle checkbox', async () => {
|
||||
fixture.componentInstance.disabled = false;
|
||||
const [checkedCheckbox, uncheckedCheckbox] = await loader.getAllHarnesses(checkboxHarness);
|
||||
await checkedCheckbox.toggle();
|
||||
await uncheckedCheckbox.toggle();
|
||||
expect(await checkedCheckbox.isChecked()).toBe(false);
|
||||
expect(await uncheckedCheckbox.isChecked()).toBe(true);
|
||||
});
|
||||
|
||||
it('should check checkbox', async () => {
|
||||
fixture.componentInstance.disabled = false;
|
||||
const [checkedCheckbox, uncheckedCheckbox] = await loader.getAllHarnesses(checkboxHarness);
|
||||
await checkedCheckbox.check();
|
||||
await uncheckedCheckbox.check();
|
||||
expect(await checkedCheckbox.isChecked()).toBe(true);
|
||||
expect(await uncheckedCheckbox.isChecked()).toBe(true);
|
||||
});
|
||||
|
||||
it('should uncheck checkbox', async () => {
|
||||
fixture.componentInstance.disabled = false;
|
||||
const [checkedCheckbox, uncheckedCheckbox] = await loader.getAllHarnesses(checkboxHarness);
|
||||
await checkedCheckbox.uncheck();
|
||||
await uncheckedCheckbox.uncheck();
|
||||
expect(await checkedCheckbox.isChecked()).toBe(false);
|
||||
expect(await uncheckedCheckbox.isChecked()).toBe(false);
|
||||
});
|
||||
|
||||
it('should not toggle disabled checkbox', async () => {
|
||||
const disabledCheckbox = await loader.getHarness(checkboxHarness.with({label: 'Second'}));
|
||||
expect(await disabledCheckbox.isChecked()).toBe(false);
|
||||
await disabledCheckbox.toggle();
|
||||
expect(await disabledCheckbox.isChecked()).toBe(false);
|
||||
});
|
||||
}
|
||||
|
||||
function getActiveElementTagName() {
|
||||
return document.activeElement ? document.activeElement.tagName.toLowerCase() : '';
|
||||
}
|
||||
|
||||
@Component({
|
||||
template: `
|
||||
<mat-checkbox
|
||||
[formControl]="ctrl"
|
||||
required
|
||||
name="first-name"
|
||||
value="first-value"
|
||||
aria-label="First checkbox">
|
||||
First
|
||||
</mat-checkbox>
|
||||
<mat-checkbox indeterminate="true" [disabled]="disabled" aria-labelledby="second-label">
|
||||
Second
|
||||
</mat-checkbox>
|
||||
<span id="second-label">Second checkbox</span>
|
||||
`
|
||||
})
|
||||
class CheckboxHarnessTest {
|
||||
ctrl = new FormControl(true);
|
||||
disabled = true;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,142 @@
|
||||
/**
|
||||
* @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 {ComponentHarness, HarnessPredicate} from '@angular/cdk-experimental/testing';
|
||||
import {coerceBooleanProperty} from '@angular/cdk/coercion';
|
||||
import {CheckboxHarnessFilters} from './checkbox-harness-filters';
|
||||
|
||||
/**
|
||||
* Harness for interacting with a standard mat-checkbox in tests.
|
||||
* @dynamic
|
||||
*/
|
||||
export class MatCheckboxHarness extends ComponentHarness {
|
||||
static hostSelector = 'mat-checkbox';
|
||||
|
||||
/**
|
||||
* Gets a `HarnessPredicate` that can be used to search for a checkbox with specific attributes.
|
||||
* @param options Options for narrowing the search:
|
||||
* - `label` finds a checkbox with specific label text.
|
||||
* @return a `HarnessPredicate` configured with the given options.
|
||||
*/
|
||||
static with(options: CheckboxHarnessFilters = {}): HarnessPredicate<MatCheckboxHarness> {
|
||||
return new HarnessPredicate(MatCheckboxHarness)
|
||||
.addOption('label', options.label,
|
||||
(harness, label) => HarnessPredicate.stringMatches(harness.getLabelText(), label));
|
||||
}
|
||||
|
||||
private _label = this.locatorFor('.mat-checkbox-label');
|
||||
private _input = this.locatorFor('input');
|
||||
private _inputContainer = this.locatorFor('.mat-checkbox-inner-container');
|
||||
|
||||
/** Gets a boolean promise indicating if the checkbox is checked. */
|
||||
async isChecked(): Promise<boolean> {
|
||||
const checked = (await this._input()).getAttribute('checked');
|
||||
return coerceBooleanProperty(await checked);
|
||||
}
|
||||
|
||||
/** Gets a boolean promise indicating if the checkbox is in an indeterminate state. */
|
||||
async isIndeterminate(): Promise<boolean> {
|
||||
const indeterminate = (await this._input()).getAttribute('indeterminate');
|
||||
return coerceBooleanProperty(await indeterminate);
|
||||
}
|
||||
|
||||
/** Gets a boolean promise indicating if the checkbox is disabled. */
|
||||
async isDisabled(): Promise<boolean> {
|
||||
const disabled = (await this._input()).getAttribute('disabled');
|
||||
return coerceBooleanProperty(await disabled);
|
||||
}
|
||||
|
||||
/** Gets a boolean promise indicating if the checkbox is required. */
|
||||
async isRequired(): Promise<boolean> {
|
||||
const required = (await this._input()).getAttribute('required');
|
||||
return coerceBooleanProperty(await required);
|
||||
}
|
||||
|
||||
/** Gets a boolean promise indicating if the checkbox is valid. */
|
||||
async isValid(): Promise<boolean> {
|
||||
const invalid = (await this.host()).hasClass('ng-invalid');
|
||||
return !(await invalid);
|
||||
}
|
||||
|
||||
/** Gets a promise for the checkbox's name. */
|
||||
async getName(): Promise<string | null> {
|
||||
return (await this._input()).getAttribute('name');
|
||||
}
|
||||
|
||||
/** Gets a promise for the checkbox's value. */
|
||||
async getValue(): Promise<string | null> {
|
||||
return (await this._input()).getAttribute('value');
|
||||
}
|
||||
|
||||
/** Gets a promise for the checkbox's aria-label. */
|
||||
async getAriaLabel(): Promise<string | null> {
|
||||
return (await this._input()).getAttribute('aria-label');
|
||||
}
|
||||
|
||||
/** Gets a promise for the checkbox's aria-labelledby. */
|
||||
async getAriaLabelledby(): Promise<string | null> {
|
||||
return (await this._input()).getAttribute('aria-labelledby');
|
||||
}
|
||||
|
||||
/** Gets a promise for the checkbox's label text. */
|
||||
async getLabelText(): Promise<string> {
|
||||
return (await this._label()).text();
|
||||
}
|
||||
|
||||
/** Focuses the checkbox and returns a void promise that indicates when the action is complete. */
|
||||
async foucs(): Promise<void> {
|
||||
return (await this._input()).focus();
|
||||
}
|
||||
|
||||
/** Blurs the checkbox and returns a void promise that indicates when the action is complete. */
|
||||
async blur(): Promise<void> {
|
||||
return (await this._input()).blur();
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggle the checked state of the checkbox and returns a void promise that indicates when the
|
||||
* action is complete.
|
||||
*
|
||||
* Note: This attempts to toggle the checkbox as a user would, by clicking it. Therefore if you
|
||||
* are using `MAT_CHECKBOX_CLICK_ACTION` to change the behavior on click, calling this method
|
||||
* might not have the expected result.
|
||||
*/
|
||||
async toggle(): Promise<void> {
|
||||
return (await this._inputContainer()).click();
|
||||
}
|
||||
|
||||
/**
|
||||
* Puts the checkbox in a checked state by toggling it if it is currently unchecked, or doing
|
||||
* nothing if it is already checked. Returns a void promise that indicates when the action is
|
||||
* complete.
|
||||
*
|
||||
* Note: This attempts to check the checkbox as a user would, by clicking it. Therefore if you
|
||||
* are using `MAT_CHECKBOX_CLICK_ACTION` to change the behavior on click, calling this method
|
||||
* might not have the expected result.
|
||||
*/
|
||||
async check(): Promise<void> {
|
||||
if (!(await this.isChecked())) {
|
||||
await this.toggle();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Puts the checkbox in an unchecked state by toggling it if it is currently checked, or doing
|
||||
* nothing if it is already unchecked. Returns a void promise that indicates when the action is
|
||||
* complete.
|
||||
*
|
||||
* Note: This attempts to uncheck the checkbox as a user would, by clicking it. Therefore if you
|
||||
* are using `MAT_CHECKBOX_CLICK_ACTION` to change the behavior on click, calling this method
|
||||
* might not have the expected result.
|
||||
*/
|
||||
async uncheck(): Promise<void> {
|
||||
if (await this.isChecked()) {
|
||||
await this.toggle();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,143 @@
|
||||
/**
|
||||
* @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 {ComponentHarness, HarnessPredicate} from '@angular/cdk-experimental/testing';
|
||||
import {coerceBooleanProperty} from '@angular/cdk/coercion';
|
||||
import {CheckboxHarnessFilters} from './checkbox-harness-filters';
|
||||
|
||||
/**
|
||||
* Harness for interacting with a MDC-based mat-checkbox in tests.
|
||||
* @dynamic
|
||||
*/
|
||||
export class MatCheckboxHarness extends ComponentHarness {
|
||||
static hostSelector = 'mat-checkbox';
|
||||
|
||||
/**
|
||||
* Gets a `HarnessPredicate` that can be used to search for a checkbox with specific attributes.
|
||||
* @param options Options for narrowing the search:
|
||||
* - `label` finds a checkbox with specific label text.
|
||||
* @return a `HarnessPredicate` configured with the given options.
|
||||
*/
|
||||
static with(options: CheckboxHarnessFilters = {}): HarnessPredicate<MatCheckboxHarness> {
|
||||
return new HarnessPredicate(MatCheckboxHarness)
|
||||
.addOption('label', options.label,
|
||||
(harness, label) => HarnessPredicate.stringMatches(harness.getLabelText(), label));
|
||||
}
|
||||
|
||||
private _label = this.locatorFor('label');
|
||||
private _input = this.locatorFor('input');
|
||||
private _inputContainer = this.locatorFor('.mdc-checkbox');
|
||||
|
||||
/** Gets a boolean promise indicating if the checkbox is checked. */
|
||||
async isChecked(): Promise<boolean> {
|
||||
const checked = (await this._input()).getAttribute('checked');
|
||||
return coerceBooleanProperty(await checked);
|
||||
}
|
||||
|
||||
/** Gets a boolean promise indicating if the checkbox is in an indeterminate state. */
|
||||
async isIndeterminate(): Promise<boolean> {
|
||||
const indeterminate = (await this._input()).getAttribute('indeterminate');
|
||||
return coerceBooleanProperty(await indeterminate);
|
||||
}
|
||||
|
||||
/** Gets a boolean promise indicating if the checkbox is disabled. */
|
||||
async isDisabled(): Promise<boolean> {
|
||||
const disabled = (await this._input()).getAttribute('disabled');
|
||||
return coerceBooleanProperty(await disabled);
|
||||
}
|
||||
|
||||
/** Gets a boolean promise indicating if the checkbox is required. */
|
||||
async isRequired(): Promise<boolean> {
|
||||
const required = (await this._input()).getAttribute('required');
|
||||
return coerceBooleanProperty(await required);
|
||||
}
|
||||
|
||||
/** Gets a boolean promise indicating if the checkbox is valid. */
|
||||
async isValid(): Promise<boolean> {
|
||||
const invalid = (await this.host()).hasClass('ng-invalid');
|
||||
return !(await invalid);
|
||||
}
|
||||
|
||||
/** Gets a promise for the checkbox's name. */
|
||||
async getName(): Promise<string | null> {
|
||||
return (await this._input()).getAttribute('name');
|
||||
}
|
||||
|
||||
/** Gets a promise for the checkbox's value. */
|
||||
async getValue(): Promise<string | null> {
|
||||
return (await this._input()).getAttribute('value');
|
||||
}
|
||||
|
||||
/** Gets a promise for the checkbox's aria-label. */
|
||||
async getAriaLabel(): Promise<string | null> {
|
||||
return (await this._input()).getAttribute('aria-label');
|
||||
}
|
||||
|
||||
/** Gets a promise for the checkbox's aria-labelledby. */
|
||||
async getAriaLabelledby(): Promise<string | null> {
|
||||
return (await this._input()).getAttribute('aria-labelledby');
|
||||
}
|
||||
|
||||
/** Gets a promise for the checkbox's label text. */
|
||||
async getLabelText(): Promise<string> {
|
||||
return (await this._label()).text();
|
||||
}
|
||||
|
||||
/** Focuses the checkbox and returns a void promise that indicates when the action is complete. */
|
||||
async foucs(): Promise<void> {
|
||||
return (await this._input()).focus();
|
||||
}
|
||||
|
||||
/** Blurs the checkbox and returns a void promise that indicates when the action is complete. */
|
||||
async blur(): Promise<void> {
|
||||
return (await this._input()).blur();
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggle the checked state of the checkbox and returns a void promise that indicates when the
|
||||
* action is complete.
|
||||
*
|
||||
* Note: This attempts to toggle the checkbox as a user would, by clicking it. Therefore if you
|
||||
* are using `MAT_CHECKBOX_CLICK_ACTION` to change the behavior on click, calling this method
|
||||
* might not have the expected result.
|
||||
*/
|
||||
async toggle(): Promise<void> {
|
||||
const elToClick = await this.isDisabled() ? this._inputContainer() : this._input();
|
||||
return (await elToClick).click();
|
||||
}
|
||||
|
||||
/**
|
||||
* Puts the checkbox in a checked state by toggling it if it is currently unchecked, or doing
|
||||
* nothing if it is already checked. Returns a void promise that indicates when the action is
|
||||
* complete.
|
||||
*
|
||||
* Note: This attempts to check the checkbox as a user would, by clicking it. Therefore if you
|
||||
* are using `MAT_CHECKBOX_CLICK_ACTION` to change the behavior on click, calling this method
|
||||
* might not have the expected result.
|
||||
*/
|
||||
async check(): Promise<void> {
|
||||
if (!(await this.isChecked())) {
|
||||
await this.toggle();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Puts the checkbox in an unchecked state by toggling it if it is currently checked, or doing
|
||||
* nothing if it is already unchecked. Returns a void promise that indicates when the action is
|
||||
* complete.
|
||||
*
|
||||
* Note: This attempts to uncheck the checkbox as a user would, by clicking it. Therefore if you
|
||||
* are using `MAT_CHECKBOX_CLICK_ACTION` to change the behavior on click, calling this method
|
||||
* might not have the expected result.
|
||||
*/
|
||||
async uncheck(): Promise<void> {
|
||||
if (await this.isChecked()) {
|
||||
await this.toggle();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load("@io_bazel_rules_sass//:defs.bzl", "sass_binary", "sass_library")
|
||||
|
||||
sass_library(
|
||||
name = "all_themes",
|
||||
srcs = [
|
||||
"_all-theme.scss",
|
||||
],
|
||||
deps = [
|
||||
"//src/material-experimental/mdc-button:mdc_button_scss_lib",
|
||||
"//src/material-experimental/mdc-card:mdc_card_scss_lib",
|
||||
"//src/material-experimental/mdc-checkbox:mdc_checkbox_scss_lib",
|
||||
"//src/material-experimental/mdc-chips:mdc_chips_scss_lib",
|
||||
"//src/material-experimental/mdc-menu:mdc_menu_scss_lib",
|
||||
"//src/material-experimental/mdc-radio:mdc_radio_scss_lib",
|
||||
"//src/material-experimental/mdc-slide-toggle:mdc_slide_toggle_scss_lib",
|
||||
"//src/material-experimental/mdc-tabs:mdc_tabs_scss_lib",
|
||||
],
|
||||
)
|
||||
|
||||
sass_binary(
|
||||
name = "indigo_pink_prebuilt",
|
||||
src = "prebuilt/indigo-pink.scss",
|
||||
include_paths = [
|
||||
"external/npm/node_modules",
|
||||
],
|
||||
deps = [
|
||||
":all_themes",
|
||||
"//src/material-experimental/mdc-typography:all_typography",
|
||||
],
|
||||
)
|
||||
@@ -0,0 +1,21 @@
|
||||
@import '../mdc-button/mdc-button';
|
||||
@import '../mdc-card/mdc-card';
|
||||
@import '../mdc-checkbox/mdc-checkbox';
|
||||
@import '../mdc-chips/mdc-chips';
|
||||
@import '../mdc-menu/mdc-menu';
|
||||
@import '../mdc-radio/mdc-radio';
|
||||
@import '../mdc-slide-toggle/mdc-slide-toggle';
|
||||
@import '../mdc-tabs/mdc-tabs';
|
||||
|
||||
@mixin angular-material-theme-mdc($theme) {
|
||||
@include mat-button-theme-mdc($theme);
|
||||
@include mat-fab-theme-mdc($theme);
|
||||
@include mat-icon-button-theme-mdc($theme);
|
||||
@include mat-card-theme-mdc($theme);
|
||||
@include mat-checkbox-theme-mdc($theme);
|
||||
@include mat-chips-theme-mdc($theme);
|
||||
@include mat-menu-theme-mdc($theme);
|
||||
@include mat-radio-theme-mdc($theme);
|
||||
@include mat-slide-toggle-theme-mdc($theme);
|
||||
@include mat-tabs-theme-mdc($theme);
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
@import '../all-theme';
|
||||
@import '../../mdc-typography/all-typography';
|
||||
|
||||
// Define a theme.
|
||||
$primary: mat-palette($mat-indigo);
|
||||
$accent: mat-palette($mat-pink, A200, A100, A400);
|
||||
|
||||
$theme: mat-light-theme($primary, $accent);
|
||||
|
||||
// Include all theme styles for the components.
|
||||
@include angular-material-theme-mdc($theme);
|
||||
@include angular-material-typography-mdc();
|
||||
@@ -0,0 +1,20 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load("@io_bazel_rules_sass//:defs.bzl", "sass_library")
|
||||
|
||||
sass_library(
|
||||
name = "all_typography",
|
||||
srcs = [
|
||||
"_all-typography.scss",
|
||||
],
|
||||
deps = [
|
||||
"//src/material-experimental/mdc-button:mdc_button_scss_lib",
|
||||
"//src/material-experimental/mdc-card:mdc_card_scss_lib",
|
||||
"//src/material-experimental/mdc-checkbox:mdc_checkbox_scss_lib",
|
||||
"//src/material-experimental/mdc-chips:mdc_chips_scss_lib",
|
||||
"//src/material-experimental/mdc-menu:mdc_menu_scss_lib",
|
||||
"//src/material-experimental/mdc-radio:mdc_radio_scss_lib",
|
||||
"//src/material-experimental/mdc-slide-toggle:mdc_slide_toggle_scss_lib",
|
||||
"//src/material-experimental/mdc-tabs:mdc_tabs_scss_lib",
|
||||
],
|
||||
)
|
||||
@@ -0,0 +1,21 @@
|
||||
@import '../mdc-button/mdc-button';
|
||||
@import '../mdc-card/mdc-card';
|
||||
@import '../mdc-checkbox/mdc-checkbox';
|
||||
@import '../mdc-chips/mdc-chips';
|
||||
@import '../mdc-menu/mdc-menu';
|
||||
@import '../mdc-radio/mdc-radio';
|
||||
@import '../mdc-slide-toggle/mdc-slide-toggle';
|
||||
@import '../mdc-tabs/mdc-tabs';
|
||||
|
||||
@mixin angular-material-typography-mdc($config: null) {
|
||||
@include mat-button-typography-mdc($config);
|
||||
@include mat-fab-typography-mdc($config);
|
||||
@include mat-icon-button-typography-mdc($config);
|
||||
@include mat-card-typography-mdc($config);
|
||||
@include mat-checkbox-typography-mdc($config);
|
||||
@include mat-chips-typography-mdc($config);
|
||||
@include mat-menu-typography-mdc($config);
|
||||
@include mat-radio-typography-mdc($config);
|
||||
@include mat-slide-toggle-typography-mdc($config);
|
||||
@include mat-tabs-typography-mdc($config);
|
||||
}
|
||||
@@ -93,6 +93,8 @@ System.config({
|
||||
'@angular/cdk-experimental/popover-edit': 'dist/packages/cdk-experimental/popover-edit/index.js',
|
||||
'@angular/cdk-experimental/scrolling': 'dist/packages/cdk-experimental/scrolling/index.js',
|
||||
'@angular/cdk-experimental/testing': 'dist/packages/cdk-experimental/testing/index.js',
|
||||
'@angular/cdk-experimental/testing/testbed': 'dist/packages/cdk-experimental/testing/testbed/index.js',
|
||||
'@angular/cdk-experimental/testing/protractor': 'dist/packages/cdk-experimental/testing/protractor/index.js',
|
||||
|
||||
'@angular/material/autocomplete': 'dist/packages/material/autocomplete/index.js',
|
||||
'@angular/material/badge': 'dist/packages/material/badge/index.js',
|
||||
|
||||
+4
-1
@@ -127,7 +127,10 @@ def ng_web_test_suite(deps = [], static_css = [], bootstrap = [], **kwargs):
|
||||
# that is needed for measuring, will unexpectedly fail. Also always adding a prebuilt theme
|
||||
# reduces the amount of setup that is needed to create a test suite Bazel target. Note that the
|
||||
# prebuilt theme will be also added to CDK test suites but shouldn't affect anything.
|
||||
static_css = static_css + ["//src/material/prebuilt-themes:indigo-pink"]
|
||||
static_css = static_css + [
|
||||
"//src/material/prebuilt-themes:indigo-pink",
|
||||
"//src/material-experimental/mdc-theming:indigo_pink_prebuilt",
|
||||
]
|
||||
|
||||
# Workaround for https://github.com/bazelbuild/rules_typescript/issues/301
|
||||
# Since some of our tests depend on CSS files which are not part of the `ng_module` rule,
|
||||
|
||||
Reference in New Issue
Block a user