@magic/test
has
Functions for asserting object properties, array contents, and string values without needing explicit type annotations or stringifying functions. Every has.* check returns a predicate, so it works directly as a test expectation:
import { has, is } from '@magic/test'{fn: () => getUser(),expect: has.property('name', is.string),info: 'user name is a string',}
has.property(key, check)
Check a single property. Accepts either a predicate or a literal value (uses is.deep.equal).
// With predicate{fn: () => getUser(),expect: has.property('name', is.string),info: 'user name is a string',}// With literal value (uses is.deep.equal){fn: () => getUser(),expect: has.property('age', 25),info: 'user age is 25',}
has.properties(spec)
Check multiple properties. Mix predicates and literal values.
{fn: () => getUser(),expect: has.properties({ name: is.string, age: is.num }),info: 'user has required properties',}// All literals{fn: () => getUser(),expect: has.properties({ name: 'John', age: 25 }),info: 'exact user match',}
has.any(spec)
Check at least one property matches. Accepts predicates or literals.
{fn: () => parseResult(),expect: has.any({ error: is.error, data: is.object }),info: 'result has either error or data',}// With literals{fn: () => parseResult(),expect: has.any({ error: 'not found', data: null }),info: 'result has specific error or null data',}
has.nested(path, check)
Check a nested property path. Accepts predicates or literal values.
{fn: () => getData(),expect: has.nested('user.profile.name', is.string),info: 'deep nested property exists',}
has.string(...substring)
Check if value is a string containing every given substring. Substring entries can also be predicates receiving the full string.
{fn: () => error.message,expect: has.string('failed to connect'),info: 'error message contains helpful context',}// can also check multiple args{fn: () => error.message,expect: has.string('failed', 'to', 'connect'),info: 'error message contains all parts',}
has.at(index, check)
Check the element at a given index of an array. Accepts a predicate, a literal value, or an array of checks (all must pass).
{fn: () => getItems(),expect: has.at(0, is.string),info: 'first item is a string',}// multiple checks must all pass{fn: () => getItems(),expect: has.at(0, [is.string, item => item === 'first']),info: 'first item is the string "first"',}
has.key(keyName)
Check if object has a specific key.
{fn: () => result,expect: has.key('data'),info: 'result has data key',}
has.keys(keyNames[])
Check if object has all specified keys.
{fn: () => user,expect: has.keys(['name', 'email']),info: 'user has required fields',}
has.includes(...items)
Check if array or string contains item (uses deep.equal for arrays). With multiple items, every item must be present.
const roles = ['admin', 'root', 'user']{fn: () => roles,expect: has.includes('admin', 'root'),info: 'user has admin and root roles',}
has.oneOf(...options)
Check if value equals one of the options (uses deep.equal).
// if only one arg and first arg is an array:{fn: () => status,expect: has.oneOf(['pending', 'done', 'failed']),info: 'status is a valid value',},// also valid{fn: () => status,expect: has.oneOf('pending', 'done', 'failed'),info: 'status is a valid value',},
has.matches(regex)
Check if string matches regex pattern.
{fn: () => phoneNumber,expect: has.matches(/^d{3}-d{4}$/),info: 'phone number is valid format',}
has.not
Negated versions of every has.* check. has.not.X(...) passes exactly when the matching has.X(...) would fail (pure logical negation):
- has.not.property(key, check)
- has.not.properties(spec)
- has.not.any(spec)
- has.not.nested(path, check)
- has.not.string(substring)
- has.not.at(index, check)
- has.not.key(keyName)
- has.not.keys(keyNames[])
- has.not.includes(item)
- has.not.oneOf(options[])
- has.not.matches(regex)
{fn: () => status,expect: has.not.oneOf(['pending', 'done']),info: 'status is not a valid state',}{fn: () => error.message,expect: has.not.string('timeout'),info: 'error is not a timeout',}{fn: () => user,expect: has.not.property('isAdmin', true),info: 'user is not an admin',}{fn: () => items,expect: has.not.includes('admin'),info: 'items do not contain admin',}
Because the negation is logical, values the base check rejects also pass: has.not.property(...)(null) is true, since has.property(...) is false for null, and has.not.keys([])({...}) is false, since has.keys([]) always passes for objects.