refactor(core): remove testing-only style utils from DomAdapters (#32291)

PR Close #32291
This commit is contained in:
Kara Erickson 2019-08-23 12:32:00 -07:00 committed by Miško Hevery
parent 46caf88b2c
commit ede5786d1e
14 changed files with 63 additions and 75 deletions

View File

@ -133,7 +133,7 @@ import {expect} from '@angular/platform-browser/testing/src/matchers';
fixture.detectChanges();
els = fixture.debugElement.queryAll(By.css('span'));
expect(els.length).toEqual(1);
expect(getDOM().hasClass(els[0].nativeElement, 'marker')).toBe(true);
expect(els[0].nativeElement.classList.contains('marker')).toBe(true);
expect(fixture.nativeElement).toHaveText('hello');
}));

View File

@ -10,6 +10,7 @@ import {Component, Directive} from '@angular/core';
import {ElementRef} from '@angular/core/src/linker/element_ref';
import {ComponentFixture, TestBed, async} from '@angular/core/testing';
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
import {hasClass} from '@angular/platform-browser/testing/src/browser_util';
import {expect} from '@angular/platform-browser/testing/src/matchers';
{
@ -37,7 +38,7 @@ import {expect} from '@angular/platform-browser/testing/src/matchers';
// We must use getDOM().querySelector instead of fixture.query here
// since the elements inside are not compiled.
const span = getDOM().querySelector(fixture.nativeElement, '#child');
expect(getDOM().hasClass(span, 'compiled')).toBeFalsy();
expect(hasClass(span, 'compiled')).toBeFalsy();
}));
it('should trigger directives on the same node', async(() => {
@ -45,7 +46,7 @@ import {expect} from '@angular/platform-browser/testing/src/matchers';
const fixture = createTestComponent(template);
fixture.detectChanges();
const span = getDOM().querySelector(fixture.nativeElement, '#child');
expect(getDOM().hasClass(span, 'compiled')).toBeTruthy();
expect(hasClass(span, 'compiled')).toBeTruthy();
}));
});
}

View File

