{"id":16172027,"url":"https://github.com/pablo-abc/uvu-expect","last_synced_at":"2026-05-18T04:37:14.284Z","repository":{"id":57389451,"uuid":"457203576","full_name":"pablo-abc/uvu-expect","owner":"pablo-abc","description":"Chai like BDD assertions for uvu","archived":false,"fork":false,"pushed_at":"2022-03-10T13:08:17.000Z","size":230,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-03-13T16:34:30.478Z","etag":null,"topics":["assertions","javascript","testing","typescript","uvu"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/pablo-abc.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2022-02-09T04:09:31.000Z","updated_at":"2022-02-20T00:27:33.000Z","dependencies_parsed_at":"2022-08-27T04:51:19.885Z","dependency_job_id":null,"html_url":"https://github.com/pablo-abc/uvu-expect","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pablo-abc%2Fuvu-expect","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pablo-abc%2Fuvu-expect/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pablo-abc%2Fuvu-expect/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pablo-abc%2Fuvu-expect/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/pablo-abc","download_url":"https://codeload.github.com/pablo-abc/uvu-expect/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247615476,"owners_count":20967183,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["assertions","javascript","testing","typescript","uvu"],"created_at":"2024-10-10T03:44:40.668Z","updated_at":"2025-09-29T08:15:23.495Z","avatar_url":"https://github.com/pablo-abc.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# uvu-expect\n\n[![NPM Version](https://img.shields.io/npm/v/uvu-expect)](https://www.npmjs.com/package/uvu-expect)\n[![NPM Downloads](https://img.shields.io/npm/dw/uvu-expect)](https://www.npmjs.com/package/uvu-expect)\n[![Tests](https://github.com/pablo-abc/uvu-expect/actions/workflows/test.yml/badge.svg)](https://github.com/pablo-abc/uvu-expect/actions/workflows/test.yml)\n[![codecov](https://codecov.io/gh/pablo-abc/uvu-expect/branch/main/graph/badge.svg?token=JV9BKQJMCF)](https://codecov.io/gh/pablo-abc/uvu-expect)\n\n[Chai](https://www.chaijs.com) like BDD assertions for [uvu](https://github.com/lukeed/uvu). You _can_ perfectly use Chai with uvu, but with this package I'm attempting to have a similar syntax with a better integration.\n\n\u003e **WARNING**: Very new package. Will most likely have breaking changes between minor versions\n\n## Installation\n\nYou can grab it from npm:\n\n```sh\nnpm install --save-dev uvu-expect\n```\n\nor if you use yarn\n\n```sh\nyarn add -D uvu-expect\n```\n\n## Usage\n\nUnlike Jest, `expect` is not available globally but needs to be imported:\n\n```javascript\nimport { expect } from 'uvu-expect';\n```\n\nThis package works much like Chai. You pass the value you want to validate to `expect` and chain assertions to it.\n\n```javascript\nexpect('a string').to.be.a.string;\n```\n\nLike Chai, you can add \"chain\" words to your assertion to make it more readable.\n\n```javascript\nexpect('a string').to.still.work.after.I.type.all.of.this.but.it.should.be.a.string;\n```\n\nThe only actual assertion on the previous example is `string`.\n\nAssertions come in two ways: properties and methods. Properties only require to be accessed in order to trigger an assertion (like `string` above), while methods need an argument to be passed in order to validate. Besides these, certain properties will add \"modifiers\" to your assertion, such as `not` to negate an assertion.\n\n### Properties\n\n\u003chr /\u003e\n\n#### .ok\n\nChecks if the supplied value is truthy.\n\n```javascript\nexpect('a string').to.be.ok;\nexpect('').to.not.be.ok;\n```\n\n\u003chr /\u003e\n\n#### .empty\n\nChecks if the target is empty. For arrays and strings, checks the `length` property to be 0. For Maps and Sets, checks the `size` property to be 0. For objects, expects it to not have any own properties.\n\n```javascript\nexpect('').to.be.empty;\nexpect([]).to.be.empty;\nexpect(new Map()).to.be.empty;\nexpect(new Set()).to.be.empty;\nexpect({}).to.be.empty;\nexpect('not empty').to.not.be.empty;\nexpect(new Set([1])).to.not.be.empty;\nexpect(new Map([['a', 1]])).to.not.be.empty;\nexpect({ a: 1 }).to.not.be.empty;\n```\n\n\u003chr /\u003e\n\n#### .resolves\n\nChecks whether a promise resolves. Every assertion done after this happens on the resolved value. You must await `expect` when using this property.\n\nAlias: `.resolve`\n\n```javascript\nawait expect(Promise.resolve(true)).resolves.to.true;\nawait expect(Promise.resolve(false)).to.resolve.to.false;\n```\n\n\u003chr /\u003e\n\n#### .rejects\n\nChecks whether a promise rejects. Every assertion done after this happens on the rejected value. You must await `expect` when using this property.\n\nAlias: `.reject`\n\n```javascript\nawait expect(Promise.reject(true)).rejects.to.true;\nawait expect(Promise.reject(false)).to.reject.to.false;\n```\n\n\u003chr /\u003e\n\n### .throws\n\nChecks whether a function throws. Every assertion done after this (if not negated) happens on the thrown value.\n\nAlias: `.throw`\n\n```javascript\nconst throwFn = () =\u003e {\n  throw new Error('I am a teapot');\n};\n\nexpect(throwFn)\n  .to.throw.instanceOf(Error)\n  .with.property('message')\n  .that.equals('I am a teapot');\n```\n\n\u003chr /\u003e\n\n#### .deep\n\nModifies the following assertion so it uses deep equality instead of strict equality. Can be used with `.equal`, `.contain`, `.members`.\n\nAlias: `.deeply`\n\n```javascript\nexpect({ a: { b: 'c' }}).to.deep.equal({ a: { b: 'c' } });\nexpect({ a: { b: 'c' }}).to.deeply.contain({ b: 'c' });\nexpect([{ a: 1 }, { b: 2 }, { c: 3 }]).to.contain.deep.members([{ b: 2 }, { a: 1 }]);\n```\n\n\u003chr /\u003e\n\n#### Checking if a value is of a specific type\n\nWe offer the properties `.string`, `.number`, `.boolean`, `.object`, `.array` and `.function`.\n\nIn the case of `.object`, this package will check for anything that JavaScript would consider an object. You can add `.plain` somewhere before to check for a plain object.\n\n```javascript\nexpect(1).to.be.a.number;\nexpect(1).to.not.be.a.string.and.to.be.a.number;\nexpect('a').to.be.a.string;\nexpect(true).to.be.a.boolean;\nexpect({}).to.be.an.object;\nexpect(new Date()).to.be.an.object;\nexpect(new Date()).to.not.be.a.plain.object;\nexpect({}).to.be.a.plain.object;\nexpect(() =\u003e undefined).to.be.a.function;\n```\n\nAlternatively you can use the `.type` method to do the same.\n\n\u003chr /\u003e\n\n#### Checking for a specific value\n\nWe offer properties to check for the specific values `.true`, `.false`, `.null`, `.undefined`.\n\n```javascript\nexpect(true).to.be.true;\nexpect(false).to.not.be.true;\nexpect(false).to.be.false;\nexpect(true).to.not.be.false;\nexpect(null).to.be.null;\nexpect(undefined).to.not.be.null;\nexpect(undefined).to.be.undefined;\nexpect(null).to.not.be.undefined;\n```\n\n### Methods\n\n\u003chr /\u003e\n\n#### .equal\n\nChecks if the your target is equal (===) to the value supplied. You can add `.deep` before to check for deep equality.\n\nWhen checking deep equality, you can use [matchers](#matchers) to make the comparison less strict.\n\nAlias: `.equals`.\n\n```javascript\nexpect('a string').to.equal('a string');\nexpect(1).to.be.a.number.that.equals(1);\nexpect('a string').not.to.equal('a different string');\n```\n\n\u003chr /\u003e\n\n#### .contain\n\nIf your target is a string, checks if the string supplied is contained in your string.\n\nIf your target is an array or set, checks if the value supplied is contained in it. You can add `.deep` if you want to compare values using deep equality.\n\nIf your target is an object, checks if the partial object supplied is contained in the object. You can add `.deep` if you want to check on deeper levels of your object.\n\nAlias: `.contains`, `.include`, `.includes`.\n\n```javascript\nexpect('zaphod and arthur').to.include('arthur');\nexpect('zaphod').to.not.include('arthur');\nexpect(['zaphod', 'arthur']).to.contain('zaphod');\nexpect(['zaphod', 'arthur']).to.not.contain('marvin');\n\n\nconst testObj = {\n  a: 1,\n  b: 2,\n  c: {\n    d: 3,\n  },\n  e: [1, 2, 3],\n};\nexpect(testObj).to.contain({ a: 1 });\nexpect(testObj).to.deeply.contain({ d: 3 });\nexpect(testObj).to.contain({ e: [1, 2, 3] });\nexpect(new Set([1, 2, 3])).to.contain(2);\nexpect(new Set([1, 2, 3])).to.not.contain(4);\nexpect([{}]).to.deeply.contain({});\n```\n\n\u003chr /\u003e\n\n#### .match\n\nAsserts that your target contains the supplied sub string, or matches the supplied regular expression. If the target provided is either an object or array, it will assert that the value \"matches\" the target,\n\nYou can use [matchers](#matchers) to make the comparison less strict.\n\nAlias: `.matches`\n\n```javascript\nexpect('zaphod and arthur').to.match('zaphod');\nexpect('zaphod and arthur').to.match(/arthur/);\nexpect('zaphod and arthur').to.not.match(/marvin/);\nexpect({ value: 'string', num: 1 }).to.match({ value: 'string' });\nexpect(['hello', 'hi', 'goodbye']).to.match(['hi', 'goodbye']);\n```\n\n\u003chr /\u003e\n\n#### .property\n\nAsserts that the object provided contains a property with the supplied name. Every assertion done after this will be done on the value returned from the property. You can use a string with dot notation (e.g. `a.b.c.1`) if you add `.nested` before.\n\n```javascript\nconst testObj = {\n  a: 1,\n  b: 2,\n  c: {\n    d: 3,\n  },\n  e: [1, 2, 3],\n};\nexpect(testObj).to.have.property('c').that.deep.equals({ d: 3 });\nexpect(testObj).to.have.a.property('a').that.equals(1);\nexpect(testObj).to.have.own.property('c').that.deep.equals({ d: 3 });\nexpect(testObj).to.have.own.property('a').that.equals(1);\nexpect(testObj).to.not.have.property('d');\nexpect(testObj).to.have.deep.own.property('d').that.equals(3);\nexpect(testObj).to.have.nested.property('c.d').that.equals(3);\nexpect(testObj).to.not.have.property('h');\n```\n\n\u003chr /\u003e\n\n#### .type\n\nAsserts if your target is the type supplied. Same as the properties described above but as a method.\n\nAlias: `.a`, `.an`.\n\n```javascript\nexpect('string').to.be.a('string');\nexpect(1).to.be.type('number');\nexpect('string').to.not.be.a('number');\nexpect({}).to.be.an('object');\nexpect({}).to.be.a.plain.type('object');\nexpect([]).to.be.an('array');\n```\n\n\u003chr /\u003e\n\n#### .instance\n\nAsserts that your target is an instance of the specified constructor.\n\nAlias: `.instanceOf`.\n\n```javascript\nexpect(new Date()).to.be.instance(Date);\nexpect(new URL('https://example.com')).to.not.be.instanceOf(Date);\n```\n\n\u003chr /\u003e\n\n#### .length\n\nAsserts that your array or string has the specified length.\n\nAlias: `.lengthOf`\n\n```javascript\nexpect('a string').to.have.a.lengthOf(8);\nexpect('a string').to.not.have.a.lengthOf(4);\nexpect([1, 2, 3]).to.have.a.length(3);\nexpect([1, 2, 3]).to.not.have.a.length(4);\n```\n\n\u003chr /\u003e\n\n#### .members\n\nAsserts that your array contains the specified members. It checks using strict equality, but you can add `.deep` somewhere before to use deep equality. You can also add `.ordered` to check if the members are in the same order as your supplied value.\n\n```javascript\nexpect([1, 2, 3]).to.have.members([3, 2, 1]);\nexpect([1, 2, 3]).to.have.members([3, 2]);\nexpect([1, 2, 3]).to.not.have.ordered.members([3, 2]);\nexpect([1, 2, 3]).to.have.ordered.members([2, 3]);\nexpect([1, 2, 3]).to.not.have.members([3, 2, 4]);\nexpect([{ a: 1 }, { b: 2 }, { c: 3 }]).to.not.have.members([{ a: 1 }]);\nexpect([{ a: 1 }, { b: 2 }, { c: 3 }]).to.have.deep.members([{ a: 1 }]);\nexpect([{ a: 1 }, { b: 2 }, { c: 3 }]).to.have.deep.members([\n  { b: 2 },\n  { a: 1 },\n]);\nexpect([{ a: 1 }, { b: 2 }, { c: 3 }]).to.not.have.deep.ordered.members([\n  { b: 2 },\n  { a: 1 },\n]);\nexpect([{ a: 1 }, { b: 2 }, { c: 3 }]).to.have.deep.ordered.members([\n  { b: 2 },\n  { c: 3 },\n]);\n```\n\n\u003chr /\u003e\n\n#### .satisfy\n\nAsserts that your supplied function returns a truthy value. The supplied function will receive the current target being validated. You can use this if none of the other assertions work for you.\n\n```javascript\nexpect('value').to.satisfy((v) =\u003e typeof v === 'string');\nexpect('value').to.not.satisfy((v) =\u003e typeof v === 'number');\nexpect([1, 2, 3]).to.be.an.array.that.satisfies((arr) =\u003e {\n  return arr.every((value) =\u003e typeof value === 'number');\n});\nexpect([1, 2, 3]).to.be.an.array.that.does.not.satisfy((arr: number[]) =\u003e {\n  return arr.every((value) =\u003e typeof value === 'string');\n});\n```\n\n### Assertions on function mocks\n\nBesides the previously mentioned assertions, we provide some assertions that work nicely with [tinyspy][tinyspy] and [sinonjs][sinonjs] (or any other mock library that shares a similar API).\n\n\u003chr /\u003e\n\n#### .called\n\nAllows you to assert if a function has been called.\n\n```javascript\nimport { expect } from 'uvu-expect';\nimport { spy } from 'tinyspy';\n\nconst mockFn = spy();\nexpect(mockFn).not.to.have.been.called;\nmockFn();\nexpect(mockFn).to.have.been.called;\n```\n\n\u003chr /\u003e\n\n#### .times\n\nMethod that allows you to assert if a function has been called a specific amount of times.\n\nAlias: `.calledTimes`\n\n```javascript\nconst mockFn = spy();\nexpect(mockFn).not.to.have.been.called;\nmockFn();\nexpect(mockFn).to.have.been.called.times(1);\nexpect(mockFn).to.have.been.called.but.not.times(2);\nexpect(mockFn).to.have.not.been.calledTimes(2);\n```\n\nYou may use `.once`, `.twice` and `.thrice` instead of `.times(1)`, `.times(2)` and `.times(3)` respectively.\n\n```javascript\nconst mockFn = spy();\nexpect(mockFn).not.to.have.been.called;\nmockFn();\nexpect(mockFn).to.have.been.called.once;\nmockFn();\nexpect(mockFn).to.have.been.called.twice;\nmockFn();\nexpect(mockFn).to.have.been.called.thrice;\n```\n\n\u003chr /\u003e\n\n#### .with\n\nAllows you to assert if a function has been called with the specified arguments. You may use `.nth` somewhere before to restrict it to a specific call. You may also use `.last` before to restrict it to the last call.\n\nAlias: `.calledWith`.\n\n```javascript\nconst mockFn = spy();\n\nmockFn(1);\nmockFn(1, 2);\nmockFn(1, 2, 3);\n\nexpect(mockFn).to.have.been.called.with(1);\nexpect(mockFn).to.have.been.called.with(1, 2);\nexpect(mockFn).to.have.been.called.with(1, 2, 3);\nexpect(mockFn).to.have.been.nth(1).called.with(1);\nexpect(mockFn).to.have.been.nth(2).called.not.with(1, 2, 3);\nexpect(mockFn).to.have.not.been.nth(2).calledWith(1, 2, 3);\nexpect(mockFn).to.have.been.last.called.with(1, 2, 3);\n```\n\n### Matchers\n\n`.deep.equal`, `.match`, `.contain` and `.with` (for function arguments) can be made less strict by using matchers. This package comes bundled with [Sinon's matchers](https://sinonjs.org/releases/v13/matchers/) via Samsam. You can access them directly from `expect`. Its useage is the same as shown on Sinon's documentation, but instead of using `sinon.*` use `expect.*`.\n\n```javascript\nexpect({\n  deep: 'string',\n  num: 1,\n  another: 'string',\n  arr: ['hello', 'goodbye'],\n  obj: {\n    prop: 'value',\n  },\n}).to.match({\n  deep: expect.match.string,\n  num: expect.match.number,\n  arr: expect.match.array.contains(['hello']),\n  obj: expect.match({\n    prop: expect.match.string,\n  }),\n});\nexpect('zaphod and arthur').to.match(expect.match('and arthur'));\nexpect('zaphod').to.match(expect.match('aphod'));\nexpect('zaphod').to.match(expect.match(/aphod/));\nexpect('1').to.match(expect.match(1));\n```\n\n## Preventing accidentally not doing any assertions\n\nSince we allow to use anything as a chain for assertions (except symbols), it is possible for you to accidentally not assert anything on your target, which would make your tests always pass. In order to mitigate this, this package will show a warning when no assertions are done after an `expect` with a message like this on your console:\n\n```\nNo assertion was done on one of your `expect` calls.\n\nMake sure you have no typos on your assertion:\nexpect(...).to.do.nothing\n```\n\nYour tests will still pass, but it will be obvious if you missed anything.\n\nIn case of false positives, you can globally disable this behaviour by calling the exported function `disableNoAssertionWarnings`, and you can enable them again by calling `enableNoAssertionWarnings`. You can disable it for a specific `expect` call by passing an object as a second argument to expect: `{ disableNoAssertionWarning: true }`.\n\n## Adding custom assertions (plugins)\n\nYou can add properties and methods to this package by using `extend`. It expects a function that will receive two helpers: `replaceProperty` and `extendProperty`.\n\nProperties are defined as an object with two functions: `onCall` and `onAccess`. `onAccess` gets executed whenever the property is accessed, and `onCall` is what will be executed if the property is called as a method. If you're using both for the same property it is recommended for `onAccess` to just add flags to your chain.\n\nWithin `onAccess` and `onCall` you can access `this`, it will contain the current flags of the chain, an `assert` function and a function to clear your flags.\n\n\u003e Since we're using proxies, `onAccess` will _always_ be called regardless of if you're using the property as a method or not.\n\n### Flags/Modifiers\n\nFlags are used to modify assertions that will be run later. For example, when adding a `.not` to your assertion, it adds a flag `negate` with a value of `true` to the chain. The method to add or access flags can be accessed through `this` like `this.flag`.\n\n```javascript\n// Accessing the value\nconst negated = this.flag('negate');\n\n// Setting the value\nthis.flag('negate', true);\n```\n\nThe target you're validating can be accessed through the flag `object`.\n\n### this.assert\n\nA method that can be accessed on `onCall` and `onAccess` that will throw an assertion error if the first argument passed is `false` (or if it's `true` if there is a `.not` before).\n\n- The first argument is the condition to assert.\n- The second argument is a message to show if the assertion failed while _not_ being negated.\n- The third argument is a message to show if the assertion failed while being negated.\n- The fourth argument is an optional object with options:\n  - `expects`: the value expected.\n  - `actual`: your actual value being validated, which defaults to `this.flag('object')`.\n  - `showDiff`: indicates if the error should show a diff (built from `expects` and `actual`). Defaults to `false`.\n  - `keepFlags`: a call to `this.assert` will \"consume\" all flags previously set. If you want to prevent this behaviour, set this property to `true`. Note that this is handled automatically when using `addProperty`, and should not be an issue when _replacing_ a handler with `replaceProperty`.\n\n```javascript\nthis.assert(\n  actual === 'zaphod',\n  'expected to be zaphod',\n  'expected to not be zaphod',\n  { actual, expects: 'zaphod', showDiff: true }\n);\n```\n\n### addProperty\n\nAdds an assertion on top of other assertions executed by the property (if it has any). The first argument is the name of the property to add. It can be an array of string if you'd like the property to have \"aliases\". The second argument is a property object (an object with an `onAccess` and `onCall` functions).\n\n```javascript\nextend(({ addProperty }) =\u003e {\n  addProperty('zaphod', {\n    onAccess() {\n      const actual = this.flag('object');\n      this.assert(\n        actual === 'zaphod',\n        'expected to be zaphod',\n        'expected to not be zaphod'\n      );\n    }\n  });\n});\n```\n\n### replaceProperty\n\nAllows you to replace a property by providing you with the original property value (the `onAccess` and `onCall` functions). It expects the name (or names if you want to add aliases) of the property to add as a first argument, and a function that will receive the original property as a second argument. This function should return a new property.\n\n```javascript\nextend(({ replaceProperty }) =\u003e {\n  // We will replace both aliases\n  replaceProperty(['equal', 'equals'], (handler) =\u003e {\n    return {\n      onCall(value) {\n        if (typeof value === 'string') {\n          this.assert(\n            value === 'zaphod',\n            'expected to be zaphod',\n            'expected to not be zaphod'\n          );\n        } else {\n          handler.onCall?.(value);\n        }\n      },\n      onAccess() {\n        handler.onAccess?.();\n      },\n    };\n  });\n});\n```\n\n## Extensions\n\nIf you want to make assertions like how you would with `@testing-library/jest-dom`, check out [uvu-expect-dom](https://github.com/pablo-abc/uvu-expect-dom).\n\n[tinyspy]: https://github.com/Aslemammad/tinyspy\n[sinonjs]: https://sinonjs.org\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpablo-abc%2Fuvu-expect","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpablo-abc%2Fuvu-expect","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpablo-abc%2Fuvu-expect/lists"}