alsatian-fluent-assertions
Version:
Fluent assertions extension to Alsatian xUnit framework.
195 lines • 9.27 kB
JavaScript
"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