@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.