UNPKG

alsatian-fluent-assertions

Version:

Fluent assertions extension to Alsatian xUnit framework.

195 lines 9.27 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const simple_matcher_with_helpers_1 = require("./simple-matcher-with-helpers"); class ElementsMatcher extends simple_matcher_with_helpers_1.SimpleMatcherWithHelpers { constructor(actualValue, nextValue, initial, prevCore, ctxt) { super(actualValue, nextValue, initial, prevCore, ctxt); } hasElements(expected, location = "contains" /* contains */, elMode = "normal" /* normal */) { this.setCurrentNode(this.hasElements.name, `${location}, ${elMode}`); this._assertHasElements(this.actualValue, expected, location, elMode, []); return this.generateFluentState(this.actualValue, null, false); } allSatisfy(predicate) { this.setCurrentNode(this.allSatisfy.name); this._assertActualArray(); this._assertExpectationIsFunction(predicate); const counterExamples = this.actualValue.filter((e) => !predicate(e)); if (this.invertedContext && this.actualValue.length === 0) { return; // .not.allSatisfy for empty array is always true. } if (this.maybeInvert(!!counterExamples.length)) { this.specError(`should all${this.negation}satisfy predicate`, predicate, counterExamples); } return this.generateFluentState(this.actualValue, null, false); } anySatisfy(predicate) { this.setCurrentNode(this.anySatisfy.name); this._assertActualArray(); this._assertExpectationIsFunction(predicate); const result = this.actualValue.some(predicate); if (this.maybeInvert(!result)) { this.specError(`some should${this.negation}satisfy predicate`, predicate, this.actualValue); } return this.generateFluentState(this.actualValue, null, false); } hasFirst() { this.setCurrentNode(this.hasFirst.name); const failMsg = `should${this.negation}have one or more elements`; this._assertHasNth(0, this.actualValue, `[an array of length >= 1]`, failMsg); return this.generateFluentState(this.actualValue, this.actualValue[0], false, true); } hasLast() { this.setCurrentNode(this.hasLast.name); const failMsg = `should${this.negation}have one or more elements`; this._assertHasNth(0, this.actualValue, `[an array of length >= 1]`, failMsg); return this.generateFluentState(this.actualValue, this.actualValue[this.actualValue.length - 1], false, true); } hasNth(n) { this.setCurrentNode(this.hasNth.name, `${n}`); if (typeof n !== "number") { this.specError("parameter should be a number", "[a number]", this.id(n)); } const index = +n; const failMsg = `should${this.negation}have ${index + 1} or more elements`; this._assertHasNth(index, this.actualValue, `[an array of length > ${index}]`, failMsg); return this.generateFluentState(this.actualValue, this.actualValue[index], false, true); } _assertHasNth(n, actual, expected, failMsg) { this.setCurrentNode(this.hasFirst.name); this._assertActualArrayOrString(); if (this.maybeInvert(this.actualValue.length <= n)) { this.specError(failMsg, expected, this.actualValue); } return this.generateFluentState(this.actualValue, this.actualValue[n], false); } _assertHasElements(actual, expected, location, elMode, path) { if (!(actual instanceof Array)) { this.specError("not an array type", expected, this.actualValue); } if (!this.invertedContext && expected.length > this.actualValue.length) { this.specError("expected array is longer than the actual array.", expected, this.actualValue); } if (location === "contains" /* contains */) { this._assertContainsElements(expected, location, elMode, path); } else if (location === "sequentialContains" /* sequentialContains */) { this._assertSequentialHasElements(expected, location, elMode, path); } else if (location === "startsWith" /* startsWith */) { this._assertHasElementsFrom(expected, 0, location, elMode, path); } else if (location === "endsWith" /* endsWith */) { const start = this.actualValue.length - expected.length; this._assertHasElementsFrom(expected, start, location, elMode, path); } else { this.specError(`Unknown LocationMode: ${location}.`, "[A known LocationMode]", `${location}`); } } _assertHasElementsFrom(expected, start, location, elMode, path) { for (let i = start; i < expected.length; i++) { this._assertElement(i, this.actualValue[i], expected[i - start], location, elMode); } } _assertContainsElements(expected, location, elMode, path) { for (let i = 0; i < expected.length; i++) { let found = false; for (const value of this.actualValue) { if (this._matchElement(value, expected[i], location, elMode)) { found = true; break; } } if (this.maybeInvert(!found)) { this.specError(`should${this.negation}contain expected elements (index: ${i}, value: ${expected[i]})`, expected, this.actualValue); } } } _assertSequentialHasElements(expected, location, elMode, path) { const lenDelta = this.actualValue.length - expected.length; let hasSeq; let anyHas = false; for (let start = 0; start <= lenDelta; start++) { hasSeq = true; for (let i = start; i < start + lenDelta; i++) { if (!this._matchElement(this.actualValue[i], expected[i - start], location, elMode)) { hasSeq = false; break; } } anyHas = anyHas || hasSeq; if (anyHas && !this.invertedContext) { // if we're inverted, check all, else break. break; } } if (this.maybeInvert(!anyHas)) { this.specError(`should${this.negation}find sequence in array`, expected, this.actualValue); } } _assertElement(index, actual, expected, location, elMode) { if (this.maybeInvert(!this._matchElement(actual, expected, location, elMode))) { this.specError(`element at index ${index} should${this.negation}match asserted element`, expected, this.actualValue); } } _matchElement(actual, expected, location, elMode) { if (elMode === "literal" /* literal */) { return actual === expected; } return this._heuristicMatch(actual, expected, location, elMode); } _heuristicMatch(actual, expected, location, elMode) { if (expected instanceof RegExp) { if (typeof actual === "string") { return expected.test(actual); } else if (actual instanceof RegExp && actual.toString() === expected.toString()) { return true; } return false; } else if (expected instanceof Function) { try { if (elMode === "asserts" /* asserts */) { return expected(this.wrap(actual)); } return expected(actual); } catch (err) { return false; } /** What would recursion even mean WRT a data structure where element position * is semantic? If you ask, "does the array contain (sequentially, from left/right, * at all, etc)," what does that mean for a sub-array? I think we should require * any recursion to be explicitly spelled out via lambda assertions. */ /* } else if (expected instanceof Array) { let newPath = path.slice(0); newPath.push(""+index); this._assertHasElements(actual, expected, location, elMode, path); */ } else { return this._deeplyEquals(actual, expected, "strictly" /* strictly */); } } _assertActualArray() { if (!(this.actualValue instanceof Array)) { this.specError("should be an array type", "an array type", this.id(this.actualValue)); } } _assertActualArrayOrString() { if (!(this.actualValue instanceof Array || typeof this.actualValue === "string")) { this.specError("should be an array or string type", "an array type", this.id(this.actualValue)); } } _assertExpectationIsFunction(expectation) { if (!(expectation instanceof Function)) { this.specError("expectation should be a function", "a function", this.id(this.actualValue)); } } } exports.ElementsMatcher = ElementsMatcher; //# sourceMappingURL=elements-matcher.js.map