From 4a61a64ca7b3a44428f6ca797e849b29106a561e Mon Sep 17 00:00:00 2001 From: Miles Malerba Date: Tue, 18 Jun 2019 15:30:35 -0700 Subject: [PATCH 1/8] feat(material-experimental/checkbox): Add test harnesses for both version of mat-checkbox --- .../testing/protractor/protractor-element.ts | 5 + src/cdk-experimental/testing/test-element.ts | 3 + .../testing/testbed/unit-test-element.ts | 5 + .../mdc-checkbox/BUILD.bazel | 2 +- .../harnes/mat-checkbox-harness.ts | 92 +++++++++++++++++++ .../harnes/mat-mdc-checkbox-harness.ts | 92 +++++++++++++++++++ 6 files changed, 198 insertions(+), 1 deletion(-) create mode 100644 src/material-experimental/mdc-checkbox/harnes/mat-checkbox-harness.ts create mode 100644 src/material-experimental/mdc-checkbox/harnes/mat-mdc-checkbox-harness.ts diff --git a/src/cdk-experimental/testing/protractor/protractor-element.ts b/src/cdk-experimental/testing/protractor/protractor-element.ts index 1a89bde62bcb..92aed550030b 100644 --- a/src/cdk-experimental/testing/protractor/protractor-element.ts +++ b/src/cdk-experimental/testing/protractor/protractor-element.ts @@ -50,4 +50,9 @@ export class ProtractorElement implements TestElement { async getAttribute(name: string): Promise { return this.element.getAttribute(name); } + + async hasClass(name: string): Promise { + const classes = (await this.getAttribute('class')) || ''; + return new Set(classes.split(/\s+/).filter(c => c)).has(name); + } } diff --git a/src/cdk-experimental/testing/test-element.ts b/src/cdk-experimental/testing/test-element.ts index 1c6ae8de55cb..9c9abf780df0 100644 --- a/src/cdk-experimental/testing/test-element.ts +++ b/src/cdk-experimental/testing/test-element.ts @@ -43,4 +43,7 @@ export interface TestElement { * falls back to reading the property. */ getAttribute(name: string): Promise; + + /** Checks whether the element has the given class. */ + hasClass(name: string): Promise; } diff --git a/src/cdk-experimental/testing/testbed/unit-test-element.ts b/src/cdk-experimental/testing/testbed/unit-test-element.ts index b778ef307d0e..562ad98b581a 100644 --- a/src/cdk-experimental/testing/testbed/unit-test-element.ts +++ b/src/cdk-experimental/testing/testbed/unit-test-element.ts @@ -100,4 +100,9 @@ export class UnitTestElement implements TestElement { } return value; } + + async hasClass(name: string): Promise { + await this._stabilize(); + return this.element.classList.contains(name); + } } diff --git a/src/material-experimental/mdc-checkbox/BUILD.bazel b/src/material-experimental/mdc-checkbox/BUILD.bazel index 679220dbdaeb..cc7c548a59d0 100644 --- a/src/material-experimental/mdc-checkbox/BUILD.bazel +++ b/src/material-experimental/mdc-checkbox/BUILD.bazel @@ -8,7 +8,7 @@ ng_module( name = "mdc-checkbox", srcs = glob( ["**/*.ts"], - exclude = ["**/*.spec.ts"], + exclude = ["**/*.spec.ts", "harnesses/**"], ), assets = [":checkbox_scss"] + glob(["**/*.html"]), module_name = "@angular/material-experimental/mdc-checkbox", diff --git a/src/material-experimental/mdc-checkbox/harnes/mat-checkbox-harness.ts b/src/material-experimental/mdc-checkbox/harnes/mat-checkbox-harness.ts new file mode 100644 index 000000000000..59024ed0151c --- /dev/null +++ b/src/material-experimental/mdc-checkbox/harnes/mat-checkbox-harness.ts @@ -0,0 +1,92 @@ +/** + * @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'; + +export class MatCheckboxHarness extends ComponentHarness { + static hostSelector = 'mat-checkbox'; + + static with(options: {label: string | RegExp}): HarnessPredicate { + 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'); + + async isChecked(): Promise { + const checked = (await this._input()).getAttribute('checked'); + return coerceBooleanProperty(await checked); + } + + async isIndeterminate(): Promise { + const indeterminate = (await this._input()).getAttribute('indeterminate'); + return coerceBooleanProperty(await indeterminate); + } + + async isDisabled(): Promise { + const disabled = (await this._input()).getAttribute('disabled'); + return coerceBooleanProperty(await disabled); + } + + async isRequired(): Promise { + const required = (await this._input()).getAttribute('required'); + return coerceBooleanProperty(await required); + } + + async isValid(): Promise { + const invalid = (await this.host()).hasClass('ng-invalid'); + return !(await invalid); + } + + async getName(): Promise { + return (await this._input()).getAttribute('name'); + } + + async getValue(): Promise { + return (await this._input()).getAttribute('value'); + } + + async getAriaLabel(): Promise { + return (await this._input()).getAttribute('aria-label'); + } + + async getAriaLabelledby(): Promise { + return (await this._input()).getAttribute('aria-labelledby'); + } + + async getLabelText(): Promise { + return (await this._label()).text(); + } + + async foucs(): Promise { + return (await this._input()).focus(); + } + + async blur(): Promise { + return (await this._input()).blur(); + } + + async toggle(): Promise { + return (await this._input()).click(); + } + + async check(): Promise { + if (!(await this.isChecked())) { + await this.toggle(); + } + } + + async uncheck(): Promise { + if (await this.isChecked()) { + await this.toggle(); + } + } +} diff --git a/src/material-experimental/mdc-checkbox/harnes/mat-mdc-checkbox-harness.ts b/src/material-experimental/mdc-checkbox/harnes/mat-mdc-checkbox-harness.ts new file mode 100644 index 000000000000..9ea51eabe262 --- /dev/null +++ b/src/material-experimental/mdc-checkbox/harnes/mat-mdc-checkbox-harness.ts @@ -0,0 +1,92 @@ +/** + * @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'; + +export class MatCheckboxHarness extends ComponentHarness { + static hostSelector = 'mat-checkbox'; + + static with(options: {label: string | RegExp}): HarnessPredicate { + 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'); + + async isChecked(): Promise { + const checked = (await this._input()).getAttribute('checked'); + return coerceBooleanProperty(await checked); + } + + async isIndeterminate(): Promise { + const indeterminate = (await this._input()).getAttribute('indeterminate'); + return coerceBooleanProperty(await indeterminate); + } + + async isDisabled(): Promise { + const disabled = (await this._input()).getAttribute('disabled'); + return coerceBooleanProperty(await disabled); + } + + async isRequired(): Promise { + const required = (await this._input()).getAttribute('required'); + return coerceBooleanProperty(await required); + } + + async isValid(): Promise { + const invalid = (await this.host()).hasClass('ng-invalid'); + return !(await invalid); + } + + async getName(): Promise { + return (await this._input()).getAttribute('name'); + } + + async getValue(): Promise { + return (await this._input()).getAttribute('value'); + } + + async getAriaLabel(): Promise { + return (await this._input()).getAttribute('aria-label'); + } + + async getAriaLabelledby(): Promise { + return (await this._input()).getAttribute('aria-labelledby'); + } + + async getLabelText(): Promise { + return (await this._label()).text(); + } + + async foucs(): Promise { + return (await this._input()).focus(); + } + + async blur(): Promise { + return (await this._input()).blur(); + } + + async toggle(): Promise { + return (await this._input()).click(); + } + + async check(): Promise { + if (!(await this.isChecked())) { + await this.toggle(); + } + } + + async uncheck(): Promise { + if (await this.isChecked()) { + await this.toggle(); + } + } +} From 1131d8ccfa516b94c2515d6d25824f6bfed79a68 Mon Sep 17 00:00:00 2001 From: Miles Malerba Date: Tue, 18 Jun 2019 16:30:01 -0700 Subject: [PATCH 2/8] add tests --- src/cdk-experimental/testing/BUILD.bazel | 2 +- src/cdk-experimental/testing/public-api.ts | 2 - .../testing/testbed/unit-test-element.ts | 2 +- .../mdc-checkbox/BUILD.bazel | 16 +- .../harness/checkbox-harness.spec.ts | 191 ++++++++++++++++++ .../checkbox-harness.ts} | 4 + .../mdc-checkbox-harness.ts} | 4 + test/karma-system-config.js | 2 + 8 files changed, 217 insertions(+), 6 deletions(-) create mode 100644 src/material-experimental/mdc-checkbox/harness/checkbox-harness.spec.ts rename src/material-experimental/mdc-checkbox/{harnes/mat-checkbox-harness.ts => harness/checkbox-harness.ts} (96%) rename src/material-experimental/mdc-checkbox/{harnes/mat-mdc-checkbox-harness.ts => harness/mdc-checkbox-harness.ts} (96%) diff --git a/src/cdk-experimental/testing/BUILD.bazel b/src/cdk-experimental/testing/BUILD.bazel index 7727dd4d60d7..b4d6e55ce92e 100644 --- a/src/cdk-experimental/testing/BUILD.bazel +++ b/src/cdk-experimental/testing/BUILD.bazel @@ -1,6 +1,6 @@ package(default_visibility = ["//visibility:public"]) -load("//tools:defaults.bzl", "ng_module", "ng_web_test_suite") +load("//tools:defaults.bzl", "ng_module", "ng_web_test_suite", "ts_library") load("@npm_angular_bazel//:index.bzl", "protractor_web_test_suite") ng_module( diff --git a/src/cdk-experimental/testing/public-api.ts b/src/cdk-experimental/testing/public-api.ts index d90b5ede9b41..5c8698a549e6 100644 --- a/src/cdk-experimental/testing/public-api.ts +++ b/src/cdk-experimental/testing/public-api.ts @@ -8,6 +8,4 @@ export * from './component-harness'; export * from './harness-environment'; -export * from './protractor'; export * from './test-element'; -export * from './testbed'; diff --git a/src/cdk-experimental/testing/testbed/unit-test-element.ts b/src/cdk-experimental/testing/testbed/unit-test-element.ts index 562ad98b581a..2ccc56d0f5be 100644 --- a/src/cdk-experimental/testing/testbed/unit-test-element.ts +++ b/src/cdk-experimental/testing/testbed/unit-test-element.ts @@ -86,7 +86,7 @@ export class UnitTestElement implements TestElement { async text(): Promise { await this._stabilize(); - return this.element.textContent || ''; + return (this.element.textContent || '').trim(); } async getAttribute(name: string): Promise { diff --git a/src/material-experimental/mdc-checkbox/BUILD.bazel b/src/material-experimental/mdc-checkbox/BUILD.bazel index cc7c548a59d0..9d97578d3142 100644 --- a/src/material-experimental/mdc-checkbox/BUILD.bazel +++ b/src/material-experimental/mdc-checkbox/BUILD.bazel @@ -2,13 +2,13 @@ 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", "harnesses/**"], + exclude = ["**/*.spec.ts", "harness/**"], ), assets = [":checkbox_scss"] + glob(["**/*.html"]), module_name = "@angular/material-experimental/mdc-checkbox", @@ -25,6 +25,15 @@ ng_module( ], ) +ts_library( + name = "harness", + srcs = glob(["harness/**/*.ts"], exclude = ["**/*.spec.ts"]), + deps = [ + "//src/cdk/coercion", + "//src/cdk-experimental/testing", + ] +) + sass_library( name = "mdc_checkbox_scss_lib", srcs = glob(["**/_*.scss"]), @@ -55,8 +64,11 @@ ng_test_library( exclude = ["**/*.e2e.spec.ts"], ), deps = [ + ":harness", ":mdc-checkbox", "//src/cdk/testing", + "//src/cdk-experimental/testing", + "//src/material/checkbox", "@npm//@angular/forms", "@npm//@angular/platform-browser", ], diff --git a/src/material-experimental/mdc-checkbox/harness/checkbox-harness.spec.ts b/src/material-experimental/mdc-checkbox/harness/checkbox-harness.spec.ts new file mode 100644 index 000000000000..3a4367f99077 --- /dev/null +++ b/src/material-experimental/mdc-checkbox/harness/checkbox-harness.spec.ts @@ -0,0 +1,191 @@ +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; +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 checkboxes = await loader.getAllHarnesses(checkboxHarness); + expect(await checkboxes[0].isChecked()).toBe(true); + expect(await checkboxes[1].isChecked()).toBe(false); + }); + + it('should get indeterminate state', async () => { + const checkboxes = await loader.getAllHarnesses(checkboxHarness); + expect(await checkboxes[0].isIndeterminate()).toBe(false); + expect(await checkboxes[1].isIndeterminate()).toBe(true); + }); + + it('should get disabled state', async () => { + const checkboxes = await loader.getAllHarnesses(checkboxHarness); + expect(await checkboxes[0].isDisabled()).toBe(false); + expect(await checkboxes[1].isDisabled()).toBe(true); + }); + + it('should get required state', async () => { + const checkboxes = await loader.getAllHarnesses(checkboxHarness); + expect(await checkboxes[0].isRequired()).toBe(true); + expect(await checkboxes[1].isRequired()).toBe(false); + }); + + it('should get valid state', async () => { + const checkboxes = await loader.getAllHarnesses(checkboxHarness); + expect(await checkboxes[0].isValid()).toBe(true); + expect(await checkboxes[1].isValid()).toBe(true); + await checkboxes[0].uncheck(); + expect(await checkboxes[0].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 checkboxes = await loader.getAllHarnesses(checkboxHarness); + expect(await checkboxes[0].getLabelText()).toBe('First'); + expect(await checkboxes[1].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 () => { + const checkboxes = await loader.getAllHarnesses(checkboxHarness); + await checkboxes[0].toggle(); + await checkboxes[1].toggle(); + expect(await checkboxes[0].isChecked()).toBe(false); + expect(await checkboxes[1].isChecked()).toBe(true); + }); + + it('should check checkbox', async () => { + const checkboxes = await loader.getAllHarnesses(checkboxHarness); + await checkboxes[0].check(); + await checkboxes[1].check(); + expect(await checkboxes[0].isChecked()).toBe(true); + expect(await checkboxes[1].isChecked()).toBe(true); + }); + + it('should uncheck checkbox', async () => { + const checkboxes = await loader.getAllHarnesses(checkboxHarness); + await checkboxes[0].uncheck(); + await checkboxes[1].uncheck(); + expect(await checkboxes[0].isChecked()).toBe(false); + expect(await checkboxes[1].isChecked()).toBe(false); + }); +} + +function getActiveElementTagName() { + return document.activeElement ? document.activeElement.tagName.toLowerCase() : ''; +} + +@Component({ + template: ` + + First + + + Second + + Second checkbox + ` +}) +class CheckboxHarnessTest { + ctrl = new FormControl(true); +} + diff --git a/src/material-experimental/mdc-checkbox/harnes/mat-checkbox-harness.ts b/src/material-experimental/mdc-checkbox/harness/checkbox-harness.ts similarity index 96% rename from src/material-experimental/mdc-checkbox/harnes/mat-checkbox-harness.ts rename to src/material-experimental/mdc-checkbox/harness/checkbox-harness.ts index 59024ed0151c..79814fe0f385 100644 --- a/src/material-experimental/mdc-checkbox/harnes/mat-checkbox-harness.ts +++ b/src/material-experimental/mdc-checkbox/harness/checkbox-harness.ts @@ -9,6 +9,10 @@ import {ComponentHarness, HarnessPredicate} from '@angular/cdk-experimental/testing'; import {coerceBooleanProperty} from '@angular/cdk/coercion'; +/** + * Harness for interacting with a standard mat-checkbox in tests. + * @dynamic + */ export class MatCheckboxHarness extends ComponentHarness { static hostSelector = 'mat-checkbox'; diff --git a/src/material-experimental/mdc-checkbox/harnes/mat-mdc-checkbox-harness.ts b/src/material-experimental/mdc-checkbox/harness/mdc-checkbox-harness.ts similarity index 96% rename from src/material-experimental/mdc-checkbox/harnes/mat-mdc-checkbox-harness.ts rename to src/material-experimental/mdc-checkbox/harness/mdc-checkbox-harness.ts index 9ea51eabe262..cfaa0068ed61 100644 --- a/src/material-experimental/mdc-checkbox/harnes/mat-mdc-checkbox-harness.ts +++ b/src/material-experimental/mdc-checkbox/harness/mdc-checkbox-harness.ts @@ -9,6 +9,10 @@ import {ComponentHarness, HarnessPredicate} from '@angular/cdk-experimental/testing'; import {coerceBooleanProperty} from '@angular/cdk/coercion'; +/** + * Harness for interacting with a MDC-based mat-checkbox in tests. + * @dynamic + */ export class MatCheckboxHarness extends ComponentHarness { static hostSelector = 'mat-checkbox'; diff --git a/test/karma-system-config.js b/test/karma-system-config.js index 3239888d0545..4477e597b873 100644 --- a/test/karma-system-config.js +++ b/test/karma-system-config.js @@ -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', From a8a20a1c28d9b0fc32fb62dbd6368d42f311cd82 Mon Sep 17 00:00:00 2001 From: Miles Malerba Date: Wed, 19 Jun 2019 14:06:03 -0700 Subject: [PATCH 3/8] doc strings --- .../mdc-checkbox/harness/checkbox-harness.ts | 34 ++++++++++++++++++- .../harness/mdc-checkbox-harness.ts | 34 ++++++++++++++++++- 2 files changed, 66 insertions(+), 2 deletions(-) diff --git a/src/material-experimental/mdc-checkbox/harness/checkbox-harness.ts b/src/material-experimental/mdc-checkbox/harness/checkbox-harness.ts index 79814fe0f385..405c39684589 100644 --- a/src/material-experimental/mdc-checkbox/harness/checkbox-harness.ts +++ b/src/material-experimental/mdc-checkbox/harness/checkbox-harness.ts @@ -16,7 +16,13 @@ import {coerceBooleanProperty} from '@angular/cdk/coercion'; export class MatCheckboxHarness extends ComponentHarness { static hostSelector = 'mat-checkbox'; - static with(options: {label: string | RegExp}): HarnessPredicate { + /** + * 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: {label?: string | RegExp} = {}): HarnessPredicate { return new HarnessPredicate(MatCheckboxHarness) .addOption('label', options.label, (harness, label) => HarnessPredicate.stringMatches(harness.getLabelText(), label)); @@ -25,69 +31,95 @@ export class MatCheckboxHarness extends ComponentHarness { private _label = this.locatorFor('.mat-checkbox-label'); private _input = this.locatorFor('input'); + /** Gets a boolean promise indicating if the checkbox is checked. */ async isChecked(): Promise { 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 { const indeterminate = (await this._input()).getAttribute('indeterminate'); return coerceBooleanProperty(await indeterminate); } + /** Gets a boolean promise indicating if the checkbox is disabled. */ async isDisabled(): Promise { const disabled = (await this._input()).getAttribute('disabled'); return coerceBooleanProperty(await disabled); } + /** Gets a boolean promise indicating if the checkbox is required. */ async isRequired(): Promise { const required = (await this._input()).getAttribute('required'); return coerceBooleanProperty(await required); } + /** Gets a boolean promise indicating if the checkbox is valid. */ async isValid(): Promise { const invalid = (await this.host()).hasClass('ng-invalid'); return !(await invalid); } + /** Gets a promise for the checkbox's name. */ async getName(): Promise { return (await this._input()).getAttribute('name'); } + /** Gets a promise for the checkbox's value. */ async getValue(): Promise { return (await this._input()).getAttribute('value'); } + /** Gets a promise for the checkbox's aria-label. */ async getAriaLabel(): Promise { return (await this._input()).getAttribute('aria-label'); } + /** Gets a promise for the checkbox's aria-labelledby. */ async getAriaLabelledby(): Promise { return (await this._input()).getAttribute('aria-labelledby'); } + /** Gets a promise for the checkbox's label text. */ async getLabelText(): Promise { return (await this._label()).text(); } + /** Focuses the checkbox and returns a void promise that indicates when the action is complete. */ async foucs(): Promise { return (await this._input()).focus(); } + /** Blurs the checkbox and returns a void promise that indicates when the action is complete. */ async blur(): Promise { return (await this._input()).blur(); } + /** + * Toggle the checked state of the checkbox and returns a void promise that indicates when the + * action is complete. + */ async toggle(): Promise { return (await this._input()).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. + */ async check(): Promise { 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. + */ async uncheck(): Promise { if (await this.isChecked()) { await this.toggle(); diff --git a/src/material-experimental/mdc-checkbox/harness/mdc-checkbox-harness.ts b/src/material-experimental/mdc-checkbox/harness/mdc-checkbox-harness.ts index cfaa0068ed61..7af8ed45de49 100644 --- a/src/material-experimental/mdc-checkbox/harness/mdc-checkbox-harness.ts +++ b/src/material-experimental/mdc-checkbox/harness/mdc-checkbox-harness.ts @@ -16,7 +16,13 @@ import {coerceBooleanProperty} from '@angular/cdk/coercion'; export class MatCheckboxHarness extends ComponentHarness { static hostSelector = 'mat-checkbox'; - static with(options: {label: string | RegExp}): HarnessPredicate { + /** + * 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: {label?: string | RegExp} = {}): HarnessPredicate { return new HarnessPredicate(MatCheckboxHarness) .addOption('label', options.label, (harness, label) => HarnessPredicate.stringMatches(harness.getLabelText(), label)); @@ -25,69 +31,95 @@ export class MatCheckboxHarness extends ComponentHarness { private _label = this.locatorFor('label'); private _input = this.locatorFor('input'); + /** Gets a boolean promise indicating if the checkbox is checked. */ async isChecked(): Promise { 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 { const indeterminate = (await this._input()).getAttribute('indeterminate'); return coerceBooleanProperty(await indeterminate); } + /** Gets a boolean promise indicating if the checkbox is disabled. */ async isDisabled(): Promise { const disabled = (await this._input()).getAttribute('disabled'); return coerceBooleanProperty(await disabled); } + /** Gets a boolean promise indicating if the checkbox is required. */ async isRequired(): Promise { const required = (await this._input()).getAttribute('required'); return coerceBooleanProperty(await required); } + /** Gets a boolean promise indicating if the checkbox is valid. */ async isValid(): Promise { const invalid = (await this.host()).hasClass('ng-invalid'); return !(await invalid); } + /** Gets a promise for the checkbox's name. */ async getName(): Promise { return (await this._input()).getAttribute('name'); } + /** Gets a promise for the checkbox's value. */ async getValue(): Promise { return (await this._input()).getAttribute('value'); } + /** Gets a promise for the checkbox's aria-label. */ async getAriaLabel(): Promise { return (await this._input()).getAttribute('aria-label'); } + /** Gets a promise for the checkbox's aria-labelledby. */ async getAriaLabelledby(): Promise { return (await this._input()).getAttribute('aria-labelledby'); } + /** Gets a promise for the checkbox's label text. */ async getLabelText(): Promise { return (await this._label()).text(); } + /** Focuses the checkbox and returns a void promise that indicates when the action is complete. */ async foucs(): Promise { return (await this._input()).focus(); } + /** Blurs the checkbox and returns a void promise that indicates when the action is complete. */ async blur(): Promise { return (await this._input()).blur(); } + /** + * Toggle the checked state of the checkbox and returns a void promise that indicates when the + * action is complete. + */ async toggle(): Promise { return (await this._input()).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. + */ async check(): Promise { 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. + */ async uncheck(): Promise { if (await this.isChecked()) { await this.toggle(); From df045385ed2d5ef01762605116018ba0fe31de36 Mon Sep 17 00:00:00 2001 From: Miles Malerba Date: Thu, 20 Jun 2019 10:03:22 -0700 Subject: [PATCH 4/8] fix BUILD setup --- src/cdk-experimental/testing/BUILD.bazel | 8 +------- src/cdk-experimental/testing/protractor/BUILD | 16 ++++++++++++++++ .../testing/protractor/index.ts | 3 +-- .../testing/protractor/public-api.ts | 10 ++++++++++ src/cdk-experimental/testing/testbed/BUILD | 17 +++++++++++++++++ src/cdk-experimental/testing/testbed/index.ts | 3 +-- .../testing/testbed/public-api.ts | 10 ++++++++++ src/cdk-experimental/testing/tests/BUILD.bazel | 2 ++ .../testing/tests/protractor.e2e.spec.ts | 4 ++-- .../testing/tests/testbed.spec.ts | 4 ++-- .../mdc-checkbox/BUILD.bazel | 17 ++++++++++++----- 11 files changed, 74 insertions(+), 20 deletions(-) create mode 100644 src/cdk-experimental/testing/protractor/BUILD create mode 100644 src/cdk-experimental/testing/protractor/public-api.ts create mode 100644 src/cdk-experimental/testing/testbed/BUILD create mode 100644 src/cdk-experimental/testing/testbed/public-api.ts diff --git a/src/cdk-experimental/testing/BUILD.bazel b/src/cdk-experimental/testing/BUILD.bazel index b4d6e55ce92e..345c147669a6 100644 --- a/src/cdk-experimental/testing/BUILD.bazel +++ b/src/cdk-experimental/testing/BUILD.bazel @@ -1,6 +1,6 @@ package(default_visibility = ["//visibility:public"]) -load("//tools:defaults.bzl", "ng_module", "ng_web_test_suite", "ts_library") +load("//tools:defaults.bzl", "ng_module", "ng_web_test_suite") load("@npm_angular_bazel//:index.bzl", "protractor_web_test_suite") ng_module( @@ -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( diff --git a/src/cdk-experimental/testing/protractor/BUILD b/src/cdk-experimental/testing/protractor/BUILD new file mode 100644 index 000000000000..0377b85756a0 --- /dev/null +++ b/src/cdk-experimental/testing/protractor/BUILD @@ -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", + ], +) diff --git a/src/cdk-experimental/testing/protractor/index.ts b/src/cdk-experimental/testing/protractor/index.ts index 7b4ab8f52b30..676ca90f1ffa 100644 --- a/src/cdk-experimental/testing/protractor/index.ts +++ b/src/cdk-experimental/testing/protractor/index.ts @@ -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'; diff --git a/src/cdk-experimental/testing/protractor/public-api.ts b/src/cdk-experimental/testing/protractor/public-api.ts new file mode 100644 index 000000000000..7b4ab8f52b30 --- /dev/null +++ b/src/cdk-experimental/testing/protractor/public-api.ts @@ -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'; diff --git a/src/cdk-experimental/testing/testbed/BUILD b/src/cdk-experimental/testing/testbed/BUILD new file mode 100644 index 000000000000..56d93eadb969 --- /dev/null +++ b/src/cdk-experimental/testing/testbed/BUILD @@ -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", + ], +) diff --git a/src/cdk-experimental/testing/testbed/index.ts b/src/cdk-experimental/testing/testbed/index.ts index 0422b3097bf3..676ca90f1ffa 100644 --- a/src/cdk-experimental/testing/testbed/index.ts +++ b/src/cdk-experimental/testing/testbed/index.ts @@ -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'; diff --git a/src/cdk-experimental/testing/testbed/public-api.ts b/src/cdk-experimental/testing/testbed/public-api.ts new file mode 100644 index 000000000000..0422b3097bf3 --- /dev/null +++ b/src/cdk-experimental/testing/testbed/public-api.ts @@ -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'; diff --git a/src/cdk-experimental/testing/tests/BUILD.bazel b/src/cdk-experimental/testing/tests/BUILD.bazel index 19556f7d3d1d..bba409024227 100644 --- a/src/cdk-experimental/testing/tests/BUILD.bazel +++ b/src/cdk-experimental/testing/tests/BUILD.bazel @@ -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", ], ) diff --git a/src/cdk-experimental/testing/tests/protractor.e2e.spec.ts b/src/cdk-experimental/testing/tests/protractor.e2e.spec.ts index 7909f50f3c61..1e882c93e875 100644 --- a/src/cdk-experimental/testing/tests/protractor.e2e.spec.ts +++ b/src/cdk-experimental/testing/tests/protractor.e2e.spec.ts @@ -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'; diff --git a/src/cdk-experimental/testing/tests/testbed.spec.ts b/src/cdk-experimental/testing/tests/testbed.spec.ts index dfe7d44ad745..9ef5f70a7e6b 100644 --- a/src/cdk-experimental/testing/tests/testbed.spec.ts +++ b/src/cdk-experimental/testing/tests/testbed.spec.ts @@ -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'; diff --git a/src/material-experimental/mdc-checkbox/BUILD.bazel b/src/material-experimental/mdc-checkbox/BUILD.bazel index 9d97578d3142..89c4670e0390 100644 --- a/src/material-experimental/mdc-checkbox/BUILD.bazel +++ b/src/material-experimental/mdc-checkbox/BUILD.bazel @@ -8,7 +8,10 @@ ng_module( name = "mdc-checkbox", srcs = glob( ["**/*.ts"], - exclude = ["**/*.spec.ts", "harness/**"], + exclude = [ + "**/*.spec.ts", + "harness/**", + ], ), assets = [":checkbox_scss"] + glob(["**/*.html"]), module_name = "@angular/material-experimental/mdc-checkbox", @@ -27,11 +30,14 @@ ng_module( ts_library( name = "harness", - srcs = glob(["harness/**/*.ts"], exclude = ["**/*.spec.ts"]), + srcs = glob( + ["harness/**/*.ts"], + exclude = ["**/*.spec.ts"], + ), deps = [ - "//src/cdk/coercion", "//src/cdk-experimental/testing", - ] + "//src/cdk/coercion", + ], ) sass_library( @@ -66,8 +72,9 @@ ng_test_library( deps = [ ":harness", ":mdc-checkbox", - "//src/cdk/testing", "//src/cdk-experimental/testing", + "//src/cdk-experimental/testing/testbed", + "//src/cdk/testing", "//src/material/checkbox", "@npm//@angular/forms", "@npm//@angular/platform-browser", From 41fcad22acd7ebd0394228f1a210478d2dbe2534 Mon Sep 17 00:00:00 2001 From: Miles Malerba Date: Thu, 20 Jun 2019 13:00:23 -0700 Subject: [PATCH 5/8] re-enable checkbox before toggle test --- .../mdc-checkbox/harness/checkbox-harness.spec.ts | 15 +++++++++++++-- .../mdc-checkbox/harness/checkbox-harness.ts | 3 ++- .../mdc-checkbox/harness/mdc-checkbox-harness.ts | 4 +++- 3 files changed, 18 insertions(+), 4 deletions(-) diff --git a/src/material-experimental/mdc-checkbox/harness/checkbox-harness.spec.ts b/src/material-experimental/mdc-checkbox/harness/checkbox-harness.spec.ts index 3a4367f99077..28945ca17f71 100644 --- a/src/material-experimental/mdc-checkbox/harness/checkbox-harness.spec.ts +++ b/src/material-experimental/mdc-checkbox/harness/checkbox-harness.spec.ts @@ -8,7 +8,7 @@ import {MatCheckboxModule as MatMdcCheckboxModule} from '../index'; import {MatCheckboxHarness} from './checkbox-harness'; import {MatCheckboxHarness as MatMdcCheckboxHarness} from './mdc-checkbox-harness'; -let fixture: ComponentFixture; +let fixture: ComponentFixture; let loader: HarnessLoader; let checkboxHarness: typeof MatCheckboxHarness; @@ -141,6 +141,7 @@ function runTests() { }); it('should toggle checkbox', async () => { + fixture.componentInstance.disabled = false; const checkboxes = await loader.getAllHarnesses(checkboxHarness); await checkboxes[0].toggle(); await checkboxes[1].toggle(); @@ -149,6 +150,7 @@ function runTests() { }); it('should check checkbox', async () => { + fixture.componentInstance.disabled = false; const checkboxes = await loader.getAllHarnesses(checkboxHarness); await checkboxes[0].check(); await checkboxes[1].check(); @@ -157,12 +159,20 @@ function runTests() { }); it('should uncheck checkbox', async () => { + fixture.componentInstance.disabled = false; const checkboxes = await loader.getAllHarnesses(checkboxHarness); await checkboxes[0].uncheck(); await checkboxes[1].uncheck(); expect(await checkboxes[0].isChecked()).toBe(false); expect(await checkboxes[1].isChecked()).toBe(false); }); + + it('should not toggle disabled checkbox', async () => { + const checkbox = await loader.getHarness(checkboxHarness.with({label: 'Second'})); + expect(await checkbox.isChecked()).toBe(false); + await checkbox.toggle(); + expect(await checkbox.isChecked()).toBe(false); + }); } function getActiveElementTagName() { @@ -179,7 +189,7 @@ function getActiveElementTagName() { aria-label="First checkbox"> First - + Second Second checkbox @@ -187,5 +197,6 @@ function getActiveElementTagName() { }) class CheckboxHarnessTest { ctrl = new FormControl(true); + disabled = true; } diff --git a/src/material-experimental/mdc-checkbox/harness/checkbox-harness.ts b/src/material-experimental/mdc-checkbox/harness/checkbox-harness.ts index 405c39684589..bd53a2a63400 100644 --- a/src/material-experimental/mdc-checkbox/harness/checkbox-harness.ts +++ b/src/material-experimental/mdc-checkbox/harness/checkbox-harness.ts @@ -30,6 +30,7 @@ export class MatCheckboxHarness extends ComponentHarness { 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 { @@ -101,7 +102,7 @@ export class MatCheckboxHarness extends ComponentHarness { * action is complete. */ async toggle(): Promise { - return (await this._input()).click(); + return (await this._inputContainer()).click(); } /** diff --git a/src/material-experimental/mdc-checkbox/harness/mdc-checkbox-harness.ts b/src/material-experimental/mdc-checkbox/harness/mdc-checkbox-harness.ts index 7af8ed45de49..7a7bc353728e 100644 --- a/src/material-experimental/mdc-checkbox/harness/mdc-checkbox-harness.ts +++ b/src/material-experimental/mdc-checkbox/harness/mdc-checkbox-harness.ts @@ -30,6 +30,7 @@ export class MatCheckboxHarness extends ComponentHarness { 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 { @@ -101,7 +102,8 @@ export class MatCheckboxHarness extends ComponentHarness { * action is complete. */ async toggle(): Promise { - return (await this._input()).click(); + const elToClick = await this.isDisabled() ? this._inputContainer() : this._input(); + return (await elToClick).click(); } /** From 974f0467ffc0b92daf3dcc2f68dcb34023231c1c Mon Sep 17 00:00:00 2001 From: Miles Malerba Date: Thu, 20 Jun 2019 13:41:20 -0700 Subject: [PATCH 6/8] add mdc theme to tests --- .../mdc-theming/BUILD.bazel | 32 +++++++++++++++++++ .../mdc-theming/_all-theme.scss | 21 ++++++++++++ .../mdc-theming/prebuilt/indigo-pink.scss | 12 +++++++ .../mdc-typography/BUILD.bazel | 20 ++++++++++++ .../mdc-typography/_all-typography.scss | 21 ++++++++++++ tools/defaults.bzl | 5 ++- 6 files changed, 110 insertions(+), 1 deletion(-) create mode 100644 src/material-experimental/mdc-theming/BUILD.bazel create mode 100644 src/material-experimental/mdc-theming/_all-theme.scss create mode 100644 src/material-experimental/mdc-theming/prebuilt/indigo-pink.scss create mode 100644 src/material-experimental/mdc-typography/BUILD.bazel create mode 100644 src/material-experimental/mdc-typography/_all-typography.scss diff --git a/src/material-experimental/mdc-theming/BUILD.bazel b/src/material-experimental/mdc-theming/BUILD.bazel new file mode 100644 index 000000000000..77601077fb8e --- /dev/null +++ b/src/material-experimental/mdc-theming/BUILD.bazel @@ -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", + ], +) diff --git a/src/material-experimental/mdc-theming/_all-theme.scss b/src/material-experimental/mdc-theming/_all-theme.scss new file mode 100644 index 000000000000..e5e3d99caf67 --- /dev/null +++ b/src/material-experimental/mdc-theming/_all-theme.scss @@ -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); +} diff --git a/src/material-experimental/mdc-theming/prebuilt/indigo-pink.scss b/src/material-experimental/mdc-theming/prebuilt/indigo-pink.scss new file mode 100644 index 000000000000..d87658dfa726 --- /dev/null +++ b/src/material-experimental/mdc-theming/prebuilt/indigo-pink.scss @@ -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(); diff --git a/src/material-experimental/mdc-typography/BUILD.bazel b/src/material-experimental/mdc-typography/BUILD.bazel new file mode 100644 index 000000000000..9bd73694e688 --- /dev/null +++ b/src/material-experimental/mdc-typography/BUILD.bazel @@ -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", + ], +) diff --git a/src/material-experimental/mdc-typography/_all-typography.scss b/src/material-experimental/mdc-typography/_all-typography.scss new file mode 100644 index 000000000000..e825c3bf6cbd --- /dev/null +++ b/src/material-experimental/mdc-typography/_all-typography.scss @@ -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); +} diff --git a/tools/defaults.bzl b/tools/defaults.bzl index df961b7108ff..cc5dad27d11d 100644 --- a/tools/defaults.bzl +++ b/tools/defaults.bzl @@ -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, From df5a44d0d775457f1c31a8badd13be7baac9679c Mon Sep 17 00:00:00 2001 From: Miles Malerba Date: Thu, 20 Jun 2019 13:58:22 -0700 Subject: [PATCH 7/8] fix lint --- .github/CODEOWNERS | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index b843f43451f8..61d0f67fba84 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -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 From be72816c20148e145f4126a09b11372fe8083581 Mon Sep 17 00:00:00 2001 From: Miles Malerba Date: Fri, 21 Jun 2019 09:19:59 -0700 Subject: [PATCH 8/8] address feedback --- .../harness/checkbox-harness-filters.ts | 11 +++ .../harness/checkbox-harness.spec.ts | 78 +++++++++---------- .../mdc-checkbox/harness/checkbox-harness.ts | 15 +++- .../harness/mdc-checkbox-harness.ts | 15 +++- 4 files changed, 78 insertions(+), 41 deletions(-) create mode 100644 src/material-experimental/mdc-checkbox/harness/checkbox-harness-filters.ts diff --git a/src/material-experimental/mdc-checkbox/harness/checkbox-harness-filters.ts b/src/material-experimental/mdc-checkbox/harness/checkbox-harness-filters.ts new file mode 100644 index 000000000000..a82a027b5cbb --- /dev/null +++ b/src/material-experimental/mdc-checkbox/harness/checkbox-harness-filters.ts @@ -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 +}; diff --git a/src/material-experimental/mdc-checkbox/harness/checkbox-harness.spec.ts b/src/material-experimental/mdc-checkbox/harness/checkbox-harness.spec.ts index 28945ca17f71..1b0c1809e064 100644 --- a/src/material-experimental/mdc-checkbox/harness/checkbox-harness.spec.ts +++ b/src/material-experimental/mdc-checkbox/harness/checkbox-harness.spec.ts @@ -68,35 +68,35 @@ function runTests() { }); it('should get checked state', async () => { - const checkboxes = await loader.getAllHarnesses(checkboxHarness); - expect(await checkboxes[0].isChecked()).toBe(true); - expect(await checkboxes[1].isChecked()).toBe(false); + 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 checkboxes = await loader.getAllHarnesses(checkboxHarness); - expect(await checkboxes[0].isIndeterminate()).toBe(false); - expect(await checkboxes[1].isIndeterminate()).toBe(true); + 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 checkboxes = await loader.getAllHarnesses(checkboxHarness); - expect(await checkboxes[0].isDisabled()).toBe(false); - expect(await checkboxes[1].isDisabled()).toBe(true); + 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 checkboxes = await loader.getAllHarnesses(checkboxHarness); - expect(await checkboxes[0].isRequired()).toBe(true); - expect(await checkboxes[1].isRequired()).toBe(false); + 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 checkboxes = await loader.getAllHarnesses(checkboxHarness); - expect(await checkboxes[0].isValid()).toBe(true); - expect(await checkboxes[1].isValid()).toBe(true); - await checkboxes[0].uncheck(); - expect(await checkboxes[0].isValid()).toBe(false); + 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 () => { @@ -120,9 +120,9 @@ function runTests() { }); it('should get label text', async () => { - const checkboxes = await loader.getAllHarnesses(checkboxHarness); - expect(await checkboxes[0].getLabelText()).toBe('First'); - expect(await checkboxes[1].getLabelText()).toBe('Second'); + const [firstCheckbox, secondCheckbox] = await loader.getAllHarnesses(checkboxHarness); + expect(await firstCheckbox.getLabelText()).toBe('First'); + expect(await secondCheckbox.getLabelText()).toBe('Second'); }); it('should focus checkbox', async () => { @@ -142,36 +142,36 @@ function runTests() { it('should toggle checkbox', async () => { fixture.componentInstance.disabled = false; - const checkboxes = await loader.getAllHarnesses(checkboxHarness); - await checkboxes[0].toggle(); - await checkboxes[1].toggle(); - expect(await checkboxes[0].isChecked()).toBe(false); - expect(await checkboxes[1].isChecked()).toBe(true); + 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 checkboxes = await loader.getAllHarnesses(checkboxHarness); - await checkboxes[0].check(); - await checkboxes[1].check(); - expect(await checkboxes[0].isChecked()).toBe(true); - expect(await checkboxes[1].isChecked()).toBe(true); + 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 checkboxes = await loader.getAllHarnesses(checkboxHarness); - await checkboxes[0].uncheck(); - await checkboxes[1].uncheck(); - expect(await checkboxes[0].isChecked()).toBe(false); - expect(await checkboxes[1].isChecked()).toBe(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 checkbox = await loader.getHarness(checkboxHarness.with({label: 'Second'})); - expect(await checkbox.isChecked()).toBe(false); - await checkbox.toggle(); - expect(await checkbox.isChecked()).toBe(false); + const disabledCheckbox = await loader.getHarness(checkboxHarness.with({label: 'Second'})); + expect(await disabledCheckbox.isChecked()).toBe(false); + await disabledCheckbox.toggle(); + expect(await disabledCheckbox.isChecked()).toBe(false); }); } diff --git a/src/material-experimental/mdc-checkbox/harness/checkbox-harness.ts b/src/material-experimental/mdc-checkbox/harness/checkbox-harness.ts index bd53a2a63400..6d52da66e03b 100644 --- a/src/material-experimental/mdc-checkbox/harness/checkbox-harness.ts +++ b/src/material-experimental/mdc-checkbox/harness/checkbox-harness.ts @@ -8,6 +8,7 @@ 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. @@ -22,7 +23,7 @@ export class MatCheckboxHarness extends ComponentHarness { * - `label` finds a checkbox with specific label text. * @return a `HarnessPredicate` configured with the given options. */ - static with(options: {label?: string | RegExp} = {}): HarnessPredicate { + static with(options: CheckboxHarnessFilters = {}): HarnessPredicate { return new HarnessPredicate(MatCheckboxHarness) .addOption('label', options.label, (harness, label) => HarnessPredicate.stringMatches(harness.getLabelText(), label)); @@ -100,6 +101,10 @@ export class MatCheckboxHarness extends ComponentHarness { /** * 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 { return (await this._inputContainer()).click(); @@ -109,6 +114,10 @@ export class MatCheckboxHarness extends ComponentHarness { * 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 { if (!(await this.isChecked())) { @@ -120,6 +129,10 @@ export class MatCheckboxHarness extends ComponentHarness { * 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 { if (await this.isChecked()) { diff --git a/src/material-experimental/mdc-checkbox/harness/mdc-checkbox-harness.ts b/src/material-experimental/mdc-checkbox/harness/mdc-checkbox-harness.ts index 7a7bc353728e..f1f4b9c2c075 100644 --- a/src/material-experimental/mdc-checkbox/harness/mdc-checkbox-harness.ts +++ b/src/material-experimental/mdc-checkbox/harness/mdc-checkbox-harness.ts @@ -8,6 +8,7 @@ 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. @@ -22,7 +23,7 @@ export class MatCheckboxHarness extends ComponentHarness { * - `label` finds a checkbox with specific label text. * @return a `HarnessPredicate` configured with the given options. */ - static with(options: {label?: string | RegExp} = {}): HarnessPredicate { + static with(options: CheckboxHarnessFilters = {}): HarnessPredicate { return new HarnessPredicate(MatCheckboxHarness) .addOption('label', options.label, (harness, label) => HarnessPredicate.stringMatches(harness.getLabelText(), label)); @@ -100,6 +101,10 @@ export class MatCheckboxHarness extends ComponentHarness { /** * 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 { const elToClick = await this.isDisabled() ? this._inputContainer() : this._input(); @@ -110,6 +115,10 @@ export class MatCheckboxHarness extends ComponentHarness { * 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 { if (!(await this.isChecked())) { @@ -121,6 +130,10 @@ export class MatCheckboxHarness extends ComponentHarness { * 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 { if (await this.isChecked()) {