From bfbce542e651f9cb92e5adec476e9cf81ff6975f Mon Sep 17 00:00:00 2001 From: Jeremy Elbourn Date: Mon, 24 Aug 2015 17:24:01 -0700 Subject: [PATCH] chore(material): add unit tests for MdButton. --- gulpfile.js | 2 + karma-js.conf.js | 2 +- modules/angular2_material/pubspec.yaml | 2 + .../src/components/button/button.html | 1 - .../src/components/button/button.ts | 11 +- modules/angular2_material/test/button_spec.ts | 140 ++++++++++++++++++ .../test/test_url_resolver.dart | 21 +++ .../test/test_url_resolver.ts | 16 ++ test-main.js | 1 + 9 files changed, 191 insertions(+), 5 deletions(-) create mode 100644 modules/angular2_material/test/button_spec.ts create mode 100644 modules/angular2_material/test/test_url_resolver.dart create mode 100644 modules/angular2_material/test/test_url_resolver.ts diff --git a/gulpfile.js b/gulpfile.js index 5e3aa9da38..baa0edfd4c 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -638,6 +638,7 @@ gulp.task('test.unit.dart', function (done) { '!build/pubget.angular2.dart', '!build/change_detect.dart', '!build/remove-pub-symlinks', + 'build.dart.material.css', '!test.unit.dart/karma-server', '!test.unit.dart/karma-run', function(error) { @@ -950,6 +951,7 @@ gulp.task('!broccoli.js.prod', function() { gulp.task('build.js.dev', ['build/clean.js'], function(done) { runSequence( 'broccoli.js.dev', + 'build.css.material', sequenceComplete(done) ); }); diff --git a/karma-js.conf.js b/karma-js.conf.js index e8d5add617..f49c9f754a 100644 --- a/karma-js.conf.js +++ b/karma-js.conf.js @@ -45,7 +45,7 @@ module.exports = function(config) { } }, - browsers: ['ChromeCanary'], + browsers: ['Chrome'], port: 9876 }); diff --git a/modules/angular2_material/pubspec.yaml b/modules/angular2_material/pubspec.yaml index 46a741318a..37fe1b831d 100644 --- a/modules/angular2_material/pubspec.yaml +++ b/modules/angular2_material/pubspec.yaml @@ -14,3 +14,5 @@ dependencies: dependency_overrides: angular2: path: ../angular2 +dev_dependencies: + guinness: '^0.1.17' diff --git a/modules/angular2_material/src/components/button/button.html b/modules/angular2_material/src/components/button/button.html index 6dcfd11d1a..4e3df0e226 100644 --- a/modules/angular2_material/src/components/button/button.html +++ b/modules/angular2_material/src/components/button/button.html @@ -1,2 +1 @@ - diff --git a/modules/angular2_material/src/components/button/button.ts b/modules/angular2_material/src/components/button/button.ts index 9c4cf95dcb..fb0691e361 100644 --- a/modules/angular2_material/src/components/button/button.ts +++ b/modules/angular2_material/src/components/button/button.ts @@ -3,6 +3,7 @@ import {Component, View, LifecycleEvent, ViewEncapsulation, OnChanges} from 'ang import {TimerWrapper} from 'angular2/src/core/facade/async'; import {isPresent} from 'angular2/src/core/facade/lang'; + // TODO(jelbourn): Ink ripples. // TODO(jelbourn): Make the `isMosueDown` stuff done with one global listener. @@ -17,6 +18,7 @@ import {isPresent} from 'angular2/src/core/facade/lang'; }) @View({ templateUrl: 'package:angular2_material/src/components/button/button.html', + styleUrls: ['package:angular2_material/src/components/button/button.css'], encapsulation: ViewEncapsulation.None, }) export class MdButton { @@ -55,7 +57,7 @@ export class MdButton { '(blur)': 'onBlur()', '[tabIndex]': 'tabIndex', '[class.md-button-focus]': 'isKeyboardFocused', - '[attr.aria-disabled]': 'disabled', + '[attr.aria-disabled]': 'isAriaDisabled', }, }) @View({ @@ -64,8 +66,6 @@ export class MdButton { }) export class MdAnchor extends MdButton implements OnChanges { tabIndex: number; - - /** Whether the component is disabled. */ disabled_: boolean; get disabled(): boolean { @@ -89,4 +89,9 @@ export class MdAnchor extends MdButton implements OnChanges { // A disabled anchor should not be in the tab flow. this.tabIndex = this.disabled ? -1 : 0; } + + /** Gets the aria-disabled value for the component, which must be a string for Dart. */ + get isAriaDisabled(): string { + return this.disabled ? 'true' : 'false'; + } } diff --git a/modules/angular2_material/test/button_spec.ts b/modules/angular2_material/test/button_spec.ts new file mode 100644 index 0000000000..6f8f5751b4 --- /dev/null +++ b/modules/angular2_material/test/button_spec.ts @@ -0,0 +1,140 @@ +import { + AsyncTestCompleter, + TestComponentBuilder, + beforeEach, + beforeEachBindings, + ddescribe, + describe, + el, + expect, + iit, + inject, + it, + xit, +} from 'angular2/test_lib'; +import {DebugElement} from 'angular2/src/core/debug/debug_element'; + +import {Component, View, ViewMetadata, UrlResolver, bind} from 'angular2/core'; + +import {MdButton, MdAnchor} from 'angular2_material/src/components/button/button'; + +import {TestUrlResolver} from './test_url_resolver'; + +import {XHR} from 'angular2/src/core/render/xhr'; +import {XHRImpl} from 'angular2/src/core/render/xhr_impl'; + + +export function main() { + describe('MdButton', () => { + let builder: TestComponentBuilder; + + beforeEachBindings(() => [ + // Need a custom URL resolver for ng-material template files in order for them to work + // with both JS and Dart output. + bind(UrlResolver) + .toValue(new TestUrlResolver()), + + // Need to use the real XHR implementation (instead of the mock) so we can actually request + // the template files, since Angular 2 doesn't have anything like $templateCache. This should + // eventually be replaced with a preprocessor that inlines templates. + bind(XHR).toClass(XHRImpl) + ]); + + beforeEach(inject([TestComponentBuilder], (tcb) => { builder = tcb; })); + + describe('button[md-button]', () => { + it('should handle a click on the button', inject([AsyncTestCompleter], (async) => { + builder.createAsync(TestApp).then(rootTestComponent => { + let testComponent = rootTestComponent.debugElement.componentInstance; + let buttonDebugElement = + getChildDebugElement(rootTestComponent.debugElement, 'button'); + + buttonDebugElement.nativeElement.click(); + expect(testComponent.clickCount).toBe(1); + + async.done(); + }); + })); + + it('should disable the button', inject([AsyncTestCompleter], (async) => { + builder.createAsync(TestApp).then(rootTestComponent => { + let testAppComponent = rootTestComponent.debugElement.componentInstance; + let buttonDebugElement = + getChildDebugElement(rootTestComponent.debugElement, 'button'); + let buttonElement = buttonDebugElement.nativeElement; + + // The button should initially be enabled. + expect(buttonElement.disabled).toBe(false); + + // After the disabled binding has been changed. + testAppComponent.isDisabled = true; + rootTestComponent.detectChanges(); + + // The button should should now be disabled. + expect(buttonElement.disabled).toBe(true); + + // Clicking the button should not invoke the handler. + buttonElement.click(); + expect(testAppComponent.clickCount).toBe(0); + async.done(); + }); + })); + }); + + describe('a[md-button]', () => { + const anchorTemplate = `Go`; + + beforeEach(() => { + builder = builder.overrideView( + TestApp, new ViewMetadata({template: anchorTemplate, directives: [MdAnchor]})); + }); + + it('should remove disabled anchors from tab order', inject([AsyncTestCompleter], (async) => { + builder.createAsync(TestApp).then(rootTestComponent => { + let testAppComponent = rootTestComponent.debugElement.componentInstance; + let anchorDebugElement = getChildDebugElement(rootTestComponent.debugElement, 'a'); + let anchorElement = anchorDebugElement.nativeElement; + + // The anchor should initially be in the tab order. + expect(anchorElement.tabIndex).toBe(0); + + // After the disabled binding has been changed. + testAppComponent.isDisabled = true; + rootTestComponent.detectChanges(); + + // The anchor should now be out of the tab order. + expect(anchorElement.tabIndex).toBe(-1); + + async.done(); + }); + + it('should preventDefault for disabled anchor clicks', + inject([AsyncTestCompleter], (async) => { + // No clear way to test this; see https://github.com/angular/angular/issues/3782 + async.done(); + })); + })); + }); + }); +} + +/** Gets a child DebugElement by tag name. */ +function getChildDebugElement(parent: DebugElement, tagName: string): DebugElement { + return parent.query(debugEl => debugEl.nativeElement.tagName.toLowerCase() == tagName); +} + +/** Test component that contains an MdButton. */ +@Component({selector: 'test-app'}) +@View({ + directives: [MdButton], + template: + `` +}) +class TestApp { + clickCount: number = 0; + isDisabled: boolean = false; + + increment() { + this.clickCount++; + } +} diff --git a/modules/angular2_material/test/test_url_resolver.dart b/modules/angular2_material/test/test_url_resolver.dart new file mode 100644 index 0000000000..cc1a15f650 --- /dev/null +++ b/modules/angular2_material/test/test_url_resolver.dart @@ -0,0 +1,21 @@ +library ng_material.test_url_resolver; + +import 'package:angular2/src/core/dom/browser_adapter.dart'; +import 'package:angular2/src/core/services/url_resolver.dart'; + +void commonDemoSetup() { + BrowserDomAdapter.makeCurrent(); +} + +class TestUrlResolver extends UrlResolver { + @override + String resolve(String baseUrl, String url) { + const MATERIAL_PKG = 'package:angular2_material/'; + + if (url.startsWith(MATERIAL_PKG)) { + return '/packages/angular2_material/' + + url.substring(MATERIAL_PKG.length); + } + return super.resolve(baseUrl, url); + } +} diff --git a/modules/angular2_material/test/test_url_resolver.ts b/modules/angular2_material/test/test_url_resolver.ts new file mode 100644 index 0000000000..b941422c43 --- /dev/null +++ b/modules/angular2_material/test/test_url_resolver.ts @@ -0,0 +1,16 @@ +import {UrlResolver} from 'angular2/src/core/services/url_resolver'; + +export class TestUrlResolver extends UrlResolver { + constructor() { + super(); + } + + resolve(baseUrl: string, url: string): string { + // The standard UrlResolver looks for "package:" templateUrls in + // node_modules, however in our repo we host material widgets at the root. + if (url.startsWith('package:angular2_material/')) { + return '/base/dist/js/dev/es5/' + url.substring(8); + } + return super.resolve(baseUrl, url); + } +} diff --git a/test-main.js b/test-main.js index 661b10f8cc..1d343aaf75 100644 --- a/test-main.js +++ b/test-main.js @@ -13,6 +13,7 @@ System.config({ paths: { 'benchpress/*': 'dist/js/dev/es5/benchpress/*.js', 'angular2/*': 'dist/js/dev/es5/angular2/*.js', + 'angular2_material/*': 'dist/js/dev/es5/angular2_material/*.js', 'rtts_assert/*': 'dist/js/dev/es5/rtts_assert/*.js', 'rx': 'node_modules/rx/dist/rx.js' }