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

PR Close #32291
This commit is contained in:
Kara Erickson 2019-08-23 13:28:33 -07:00 committed by Miško Hevery
parent ede5786d1e
commit 30dabdf8fc
14 changed files with 53 additions and 106 deletions

View File

@ -103,8 +103,7 @@ import {expect} from '@angular/platform-browser/testing/src/matchers';
fixture.detectChanges();
expect(fixture.debugElement.queryAll(By.css('span')).length).toEqual(3);
expect(getDOM().getText(fixture.nativeElement))
.toEqual('helloNumberhelloStringhelloFunction');
expect(fixture.nativeElement.textContent).toEqual('helloNumberhelloStringhelloFunction');
getComponent().numberCondition = 0;
fixture.detectChanges();

View File

@ -15,7 +15,7 @@ import {ComponentRef} from '@angular/core/src/linker/component_factory';
import {getLocaleId} from '@angular/core/src/render3';
import {BrowserModule} from '@angular/platform-browser';
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
import {dispatchEvent, getContent} from '@angular/platform-browser/testing/src/browser_util';
import {createTemplate, dispatchEvent, getContent} from '@angular/platform-browser/testing/src/browser_util';
import {expect} from '@angular/platform-browser/testing/src/matchers';
import {onlyInIvy} from '@angular/private/testing';
@ -35,7 +35,7 @@ class SomeComponent {
function createRootEl(selector = 'bootstrap-app') {
const doc = TestBed.get(DOCUMENT);
const rootEl = <HTMLElement>getDOM().firstChild(
getContent(getDOM().createTemplate(`<${selector}></${selector}>`)));
getContent(createTemplate(`<${selector}></${selector}>`)));
const oldRoots = getDOM().querySelectorAll(doc, selector);
for (let i = 0; i < oldRoots.length; i++) {
getDOM().remove(oldRoots[i]);

View File

@ -17,37 +17,6 @@ import {el, isTextNode, stringifyElement} from '@angular/platform-browser/testin
defaultDoc = getDOM().supportsDOMEvents() ? document : getDOM().createHtmlDocument();
});
it('should not coalesque text nodes', () => {
const el1 = el('<div>a</div>');
const el2 = el('<div>b</div>');
getDOM().appendChild(el2, getDOM().firstChild(el1));
expect(getDOM().childNodes(el2).length).toBe(2);
const el2Clone = getDOM().clone(el2);
expect(getDOM().childNodes(el2Clone).length).toBe(2);
});
it('should clone correctly', () => {
const el1 = el('<div x="y">a<span>b</span></div>');
const clone = getDOM().clone(el1);
expect(clone).not.toBe(el1);
getDOM().setAttribute(clone, 'test', '1');
expect(stringifyElement(clone)).toEqual('<div test="1" x="y">a<span>b</span></div>');
expect(getDOM().getAttribute(el1, 'test')).toBeFalsy();
const cNodes = getDOM().childNodes(clone);
const firstChild = cNodes[0];
const secondChild = cNodes[1];
expect(getDOM().parentElement(firstChild)).toBe(clone);
expect(getDOM().nextSibling(firstChild)).toBe(secondChild);
expect(isTextNode(firstChild)).toBe(true);
expect(getDOM().parentElement(secondChild)).toBe(clone);
expect(getDOM().nextSibling(secondChild)).toBeFalsy();
expect(getDOM().isElementNode(secondChild)).toBe(true);
});
it('should be able to create text nodes and use them with the other APIs', () => {
const t = getDOM().createTextNode('hello');
expect(isTextNode(t)).toBe(true);

View File

@ -1006,8 +1006,8 @@ function declareTests(config?: {useJit: boolean}) {
getDOM().dispatchEvent(fixture.debugElement.children[1].nativeElement, dispatchedEvent2);
expect(getDOM().isPrevented(dispatchedEvent)).toBe(true);
expect(getDOM().isPrevented(dispatchedEvent2)).toBe(false);
expect(getDOM().getChecked(fixture.debugElement.children[0].nativeElement)).toBeFalsy();
expect(getDOM().getChecked(fixture.debugElement.children[1].nativeElement)).toBeTruthy();
expect(fixture.debugElement.children[0].nativeElement.checked).toBeFalsy();
expect(fixture.debugElement.children[1].nativeElement.checked).toBeTruthy();
});
}
@ -2689,15 +2689,14 @@ class ComponentWithoutView {
@Directive({selector: '[no-duplicate]'})
class DuplicateDir {
constructor(elRef: ElementRef) {
getDOM().setText(elRef.nativeElement, getDOM().getText(elRef.nativeElement) + 'noduplicate');
getDOM().setText(elRef.nativeElement, elRef.nativeElement.textContent + 'noduplicate');
}
}
@Directive({selector: '[no-duplicate]'})
class OtherDuplicateDir {
constructor(elRef: ElementRef) {
getDOM().setText(
elRef.nativeElement, getDOM().getText(elRef.nativeElement) + 'othernoduplicate');
getDOM().setText(elRef.nativeElement, elRef.nativeElement.textContent + 'othernoduplicate');
}
}

View File

@ -19,7 +19,7 @@ import {ARG_TYPE_VALUES, checkNodeInlineOrDynamic, compViewDef, createAndGetRoot
it('should create text nodes without parents', () => {
const rootNodes = createAndGetRootNodes(compViewDef([textDef(0, null, ['a'])])).rootNodes;
expect(rootNodes.length).toBe(1);
expect(getDOM().getText(rootNodes[0])).toBe('a');
expect(rootNodes[0].textContent).toBe('a');
});
it('should create views with multiple root text nodes', () => {
@ -36,8 +36,8 @@ import {ARG_TYPE_VALUES, checkNodeInlineOrDynamic, compViewDef, createAndGetRoot
textDef(1, null, ['a']),
])).rootNodes;
expect(rootNodes.length).toBe(1);
const textNode = getDOM().firstChild(rootNodes[0]);
expect(getDOM().getText(textNode)).toBe('a');
const textNode = getDOM().firstChild(rootNodes[0]) as Element;
expect(textNode.textContent).toBe('a');
});
it('should add debug information to the renderer', () => {
@ -61,7 +61,7 @@ import {ARG_TYPE_VALUES, checkNodeInlineOrDynamic, compViewDef, createAndGetRoot
Services.checkAndUpdateView(view);
expect(getDOM().getText(rootNodes[0])).toBe('0a1b2');
expect(rootNodes[0].textContent).toBe('0a1b2');
});
});

View File

@ -19,8 +19,9 @@ export class DOMTestComponentRenderer extends TestComponentRenderer {
constructor(@Inject(DOCUMENT) private _doc: any) { super(); }
insertRootElement(rootElId: string) {
const rootEl = <HTMLElement>getDOM().firstChild(
getContent(getDOM().createTemplate(`<div id="${rootElId}"></div>`)));
const template = getDOM().getDefaultDocument().createElement('template');
template.innerHTML = `<div id="${rootElId}"></div>`;
const rootEl = <HTMLElement>getDOM().firstChild(getContent(template));
// TODO(juliemr): can/should this be optional?
const oldRoots = getDOM().querySelectorAll(this._doc, '[id^=root]');

View File

@ -146,14 +146,6 @@ export class BrowserDomAdapter extends GenericBrowserDomAdapter {
nextSibling(el: Node): Node|null { return el.nextSibling; }
parentElement(el: Node): Node|null { return el.parentNode; }
childNodes(el: any): Node[] { return el.childNodes; }
childNodesAsList(el: Node): any[] {
const childNodes = el.childNodes;
const res = [];
for (let i = 0; i < childNodes.length; i++) {
res[i] = childNodes[i];
}
return res;
}
clearNodes(el: Node) {
while (el.firstChild) {
el.removeChild(el.firstChild);
@ -168,17 +160,9 @@ export class BrowserDomAdapter extends GenericBrowserDomAdapter {
return node;
}
insertBefore(parent: Node, ref: Node, node: Node) { parent.insertBefore(node, ref); }
getText(el: Node): string|null { return el.textContent; }
setText(el: Node, value: string) { el.textContent = value; }
getValue(el: any): string { return el.value; }
setValue(el: any, value: string) { el.value = value; }
getChecked(el: any): boolean { return el.checked; }
createComment(text: string): Comment { return this.getDefaultDocument().createComment(text); }
createTemplate(html: any): HTMLElement {
const t = this.getDefaultDocument().createElement('template');
t.innerHTML = html;
return t;
}
createElement(tagName: string, doc?: Document): HTMLElement {
doc = doc || this.getDefaultDocument();
return doc.createElement(tagName);
@ -192,7 +176,6 @@ export class BrowserDomAdapter extends GenericBrowserDomAdapter {
return doc.createTextNode(text);
}
getHost(el: HTMLElement): HTMLElement { return (<any>el).host; }
clone(node: Node): Node { return node.cloneNode(true); }
getElementsByTagName(element: any, name: string): HTMLElement[] {
return element.getElementsByTagName(name);
}

View File

@ -19,6 +19,5 @@ import {DomAdapter} from '../dom/dom_adapter';
export abstract class GenericBrowserDomAdapter extends DomAdapter {
constructor() { super(); }
getDistributedNodes(el: HTMLElement): Node[] { return (<any>el).getDistributedNodes(); }
supportsDOMEvents(): boolean { return true; }
}

View File

@ -60,31 +60,22 @@ export abstract class DomAdapter {
abstract nextSibling(el: any): Node|null;
abstract parentElement(el: any): Node|null;
abstract childNodes(el: any): Node[];
abstract childNodesAsList(el: any): Node[];
abstract clearNodes(el: any): any;
abstract appendChild(el: any, node: any): any;
abstract removeChild(el: any, node: any): any;
abstract remove(el: any): Node;
abstract insertBefore(parent: any, ref: any, node: any): any;
abstract getText(el: any): string|null;
abstract setText(el: any, value: string): any;
abstract getValue(el: any): string;
abstract setValue(el: any, value: string): any;
abstract getChecked(el: any): boolean;
abstract createComment(text: string): any;
abstract createTemplate(html: any): HTMLElement;
abstract createElement(tagName: any, doc?: any): HTMLElement;
abstract createElementNS(ns: string, tagName: string, doc?: any): Element;
abstract createTextNode(text: string, doc?: any): Text;
abstract getHost(el: any): any;
abstract getDistributedNodes(el: any): Node[];
abstract clone /*<T extends Node>*/ (node: Node /*T*/): Node /*T*/;
abstract getElementsByTagName(element: any, name: string): HTMLElement[];
// Used by Meta
abstract remove(el: any): Node;
abstract getAttribute(element: any, attribute: string): string|null;
// Used by platform-server
abstract clearNodes(el: any): any;
abstract appendChild(el: any, node: any): any;
abstract removeChild(el: any, node: any): any;
abstract insertBefore(parent: any, ref: any, node: any): any;
abstract setText(el: any, value: string): any;
abstract createComment(text: string): any;
abstract createElement(tagName: any, doc?: any): HTMLElement;
abstract createElementNS(ns: string, tagName: string, doc?: any): Element;
abstract createTextNode(text: string, doc?: any): Text;
abstract getElementsByTagName(element: any, name: string): HTMLElement[];
abstract addClass(element: any, className: string): any;
abstract removeClass(element: any, className: string): any;
abstract getStyle(element: any, styleName: string): any;
@ -107,6 +98,7 @@ export abstract class DomAdapter {
// Used by Testability
abstract isShadowRoot(node: any): boolean;
abstract getHost(el: any): any;
// Used by KeyEventsPlugin
abstract getEventKey(event: any): string;

View File

@ -95,7 +95,7 @@ export function dispatchEvent(element: any, eventType: any): void {
}
export function el(html: string): HTMLElement {
return <HTMLElement>getDOM().firstChild(getContent(getDOM().createTemplate(html)));
return <HTMLElement>getDOM().firstChild(getContent(createTemplate(html)));
}
export function normalizeCSS(css: string): string {
@ -160,7 +160,7 @@ export function stringifyElement(el: any /** TODO #9100 */): string {
} else if (isCommentNode(el)) {
result += `<!--${el.nodeValue}-->`;
} else {
result += getDOM().getText(el);
result += el.textContent;
}
return result;
@ -212,3 +212,18 @@ export function hasClass(element: any, className: string): boolean {
export function sortedClassList(element: any): any[] {
return Array.prototype.slice.call(element.classList, 0).sort();
}
export function createTemplate(html: any): HTMLElement {
const t = getDOM().getDefaultDocument().createElement('template');
t.innerHTML = html;
return t;
}
export function childNodesAsList(el: Node): any[] {
const childNodes = el.childNodes;
const res = [];
for (let i = 0; i < childNodes.length; i++) {
res[i] = childNodes[i];
}
return res;
}

View File

@ -11,7 +11,7 @@ import {Type, ɵglobal as global} from '@angular/core';
import {ComponentFixture} from '@angular/core/testing';
import {By, ɵgetDOM as getDOM} from '@angular/platform-browser';
import {hasClass, hasStyle, isCommentNode} from './browser_util';
import {childNodesAsList, hasClass, hasStyle, isCommentNode} from './browser_util';
@ -293,18 +293,18 @@ function elementText(n: any): string {
}
if (getDOM().isElementNode(n) && (n as Element).tagName == 'CONTENT') {
return elementText(Array.prototype.slice.apply(getDOM().getDistributedNodes(n)));
return elementText(Array.prototype.slice.apply((<any>n).getDistributedNodes()));
}
if (hasShadowRoot(n)) {
return elementText(getDOM().childNodesAsList((<any>n).shadowRoot));
return elementText(childNodesAsList((<any>n).shadowRoot));
}
if (hasNodes(n)) {
return elementText(getDOM().childNodesAsList(n));
return elementText(childNodesAsList(n));
}
return getDOM().getText(n) !;
return (n as any).textContent;
}
function hasShadowRoot(node: any): boolean {

View File

@ -197,8 +197,6 @@ export class DominoAdapter extends BrowserDomAdapter {
performanceNow(): number { return Date.now(); }
getDistributedNodes(el: any): Node[] { throw _notImplemented('getDistributedNodes'); }
supportsCookies(): boolean { return false; }
getCookie(name: string): string { throw _notImplemented('getCookie'); }
}

View File

@ -412,7 +412,7 @@ class HiddenModule {
expect(doc.head).toBe(getDOM().querySelector(doc, 'head'));
expect(doc.body).toBe(getDOM().querySelector(doc, 'body'));
expect(getDOM().getText(doc.documentElement)).toEqual('Works!');
expect(doc.documentElement.textContent).toEqual('Works!');
platform.destroy();
});
@ -428,13 +428,13 @@ class HiddenModule {
platform.bootstrapModule(ExampleModule).then((moduleRef) => {
const doc = moduleRef.injector.get(DOCUMENT);
expect(getDOM().getText(doc.documentElement)).toEqual('Works!');
expect(doc.documentElement.textContent).toEqual('Works!');
platform.destroy();
});
platform2.bootstrapModule(ExampleModule2).then((moduleRef) => {
const doc = moduleRef.injector.get(DOCUMENT);
expect(getDOM().getText(doc.documentElement)).toEqual('Works too!');
expect(doc.documentElement.textContent).toEqual('Works too!');
platform2.destroy();
});
}));
@ -449,7 +449,7 @@ class HiddenModule {
const state = ref.injector.get(PlatformState);
const doc = ref.injector.get(DOCUMENT);
const title = getDOM().querySelector(doc, 'title');
expect(getDOM().getText(title)).toBe('Test App Title');
expect(title.textContent).toBe('Test App Title');
expect(state.renderToString()).toContain('<title>Test App Title</title>');
});
}));
@ -477,7 +477,7 @@ class HiddenModule {
const head = getDOM().getElementsByTagName(doc, 'head')[0];
const styles: any[] = head.children as any;
expect(styles.length).toBe(1);
expect(getDOM().getText(styles[0])).toContain('color: red');
expect(styles[0].textContent).toContain('color: red');
expect(getDOM().getAttribute(styles[0], 'ng-transition')).toBe('example-styles');
});
}));

