2017-12-08 00:37:22 +00:00
|
|
|
/**
|
|
|
|
* Copyright 2017 Google Inc. All rights reserved.
|
|
|
|
*
|
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
* you may not use this file except in compliance with the License.
|
|
|
|
* You may obtain a copy of the License at
|
|
|
|
*
|
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
*
|
|
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
* See the License for the specific language governing permissions and
|
|
|
|
* limitations under the License.
|
|
|
|
*/
|
|
|
|
|
|
|
|
module.exports = class Matchers {
|
|
|
|
constructor(customMatchers = {}) {
|
|
|
|
this._matchers = {};
|
|
|
|
Object.assign(this._matchers, DefaultMatchers);
|
|
|
|
Object.assign(this._matchers, customMatchers);
|
|
|
|
this.expect = this.expect.bind(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
addMatcher(name, matcher) {
|
|
|
|
this._matchers[name] = matcher;
|
|
|
|
}
|
|
|
|
|
|
|
|
expect(value) {
|
|
|
|
return new Expect(value, this._matchers);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
class Expect {
|
|
|
|
constructor(value, matchers) {
|
|
|
|
this.not = {};
|
|
|
|
this.not.not = this;
|
|
|
|
for (const matcherName of Object.keys(matchers)) {
|
|
|
|
const matcher = matchers[matcherName];
|
2018-04-26 18:13:22 +00:00
|
|
|
this[matcherName] = applyMatcher.bind(null, matcherName, matcher, false /* inverse */, value);
|
|
|
|
this.not[matcherName] = applyMatcher.bind(null, matcherName, matcher, true /* inverse */, value);
|
2017-12-08 00:37:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
function applyMatcher(matcherName, matcher, inverse, value, ...args) {
|
|
|
|
const result = matcher.call(null, value, ...args);
|
2018-04-26 18:13:22 +00:00
|
|
|
const message = `expect.${inverse ? 'not.' : ''}${matcherName} failed` + (result.message ? `: ${result.message}` : '');
|
2018-05-31 23:53:51 +00:00
|
|
|
if (result.pass === inverse)
|
|
|
|
throw new Error(message);
|
2017-12-08 00:37:22 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const DefaultMatchers = {
|
|
|
|
toBe: function(value, other, message) {
|
2017-12-15 22:14:27 +00:00
|
|
|
message = message || `${value} == ${other}`;
|
2017-12-08 00:37:22 +00:00
|
|
|
return { pass: value === other, message };
|
|
|
|
},
|
|
|
|
|
|
|
|
toBeFalsy: function(value, message) {
|
2017-12-15 22:14:27 +00:00
|
|
|
message = message || `${value}`;
|
2017-12-08 00:37:22 +00:00
|
|
|
return { pass: !value, message };
|
|
|
|
},
|
|
|
|
|
|
|
|
toBeTruthy: function(value, message) {
|
2017-12-15 22:14:27 +00:00
|
|
|
message = message || `${value}`;
|
2017-12-08 00:37:22 +00:00
|
|
|
return { pass: !!value, message };
|
|
|
|
},
|
|
|
|
|
|
|
|
toBeGreaterThan: function(value, other, message) {
|
2017-12-15 22:14:27 +00:00
|
|
|
message = message || `${value} > ${other}`;
|
2017-12-08 00:37:22 +00:00
|
|
|
return { pass: value > other, message };
|
|
|
|
},
|
|
|
|
|
|
|
|
toBeGreaterThanOrEqual: function(value, other, message) {
|
2017-12-15 22:14:27 +00:00
|
|
|
message = message || `${value} >= ${other}`;
|
2017-12-08 00:37:22 +00:00
|
|
|
return { pass: value >= other, message };
|
|
|
|
},
|
|
|
|
|
|
|
|
toBeLessThan: function(value, other, message) {
|
2017-12-15 22:14:27 +00:00
|
|
|
message = message || `${value} < ${other}`;
|
2017-12-08 00:37:22 +00:00
|
|
|
return { pass: value < other, message };
|
|
|
|
},
|
|
|
|
|
|
|
|
toBeLessThanOrEqual: function(value, other, message) {
|
2017-12-15 22:14:27 +00:00
|
|
|
message = message || `${value} <= ${other}`;
|
2017-12-08 00:37:22 +00:00
|
|
|
return { pass: value <= other, message };
|
|
|
|
},
|
|
|
|
|
|
|
|
toBeNull: function(value, message) {
|
2017-12-15 22:14:27 +00:00
|
|
|
message = message || `${value} == null`;
|
2017-12-08 00:37:22 +00:00
|
|
|
return { pass: value === null, message };
|
|
|
|
},
|
|
|
|
|
|
|
|
toContain: function(value, other, message) {
|
2017-12-15 22:14:27 +00:00
|
|
|
message = message || `${value} ⊇ ${other}`;
|
2017-12-08 00:37:22 +00:00
|
|
|
return { pass: value.includes(other), message };
|
|
|
|
},
|
|
|
|
|
|
|
|
toEqual: function(value, other, message) {
|
2018-03-15 18:49:53 +00:00
|
|
|
const valueJson = stringify(value);
|
|
|
|
const otherJson = stringify(other);
|
|
|
|
message = message || `${valueJson} ≈ ${otherJson}`;
|
|
|
|
return { pass: valueJson === otherJson, message };
|
2017-12-08 00:37:22 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
toBeCloseTo: function(value, other, precision, message) {
|
|
|
|
return {
|
|
|
|
pass: Math.abs(value - other) < Math.pow(10, -precision),
|
|
|
|
message
|
|
|
|
};
|
2018-08-09 23:51:12 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
toBeInstanceOf: function(value, other, message) {
|
|
|
|
message = message || `${value.constructor.name} instanceof ${other.name}`;
|
|
|
|
return { pass: value instanceof other, message };
|
|
|
|
},
|
2017-12-08 00:37:22 +00:00
|
|
|
};
|
2018-03-15 18:49:53 +00:00
|
|
|
|
|
|
|
function stringify(value) {
|
|
|
|
function stabilize(key, object) {
|
|
|
|
if (typeof object !== 'object' || object === undefined || object === null)
|
|
|
|
return object;
|
|
|
|
const result = {};
|
|
|
|
for (const key of Object.keys(object).sort())
|
|
|
|
result[key] = object[key];
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2019-02-06 21:49:14 +00:00
|
|
|
return JSON.stringify(stabilize(null, value), stabilize, 2);
|
2018-03-15 18:49:53 +00:00
|
|
|
}
|