218 lines
6.2 KiB
TypeScript
218 lines
6.2 KiB
TypeScript
/**
|
|
* @license
|
|
* Copyright Google LLC All Rights Reserved.
|
|
*
|
|
* Use of this source code is governed by an MIT-style license that can be
|
|
* found in the LICENSE file at https://angular.io/license
|
|
*/
|
|
|
|
import {TI18n} from '@angular/core/src/render3/interfaces/i18n';
|
|
import {TNode} from '@angular/core/src/render3/interfaces/node';
|
|
import {TView} from '@angular/core/src/render3/interfaces/view';
|
|
|
|
import {isDOMElement, isDOMText, isTI18n, isTNode, isTView} from './is_shape_of';
|
|
|
|
|
|
/**
|
|
* Generic matcher which asserts that an object is of a given shape (`shapePredicate`) and that it
|
|
* contains a subset of properties.
|
|
*
|
|
* @param name Name of `shapePredicate` to display when assertion fails.
|
|
* @param shapePredicate Predicate which verifies that the object is of correct shape.
|
|
* @param expected Expected set of properties to be found on the object.
|
|
*/
|
|
export function matchObjectShape<T>(
|
|
name: string, shapePredicate: (obj: any) => obj is T,
|
|
expected: Partial<T> = {}): jasmine.AsymmetricMatcher<T> {
|
|
const matcher = function() {};
|
|
let _actual: any = null;
|
|
|
|
matcher.asymmetricMatch = function(actual: any) {
|
|
_actual = actual;
|
|
if (!shapePredicate(actual)) return false;
|
|
for (const key in expected) {
|
|
if (expected.hasOwnProperty(key) && !jasmine.matchersUtil.equals(actual[key], expected[key]))
|
|
return false;
|
|
}
|
|
return true;
|
|
};
|
|
matcher.jasmineToString = function() {
|
|
return `${toString(_actual, false)} != ${toString(expected, true)})`;
|
|
};
|
|
|
|
function toString(obj: any, isExpected: boolean) {
|
|
if (isExpected || shapePredicate(obj)) {
|
|
const props =
|
|
Object.keys(expected).map(key => `${key}: ${JSON.stringify((obj as any)[key])}`);
|
|
if (isExpected === false) {
|
|
// Push something to let the user know that there may be other ignored properties in actual
|
|
props.push('...');
|
|
}
|
|
return `${name}({${props.length === 0 ? '' : '\n ' + props.join(',\n ') + '\n'}})`;
|
|
} else {
|
|
return JSON.stringify(obj);
|
|
}
|
|
}
|
|
return matcher;
|
|
}
|
|
|
|
|
|
/**
|
|
* Asymmetric matcher which matches a `TView` of a given shape.
|
|
*
|
|
* Expected usage:
|
|
* ```
|
|
* expect(tNode).toEqual(matchTView({type: TViewType.Root}));
|
|
* expect({
|
|
* node: tNode
|
|
* }).toEqual({
|
|
* node: matchTNode({type: TViewType.Root})
|
|
* });
|
|
* ```
|
|
*
|
|
* @param expected optional properties which the `TView` must contain.
|
|
*/
|
|
export function matchTView(expected?: Partial<TView>): jasmine.AsymmetricMatcher<TView> {
|
|
return matchObjectShape('TView', isTView, expected);
|
|
}
|
|
|
|
/**
|
|
* Asymmetric matcher which matches a `TNode` of a given shape.
|
|
*
|
|
* Expected usage:
|
|
* ```
|
|
* expect(tNode).toEqual(matchTNode({type: TNodeType.Element}));
|
|
* expect({
|
|
* node: tNode
|
|
* }).toEqual({
|
|
* node: matchTNode({type: TNodeType.Element})
|
|
* });
|
|
* ```
|
|
*
|
|
* @param expected optional properties which the `TNode` must contain.
|
|
*/
|
|
export function matchTNode(expected?: Partial<TNode>): jasmine.AsymmetricMatcher<TNode> {
|
|
return matchObjectShape('TNode', isTNode, expected);
|
|
}
|
|
|
|
|
|
/**
|
|
* Asymmetric matcher which matches a `T18n` of a given shape.
|
|
*
|
|
* Expected usage:
|
|
* ```
|
|
* expect(tNode).toEqual(matchT18n({vars: 0}));
|
|
* expect({
|
|
* node: tNode
|
|
* }).toEqual({
|
|
* node: matchT18n({vars: 0})
|
|
* });
|
|
* ```
|
|
*
|
|
* @param expected optional properties which the `TI18n` must contain.
|
|
*/
|
|
export function matchTI18n(expected?: Partial<TI18n>): jasmine.AsymmetricMatcher<TI18n> {
|
|
return matchObjectShape('TI18n', isTI18n, expected);
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
* Asymmetric matcher which matches a DOM Element.
|
|
*
|
|
* Expected usage:
|
|
* ```
|
|
* expect(div).toEqual(matchT18n('div', {id: '123'}));
|
|
* expect({
|
|
* node: div
|
|
* }).toEqual({
|
|
* node: matchT18n('div', {id: '123'})
|
|
* });
|
|
* ```
|
|
*
|
|
* @param expectedTagName optional DOM tag name.
|
|
* @param expectedAttributes optional DOM element properties.
|
|
*/
|
|
export function matchDomElement(
|
|
expectedTagName: string|undefined = undefined,
|
|
expectedAttrs: {[key: string]: string|null} = {}): jasmine.AsymmetricMatcher<Element> {
|
|
const matcher = function() {};
|
|
let _actual: any = null;
|
|
|
|
matcher.asymmetricMatch = function(actual: any) {
|
|
_actual = actual;
|
|
if (!isDOMElement(actual)) return false;
|
|
if (expectedTagName && (expectedTagName.toUpperCase() !== actual.tagName.toUpperCase())) {
|
|
return false;
|
|
}
|
|
if (expectedAttrs) {
|
|
for (const attrName in expectedAttrs) {
|
|
if (expectedAttrs.hasOwnProperty(attrName)) {
|
|
const expectedAttrValue = expectedAttrs[attrName];
|
|
const actualAttrValue = actual.getAttribute(attrName);
|
|
if (expectedAttrValue !== actualAttrValue) {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
};
|
|
matcher.jasmineToString = function() {
|
|
let actualStr = isDOMElement(_actual) ? `<${_actual.tagName}${toString(_actual.attributes)}>` :
|
|
JSON.stringify(_actual);
|
|
let expectedStr = `<${expectedTagName || '*'}${
|
|
Object.keys(expectedAttrs).map(key => ` ${key}=${JSON.stringify(expectedAttrs[key])}`)}>`;
|
|
return `[${actualStr} != ${expectedStr}]`;
|
|
};
|
|
|
|
function toString(attrs: NamedNodeMap) {
|
|
let text = '';
|
|
for (let i = 0; i < attrs.length; i++) {
|
|
const attr = attrs[i];
|
|
text += ` ${attr.name}=${JSON.stringify(attr.value)}`;
|
|
}
|
|
return text;
|
|
}
|
|
|
|
|
|
return matcher;
|
|
}
|
|
|
|
/**
|
|
* Asymmetric matcher which matches DOM text node.
|
|
*
|
|
* Expected usage:
|
|
* ```
|
|
* expect(div).toEqual(matchDomText('text'));
|
|
* expect({
|
|
* node: div
|
|
* }).toEqual({
|
|
* node: matchDomText('text')
|
|
* });
|
|
* ```
|
|
*
|
|
* @param expectedText optional DOM text.
|
|
*/
|
|
export function matchDomText(expectedText: string|undefined = undefined):
|
|
jasmine.AsymmetricMatcher<Text> {
|
|
const matcher = function() {};
|
|
let _actual: any = null;
|
|
|
|
matcher.asymmetricMatch = function(actual: any) {
|
|
_actual = actual;
|
|
if (!isDOMText(actual)) return false;
|
|
if (expectedText && (expectedText !== actual.textContent)) {
|
|
return false;
|
|
}
|
|
return true;
|
|
};
|
|
matcher.jasmineToString = function() {
|
|
let actualStr = isDOMText(_actual) ? `#TEXT: ${JSON.stringify(_actual.textContent)}` :
|
|
JSON.stringify(_actual);
|
|
let expectedStr = `#TEXT: ${JSON.stringify(expectedText)}`;
|
|
return `[${actualStr} != ${expectedStr}]`;
|
|
};
|
|
|
|
return matcher;
|
|
} |