New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

assertly

Package Overview
Dependencies
Maintainers
1
Versions
16
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

assertly - npm Package Compare versions

Comparing version 1.0.0-beta.3 to 1.0.0-rc.0

docs/Add-ons.md

456

Assert.js
'use strict';
const inspect = require('./inspect').inspect;
const isNode = typeof window === 'undefined';
const inspect = require(isNode ? 'util' : './inspect').inspect;
const arraySlice = Array.prototype.slice;
const toString = Object.prototype.toString;
const toStringMap = {};
const toStringRe = /^\[object ([^\]]+)]$/;
const useTypeOfRe = /booolean|number|string|undefined/;
function toArray (value) {
if (!value) {
return [];
}
if (!Array.isArray(value)) {
value = [value];
}
return value;
}
/**

@@ -32,12 +17,14 @@ * @class Assert

constructor (value) {
constructor (value, previous) {
let me = this;
me.modifiers = {};
me.state = me._getEntry('$', false);
me.value = value;
if (!me.state) {
me._modifiers = {};
me._previous = previous || null;
me._state = me._getEntry('$', false);
if (!me._state) {
me.constructor.setup();
me.state = me._getEntry('$');
me._state = me._getEntry('$');
}

@@ -51,5 +38,10 @@ }

if (expected) {
let result = !me.def.fn.call(me, me.value, ...expected);
try {
let result = !me._def.fn.call(me, me.value, ...expected);
me.failed = me.modifiers.not ? !result : result;
me.failed = me._modifiers.not ? !result : result;
}
catch (e) {
me.failed = e;
}
}

@@ -59,3 +51,3 @@ }

before (def) {
this.def = def;
this._def = def;
}

@@ -66,14 +58,14 @@

let A = me.constructor;
let async = me.async;
let previous = A._previous;
let async = me._async;
let previous = A._current;
if (previous && !async) {
me.async = previous.async.then(() => {
me._async = previous._async.then(() => {
me.begin(expected);
return me.async; // this is reassigned below
return me._async; // this is reassigned below
},
e => {
if (A._previous === me) {
if (A._current === me) {
// If this is the end of the line of assertions, clean things up
A._previous = null;
A._current = null;
}

@@ -85,12 +77,12 @@

// This assert was made after some other async asserts, so get in line...
A._previous = me;
A._current = me;
}
else if (async || A._isPromise(me.value) || A._isPromise(...expected)) {
me.async = A.Promise.all(expected.concat(me.value)).then(values => {
else if (async || Util.isPromise(me.value) || Util.isPromise(...expected)) {
me._async = A.Promise.all(expected.concat(me.value)).then(values => {
me.expected = values;
me.value = values.pop();
if (A._previous === me) {
if (A._current === me) {
// If this is the end of the line of assertions, clean things up
A._previous = null;
A._current = null;
}

@@ -102,4 +94,4 @@

e => {
if (A._previous === me) {
A._previous = null;
if (A._current === me) {
A._current = null;
}

@@ -113,3 +105,3 @@

// This is the first async assert, so start the line with it...
A._previous = me;
A._current = me;
}

@@ -128,3 +120,3 @@ }

let me = this;
let def = me.def;
let def = me._def;
let fn = def.explain;

@@ -139,3 +131,3 @@ let exp = me.expected;

let mods = Object.keys(me.modifiers);
let mods = Object.keys(me._modifiers);

@@ -159,6 +151,6 @@ me.assertions = mods;

let me = this;
let async = me.async;
let async = me._async;
let A = me.constructor;
let conjunction = {
and: new A(me.value)
and: new A(me.value, me)
};

@@ -173,3 +165,3 @@

prop (name) {
get (name) {
let v = this.value;

@@ -207,3 +199,3 @@

fn (actual, expected) {
let te = A.typeOf(expected);
let te = Util.typeOf(expected);
let re = te === 'regexp';

@@ -215,3 +207,3 @@

let t = A.typeOf(actual);
let t = Util.typeOf(actual);

@@ -226,3 +218,3 @@ if (re) {

if (typeof type === 'string') {
this.expectation = type;
this.expectation = type; // removes the quotes on "type"
}

@@ -246,3 +238,3 @@ }

this.assertions.pop(); // remove "approximately"
this.assertions.pop(); // remove "approx"
this.expectation = `${A.print(expected)} ± ${A.print(epsilon)}`;

@@ -271,3 +263,3 @@ }

if (t === 'string' || A.isArrayLike(actual)) {
if (t === 'string' || Util.isArrayLike(actual)) {
ret = !actual.length;

@@ -289,5 +281,9 @@ }

'to.equal' (actual, expected) {
return A.isEqual(actual, expected);
return Util.isEqual(actual, expected);
},
'falsy' (actual) {
return !actual;
},
'greaterThan,gt,above' (actual, expected) {

@@ -307,4 +303,4 @@ return actual > expected;

let ok = true;
let only = this.modifiers.only;
let own = this.modifiers.own;
let only = this._modifiers.only;
let own = this._modifiers.own;
let keyMap = only && {};

@@ -370,6 +366,6 @@

fn (object, property, value) {
let only = this.modifiers.only;
let only = this._modifiers.only;
//TODO deep properties "foo.bar.baz"
if (this.modifiers.own) {
if (this._modifiers.own) {
if (!object.hasOwnProperty(property)) {

@@ -412,3 +408,3 @@ return false;

fn (actual, expected) {
return A.isEqual(actual, expected, true);
return Util.isEqual(actual, expected, true);
},

@@ -444,18 +440,4 @@ explain () {

'truthy,ok': {
fn (actual) {
return !!actual;
},
explain () {
let assertions = this.assertions;
let not = this.modifiers.not;
assertions.pop(); // remove "truthy"
if (not) {
assertions.splice(assertions.indexOf('not'), 1);
}
this.expectation = not ? 'falsy' : 'truthy';
}
'truthy,ok' (actual) {
return !!actual;
},

@@ -471,3 +453,3 @@

else if (typeof min === 'string') {
let m = this.constructor.rangeRe.exec(min);
let m = Util.rangeRe.exec(min);
lo = m[1];

@@ -493,3 +475,2 @@ min = parseFloat(m[2]);

static normalize (registry) {
let A = this;
let ret = {};

@@ -508,28 +489,28 @@

def = {
before: def // to: 'be' OR 'to.have': ['only', 'own']
after: def // to: 'be' OR 'to.have': ['only', 'own']
};
}
let after = toArray(def.after);
let before = toArray(def.before);
let after = Util.toArray(def.after);
let before = Util.toArray(def.before);
if (A.tupleRe.test(name)) {
if (Util.tupleRe.test(name)) {
// 'afters.name,alias.befores'
let tuples = name.split(A.tupleRe);
let a = tuples.shift();
let tuples = name.split(Util.tupleRe);
let b = tuples.shift();
name = tuples.shift();
let b = tuples[0];
let a = tuples[0];
a = a && a.split(',');
b = b && b.split(',');
a = a && a.split(',');
before = b ? before.concat(b) : before;
after = a ? after.concat(a) : after;
after = a ? after.concat(a) : after;
before = b ? before.concat(b) : before;
}
if (!after.length) {
after = [def.fn ? (name === 'be' ? 'to' : 'be') : '$'];
if (!before.length) {
before = [def.fn ? (name === 'be' ? 'to' : 'be') : '$'];
}
if (after.indexOf('to') > -1 && after.indexOf('not') < 0) {
after = ['not'].concat(after);
if (before.indexOf('to') > -1 && before.indexOf('not') < 0) {
before = ['not'].concat(before);
}

@@ -560,4 +541,11 @@

const P = A.prototype;
const registry = A.normalize((typeof name === 'string') ? { [name] : def } : name);
const t = typeof name;
if (t === 'function') {
name.call(A, A, Util);
return;
}
const registry = A.normalize((t === 'string') ? { [name]: def } : name);
for (name in registry) {

@@ -571,5 +559,15 @@ def = registry[name];

let entry = A._getEntry(s, name);
entry.add(def.before);
entry.add(def.after);
if (fn) {
let was = entry.def;
if (was) {
def.fn._super = was.fn;
if (def.explain) {
def.explain._super = was.explain || null;
}
}
entry.def = def;
entry.fn = fn;

@@ -582,4 +580,4 @@ A._addFn(P, s, fn);

for (let a of def.after) {
entry = A._getEntry(a);
for (let b of def.before) {
entry = A._getEntry(b);
entry.add(s);

@@ -598,2 +596,26 @@ }

static print (obj, options) {
let t = Util.typeOf(obj);
if (t === 'error') {
if (obj.message) {
return `${obj.name}(${this.print(obj.message)})`;
}
return `${obj.name}`;
}
else if (t === 'arguments') {
obj = arraySlice.call(obj);
}
else if (t === 'number') {
if (isNaN(obj)) {
return 'NaN';
}
if (!isFinite(obj)) {
return obj < 0 ? '-∞' : '∞';
}
}
return Util.inspect(obj, options);
}
static report (assertion) {

@@ -623,3 +645,3 @@ let failure = assertion.failed;

static wrapAssertion (def) {
return function (...expected) {
let wrap = function (...expected) {
let me = this;

@@ -634,2 +656,6 @@

};
wrap.def = def;
return wrap;
}

@@ -641,3 +667,3 @@

/**
* This method accepts a modifier (such as `'not'`) and updates the `modifiers`
* This method accepts a modifier (such as `'not'`) and updates the `_modifiers`
* map accordingly. This method will throw an `Error` if modifier cannot be used

@@ -649,7 +675,7 @@ * in the current state.

_applyModifier (modifier) {
let state = this.state;
let state = this._state;
this.state = state = state.get(modifier);
this._state = state = state.get(modifier);
this.modifiers[state.name] = true;
this._modifiers[state.name] = true;
}

@@ -663,3 +689,3 @@

if (Object.getOwnPropertyDescriptor(target, name)) {
throw new Error(`Expectation modifier "${name}" already defined`);
throw new Error(`Modifier "${name}" already defined`);
}

@@ -684,2 +710,4 @@

Object.defineProperty(target, modifier, {
configurable: true,
get () {

@@ -746,8 +774,38 @@ let bound = function (...args) {

}
if (name !== '$' && !Util.validNameRe.test(name)) {
throw new Error(`Cannot register invalid name "${name}"`);
}
let A = this;
let registry = A.hasOwnProperty('registry') ? A.registry : (A.registry = {});
if (!A.hasOwnProperty('registry')) {
A.registry = {};
A.forbidden = {};
let names = [];
for (let C = A; ; C = Object.getPrototypeOf(C)) {
names.push(...Object.getOwnPropertyNames(C.prototype));
if (C === Assert) {
break;
}
}
names.sort();
for (let key of names) {
if (Util.validNameRe.test(key)) {
A.forbidden[key] = true; // all keys (even inherited ones)
}
}
}
let registry = A.registry;
let entry = registry[name];
if (!entry && autoCreate !== false) {
if (A.forbidden[name]) {
throw new Error(`Cannot redefine "${name}"`);
}
registry[name] = entry = new A.Modifier(A, name, canonicalName);

@@ -758,5 +816,80 @@ }

}
} // Assert
static isArrayLike (v) {
if (!v || this._notArrayLikeRe.test(typeof v)) {
Assert.Modifier = class {
constructor (owner, name, canonicalName) {
this.all = [];
this.map = {};
this.name = name;
this.owner = owner;
this.canonicalName = canonicalName || name;
this.alias = this.canonicalName !== name;
}
add (name) {
if (!name) {
return;
}
let map = this.map;
let all = this.all;
let names = (typeof name === 'string') ? [name] : name;
for (let s of names) {
if (!map[s]) {
map[s] = true;
all.push(s);
}
}
}
get (name) {
if (!this.map[name]) {
let msg = `Expectation cannot use "${name}" `;
if (this.name === '$') {
msg += 'immediately';
} else {
msg += `after "${this.name}"`;
}
throw new Error(msg);
}
return this.owner.registry[name];
}
};
Assert.Promise = (typeof Promise !== 'undefined') && Promise.resolve && Promise;
// This prevents shape changes but also is used to generate the list of forbidden
// names:
Object.assign(Assert.prototype, {
_async: null,
_def: null,
actual: null,
assertions: null,
expectation: null,
expected: null,
failed: null,
value: null
});
const Util = Assert.Util = {
notArrayLikeRe: /^(?:function|string)$/,
promiseTypesRe: /^(?:function|object)$/,
rangeRe: /^\s*([\[(])\s*(\d+),\s*(\d+)\s*([\])])\s*$/,
tupleRe: /[\.|\/]/,
validNameRe: /^[a-z][a-z0-9_]*$/i,
inspect: inspect,
isArrayLike (v) {
if (!v || Util.notArrayLikeRe.test(typeof v)) {
return false;

@@ -773,5 +906,5 @@ }

return false;
}
},
static isEqual (o1, o2, strict) {
isEqual (o1, o2, strict) {
if (o1 === o2) {

@@ -800,4 +933,4 @@ return true;

let t1 = this.typeOf(o1);
let t2 = this.typeOf(o2);
let t1 = Util.typeOf(o1);
let t2 = Util.typeOf(o2);

@@ -856,3 +989,3 @@ if (t1 === t2) {

k = keys1[i];
if (!this.isEqual(o1[k], o2[k], strict)) {
if (!Util.isEqual(o1[k], o2[k], strict)) {
return false;

@@ -863,7 +996,7 @@ }

return true;
}
},
static _isPromise (...obj) {
isPromise (...obj) {
for (let o of obj) {
if (o && this.promiseTypesRe.test(typeof o) && typeof o.then === 'function') {
if (o && Util.promiseTypesRe.test(typeof o) && typeof o.then === 'function') {
return true;

@@ -874,29 +1007,25 @@ }

return false;
}
},
static print (obj, options) {
let t = this.typeOf(obj);
toArray (value) {
if (value == null) {
return [];
}
if (t === 'error') {
if (obj.message) {
return `${obj.name}(${this.print(obj.message)})`;
if (!Array.isArray(value)) {
if (Util.isArrayLike(value)) {
let ret = [];
for (let i = value.length; i--; ) {
ret[i] = value[i];
}
return ret;
}
return `${obj.name}`;
value = [value];
}
else if (t === 'arguments') {
obj = arraySlice.call(obj);
}
else if (t === 'number') {
if (isNaN(obj)) {
return 'NaN';
}
if (!isFinite(obj)) {
return obj < 0 ? '-∞' : '∞';
}
}
return inspect(obj, options);
}
return value;
},
static typeOf (v) {
typeOf (v) {
if (v === null) {

@@ -906,11 +1035,12 @@ return 'null';

let cache = Util._typeOf.cache;
let t = typeof v;
if (!useTypeOfRe.test(t)) {
if (!Util._typeOf.useTypeOfRe.test(t)) {
let s = toString.call(v);
if (!(t = toStringMap[s])) {
let m = toStringRe.exec(s);
if (!(t = cache[s])) {
let m = Util._typeOf.toStringRe.exec(s);
toStringMap[s] = t = m ? m[1].toLowerCase() : s;
cache[s] = t = m ? m[1].toLowerCase() : s;
}

@@ -920,61 +1050,11 @@ }

return t;
}
} // Assert
},
Assert._notArrayLikeRe = /^(?:function|string)$/;
Assert.promiseTypesRe = /^(?:function|object)$/;
Assert.rangeRe = /^\s*([\[(])\s*(\d+),\s*(\d+)\s*([\])])\s*$/;
Assert.tupleRe = /[\.|\/]/;
Assert.Modifier = class {
constructor (owner, name, canonicalName) {
this.all = [];
this.map = {};
this.name = name;
this.owner = owner;
this.canonicalName = canonicalName || name;
this.alias = this.canonicalName !== name;
_typeOf: {
cache: {},
toStringRe: /^\[object ([^\]]+)]$/,
useTypeOfRe: /booolean|number|string|undefined/
}
add (name) {
if (!name) {
return;
}
let map = this.map;
let all = this.all;
let names = (typeof name === 'string') ? [name] : name;
for (let s of names) {
if (!map[s]) {
map[s] = true;
all.push(s);
}
}
}
get (name) {
if (!this.map[name]) {
let msg = `Expectation cannot use "${name}" `;
if (this.name === '$') {
msg += 'immediately';
} else {
msg += `after "${this.name}"`;
}
throw new Error(msg);
}
return this.owner.registry[name];
}
};
Assert.Promise = (typeof Promise !== 'undefined') && Promise.resolve && Promise;
Object.assign(Assert.prototype, {
async: null
});
module.exports = Assert;

@@ -9,3 +9,3 @@ 'use strict';

let entries = Assert.entries;
let entries = Assert.registry;
let keys = Object.keys(entries);

@@ -12,0 +12,0 @@

@@ -45,3 +45,3 @@ # Assert

Each piece of the dot-chain (`to`, `not` and `be`) are first processed as a property
getter and are tracked in a `modifiers` object.
getter and are tracked in a `_modifiers` object.

@@ -55,11 +55,11 @@ Note: Due to this use of property getters means **assertly** cannot support IE8 even

a.to; // sets "a.modifiers.to = true" and returns "a"
a.not; // sets "a.modifiers.not = true" and returns "a"
a.be(2); // sets "a.modifiers.be = true" and calls "a.be()"
a.to; // sets "a._modifiers.to = true" and returns "a"
a.not; // sets "a._modifiers.not = true" and returns "a"
a.be(2); // sets "a._modifiers.be = true" and calls "a.be()"
So in the end we have this:
a.modifiers = { to: true, not: true, be: true };
a._modifiers = { to: true, not: true, be: true };
The assertion is also collected in the `modifiers` object because the property getter
The assertion is also collected in the `_modifiers` object because the property getter
cannot know if `a.be` will be invoked or use as a step in a deeper dot-chain:

@@ -82,25 +82,15 @@

the `and` method (as well as a `then` method when using promises). The `and` method
creates a new `Assert` instance and passes along the same value.
creates a new `Assert` instance and passes along the same value and itself:
## Reporting
return {
and: new Assert(this.value, this)
};
Inside the assertion, the `Assert` instance is passed to a static method to report
the result:
The previous `Assert` instance is stored as `_previous` on the new instance.
Assert.report(this);
If the assertion fails, this method calls another static method:
Assert.reportFailure(this);
It is this method that contains the `throw` statement.
Because `Assert` is an ES6 class these static methods can be overridden in a derived
class. This is most useful when [integrating](./Integration.md) with other modules.
## Custom Modifiers and Assertions
All of the modifiers and assertions provided by `Assert` are dynamically added
by the `setup()` method using the `register()` method.
using the `register()` method.
For more on these use cases, see [Extensibility](./Extensibility.md).
# Extensibility
The [Assert](./Assert.md) class is primarily intended to be created as a worker object
by the public `expect` method. The `Assert` class is also designed, however, to allow
for derivation and customization.
by the public `expect` method. The `Assert` class can also be extended or customized.

@@ -10,4 +9,4 @@ ## Registering Customizations

All of the modifiers and assertions are incorporated by the static `register` method.
This method accepts an object that describes the allowed modifiers and their sequence
as well as the assertion functions.
This method accepts an object (called the "registry") that describes the allowed
modifiers and their sequence as well as the assertion functions.

@@ -23,2 +22,3 @@ Consider this minimal form:

// the rest of the arguments cme from the call to the assertion
// "this" is the Assert instance

@@ -55,5 +55,91 @@ return actual === expected;

The `this` pointer when an assertion is called in the `Assert` instance. The most
likely property to use is the `_modifiers` object which hold the pieces of the dot-chain
used to arrive at the assertion (this includes the modifiers and the assertion method
itself).
While one can access `this._modifiers.not` in the assertion method, this is not
recommended. This is because the truth result returned will be toggled based on this
modifier and so is not needed in the truth test.
When writing custom assertions, `Assert.Util` has some helpful [utility](./Utils.md)
methods.
#### Explaining Assertions
The default mechanism for generating explanatory text for an assert is acceptable in
most cases. This is because it reads almost identically to the assertion in the code
itself. There are times, however, explanations can be improved with a little logic.
Assert.register({
christmas: {
fn (value) {
return value.getMonth() === 11 && value.getDate() === 25;
},
explain (value) {
let y = value.getFullYear();
let x = +value;
let t = +new Date(y, 11, 25) - x;
let not = this._modifiers.not ? 'not ' : '';
if (t < 0) {
t = +new Date(y+1, 11, 25) - x;
}
t = Math.round(t / (24 * 60 * 60 * 1000));
if (t) {
return `Expected ${t} days before Christmas ${not}to be Christmas`;
}
return `Expected Christmas ${not}to be Christmas`;
}
}
});
The parameters passed to `explain` are the same as those passed to the assertion `fn`,
which are first the "actual" value (the one passed to `expect`) and then the values
passed in the assertion call.
For example:
expect(x).to.be.foo(y, z);
// ...
Assert.register({
foo: {
fn (x, y, z) {
// truth test
},
explain (x, y, z) {
// explain the truth test
}
}
});
The `this` pointer for an `explain` method is the `Assert` instance. Again, the most
likely property to use is `_modifiers`.
The `explain` method can (as above) return the full explanation. Alternatively, it
can adjust the properties that are normally concatenated. The following properties
are stored on the `Assert` instance for this purpose:
- `actual` - A string with the printed (`Assert.print`) `value`.
- `assertions` - A String[] of the modifiers followed by the assertion.
- `expectation` - A string with the printed (`Assert.print`) `expected`.
The default explanation consists of:
Expected ${actual} ${assertions.join(' ')} ${expectation}
For example:
Expected 4 to be 2
### Modifiers
Modifiers are added in the same way:
Modifiers are added in basically the same way as assertions:

@@ -76,22 +162,57 @@ Assert.register({

### Registry Keys
As shown above, the keys of the registry object (the object passed to `register`) can
contain some special syntactic forms.
For a complete example:
foo,bar,zip.thing,alt,other.blerp,derp
\ / \ / \ / \ /
\_______/ \_/ \_____/ \______/
before name aliases after
The key is first split on `'.'` (or alternatively `'|'` or `'/'` for readability).
For example:
foo,bar,zip|thing,alt,other|blerp,derp
// or
foo,bar,zip/thing,alt,other/blerp,derp
If there is only one element, that element is the name and its possible alternate
aliases.
If there are two or more elements in the split, the first element is the **before**
collection and the second is the name and aliases. The set of **before** values are
those words that come _before_ the word being defined. As with 'to.randomly' above,
the 'to' is a **before** for the word 'randomly'.
Only if there are three parts in the split does the **after** set come into play.
This set is the names that can come _after_ this name.
All sets of names can be comma-delimited. When this is applied to the primary name,
the second and subsequent names are considered aliases.
### Advanced Configuration
The value of each property passed to `register` is often a simple value, but its full
and normalized form is an object with the following properties:
The value of each property in the registry object is often a simple value, but its
full, normalized form is an object with the following properties:
- `alias` - The array of alternate names (e.g. "a" and "an").
- `after` - The array of names that this name comes after.
- `before` - The array of names that this name comes before.
- `after` - The array of names that come _after_ this name.
- `before` - The array of names that come _before_ this name.
- `fn` - The assertion `Function` that will return `true` for success.
- `explain` - The `Function` that will return a string explaining the assertion.
Rewriting the above in fully normal form:
Rewriting the 'to.randomly' in normal form:
Assert.register({
randomly: {
after: ['to']
before: ['to']
},
throw: {
after: ['to', 'randomly'],
before: ['to', 'randomly'],
fn (fn, type) {

@@ -106,2 +227,16 @@ if (this.modifiers.randomly) {

Rewriting the complete example:
foo,bar,zip|thing,alt,other|blerp,derp
Would look like this:
Assert.register({
thing: {
alias: ['alt', 'other' ],
before: ['foo', 'bar', 'zip']
after: ['blerp', 'derp']
}
});
## Adjusting The Defaults

@@ -125,2 +260,31 @@

### Replacing Assertions
When an assertion is already registered, calling `register` again will replace it.
The original assertion function and its `explain` method are preserved on the new
functions.
For example:
Assert.setup(); // initialize with defaults
// Take over nan assertion (which has an explain)
Assert.register({
nan: {
fn: function fn (actual, expected) {
let r = fn._super.call(this, actual, expected);
return r;
},
explain: function fn (actual, expected) {
let r = fn._super.call(this, actual, expected);
return r;
}
}
});
The `_super` property is stored on the function objects to form a chain. The odd
syntax of declaring a named method is needed only when access to these replaced
functions is desired.
## Extending Assert

@@ -147,1 +311,13 @@

common practice).
As a derived class, it is also possible to implement the [Lifecycle](./Lifecycle.md)
methods and customize these behaviors.
### Add-ons
You can use [add-ons](./Add-ons.md) with such classes in the same way as `Assert`
itself:
const addon = require('my-assertly-addon');
MyAssert.register(addon.init);

@@ -7,89 +7,7 @@ # Integration

Because [Assert](./Assert.md) is an ES6 class, it can be extended or even modified as
necessary. Since testing is a much more controlled environment, it is not nearly as
Because [Assert](./Assert.md) is an ES6 class, it can be extended cleanly. It can also
be modified. Since testing is a much more controlled environment, it is not nearly as
"hackish" to modify the `Assert` class and **assertly** tries to make this process as
straightforward as possible.
## Assert Life-Cycle Methods
Each `Assert` instance goes through a simple life-cycle:
- constructor
- before
- begin
- assertion
- explain
- failure
- report
- finish
The `constructor` is passed the "actual" value by the `expect` static method. After
the dot-chain is processed and the `modifiers` are accumulated, eventually an
assertion method is called. When that happens, the rest of the life-cycle is run.
### before
The `before` method is called when the assertion method is called. It is passed
the "definition" object that defines the assertion. This is stored as `def` on the
`Assert` instance.
### begin
This method is called passing the array of "expected" arguments (the arguments that
were passed to the assertion method).
To support promises, `begin` checks for a pending assertion and chains its evaluation
to that assertion's completion. If the "actual" value (given to the constructor) or
the "expected" array have promises, `begin` uses `Assert.Promise.all()` to resolve
them and calls `begin` again with the resolved values.
In the end, `begin` stores the array it is given as the `expected` property on the
`Assert` instance.
### assertion
This method is called to invoke the actual assertion logic (`this.def.fn()`) and
pass the actual and expected values. If the `not` modifier is present, the result
is inverted.
This method will set the `failed` property (possibly to `false`).
This method does nothing if called before the `expected` instance property is set
by `begin`.
### explain
This method is called to generate a string to explain an assertion. It is only
called (by default) when an assertion fails. It is called by `report` before the
instance is passed to the `Assert.report()` static method (more below).
In the act of generating the explanation, several other properties are set:
- `actual` - A string with the printed (`Assert.print`) `value`.
- `assertions` - A String[] of the modifiers followed by the assertion.
- `expectation` - A string with the printed (`Assert.print`) `expected`.
These are primarily of interest when writing custom assertions. See
[Extensibility](./Extensibility.md).
### failure
This method is called if the promise chain rejects and the assertion cannot be
evaluated. This sets `failed` to the `Error` object given.
### report
This method is called to report the `Assert` results. Before passing on to the
`Assert.report()` static method (see below), this method ensures that the `failed`
property (if set to `true`) has been converted to a string explaining the failure.
This method does nothing if called before the `failed` instance property is set
by `assertion`.
### finish
This method is called at the end of the life-cycle. It returns an object that
can be used as a conjunction (via `and`) or a promise (via `then`) if the assertion
was asynchronous.
## Static Methods

@@ -106,2 +24,7 @@

### register
This method is called to register modifiers and assertion methods. For more details
see [Extensibility](./Extensibility.md).
### report

@@ -125,4 +48,4 @@

This method is called to wrap the assertion definition (the sole argument) to
provide the proper life-cycles (as described above). The returned function is
provide the proper [lifecycle](./Lifecycle.md). The returned function is
what is returned by the property getter when the assertion is requested. For
more details on an assertion definition see [Extensibility](./Extensibility.md).
{
"name": "assertly",
"version": "1.0.0-beta.3",
"version": "1.0.0-rc.0",
"description": "Assert class for BDD-style assertions",

@@ -5,0 +5,0 @@ "main": "Assert.js",

@@ -9,4 +9,5 @@ # assertly

There are some additional goals, however, that led to this library:
Assertly was created to address these shortcomings in **expect.js**:
- [Add-ons](docs/Add-ons.md)
- [Extensibility](docs/Extensibility.md)

@@ -16,6 +17,30 @@ - [Integration](docs/Integration.md)

## Why Not Chai?
Chai pretty much has the above covered, but the somewhat common (and troubling)
practice of using "dangling getters" is something I think should be avoided. While
their use is not essential, it is, as mentioned, common practice. For example:
expect(x).to.be.null; // dangling getter
IDE's and linters **rightly** warn that an expression like the above "has no side-effects"
or "does nothing".
# API
The Assertly API is based on BDD-style expectations. For example:
expect(x).to.be(2);
Where "x" is the "actual" value and 2 is the "expected" value. All things begin with
the call to the `expect` method which returns an `Assert` instance.
This instance has properties (like `to`) that modify the conditions of the expectation
(called "modifiers") and methods (like `be`) that test these conditions (called
"assertions").
## Modifiers
Following are the modifiers provided by Assertly itself.
### not

@@ -38,3 +63,3 @@

Following are the assertion methods and their common aliases ("aka" = "also known as").
Following are the assertion methods and their aliases ("aka" = "also known as").

@@ -139,2 +164,18 @@ ### a (aka: "an")

### falsy
- to[.not].be.falsy
The `falsy` assertion ensures that the `actual` value is "false-like". In other
words, that it would fail an `if` test.
For example:
expect(x).to.be.falsy();
The above is equivalent to:
expect(!x).to.be(true);
### greaterThan (aka: "above", "gt")

@@ -236,3 +277,3 @@

expect(b).to.have.only.own.key('a');
expect(b).to.have.only.own.key('b');

@@ -245,3 +286,2 @@ This assertion succeeds because, while "b" inherits the "a" property, "b" is the

- to[.not].only.have.length
- to[.not].have.length

@@ -356,7 +396,7 @@

expect(1).to.equal('1'); // fails since 1 !== '1'
expect(1).to.be.same('1'); // fails since 1 !== '1'
expect(1).to.equal(1); // succeeds just like "to.be(1)"
expect(1).to.be.same(1); // succeeds just like "to.be(1)"
For object and arrays, `equal` compares corresponding properties and elements with the
For object and arrays, `same` compares corresponding properties and elements with the
same logic. In other words, the following passes for `equal` and fails for `same``:

@@ -363,0 +403,0 @@

Sorry, the diff of this file is too big to display

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc