chore(material): add unit tests for MdButton.
This commit is contained in:
parent
d2458866c1
commit
bfbce542e6
|
@ -638,6 +638,7 @@ gulp.task('test.unit.dart', function (done) {
|
||||||
'!build/pubget.angular2.dart',
|
'!build/pubget.angular2.dart',
|
||||||
'!build/change_detect.dart',
|
'!build/change_detect.dart',
|
||||||
'!build/remove-pub-symlinks',
|
'!build/remove-pub-symlinks',
|
||||||
|
'build.dart.material.css',
|
||||||
'!test.unit.dart/karma-server',
|
'!test.unit.dart/karma-server',
|
||||||
'!test.unit.dart/karma-run',
|
'!test.unit.dart/karma-run',
|
||||||
function(error) {
|
function(error) {
|
||||||
|
@ -950,6 +951,7 @@ gulp.task('!broccoli.js.prod', function() {
|
||||||
gulp.task('build.js.dev', ['build/clean.js'], function(done) {
|
gulp.task('build.js.dev', ['build/clean.js'], function(done) {
|
||||||
runSequence(
|
runSequence(
|
||||||
'broccoli.js.dev',
|
'broccoli.js.dev',
|
||||||
|
'build.css.material',
|
||||||
sequenceComplete(done)
|
sequenceComplete(done)
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
|
@ -45,7 +45,7 @@ module.exports = function(config) {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
browsers: ['ChromeCanary'],
|
browsers: ['Chrome'],
|
||||||
|
|
||||||
port: 9876
|
port: 9876
|
||||||
});
|
});
|
||||||
|
|
|
@ -14,3 +14,5 @@ dependencies:
|
||||||
dependency_overrides:
|
dependency_overrides:
|
||||||
angular2:
|
angular2:
|
||||||
path: ../angular2
|
path: ../angular2
|
||||||
|
dev_dependencies:
|
||||||
|
guinness: '^0.1.17'
|
||||||
|
|
|
@ -1,2 +1 @@
|
||||||
<style>@import "package:angular2_material/src/components/button/button.css";</style>
|
|
||||||
<span class="md-button-wrapper"><ng-content></ng-content></span>
|
<span class="md-button-wrapper"><ng-content></ng-content></span>
|
||||||
|
|
|
@ -3,6 +3,7 @@ import {Component, View, LifecycleEvent, ViewEncapsulation, OnChanges} from 'ang
|
||||||
import {TimerWrapper} from 'angular2/src/core/facade/async';
|
import {TimerWrapper} from 'angular2/src/core/facade/async';
|
||||||
import {isPresent} from 'angular2/src/core/facade/lang';
|
import {isPresent} from 'angular2/src/core/facade/lang';
|
||||||
|
|
||||||
|
|
||||||
// TODO(jelbourn): Ink ripples.
|
// TODO(jelbourn): Ink ripples.
|
||||||
// TODO(jelbourn): Make the `isMosueDown` stuff done with one global listener.
|
// TODO(jelbourn): Make the `isMosueDown` stuff done with one global listener.
|
||||||
|
|
||||||
|
@ -17,6 +18,7 @@ import {isPresent} from 'angular2/src/core/facade/lang';
|
||||||
})
|
})
|
||||||
@View({
|
@View({
|
||||||
templateUrl: 'package:angular2_material/src/components/button/button.html',
|
templateUrl: 'package:angular2_material/src/components/button/button.html',
|
||||||
|
styleUrls: ['package:angular2_material/src/components/button/button.css'],
|
||||||
encapsulation: ViewEncapsulation.None,
|
encapsulation: ViewEncapsulation.None,
|
||||||
})
|
})
|
||||||
export class MdButton {
|
export class MdButton {
|
||||||
|
@ -55,7 +57,7 @@ export class MdButton {
|
||||||
'(blur)': 'onBlur()',
|
'(blur)': 'onBlur()',
|
||||||
'[tabIndex]': 'tabIndex',
|
'[tabIndex]': 'tabIndex',
|
||||||
'[class.md-button-focus]': 'isKeyboardFocused',
|
'[class.md-button-focus]': 'isKeyboardFocused',
|
||||||
'[attr.aria-disabled]': 'disabled',
|
'[attr.aria-disabled]': 'isAriaDisabled',
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
@View({
|
@View({
|
||||||
|
@ -64,8 +66,6 @@ export class MdButton {
|
||||||
})
|
})
|
||||||
export class MdAnchor extends MdButton implements OnChanges {
|
export class MdAnchor extends MdButton implements OnChanges {
|
||||||
tabIndex: number;
|
tabIndex: number;
|
||||||
|
|
||||||
/** Whether the component is disabled. */
|
|
||||||
disabled_: boolean;
|
disabled_: boolean;
|
||||||
|
|
||||||
get 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.
|
// A disabled anchor should not be in the tab flow.
|
||||||
this.tabIndex = this.disabled ? -1 : 0;
|
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';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 = `<a md-button href="http://google.com" [disabled]="isDisabled">Go</a>`;
|
||||||
|
|
||||||
|
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:
|
||||||
|
`<button md-button type="button" (click)="increment()" [disabled]="isDisabled">Go</button>`
|
||||||
|
})
|
||||||
|
class TestApp {
|
||||||
|
clickCount: number = 0;
|
||||||
|
isDisabled: boolean = false;
|
||||||
|
|
||||||
|
increment() {
|
||||||
|
this.clickCount++;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -13,6 +13,7 @@ System.config({
|
||||||
paths: {
|
paths: {
|
||||||
'benchpress/*': 'dist/js/dev/es5/benchpress/*.js',
|
'benchpress/*': 'dist/js/dev/es5/benchpress/*.js',
|
||||||
'angular2/*': 'dist/js/dev/es5/angular2/*.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',
|
'rtts_assert/*': 'dist/js/dev/es5/rtts_assert/*.js',
|
||||||
'rx': 'node_modules/rx/dist/rx.js'
|
'rx': 'node_modules/rx/dist/rx.js'
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue