diff --git a/modules/angular2/src/core/dom/browser_adapter.dart b/modules/angular2/src/core/dom/browser_adapter.dart index d7de254ffa..ddd7f9cfd7 100644 --- a/modules/angular2/src/core/dom/browser_adapter.dart +++ b/modules/angular2/src/core/dom/browser_adapter.dart @@ -1,4 +1,4 @@ @Deprecated('import this library from "package:angular2/platform/browser_static.dart"') library angular2.browser_adapter_reexport; -export 'package:angular2/src/platform/browser/browser_adapter.dart'; \ No newline at end of file +export 'package:angular2/src/platform/browser/browser_adapter.dart'; diff --git a/modules/angular2/src/platform/browser/browser_adapter.dart b/modules/angular2/src/platform/browser/browser_adapter.dart index 4c8c55c73f..507a6502de 100644 --- a/modules/angular2/src/platform/browser/browser_adapter.dart +++ b/modules/angular2/src/platform/browser/browser_adapter.dart @@ -4,6 +4,7 @@ import 'dart:html'; import 'package:angular2/platform/common_dom.dart' show setRootDomAdapter; import 'generic_browser_adapter.dart' show GenericBrowserDomAdapter; import 'package:angular2/src/facade/browser.dart'; +import 'package:angular2/src/facade/lang.dart' show isBlank, isPresent; import 'dart:js' as js; // WARNING: Do not expose outside this class. Parsing HTML using this @@ -335,6 +336,11 @@ class BrowserDomAdapter extends GenericBrowserDomAdapter { element.style.setProperty(stylename, stylevalue); } + bool hasStyle(Element element, String stylename, [String stylevalue]) { + var value = this.getStyle(element, stylename); + return isPresent(stylevalue) ? value == stylevalue : value.length > 0; + } + void removeStyle(Element element, String stylename) { element.style.removeProperty(stylename); } diff --git a/modules/angular2/src/platform/browser/browser_adapter.ts b/modules/angular2/src/platform/browser/browser_adapter.ts index d79afde928..53f586fd5c 100644 --- a/modules/angular2/src/platform/browser/browser_adapter.ts +++ b/modules/angular2/src/platform/browser/browser_adapter.ts @@ -207,6 +207,10 @@ export class BrowserDomAdapter extends GenericBrowserDomAdapter { } removeStyle(element, stylename: string) { element.style[stylename] = null; } getStyle(element, stylename: string): string { return element.style[stylename]; } + hasStyle(element, stylename: string, stylevalue: string = null): boolean { + var value = this.getStyle(element, stylename) || ''; + return stylevalue ? value == stylevalue : value.length > 0; + } tagName(element): string { return element.tagName; } attributeMap(element): Map { var res = new Map(); diff --git a/modules/angular2/src/platform/dom/dom_adapter.ts b/modules/angular2/src/platform/dom/dom_adapter.ts index 96e154e6d8..968ea19c8c 100644 --- a/modules/angular2/src/platform/dom/dom_adapter.ts +++ b/modules/angular2/src/platform/dom/dom_adapter.ts @@ -89,6 +89,7 @@ export abstract class DomAdapter { abstract setStyle(element, stylename: string, stylevalue: string); abstract removeStyle(element, stylename: string); abstract getStyle(element, stylename: string): string; + abstract hasStyle(element, stylename: string, stylevalue?: string): boolean; abstract tagName(element): string; abstract attributeMap(element): Map; abstract hasAttribute(element, attribute: string): boolean; diff --git a/modules/angular2/src/platform/server/abstract_html_adapter.dart b/modules/angular2/src/platform/server/abstract_html_adapter.dart index d845769b96..d8af52c1cd 100644 --- a/modules/angular2/src/platform/server/abstract_html_adapter.dart +++ b/modules/angular2/src/platform/server/abstract_html_adapter.dart @@ -6,6 +6,8 @@ import 'package:html/dom.dart'; import 'package:angular2/platform/common_dom.dart'; import 'package:angular2/src/compiler/xhr.dart'; +import 'package:angular2/src/facade/lang.dart' show isBlank, isPresent; + const _attrToPropMap = const { 'innerHtml': 'innerHTML', 'readonly': 'readOnly', @@ -265,6 +267,10 @@ abstract class AbstractHtml5LibAdapter implements DomAdapter { throw 'not implemented'; } + bool hasStyle(Element element, String styleName, [String styleValue]) { + throw 'not implemented'; + } + removeStyle(element, String stylename) { throw 'not implemented'; } diff --git a/modules/angular2/src/platform/server/parse5_adapter.ts b/modules/angular2/src/platform/server/parse5_adapter.ts index 5fd9f1a390..b9c4944a09 100644 --- a/modules/angular2/src/platform/server/parse5_adapter.ts +++ b/modules/angular2/src/platform/server/parse5_adapter.ts @@ -371,6 +371,10 @@ export class Parse5DomAdapter extends DomAdapter { hasClass(element, classname: string): boolean { return ListWrapper.contains(this.classList(element), classname); } + hasStyle(element, stylename: string, stylevalue: string = null): boolean { + var value = this.getStyle(element, stylename) || ''; + return stylevalue ? value == stylevalue : value.length > 0; + } /** @internal */ _readStyleAttribute(element) { var styleMap = {}; diff --git a/modules/angular2/src/testing/matchers.dart b/modules/angular2/src/testing/matchers.dart index f24aab61cc..5384a3df5e 100644 --- a/modules/angular2/src/testing/matchers.dart +++ b/modules/angular2/src/testing/matchers.dart @@ -6,6 +6,8 @@ import 'package:guinness/guinness.dart' as gns; import 'package:angular2/src/platform/dom/dom_adapter.dart' show DOM; +import 'package:angular2/src/facade/lang.dart' show isString; + Expect expect(actual, [matcher]) { final expect = new Expect(actual); if (matcher != null) expect.to(matcher); @@ -14,6 +16,18 @@ Expect expect(actual, [matcher]) { const _u = const Object(); +bool elementContainsStyle(element, styles) { + var allPassed = true; + if (isString(styles)) { + allPassed = DOM.hasStyle(element, styles); + } else { + styles.forEach((prop, style) { + allPassed = allPassed && DOM.hasStyle(element, prop, style); + }); + } + return allPassed; +} + expectErrorMessage(actual, expectedMessage) { expect(actual.toString()).toContain(expectedMessage); } @@ -38,6 +52,9 @@ class Expect extends gns.Expect { void toBePromise() => gns.guinness.matchers.toBeTrue(actual is Future); void toHaveCssClass(className) => gns.guinness.matchers.toBeTrue(DOM.hasClass(actual, className)); + void toHaveCssStyle(styles) { + gns.guinness.matchers.toBeTrue(elementContainsStyle(actual, styles)); + } void toImplement(expected) => toBeA(expected); void toBeNaN() => gns.guinness.matchers.toBeTrue(double.NAN.compareTo(actual) == 0); @@ -78,6 +95,9 @@ class NotExpect extends gns.NotExpect { void toBePromise() => gns.guinness.matchers.toBeFalse(actual is Future); void toHaveCssClass(className) => gns.guinness.matchers.toBeFalse(DOM.hasClass(actual, className)); + void toHaveCssStyle(styles) { + gns.guinness.matchers.toBeFalse(elementContainsStyle(actual, styles)); + } void toBeNull() => gns.guinness.matchers.toBeFalse(actual == null); Function get _expect => gns.guinness.matchers.expect; } diff --git a/modules/angular2/src/testing/matchers.ts b/modules/angular2/src/testing/matchers.ts index a44a218493..163b68e92c 100644 --- a/modules/angular2/src/testing/matchers.ts +++ b/modules/angular2/src/testing/matchers.ts @@ -1,5 +1,6 @@ import {DOM} from 'angular2/src/platform/dom/dom_adapter'; -import {global} from 'angular2/src/facade/lang'; +import {global, isString} from 'angular2/src/facade/lang'; +import {StringMapWrapper} from 'angular2/src/facade/collection'; export interface NgMatchers extends jasmine.Matchers { @@ -7,6 +8,7 @@ export interface NgMatchers extends jasmine.Matchers { toBeAnInstanceOf(expected: any): boolean; toHaveText(expected: any): boolean; toHaveCssClass(expected: any): boolean; + toHaveCssStyle(expected: any): boolean; toImplement(expected: any): boolean; toContainError(expected: any): boolean; toThrowErrorWith(expectedMessage: any): boolean; @@ -105,6 +107,31 @@ _global.beforeEach(function() { } }, + toHaveCssStyle: function() { + return { + compare: function(actual, styles) { + var allPassed; + if (isString(styles)) { + allPassed = DOM.hasStyle(actual, styles); + } else { + allPassed = !StringMapWrapper.isEmpty(styles); + StringMapWrapper.forEach(styles, (style, prop) => { + allPassed = allPassed && DOM.hasStyle(actual, prop, style); + }); + } + + return { + pass: allPassed, + get message() { + var expectedValueStr = isString(styles) ? styles : JSON.stringify(styles); + return `Expected ${actual.outerHTML} ${!allPassed ? ' ' : 'not '}to contain the + CSS ${isString(styles) ? 'property' : 'styles'} "${expectedValueStr}"`; + } + }; + } + }; + }, + toContainError: function() { return { compare: function(actual, expectedText) { diff --git a/modules/angular2/test/public_api_spec.ts b/modules/angular2/test/public_api_spec.ts index 3cc281ef35..98ec477d2a 100644 --- a/modules/angular2/test/public_api_spec.ts +++ b/modules/angular2/test/public_api_spec.ts @@ -234,6 +234,7 @@ var NG_ALL = [ 'BrowserDomAdapter.setGlobalVar():js', 'BrowserDomAdapter.setInnerHTML():js', 'BrowserDomAdapter.setProperty():js', + 'BrowserDomAdapter.hasStyle():js', 'BrowserDomAdapter.setStyle():js', 'BrowserDomAdapter.setText():js', 'BrowserDomAdapter.setTitle():js', diff --git a/modules/angular2/test/testing/testing_public_spec.ts b/modules/angular2/test/testing/testing_public_spec.ts index d037aacb53..e4485e36ea 100644 --- a/modules/angular2/test/testing/testing_public_spec.ts +++ b/modules/angular2/test/testing/testing_public_spec.ts @@ -109,6 +109,29 @@ export function main() { expect(el).not.toHaveCssClass('fatias'); }); }); + + describe('toHaveCssStyle', () => { + it('should assert that the CSS style is present', () => { + var el = document.createElement('div'); + expect(el).not.toHaveCssStyle('width'); + + el.style.setProperty('width', '100px'); + expect(el).toHaveCssStyle('width'); + }); + + it('should assert that the styles are matched against the element', () => { + var el = document.createElement('div'); + expect(el).not.toHaveCssStyle({width: '100px', height: '555px'}); + + el.style.setProperty('width', '100px'); + expect(el).toHaveCssStyle({width: '100px'}); + expect(el).not.toHaveCssStyle({width: '100px', height: '555px'}); + + el.style.setProperty('height', '555px'); + expect(el).toHaveCssStyle({height: '555px'}); + expect(el).toHaveCssStyle({width: '100px', height: '555px'}); + }); + }); }); describe('using the test injector with the inject helper', () => {