@ -13,6 +13,7 @@ import {TestBed, fakeAsync, flushMicrotasks} from '@angular/core/testing';
import {ɵDomRendererFactory2} from '@angular/platform-browser';
import {ANIMATION_MODULE_TYPE, BrowserAnimationsModule, NoopAnimationsModule} from '@angular/platform-browser/animations';
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
import {hasStyle} from '@angular/platform-browser/testing/src/browser_util';
import {ivyEnabled, modifiedInIvy} from '@angular/private/testing';
const DEFAULT_NAMESPACE_ID = 'id';
@ -1446,8 +1447,7 @@ const DEFAULT_COMPONENT_ID = '1';
const player = engine.players.pop();
player.finish();
expect(getDOM().hasStyle(cmp.element.nativeElement, 'background-color', 'green'))
.toBeTruthy();
expect(hasStyle(cmp.element.nativeElement, 'background-color', 'green')).toBeTruthy();
});
it('should retain state styles when the underlying DOM structure changes even if there are no insert/remove animations',
@ -2039,7 +2039,7 @@ const DEFAULT_COMPONENT_ID = '1';
player.finish();
flushMicrotasks();
expect(getDOM().hasStyle(element, 'color', 'red')).toBeTruthy();
expect(hasStyle(element, 'color', 'red')).toBeTruthy();
cmp.exp = '1';
cmp.color = 'blue';
@ -2047,7 +2047,7 @@ const DEFAULT_COMPONENT_ID = '1';
resetLog();
flushMicrotasks();
expect(getDOM().hasStyle(element, 'color', 'blue')).toBeTruthy();
expect(hasStyle(element, 'color', 'blue')).toBeTruthy();
cmp.exp = '1';
cmp.color = null;
@ -2055,7 +2055,7 @@ const DEFAULT_COMPONENT_ID = '1';
resetLog();
flushMicrotasks();
expect(getDOM().hasStyle(element, 'color', 'black')).toBeTruthy();
expect(hasStyle(element, 'color', 'black')).toBeTruthy();
}));
it('should substitute in values if the provided state match is an object with values', () => {
@ -2157,9 +2157,9 @@ const DEFAULT_COMPONENT_ID = '1';
p1.finish();
flushMicrotasks();
expect(getDOM().hasStyle(element, 'color', 'blue')).toBeTruthy();
expect(getDOM().hasStyle(element, 'fontSize', '50px')).toBeTruthy();
expect(getDOM().hasStyle(element, 'width', '888px')).toBeTruthy();
expect(hasStyle(element, 'color', 'blue')).toBeTruthy();
expect(hasStyle(element, 'fontSize', '50px')).toBeTruthy();
expect(hasStyle(element, 'width', '888px')).toBeTruthy();
}));
it('should only evaluate final state param substitutions from the expression and state values and not from the transition options ',
@ -2223,8 +2223,8 @@ const DEFAULT_COMPONENT_ID = '1';
p1.finish();
flushMicrotasks();
expect(getDOM().hasStyle(element, 'width', '100px')).toBeTruthy();
expect(getDOM().hasStyle(element, 'height', '100px')).toBeTruthy();
expect(hasStyle(element, 'width', '100px')).toBeTruthy();
expect(hasStyle(element, 'height', '100px')).toBeTruthy();
}));
it('should not flush animations twice when an inner component runs change detection', () => {

View File

@ -12,6 +12,7 @@ import {Component, DebugNode, Directive, ElementRef, EmbeddedViewRef, EventEmitt
import {ComponentFixture, TestBed, async} from '@angular/core/testing';
import {By} from '@angular/platform-browser/src/dom/debug/by';
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
import {hasClass} from '@angular/platform-browser/testing/src/browser_util';
import {expect} from '@angular/platform-browser/testing/src/matchers';
import {ivyEnabled} from '@angular/private/testing';
@ -254,24 +255,24 @@ class TestCmptWithPropBindings {
// The root component has 3 elements in its view.
expect(childEls.length).toEqual(3);
expect(getDOM().hasClass(childEls[0].nativeElement, 'parent')).toBe(true);
expect(getDOM().hasClass(childEls[1].nativeElement, 'parent')).toBe(true);
expect(getDOM().hasClass(childEls[2].nativeElement, 'child-comp-class')).toBe(true);
expect(hasClass(childEls[0].nativeElement, 'parent')).toBe(true);
expect(hasClass(childEls[1].nativeElement, 'parent')).toBe(true);
expect(hasClass(childEls[2].nativeElement, 'child-comp-class')).toBe(true);
const nested = childEls[0].children;
expect(nested.length).toEqual(1);
expect(getDOM().hasClass(nested[0].nativeElement, 'parentnested')).toBe(true);
expect(hasClass(nested[0].nativeElement, 'parentnested')).toBe(true);
const childComponent = childEls[2];
const childCompChildren = childComponent.children;
expect(childCompChildren.length).toEqual(2);
expect(getDOM().hasClass(childCompChildren[0].nativeElement, 'child')).toBe(true);
expect(getDOM().hasClass(childCompChildren[1].nativeElement, 'child')).toBe(true);
expect(hasClass(childCompChildren[0].nativeElement, 'child')).toBe(true);
expect(hasClass(childCompChildren[1].nativeElement, 'child')).toBe(true);
const childNested = childCompChildren[0].children;
expect(childNested.length).toEqual(1);
expect(getDOM().hasClass(childNested[0].nativeElement, 'childnested')).toBe(true);
expect(hasClass(childNested[0].nativeElement, 'childnested')).toBe(true);
});
it('should list conditional component child elements', () => {
@ -282,8 +283,8 @@ class TestCmptWithPropBindings {
// The root component has 2 elements in its view.
expect(childEls.length).toEqual(2);
expect(getDOM().hasClass(childEls[0].nativeElement, 'parent')).toBe(true);
expect(getDOM().hasClass(childEls[1].nativeElement, 'cond-content-comp-class')).toBe(true);
expect(hasClass(childEls[0].nativeElement, 'parent')).toBe(true);
expect(hasClass(childEls[1].nativeElement, 'cond-content-comp-class')).toBe(true);
const conditionalContentComp = childEls[1];
@ -353,7 +354,7 @@ class TestCmptWithPropBindings {
const childTestEls = fixture.debugElement.queryAll(By.css('child-comp'));
expect(childTestEls.length).toBe(1);
expect(getDOM().hasClass(childTestEls[0].nativeElement, 'child-comp-class')).toBe(true);
expect(hasClass(childTestEls[0].nativeElement, 'child-comp-class')).toBe(true);
});
it('should query child elements by directive', () => {
@ -363,10 +364,10 @@ class TestCmptWithPropBindings {
const childTestEls = fixture.debugElement.queryAll(By.directive(MessageDir));
expect(childTestEls.length).toBe(4);
expect(getDOM().hasClass(childTestEls[0].nativeElement, 'parent')).toBe(true);
expect(getDOM().hasClass(childTestEls[1].nativeElement, 'parentnested')).toBe(true);
expect(getDOM().hasClass(childTestEls[2].nativeElement, 'child')).toBe(true);
expect(getDOM().hasClass(childTestEls[3].nativeElement, 'childnested')).toBe(true);
expect(hasClass(childTestEls[0].nativeElement, 'parent')).toBe(true);
expect(hasClass(childTestEls[1].nativeElement, 'parentnested')).toBe(true);
expect(hasClass(childTestEls[2].nativeElement, 'child')).toBe(true);
expect(hasClass(childTestEls[3].nativeElement, 'childnested')).toBe(true);
});
it('should query projected child elements by directive', () => {

View File

@ -139,8 +139,8 @@ const removeEventListener = '__zone_symbol__removeEventListener' as 'removeEvent
Services.checkAndUpdateView(view);
const el = rootNodes[0];
expect(getDOM().hasClass(el, 'c1')).toBeTruthy();
expect(getDOM().hasClass(el, 'c2')).toBeTruthy();
expect(el.classList.contains('c1')).toBeTruthy();
expect(el.classList.contains('c2')).toBeTruthy();
});
});
});

View File

@ -11,7 +11,7 @@ import {ComponentFixture, TestBed, fakeAsync, tick} from '@angular/core/testing'
import {AbstractControl, AsyncValidator, AsyncValidatorFn, COMPOSITION_BUFFER_MODE, FormArray, FormControl, FormControlDirective, FormControlName, FormGroup, FormGroupDirective, FormsModule, NG_ASYNC_VALIDATORS, NG_VALIDATORS, ReactiveFormsModule, Validators} from '@angular/forms';
import {By} from '@angular/platform-browser/src/dom/debug/by';
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
import {dispatchEvent} from '@angular/platform-browser/testing/src/browser_util';
import {dispatchEvent, sortedClassList} from '@angular/platform-browser/testing/src/browser_util';
import {merge, timer} from 'rxjs';
import {tap} from 'rxjs/operators';
@ -2464,10 +2464,6 @@ class UniqLoginValidator implements AsyncValidator {
validate(c: AbstractControl) { return uniqLoginAsyncValidator(this.expected)(c); }
}
function sortedClassList(el: HTMLElement) {
return getDOM().classList(el).sort();
}
@Component({selector: 'form-control-comp', template: `<input type="text" [formControl]="control">`})
class FormControlComp {
// TODO(issue/24571): remove '!'.

View File

@ -11,7 +11,7 @@ import {ComponentFixture, TestBed, async, fakeAsync, tick} from '@angular/core/t
import {AbstractControl, AsyncValidator, COMPOSITION_BUFFER_MODE, FormControl, FormsModule, NG_ASYNC_VALIDATORS, NgForm, NgFormSelectorWarning, NgModel} from '@angular/forms';
import {By} from '@angular/platform-browser/src/dom/debug/by';
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
import {dispatchEvent} from '@angular/platform-browser/testing/src/browser_util';
import {dispatchEvent, sortedClassList} from '@angular/platform-browser/testing/src/browser_util';
import {merge} from 'rxjs';
import {NgModelCustomComp, NgModelCustomWrapper} from './value_accessor_integration_spec';
@ -1924,9 +1924,3 @@ class NgModelChangesForm {
class NgModelChangeState {
onNgModelChange = () => {};
}
function sortedClassList(el: HTMLElement) {
const l = getDOM().classList(el);
l.sort();
return l;
}

View File

@ -196,12 +196,8 @@ export class BrowserDomAdapter extends GenericBrowserDomAdapter {
getElementsByTagName(element: any, name: string): HTMLElement[] {
return element.getElementsByTagName(name);
}
classList(element: any): any[] { return Array.prototype.slice.call(element.classList, 0); }
addClass(element: any, className: string) { element.classList.add(className); }
removeClass(element: any, className: string) { element.classList.remove(className); }
hasClass(element: any, className: string): boolean {
return element.classList.contains(className);
}
setStyle(element: any, styleName: string, styleValue: string) {
element.style[styleName] = styleValue;
}
@ -210,11 +206,8 @@ export class BrowserDomAdapter extends GenericBrowserDomAdapter {
// see https://github.com/angular/angular/issues/7916
element.style[stylename] = '';
}
getStyle(element: any, stylename: string): string { return element.style[stylename]; }
hasStyle(element: any, styleName: string, styleValue?: string|null): boolean {
const value = this.getStyle(element, styleName) || '';
return styleValue ? value == styleValue : value.length > 0;
}
getAttribute(element: Element, attribute: string): string|null {
return element.getAttribute(attribute);

View File

@ -80,19 +80,16 @@ export abstract class DomAdapter {
abstract getDistributedNodes(el: any): Node[];
abstract clone /*<T extends Node>*/ (node: Node /*T*/): Node /*T*/;
abstract getElementsByTagName(element: any, name: string): HTMLElement[];
abstract classList(element: any): any[];
abstract addClass(element: any, className: string): any;
abstract removeClass(element: any, className: string): any;
abstract hasClass(element: any, className: string): boolean;
abstract setStyle(element: any, styleName: string, styleValue: string): any;
abstract removeStyle(element: any, styleName: string): any;
abstract getStyle(element: any, styleName: string): string;
abstract hasStyle(element: any, styleName: string, styleValue?: string): boolean;
// Used by Meta
abstract getAttribute(element: any, attribute: string): string|null;
// Used by platform-server
abstract addClass(element: any, className: string): any;
abstract removeClass(element: any, className: string): any;
abstract getStyle(element: any, styleName: string): any;
abstract setStyle(element: any, styleName: string, styleValue: string): any;
abstract removeStyle(element: any, styleName: string): any;
abstract setAttribute(element: any, name: string, value: string): any;
abstract setAttributeNS(element: any, ns: string, name: string, value: string): any;
abstract removeAttribute(element: any, attribute: string): any;

View File

@ -199,3 +199,16 @@ export function setCookie(name: string, value: string) {
export function supportsWebAnimation(): boolean {
return typeof(<any>Element).prototype['animate'] === 'function';
}
export function hasStyle(element: any, styleName: string, styleValue?: string | null): boolean {
const value = element.style[styleName] || '';
return styleValue ? value == styleValue : value.length > 0;
}
export function hasClass(element: any, className: string): boolean {
return element.classList.contains(className);
}
export function sortedClassList(element: any): any[] {
return Array.prototype.slice.call(element.classList, 0).sort();
}

View File

@ -10,7 +10,8 @@
import {Type, ɵglobal as global} from '@angular/core';
import {ComponentFixture} from '@angular/core/testing';
import {By, ɵgetDOM as getDOM} from '@angular/platform-browser';
import {isCommentNode} from './browser_util';
import {hasClass, hasStyle, isCommentNode} from './browser_util';
@ -186,7 +187,7 @@ _global.beforeEach(function() {
function buildError(isNot: boolean) {
return function(actual: any, className: string) {
return {
pass: getDOM().hasClass(actual, className) == !isNot,
pass: hasClass(actual, className) == !isNot,
get message() {
return `Expected ${actual.outerHTML} ${isNot ? 'not ' : ''}to contain the CSS class "${className}"`;
}
@ -200,12 +201,11 @@ _global.beforeEach(function() {
compare: function(actual: any, styles: {[k: string]: string}|string) {
let allPassed: boolean;
if (typeof styles === 'string') {
allPassed = getDOM().hasStyle(actual, styles);
allPassed = hasStyle(actual, styles);
} else {
allPassed = Object.keys(styles).length !== 0;
Object.keys(styles).forEach(prop => {
allPassed = allPassed && getDOM().hasStyle(actual, prop, styles[prop]);
});
Object.keys(styles).forEach(
prop => { allPassed = allPassed && hasStyle(actual, prop, styles[prop]); });
}
return {

View File

@ -162,6 +162,7 @@ export class DominoAdapter extends BrowserDomAdapter {
}
element.setAttribute('style', styleAttrValue);
}
setStyle(element: any, styleName: string, styleValue?: string|null) {
styleName = styleName.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase();
const styleMap = this._readStyleAttribute(element);
@ -173,14 +174,11 @@ export class DominoAdapter extends BrowserDomAdapter {
// see https://github.com/angular/angular/issues/7916
this.setStyle(element, styleName, '');
}
getStyle(element: any, styleName: string): string {
const styleMap = this._readStyleAttribute(element);
return styleMap[styleName] || '';
}
hasStyle(element: any, styleName: string, styleValue?: string): boolean {
const value = this.getStyle(element, styleName);
return styleValue ? value == styleValue : value.length > 0;
}
dispatchEvent(el: Node, evt: any) {
el.dispatchEvent(evt);

View File

@ -89,16 +89,11 @@ export class WorkerDomAdapter extends DomAdapter {
getDistributedNodes(el: any): Node[] { throw 'not implemented'; }
clone(node: Node): Node { throw 'not implemented'; }
getElementsByTagName(element: any, name: string): HTMLElement[] { throw 'not implemented'; }
classList(element: any): any[] { throw 'not implemented'; }
addClass(element: any, className: string) { throw 'not implemented'; }
removeClass(element: any, className: string) { throw 'not implemented'; }
hasClass(element: any, className: string): boolean { throw 'not implemented'; }
setStyle(element: any, styleName: string, styleValue: string) { throw 'not implemented'; }
removeStyle(element: any, styleName: string) { throw 'not implemented'; }
getStyle(element: any, styleName: string): string { throw 'not implemented'; }
hasStyle(element: any, styleName: string, styleValue?: string): boolean {
throw 'not implemented';
}
getAttribute(element: any, attribute: string): string { throw 'not implemented'; }
setAttribute(element: any, name: string, value: string) { throw 'not implemented'; }
setAttributeNS(element: any, ns: string, name: string, value: string) { throw 'not implemented'; }

View File

@ -12,7 +12,7 @@ import {platformBrowserDynamicTesting} from '@angular/platform-browser-dynamic/t
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
import {DomRendererFactory2} from '@angular/platform-browser/src/dom/dom_renderer';
import {BrowserTestingModule} from '@angular/platform-browser/testing';
import {browserDetection, dispatchEvent} from '@angular/platform-browser/testing/src/browser_util';
import {browserDetection, dispatchEvent, hasClass} from '@angular/platform-browser/testing/src/browser_util';
import {expect} from '@angular/platform-browser/testing/src/matchers';
import {ClientMessageBrokerFactory} from '@angular/platform-webworker/src/web_workers/shared/client_message_broker';
import {RenderStore} from '@angular/platform-webworker/src/web_workers/shared/render_store';
@ -109,10 +109,10 @@ let lastCreatedRenderer: Renderer2;
expect(el.tabIndex).toEqual(1);
lastCreatedRenderer.addClass(workerEl, 'a');
expect(getDOM().hasClass(el, 'a')).toBe(true);
expect(hasClass(el, 'a')).toBe(true);
lastCreatedRenderer.removeClass(workerEl, 'a');
expect(getDOM().hasClass(el, 'a')).toBe(false);
expect(hasClass(el, 'a')).toBe(false);
lastCreatedRenderer.setStyle(workerEl, 'width', '10px');
expect(getDOM().getStyle(el, 'width')).toEqual('10px');