View File

@ -69,25 +69,17 @@ export class WorkerDomAdapter extends DomAdapter {
nextSibling(el: any): Node { throw 'not implemented'; }
parentElement(el: any): Node { throw 'not implemented'; }
childNodes(el: any): Node[] { throw 'not implemented'; }
childNodesAsList(el: any): Node[] { throw 'not implemented'; }
clearNodes(el: any) { throw 'not implemented'; }
appendChild(el: any, node: any) { throw 'not implemented'; }
removeChild(el: any, node: any) { throw 'not implemented'; }
remove(el: any): Node { throw 'not implemented'; }
insertBefore(parent: any, el: any, node: any) { throw 'not implemented'; }
getText(el: any): string { throw 'not implemented'; }
setText(el: any, value: string) { throw 'not implemented'; }
getValue(el: any): string { throw 'not implemented'; }
setValue(el: any, value: string) { throw 'not implemented'; }
getChecked(el: any): boolean { throw 'not implemented'; }
createComment(text: string): any { throw 'not implemented'; }
createTemplate(html: any): HTMLElement { throw 'not implemented'; }
createElement(tagName: any, doc?: any): HTMLElement { throw 'not implemented'; }
createElementNS(ns: string, tagName: string, doc?: any): Element { throw 'not implemented'; }
createTextNode(text: string, doc?: any): Text { throw 'not implemented'; }
getHost(el: any): any { throw 'not implemented'; }
getDistributedNodes(el: any): Node[] { throw 'not implemented'; }
clone(node: Node): Node { throw 'not implemented'; }
getElementsByTagName(element: any, name: string): HTMLElement[] { throw 'not implemented'; }
addClass(element: any, className: string) { throw 'not implemented'; }
removeClass(element: any, className: string) { throw 'not implemented'; }