UNPKG

alsatian-fluent-assertions

Version:

Fluent assertions extension to Alsatian xUnit framework.

167 lines 7.69 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const alsatian_1 = require("alsatian"); const nested_properties_match_error_1 = require("../errors/nested-properties-match-error"); const elements_matcher_1 = require("./elements-matcher"); class PropertiesMatcher extends elements_matcher_1.ElementsMatcher { constructor(actualValue, nextValue, initial, prevCore, ctxt) { super(actualValue, nextValue, initial, prevCore, ctxt); } hasProperties(expected, mode = "normal" /* normal */) { this.setCurrentNode(this.hasProperties.name, null); this._properties(this.actualValue, expected, [], mode); return this.generateFluentState(this.actualValue, null, false); } hasAll(expected, mode = "normal" /* normal */) { this.setCurrentNode(this.hasAll.name, null); this._properties(this.actualValue, expected, [], mode); return this.generateFluentState(this.actualValue, null, false); } hasKeys(expectedKeys) { this.setCurrentNode(this.hasKeys.name, null); if (this.nullOrUndefined(this.actualValue)) { if (!this.invertedContext) { this.specError(`should be defined`, undefined, undefined); } return; // .not.hasKeys always passes when target not defined. } if (!(expectedKeys instanceof Array)) { this.specError(`param expectedKeys should be an array type`, "[an array type]", this.id(expectedKeys)); } if (this.maybeInvert(!expectedKeys.every(k => typeof this.actualValue[k] !== "undefined"))) { this.specError(`should${this.negation}contain all`, expectedKeys, this.actualValue); } return this.generateFluentState(this.actualValue, null, false); } // tslint:disable-next-line /** Asserts that the actual has the expected properties (recursive). */ _properties(actualObject, expectedObject, path, mode) { const notDefined = !this._assertNestedPropertiesDefined(actualObject, expectedObject, path); if (this.invertedContext && notDefined) { // notDefined when inverted is the same as saying, "it doesn't have these properties," // so return without error. return; } const keys = Object.keys(expectedObject); /*tslint:disable:forin*/ for (const i in keys) { /*tslint:enable:forin*/ const k = keys[i]; const curPath = path.slice(0); // clone curPath.push(k); const expected = expectedObject[k]; const actual = actualObject[k]; this._assertPropertyByType(k, actual, expected, curPath, mode); } } _assertNestedPropertiesDefined(actualObject, expectedObject, path) { if (this.nullOrUndefined(actualObject)) { this._throwIfUnnecessarilyUndefined(actualObject, expectedObject, path); return false; } return true; } // tslint:disable-next-line /** Only throws if undefined props/obj isn't warranted. */ _throwIfUnnecessarilyUndefined(actualObject, expectedObject, path) { // throw iff an undefined property isn't simply satisfying a prior negation (e.g., not.has()). if (!this.invertedContext) { if (path.length > 0) { const prop = path[path.length - 1]; const fpath = this.formatKeyPath(path); const msg = `property '${prop}' should be defined at path '${fpath}'`; this.specError(msg, undefined, undefined); } else { this.specError("expected object should be defined", undefined, undefined); } } } _assertPropertyByType(k, actual, expected, curPath, mode) { if (mode !== "literal" /* literal */ && typeof expected === "function") { this._assertFnProperty(k, expected, actual, curPath, mode); } else if (mode !== "literal" /* literal */ && expected instanceof RegExp) { this._assertRegExpProperty(k, expected, actual, curPath); } else if (expected !== null && typeof expected === "object" && Object.keys(expected).length > 0 // not a no-op ) { this._properties(actual, expected, curPath, mode); } else if (this.maybeInvert(expected !== actual)) { const fpath = this.formatKeyPath(curPath); const msg = `property ${k} at path '${fpath}' should${this.negation}equal`; this.specError(msg, expected, actual); } } formatKeyPath(path) { path.unshift("$"); return path.join("."); } // tslint:disable-next-line /** * A properties assertion function can fail by falsy return value, or by * throwing an error (perhaps from nested assertions). */ _assertFnProperty(key, assertion, actual, path, matchMode) { let check = null; try { if (matchMode === "asserts" /* asserts */) { check = assertion(this.wrap(actual)); if (typeof check === "boolean") { this._assertFnBoolean(check, assertion, actual, path); } } else { check = assertion(actual); this._assertFnBoolean(check, assertion, actual, path); } } catch (err) { this._failFnError(err, path); } } _failFnError(err, path) { if (err instanceof alsatian_1.MatchError) { throw new nested_properties_match_error_1.NestedPropertiesMatchError(this, "failed nested expectation", this.formatKeyPath(path), err); } else { throw new nested_properties_match_error_1.NestedPropertiesMatchError(this, "threw unexpected error", this.formatKeyPath(path), err); } } _assertFnBoolean(check, assertion, actual, path) { const fpath = this.formatKeyPath(path); let msg = `Property at path '${fpath}': `; if (typeof check === "boolean" && this.maybeInvert(!check)) { (msg = msg + `should${this.negation}satisfy lambda assertion`), this.specError(msg, this.getFnString(assertion), actual); } else if (this.invert) { (msg = msg + "expected lambda to return false, or yield a failed nested expectation or error"), this.specError(msg, this.getFnString(assertion), actual); } } _assertRegExpProperty(key, regexp, actual, path) { const kpath = this.formatKeyPath(path); if (actual instanceof RegExp) { if (this.maybeInvert(actual.toString() !== regexp.toString())) { const msg = `regular expressions at path '${kpath}' should${this.negation}be equal`; this.specError(msg, this.id(regexp), this.id(actual)); } } else if (typeof actual !== "string") { const msg = `expected type 'string' for regexp match at path '${kpath}'`; this.specError(msg, "string", typeof actual); } else if (this.maybeInvert(!regexp.test(actual))) { const msg = `regular expression at path '${kpath}' should${this.negation}match`; this.specError(msg, this.id(regexp), actual); } } } exports.PropertiesMatcher = PropertiesMatcher; //# sourceMappingURL=properties-matcher.js.map