├── .github ├── FUNDING.yml └── workflows │ ├── browsers.yml │ ├── node.js.yml │ └── npm-publish.yml ├── .gitignore ├── .mailmap ├── .npmignore ├── .prettierrc.json ├── CODEOWNERS ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── History.md ├── LICENSE ├── README.md ├── ReleaseNotes.md ├── eslint.config.js ├── index.js ├── lib ├── chai.js └── chai │ ├── assertion.js │ ├── config.js │ ├── core │ └── assertions.js │ ├── interface │ ├── assert.js │ ├── expect.js │ └── should.js │ └── utils │ ├── addChainableMethod.js │ ├── addLengthGuard.js │ ├── addMethod.js │ ├── addProperty.js │ ├── compareByInspect.js │ ├── expectTypes.js │ ├── flag.js │ ├── getActual.js │ ├── getMessage.js │ ├── getOperator.js │ ├── getOwnEnumerableProperties.js │ ├── getOwnEnumerablePropertySymbols.js │ ├── getProperties.js │ ├── index.js │ ├── inspect.js │ ├── isNaN.js │ ├── isProxyEnabled.js │ ├── objDisplay.js │ ├── overwriteChainableMethod.js │ ├── overwriteMethod.js │ ├── overwriteProperty.js │ ├── proxify.js │ ├── test.js │ ├── transferFlags.js │ └── type-detect.js ├── package-lock.json ├── package.json ├── register-assert.js ├── register-expect.js ├── register-should.js ├── test ├── assert.js ├── auth │ └── .gitkeep ├── bootstrap │ └── index.js ├── configuration.js ├── display │ ├── errors.js │ └── message.js ├── expect.js ├── globalErr.js ├── globalShould.js ├── plugins.js ├── should.js ├── subset.js ├── type-detect │ ├── deno-test.ts │ ├── dom.js │ ├── index.js │ ├── new-ecmascript-types.js │ ├── node.js │ └── tostringtag-extras.js ├── utilities.js └── virtual-machines.js ├── tsconfig.json └── web-test-runner.config.js /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | open_collective: chaijs -------------------------------------------------------------------------------- /.github/workflows/browsers.yml: -------------------------------------------------------------------------------- 1 | # This workflow will do a clean install of node dependencies, build the source 2 | # code and run tests across different versions of browsers 3 | 4 | name: Browsers CI 5 | 6 | on: 7 | push: 8 | branches: [ main, 4.x.x ] 9 | pull_request: 10 | branches: [ main, 4.x.x ] 11 | 12 | jobs: 13 | build: 14 | 15 | runs-on: ubuntu-latest 16 | 17 | strategy: 18 | matrix: 19 | browser-name: 20 | - chromium 21 | - firefox 22 | 23 | steps: 24 | - uses: actions/checkout@v4 25 | - name: ${{ matrix.browser-name }} 26 | uses: actions/setup-node@v4 27 | - run: npm ci 28 | - run: npx playwright install --with-deps 29 | - run: npm run build --if-present 30 | - run: npm run test-chrome -- --browsers ${{ matrix.browser-name }} 31 | -------------------------------------------------------------------------------- /.github/workflows/node.js.yml: -------------------------------------------------------------------------------- 1 | # This workflow will do a clean install of node dependencies, build the source code and run tests across different versions of node 2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions 3 | 4 | name: Node.js CI 5 | 6 | on: 7 | push: 8 | branches: [ main, 4.x.x ] 9 | pull_request: 10 | branches: [ main, 4.x.x ] 11 | 12 | jobs: 13 | build: 14 | 15 | runs-on: ubuntu-latest 16 | 17 | strategy: 18 | matrix: 19 | node-version: 20 | - 18 # to be removed 2025-04-30 21 | - 20 # to be removed 2026-04-30 22 | - latest 23 | # See supported Node.js release schedule at https://github.com/nodejs/release#release-schedule 24 | 25 | steps: 26 | - uses: actions/checkout@v4 27 | - name: Use Node.js ${{ matrix.node-version }} 28 | uses: actions/setup-node@v4 29 | with: 30 | node-version: ${{ matrix.node-version }} 31 | - run: npm ci 32 | - run: npm run build --if-present 33 | - run: npm run test-node 34 | -------------------------------------------------------------------------------- /.github/workflows/npm-publish.yml: -------------------------------------------------------------------------------- 1 | # This workflow will run tests using node and then publish a package to GitHub Packages when a release is created 2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/publishing-nodejs-packages 3 | 4 | name: Publish to npm 5 | 6 | on: 7 | release: 8 | types: [created] 9 | 10 | jobs: 11 | build: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v4 15 | - uses: actions/setup-node@v4 16 | with: 17 | node-version: 22 18 | - run: npm ci 19 | - run: npx playwright install --with-deps 20 | - run: npm run build --if-present 21 | - run: npm test 22 | 23 | publish-npm: 24 | needs: build 25 | runs-on: ubuntu-latest 26 | permissions: 27 | id-token: write 28 | steps: 29 | - uses: actions/checkout@v4 30 | - uses: actions/setup-node@v4 31 | with: 32 | node-version: 22.x 33 | registry-url: "https://registry.npmjs.org" 34 | cache: "npm" 35 | - run: npm ci 36 | - run: npm run build --if-present 37 | - run: npm version ${TAG_NAME} --git-tag-version=false 38 | env: 39 | TAG_NAME: ${{ github.ref_name }} 40 | - run: npm publish --provenance --access public 41 | env: 42 | NODE_AUTH_TOKEN: ${{ secrets.npm_secret }} 43 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | lib-cov 2 | *.seed 3 | *.log 4 | *.csv 5 | *.dat 6 | *.out 7 | *.pid 8 | *.gz 9 | 10 | pids 11 | logs 12 | results 13 | build 14 | components 15 | 16 | node_modules 17 | npm-debug.log 18 | 19 | coverage 20 | /chai.js 21 | 22 | test/auth/* 23 | !test/auth/.gitkeep 24 | 25 | /chai.js 26 | /chai.cjs 27 | -------------------------------------------------------------------------------- /.mailmap: -------------------------------------------------------------------------------- 1 | Domenic Denicola 2 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .git* 2 | docs/ 3 | test/ 4 | support/ 5 | component.json 6 | components/ 7 | build/ 8 | lib-cov/ 9 | coverage/ 10 | .travis.yml 11 | .mailmap 12 | Makefile 13 | *.swp 14 | .DS_Store 15 | -------------------------------------------------------------------------------- /.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "bracketSpacing": false, 3 | "printWidth": 80, 4 | "semi": true, 5 | "singleQuote": true, 6 | "tabWidth": 2, 7 | "trailingComma": "none", 8 | "useTabs": false, 9 | "arrowParens": "always" 10 | } 11 | -------------------------------------------------------------------------------- /CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @chaijs/chai 2 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Code of Conduct 2 | 3 | > Read in: [Español](http://contributor-covenant.org/version/1/3/0/es/) | 4 | [Français](http://contributor-covenant.org/version/1/3/0/fr/) | 5 | [Italiano](http://contributor-covenant.org/version/1/3/0/it/) | 6 | [Magyar](http://contributor-covenant.org/version/1/3/0/hu/) | 7 | [Polskie](http://contributor-covenant.org/version/1/3/0/pl/) | 8 | [Português](http://contributor-covenant.org/version/1/3/0/pt/) | 9 | [Português do Brasil](http://contributor-covenant.org/version/1/3/0/pt_br/) 10 | 11 | As contributors and maintainers of this project, and in the interest of 12 | fostering an open and welcoming community, we pledge to respect all people who 13 | contribute through reporting issues, posting feature requests, updating 14 | documentation, submitting pull requests or patches, and other activities. 15 | 16 | We are committed to making participation in this project a harassment-free 17 | experience for everyone, regardless of level of experience, gender, gender 18 | identity and expression, sexual orientation, disability, personal appearance, 19 | body size, race, ethnicity, age, religion, or nationality. 20 | 21 | Examples of unacceptable behavior by participants include: 22 | 23 | * The use of sexualized language or imagery 24 | * Personal attacks 25 | * Trolling or insulting/derogatory comments 26 | * Public or private harassment 27 | * Publishing other's private information, such as physical or electronic 28 | addresses, without explicit permission 29 | * Other unethical or unprofessional conduct 30 | 31 | Project maintainers have the right and responsibility to remove, edit, or 32 | reject comments, commits, code, wiki edits, issues, and other contributions 33 | that are not aligned to this Code of Conduct, or to ban temporarily or 34 | permanently any contributor for other behaviors that they deem inappropriate, 35 | threatening, offensive, or harmful. 36 | 37 | By adopting this Code of Conduct, project maintainers commit themselves to 38 | fairly and consistently applying these principles to every aspect of managing 39 | this project. Project maintainers who do not follow or enforce the Code of 40 | Conduct may be permanently removed from the project team. 41 | 42 | This Code of Conduct applies both within project spaces and in public spaces 43 | when an individual is representing the project or its community. 44 | 45 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 46 | reported by contacting a project maintainer at chaijs@keithcirkel.co.uk. All 47 | complaints will be reviewed and investigated and will result in a response that 48 | is deemed necessary and appropriate to the circumstances. Maintainers are 49 | obligated to maintain confidentiality with regard to the reporter of an 50 | incident. 51 | 52 | 53 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], 54 | version 1.3.0, available at 55 | [http://contributor-covenant.org/version/1/3/0/][version] 56 | 57 | [homepage]: http://contributor-covenant.org 58 | [version]: http://contributor-covenant.org/version/1/3/0/ 59 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Chai Contribution Guidelines 2 | 3 | We like to encourage you to contribute to the Chai.js repository. This should be as easy as possible for you but there are a few things to consider when contributing. The following guidelines for contribution should be followed if you want to submit a pull request or open an issue. 4 | 5 | Following these guidelines helps to communicate that you respect the time of the developers managing and developing this open source project. In return, they should reciprocate that respect in addressing your issue or assessing patches and features. 6 | 7 | #### Table of Contents 8 | 9 | - [TLDR;](#tldr) 10 | - [Contributing](#contributing) 11 | - [Bug Reports](#bugs) 12 | - [Feature Requests](#features) 13 | - [Pull Requests](#pull-requests) 14 | - [Releasing](#releasing) 15 | - [Support](#support) 16 | - [Resources](#resources) 17 | - [Core Contributors](#contributors) 18 | 19 | 20 | ## TLDR; 21 | 22 | - Creating an Issue or Pull Request requires a [GitHub](http://github.com) account. 23 | - Issue reports should be **clear**, **concise** and **reproducible**. Check to see if your issue has already been resolved in the [master]() branch or already reported in Chai's [GitHub Issue Tracker](https://github.com/chaijs/chai/issues). 24 | - Pull Requests must adhere to strict [coding style guidelines](https://github.com/chaijs/chai/wiki/Chai-Coding-Style-Guide). 25 | - In general, avoid submitting PRs for new Assertions without asking core contributors first. More than likely it would be better implemented as a plugin. 26 | - Additional support is available via the [Google Group](http://groups.google.com/group/chaijs) or on irc.freenode.net#chaijs. 27 | - **IMPORTANT**: By submitting a patch, you agree to allow the project owner to license your work under the same license as that used by the project. 28 | 29 | 30 | 31 | 32 | ## Contributing 33 | 34 | The issue tracker is the preferred channel for [bug reports](#bugs), 35 | [feature requests](#features) and [submitting pull 36 | requests](#pull-requests), but please respect the following restrictions: 37 | 38 | * Please **do not** use the issue tracker for personal support requests (use 39 | [Google Group](https://groups.google.com/forum/#!forum/chaijs) or IRC). 40 | * Please **do not** derail or troll issues. Keep the discussion on topic and 41 | respect the opinions of others 42 | 43 | 44 | ### Bug Reports 45 | 46 | A bug is a **demonstrable problem** that is caused by the code in the repository. 47 | 48 | Guidelines for bug reports: 49 | 50 | 1. **Use the GitHub issue search** — check if the issue has already been reported. 51 | 2. **Check if the issue has been fixed** — try to reproduce it using the latest `master` or development branch in the repository. 52 | 3. **Isolate the problem** — create a test case to demonstrate your issue. Provide either a repo, gist, or code sample to demonstrate you problem. 53 | 54 | A good bug report shouldn't leave others needing to chase you up for more information. Please try to be as detailed as possible in your report. What is your environment? What steps will reproduce the issue? What browser(s) and/or Node.js versions experience the problem? What would you expect to be the outcome? All these details will help people to fix any potential bugs. 55 | 56 | Example: 57 | 58 | > Short and descriptive example bug report title 59 | > 60 | > A summary of the issue and the browser/OS environment in which it occurs. If suitable, include the steps required to reproduce the bug. 61 | > 62 | > 1. This is the first step 63 | > 2. This is the second step 64 | > 3. Further steps, etc. 65 | > 66 | > `` - a link to the reduced test case OR 67 | > ```js 68 | > expect(a).to.equal('a'); 69 | > // code sample 70 | > ``` 71 | > 72 | > Any other information you want to share that is relevant to the issue being reported. This might include the lines of code that you have identified as causing the bug, and potential solutions (and your opinions on their merits). 73 | 74 | 75 | ### Feature Requests 76 | 77 | Feature requests are welcome. But take a moment to find out whether your idea fits with the scope and aims of the project. It's up to *you* to make a strong case to convince the project's developers of the merits of this feature. Please provide as much detail and context as possible. 78 | 79 | Furthermore, since Chai.js has a [robust plugin API](http://chaijs.com/guide/plugins/), we encourage you to publish **new Assertions** as plugins. If your feature is an enhancement to an **existing Assertion**, please propose your changes as an issue prior to opening a pull request. If the core Chai.js contributors feel your plugin would be better suited as a core assertion, they will invite you to open a PR in [chaijs/chai](https://github.com/chaijs/chai). 80 | 81 | 82 | ### Pull Requests 83 | 84 | - PRs for new core-assertions are advised against. 85 | - PRs for core-assertion bug fixes are always welcome. 86 | - PRs for enhancing the interfaces are always welcome. 87 | - PRs that increase test coverage are always welcome. 88 | - PRs are scrutinized for coding-style. 89 | 90 | Good pull requests - patches, improvements, new features - are a fantastic help. They should remain focused in scope and avoid containing unrelated commits. 91 | 92 | **Please ask first** before embarking on any significant pull request (e.g. implementing features, refactoring code), otherwise you risk spending a lot of time working on something that the project's developers might not want to merge into the project. 93 | 94 | Please adhere to the coding conventions used throughout a project (indentation, accurate comments, etc.) and any other requirements (such as test coverage). Please review the [Chai.js Coding Style Guide](https://github.com/chaijs/chai/wiki/Chai-Coding-Style-Guide). 95 | 96 | Follow this process if you'd like your work considered for inclusion in the project: 97 | 98 | 1. [Fork](http://help.github.com/fork-a-repo/) the project, clone your fork, and configure the remotes: 99 | 100 | ```bash 101 | # Clone your fork of the repo into the current directory 102 | git clone https://github.com// 103 | # Navigate to the newly cloned directory 104 | cd 105 | # Assign the original repo to a remote called "upstream" 106 | git remote add upstream https://github.com// 107 | ``` 108 | 109 | 2. If you cloned a while ago, get the latest changes from upstream: 110 | 111 | ```bash 112 | git checkout 113 | git pull upstream 114 | ``` 115 | 116 | 3. Create a new topic branch (off the main project development branch) to contain your feature, change, or fix: 117 | 118 | ```bash 119 | git checkout -b 120 | ``` 121 | 122 | 4. Commit your changes in logical chunks. Use Git's [interactive rebase](https://help.github.com/articles/interactive-rebase) feature to tidy up your commits before making them public. 123 | 124 | 5. Run you code to make sure it works. If you're still having problems please try to run `make clean` and then test your code again. 125 | 126 | ```bash 127 | npm test 128 | # when finished running tests... 129 | git checkout chai.js 130 | ``` 131 | 132 | 6. Locally merge (or rebase) the upstream development branch into your topic branch: 133 | 134 | ```bash 135 | git pull [--rebase] upstream 136 | ``` 137 | 138 | 7. Push your topic branch up to your fork: 139 | 140 | ```bash 141 | git push origin 142 | ``` 143 | 144 | 8. [Open a Pull Request](https://help.github.com/articles/using-pull-requests/) with a clear title and description. 145 | 146 | **IMPORTANT**: By submitting a patch, you agree to allow the project owner to license your work under the same license as that used by the project. 147 | 148 | 149 | ## Releasing 150 | 151 | Releases can be **prepared** by anyone with access to the code. 152 | 153 | Simply run `make release-major`, `make release-minor`, or `make-release-patch` 154 | and it will automatically do the following: 155 | 156 | - Build chai.js 157 | - Bump the version numbers across the project 158 | - Make a commit within git 159 | 160 | All you need to do is push the commit up and make a pull request, one of the core contributors will merge it and publish a release. 161 | 162 | ### Publishing a Release 163 | 164 | Anyone who is a core contributor (see the [Core Contributors Heading in the Readme](https://github.com/chaijs/chai#core-contributors)) can publish a release: 165 | 166 | 1. Go to the [Releases page on Github](https://github.com/chaijs/chai/releases) 167 | 2. Hit "Draft a new release" (if you can't see this, you're not a core contributor!) 168 | 3. Write human-friendly Release Notes based on changelog. 169 | - The release title is "x.x.x / YYYY-MM-DD" (where x.x.x is the version number) 170 | - If breaking changes, write migration tutorial(s) and reasoning. 171 | - Callouts for community contributions (PRs) with links to PR and contributing user. 172 | - Callouts for other fixes made by core contributors with links to issue. 173 | 4. Hit "Save Draft" and get other core contributors to check your work, or alternatively hit "Publish release" 174 | 5. That's it! 175 | 176 | 177 | ## Support 178 | 179 | 180 | ### Resources 181 | 182 | For most of the documentation you are going to want to visit [ChaiJS.com](http://chaijs.com). 183 | 184 | - [Getting Started Guide](http://chaijs.com/guide/) 185 | - [API Reference](http://chaijs.com/api/) 186 | - [Plugins](http://chaijs.com/plugins/) 187 | 188 | Alternatively, the [wiki](https://github.com/chaijs/chai/wiki) might be what you are looking for. 189 | 190 | - [Chai Coding Style Guide](https://github.com/chaijs/chai/wiki/Chai-Coding-Style-Guide) 191 | - [Third-party Resources](https://github.com/chaijs/chai/wiki/Third-Party-Resources) 192 | 193 | Or finally, you may find a core-contributor or like-minded developer in any of our support channels. 194 | 195 | - IRC: irc.freenode.org #chaijs 196 | - [Mailing List / Google Group](https://groups.google.com/forum/#!forum/chaijs) 197 | 198 | 199 | ### Core Contributors 200 | 201 | Feel free to reach out to any of the core-contributors with you questions or concerns. We will do our best to respond in a timely manner. 202 | 203 | - Jake Luer 204 | - GH: [@logicalparadox](https://github.com/logicalparadox) 205 | - TW: [@jakeluer](http://twitter.com/jakeluer) 206 | - IRC: logicalparadox 207 | - Veselin Todorov 208 | - GH: [@vesln](https://github.com/vesln/) 209 | - TW: [@vesln](http://twitter.com/vesln) 210 | - IRC: vesln 211 | - Keith Cirkel 212 | - GH: [@keithamus](https://github.com/keithamus) 213 | - TW: [@keithamus](http://twitter.com/keithamus) 214 | - IRC: keithamus 215 | - Lucas Fernandes da Costa 216 | - GH: [@lucasfcosta](https://github.com/lucasfcosta) 217 | - TW: [@lfernandescosta](https://twitter.com/lfernandescosta) 218 | - IRC: lucasfcosta 219 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Chai.js Assertion Library 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 | ChaiJS 4 | 5 |
6 | chai 7 |

8 | 9 |

10 | Chai is a BDD / TDD assertion library for node and the browser that can be delightfully paired with any javascript testing framework. 11 |

12 | 13 |

14 | 15 | downloads:? 19 | 20 | 21 | node:? 25 | 26 |
27 | 28 | Join the Slack chat 32 | 33 | 34 | Join the Gitter chat 38 | 39 | 40 | OpenCollective Backers 44 | 45 |

46 | 47 | For more information or to download plugins, view the [documentation](http://chaijs.com). 48 | 49 | ## What is Chai? 50 | 51 | Chai is an _assertion library_, similar to Node's built-in `assert`. It makes testing much easier by giving you lots of assertions you can run against your code. 52 | 53 | ## Installation 54 | 55 | ### Node.js 56 | 57 | `chai` is available on [npm](http://npmjs.org). To install it, type: 58 | 59 | $ npm install --save-dev chai 60 | 61 | ### Browsers 62 | 63 | You can also use it within the browser; install via npm and use the `chai.js` file found within the download. For example: 64 | 65 | ```html 66 | 67 | ``` 68 | 69 | ## Usage 70 | 71 | Import the library in your code, and then pick one of the styles you'd like to use - either `assert`, `expect` or `should`: 72 | 73 | ```js 74 | import { assert } from 'chai'; // Using Assert style 75 | import { expect } from 'chai'; // Using Expect style 76 | import { should } from 'chai'; // Using Should style 77 | ``` 78 | 79 | ### Register the chai testing style globally 80 | 81 | ```js 82 | import 'chai/register-assert'; // Using Assert style 83 | import 'chai/register-expect'; // Using Expect style 84 | import 'chai/register-should'; // Using Should style 85 | ``` 86 | 87 | ### Import assertion styles as local variables 88 | 89 | ```js 90 | import { assert } from 'chai'; // Using Assert style 91 | import { expect } from 'chai'; // Using Expect style 92 | import { should } from 'chai'; // Using Should style 93 | should(); // Modifies `Object.prototype` 94 | 95 | import { expect, use } from 'chai'; // Creates local variables `expect` and `use`; useful for plugin use 96 | ``` 97 | 98 | ### Usage with Mocha 99 | 100 | ```bash 101 | mocha spec.js --require chai/register-assert.js # Using Assert style 102 | mocha spec.js --require chai/register-expect.js # Using Expect style 103 | mocha spec.js --require chai/register-should.js # Using Should style 104 | ``` 105 | 106 | [Read more about these styles in our docs](http://chaijs.com/guide/styles/). 107 | 108 | ## Plugins 109 | 110 | Chai offers a robust Plugin architecture for extending Chai's assertions and interfaces. 111 | 112 | - Need a plugin? View the [official plugin list](http://chaijs.com/plugins). 113 | - Want to build a plugin? Read the [plugin api documentation](http://chaijs.com/guide/plugins/). 114 | - Have a plugin and want it listed? Simply add the following keywords to your package.json: 115 | - `chai-plugin` 116 | - `browser` if your plugin works in the browser as well as Node.js 117 | - `browser-only` if your plugin does not work with Node.js 118 | 119 | ### Related Projects 120 | 121 | - [chaijs / chai-docs](https://github.com/chaijs/chai-docs): The chaijs.com website source code. 122 | - [chaijs / assertion-error](https://github.com/chaijs/assertion-error): Custom `Error` constructor thrown upon an assertion failing. 123 | - [chaijs / deep-eql](https://github.com/chaijs/deep-eql): Improved deep equality testing for Node.js and the browser. 124 | - [chaijs / check-error](https://github.com/chaijs/check-error): Error comparison and information related utility for Node.js and the browser. 125 | - [chaijs / loupe](https://github.com/chaijs/loupe): Inspect utility for Node.js and browsers. 126 | - [chaijs / pathval](https://github.com/chaijs/pathval): Object value retrieval given a string path. 127 | 128 | ### Contributing 129 | 130 | Thank you very much for considering to contribute! 131 | 132 | Please make sure you follow our [Code Of Conduct](https://github.com/chaijs/chai/blob/master/CODE_OF_CONDUCT.md) and we also strongly recommend reading our [Contributing Guide](https://github.com/chaijs/chai/blob/master/CONTRIBUTING.md). 133 | 134 | Here are a few issues other contributors frequently ran into when opening pull requests: 135 | 136 | - Please do not commit changes to the `chai.js` build. We do it once per release. 137 | - Before pushing your commits, please make sure you [rebase](https://github.com/chaijs/chai/blob/master/CONTRIBUTING.md#pull-requests) them. 138 | 139 | ### Contributors 140 | 141 | Please see the full 142 | [Contributors Graph](https://github.com/chaijs/chai/graphs/contributors) for our 143 | list of contributors. 144 | 145 | ### Core Contributors 146 | 147 | Feel free to reach out to any of the core contributors with your questions or 148 | concerns. We will do our best to respond in a timely manner. 149 | 150 | [![Jake Luer](https://avatars3.githubusercontent.com/u/58988?v=3&s=50)](https://github.com/logicalparadox) 151 | [![Veselin Todorov](https://avatars3.githubusercontent.com/u/330048?v=3&s=50)](https://github.com/vesln) 152 | [![Keith Cirkel](https://avatars3.githubusercontent.com/u/118266?v=3&s=50)](https://github.com/keithamus) 153 | [![Lucas Fernandes da Costa](https://avatars3.githubusercontent.com/u/6868147?v=3&s=50)](https://github.com/lucasfcosta) 154 | [![Grant Snodgrass](https://avatars3.githubusercontent.com/u/17260989?v=3&s=50)](https://github.com/meeber) 155 | -------------------------------------------------------------------------------- /ReleaseNotes.md: -------------------------------------------------------------------------------- 1 | # Release Notes 2 | 3 | ## Note 4 | 5 | As of 3.0.0, the ReleaseNotes.md file has been deprecated. [Please refer to the release notes available on Github](https://github.com/chaijs/chai/releases). Or 6 | [the release notes on the chaijs.com website](https://chaijs.com/releases). 7 | 8 | --- 9 | 10 | ## 2.3.0 / 2015-04-26 11 | 12 | Added `ownPropertyDescriptor` assertion: 13 | 14 | ```js 15 | expect('test').to.have.ownPropertyDescriptor('length'); 16 | expect('test').to.have.ownPropertyDescriptor('length', { enumerable: false, configurable: false, writable: false, value: 4 }); 17 | expect('test').not.to.have.ownPropertyDescriptor('length', { enumerable: false, configurable: false, writable: false, value: 3 }); 18 | expect('test').ownPropertyDescriptor('length').to.have.property('enumerable', false); 19 | expect('test').ownPropertyDescriptor('length').to.have.keys('value'); 20 | ``` 21 | 22 | ### Community Contributions 23 | 24 | #### Code Features & Fixes 25 | 26 | * [#408](https://github.com/chaijs/chai/pull/408) Add `ownPropertyDescriptor` 27 | assertion. 28 | By [@ljharb](https://github.com/ljharb) 29 | * [#422](https://github.com/chaijs/chai/pull/422) Improve ownPropertyDescriptor 30 | tests. 31 | By [@ljharb](https://github.com/ljharb) 32 | 33 | #### Documentation fixes 34 | 35 | * [#417](https://github.com/chaijs/chai/pull/417) Fix documentation typo 36 | By [@astorije](https://github.com/astorije) 37 | * [#423](https://github.com/chaijs/chai/pull/423) Fix inconsistency in docs. 38 | By [@ehntoo](https://github.com/ehntoo) 39 | 40 | 41 | ## 2.2.0 / 2015-03-26 42 | 43 | Deep property strings can now be escaped using `\\` - for example: 44 | 45 | ```js 46 | var deepCss = { '.link': { '[target]': 42 }}; 47 | expect(deepCss).to.have.deep.property('\\.link.\\[target\\]', 42) 48 | ``` 49 | 50 | ### Community Contributions 51 | 52 | #### Code Features & Fixes 53 | 54 | * [#402](https://github.com/chaijs/chai/pull/402) Allow escaping of deep 55 | property keys. 56 | By [@umireon](https://github.com/umireon) 57 | 58 | #### Documentation fixes 59 | 60 | * [#405](https://github.com/chaijs/chai/pull/405) Tweak documentation around 61 | deep property escaping. 62 | By [@keithamus](https://github.com/keithamus) 63 | 64 | 65 | ## 2.1.2 / 2015-03-15 66 | 67 | A minor bug fix. No new features. 68 | 69 | ### Community Contributions 70 | 71 | #### Code Features & Fixes 72 | 73 | * [#395](https://github.com/chaijs/chai/pull/395) Fix eval-related bugs with 74 | assert.operator ([#386](https://github.com/chaijs/chai/pull/386)). 75 | By [@cjqed](https://github.com/cjqed) 76 | 77 | ## 2.1.1 / 2015-03-04 78 | 79 | Two minor bugfixes. No new features. 80 | 81 | ### Community Contributions 82 | 83 | #### Code Features & Fixes 84 | 85 | * [#385](https://github.com/chaijs/chai/pull/385) Fix a bug (also described in 86 | [#387](https://github.com/chaijs/chai/pull/385)) where `deep.property` would not work with single 87 | key names. By [@eldritch-fossicker](https://github.com/eldritch-fossicker) 88 | * [#379](https://github.com/chaijs/chai/pull/379) Fix bug where tools which overwrite 89 | primitive prototypes, such as Babel or core-js would fail. 90 | By [@dcneiner](https://github.com/dcneiner) 91 | 92 | #### Documentation fixes 93 | 94 | * [#382](https://github.com/chaijs/chai/pull/382) Add doc for showDiff argument in assert. 95 | By [@astorije](https://github.com/astorije) 96 | * [#383](https://github.com/chaijs/chai/pull/383) Improve wording for truncateTreshold docs 97 | By [@gurdiga](https://github.com/gurdiga) 98 | * [#381](https://github.com/chaijs/chai/pull/381) Improve wording for assert.empty docs 99 | By [@astorije](https://github.com/astorije) 100 | 101 | ## 2.1.0 / 2015-02-23 102 | 103 | Small release; fixes an issue where the Chai lib was incorrectly reporting the 104 | version number. 105 | 106 | Adds new `should.fail()` and `expect.fail()` methods, which are convinience 107 | methods to throw Assertion Errors. 108 | 109 | ### Community Contributions 110 | 111 | #### Code Features & Fixes 112 | 113 | * [#356](https://github.com/chaijs/chai/pull/356) Add should.fail(), expect.fail(). By [@Soviut](https://github.com/Soviut) 114 | * [#374](https://github.com/chaijs/chai/pull/374) Increment version. By [@jmm](https://github.com/jmm) 115 | 116 | ## 2.0.0 / 2015-02-09 117 | 118 | Unfortunately with 1.10.0 - compatibility broke with older versions because of 119 | the `addChainableNoop`. This change has been reverted. 120 | 121 | Any plugins using `addChainableNoop` should cease to do so. 122 | 123 | Any developers wishing for this behaviour can use [dirty-chai](https://www.npmjs.com/package/dirty-chai) 124 | by [@joshperry](https://github.com/joshperry) 125 | 126 | ### Community Contributions 127 | 128 | #### Code Features & Fixes 129 | 130 | * [#361](https://github.com/chaijs/chai/pull/361) `.keys()` now accepts Objects, extracting keys from them. By [@gregglind](https://github.com/gregglind) 131 | * [#359](https://github.com/chaijs/chai/pull/359) `.keys()` no longer mutates passed arrays. By [@gregglind](https://github.com/gregglind) 132 | * [#349](https://github.com/chaijs/chai/pull/349) Add a new chainable keyword - `.which`. By [@toastynerd](https://github.com/toastynerd) 133 | * [#333](https://github.com/chaijs/chai/pull/333) Add `.change`, `.increase` and `.decrease` assertions. By [@cmpolis](https://github.com/cmpolis) 134 | * [#335](https://github.com/chaijs/chai/pull/335) `chai.util` is now exposed [@DingoEatingFuzz](https://github.com/DingoEatingFuzz) 135 | * [#328](https://github.com/chaijs/chai/pull/328) Add `.includes` and `.contains` aliases (for `.include` and `.contain`). By [@lo1tuma](https://github.com/lo1tuma) 136 | * [#313](https://github.com/chaijs/chai/pull/313) Add `.any.keys()` and `.all.keys()` qualifiers. By [@cjqed](https://github.com/cjqed) 137 | * [#312](https://github.com/chaijs/chai/pull/312) Add `assert.sameDeepMembers()`. By [@cjqed](https://github.com/cjqed) 138 | * [#311](https://github.com/chaijs/chai/pull/311) Add `assert.isAbove()` and `assert.isBelow()`. By [@cjqed](https://github.com/cjqed) 139 | * [#308](https://github.com/chaijs/chai/pull/308) `property` and `deep.property` now pass if a value is set to `undefined`. By [@prodatakey](https://github.com/prodatakey) 140 | * [#309](https://github.com/chaijs/chai/pull/309) optimize deep equal in Arrays. By [@ericdouglas](https://github.com/ericdouglas) 141 | * [#306](https://github.com/chaijs/chai/pull/306) revert #297 - allowing lint-friendly tests. By [@keithamus](https://github.com/keithamus) 142 | 143 | #### Documentation fixes 144 | 145 | * [#357](https://github.com/chaijs/chai/pull/357) Copyright year updated in docs. By [@danilovaz](https://github.com/danilovaz) 146 | * [#325](https://github.com/chaijs/chai/pull/325) Fix documentation for overwriteChainableMethod. By [@chasenlehara](https://github.com/chasenlehara) 147 | * [#334](https://github.com/chaijs/chai/pull/334) Typo fix. By [@hurrymaplelad](https://github.com/hurrymaplelad) 148 | * [#317](https://github.com/chaijs/chai/pull/317) Typo fix. By [@jasonkarns](https://github.com/jasonkarns) 149 | * [#318](https://github.com/chaijs/chai/pull/318) Typo fix. By [@jasonkarns](https://github.com/jasonkarns) 150 | * [#316](https://github.com/chaijs/chai/pull/316) Typo fix. By [@jasonkarns](https://github.com/jasonkarns) 151 | 152 | 153 | ## 1.10.0 / 2014-11-10 154 | 155 | The following changes are required if you are upgrading from the previous version: 156 | 157 | - **Users:** 158 | - No changes required 159 | - **Plugin Developers:** 160 | - Review `addChainableNoop` notes below. 161 | - **Core Contributors:** 162 | - Refresh `node_modules` folder for updated dependencies. 163 | 164 | ### Noop Function for Terminating Assertion Properties 165 | 166 | The following assertions can now also be used in the function-call form: 167 | 168 | * ok 169 | * true 170 | * false 171 | * null 172 | * undefined 173 | * exist 174 | * empty 175 | * arguments 176 | * Arguments 177 | 178 | The above list of assertions are property getters that assert immediately on 179 | access. Because of that, they were written to be used by terminating the assertion 180 | chain with a property access. 181 | 182 | ```js 183 | expect(true).to.be.true; 184 | foo.should.be.ok; 185 | ``` 186 | 187 | This syntax is definitely aesthetically pleasing but, if you are linting your 188 | test code, your linter will complain with an error something like "Expected an 189 | assignment or function call and instead saw an expression." Since the linter 190 | doesn't know about the property getter it assumes this line has no side-effects, 191 | and throws a warning in case you made a mistake. 192 | 193 | Squelching these errors is not a good solution as test code is getting to be 194 | just as important as, if not more than, production code. Catching syntactical 195 | errors in tests using static analysis is a great tool to help make sure that your 196 | tests are well-defined and free of typos. 197 | 198 | A better option was to provide a function-call form for these assertions so that 199 | the code's intent is more clear and the linters stop complaining about something 200 | looking off. This form is added in addition to the existing property access form 201 | and does not impact existing test code. 202 | 203 | ```js 204 | expect(true).to.be.true(); 205 | foo.should.be.ok(); 206 | ``` 207 | 208 | These forms can also be mixed in any way, these are all functionally identical: 209 | 210 | ```js 211 | expect(true).to.be.true.and.not.false(); 212 | expect(true).to.be.true().and.not.false; 213 | expect(true).to.be.true.and.not.false; 214 | ``` 215 | 216 | #### Plugin Authors 217 | 218 | If you would like to provide this function-call form for your terminating assertion 219 | properties, there is a new function to register these types of asserts. Instead 220 | of using `addProperty` to register terminating assertions, simply use `addChainableNoop` 221 | instead; the arguments to both are identical. The latter will make the assertion 222 | available in both the attribute and function-call forms and should have no impact 223 | on existing users of your plugin. 224 | 225 | ### Community Contributions 226 | 227 | - [#297](https://github.com/chaijs/chai/pull/297) Allow writing lint-friendly tests. [@joshperry](https://github.com/joshperry) 228 | - [#298](https://github.com/chaijs/chai/pull/298) Add check for logging `-0`. [@dasilvacontin](https://github.com/dasilvacontin) 229 | - [#300](https://github.com/chaijs/chai/pull/300) Fix #299: the test is defining global variables [@julienw](https://github.com/julienw) 230 | 231 | Thank you to all who took time to contribute! 232 | 233 | ## 1.9.2 / 2014-09-29 234 | 235 | The following changes are required if you are upgrading from the previous version: 236 | 237 | - **Users:** 238 | - No changes required 239 | - **Plugin Developers:** 240 | - No changes required 241 | - **Core Contributors:** 242 | - Refresh `node_modules` folder for updated dependencies. 243 | 244 | ### Community Contributions 245 | 246 | - [#264](https://github.com/chaijs/chai/pull/264) Show diff for keys assertions [@cjthompson](https://github.com/cjthompson) 247 | - [#267](https://github.com/chaijs/chai/pull/267) Use SVG badges [@shinnn](https://github.com/shinnn) 248 | - [#268](https://github.com/chaijs/chai/pull/268) Allow messages to be functions (sinon-compat) [@charlierudolph](https://github.com/charlierudolph) 249 | - [#269](https://github.com/chaijs/chai/pull/269) Remove unused argument for #lengthOf [@charlierudolph](https://github.com/charlierudolph) 250 | - [#275](https://github.com/chaijs/chai/pull/275) Rewrite pretty-printing HTML elements to prevent throwing internal errors [@DrRataplan](https://github.com/DrRataplan) 251 | - [#277](https://github.com/chaijs/chai/pull/277) Fix assert documentation for #sameMembers [@charlierudolph](https://github.com/charlierudolph) 252 | - [#279](https://github.com/chaijs/chai/pull/279) closeTo should check value's type before assertion [@mohayonao](https://github.com/mohayonao) 253 | - [#289](https://github.com/chaijs/chai/pull/289) satisfy is called twice [@charlierudolph](https://github.com/charlierudolph) 254 | - [#292](https://github.com/chaijs/chai/pull/292) resolve conflicts with node-webkit and global usage [@boneskull](https://github.com/boneskull) 255 | 256 | Thank you to all who took time to contribute! 257 | 258 | ## 1.9.1 / 2014-03-19 259 | 260 | The following changes are required if you are upgrading from the previous version: 261 | 262 | - **Users:** 263 | - Migrate configuration options to new interface. (see notes) 264 | - **Plugin Developers:** 265 | - No changes required 266 | - **Core Contributors:** 267 | - Refresh `node_modules` folder for updated dependencies. 268 | 269 | ### Configuration 270 | 271 | There have been requests for changes and additions to the configuration mechanisms 272 | and their impact in the Chai architecture. As such, we have decoupled the 273 | configuration from the `Assertion` constructor. This not only allows for centralized 274 | configuration, but will allow us to shift the responsibility from the `Assertion` 275 | constructor to the `assert` interface in future releases. 276 | 277 | These changes have been implemented in a non-breaking way, but a depretiation 278 | warning will be presented to users until they migrate. The old config method will 279 | be removed in either `v1.11.0` or `v2.0.0`, whichever comes first. 280 | 281 | #### Quick Migration 282 | 283 | ```js 284 | // change this: 285 | chai.Assertion.includeStack = true; 286 | chai.Assertion.showDiff = false; 287 | 288 | // ... to this: 289 | chai.config.includeStack = true; 290 | chai.config.showDiff = false; 291 | ``` 292 | 293 | #### All Config Options 294 | 295 | ##### config.includeStack 296 | 297 | - **@param** _{Boolean}_ 298 | - **@default** `false` 299 | 300 | User configurable property, influences whether stack trace is included in 301 | Assertion error message. Default of `false` suppresses stack trace in the error 302 | message. 303 | 304 | ##### config.showDiff 305 | 306 | - **@param** _{Boolean}_ 307 | - **@default** `true` 308 | 309 | User configurable property, influences whether or not the `showDiff` flag 310 | should be included in the thrown AssertionErrors. `false` will always be `false`; 311 | `true` will be true when the assertion has requested a diff be shown. 312 | 313 | ##### config.truncateThreshold **(NEW)** 314 | 315 | - **@param** _{Number}_ 316 | - **@default** `40` 317 | 318 | User configurable property, sets length threshold for actual and expected values 319 | in assertion errors. If this threshold is exceeded, the value is truncated. 320 | 321 | Set it to zero if you want to disable truncating altogether. 322 | 323 | ```js 324 | chai.config.truncateThreshold = 0; // disable truncating 325 | ``` 326 | 327 | ### Community Contributions 328 | 329 | - [#228](https://github.com/chaijs/chai/pull/228) Deep equality check for memebers. [@duncanbeevers](https://github.com/duncanbeevers) 330 | - [#247](https://github.com/chaijs/chai/pull/247) Proofreading. [@didorellano](https://github.com/didoarellano) 331 | - [#244](https://github.com/chaijs/chai/pull/244) Fix `contain`/`include` 1.9.0 regression. [@leider](https://github.com/leider) 332 | - [#233](https://github.com/chaijs/chai/pull/233) Improvements to `ssfi` for `assert` interface. [@refack](https://github.com/refack) 333 | - [#251](https://github.com/chaijs/chai/pull/251) New config option: object display threshold. [@romario333](https://github.com/romario333) 334 | 335 | Thank you to all who took time to contribute! 336 | 337 | ### Other Bug Fixes 338 | 339 | - [#183](https://github.com/chaijs/chai/issues/183) Allow `undefined` for actual. (internal api) 340 | - Update Karam(+plugins)/Istanbul to most recent versions. 341 | 342 | ## 1.9.0 / 2014-01-29 343 | 344 | The following changes are required if you are upgrading from the previous version: 345 | 346 | - **Users:** 347 | - No changes required 348 | - **Plugin Developers:** 349 | - Review [#219](https://github.com/chaijs/chai/pull/219). 350 | - **Core Contributors:** 351 | - Refresh `node_modules` folder for updated dependencies. 352 | 353 | ### Community Contributions 354 | 355 | - [#202](https://github.com/chaijs/chai/pull/201) Improve error message for .throw(). [@andreineculau](https://github.com/andreineculau) 356 | - [#217](https://github.com/chaijs/chai/pull/217) Chai tests can be run with `--watch`. [@demands](https://github.com/demands) 357 | - [#219](https://github.com/chaijs/chai/pull/219) Add overwriteChainableMethod utility. [@demands](https://github.com/demands) 358 | - [#224](https://github.com/chaijs/chai/pull/224) Return error on throw method to chain on error properties. [@vbardales](https://github.com/vbardales) 359 | - [#226](https://github.com/chaijs/chai/pull/226) Add `has` to language chains. [@duncanbeevers](https://github.com/duncanbeevers) 360 | - [#230](https://github.com/chaijs/chai/pull/230) Support `{a:1,b:2}.should.include({a:1})` [@jkroso](https://github.com/jkroso) 361 | - [#231](https://github.com/chaijs/chai/pull/231) Update Copyright notices to 2014 [@duncanbeevers](https://github.com/duncanbeevers) 362 | - [#232](https://github.com/chaijs/chai/pull/232) Avoid error instantiation if possible on assert.throws. [@laconbass](https://github.com/laconbass) 363 | 364 | Thank you to all who took time to contribute! 365 | 366 | ### Other Bug Fixes 367 | 368 | - [#225](https://github.com/chaijs/chai/pull/225) Improved AMD wrapper provided by upstream `component(1)`. 369 | - [#185](https://github.com/chaijs/chai/issues/185) `assert.throws()` returns thrown error for further assertions. 370 | - [#237](https://github.com/chaijs/chai/pull/237) Remove coveralls/jscoverage, include istanbul coverage report in travis test. 371 | - Update Karma and Sauce runner versions for consistent CI results. No more karma@canary. 372 | 373 | ## 1.8.1 / 2013-10-10 374 | 375 | The following changes are required if you are upgrading from the previous version: 376 | 377 | - **Users:** 378 | - Refresh `node_modules` folder for updated dependencies. 379 | - **Plugin Developers:** 380 | - No changes required 381 | - **Core Contributors:** 382 | - Refresh `node_modules` folder for updated dependencies. 383 | 384 | ### Browserify 385 | 386 | This is a small patch that updates the dependency tree so browserify users can install 387 | chai. (Remove conditional requires) 388 | 389 | ## 1.8.0 / 2013-09-18 390 | 391 | The following changes are required if you are upgrading from the previous version: 392 | 393 | - **Users:** 394 | - See `deep.equal` notes. 395 | - **Plugin Developers:** 396 | - No changes required 397 | - **Core Contributors:** 398 | - Refresh `node_modules` folder for updated dependencies. 399 | 400 | ### Deep Equals 401 | 402 | This version of Chai focused on a overhaul to the deep equal utility. The code for this 403 | tool has been removed from the core lib and can now be found at: 404 | [chai / deep-eql](https://github.com/chaijs/deep-eql). As stated in previous releases, 405 | this is part of a larger initiative to provide transparency, independent testing, and coverage for 406 | some of the more complicated internal tools. 407 | 408 | For the most part `.deep.equal` will behave the same as it has. However, in order to provide a 409 | consistent ruleset across all types being tested, the following changes have been made and _might_ 410 | require changes to your tests. 411 | 412 | **1.** Strict equality for non-traversable nodes according to [egal](http://wiki.ecmascript.org/doku.php?id=harmony:egal). 413 | 414 | _Previously:_ Non-traversable equal via `===`. 415 | 416 | ```js 417 | expect(NaN).to.deep.equal(NaN); 418 | expect(-0).to.not.deep.equal(+0); 419 | ``` 420 | 421 | **2.** Arguments are not Arrays (and all types must be equal): 422 | 423 | _Previously:_ Some crazy nonsense that led to empty arrays deep equaling empty objects deep equaling dates. 424 | 425 | ```js 426 | expect(arguments).to.not.deep.equal([]); 427 | expect(Array.prototype.slice.call(arguments)).to.deep.equal([]); 428 | ``` 429 | 430 | - [#156](https://github.com/chaijs/chai/issues/156) Empty object is eql to empty array 431 | - [#192](https://github.com/chaijs/chai/issues/192) empty object is eql to a Date object 432 | - [#194](https://github.com/chaijs/chai/issues/194) refactor deep-equal utility 433 | 434 | ### CI and Browser Testing 435 | 436 | Chai now runs the browser CI suite using [Karma](http://karma-runner.github.io/) directed at 437 | [SauceLabs](https://saucelabs.com/). This means we get to know where our browser support stands... 438 | and we get a cool badge: 439 | 440 | [![Selenium Test Status](https://saucelabs.com/browser-matrix/logicalparadox.svg)](https://saucelabs.com/u/logicalparadox) 441 | 442 | Look for the list of browsers/versions to expand over the coming releases. 443 | 444 | - [#195](https://github.com/chaijs/chai/issues/195) karma test framework 445 | 446 | ## 1.7.2 / 2013-06-27 447 | 448 | The following changes are required if you are upgrading from the previous version: 449 | 450 | - **Users:** 451 | - No changes required. 452 | - **Plugin Developers:** 453 | - No changes required 454 | - **Core Contributors:** 455 | - Refresh `node_modules` folder for updated dependencies. 456 | 457 | ### Coverage Reporting 458 | 459 | Coverage reporting has always been available for core-developers but the data has never been published 460 | for our end users. In our ongoing effort to improve accountability this data will now be published via 461 | the [coveralls.io](https://coveralls.io/) service. A badge has been added to the README and the full report 462 | can be viewed online at the [chai coveralls project](https://coveralls.io/r/chaijs/chai). Furthermore, PRs 463 | will receive automated messages indicating how their PR impacts test coverage. This service is tied to TravisCI. 464 | 465 | ### Other Fixes 466 | 467 | - [#175](https://github.com/chaijs/chai/issues/175) Add `bower.json`. (Fix ignore all) 468 | 469 | ## 1.7.1 / 2013-06-24 470 | 471 | The following changes are required if you are upgrading from the previous version: 472 | 473 | - **Users:** 474 | - No changes required. 475 | - **Plugin Developers:** 476 | - No changes required 477 | - **Core Contributors:** 478 | - Refresh `node_modules` folder for updated dependencies. 479 | 480 | ### Official Bower Support 481 | 482 | Support has been added for the Bower Package Manager ([bower.io])(http://bower.io/). Though 483 | Chai could be installed via Bower in the past, this update adds official support via the `bower.json` 484 | specification file. 485 | 486 | - [#175](https://github.com/chaijs/chai/issues/175) Add `bower.json`. 487 | 488 | ## 1.7.0 / 2013-06-17 489 | 490 | The following changes are required if you are upgrading from the previous version: 491 | 492 | - **Users:** 493 | - No changes required. 494 | - **Plugin Developers:** 495 | - Review AssertionError update notice. 496 | - **Core Contributors:** 497 | - Refresh `node_modules` folder for updated dependencies. 498 | 499 | ### AssertionError Update Notice 500 | 501 | Chai now uses [chaijs/assertion-error](https://github.com/chaijs/assertion-error) instead an internal 502 | constructor. This will allow for further iteration/experimentation of the AssertionError constructor 503 | independant of Chai. Future plans include stack parsing for callsite support. 504 | 505 | This update constructor has a different constructor param signature that conforms more with the standard 506 | `Error` object. If your plugin throws and `AssertionError` directly you will need to update your plugin 507 | with the new signature. 508 | 509 | ```js 510 | var AssertionError = require('chai').AssertionError; 511 | 512 | /** 513 | * previous 514 | * 515 | * @param {Object} options 516 | */ 517 | 518 | throw new AssertionError({ 519 | message: 'An assertion error occurred' 520 | , actual: actual 521 | , expect: expect 522 | , startStackFunction: arguments.callee 523 | , showStack: true 524 | }); 525 | 526 | /** 527 | * new 528 | * 529 | * @param {String} message 530 | * @param {Object} options 531 | * @param {Function} start stack function 532 | */ 533 | 534 | throw new AssertionError('An assertion error occurred', { 535 | actual: actual 536 | , expect: expect 537 | , showStack: true 538 | }, arguments.callee); 539 | 540 | // other signatures 541 | throw new AssertionError('An assertion error occurred'); 542 | throw new AssertionError('An assertion error occurred', null, arguments.callee); 543 | ``` 544 | 545 | #### External Dependencies 546 | 547 | This is the first non-developement dependency for Chai. As Chai continues to evolve we will begin adding 548 | more; the next will likely be improved type detection and deep equality. With Chai's userbase continually growing 549 | there is an higher need for accountability and documentation. External dependencies will allow us to iterate and 550 | test on features independent from our interfaces. 551 | 552 | Note: The browser packaged version `chai.js` will ALWAYS contain all dependencies needed to run Chai. 553 | 554 | ### Community Contributions 555 | 556 | - [#169](https://github.com/chaijs/chai/pull/169) Fix deep equal comparison for Date/Regexp types. [@katsgeorgeek](https://github.com/katsgeorgeek) 557 | - [#171](https://github.com/chaijs/chai/pull/171) Add `assert.notOk()`. [@Bartvds](https://github.com/Bartvds) 558 | - [#173](https://github.com/chaijs/chai/pull/173) Fix `inspect` utility. [@domenic](https://github.com/domenic) 559 | 560 | Thank you to all who took the time to contribute! 561 | 562 | ## 1.6.1 / 2013-06-05 563 | 564 | The following changes are required if you are upgrading from the previous version: 565 | 566 | - **Users:** 567 | - No changes required. 568 | - **Plugin Developers:** 569 | - No changes required. 570 | - **Core Contributors:** 571 | - Refresh `node_modules` folder for updated developement dependencies. 572 | 573 | ### Deep Equality 574 | 575 | Regular Expressions are now tested as part of all deep equality assertions. In previous versions 576 | they silently passed for all scenarios. Thanks to [@katsgeorgeek](https://github.com/katsgeorgeek) for the contribution. 577 | 578 | ### Community Contributions 579 | 580 | - [#161](https://github.com/chaijs/chai/pull/161) Fix documented name for assert interface's isDefined method. [@brandonpayton](https://github.com/brandonpayton) 581 | - [#168](https://github.com/chaijs/chai/pull/168) Fix comparison equality of two regexps for when using deep equality. [@katsgeorgeek](https://github.com/katsgeorgeek) 582 | 583 | Thank you to all who took the time to contribute! 584 | 585 | ### Additional Notes 586 | 587 | - Mocha has been locked at version `1.8.x` to ensure `mocha-phantomjs` compatibility. 588 | 589 | ## 1.6.0 / 2013-04-29 590 | 591 | The following changes are required if you are upgrading from the previous version: 592 | 593 | - **Users:** 594 | - No changes required. 595 | - **Plugin Developers:** 596 | - No changes required. 597 | - **Core Contributors:** 598 | - Refresh `node_modules` folder for updated developement dependencies. 599 | 600 | ### New Assertions 601 | 602 | #### Array Members Inclusion 603 | 604 | Asserts that the target is a superset of `set`, or that the target and `set` have the same members. 605 | Order is not taken into account. Thanks to [@NickHeiner](https://github.com/NickHeiner) for the contribution. 606 | 607 | ```js 608 | // (expect/should) full set 609 | expect([4, 2]).to.have.members([2, 4]); 610 | expect([5, 2]).to.not.have.members([5, 2, 1]); 611 | 612 | // (expect/should) inclusion 613 | expect([1, 2, 3]).to.include.members([3, 2]); 614 | expect([1, 2, 3]).to.not.include.members([3, 2, 8]); 615 | 616 | // (assert) full set 617 | assert.sameMembers([ 1, 2, 3 ], [ 2, 1, 3 ], 'same members'); 618 | 619 | // (assert) inclusion 620 | assert.includeMembers([ 1, 2, 3 ], [ 2, 1 ], 'include members'); 621 | 622 | ``` 623 | 624 | #### Non-inclusion for Assert Interface 625 | 626 | Most `assert` functions have a negative version, like `instanceOf()` has a corresponding `notInstaceOf()`. 627 | However `include()` did not have a corresponding `notInclude()`. This has been added. 628 | 629 | ```js 630 | assert.notInclude([ 1, 2, 3 ], 8); 631 | assert.notInclude('foobar', 'baz'); 632 | ``` 633 | 634 | ### Community Contributions 635 | 636 | - [#140](https://github.com/chaijs/chai/pull/140) Restore `call`/`apply` methods for plugin interface. [@RubenVerborgh](https://github.com/RubenVerborgh) 637 | - [#148](https://github.com/chaijs/chai/issues/148)/[#153](https://github.com/chaijs/chai/pull/153) Add `members` and `include.members` assertions. [#NickHeiner](https://github.com/NickHeiner) 638 | 639 | Thank you to all who took time to contribute! 640 | 641 | ### Other Bug Fixes 642 | 643 | - [#142](https://github.com/chaijs/chai/issues/142) `assert#include` will no longer silently pass on wrong-type haystack. 644 | - [#158](https://github.com/chaijs/chai/issues/158) `assert#notInclude` has been added. 645 | - Travis-CI now tests Node.js `v0.10.x`. Support for `v0.6.x` has been removed. `v0.8.x` is still tested as before. 646 | 647 | ## 1.5.0 / 2013-02-03 648 | 649 | ### Migration Requirements 650 | 651 | The following changes are required if you are upgrading from the previous version: 652 | 653 | - **Users:** 654 | - _Update [2013-02-04]:_ Some users may notice a small subset of deep equality assertions will no longer pass. This is the result of 655 | [#120](https://github.com/chaijs/chai/issues/120), an improvement to our deep equality algorithm. Users will need to revise their assertions 656 | to be more granular should this occur. Further information: [#139](https://github.com/chaijs/chai/issues/139). 657 | - **Plugin Developers:** 658 | - No changes required. 659 | - **Core Contributors:** 660 | - Refresh `node_modules` folder for updated developement dependencies. 661 | 662 | ### Community Contributions 663 | 664 | - [#126](https://github.com/chaijs/chai/pull/126): Add `eqls` alias for `eql`. [@RubenVerborgh](https://github.com/RubenVerborgh) 665 | - [#127](https://github.com/chaijs/chai/issues/127): Performance refactor for chainable methods. [@RubenVerborgh](https://github.com/RubenVerborgh) 666 | - [#133](https://github.com/chaijs/chai/pull/133): Assertion `.throw` support for primitives. [@RubenVerborgh](https://github.com/RubenVerborgh) 667 | - [#137](https://github.com/chaijs/chai/issues/137): Assertion `.throw` support for empty messages. [@timnew](https://github.com/timnew) 668 | - [#136](https://github.com/chaijs/chai/pull/136): Fix backward negation messages when using `.above()` and `.below()`. [@whatthejeff](https://github.com/whatthejeff) 669 | 670 | Thank you to all who took time to contribute! 671 | 672 | ### Other Bug Fixes 673 | 674 | - Improve type detection of `.a()`/`.an()` to work in cross-browser scenarios. 675 | - [#116](https://github.com/chaijs/chai/issues/116): `.throw()` has cleaner display of errors when WebKit browsers. 676 | - [#120](https://github.com/chaijs/chai/issues/120): `.eql()` now works to compare dom nodes in browsers. 677 | 678 | 679 | ### Usage Updates 680 | 681 | #### For Users 682 | 683 | **1. Component Support:** Chai now included the proper configuration to be installed as a 684 | [component](https://github.com/component/component). Component users are encouraged to consult 685 | [chaijs.com](http://chaijs.com) for the latest version number as using the master branch 686 | does not gaurantee stability. 687 | 688 | ```js 689 | // relevant component.json 690 | devDependencies: { 691 | "chaijs/chai": "1.5.0" 692 | } 693 | ``` 694 | 695 | Alternatively, bleeding-edge is available: 696 | 697 | $ component install chaijs/chai 698 | 699 | **2. Configurable showDiff:** Some test runners (such as [mocha](http://visionmedia.github.com/mocha/)) 700 | include support for showing the diff of strings and objects when an equality error occurs. Chai has 701 | already included support for this, however some users may not prefer this display behavior. To revert to 702 | no diff display, the following configuration is available: 703 | 704 | ```js 705 | chai.Assertion.showDiff = false; // diff output disabled 706 | chai.Assertion.showDiff = true; // default, diff output enabled 707 | ``` 708 | 709 | #### For Plugin Developers 710 | 711 | **1. New Utility - type**: The new utility `.type()` is available as a better implementation of `typeof` 712 | that can be used cross-browser. It handles the inconsistencies of Array, `null`, and `undefined` detection. 713 | 714 | - **@param** _{Mixed}_ object to detect type of 715 | - **@return** _{String}_ object type 716 | 717 | ```js 718 | chai.use(function (c, utils) { 719 | // some examples 720 | utils.type({}); // 'object' 721 | utils.type(null); // `null' 722 | utils.type(undefined); // `undefined` 723 | utils.type([]); // `array` 724 | }); 725 | ``` 726 | 727 | #### For Core Contributors 728 | 729 | **1. Browser Testing**: Browser testing of the `./chai.js` file is now available in the command line 730 | via PhantomJS. `make test` and Travis-CI will now also rebuild and test `./chai.js`. Consequently, all 731 | pull requests will now be browser tested in this way. 732 | 733 | _Note: Contributors opening pull requests should still NOT include the browser build._ 734 | 735 | **2. SauceLabs Testing**: Early SauceLab support has been enabled with the file `./support/mocha-cloud.js`. 736 | Those interested in trying it out should create a free [Open Sauce](https://saucelabs.com/signup/plan) account 737 | and include their credentials in `./test/auth/sauce.json`. 738 | -------------------------------------------------------------------------------- /eslint.config.js: -------------------------------------------------------------------------------- 1 | import jsdoc from "eslint-plugin-jsdoc"; 2 | import eslintjs from "@eslint/js"; 3 | 4 | const {configs: eslintConfigs} = eslintjs; 5 | 6 | export default [ 7 | jsdoc.configs["flat/recommended"], 8 | eslintConfigs["recommended"], 9 | { 10 | languageOptions: { 11 | // if we ever use more globals than this, pull in the `globals` package 12 | globals: { 13 | console: false 14 | } 15 | }, 16 | rules: { 17 | "no-var": "error", 18 | "jsdoc/require-param-description": "off", 19 | "jsdoc/require-returns-description": "off", 20 | "jsdoc/tag-lines": ["error", "any", { startLines: 1 }], 21 | "no-unused-vars": ["error", { 22 | argsIgnorePattern: "^_", 23 | caughtErrorsIgnorePattern: "^_" 24 | }] 25 | }, 26 | }, 27 | ]; 28 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | export * from './lib/chai.js'; 2 | -------------------------------------------------------------------------------- /lib/chai.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * chai 3 | * Copyright(c) 2011-2014 Jake Luer 4 | * MIT Licensed 5 | */ 6 | 7 | import * as util from './chai/utils/index.js'; 8 | import {AssertionError} from 'assertion-error'; 9 | import {config} from './chai/config.js'; 10 | import './chai/core/assertions.js'; 11 | import {expect} from './chai/interface/expect.js'; 12 | import {Assertion} from './chai/assertion.js'; 13 | import * as should from './chai/interface/should.js'; 14 | import {assert} from './chai/interface/assert.js'; 15 | 16 | const used = []; 17 | 18 | // Assertion Error 19 | export {AssertionError}; 20 | 21 | /** 22 | * # .use(function) 23 | * 24 | * Provides a way to extend the internals of Chai. 25 | * 26 | * @param {Function} fn 27 | * @returns {this} for chaining 28 | * @public 29 | */ 30 | export function use(fn) { 31 | const exports = { 32 | use, 33 | AssertionError, 34 | util, 35 | config, 36 | expect, 37 | assert, 38 | Assertion, 39 | ...should 40 | }; 41 | 42 | if (!~used.indexOf(fn)) { 43 | fn(exports, util); 44 | used.push(fn); 45 | } 46 | 47 | return exports; 48 | } 49 | 50 | // Utility Functions 51 | export {util}; 52 | 53 | // Configuration 54 | export {config}; 55 | 56 | // Primary `Assertion` prototype 57 | export * from './chai/assertion.js'; 58 | 59 | // Expect interface 60 | export * from './chai/interface/expect.js'; 61 | 62 | // Should interface 63 | export * from './chai/interface/should.js'; 64 | 65 | // Assert interface 66 | export * from './chai/interface/assert.js'; 67 | -------------------------------------------------------------------------------- /lib/chai/assertion.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * chai 3 | * http://chaijs.com 4 | * Copyright(c) 2011-2014 Jake Luer 5 | * MIT Licensed 6 | */ 7 | 8 | import {config} from './config.js'; 9 | import {AssertionError} from 'assertion-error'; 10 | import * as util from './utils/index.js'; 11 | 12 | export class Assertion { 13 | /** @type {{}} */ 14 | __flags = {} 15 | 16 | /** 17 | * Creates object for chaining. 18 | * `Assertion` objects contain metadata in the form of flags. Three flags can 19 | * be assigned during instantiation by passing arguments to this constructor: 20 | * 21 | * - `object`: This flag contains the target of the assertion. For example, in 22 | * the assertion `expect(numKittens).to.equal(7);`, the `object` flag will 23 | * contain `numKittens` so that the `equal` assertion can reference it when 24 | * needed. 25 | * 26 | * - `message`: This flag contains an optional custom error message to be 27 | * prepended to the error message that's generated by the assertion when it 28 | * fails. 29 | * 30 | * - `ssfi`: This flag stands for "start stack function indicator". It 31 | * contains a function reference that serves as the starting point for 32 | * removing frames from the stack trace of the error that's created by the 33 | * assertion when it fails. The goal is to provide a cleaner stack trace to 34 | * end users by removing Chai's internal functions. Note that it only works 35 | * in environments that support `Error.captureStackTrace`, and only when 36 | * `Chai.config.includeStack` hasn't been set to `false`. 37 | * 38 | * - `lockSsfi`: This flag controls whether or not the given `ssfi` flag 39 | * should retain its current value, even as assertions are chained off of 40 | * this object. This is usually set to `true` when creating a new assertion 41 | * from within another assertion. It's also temporarily set to `true` before 42 | * an overwritten assertion gets called by the overwriting assertion. 43 | * 44 | * - `eql`: This flag contains the deepEqual function to be used by the assertion. 45 | * 46 | * @param {unknown} obj target of the assertion 47 | * @param {string} [msg] (optional) custom error message 48 | * @param {Function} [ssfi] (optional) starting point for removing stack frames 49 | * @param {boolean} [lockSsfi] (optional) whether or not the ssfi flag is locked 50 | */ 51 | constructor(obj, msg, ssfi, lockSsfi) { 52 | util.flag(this, 'ssfi', ssfi || Assertion); 53 | util.flag(this, 'lockSsfi', lockSsfi); 54 | util.flag(this, 'object', obj); 55 | util.flag(this, 'message', msg); 56 | util.flag(this, 'eql', config.deepEqual || util.eql); 57 | 58 | return util.proxify(this); 59 | } 60 | 61 | /** @returns {boolean} */ 62 | static get includeStack() { 63 | console.warn( 64 | 'Assertion.includeStack is deprecated, use chai.config.includeStack instead.' 65 | ); 66 | return config.includeStack; 67 | } 68 | 69 | /** @param {boolean} value */ 70 | static set includeStack(value) { 71 | console.warn( 72 | 'Assertion.includeStack is deprecated, use chai.config.includeStack instead.' 73 | ); 74 | config.includeStack = value; 75 | } 76 | 77 | /** @returns {boolean} */ 78 | static get showDiff() { 79 | console.warn( 80 | 'Assertion.showDiff is deprecated, use chai.config.showDiff instead.' 81 | ); 82 | return config.showDiff; 83 | } 84 | 85 | /** @param {boolean} value */ 86 | static set showDiff(value) { 87 | console.warn( 88 | 'Assertion.showDiff is deprecated, use chai.config.showDiff instead.' 89 | ); 90 | config.showDiff = value; 91 | } 92 | 93 | /** 94 | * @param {string} name 95 | * @param {Function} fn 96 | */ 97 | static addProperty(name, fn) { 98 | util.addProperty(this.prototype, name, fn); 99 | } 100 | 101 | /** 102 | * @param {string} name 103 | * @param {Function} fn 104 | */ 105 | static addMethod(name, fn) { 106 | util.addMethod(this.prototype, name, fn); 107 | } 108 | 109 | /** 110 | * @param {string} name 111 | * @param {Function} fn 112 | * @param {Function} chainingBehavior 113 | */ 114 | static addChainableMethod(name, fn, chainingBehavior) { 115 | util.addChainableMethod(this.prototype, name, fn, chainingBehavior); 116 | } 117 | 118 | /** 119 | * @param {string} name 120 | * @param {Function} fn 121 | */ 122 | static overwriteProperty(name, fn) { 123 | util.overwriteProperty(this.prototype, name, fn); 124 | } 125 | 126 | /** 127 | * @param {string} name 128 | * @param {Function} fn 129 | */ 130 | static overwriteMethod(name, fn) { 131 | util.overwriteMethod(this.prototype, name, fn); 132 | } 133 | 134 | /** 135 | * @param {string} name 136 | * @param {Function} fn 137 | * @param {Function} chainingBehavior 138 | */ 139 | static overwriteChainableMethod(name, fn, chainingBehavior) { 140 | util.overwriteChainableMethod(this.prototype, name, fn, chainingBehavior); 141 | } 142 | 143 | /** 144 | * ### .assert(expression, message, negateMessage, expected, actual, showDiff) 145 | * 146 | * Executes an expression and check expectations. Throws AssertionError for reporting if test doesn't pass. 147 | * 148 | * @name assert 149 | * @param {unknown} _expr to be tested 150 | * @param {string | Function} msg or function that returns message to display if expression fails 151 | * @param {string | Function} _negateMsg or function that returns negatedMessage to display if negated expression fails 152 | * @param {unknown} expected value (remember to check for negation) 153 | * @param {unknown} _actual (optional) will default to `this.obj` 154 | * @param {boolean} showDiff (optional) when set to `true`, assert will display a diff in addition to the message if expression fails 155 | * @returns {void} 156 | */ 157 | assert(_expr, msg, _negateMsg, expected, _actual, showDiff) { 158 | var ok = util.test(this, arguments); 159 | if (false !== showDiff) showDiff = true; 160 | if (undefined === expected && undefined === _actual) showDiff = false; 161 | if (true !== config.showDiff) showDiff = false; 162 | 163 | if (!ok) { 164 | msg = util.getMessage(this, arguments); 165 | var actual = util.getActual(this, arguments); 166 | /** @type {Record} */ 167 | var assertionErrorObjectProperties = { 168 | actual: actual, 169 | expected: expected, 170 | showDiff: showDiff 171 | }; 172 | 173 | var operator = util.getOperator(this, arguments); 174 | if (operator) { 175 | assertionErrorObjectProperties.operator = operator; 176 | } 177 | 178 | throw new AssertionError( 179 | msg, 180 | assertionErrorObjectProperties, 181 | // @ts-expect-error Not sure what to do about these types yet 182 | config.includeStack ? this.assert : util.flag(this, 'ssfi') 183 | ); 184 | } 185 | } 186 | 187 | /** 188 | * Quick reference to stored `actual` value for plugin developers. 189 | * 190 | * @returns {unknown} 191 | */ 192 | get _obj() { 193 | return util.flag(this, 'object'); 194 | } 195 | 196 | /** 197 | * Quick reference to stored `actual` value for plugin developers. 198 | * 199 | * @param {unknown} val 200 | */ 201 | set _obj(val) { 202 | util.flag(this, 'object', val); 203 | } 204 | } 205 | -------------------------------------------------------------------------------- /lib/chai/config.js: -------------------------------------------------------------------------------- 1 | export const config = { 2 | /** 3 | * ### config.includeStack 4 | * 5 | * User configurable property, influences whether stack trace 6 | * is included in Assertion error message. Default of false 7 | * suppresses stack trace in the error message. 8 | * 9 | * chai.config.includeStack = true; // enable stack on error 10 | * 11 | * @param {boolean} 12 | * @public 13 | */ 14 | includeStack: false, 15 | 16 | /** 17 | * ### config.showDiff 18 | * 19 | * User configurable property, influences whether or not 20 | * the `showDiff` flag should be included in the thrown 21 | * AssertionErrors. `false` will always be `false`; `true` 22 | * will be true when the assertion has requested a diff 23 | * be shown. 24 | * 25 | * @param {boolean} 26 | * @public 27 | */ 28 | showDiff: true, 29 | 30 | /** 31 | * ### config.truncateThreshold 32 | * 33 | * User configurable property, sets length threshold for actual and 34 | * expected values in assertion errors. If this threshold is exceeded, for 35 | * example for large data structures, the value is replaced with something 36 | * like `[ Array(3) ]` or `{ Object (prop1, prop2) }`. 37 | * 38 | * Set it to zero if you want to disable truncating altogether. 39 | * 40 | * This is especially userful when doing assertions on arrays: having this 41 | * set to a reasonable large value makes the failure messages readily 42 | * inspectable. 43 | * 44 | * chai.config.truncateThreshold = 0; // disable truncating 45 | * 46 | * @param {number} 47 | * @public 48 | */ 49 | truncateThreshold: 40, 50 | 51 | /** 52 | * ### config.useProxy 53 | * 54 | * User configurable property, defines if chai will use a Proxy to throw 55 | * an error when a non-existent property is read, which protects users 56 | * from typos when using property-based assertions. 57 | * 58 | * Set it to false if you want to disable this feature. 59 | * 60 | * chai.config.useProxy = false; // disable use of Proxy 61 | * 62 | * This feature is automatically disabled regardless of this config value 63 | * in environments that don't support proxies. 64 | * 65 | * @param {boolean} 66 | * @public 67 | */ 68 | useProxy: true, 69 | 70 | /** 71 | * ### config.proxyExcludedKeys 72 | * 73 | * User configurable property, defines which properties should be ignored 74 | * instead of throwing an error if they do not exist on the assertion. 75 | * This is only applied if the environment Chai is running in supports proxies and 76 | * if the `useProxy` configuration setting is enabled. 77 | * By default, `then` and `inspect` will not throw an error if they do not exist on the 78 | * assertion object because the `.inspect` property is read by `util.inspect` (for example, when 79 | * using `console.log` on the assertion object) and `.then` is necessary for promise type-checking. 80 | * 81 | * // By default these keys will not throw an error if they do not exist on the assertion object 82 | * chai.config.proxyExcludedKeys = ['then', 'inspect']; 83 | * 84 | * @param {Array} 85 | * @public 86 | */ 87 | proxyExcludedKeys: ['then', 'catch', 'inspect', 'toJSON'], 88 | 89 | /** 90 | * ### config.deepEqual 91 | * 92 | * User configurable property, defines which a custom function to use for deepEqual 93 | * comparisons. 94 | * By default, the function used is the one from the `deep-eql` package without custom comparator. 95 | * 96 | * // use a custom comparator 97 | * chai.config.deepEqual = (expected, actual) => { 98 | * return chai.util.eql(expected, actual, { 99 | * comparator: (expected, actual) => { 100 | * // for non number comparison, use the default behavior 101 | * if(typeof expected !== 'number') return null; 102 | * // allow a difference of 10 between compared numbers 103 | * return typeof actual === 'number' && Math.abs(actual - expected) < 10 104 | * } 105 | * }) 106 | * }; 107 | * 108 | * @param {Function} 109 | * @public 110 | */ 111 | deepEqual: null 112 | }; 113 | -------------------------------------------------------------------------------- /lib/chai/interface/expect.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * chai 3 | * Copyright(c) 2011-2014 Jake Luer 4 | * MIT Licensed 5 | */ 6 | 7 | import * as chai from '../../../index.js'; 8 | import {Assertion} from '../assertion.js'; 9 | import {AssertionError} from 'assertion-error'; 10 | 11 | /** 12 | * @param {unknown} val 13 | * @param {string} message 14 | * @returns {Assertion} 15 | */ 16 | function expect(val, message) { 17 | return new Assertion(val, message); 18 | } 19 | 20 | export {expect}; 21 | 22 | /** 23 | * ### .fail([message]) 24 | * ### .fail(actual, expected, [message], [operator]) 25 | * 26 | * Throw a failure. 27 | * 28 | * expect.fail(); 29 | * expect.fail("custom error message"); 30 | * expect.fail(1, 2); 31 | * expect.fail(1, 2, "custom error message"); 32 | * expect.fail(1, 2, "custom error message", ">"); 33 | * expect.fail(1, 2, undefined, ">"); 34 | * 35 | * @name fail 36 | * @param {unknown} actual 37 | * @param {unknown} expected 38 | * @param {string} message 39 | * @param {string} operator 40 | * @namespace expect 41 | * @public 42 | */ 43 | expect.fail = function (actual, expected, message, operator) { 44 | if (arguments.length < 2) { 45 | message = actual; 46 | actual = undefined; 47 | } 48 | 49 | message = message || 'expect.fail()'; 50 | throw new AssertionError( 51 | message, 52 | { 53 | actual: actual, 54 | expected: expected, 55 | operator: operator 56 | }, 57 | chai.expect.fail 58 | ); 59 | }; 60 | -------------------------------------------------------------------------------- /lib/chai/interface/should.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * chai 3 | * Copyright(c) 2011-2014 Jake Luer 4 | * MIT Licensed 5 | */ 6 | 7 | import {Assertion} from '../assertion.js'; 8 | import {AssertionError} from 'assertion-error'; 9 | 10 | /** 11 | * @returns {void} 12 | */ 13 | function loadShould() { 14 | // explicitly define this method as function as to have it's name to include as `ssfi` 15 | /** 16 | * @returns {Assertion} 17 | */ 18 | function shouldGetter() { 19 | if ( 20 | this instanceof String || 21 | this instanceof Number || 22 | this instanceof Boolean || 23 | (typeof Symbol === 'function' && this instanceof Symbol) || 24 | (typeof BigInt === 'function' && this instanceof BigInt) 25 | ) { 26 | return new Assertion(this.valueOf(), null, shouldGetter); 27 | } 28 | return new Assertion(this, null, shouldGetter); 29 | } 30 | /** 31 | * @param {unknown} value 32 | */ 33 | function shouldSetter(value) { 34 | // See https://github.com/chaijs/chai/issues/86: this makes 35 | // `whatever.should = someValue` actually set `someValue`, which is 36 | // especially useful for `global.should = require('chai').should()`. 37 | // 38 | // Note that we have to use [[DefineProperty]] instead of [[Put]] 39 | // since otherwise we would trigger this very setter! 40 | Object.defineProperty(this, 'should', { 41 | value: value, 42 | enumerable: true, 43 | configurable: true, 44 | writable: true 45 | }); 46 | } 47 | // modify Object.prototype to have `should` 48 | Object.defineProperty(Object.prototype, 'should', { 49 | set: shouldSetter, 50 | get: shouldGetter, 51 | configurable: true 52 | }); 53 | 54 | let should = {}; 55 | 56 | /** 57 | * ### .fail([message]) 58 | * ### .fail(actual, expected, [message], [operator]) 59 | * 60 | * Throw a failure. 61 | * 62 | * should.fail(); 63 | * should.fail("custom error message"); 64 | * should.fail(1, 2); 65 | * should.fail(1, 2, "custom error message"); 66 | * should.fail(1, 2, "custom error message", ">"); 67 | * should.fail(1, 2, undefined, ">"); 68 | * 69 | * @name fail 70 | * @param {unknown} actual 71 | * @param {unknown} expected 72 | * @param {string} message 73 | * @param {string} operator 74 | * @namespace BDD 75 | * @public 76 | */ 77 | should.fail = function (actual, expected, message, operator) { 78 | if (arguments.length < 2) { 79 | message = actual; 80 | actual = undefined; 81 | } 82 | 83 | message = message || 'should.fail()'; 84 | throw new AssertionError( 85 | message, 86 | { 87 | actual: actual, 88 | expected: expected, 89 | operator: operator 90 | }, 91 | should.fail 92 | ); 93 | }; 94 | 95 | /** 96 | * ### .equal(actual, expected, [message]) 97 | * 98 | * Asserts non-strict equality (`==`) of `actual` and `expected`. 99 | * 100 | * should.equal(3, '3', '== coerces values to strings'); 101 | * 102 | * @name equal 103 | * @param {unknown} actual 104 | * @param {unknown} expected 105 | * @param {string} message 106 | * @namespace Should 107 | * @public 108 | */ 109 | should.equal = function (actual, expected, message) { 110 | new Assertion(actual, message).to.equal(expected); 111 | }; 112 | 113 | /** 114 | * ### .throw(function, [constructor/string/regexp], [string/regexp], [message]) 115 | * 116 | * Asserts that `function` will throw an error that is an instance of 117 | * `constructor`, or alternately that it will throw an error with message 118 | * matching `regexp`. 119 | * 120 | * should.throw(fn, 'function throws a reference error'); 121 | * should.throw(fn, /function throws a reference error/); 122 | * should.throw(fn, ReferenceError); 123 | * should.throw(fn, ReferenceError, 'function throws a reference error'); 124 | * should.throw(fn, ReferenceError, /function throws a reference error/); 125 | * 126 | * @name throw 127 | * @alias Throw 128 | * @param {Function} fn 129 | * @param {Error} errt 130 | * @param {RegExp} errs 131 | * @param {string} msg 132 | * @see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Error#Error_types 133 | * @namespace Should 134 | * @public 135 | */ 136 | should.Throw = function (fn, errt, errs, msg) { 137 | new Assertion(fn, msg).to.Throw(errt, errs); 138 | }; 139 | 140 | /** 141 | * ### .exist 142 | * 143 | * Asserts that the target is neither `null` nor `undefined`. 144 | * 145 | * var foo = 'hi'; 146 | * should.exist(foo, 'foo exists'); 147 | * 148 | * @param {unknown} val 149 | * @param {string} msg 150 | * @name exist 151 | * @namespace Should 152 | * @public 153 | */ 154 | should.exist = function (val, msg) { 155 | new Assertion(val, msg).to.exist; 156 | }; 157 | 158 | // negation 159 | should.not = {}; 160 | 161 | /** 162 | * ### .not.equal(actual, expected, [message]) 163 | * 164 | * Asserts non-strict inequality (`!=`) of `actual` and `expected`. 165 | * 166 | * should.not.equal(3, 4, 'these numbers are not equal'); 167 | * 168 | * @name not.equal 169 | * @param {unknown} actual 170 | * @param {unknown} expected 171 | * @param {string} msg 172 | * @namespace Should 173 | * @public 174 | */ 175 | should.not.equal = function (actual, expected, msg) { 176 | new Assertion(actual, msg).to.not.equal(expected); 177 | }; 178 | 179 | /** 180 | * ### .throw(function, [constructor/regexp], [message]) 181 | * 182 | * Asserts that `function` will _not_ throw an error that is an instance of 183 | * `constructor`, or alternately that it will not throw an error with message 184 | * matching `regexp`. 185 | * 186 | * should.not.throw(fn, Error, 'function does not throw'); 187 | * 188 | * @name not.throw 189 | * @alias not.Throw 190 | * @param {Function} fn 191 | * @param {Error} errt 192 | * @param {RegExp} errs 193 | * @param {string} msg 194 | * @see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Error#Error_types 195 | * @namespace Should 196 | * @public 197 | */ 198 | should.not.Throw = function (fn, errt, errs, msg) { 199 | new Assertion(fn, msg).to.not.Throw(errt, errs); 200 | }; 201 | 202 | /** 203 | * ### .not.exist 204 | * 205 | * Asserts that the target is neither `null` nor `undefined`. 206 | * 207 | * var bar = null; 208 | * should.not.exist(bar, 'bar does not exist'); 209 | * 210 | * @namespace Should 211 | * @name not.exist 212 | * @param {unknown} val 213 | * @param {string} msg 214 | * @public 215 | */ 216 | should.not.exist = function (val, msg) { 217 | new Assertion(val, msg).to.not.exist; 218 | }; 219 | 220 | should['throw'] = should['Throw']; 221 | should.not['throw'] = should.not['Throw']; 222 | 223 | return should; 224 | } 225 | 226 | export const should = loadShould; 227 | export const Should = loadShould; 228 | -------------------------------------------------------------------------------- /lib/chai/utils/addChainableMethod.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Chai - addChainingMethod utility 3 | * Copyright(c) 2012-2014 Jake Luer 4 | * MIT Licensed 5 | */ 6 | 7 | import {Assertion} from '../assertion.js'; 8 | import {addLengthGuard} from './addLengthGuard.js'; 9 | import {flag} from './flag.js'; 10 | import {proxify} from './proxify.js'; 11 | import {transferFlags} from './transferFlags.js'; 12 | 13 | /** 14 | * Module variables 15 | */ 16 | 17 | // Check whether `Object.setPrototypeOf` is supported 18 | let canSetPrototype = typeof Object.setPrototypeOf === 'function'; 19 | 20 | // Without `Object.setPrototypeOf` support, this module will need to add properties to a function. 21 | // However, some of functions' own props are not configurable and should be skipped. 22 | let testFn = function () {}; 23 | let excludeNames = Object.getOwnPropertyNames(testFn).filter(function (name) { 24 | let propDesc = Object.getOwnPropertyDescriptor(testFn, name); 25 | 26 | // Note: PhantomJS 1.x includes `callee` as one of `testFn`'s own properties, 27 | // but then returns `undefined` as the property descriptor for `callee`. As a 28 | // workaround, we perform an otherwise unnecessary type-check for `propDesc`, 29 | // and then filter it out if it's not an object as it should be. 30 | if (typeof propDesc !== 'object') return true; 31 | 32 | return !propDesc.configurable; 33 | }); 34 | 35 | // Cache `Function` properties 36 | let call = Function.prototype.call, 37 | apply = Function.prototype.apply; 38 | 39 | /** 40 | * ### .addChainableMethod(ctx, name, method, chainingBehavior) 41 | * 42 | * Adds a method to an object, such that the method can also be chained. 43 | * 44 | * utils.addChainableMethod(chai.Assertion.prototype, 'foo', function (str) { 45 | * var obj = utils.flag(this, 'object'); 46 | * new chai.Assertion(obj).to.be.equal(str); 47 | * }); 48 | * 49 | * Can also be accessed directly from `chai.Assertion`. 50 | * 51 | * chai.Assertion.addChainableMethod('foo', fn, chainingBehavior); 52 | * 53 | * The result can then be used as both a method assertion, executing both `method` and 54 | * `chainingBehavior`, or as a language chain, which only executes `chainingBehavior`. 55 | * 56 | * expect(fooStr).to.be.foo('bar'); 57 | * expect(fooStr).to.be.foo.equal('foo'); 58 | * 59 | * @param {object} ctx object to which the method is added 60 | * @param {string} name of method to add 61 | * @param {Function} method function to be used for `name`, when called 62 | * @param {Function} chainingBehavior function to be called every time the property is accessed 63 | * @namespace Utils 64 | * @name addChainableMethod 65 | * @public 66 | */ 67 | export function addChainableMethod(ctx, name, method, chainingBehavior) { 68 | if (typeof chainingBehavior !== 'function') { 69 | chainingBehavior = function () {}; 70 | } 71 | 72 | let chainableBehavior = { 73 | method: method, 74 | chainingBehavior: chainingBehavior 75 | }; 76 | 77 | // save the methods so we can overwrite them later, if we need to. 78 | if (!ctx.__methods) { 79 | ctx.__methods = {}; 80 | } 81 | ctx.__methods[name] = chainableBehavior; 82 | 83 | Object.defineProperty(ctx, name, { 84 | get: function chainableMethodGetter() { 85 | chainableBehavior.chainingBehavior.call(this); 86 | 87 | let chainableMethodWrapper = function () { 88 | // Setting the `ssfi` flag to `chainableMethodWrapper` causes this 89 | // function to be the starting point for removing implementation 90 | // frames from the stack trace of a failed assertion. 91 | // 92 | // However, we only want to use this function as the starting point if 93 | // the `lockSsfi` flag isn't set. 94 | // 95 | // If the `lockSsfi` flag is set, then this assertion is being 96 | // invoked from inside of another assertion. In this case, the `ssfi` 97 | // flag has already been set by the outer assertion. 98 | // 99 | // Note that overwriting a chainable method merely replaces the saved 100 | // methods in `ctx.__methods` instead of completely replacing the 101 | // overwritten assertion. Therefore, an overwriting assertion won't 102 | // set the `ssfi` or `lockSsfi` flags. 103 | if (!flag(this, 'lockSsfi')) { 104 | flag(this, 'ssfi', chainableMethodWrapper); 105 | } 106 | 107 | let result = chainableBehavior.method.apply(this, arguments); 108 | if (result !== undefined) { 109 | return result; 110 | } 111 | 112 | let newAssertion = new Assertion(); 113 | transferFlags(this, newAssertion); 114 | return newAssertion; 115 | }; 116 | 117 | addLengthGuard(chainableMethodWrapper, name, true); 118 | 119 | // Use `Object.setPrototypeOf` if available 120 | if (canSetPrototype) { 121 | // Inherit all properties from the object by replacing the `Function` prototype 122 | let prototype = Object.create(this); 123 | // Restore the `call` and `apply` methods from `Function` 124 | prototype.call = call; 125 | prototype.apply = apply; 126 | Object.setPrototypeOf(chainableMethodWrapper, prototype); 127 | } 128 | // Otherwise, redefine all properties (slow!) 129 | else { 130 | let asserterNames = Object.getOwnPropertyNames(ctx); 131 | asserterNames.forEach(function (asserterName) { 132 | if (excludeNames.indexOf(asserterName) !== -1) { 133 | return; 134 | } 135 | 136 | let pd = Object.getOwnPropertyDescriptor(ctx, asserterName); 137 | Object.defineProperty(chainableMethodWrapper, asserterName, pd); 138 | }); 139 | } 140 | 141 | transferFlags(this, chainableMethodWrapper); 142 | return proxify(chainableMethodWrapper); 143 | }, 144 | configurable: true 145 | }); 146 | } 147 | -------------------------------------------------------------------------------- /lib/chai/utils/addLengthGuard.js: -------------------------------------------------------------------------------- 1 | const fnLengthDesc = Object.getOwnPropertyDescriptor(function () {}, 'length'); 2 | 3 | /*! 4 | * Chai - addLengthGuard utility 5 | * Copyright(c) 2012-2014 Jake Luer 6 | * MIT Licensed 7 | */ 8 | 9 | /** 10 | * ### .addLengthGuard(fn, assertionName, isChainable) 11 | * 12 | * Define `length` as a getter on the given uninvoked method assertion. The 13 | * getter acts as a guard against chaining `length` directly off of an uninvoked 14 | * method assertion, which is a problem because it references `function`'s 15 | * built-in `length` property instead of Chai's `length` assertion. When the 16 | * getter catches the user making this mistake, it throws an error with a 17 | * helpful message. 18 | * 19 | * There are two ways in which this mistake can be made. The first way is by 20 | * chaining the `length` assertion directly off of an uninvoked chainable 21 | * method. In this case, Chai suggests that the user use `lengthOf` instead. The 22 | * second way is by chaining the `length` assertion directly off of an uninvoked 23 | * non-chainable method. Non-chainable methods must be invoked prior to 24 | * chaining. In this case, Chai suggests that the user consult the docs for the 25 | * given assertion. 26 | * 27 | * If the `length` property of functions is unconfigurable, then return `fn` 28 | * without modification. 29 | * 30 | * Note that in ES6, the function's `length` property is configurable, so once 31 | * support for legacy environments is dropped, Chai's `length` property can 32 | * replace the built-in function's `length` property, and this length guard will 33 | * no longer be necessary. In the mean time, maintaining consistency across all 34 | * environments is the priority. 35 | * 36 | * @param {Function} fn 37 | * @param {string} assertionName 38 | * @param {boolean} isChainable 39 | * @returns {unknown} 40 | * @namespace Utils 41 | * @name addLengthGuard 42 | */ 43 | export function addLengthGuard(fn, assertionName, isChainable) { 44 | if (!fnLengthDesc.configurable) return fn; 45 | 46 | Object.defineProperty(fn, 'length', { 47 | get: function () { 48 | if (isChainable) { 49 | throw Error( 50 | 'Invalid Chai property: ' + 51 | assertionName + 52 | '.length. Due' + 53 | ' to a compatibility issue, "length" cannot directly follow "' + 54 | assertionName + 55 | '". Use "' + 56 | assertionName + 57 | '.lengthOf" instead.' 58 | ); 59 | } 60 | 61 | throw Error( 62 | 'Invalid Chai property: ' + 63 | assertionName + 64 | '.length. See' + 65 | ' docs for proper usage of "' + 66 | assertionName + 67 | '".' 68 | ); 69 | } 70 | }); 71 | 72 | return fn; 73 | } 74 | -------------------------------------------------------------------------------- /lib/chai/utils/addMethod.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Chai - addMethod utility 3 | * Copyright(c) 2012-2014 Jake Luer 4 | * MIT Licensed 5 | */ 6 | 7 | import {addLengthGuard} from './addLengthGuard.js'; 8 | import {flag} from './flag.js'; 9 | import {proxify} from './proxify.js'; 10 | import {transferFlags} from './transferFlags.js'; 11 | import {Assertion} from '../assertion.js'; 12 | 13 | /** 14 | * ### .addMethod(ctx, name, method) 15 | * 16 | * Adds a method to the prototype of an object. 17 | * 18 | * utils.addMethod(chai.Assertion.prototype, 'foo', function (str) { 19 | * var obj = utils.flag(this, 'object'); 20 | * new chai.Assertion(obj).to.be.equal(str); 21 | * }); 22 | * 23 | * Can also be accessed directly from `chai.Assertion`. 24 | * 25 | * chai.Assertion.addMethod('foo', fn); 26 | * 27 | * Then can be used as any other assertion. 28 | * 29 | * expect(fooStr).to.be.foo('bar'); 30 | * 31 | * @param {object} ctx object to which the method is added 32 | * @param {string} name of method to add 33 | * @param {Function} method function to be used for name 34 | * @namespace Utils 35 | * @name addMethod 36 | * @public 37 | */ 38 | export function addMethod(ctx, name, method) { 39 | let methodWrapper = function () { 40 | // Setting the `ssfi` flag to `methodWrapper` causes this function to be the 41 | // starting point for removing implementation frames from the stack trace of 42 | // a failed assertion. 43 | // 44 | // However, we only want to use this function as the starting point if the 45 | // `lockSsfi` flag isn't set. 46 | // 47 | // If the `lockSsfi` flag is set, then either this assertion has been 48 | // overwritten by another assertion, or this assertion is being invoked from 49 | // inside of another assertion. In the first case, the `ssfi` flag has 50 | // already been set by the overwriting assertion. In the second case, the 51 | // `ssfi` flag has already been set by the outer assertion. 52 | if (!flag(this, 'lockSsfi')) { 53 | flag(this, 'ssfi', methodWrapper); 54 | } 55 | 56 | let result = method.apply(this, arguments); 57 | if (result !== undefined) return result; 58 | 59 | let newAssertion = new Assertion(); 60 | transferFlags(this, newAssertion); 61 | return newAssertion; 62 | }; 63 | 64 | addLengthGuard(methodWrapper, name, false); 65 | ctx[name] = proxify(methodWrapper, name); 66 | } 67 | -------------------------------------------------------------------------------- /lib/chai/utils/addProperty.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Chai - addProperty utility 3 | * Copyright(c) 2012-2014 Jake Luer 4 | * MIT Licensed 5 | */ 6 | 7 | import {Assertion} from '../assertion.js'; 8 | import {flag} from './flag.js'; 9 | import {isProxyEnabled} from './isProxyEnabled.js'; 10 | import {transferFlags} from './transferFlags.js'; 11 | 12 | /** 13 | * ### .addProperty(ctx, name, getter) 14 | * 15 | * Adds a property to the prototype of an object. 16 | * 17 | * utils.addProperty(chai.Assertion.prototype, 'foo', function () { 18 | * var obj = utils.flag(this, 'object'); 19 | * new chai.Assertion(obj).to.be.instanceof(Foo); 20 | * }); 21 | * 22 | * Can also be accessed directly from `chai.Assertion`. 23 | * 24 | * chai.Assertion.addProperty('foo', fn); 25 | * 26 | * Then can be used as any other assertion. 27 | * 28 | * expect(myFoo).to.be.foo; 29 | * 30 | * @param {object} ctx object to which the property is added 31 | * @param {string} name of property to add 32 | * @param {Function} getter function to be used for name 33 | * @namespace Utils 34 | * @name addProperty 35 | * @public 36 | */ 37 | export function addProperty(ctx, name, getter) { 38 | getter = getter === undefined ? function () {} : getter; 39 | 40 | Object.defineProperty(ctx, name, { 41 | get: function propertyGetter() { 42 | // Setting the `ssfi` flag to `propertyGetter` causes this function to 43 | // be the starting point for removing implementation frames from the 44 | // stack trace of a failed assertion. 45 | // 46 | // However, we only want to use this function as the starting point if 47 | // the `lockSsfi` flag isn't set and proxy protection is disabled. 48 | // 49 | // If the `lockSsfi` flag is set, then either this assertion has been 50 | // overwritten by another assertion, or this assertion is being invoked 51 | // from inside of another assertion. In the first case, the `ssfi` flag 52 | // has already been set by the overwriting assertion. In the second 53 | // case, the `ssfi` flag has already been set by the outer assertion. 54 | // 55 | // If proxy protection is enabled, then the `ssfi` flag has already been 56 | // set by the proxy getter. 57 | if (!isProxyEnabled() && !flag(this, 'lockSsfi')) { 58 | flag(this, 'ssfi', propertyGetter); 59 | } 60 | 61 | let result = getter.call(this); 62 | if (result !== undefined) return result; 63 | 64 | let newAssertion = new Assertion(); 65 | transferFlags(this, newAssertion); 66 | return newAssertion; 67 | }, 68 | configurable: true 69 | }); 70 | } 71 | -------------------------------------------------------------------------------- /lib/chai/utils/compareByInspect.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Chai - compareByInspect utility 3 | * Copyright(c) 2011-2016 Jake Luer 4 | * MIT Licensed 5 | */ 6 | 7 | import {inspect} from './inspect.js'; 8 | 9 | /** 10 | * ### .compareByInspect(mixed, mixed) 11 | * 12 | * To be used as a compareFunction with Array.prototype.sort. Compares elements 13 | * using inspect instead of default behavior of using toString so that Symbols 14 | * and objects with irregular/missing toString can still be sorted without a 15 | * TypeError. 16 | * 17 | * @param {unknown} a first element to compare 18 | * @param {unknown} b second element to compare 19 | * @returns {number} -1 if 'a' should come before 'b'; otherwise 1 20 | * @name compareByInspect 21 | * @namespace Utils 22 | * @public 23 | */ 24 | export function compareByInspect(a, b) { 25 | return inspect(a) < inspect(b) ? -1 : 1; 26 | } 27 | -------------------------------------------------------------------------------- /lib/chai/utils/expectTypes.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Chai - expectTypes utility 3 | * Copyright(c) 2012-2014 Jake Luer 4 | * MIT Licensed 5 | */ 6 | 7 | import {AssertionError} from 'assertion-error'; 8 | import {flag} from './flag.js'; 9 | import {type} from './type-detect.js'; 10 | 11 | /** 12 | * ### .expectTypes(obj, types) 13 | * 14 | * Ensures that the object being tested against is of a valid type. 15 | * 16 | * utils.expectTypes(this, ['array', 'object', 'string']); 17 | * 18 | * @param {unknown} obj constructed Assertion 19 | * @param {Array} types A list of allowed types for this assertion 20 | * @namespace Utils 21 | * @name expectTypes 22 | * @public 23 | */ 24 | export function expectTypes(obj, types) { 25 | let flagMsg = flag(obj, 'message'); 26 | let ssfi = flag(obj, 'ssfi'); 27 | 28 | flagMsg = flagMsg ? flagMsg + ': ' : ''; 29 | 30 | obj = flag(obj, 'object'); 31 | types = types.map(function (t) { 32 | return t.toLowerCase(); 33 | }); 34 | types.sort(); 35 | 36 | // Transforms ['lorem', 'ipsum'] into 'a lorem, or an ipsum' 37 | let str = types 38 | .map(function (t, index) { 39 | let art = ~['a', 'e', 'i', 'o', 'u'].indexOf(t.charAt(0)) ? 'an' : 'a'; 40 | let or = types.length > 1 && index === types.length - 1 ? 'or ' : ''; 41 | return or + art + ' ' + t; 42 | }) 43 | .join(', '); 44 | 45 | let objType = type(obj).toLowerCase(); 46 | 47 | if ( 48 | !types.some(function (expected) { 49 | return objType === expected; 50 | }) 51 | ) { 52 | throw new AssertionError( 53 | flagMsg + 'object tested must be ' + str + ', but ' + objType + ' given', 54 | undefined, 55 | ssfi 56 | ); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /lib/chai/utils/flag.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Chai - flag utility 3 | * Copyright(c) 2012-2014 Jake Luer 4 | * MIT Licensed 5 | */ 6 | 7 | /** 8 | * ### .flag(object, key, [value]) 9 | * 10 | * Get or set a flag value on an object. If a 11 | * value is provided it will be set, else it will 12 | * return the currently set value or `undefined` if 13 | * the value is not set. 14 | * 15 | * utils.flag(this, 'foo', 'bar'); // setter 16 | * utils.flag(this, 'foo'); // getter, returns `bar` 17 | * 18 | * @template {{__flags?: {[key: PropertyKey]: unknown}}} T 19 | * @param {T} obj object constructed Assertion 20 | * @param {string} key 21 | * @param {unknown} [value] 22 | * @namespace Utils 23 | * @name flag 24 | * @returns {unknown | undefined} 25 | * @private 26 | */ 27 | export function flag(obj, key, value) { 28 | let flags = obj.__flags || (obj.__flags = Object.create(null)); 29 | if (arguments.length === 3) { 30 | flags[key] = value; 31 | } else { 32 | return flags[key]; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /lib/chai/utils/getActual.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Chai - getActual utility 3 | * Copyright(c) 2012-2014 Jake Luer 4 | * MIT Licensed 5 | */ 6 | 7 | /** 8 | * ### .getActual(object, [actual]) 9 | * 10 | * Returns the `actual` value for an Assertion. 11 | * 12 | * @param {object} obj object (constructed Assertion) 13 | * @param {unknown} args chai.Assertion.prototype.assert arguments 14 | * @returns {unknown} 15 | * @namespace Utils 16 | * @name getActual 17 | */ 18 | export function getActual(obj, args) { 19 | return args.length > 4 ? args[4] : obj._obj; 20 | } 21 | -------------------------------------------------------------------------------- /lib/chai/utils/getMessage.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Chai - message composition utility 3 | * Copyright(c) 2012-2014 Jake Luer 4 | * MIT Licensed 5 | */ 6 | 7 | import {flag} from './flag.js'; 8 | import {getActual} from './getActual.js'; 9 | import {objDisplay} from './objDisplay.js'; 10 | 11 | /** 12 | * ### .getMessage(object, message, negateMessage) 13 | * 14 | * Construct the error message based on flags 15 | * and template tags. Template tags will return 16 | * a stringified inspection of the object referenced. 17 | * 18 | * Message template tags: 19 | * - `#{this}` current asserted object 20 | * - `#{act}` actual value 21 | * - `#{exp}` expected value 22 | * 23 | * @param {object} obj object (constructed Assertion) 24 | * @param {IArguments} args chai.Assertion.prototype.assert arguments 25 | * @returns {string} 26 | * @namespace Utils 27 | * @name getMessage 28 | * @public 29 | */ 30 | export function getMessage(obj, args) { 31 | let negate = flag(obj, 'negate'); 32 | let val = flag(obj, 'object'); 33 | let expected = args[3]; 34 | let actual = getActual(obj, args); 35 | let msg = negate ? args[2] : args[1]; 36 | let flagMsg = flag(obj, 'message'); 37 | 38 | if (typeof msg === 'function') msg = msg(); 39 | msg = msg || ''; 40 | msg = msg 41 | .replace(/#\{this\}/g, function () { 42 | return objDisplay(val); 43 | }) 44 | .replace(/#\{act\}/g, function () { 45 | return objDisplay(actual); 46 | }) 47 | .replace(/#\{exp\}/g, function () { 48 | return objDisplay(expected); 49 | }); 50 | 51 | return flagMsg ? flagMsg + ': ' + msg : msg; 52 | } 53 | -------------------------------------------------------------------------------- /lib/chai/utils/getOperator.js: -------------------------------------------------------------------------------- 1 | import {flag} from './flag.js'; 2 | import {type} from './type-detect.js'; 3 | 4 | /** 5 | * @param {unknown} obj 6 | * @returns {boolean} 7 | */ 8 | function isObjectType(obj) { 9 | let objectType = type(obj); 10 | let objectTypes = ['Array', 'Object', 'Function']; 11 | 12 | return objectTypes.indexOf(objectType) !== -1; 13 | } 14 | 15 | /** 16 | * ### .getOperator(message) 17 | * 18 | * Extract the operator from error message. 19 | * Operator defined is based on below link 20 | * https://nodejs.org/api/assert.html#assert_assert. 21 | * 22 | * Returns the `operator` or `undefined` value for an Assertion. 23 | * 24 | * @param {object} obj object (constructed Assertion) 25 | * @param {unknown} args chai.Assertion.prototype.assert arguments 26 | * @returns {unknown} 27 | * @namespace Utils 28 | * @name getOperator 29 | * @public 30 | */ 31 | export function getOperator(obj, args) { 32 | let operator = flag(obj, 'operator'); 33 | let negate = flag(obj, 'negate'); 34 | let expected = args[3]; 35 | let msg = negate ? args[2] : args[1]; 36 | 37 | if (operator) { 38 | return operator; 39 | } 40 | 41 | if (typeof msg === 'function') msg = msg(); 42 | 43 | msg = msg || ''; 44 | if (!msg) { 45 | return undefined; 46 | } 47 | 48 | if (/\shave\s/.test(msg)) { 49 | return undefined; 50 | } 51 | 52 | let isObject = isObjectType(expected); 53 | if (/\snot\s/.test(msg)) { 54 | return isObject ? 'notDeepStrictEqual' : 'notStrictEqual'; 55 | } 56 | 57 | return isObject ? 'deepStrictEqual' : 'strictEqual'; 58 | } 59 | -------------------------------------------------------------------------------- /lib/chai/utils/getOwnEnumerableProperties.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Chai - getOwnEnumerableProperties utility 3 | * Copyright(c) 2011-2016 Jake Luer 4 | * MIT Licensed 5 | */ 6 | 7 | import {getOwnEnumerablePropertySymbols} from './getOwnEnumerablePropertySymbols.js'; 8 | 9 | /** 10 | * ### .getOwnEnumerableProperties(object) 11 | * 12 | * This allows the retrieval of directly-owned enumerable property names and 13 | * symbols of an object. This function is necessary because Object.keys only 14 | * returns enumerable property names, not enumerable property symbols. 15 | * 16 | * @param {object} obj 17 | * @returns {Array} 18 | * @namespace Utils 19 | * @name getOwnEnumerableProperties 20 | * @public 21 | */ 22 | export function getOwnEnumerableProperties(obj) { 23 | return Object.keys(obj).concat(getOwnEnumerablePropertySymbols(obj)); 24 | } 25 | -------------------------------------------------------------------------------- /lib/chai/utils/getOwnEnumerablePropertySymbols.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Chai - getOwnEnumerablePropertySymbols utility 3 | * Copyright(c) 2011-2016 Jake Luer 4 | * MIT Licensed 5 | */ 6 | 7 | /** 8 | * ### .getOwnEnumerablePropertySymbols(object) 9 | * 10 | * This allows the retrieval of directly-owned enumerable property symbols of an 11 | * object. This function is necessary because Object.getOwnPropertySymbols 12 | * returns both enumerable and non-enumerable property symbols. 13 | * 14 | * @param {object} obj 15 | * @returns {Array} 16 | * @namespace Utils 17 | * @name getOwnEnumerablePropertySymbols 18 | * @public 19 | */ 20 | export function getOwnEnumerablePropertySymbols(obj) { 21 | if (typeof Object.getOwnPropertySymbols !== 'function') return []; 22 | 23 | return Object.getOwnPropertySymbols(obj).filter(function (sym) { 24 | return Object.getOwnPropertyDescriptor(obj, sym).enumerable; 25 | }); 26 | } 27 | -------------------------------------------------------------------------------- /lib/chai/utils/getProperties.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Chai - getProperties utility 3 | * Copyright(c) 2012-2014 Jake Luer 4 | * MIT Licensed 5 | */ 6 | 7 | /** 8 | * ### .getProperties(object) 9 | * 10 | * This allows the retrieval of property names of an object, enumerable or not, 11 | * inherited or not. 12 | * 13 | * @param {object} object 14 | * @returns {Array} 15 | * @namespace Utils 16 | * @name getProperties 17 | * @public 18 | */ 19 | export function getProperties(object) { 20 | let result = Object.getOwnPropertyNames(object); 21 | 22 | /** 23 | * @param {unknown} property 24 | */ 25 | function addProperty(property) { 26 | if (result.indexOf(property) === -1) { 27 | result.push(property); 28 | } 29 | } 30 | 31 | let proto = Object.getPrototypeOf(object); 32 | while (proto !== null) { 33 | Object.getOwnPropertyNames(proto).forEach(addProperty); 34 | proto = Object.getPrototypeOf(proto); 35 | } 36 | 37 | return result; 38 | } 39 | -------------------------------------------------------------------------------- /lib/chai/utils/index.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * chai 3 | * Copyright(c) 2011 Jake Luer 4 | * MIT Licensed 5 | */ 6 | 7 | // Dependencies that are used for multiple exports are required here only once 8 | import * as checkError from 'check-error'; 9 | 10 | // test utility 11 | export {test} from './test.js'; 12 | 13 | // type utility 14 | import {type} from './type-detect.js'; 15 | export {type}; 16 | 17 | // expectTypes utility 18 | export {expectTypes} from './expectTypes.js'; 19 | 20 | // message utility 21 | export {getMessage} from './getMessage.js'; 22 | 23 | // actual utility 24 | export {getActual} from './getActual.js'; 25 | 26 | // Inspect util 27 | export {inspect} from './inspect.js'; 28 | 29 | // Object Display util 30 | export {objDisplay} from './objDisplay.js'; 31 | 32 | // Flag utility 33 | export {flag} from './flag.js'; 34 | 35 | // Flag transferring utility 36 | export {transferFlags} from './transferFlags.js'; 37 | 38 | // Deep equal utility 39 | export {default as eql} from 'deep-eql'; 40 | 41 | // Deep path info 42 | export {getPathInfo, hasProperty} from 'pathval'; 43 | 44 | /** 45 | * Function name 46 | * 47 | * @param {Function} fn 48 | * @returns {string} 49 | */ 50 | export function getName(fn) { 51 | return fn.name; 52 | } 53 | 54 | // add Property 55 | export {addProperty} from './addProperty.js'; 56 | 57 | // add Method 58 | export {addMethod} from './addMethod.js'; 59 | 60 | // overwrite Property 61 | export {overwriteProperty} from './overwriteProperty.js'; 62 | 63 | // overwrite Method 64 | export {overwriteMethod} from './overwriteMethod.js'; 65 | 66 | // Add a chainable method 67 | export {addChainableMethod} from './addChainableMethod.js'; 68 | 69 | // Overwrite chainable method 70 | export {overwriteChainableMethod} from './overwriteChainableMethod.js'; 71 | 72 | // Compare by inspect method 73 | export {compareByInspect} from './compareByInspect.js'; 74 | 75 | // Get own enumerable property symbols method 76 | export {getOwnEnumerablePropertySymbols} from './getOwnEnumerablePropertySymbols.js'; 77 | 78 | // Get own enumerable properties method 79 | export {getOwnEnumerableProperties} from './getOwnEnumerableProperties.js'; 80 | 81 | // Checks error against a given set of criteria 82 | export {checkError}; 83 | 84 | // Proxify util 85 | export {proxify} from './proxify.js'; 86 | 87 | // addLengthGuard util 88 | export {addLengthGuard} from './addLengthGuard.js'; 89 | 90 | // isProxyEnabled helper 91 | export {isProxyEnabled} from './isProxyEnabled.js'; 92 | 93 | // isNaN method 94 | export {isNaN} from './isNaN.js'; 95 | 96 | // getOperator method 97 | export {getOperator} from './getOperator.js'; 98 | 99 | /** 100 | * Determines if an object is a `RegExp` 101 | * This is used since `instanceof` will not work in virtual contexts 102 | * 103 | * @param {*} obj Object to test 104 | * @returns {boolean} 105 | */ 106 | export function isRegExp(obj) { 107 | return Object.prototype.toString.call(obj) === '[object RegExp]'; 108 | } 109 | 110 | /** 111 | * Determines if an object is numeric or not 112 | * 113 | * @param {unknown} obj Object to test 114 | * @returns {boolean} 115 | */ 116 | export function isNumeric(obj) { 117 | return ['Number', 'BigInt'].includes(type(obj)); 118 | } 119 | -------------------------------------------------------------------------------- /lib/chai/utils/inspect.js: -------------------------------------------------------------------------------- 1 | // This is (almost) directly from Node.js utils 2 | // https://github.com/joyent/node/blob/f8c335d0caf47f16d31413f89aa28eda3878e3aa/lib/util.js 3 | 4 | import {inspect as _inspect} from 'loupe'; 5 | import {config} from '../config.js'; 6 | 7 | /** 8 | * ### .inspect(obj, [showHidden], [depth], [colors]) 9 | * 10 | * Echoes the value of a value. Tries to print the value out 11 | * in the best way possible given the different types. 12 | * 13 | * @param {object} obj The object to print out. 14 | * @param {boolean} showHidden Flag that shows hidden (not enumerable) 15 | * properties of objects. Default is false. 16 | * @param {number} depth Depth in which to descend in object. Default is 2. 17 | * @param {boolean} colors Flag to turn on ANSI escape codes to color the 18 | * output. Default is false (no coloring). 19 | * @returns {string} 20 | * @namespace Utils 21 | * @name inspect 22 | */ 23 | export function inspect(obj, showHidden, depth, colors) { 24 | let options = { 25 | colors: colors, 26 | depth: typeof depth === 'undefined' ? 2 : depth, 27 | showHidden: showHidden, 28 | truncate: config.truncateThreshold ? config.truncateThreshold : Infinity 29 | }; 30 | return _inspect(obj, options); 31 | } 32 | -------------------------------------------------------------------------------- /lib/chai/utils/isNaN.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Chai - isNaN utility 3 | * Copyright(c) 2012-2015 Sakthipriyan Vairamani 4 | * MIT Licensed 5 | */ 6 | 7 | export const isNaN = Number.isNaN; 8 | -------------------------------------------------------------------------------- /lib/chai/utils/isProxyEnabled.js: -------------------------------------------------------------------------------- 1 | import {config} from '../config.js'; 2 | 3 | /*! 4 | * Chai - isProxyEnabled helper 5 | * Copyright(c) 2012-2014 Jake Luer 6 | * MIT Licensed 7 | */ 8 | 9 | /** 10 | * ### .isProxyEnabled() 11 | * 12 | * Helper function to check if Chai's proxy protection feature is enabled. If 13 | * proxies are unsupported or disabled via the user's Chai config, then return 14 | * false. Otherwise, return true. 15 | * 16 | * @namespace Utils 17 | * @name isProxyEnabled 18 | * @returns {boolean} 19 | */ 20 | export function isProxyEnabled() { 21 | return ( 22 | config.useProxy && 23 | typeof Proxy !== 'undefined' && 24 | typeof Reflect !== 'undefined' 25 | ); 26 | } 27 | -------------------------------------------------------------------------------- /lib/chai/utils/objDisplay.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Chai - flag utility 3 | * Copyright(c) 2012-2014 Jake Luer 4 | * MIT Licensed 5 | */ 6 | 7 | import {inspect} from './inspect.js'; 8 | import {config} from '../config.js'; 9 | 10 | /** 11 | * ### .objDisplay(object) 12 | * 13 | * Determines if an object or an array matches 14 | * criteria to be inspected in-line for error 15 | * messages or should be truncated. 16 | * 17 | * @param {unknown} obj javascript object to inspect 18 | * @returns {string} stringified object 19 | * @name objDisplay 20 | * @namespace Utils 21 | * @public 22 | */ 23 | export function objDisplay(obj) { 24 | let str = inspect(obj), 25 | type = Object.prototype.toString.call(obj); 26 | 27 | if (config.truncateThreshold && str.length >= config.truncateThreshold) { 28 | if (type === '[object Function]') { 29 | return !obj.name || obj.name === '' 30 | ? '[Function]' 31 | : '[Function: ' + obj.name + ']'; 32 | } else if (type === '[object Array]') { 33 | return '[ Array(' + obj.length + ') ]'; 34 | } else if (type === '[object Object]') { 35 | let keys = Object.keys(obj), 36 | kstr = 37 | keys.length > 2 38 | ? keys.splice(0, 2).join(', ') + ', ...' 39 | : keys.join(', '); 40 | return '{ Object (' + kstr + ') }'; 41 | } else { 42 | return str; 43 | } 44 | } else { 45 | return str; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /lib/chai/utils/overwriteChainableMethod.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Chai - overwriteChainableMethod utility 3 | * Copyright(c) 2012-2014 Jake Luer 4 | * MIT Licensed 5 | */ 6 | 7 | import {Assertion} from '../assertion.js'; 8 | import {transferFlags} from './transferFlags.js'; 9 | 10 | /** 11 | * ### .overwriteChainableMethod(ctx, name, method, chainingBehavior) 12 | * 13 | * Overwrites an already existing chainable method 14 | * and provides access to the previous function or 15 | * property. Must return functions to be used for 16 | * name. 17 | * 18 | * utils.overwriteChainableMethod(chai.Assertion.prototype, 'lengthOf', 19 | * function (_super) { 20 | * } 21 | * , function (_super) { 22 | * } 23 | * ); 24 | * 25 | * Can also be accessed directly from `chai.Assertion`. 26 | * 27 | * chai.Assertion.overwriteChainableMethod('foo', fn, fn); 28 | * 29 | * Then can be used as any other assertion. 30 | * 31 | * expect(myFoo).to.have.lengthOf(3); 32 | * expect(myFoo).to.have.lengthOf.above(3); 33 | * 34 | * @param {object} ctx object whose method / property is to be overwritten 35 | * @param {string} name of method / property to overwrite 36 | * @param {Function} method function that returns a function to be used for name 37 | * @param {Function} chainingBehavior function that returns a function to be used for property 38 | * @namespace Utils 39 | * @name overwriteChainableMethod 40 | * @public 41 | */ 42 | export function overwriteChainableMethod(ctx, name, method, chainingBehavior) { 43 | let chainableBehavior = ctx.__methods[name]; 44 | 45 | let _chainingBehavior = chainableBehavior.chainingBehavior; 46 | chainableBehavior.chainingBehavior = 47 | function overwritingChainableMethodGetter() { 48 | let result = chainingBehavior(_chainingBehavior).call(this); 49 | if (result !== undefined) { 50 | return result; 51 | } 52 | 53 | let newAssertion = new Assertion(); 54 | transferFlags(this, newAssertion); 55 | return newAssertion; 56 | }; 57 | 58 | let _method = chainableBehavior.method; 59 | chainableBehavior.method = function overwritingChainableMethodWrapper() { 60 | let result = method(_method).apply(this, arguments); 61 | if (result !== undefined) { 62 | return result; 63 | } 64 | 65 | let newAssertion = new Assertion(); 66 | transferFlags(this, newAssertion); 67 | return newAssertion; 68 | }; 69 | } 70 | -------------------------------------------------------------------------------- /lib/chai/utils/overwriteMethod.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Chai - overwriteMethod utility 3 | * Copyright(c) 2012-2014 Jake Luer 4 | * MIT Licensed 5 | */ 6 | 7 | import {Assertion} from '../assertion.js'; 8 | import {addLengthGuard} from './addLengthGuard.js'; 9 | import {flag} from './flag.js'; 10 | import {proxify} from './proxify.js'; 11 | import {transferFlags} from './transferFlags.js'; 12 | 13 | /** 14 | * ### .overwriteMethod(ctx, name, fn) 15 | * 16 | * Overwrites an already existing method and provides 17 | * access to previous function. Must return function 18 | * to be used for name. 19 | * 20 | * utils.overwriteMethod(chai.Assertion.prototype, 'equal', function (_super) { 21 | * return function (str) { 22 | * var obj = utils.flag(this, 'object'); 23 | * if (obj instanceof Foo) { 24 | * new chai.Assertion(obj.value).to.equal(str); 25 | * } else { 26 | * _super.apply(this, arguments); 27 | * } 28 | * } 29 | * }); 30 | * 31 | * Can also be accessed directly from `chai.Assertion`. 32 | * 33 | * chai.Assertion.overwriteMethod('foo', fn); 34 | * 35 | * Then can be used as any other assertion. 36 | * 37 | * expect(myFoo).to.equal('bar'); 38 | * 39 | * @param {object} ctx object whose method is to be overwritten 40 | * @param {string} name of method to overwrite 41 | * @param {Function} method function that returns a function to be used for name 42 | * @namespace Utils 43 | * @name overwriteMethod 44 | * @public 45 | */ 46 | export function overwriteMethod(ctx, name, method) { 47 | let _method = ctx[name], 48 | _super = function () { 49 | throw new Error(name + ' is not a function'); 50 | }; 51 | 52 | if (_method && 'function' === typeof _method) _super = _method; 53 | 54 | let overwritingMethodWrapper = function () { 55 | // Setting the `ssfi` flag to `overwritingMethodWrapper` causes this 56 | // function to be the starting point for removing implementation frames from 57 | // the stack trace of a failed assertion. 58 | // 59 | // However, we only want to use this function as the starting point if the 60 | // `lockSsfi` flag isn't set. 61 | // 62 | // If the `lockSsfi` flag is set, then either this assertion has been 63 | // overwritten by another assertion, or this assertion is being invoked from 64 | // inside of another assertion. In the first case, the `ssfi` flag has 65 | // already been set by the overwriting assertion. In the second case, the 66 | // `ssfi` flag has already been set by the outer assertion. 67 | if (!flag(this, 'lockSsfi')) { 68 | flag(this, 'ssfi', overwritingMethodWrapper); 69 | } 70 | 71 | // Setting the `lockSsfi` flag to `true` prevents the overwritten assertion 72 | // from changing the `ssfi` flag. By this point, the `ssfi` flag is already 73 | // set to the correct starting point for this assertion. 74 | let origLockSsfi = flag(this, 'lockSsfi'); 75 | flag(this, 'lockSsfi', true); 76 | let result = method(_super).apply(this, arguments); 77 | flag(this, 'lockSsfi', origLockSsfi); 78 | 79 | if (result !== undefined) { 80 | return result; 81 | } 82 | 83 | let newAssertion = new Assertion(); 84 | transferFlags(this, newAssertion); 85 | return newAssertion; 86 | }; 87 | 88 | addLengthGuard(overwritingMethodWrapper, name, false); 89 | ctx[name] = proxify(overwritingMethodWrapper, name); 90 | } 91 | -------------------------------------------------------------------------------- /lib/chai/utils/overwriteProperty.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Chai - overwriteProperty utility 3 | * Copyright(c) 2012-2014 Jake Luer 4 | * MIT Licensed 5 | */ 6 | 7 | import {Assertion} from '../assertion.js'; 8 | import {flag} from './flag.js'; 9 | import {isProxyEnabled} from './isProxyEnabled.js'; 10 | import {transferFlags} from './transferFlags.js'; 11 | 12 | /** 13 | * ### .overwriteProperty(ctx, name, fn) 14 | * 15 | * Overwrites an already existing property getter and provides 16 | * access to previous value. Must return function to use as getter. 17 | * 18 | * utils.overwriteProperty(chai.Assertion.prototype, 'ok', function (_super) { 19 | * return function () { 20 | * var obj = utils.flag(this, 'object'); 21 | * if (obj instanceof Foo) { 22 | * new chai.Assertion(obj.name).to.equal('bar'); 23 | * } else { 24 | * _super.call(this); 25 | * } 26 | * } 27 | * }); 28 | * 29 | * Can also be accessed directly from `chai.Assertion`. 30 | * 31 | * chai.Assertion.overwriteProperty('foo', fn); 32 | * 33 | * Then can be used as any other assertion. 34 | * 35 | * expect(myFoo).to.be.ok; 36 | * 37 | * @param {object} ctx object whose property is to be overwritten 38 | * @param {string} name of property to overwrite 39 | * @param {Function} getter function that returns a getter function to be used for name 40 | * @namespace Utils 41 | * @name overwriteProperty 42 | * @public 43 | */ 44 | export function overwriteProperty(ctx, name, getter) { 45 | let _get = Object.getOwnPropertyDescriptor(ctx, name), 46 | _super = function () {}; 47 | 48 | if (_get && 'function' === typeof _get.get) _super = _get.get; 49 | 50 | Object.defineProperty(ctx, name, { 51 | get: function overwritingPropertyGetter() { 52 | // Setting the `ssfi` flag to `overwritingPropertyGetter` causes this 53 | // function to be the starting point for removing implementation frames 54 | // from the stack trace of a failed assertion. 55 | // 56 | // However, we only want to use this function as the starting point if 57 | // the `lockSsfi` flag isn't set and proxy protection is disabled. 58 | // 59 | // If the `lockSsfi` flag is set, then either this assertion has been 60 | // overwritten by another assertion, or this assertion is being invoked 61 | // from inside of another assertion. In the first case, the `ssfi` flag 62 | // has already been set by the overwriting assertion. In the second 63 | // case, the `ssfi` flag has already been set by the outer assertion. 64 | // 65 | // If proxy protection is enabled, then the `ssfi` flag has already been 66 | // set by the proxy getter. 67 | if (!isProxyEnabled() && !flag(this, 'lockSsfi')) { 68 | flag(this, 'ssfi', overwritingPropertyGetter); 69 | } 70 | 71 | // Setting the `lockSsfi` flag to `true` prevents the overwritten 72 | // assertion from changing the `ssfi` flag. By this point, the `ssfi` 73 | // flag is already set to the correct starting point for this assertion. 74 | let origLockSsfi = flag(this, 'lockSsfi'); 75 | flag(this, 'lockSsfi', true); 76 | let result = getter(_super).call(this); 77 | flag(this, 'lockSsfi', origLockSsfi); 78 | 79 | if (result !== undefined) { 80 | return result; 81 | } 82 | 83 | let newAssertion = new Assertion(); 84 | transferFlags(this, newAssertion); 85 | return newAssertion; 86 | }, 87 | configurable: true 88 | }); 89 | } 90 | -------------------------------------------------------------------------------- /lib/chai/utils/proxify.js: -------------------------------------------------------------------------------- 1 | import {config} from '../config.js'; 2 | import {flag} from './flag.js'; 3 | import {getProperties} from './getProperties.js'; 4 | import {isProxyEnabled} from './isProxyEnabled.js'; 5 | 6 | /*! 7 | * Chai - proxify utility 8 | * Copyright(c) 2012-2014 Jake Luer 9 | * MIT Licensed 10 | */ 11 | 12 | /** @type {PropertyKey[]} */ 13 | const builtins = ['__flags', '__methods', '_obj', 'assert']; 14 | 15 | /** 16 | * ### .proxify(object) 17 | * 18 | * Return a proxy of given object that throws an error when a non-existent 19 | * property is read. By default, the root cause is assumed to be a misspelled 20 | * property, and thus an attempt is made to offer a reasonable suggestion from 21 | * the list of existing properties. However, if a nonChainableMethodName is 22 | * provided, then the root cause is instead a failure to invoke a non-chainable 23 | * method prior to reading the non-existent property. 24 | * 25 | * If proxies are unsupported or disabled via the user's Chai config, then 26 | * return object without modification. 27 | * 28 | * @namespace Utils 29 | * @template {object} T 30 | * @param {T} obj 31 | * @param {string} [nonChainableMethodName] 32 | * @returns {T} 33 | */ 34 | export function proxify(obj, nonChainableMethodName) { 35 | if (!isProxyEnabled()) return obj; 36 | 37 | return new Proxy(obj, { 38 | get: function proxyGetter(target, property) { 39 | // This check is here because we should not throw errors on Symbol properties 40 | // such as `Symbol.toStringTag`. 41 | // The values for which an error should be thrown can be configured using 42 | // the `config.proxyExcludedKeys` setting. 43 | if ( 44 | typeof property === 'string' && 45 | config.proxyExcludedKeys.indexOf(property) === -1 && 46 | !Reflect.has(target, property) 47 | ) { 48 | // Special message for invalid property access of non-chainable methods. 49 | if (nonChainableMethodName) { 50 | throw Error( 51 | 'Invalid Chai property: ' + 52 | nonChainableMethodName + 53 | '.' + 54 | property + 55 | '. See docs for proper usage of "' + 56 | nonChainableMethodName + 57 | '".' 58 | ); 59 | } 60 | 61 | // If the property is reasonably close to an existing Chai property, 62 | // suggest that property to the user. Only suggest properties with a 63 | // distance less than 4. 64 | let suggestion = null; 65 | let suggestionDistance = 4; 66 | getProperties(target).forEach(function (prop) { 67 | if ( 68 | // we actually mean to check `Object.prototype` here 69 | // eslint-disable-next-line no-prototype-builtins 70 | !Object.prototype.hasOwnProperty(prop) && 71 | builtins.indexOf(prop) === -1 72 | ) { 73 | let dist = stringDistanceCapped(property, prop, suggestionDistance); 74 | if (dist < suggestionDistance) { 75 | suggestion = prop; 76 | suggestionDistance = dist; 77 | } 78 | } 79 | }); 80 | 81 | if (suggestion !== null) { 82 | throw Error( 83 | 'Invalid Chai property: ' + 84 | property + 85 | '. Did you mean "' + 86 | suggestion + 87 | '"?' 88 | ); 89 | } else { 90 | throw Error('Invalid Chai property: ' + property); 91 | } 92 | } 93 | 94 | // Use this proxy getter as the starting point for removing implementation 95 | // frames from the stack trace of a failed assertion. For property 96 | // assertions, this prevents the proxy getter from showing up in the stack 97 | // trace since it's invoked before the property getter. For method and 98 | // chainable method assertions, this flag will end up getting changed to 99 | // the method wrapper, which is good since this frame will no longer be in 100 | // the stack once the method is invoked. Note that Chai builtin assertion 101 | // properties such as `__flags` are skipped since this is only meant to 102 | // capture the starting point of an assertion. This step is also skipped 103 | // if the `lockSsfi` flag is set, thus indicating that this assertion is 104 | // being called from within another assertion. In that case, the `ssfi` 105 | // flag is already set to the outer assertion's starting point. 106 | if (builtins.indexOf(property) === -1 && !flag(target, 'lockSsfi')) { 107 | flag(target, 'ssfi', proxyGetter); 108 | } 109 | 110 | return Reflect.get(target, property); 111 | } 112 | }); 113 | } 114 | 115 | /** 116 | * # stringDistanceCapped(strA, strB, cap) 117 | * Return the Levenshtein distance between two strings, but no more than cap. 118 | * 119 | * @param {string} strA 120 | * @param {string} strB 121 | * @param {number} cap 122 | * @returns {number} min(string distance between strA and strB, cap) 123 | * @private 124 | */ 125 | function stringDistanceCapped(strA, strB, cap) { 126 | if (Math.abs(strA.length - strB.length) >= cap) { 127 | return cap; 128 | } 129 | 130 | let memo = []; 131 | // `memo` is a two-dimensional array containing distances. 132 | // memo[i][j] is the distance between strA.slice(0, i) and 133 | // strB.slice(0, j). 134 | for (let i = 0; i <= strA.length; i++) { 135 | memo[i] = Array(strB.length + 1).fill(0); 136 | memo[i][0] = i; 137 | } 138 | for (let j = 0; j < strB.length; j++) { 139 | memo[0][j] = j; 140 | } 141 | 142 | for (let i = 1; i <= strA.length; i++) { 143 | let ch = strA.charCodeAt(i - 1); 144 | for (let j = 1; j <= strB.length; j++) { 145 | if (Math.abs(i - j) >= cap) { 146 | memo[i][j] = cap; 147 | continue; 148 | } 149 | memo[i][j] = Math.min( 150 | memo[i - 1][j] + 1, 151 | memo[i][j - 1] + 1, 152 | memo[i - 1][j - 1] + (ch === strB.charCodeAt(j - 1) ? 0 : 1) 153 | ); 154 | } 155 | } 156 | 157 | return memo[strA.length][strB.length]; 158 | } 159 | -------------------------------------------------------------------------------- /lib/chai/utils/test.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Chai - test utility 3 | * Copyright(c) 2012-2014 Jake Luer 4 | * MIT Licensed 5 | */ 6 | 7 | import {flag} from './flag.js'; 8 | 9 | /** 10 | * ### .test(object, expression) 11 | * 12 | * Test an object for expression. 13 | * 14 | * @param {object} obj (constructed Assertion) 15 | * @param {unknown} args 16 | * @returns {unknown} 17 | * @namespace Utils 18 | * @name test 19 | */ 20 | export function test(obj, args) { 21 | let negate = flag(obj, 'negate'), 22 | expr = args[0]; 23 | return negate ? !expr : expr; 24 | } 25 | -------------------------------------------------------------------------------- /lib/chai/utils/transferFlags.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Chai - transferFlags utility 3 | * Copyright(c) 2012-2014 Jake Luer 4 | * MIT Licensed 5 | */ 6 | 7 | /** 8 | * ### .transferFlags(assertion, object, includeAll = true) 9 | * 10 | * Transfer all the flags for `assertion` to `object`. If 11 | * `includeAll` is set to `false`, then the base Chai 12 | * assertion flags (namely `object`, `ssfi`, `lockSsfi`, 13 | * and `message`) will not be transferred. 14 | * 15 | * var newAssertion = new Assertion(); 16 | * utils.transferFlags(assertion, newAssertion); 17 | * 18 | * var anotherAssertion = new Assertion(myObj); 19 | * utils.transferFlags(assertion, anotherAssertion, false); 20 | * 21 | * @param {import('../assertion.js').Assertion} assertion the assertion to transfer the flags from 22 | * @param {object} object the object to transfer the flags to; usually a new assertion 23 | * @param {boolean} includeAll 24 | * @namespace Utils 25 | * @name transferFlags 26 | * @private 27 | */ 28 | export function transferFlags(assertion, object, includeAll) { 29 | let flags = assertion.__flags || (assertion.__flags = Object.create(null)); 30 | 31 | if (!object.__flags) { 32 | object.__flags = Object.create(null); 33 | } 34 | 35 | includeAll = arguments.length === 3 ? includeAll : true; 36 | 37 | for (let flag in flags) { 38 | if ( 39 | includeAll || 40 | (flag !== 'object' && 41 | flag !== 'ssfi' && 42 | flag !== 'lockSsfi' && 43 | flag != 'message') 44 | ) { 45 | object.__flags[flag] = flags[flag]; 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /lib/chai/utils/type-detect.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {unknown} obj 3 | * @returns {string} 4 | */ 5 | export function type(obj) { 6 | if (typeof obj === 'undefined') { 7 | return 'undefined'; 8 | } 9 | 10 | if (obj === null) { 11 | return 'null'; 12 | } 13 | 14 | const stringTag = obj[Symbol.toStringTag]; 15 | if (typeof stringTag === 'string') { 16 | return stringTag; 17 | } 18 | const type = Object.prototype.toString.call(obj).slice(8, -1); 19 | return type; 20 | } 21 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "author": "Jake Luer ", 3 | "name": "chai", 4 | "type": "module", 5 | "description": "BDD/TDD assertion library for node.js and the browser. Test framework agnostic.", 6 | "keywords": [ 7 | "test", 8 | "assertion", 9 | "assert", 10 | "testing", 11 | "chai" 12 | ], 13 | "homepage": "http://chaijs.com", 14 | "license": "MIT", 15 | "contributors": [ 16 | "Jake Luer ", 17 | "Domenic Denicola (http://domenicdenicola.com)", 18 | "Veselin Todorov ", 19 | "John Firebaugh " 20 | ], 21 | "version": "0.0.0-development", 22 | "repository": { 23 | "type": "git", 24 | "url": "https://github.com/chaijs/chai" 25 | }, 26 | "bugs": { 27 | "url": "https://github.com/chaijs/chai/issues" 28 | }, 29 | "main": "./chai.js", 30 | "scripts": { 31 | "prebuild": "npm run clean", 32 | "build": "npm run build:esm", 33 | "build:esm": "esbuild --bundle --format=esm --keep-names --outfile=chai.js index.js", 34 | "format": "prettier --write lib", 35 | "pretest": "npm run lint && npm run build", 36 | "test": "npm run test-node && npm run test-chrome", 37 | "test-node": "c8 --99 --check-coverage mocha --require ./test/bootstrap/index.js test/*.js", 38 | "test-chrome": "web-test-runner --playwright", 39 | "lint": "npm run lint:js && npm run lint:format", 40 | "lint:js": "eslint lib/", 41 | "lint:format": "prettier --check lib", 42 | "lint:types": "tsc", 43 | "clean": "rm -rf chai.js coverage/" 44 | }, 45 | "engines": { 46 | "node": ">=12" 47 | }, 48 | "dependencies": { 49 | "assertion-error": "^2.0.1", 50 | "check-error": "^2.1.1", 51 | "deep-eql": "^5.0.1", 52 | "loupe": "^3.1.0", 53 | "pathval": "^2.0.0" 54 | }, 55 | "devDependencies": { 56 | "@eslint/js": "^9.17.0", 57 | "@rollup/plugin-commonjs": "^25.0.7", 58 | "@web/dev-server-rollup": "^0.6.1", 59 | "@web/test-runner": "^0.18.0", 60 | "@web/test-runner-playwright": "^0.11.0", 61 | "c8": "^10.1.3", 62 | "esbuild": "^0.25.0", 63 | "eslint": "^8.56.0", 64 | "eslint-plugin-jsdoc": "^48.0.4", 65 | "mocha": "^10.2.0", 66 | "prettier": "^3.4.2", 67 | "typescript": "~5.7.3" 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /register-assert.js: -------------------------------------------------------------------------------- 1 | import {assert} from './index.js'; 2 | 3 | globalThis.assert = assert; 4 | -------------------------------------------------------------------------------- /register-expect.js: -------------------------------------------------------------------------------- 1 | import {expect} from './index.js'; 2 | 3 | globalThis.expect = expect; 4 | -------------------------------------------------------------------------------- /register-should.js: -------------------------------------------------------------------------------- 1 | import {should} from './index.js'; 2 | 3 | globalThis.should = should(); 4 | -------------------------------------------------------------------------------- /test/auth/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chaijs/chai/9ad4c77cc91651fbaf0ed96cf43cc365f4e634af/test/auth/.gitkeep -------------------------------------------------------------------------------- /test/bootstrap/index.js: -------------------------------------------------------------------------------- 1 | import * as chai from '../../index.js'; 2 | 3 | var isStackSupported = false; 4 | if (typeof Error.captureStackTrace !== 'undefined') { 5 | try { 6 | throw Error(); 7 | } catch (err) { 8 | if (typeof err.stack !== 'undefined') isStackSupported = true; 9 | } 10 | } 11 | 12 | /** 13 | * Validate that the given function throws an error. 14 | * 15 | * By default, also validate that the thrown error's stack trace doesn't contain 16 | * Chai implementation frames. Stack trace validation can be disabled by 17 | * providing a truthy `skipStackTest` argument. 18 | * 19 | * Optionally validate some additional properties of the error: 20 | * 21 | * If val is a string, validate val equals the error's .message 22 | * If val is a regex, validate val matches the error's .message 23 | * If val is an object, validate val's props are included in the error object 24 | * 25 | * @param {Function} function that's expected to throw an error 26 | * @param {Mixed} expected properties of the expected error 27 | * @param {Boolean} skipStackTest if truthy, don't validate stack trace 28 | */ 29 | 30 | export function globalErr(fn, val, skipStackTest) { 31 | if (chai.util.type(fn) !== 'Function') 32 | throw new chai.AssertionError('Invalid fn'); 33 | 34 | try { 35 | fn(); 36 | } catch (err) { 37 | if (isStackSupported && !skipStackTest) { 38 | chai.expect(err).to.have.property('stack') 39 | .that.has.string('globalErr') 40 | .but.does.not.match( 41 | /at [a-zA-Z]*(Getter|Wrapper|(\.)*assert)/, 42 | 'implementation frames not properly filtered from stack trace' 43 | ); 44 | } 45 | 46 | switch (chai.util.type(val).toLowerCase()) { 47 | case 'undefined': return; 48 | case 'string': return chai.expect(err.message).to.equal(val); 49 | case 'regexp': return chai.expect(err.message).to.match(val); 50 | case 'object': return Object.keys(val).forEach(function (key) { 51 | chai.expect(err).to.have.property(key).and.to.deep.equal(val[key]); 52 | }); 53 | } 54 | 55 | throw new chai.AssertionError('Invalid val'); 56 | } 57 | 58 | throw new chai.AssertionError('Expected an error'); 59 | }; 60 | -------------------------------------------------------------------------------- /test/configuration.js: -------------------------------------------------------------------------------- 1 | import * as chai from '../index.js'; 2 | import {globalErr as err} from './bootstrap/index.js'; 3 | 4 | import '../register-should.js'; 5 | 6 | describe('configuration', function () { 7 | var assert = chai.assert; 8 | var expect = chai.expect; 9 | 10 | var origConfig; 11 | 12 | beforeEach(function() { 13 | // backup current config 14 | function clone(o) { 15 | return JSON.parse(JSON.stringify(o)); 16 | } 17 | origConfig = clone(chai.config); 18 | }); 19 | 20 | afterEach(function() { 21 | // restore config 22 | Object.keys(origConfig).forEach(function(key) { 23 | chai.config[key] = origConfig[key]; 24 | }); 25 | }); 26 | 27 | describe('includeStack', function() { 28 | // Skip tests if `Error.captureStackTrace` is unsupported 29 | if (typeof Error.captureStackTrace === 'undefined') return; 30 | 31 | try { 32 | throw Error(); 33 | } catch (err) { 34 | // Skip tests if `err.stack` is unsupported 35 | if (typeof err.stack === 'undefined') return; 36 | } 37 | 38 | // Create overwritten assertions that always fail 39 | before(function () { 40 | chai.util.addProperty(chai.Assertion.prototype, 'tmpProperty', function () {}); 41 | chai.util.overwriteProperty(chai.Assertion.prototype, 'tmpProperty', function () { 42 | return function () { 43 | this.assert(false); 44 | }; 45 | }); 46 | 47 | chai.util.addMethod(chai.Assertion.prototype, 'tmpMethod', function () {}); 48 | chai.util.overwriteMethod(chai.Assertion.prototype, 'tmpMethod', function () { 49 | return function () { 50 | this.assert(false); 51 | }; 52 | }); 53 | 54 | chai.util.addChainableMethod(chai.Assertion.prototype, 'tmpChainableMethod', function () {}, function () {}); 55 | chai.util.overwriteChainableMethod(chai.Assertion.prototype, 'tmpChainableMethod', function (_super) { 56 | return function () { 57 | this.assert(false); 58 | }; 59 | }, function () { 60 | return function () {}; 61 | }); 62 | }); 63 | 64 | // Delete overwritten assertions 65 | after(function () { 66 | delete chai.Assertion.prototype.tmpProperty; 67 | delete chai.Assertion.prototype.tmpMethod; 68 | delete chai.Assertion.prototype.tmpChainableMethod; 69 | }); 70 | 71 | describe('expect interface', function () { 72 | // Functions that always throw an error 73 | function badPropertyAssertion() { 74 | expect(42).to.be.false; 75 | } 76 | function badOverwrittenPropertyAssertion() { 77 | expect(42).tmpProperty; 78 | } 79 | function badMethodAssertion() { 80 | expect(42).to.equal(false); 81 | } 82 | function badOverwrittenMethodAssertion() { 83 | expect(42).tmpMethod(); 84 | } 85 | function badChainableMethodAssertion() { 86 | expect(42).to.be.a('string'); 87 | } 88 | function badOverwrittenChainableMethodAssertion() { 89 | expect(42).tmpChainableMethod(); 90 | } 91 | 92 | describe('when true', function () { 93 | describe('failed property assertions', function () { 94 | var caughtErr = '__PRETEST__'; 95 | 96 | before(function () { 97 | chai.config.includeStack = true; 98 | 99 | try { 100 | badPropertyAssertion(); 101 | } catch (err) { 102 | caughtErr = err; 103 | } 104 | }); 105 | 106 | it('should include Chai frames in stack trace', function () { 107 | expect(caughtErr.stack).to.contain('propertyGetter'); 108 | expect(caughtErr.stack).to.contain('proxyGetter'); 109 | }); 110 | 111 | it('should include user frames in stack trace', function () { 112 | expect(caughtErr.stack).to.contain('badPropertyAssertion'); 113 | }); 114 | }); 115 | 116 | describe('failed overwritten property assertions', function () { 117 | var caughtErr = '__PRETEST__'; 118 | 119 | before(function () { 120 | chai.config.includeStack = true; 121 | 122 | try { 123 | badOverwrittenPropertyAssertion(); 124 | } catch (err) { 125 | caughtErr = err; 126 | } 127 | }); 128 | 129 | it('should include Chai frames in stack trace', function () { 130 | expect(caughtErr.stack).to.contain('overwritingPropertyGetter'); 131 | expect(caughtErr.stack).to.contain('proxyGetter'); 132 | }); 133 | 134 | it('should include user frames in stack trace', function () { 135 | expect(caughtErr.stack).to.contain('badOverwrittenPropertyAssertion'); 136 | }); 137 | }); 138 | 139 | describe('failed method assertions', function () { 140 | var caughtErr = '__PRETEST__'; 141 | 142 | before(function () { 143 | chai.config.includeStack = true; 144 | 145 | try { 146 | badMethodAssertion(); 147 | } catch (err) { 148 | caughtErr = err; 149 | } 150 | }); 151 | 152 | it('should include Chai frames in stack trace', function () { 153 | expect(caughtErr.stack).to.contain('methodWrapper'); 154 | }); 155 | 156 | it('should include user frames in stack trace', function () { 157 | expect(caughtErr.stack).to.contain('badMethodAssertion'); 158 | }); 159 | }); 160 | 161 | describe('failed overwritten method assertions', function () { 162 | var caughtErr = '__PRETEST__'; 163 | 164 | before(function () { 165 | chai.config.includeStack = true; 166 | 167 | try { 168 | badOverwrittenMethodAssertion(); 169 | } catch (err) { 170 | caughtErr = err; 171 | } 172 | }); 173 | 174 | it('should include Chai frames in stack trace', function () { 175 | expect(caughtErr.stack).to.contain('overwritingMethodWrapper'); 176 | }); 177 | 178 | it('should include user frames in stack trace', function () { 179 | expect(caughtErr.stack).to.contain('badOverwrittenMethodAssertion'); 180 | }); 181 | }); 182 | 183 | describe('failed chainable method assertions', function () { 184 | var caughtErr = '__PRETEST__'; 185 | 186 | before(function () { 187 | chai.config.includeStack = true; 188 | 189 | try { 190 | badChainableMethodAssertion(); 191 | } catch (err) { 192 | caughtErr = err; 193 | } 194 | }); 195 | 196 | it('should include Chai frames in stack trace', function () { 197 | expect(caughtErr.stack).to.contain('chainableMethodWrapper'); 198 | }); 199 | 200 | it('should include user frames in stack trace', function () { 201 | expect(caughtErr.stack).to.contain('badChainableMethodAssertion'); 202 | }); 203 | }); 204 | 205 | describe('failed overwritten chainable method assertions', function () { 206 | var caughtErr = '__PRETEST__'; 207 | 208 | before(function () { 209 | chai.config.includeStack = true; 210 | 211 | try { 212 | badOverwrittenChainableMethodAssertion(); 213 | } catch (err) { 214 | caughtErr = err; 215 | } 216 | }); 217 | 218 | it('should include Chai frames in stack trace', function () { 219 | expect(caughtErr.stack).to.contain('overwritingChainableMethodWrapper'); 220 | }); 221 | 222 | it('should include user frames in stack trace', function () { 223 | expect(caughtErr.stack).to.contain('badOverwrittenChainableMethodAssertion'); 224 | }); 225 | }); 226 | }); 227 | 228 | describe('when false', function () { 229 | describe('failed property assertions', function () { 230 | var caughtErr = '__PRETEST__'; 231 | 232 | before(function () { 233 | chai.config.includeStack = false; 234 | 235 | try { 236 | badPropertyAssertion(); 237 | } catch (err) { 238 | caughtErr = err; 239 | } 240 | }); 241 | 242 | it('should not include Chai frames in stack trace', function () { 243 | expect(caughtErr.stack).to.not.contain('propertyGetter'); 244 | expect(caughtErr.stack).to.not.contain('proxyGetter'); 245 | }); 246 | 247 | it('should include user frames in stack trace', function () { 248 | expect(caughtErr.stack).to.contain('badPropertyAssertion'); 249 | }); 250 | }); 251 | 252 | describe('failed overwritten property assertions', function () { 253 | var caughtErr = '__PRETEST__'; 254 | 255 | before(function () { 256 | chai.config.includeStack = false; 257 | 258 | try { 259 | badOverwrittenPropertyAssertion(); 260 | } catch (err) { 261 | caughtErr = err; 262 | } 263 | }); 264 | 265 | it('should not include Chai frames in stack trace', function () { 266 | expect(caughtErr.stack).to.not.contain('overwritingPropertyGetter'); 267 | expect(caughtErr.stack).to.not.contain('proxyGetter'); 268 | }); 269 | 270 | it('should include user frames in stack trace', function () { 271 | expect(caughtErr.stack).to.contain('badOverwrittenPropertyAssertion'); 272 | }); 273 | }); 274 | 275 | describe('failed method assertions', function () { 276 | var caughtErr = '__PRETEST__'; 277 | 278 | before(function () { 279 | chai.config.includeStack = false; 280 | 281 | try { 282 | badMethodAssertion(); 283 | } catch (err) { 284 | caughtErr = err; 285 | } 286 | }); 287 | 288 | it('should not include Chai frames in stack trace', function () { 289 | expect(caughtErr.stack).to.not.contain('methodWrapper'); 290 | }); 291 | 292 | it('should include user frames in stack trace', function () { 293 | expect(caughtErr.stack).to.contain('badMethodAssertion'); 294 | }); 295 | }); 296 | 297 | describe('failed overwritten method assertions', function () { 298 | var caughtErr = '__PRETEST__'; 299 | 300 | before(function () { 301 | chai.config.includeStack = false; 302 | 303 | try { 304 | badOverwrittenMethodAssertion(); 305 | } catch (err) { 306 | caughtErr = err; 307 | } 308 | }); 309 | 310 | it('should not include Chai frames in stack trace', function () { 311 | expect(caughtErr.stack).to.not.contain('overwritingMethodWrapper'); 312 | }); 313 | 314 | it('should include user frames in stack trace', function () { 315 | expect(caughtErr.stack).to.contain('badOverwrittenMethodAssertion'); 316 | }); 317 | }); 318 | 319 | describe('failed chainable method assertions', function () { 320 | var caughtErr = '__PRETEST__'; 321 | 322 | before(function () { 323 | chai.config.includeStack = false; 324 | 325 | try { 326 | badChainableMethodAssertion(); 327 | } catch (err) { 328 | caughtErr = err; 329 | } 330 | }); 331 | 332 | it('should not include Chai frames in stack trace', function () { 333 | expect(caughtErr.stack).to.not.contain('chainableMethodWrapper'); 334 | }); 335 | 336 | it('should include user frames in stack trace', function () { 337 | expect(caughtErr.stack).to.contain('badChainableMethodAssertion'); 338 | }); 339 | }); 340 | 341 | describe('failed overwritten chainable method assertions', function () { 342 | var caughtErr = '__PRETEST__'; 343 | 344 | before(function () { 345 | chai.config.includeStack = false; 346 | 347 | try { 348 | badOverwrittenChainableMethodAssertion(); 349 | } catch (err) { 350 | caughtErr = err; 351 | } 352 | }); 353 | 354 | it('should not include Chai frames in stack trace', function () { 355 | expect(caughtErr.stack).to.not.contain('overwritingChainableMethodWrapper'); 356 | }); 357 | 358 | it('should include user frames in stack trace', function () { 359 | expect(caughtErr.stack).to.contain('badOverwrittenChainableMethodAssertion'); 360 | }); 361 | }); 362 | }); 363 | }); 364 | 365 | describe('should interface', function () { 366 | // Functions that always throw an error 367 | function badPropertyAssertion() { 368 | (42).should.be.false; 369 | } 370 | function badOverwrittenPropertyAssertion() { 371 | (42).should.tmpProperty; 372 | } 373 | function badMethodAssertion() { 374 | (42).should.equal(false); 375 | } 376 | function badOverwrittenMethodAssertion() { 377 | (42).should.tmpMethod(); 378 | } 379 | function badChainableMethodAssertion() { 380 | (42).should.be.a('string'); 381 | } 382 | function badOverwrittenChainableMethodAssertion() { 383 | (42).should.tmpChainableMethod(); 384 | } 385 | 386 | describe('when true', function () { 387 | describe('failed property assertions', function () { 388 | var caughtErr = '__PRETEST__'; 389 | 390 | before(function () { 391 | chai.config.includeStack = true; 392 | 393 | try { 394 | badPropertyAssertion(); 395 | } catch (err) { 396 | caughtErr = err; 397 | } 398 | }); 399 | 400 | it('should include Chai frames in stack trace', function () { 401 | expect(caughtErr.stack).to.contain('propertyGetter'); 402 | expect(caughtErr.stack).to.contain('proxyGetter'); 403 | }); 404 | 405 | it('should include user frames in stack trace', function () { 406 | expect(caughtErr.stack).to.contain('badPropertyAssertion'); 407 | }); 408 | }); 409 | 410 | describe('failed overwritten property assertions', function () { 411 | var caughtErr = '__PRETEST__'; 412 | 413 | before(function () { 414 | chai.config.includeStack = true; 415 | 416 | try { 417 | badOverwrittenPropertyAssertion(); 418 | } catch (err) { 419 | caughtErr = err; 420 | } 421 | }); 422 | 423 | it('should include Chai frames in stack trace', function () { 424 | expect(caughtErr.stack).to.contain('overwritingPropertyGetter'); 425 | expect(caughtErr.stack).to.contain('proxyGetter'); 426 | }); 427 | 428 | it('should include user frames in stack trace', function () { 429 | expect(caughtErr.stack).to.contain('badOverwrittenPropertyAssertion'); 430 | }); 431 | }); 432 | 433 | describe('failed method assertions', function () { 434 | var caughtErr = '__PRETEST__'; 435 | 436 | before(function () { 437 | chai.config.includeStack = true; 438 | 439 | try { 440 | badMethodAssertion(); 441 | } catch (err) { 442 | caughtErr = err; 443 | } 444 | }); 445 | 446 | it('should include Chai frames in stack trace', function () { 447 | expect(caughtErr.stack).to.contain('methodWrapper'); 448 | }); 449 | 450 | it('should include user frames in stack trace', function () { 451 | expect(caughtErr.stack).to.contain('badMethodAssertion'); 452 | }); 453 | }); 454 | 455 | describe('failed overwritten method assertions', function () { 456 | var caughtErr = '__PRETEST__'; 457 | 458 | before(function () { 459 | chai.config.includeStack = true; 460 | 461 | try { 462 | badOverwrittenMethodAssertion(); 463 | } catch (err) { 464 | caughtErr = err; 465 | } 466 | }); 467 | 468 | it('should include Chai frames in stack trace', function () { 469 | expect(caughtErr.stack).to.contain('overwritingMethodWrapper'); 470 | }); 471 | 472 | it('should include user frames in stack trace', function () { 473 | expect(caughtErr.stack).to.contain('badOverwrittenMethodAssertion'); 474 | }); 475 | }); 476 | 477 | describe('failed chainable method assertions', function () { 478 | var caughtErr = '__PRETEST__'; 479 | 480 | before(function () { 481 | chai.config.includeStack = true; 482 | 483 | try { 484 | badChainableMethodAssertion(); 485 | } catch (err) { 486 | caughtErr = err; 487 | } 488 | }); 489 | 490 | it('should include Chai frames in stack trace', function () { 491 | expect(caughtErr.stack).to.contain('chainableMethodWrapper'); 492 | }); 493 | 494 | it('should include user frames in stack trace', function () { 495 | expect(caughtErr.stack).to.contain('badChainableMethodAssertion'); 496 | }); 497 | }); 498 | 499 | describe('failed overwritten chainable method assertions', function () { 500 | var caughtErr = '__PRETEST__'; 501 | 502 | before(function () { 503 | chai.config.includeStack = true; 504 | 505 | try { 506 | badOverwrittenChainableMethodAssertion(); 507 | } catch (err) { 508 | caughtErr = err; 509 | } 510 | }); 511 | 512 | it('should include Chai frames in stack trace', function () { 513 | expect(caughtErr.stack).to.contain('overwritingChainableMethodWrapper'); 514 | }); 515 | 516 | it('should include user frames in stack trace', function () { 517 | expect(caughtErr.stack).to.contain('badOverwrittenChainableMethodAssertion'); 518 | }); 519 | }); 520 | }); 521 | 522 | describe('when false', function () { 523 | describe('failed property assertions', function () { 524 | var caughtErr = '__PRETEST__'; 525 | 526 | before(function () { 527 | chai.config.includeStack = false; 528 | 529 | try { 530 | badPropertyAssertion(); 531 | } catch (err) { 532 | caughtErr = err; 533 | } 534 | }); 535 | 536 | it('should not include Chai frames in stack trace', function () { 537 | expect(caughtErr.stack).to.not.contain('propertyGetter'); 538 | expect(caughtErr.stack).to.not.contain('proxyGetter'); 539 | }); 540 | 541 | it('should include user frames in stack trace', function () { 542 | expect(caughtErr.stack).to.contain('badPropertyAssertion'); 543 | }); 544 | }); 545 | 546 | describe('failed overwritten property assertions', function () { 547 | var caughtErr = '__PRETEST__'; 548 | 549 | before(function () { 550 | chai.config.includeStack = false; 551 | 552 | try { 553 | badOverwrittenPropertyAssertion(); 554 | } catch (err) { 555 | caughtErr = err; 556 | } 557 | }); 558 | 559 | it('should not include Chai frames in stack trace', function () { 560 | expect(caughtErr.stack).to.not.contain('overwritingPropertyGetter'); 561 | expect(caughtErr.stack).to.not.contain('proxyGetter'); 562 | }); 563 | 564 | it('should include user frames in stack trace', function () { 565 | expect(caughtErr.stack).to.contain('badOverwrittenPropertyAssertion'); 566 | }); 567 | }); 568 | 569 | describe('failed method assertions', function () { 570 | var caughtErr = '__PRETEST__'; 571 | 572 | before(function () { 573 | chai.config.includeStack = false; 574 | 575 | try { 576 | badMethodAssertion(); 577 | } catch (err) { 578 | caughtErr = err; 579 | } 580 | }); 581 | 582 | it('should not include Chai frames in stack trace', function () { 583 | expect(caughtErr.stack).to.not.contain('methodWrapper'); 584 | }); 585 | 586 | it('should include user frames in stack trace', function () { 587 | expect(caughtErr.stack).to.contain('badMethodAssertion'); 588 | }); 589 | }); 590 | 591 | describe('failed overwritten method assertions', function () { 592 | var caughtErr = '__PRETEST__'; 593 | 594 | before(function () { 595 | chai.config.includeStack = false; 596 | 597 | try { 598 | badOverwrittenMethodAssertion(); 599 | } catch (err) { 600 | caughtErr = err; 601 | } 602 | }); 603 | 604 | it('should not include Chai frames in stack trace', function () { 605 | expect(caughtErr.stack).to.not.contain('overwritingMethodWrapper'); 606 | }); 607 | 608 | it('should include user frames in stack trace', function () { 609 | expect(caughtErr.stack).to.contain('badOverwrittenMethodAssertion'); 610 | }); 611 | }); 612 | 613 | describe('failed chainable method assertions', function () { 614 | var caughtErr = '__PRETEST__'; 615 | 616 | before(function () { 617 | chai.config.includeStack = false; 618 | 619 | try { 620 | badChainableMethodAssertion(); 621 | } catch (err) { 622 | caughtErr = err; 623 | } 624 | }); 625 | 626 | it('should not include Chai frames in stack trace', function () { 627 | expect(caughtErr.stack).to.not.contain('chainableMethodWrapper'); 628 | }); 629 | 630 | it('should include user frames in stack trace', function () { 631 | expect(caughtErr.stack).to.contain('badChainableMethodAssertion'); 632 | }); 633 | }); 634 | 635 | describe('failed overwritten chainable method assertions', function () { 636 | var caughtErr = '__PRETEST__'; 637 | 638 | before(function () { 639 | chai.config.includeStack = false; 640 | 641 | try { 642 | badOverwrittenChainableMethodAssertion(); 643 | } catch (err) { 644 | caughtErr = err; 645 | } 646 | }); 647 | 648 | it('should not include Chai frames in stack trace', function () { 649 | expect(caughtErr.stack).to.not.contain('overwritingChainableMethodWrapper'); 650 | }); 651 | 652 | it('should include user frames in stack trace', function () { 653 | expect(caughtErr.stack).to.contain('badOverwrittenChainableMethodAssertion'); 654 | }); 655 | }); 656 | }); 657 | }); 658 | }); 659 | 660 | describe('truncateThreshold', function() { 661 | it('is 20', function() { 662 | chai.config.truncateThreshold = 20; 663 | 664 | err(function() { 665 | assert.deepEqual({v: 'something longer than 20'}, {v: 'x'}); 666 | }, "expected { Object (v) } to deeply equal { v: 'x' }"); 667 | }); 668 | 669 | it('is 0', function() { 670 | chai.config.truncateThreshold = 0; 671 | 672 | err(function() { 673 | assert.deepEqual({v: 'something longer than 20'}, {v: 'x'}); 674 | }, "expected { v: 'something longer than 20' } to deeply equal { v: 'x' }"); 675 | }); 676 | }); 677 | 678 | describe('deprecated properties', function() { 679 | var origWarnFn; 680 | var warnings; 681 | 682 | beforeEach(function() { 683 | origWarnFn = console.warn; 684 | warnings = []; 685 | console.warn = function(message) { 686 | warnings.push(message); 687 | }; 688 | }); 689 | 690 | afterEach(function() { 691 | console.warn = origWarnFn; 692 | }); 693 | 694 | it('Assertion.includeStack warns that it is deprecated', function() { 695 | chai.Assertion.includeStack; 696 | 697 | assert.equal(warnings.length, 1); 698 | assert.equal(warnings[0], 'Assertion.includeStack is deprecated, use chai.config.includeStack instead.'); 699 | 700 | chai.Assertion.includeStack = true; 701 | 702 | assert.equal(warnings.length, 2); 703 | assert.equal(warnings[1], 'Assertion.includeStack is deprecated, use chai.config.includeStack instead.'); 704 | }); 705 | 706 | it('Assertion.includeStack is kept in sync with config.includeStack', function() { 707 | assert.equal(chai.Assertion.includeStack, chai.config.includeStack); 708 | chai.Assertion.includeStack = !chai.Assertion.includeStack; 709 | assert.equal(chai.Assertion.includeStack, chai.config.includeStack); 710 | chai.config.includeStack = !chai.config.includeStack; 711 | assert.equal(chai.Assertion.includeStack, chai.config.includeStack); 712 | }); 713 | 714 | it('Assertion.showDiff warns that it is deprecated', function() { 715 | chai.Assertion.showDiff; 716 | 717 | assert.equal(warnings.length, 1); 718 | assert.equal(warnings[0], 'Assertion.showDiff is deprecated, use chai.config.showDiff instead.'); 719 | 720 | chai.Assertion.showDiff = true; 721 | 722 | assert.equal(warnings.length, 2); 723 | assert.equal(warnings[1], 'Assertion.showDiff is deprecated, use chai.config.showDiff instead.'); 724 | }); 725 | 726 | it('Assertion.showDiff is kept in sync with config.showDiff', function() { 727 | assert.equal(chai.Assertion.showDiff, chai.config.showDiff); 728 | chai.Assertion.showDiff = !chai.Assertion.showDiff; 729 | assert.equal(chai.Assertion.showDiff, chai.config.showDiff); 730 | chai.config.showDiff = !chai.config.showDiff; 731 | assert.equal(chai.Assertion.showDiff, chai.config.showDiff); 732 | }); 733 | }); 734 | 735 | describe('useProxy', function() { 736 | var readNoExistentProperty = function() { 737 | expect(false).to.be.tue; // typo: tue should be true 738 | }; 739 | 740 | it('should have default value equal to true', function() { 741 | expect(chai.config.useProxy).to.be.true; 742 | }); 743 | 744 | describe('when true', function() { 745 | it('should use proxy unless user\'s environment doesn\'t support', function() { 746 | expect(readNoExistentProperty).to.throw('Invalid Chai property: tue'); 747 | }); 748 | }); 749 | 750 | describe('when false', function() { 751 | it('should not use proxy', function() { 752 | chai.config.useProxy = false; 753 | 754 | expect(readNoExistentProperty).to.not.throw('Invalid Chai property: tue'); 755 | }); 756 | }); 757 | }); 758 | 759 | describe('proxyExcludedKeys', function() { 760 | var readNoExistentProperty = function(prop) { 761 | return function() { 762 | var assertion = expect(false); 763 | expect(assertion).to.not.have.key(prop); 764 | assertion[prop]; 765 | } 766 | }; 767 | 768 | it('should have default value equal to `[\'then\', \'catch\', \'inspect\', \'toJSON\']`', function() { 769 | expect(chai.config.proxyExcludedKeys).to.be.deep.equal(['then', 'catch', 'inspect', 'toJSON']); 770 | }); 771 | 772 | it('should not throw when accessing non-existing `then` and `inspect` in an environment with proxy support', function() { 773 | // Since these will not throw if the environment does not support proxies we don't need any `if` clause here 774 | expect(readNoExistentProperty('then')).to.not.throw(); 775 | expect(readNoExistentProperty('inspect')).to.not.throw(); 776 | }); 777 | 778 | it('should throw for properties which are not on the `proxyExcludedKeys` Array in an environment with proxy support', function() { 779 | chai.config.proxyExcludedKeys = []; 780 | 781 | expect(readNoExistentProperty('then')).to.throw('Invalid Chai property: then'); 782 | expect(readNoExistentProperty('inspect')).to.throw('Invalid Chai property: inspect'); 783 | }); 784 | }); 785 | 786 | describe('deepEqual', function() { 787 | it('should use custom deepEqual function for deepEqual comparison', function(){ 788 | chai.config.deepEqual = (expected, actual) => { 789 | return chai.util.eql(expected, actual, { 790 | comparator: (expected, actual) => { 791 | // for non number comparison, use the default behavior 792 | if(typeof expected !== 'number') return null; 793 | // allow a difference of 10 between compared numbers 794 | return typeof actual === 'number' && Math.abs(actual - expected) < 10 795 | } 796 | }) 797 | }; 798 | assert.deepEqual({v: 1}, {v: 10}); 799 | err(function() { 800 | assert.deepEqual({v: 1}, {v: 100}); 801 | }, "expected { v: 1 } to deeply equal { v: 100 }"); 802 | }) 803 | }) 804 | }); 805 | -------------------------------------------------------------------------------- /test/display/errors.js: -------------------------------------------------------------------------------- 1 | import * as chai from '../../index.js'; 2 | 3 | var expect = chai.expect; 4 | 5 | chai.config.includeStack = true; 6 | 7 | describe('error display', function () { 8 | 9 | it('show error line', function () { 10 | expect(4).to.equal(2); 11 | }); 12 | 13 | }); 14 | -------------------------------------------------------------------------------- /test/display/message.js: -------------------------------------------------------------------------------- 1 | import * as chai from '../../index.js' 2 | 3 | const expect = chai.expect 4 | 5 | var deepObj = { 6 | green: { tea: 'matcha' } 7 | , teas: [ 8 | 'chai' 9 | , 'matcha' 10 | , { tea: 'konacha' } 11 | ] 12 | }; 13 | 14 | var deepObj2 = { 15 | green: { tea: 'matcha' } 16 | , teas: [ 17 | 'chai' 18 | , 'oolong' 19 | , { tea: 'konacha' } 20 | ] 21 | }; 22 | 23 | chai.config.includeStack = true; 24 | 25 | describe('object display', function () { 26 | 27 | it('property', function () { 28 | deepObj.should.have.property('chai'); 29 | }); 30 | 31 | it('deep equal', function () { 32 | deepObj.should.deep.equal(deepObj2); 33 | }); 34 | 35 | it('deep equal no diff', function () { 36 | chai.config.showDiff = false; 37 | deepObj.should.deep.equal(deepObj2); 38 | chai.config.showDiff = true; 39 | }); 40 | 41 | }); 42 | 43 | describe('undefined/null display', function() { 44 | it('undefined for actual', function() { 45 | expect(undefined).to.equal(null); 46 | }); 47 | }); 48 | -------------------------------------------------------------------------------- /test/globalErr.js: -------------------------------------------------------------------------------- 1 | import * as chai from '../index.js'; 2 | import {globalErr as err} from './bootstrap/index.js'; 3 | 4 | describe('globalErr', function () { 5 | var noop = function () {} 6 | , Err = chai.AssertionError 7 | , expect = chai.expect; 8 | 9 | it('should pass if string val equals error message', function () { 10 | err(function () { 11 | expect('cat').to.equal('dog') 12 | }, 'expected \'cat\' to equal \'dog\''); 13 | }); 14 | 15 | it('should pass if regex val matches error message', function () { 16 | err(function () { 17 | expect('cat').to.equal('dog') 18 | }, /expected 'cat' to equal 'dog'/); 19 | }); 20 | 21 | it('should pass if object val\'s props are included in error object', function () { 22 | err(function () { 23 | expect('cat').to.equal('dog'); 24 | }, { 25 | message: 'expected \'cat\' to equal \'dog\'' 26 | , expected: 'dog' 27 | , actual: 'cat' 28 | }); 29 | 30 | err(function () { 31 | expect({cat: 'meow'}).to.equal({dog: 'woof'}); 32 | }, { 33 | message: 'expected { cat: \'meow\' } to equal { dog: \'woof\' }' 34 | , expected: {dog: 'woof'} 35 | , actual: {cat: 'meow'} 36 | }); 37 | }); 38 | 39 | it('should throw if string val does not equal error message', function () { 40 | err(function () { 41 | err(function () { throw new Err('cat') }, 'dog'); 42 | }, { 43 | message: 'expected \'cat\' to equal \'dog\'' 44 | , expected: 'dog' 45 | , actual: 'cat' 46 | }); 47 | }); 48 | 49 | it('should pass operator if possible during none object comparison', function () { 50 | err(function () { 51 | expect('cat').to.equal('dog'); 52 | }, { 53 | message: 'expected \'cat\' to equal \'dog\'' 54 | , expected: 'dog' 55 | , actual: 'cat' 56 | , operator: 'strictEqual' 57 | }); 58 | 59 | err(function () { 60 | expect('cat').to.not.equal('cat'); 61 | }, { 62 | message: 'expected \'cat\' to not equal \'cat\'' 63 | , expected: 'cat' 64 | , actual: 'cat' 65 | , operator: 'notStrictEqual' 66 | }); 67 | }); 68 | 69 | it('should pass operator if possible during plain object comparison', function () { 70 | var val1 = { 71 | propVal1: 'val1' 72 | }; 73 | 74 | var val2 = { 75 | propVal2: 'val2' 76 | }; 77 | 78 | err(function () { 79 | expect(val1).to.equal(val2); 80 | }, { 81 | message: "expected { propVal1: 'val1' } to equal { propVal2: 'val2' }" 82 | , expected: val2 83 | , actual: val1 84 | , operator: 'deepStrictEqual' 85 | }); 86 | 87 | err(function () { 88 | expect(val1).to.not.equal(val1); 89 | }, { 90 | message: "expected { propVal1: 'val1' } to not equal { propVal1: 'val1' }" 91 | , expected: val1 92 | , actual: val1 93 | , operator: 'notDeepStrictEqual' 94 | }); 95 | }); 96 | 97 | it('should pass operator if possible during function comparison', function () { 98 | function f1 () { 99 | this.propF1 = 'propF1'; 100 | } 101 | 102 | function f2 () { 103 | this.propF2 = 'propF2'; 104 | } 105 | 106 | err(function () { 107 | expect(f1).to.equal(f2); 108 | }, { 109 | message: "expected [Function f1] to equal [Function f2]" 110 | , expected: f2 111 | , actual: f1 112 | , operator: 'deepStrictEqual' 113 | }); 114 | 115 | err(function () { 116 | expect(f1).to.not.equal(f1); 117 | }, { 118 | message: "expected [Function f1] to not equal [Function f1]" 119 | , expected: f1 120 | , actual: f1 121 | , operator: 'notDeepStrictEqual' 122 | }); 123 | }); 124 | 125 | it('should pass operator if possible during object comparison', function () { 126 | var val1 = [ 127 | 'string1' 128 | , 'string2' 129 | , 'string3' 130 | , 'string4' 131 | ]; 132 | 133 | var val2 = [ 134 | 'string5' 135 | , 'string6' 136 | , 'string7' 137 | , 'string8' 138 | ]; 139 | err(function () { 140 | expect(val1).to.equal(val2); 141 | }, { 142 | message: "expected [ 'string1', 'string2', …(2) ] to equal [ 'string5', 'string6', …(2) ]" 143 | , expected: val2 144 | , actual: val1 145 | , operator: 'deepStrictEqual' 146 | }); 147 | 148 | err(function () { 149 | expect(val1).to.not.equal(val1); 150 | }, { 151 | message: "expected [ 'string1', 'string2', …(2) ] to not equal [ 'string1', 'string2', …(2) ]" 152 | , expected: val1 153 | , actual: val1 154 | , operator: 'notDeepStrictEqual' 155 | }); 156 | }); 157 | 158 | it('should throw if regex val does not match error message', function () { 159 | err(function () { 160 | err(function () { throw new Err('cat') }, /dog/); 161 | }, 'expected \'cat\' to match /dog/'); 162 | }); 163 | 164 | it('should throw if object val\'s props are not included in error object', function () { 165 | err(function () { 166 | err(function () { throw new Err('cat') }, {text: 'cat'}); 167 | }, /expected AssertionError: cat to have property \'text\'/); 168 | 169 | err(function () { 170 | err(function () { throw new Err('cat') }, {message: 'dog'}); 171 | }, 'expected \'cat\' to deeply equal \'dog\'', true); 172 | }); 173 | 174 | it('should throw if fn does not throw', function () { 175 | err(function () { err(noop) }, 'Expected an error'); 176 | }); 177 | 178 | it('should throw if fn is invalid', function () { 179 | var vals = [ 180 | 'cat' 181 | , 42 182 | , [] 183 | , new RegExp() 184 | , new Date() 185 | , null 186 | , undefined 187 | ]; 188 | 189 | vals.push(Symbol()); 190 | vals.push(new Map()); 191 | vals.push(new Set()); 192 | vals.push(new WeakMap()); 193 | vals.push(new WeakSet()); 194 | vals.push(new Promise(noop)); 195 | 196 | vals.forEach(function (val) { 197 | err(function () { err(val) }, 'Invalid fn') 198 | }); 199 | }); 200 | 201 | it('should throw if val is invalid', function () { 202 | var vals = [ 203 | 42 204 | , [] 205 | , new Date() 206 | , noop 207 | , null 208 | ]; 209 | 210 | vals.push(Symbol()); 211 | vals.push(new Map()); 212 | vals.push(new WeakMap()); 213 | vals.push(new Set()); 214 | vals.push(new WeakSet()); 215 | vals.push(new Promise(noop)); 216 | 217 | vals.forEach(function (val) { 218 | err(function () { 219 | err(function () { throw new Err('Test error') }, val) 220 | }, 'Invalid val') 221 | }); 222 | }); 223 | 224 | describe('skipStackTest', function () { 225 | // Skip tests if `Error.captureStackTrace` is unsupported 226 | if (typeof Error.captureStackTrace === 'undefined') return; 227 | 228 | try { 229 | throw Error(); 230 | } catch (err) { 231 | // Skip tests if `err.stack` is unsupported 232 | if (typeof err.stack === 'undefined') return; 233 | } 234 | 235 | // Note: `.to.not.throw` isn't used for the assertions that aren't expected 236 | // to throw an error because it'll pollute the very same stack trace which 237 | // is being asserted on. Instead, if `err` throws an error, then Mocha will 238 | // use that error as the reason the test failed. 239 | describe('falsey', function () { 240 | it('should throw if "Getter" is in the stack trace', function () { 241 | err(function () { 242 | err(function fakeGetter () { 243 | throw Error('my stack trace contains a fake implementation frame'); 244 | }); 245 | }, /implementation frames not properly filtered from stack trace/, true); 246 | }); 247 | 248 | it('should throw if "Wrapper" is in the stack trace', function () { 249 | err(function () { 250 | err(function fakeWrapper () { 251 | throw Error('my stack trace contains a fake implementation frame'); 252 | }); 253 | }, /implementation frames not properly filtered from stack trace/, true); 254 | }); 255 | 256 | it('should throw if "assert" is in the stack trace', function () { 257 | err(function () { 258 | err(function assertFake () { 259 | throw Error('my stack trace contains a fake implementation frame'); 260 | }); 261 | }, /implementation frames not properly filtered from stack trace/, true); 262 | }); 263 | 264 | it('shouldn\'t throw if "Getter", "Wrapper", "assert" aren\'t in the stack trace', function () { 265 | err(function safeFnName () { 266 | throw Error('my stack trace doesn\'t contain implementation frames'); 267 | }); 268 | }); 269 | }); 270 | 271 | describe('truthy', function () { 272 | it('shouldn\'t throw if "Getter" is in the stack trace', function () { 273 | err(function fakeGetter () { 274 | throw Error('my stack trace contains a fake implementation frame'); 275 | }, undefined, true); 276 | }); 277 | 278 | it('shouldn\'t throw if "Wrapper" is in the stack trace', function () { 279 | err(function fakeWrapper () { 280 | throw Error('my stack trace contains a fake implementation frame'); 281 | }, undefined, true); 282 | }); 283 | 284 | it('shouldn\'t throw if "assert" is in the stack trace', function () { 285 | err(function assertFake () { 286 | throw Error('my stack trace contains a fake implementation frame'); 287 | }, undefined, true); 288 | }); 289 | 290 | it('shouldn\'t throw if "Getter", "Wrapper", "assert" aren\'t in the stack trace', function () { 291 | err(function safeFnName () { 292 | throw Error('my stack trace doesn\'t contain implementation frames'); 293 | }, undefined, true); 294 | }); 295 | }); 296 | }); 297 | }); 298 | -------------------------------------------------------------------------------- /test/globalShould.js: -------------------------------------------------------------------------------- 1 | import * as chai from '../index.js'; 2 | 3 | describe('global should', function () { 4 | it('works', function () { 5 | var theGlobal = typeof window !== 'undefined' 6 | ? window 7 | : global; 8 | 9 | theGlobal.globalShould = chai.should(); 10 | 11 | try { 12 | globalShould.not.exist(undefined); 13 | } finally { 14 | delete theGlobal.globalShould; 15 | } 16 | }); 17 | }); 18 | -------------------------------------------------------------------------------- /test/plugins.js: -------------------------------------------------------------------------------- 1 | import * as chai from '../index.js'; 2 | 3 | describe('plugins', function () { 4 | 5 | function plugin (chai) { 6 | if (chai.Assertion.prototype.testing) return; 7 | 8 | Object.defineProperty(chai.Assertion.prototype, 'testing', { 9 | get: function () { 10 | return 'successful'; 11 | } 12 | }); 13 | } 14 | 15 | it('basic usage', function () { 16 | chai.use(plugin); 17 | var expect = chai.expect; 18 | expect(expect('').testing).to.equal('successful'); 19 | }); 20 | 21 | it('double plugin', function () { 22 | chai.expect(function () { 23 | chai.use(plugin); 24 | }).to.not.throw(); 25 | }); 26 | 27 | it('nested plugin', function () { 28 | chai.use(function (chai) { 29 | chai.use(plugin); 30 | }); 31 | var expect = chai.expect; 32 | expect(expect('').testing).to.equal('successful'); 33 | }); 34 | 35 | it('chained plugin', function () { 36 | chai.use(function (chaiObj) { 37 | Object.defineProperty(chaiObj.Assertion.prototype, 'testing2', { 38 | get() { 39 | return 'bleep bloop'; 40 | } 41 | }); 42 | }).use(plugin); 43 | var expect = chai.expect; 44 | expect(expect('').testing).to.equal('successful'); 45 | expect(expect('').testing2).to.equal('bleep bloop'); 46 | }); 47 | 48 | it('.use detached from chai object', function () { 49 | function anotherPlugin (chai) { 50 | Object.defineProperty(chai.Assertion.prototype, 'moreTesting', { 51 | get: function () { 52 | return 'more success'; 53 | } 54 | }); 55 | } 56 | 57 | var use = chai.use; 58 | use(anotherPlugin); 59 | 60 | var expect = chai.expect; 61 | expect(expect('').moreTesting).to.equal('more success'); 62 | }); 63 | }); 64 | -------------------------------------------------------------------------------- /test/subset.js: -------------------------------------------------------------------------------- 1 | import * as chai from '../index.js'; 2 | 3 | describe('containsSubset', function () { 4 | const {assert, expect} = chai; 5 | const should = chai.Should(); 6 | 7 | describe('plain object', function () { 8 | var testedObject = { 9 | a: 'b', 10 | c: 'd' 11 | }; 12 | 13 | it('should pass for smaller object', function () { 14 | expect(testedObject).to.containSubset({ 15 | a: 'b' 16 | }); 17 | }); 18 | 19 | it('should pass for same object', function () { 20 | expect(testedObject).to.containSubset({ 21 | a: 'b', 22 | c: 'd' 23 | }); 24 | }); 25 | 26 | it('should pass for similar, but not the same object', function () { 27 | expect(testedObject).to.not.containSubset({ 28 | a: 'notB', 29 | c: 'd' 30 | }); 31 | }); 32 | }); 33 | 34 | describe('complex object', function () { 35 | var testedObject = { 36 | a: 'b', 37 | c: 'd', 38 | e: { 39 | foo: 'bar', 40 | baz: { 41 | qux: 'quux' 42 | } 43 | } 44 | }; 45 | 46 | it('should pass for smaller object', function () { 47 | expect(testedObject).to.containSubset({ 48 | a: 'b', 49 | e: { 50 | foo: 'bar' 51 | } 52 | }); 53 | }); 54 | 55 | it('should pass for smaller object', function () { 56 | expect(testedObject).to.containSubset({ 57 | e: { 58 | foo: 'bar', 59 | baz: { 60 | qux: 'quux' 61 | } 62 | } 63 | }); 64 | }); 65 | 66 | it('should pass for same object', function () { 67 | expect(testedObject).to.containSubset({ 68 | a: 'b', 69 | c: 'd', 70 | e: { 71 | foo: 'bar', 72 | baz: { 73 | qux: 'quux' 74 | } 75 | } 76 | }); 77 | }); 78 | 79 | it('should pass for similar, but not the same object', function () { 80 | expect(testedObject).to.not.containSubset({ 81 | e: { 82 | foo: 'bar', 83 | baz: { 84 | qux: 'notAQuux' 85 | } 86 | } 87 | }); 88 | }); 89 | 90 | it('should fail if comparing when comparing objects to dates', function () { 91 | expect(testedObject).to.not.containSubset({ 92 | e: new Date() 93 | }); 94 | }); 95 | }); 96 | 97 | describe('circular objects', function () { 98 | var object = {}; 99 | 100 | before(function () { 101 | object.arr = [object, object]; 102 | object.arr.push(object.arr); 103 | object.obj = object; 104 | }); 105 | 106 | it('should contain subdocument', function () { 107 | expect(object).to.containSubset({ 108 | arr: [{arr: []}, {arr: []}, [{arr: []}, {arr: []}]] 109 | }); 110 | }); 111 | 112 | it('should not contain similar object', function () { 113 | expect(object).to.not.containSubset({ 114 | arr: [{arr: ['just random field']}, {arr: []}, [{arr: []}, {arr: []}]] 115 | }); 116 | }); 117 | }); 118 | 119 | describe('object with compare function', function () { 120 | it('should pass when function returns true', function () { 121 | expect({a: 5}).to.containSubset({a: (a) => a}); 122 | }); 123 | 124 | it('should fail when function returns false', function () { 125 | expect({a: 5}).to.not.containSubset({a: (a) => !a}); 126 | }); 127 | 128 | it('should pass for function with no arguments', function () { 129 | expect({a: 5}).to.containSubset({a: () => true}); 130 | }); 131 | }); 132 | 133 | describe('comparison of non objects', function () { 134 | it('should fail if actual subset is null', function () { 135 | expect(null).to.not.containSubset({a: 1}); 136 | }); 137 | 138 | it('should fail if expected subset is not a object', function () { 139 | expect({a: 1}).to.not.containSubset(null); 140 | }); 141 | 142 | it('should not fail for same non-object (string) variables', function () { 143 | expect('string').to.containSubset('string'); 144 | }); 145 | }); 146 | 147 | describe('assert style of test', function () { 148 | it('should find subset', function () { 149 | assert.containsSubset({a: 1, b: 2}, {a: 1}); 150 | assert.containSubset({a: 1, b: 2}, {a: 1}); 151 | }); 152 | 153 | it('negated assert style should function', function () { 154 | assert.doesNotContainSubset({a: 1, b: 2}, {a: 3}); 155 | }); 156 | }); 157 | 158 | describe('should style of test', function () { 159 | const objectA = {a: 1, b: 2}; 160 | 161 | it('should find subset', function () { 162 | objectA.should.containSubset({a: 1}); 163 | }); 164 | 165 | it('negated should style should function', function () { 166 | objectA.should.not.containSubset({a: 3}); 167 | }); 168 | }); 169 | 170 | describe('comparison of dates', function () { 171 | it('should pass for the same date', function () { 172 | expect(new Date('2015-11-30')).to.containSubset(new Date('2015-11-30')); 173 | }); 174 | 175 | it('should pass for the same date if nested', function () { 176 | expect({a: new Date('2015-11-30')}).to.containSubset({ 177 | a: new Date('2015-11-30') 178 | }); 179 | }); 180 | 181 | it('should fail for a different date', function () { 182 | expect(new Date('2015-11-30')).to.not.containSubset( 183 | new Date('2012-02-22') 184 | ); 185 | }); 186 | 187 | it('should fail for a different date if nested', function () { 188 | expect({a: new Date('2015-11-30')}).to.not.containSubset({ 189 | a: new Date('2012-02-22') 190 | }); 191 | }); 192 | 193 | it('should fail for invalid expected date', function () { 194 | expect(new Date('2015-11-30')).to.not.containSubset( 195 | new Date('not valid date') 196 | ); 197 | }); 198 | 199 | it('should fail for invalid actual date', function () { 200 | expect(new Date('not valid actual date')).to.not.containSubset( 201 | new Date('not valid expected date') 202 | ); 203 | }); 204 | }); 205 | 206 | describe('cyclic objects', () => { 207 | it('should pass', () => { 208 | const child = {}; 209 | const parent = { 210 | children: [child] 211 | }; 212 | child.parent = parent; 213 | 214 | const myObject = { 215 | a: 1, 216 | b: 'two', 217 | c: parent 218 | }; 219 | expect(myObject).to.containSubset({ 220 | a: 1, 221 | c: parent 222 | }); 223 | }); 224 | }); 225 | }); 226 | -------------------------------------------------------------------------------- /test/type-detect/deno-test.ts: -------------------------------------------------------------------------------- 1 | /* global Deno:readonly */ 2 | // @ts-nocheck 3 | import { assertEquals } from 'https://deno.land/std/testing/asserts.ts'; 4 | import typeDetect from '../index.ts'; 5 | Deno.test('type detect works', () => { 6 | assertEquals(typeDetect('hello'), 'string'); 7 | }); 8 | -------------------------------------------------------------------------------- /test/type-detect/dom.js: -------------------------------------------------------------------------------- 1 | import * as chai from '../../index.js'; 2 | 3 | function assert (expr, msg) { 4 | if (!expr) { 5 | throw new Error(msg || 'Assertion Failed'); 6 | } 7 | } 8 | 9 | const type = chai.util.type 10 | 11 | function describeIf(condition) { 12 | return condition ? describe : describe.skip; 13 | } 14 | function itIf(condition) { 15 | return condition ? it : it.skip; 16 | } 17 | describeIf(typeof window !== 'undefined' && typeof window.document !== 'undefined')('DOM Specific', () => { 18 | 19 | it('window', () => { 20 | assert(type(window) === 'Window'); 21 | }); 22 | 23 | it('document', () => { 24 | assert(type(document) === 'HTMLDocument'); 25 | }); 26 | 27 | it('domparser', () => { 28 | assert(type(new DOMParser()) === 'DOMParser'); 29 | }); 30 | 31 | it('history', () => { 32 | assert(type(window.history) === 'History'); 33 | }); 34 | 35 | it('location', () => { 36 | assert(type(window.location) === 'Location'); 37 | }); 38 | 39 | it('attr', () => { 40 | const div = document.createElement('div'); 41 | div.setAttribute('id', 'foo'); 42 | assert(type(div.getAttributeNode('id')) === 'Attr'); 43 | }); 44 | 45 | describe('Events', () => { 46 | 47 | it('event', () => { 48 | assert(type(document.createEvent('Event')) === 'Event'); 49 | }); 50 | 51 | itIf(typeof HashChangeEvent !== 'undefined')('HashChangeEvent', () => { 52 | assert(type(new HashChangeEvent('')) === 'HashChangeEvent'); 53 | }); 54 | 55 | }); 56 | 57 | describe('Navigator', () => { 58 | 59 | it('navigator', () => { 60 | assert(type(window.navigator) === 'Navigator'); 61 | }); 62 | 63 | itIf(typeof navigator !== 'undefined' && 'geolocation' in navigator)('geolocation', () => { 64 | assert(type(navigator.geolocation) === 'Geolocation'); 65 | }); 66 | 67 | itIf(typeof navigator !== 'undefined' && 'connection' in navigator)('networkinformation', () => { 68 | assert(type(navigator.connection) === 'NetworkInformation'); 69 | }); 70 | 71 | itIf(typeof navigator !== 'undefined' && 'mediaDevices' in navigator)('mediadevices', () => { 72 | assert(type(navigator.mediaDevices) === 'MediaDevices'); 73 | }); 74 | 75 | itIf(typeof navigator !== 'undefined' && 'mimeTypes' in navigator)('mimetypearray', () => { 76 | assert(type(navigator.mimeTypes) === 'MimeTypeArray'); 77 | }); 78 | 79 | itIf(typeof navigator !== 'undefined' && 'nfc' in navigator)('nfc', () => { 80 | assert(type(navigator.nfc) === 'NFC'); 81 | }); 82 | 83 | itIf(typeof navigator !== 'undefined' && 'permissions' in navigator)('permissions', () => { 84 | assert(type(navigator.permissions) === 'Permissions'); 85 | }); 86 | 87 | itIf(typeof navigator !== 'undefined' && 'plugins' in navigator)('pluginarray', () => { 88 | assert(type(navigator.plugins) === 'PluginArray'); 89 | }); 90 | 91 | itIf(typeof navigator !== 'undefined' && 'plugins' in navigator && navigator.plugins.length)('plugin', () => { 92 | assert(type(navigator.plugins[0]) === 'Plugin'); 93 | }); 94 | 95 | itIf(typeof navigator !== 'undefined' && 'presentation' in navigator)('presentation', () => { 96 | assert(type(navigator.presentation) === 'Presentation'); 97 | }); 98 | 99 | itIf(typeof navigator !== 'undefined' && 'serviceworker' in navigator)('serviceworkercontainer', () => { 100 | assert(type(navigator.serviceworker) === 'ServiceWorkerContainer'); 101 | }); 102 | 103 | itIf(typeof navigator !== 'undefined' && 'services' in navigator)('serviceportcollection', () => { 104 | assert(type(navigator.services) === 'ServicePortCollection'); 105 | }); 106 | 107 | itIf(typeof navigator !== 'undefined' && 'storage' in navigator)('storagemanager', () => { 108 | assert(type(navigator.storage) === 'StorageManager'); 109 | }); 110 | 111 | itIf(typeof navigator !== 'undefined' && 'storageQuota' in navigator)('storagequota', () => { 112 | assert(type(navigator.storageQuota) === 'StorageQuota'); 113 | }); 114 | 115 | itIf(typeof navigator !== 'undefined' && 'usb' in navigator)('usb', () => { 116 | assert(type(navigator.usb) === 'USB'); 117 | }); 118 | 119 | }); 120 | 121 | describe('(HTMLElements)', () => { 122 | 123 | it('HTMLAreaElement', () => { 124 | assert(type(document.createElement('Area')) === 'HTMLAreaElement'); 125 | }); 126 | 127 | it('HTMLBRElement', () => { 128 | assert(type(document.createElement('BR')) === 'HTMLBRElement'); 129 | }); 130 | 131 | it('HTMLBaseElement', () => { 132 | assert(type(document.createElement('Base')) === 'HTMLBaseElement'); 133 | }); 134 | 135 | it('HTMLBodyElement', () => { 136 | assert(type(document.createElement('Body')) === 'HTMLBodyElement'); 137 | }); 138 | 139 | it('HTMLButtonElement', () => { 140 | assert(type(document.createElement('Button')) === 'HTMLButtonElement'); 141 | }); 142 | 143 | it('HTMLCanvasElement', () => { 144 | assert(type(document.createElement('Canvas')) === 'HTMLCanvasElement'); 145 | }); 146 | 147 | it('HTMLDListElement', () => { 148 | assert(type(document.createElement('DL')) === 'HTMLDListElement'); 149 | }); 150 | 151 | // not yet supported in Safari 152 | itIf(typeof HTMLDataListElement === 'function')('HTMLDataListElement', () => { 153 | assert(type(document.createElement('DataList')) === 'HTMLDataListElement'); 154 | }); 155 | 156 | it('HTMLDivElement', () => { 157 | assert(type(document.createElement('Div')) === 'HTMLDivElement'); 158 | }); 159 | 160 | it('HTMLFieldSetElement', () => { 161 | assert(type(document.createElement('FieldSet')) === 'HTMLFieldSetElement'); 162 | }); 163 | 164 | it('HTMLFormElement', () => { 165 | assert(type(document.createElement('Form')) === 'HTMLFormElement'); 166 | }); 167 | 168 | it('HTMLFrameSetElement', () => { 169 | assert(type(document.createElement('FrameSet')) === 'HTMLFrameSetElement'); 170 | }); 171 | 172 | it('HTMLHRElement', () => { 173 | assert(type(document.createElement('HR')) === 'HTMLHRElement'); 174 | }); 175 | 176 | it('HTMLHeadElement', () => { 177 | assert(type(document.createElement('Head')) === 'HTMLHeadElement'); 178 | }); 179 | 180 | it('HTMLHeadingElement', () => { 181 | assert(type(document.createElement('H1')) === 'HTMLHeadingElement'); 182 | assert(type(document.createElement('H2')) === 'HTMLHeadingElement'); 183 | assert(type(document.createElement('H3')) === 'HTMLHeadingElement'); 184 | assert(type(document.createElement('H4')) === 'HTMLHeadingElement'); 185 | assert(type(document.createElement('H5')) === 'HTMLHeadingElement'); 186 | assert(type(document.createElement('H6')) === 'HTMLHeadingElement'); 187 | }); 188 | 189 | it('HTMLHtmlElement', () => { 190 | assert(type(document.createElement('Html')) === 'HTMLHtmlElement'); 191 | }); 192 | 193 | it('HTMLIFrameElement', () => { 194 | assert(type(document.createElement('IFrame')) === 'HTMLIFrameElement'); 195 | }); 196 | 197 | it('HTMLImageElement', () => { 198 | assert(type(document.createElement('Img')) === 'HTMLImageElement'); 199 | }); 200 | 201 | it('HTMLInputElement', () => { 202 | assert(type(document.createElement('Input')) === 'HTMLInputElement'); 203 | }); 204 | 205 | it('HTMLLIElement', () => { 206 | assert(type(document.createElement('LI')) === 'HTMLLIElement'); 207 | }); 208 | 209 | it('HTMLLabelElement', () => { 210 | assert(type(document.createElement('Label')) === 'HTMLLabelElement'); 211 | }); 212 | 213 | it('HTMLLegendElement', () => { 214 | assert(type(document.createElement('Legend')) === 'HTMLLegendElement'); 215 | }); 216 | 217 | it('HTMLLinkElement', () => { 218 | assert(type(document.createElement('Link')) === 'HTMLLinkElement'); 219 | }); 220 | 221 | it('HTMLMapElement', () => { 222 | assert(type(document.createElement('Map')) === 'HTMLMapElement'); 223 | }); 224 | 225 | it('HTMLMetaElement', () => { 226 | assert(type(document.createElement('Meta')) === 'HTMLMetaElement'); 227 | }); 228 | 229 | itIf(typeof HTMLMeterElement !== 'undefined')('HTMLMeterElement', () => { 230 | assert(type(document.createElement('Meter')) === 'HTMLMeterElement'); 231 | }); 232 | 233 | it('HTMLModElement', () => { 234 | assert(type(document.createElement('Del')) === 'HTMLModElement'); 235 | }); 236 | 237 | it('HTMLOListElement', () => { 238 | assert(type(document.createElement('OL')) === 'HTMLOListElement'); 239 | }); 240 | 241 | it('HTMLOptGroupElement', () => { 242 | assert(type(document.createElement('OptGroup')) === 'HTMLOptGroupElement'); 243 | }); 244 | 245 | it('HTMLOptionElement', () => { 246 | assert(type(document.createElement('Option')) === 'HTMLOptionElement'); 247 | }); 248 | 249 | itIf(typeof HTMLOutputElement !== 'undefined')('HTMLOutputElement', () => { 250 | assert(type(document.createElement('Output')) === 'HTMLOutputElement'); 251 | }); 252 | 253 | it('HTMLParagraphElement', () => { 254 | assert(type(document.createElement('P')) === 'HTMLParagraphElement'); 255 | }); 256 | 257 | it('HTMLParamElement', () => { 258 | assert(type(document.createElement('Param')) === 'HTMLParamElement'); 259 | }); 260 | 261 | it('HTMLPreElement', () => { 262 | assert(type(document.createElement('Pre')) === 'HTMLPreElement'); 263 | }); 264 | 265 | itIf(typeof HTMLProgressElement !== 'undefined')('HTMLProgressElement', () => { 266 | assert(type(document.createElement('Progress')) === 'HTMLProgressElement'); 267 | }); 268 | 269 | it('HTMLQuoteElement', () => { 270 | assert(type(document.createElement('BlockQuote')) === 'HTMLQuoteElement'); 271 | assert(type(document.createElement('Q')) === 'HTMLQuoteElement'); 272 | }); 273 | 274 | it('HTMLScriptElement', () => { 275 | assert(type(document.createElement('Script')) === 'HTMLScriptElement'); 276 | }); 277 | 278 | it('HTMLSelectElement', () => { 279 | assert(type(document.createElement('Select')) === 'HTMLSelectElement'); 280 | }); 281 | 282 | it('HTMLSpanElement', () => { 283 | assert(type(document.createElement('Span')) === 'HTMLSpanElement'); 284 | }); 285 | 286 | it('HTMLStyleElement', () => { 287 | assert(type(document.createElement('Style')) === 'HTMLStyleElement'); 288 | }); 289 | 290 | it('HTMLTableCaptionElement', () => { 291 | assert(type(document.createElement('Caption')) === 'HTMLTableCaptionElement'); 292 | }); 293 | 294 | it('HTMLTableCellElement', () => { 295 | assert(type(document.createElement('TD')) === 'HTMLTableCellElement'); 296 | }); 297 | 298 | it('HTMLTableHeaderCellElement', () => { 299 | assert(type(document.createElement('TH')) === 'HTMLTableCellElement'); 300 | }); 301 | 302 | it('HTMLTableColElement', () => { 303 | assert(type(document.createElement('Col')) === 'HTMLTableColElement'); 304 | assert(type(document.createElement('ColGroup')) === 'HTMLTableColElement'); 305 | }); 306 | 307 | it('HTMLTableElement', () => { 308 | assert(type(document.createElement('Table')) === 'HTMLTableElement'); 309 | }); 310 | 311 | it('HTMLTableRowElement', () => { 312 | assert(type(document.createElement('TR')) === 'HTMLTableRowElement'); 313 | }); 314 | 315 | it('HTMLTableSectionElement', () => { 316 | assert(type(document.createElement('THead')) === 'HTMLTableSectionElement'); 317 | assert(type(document.createElement('TBody')) === 'HTMLTableSectionElement'); 318 | assert(type(document.createElement('TFoot')) === 'HTMLTableSectionElement'); 319 | }); 320 | 321 | it('HTMLTextAreaElement', () => { 322 | assert(type(document.createElement('TextArea')) === 'HTMLTextAreaElement'); 323 | }); 324 | 325 | it('HTMLTitleElement', () => { 326 | assert(type(document.createElement('Title')) === 'HTMLTitleElement'); 327 | }); 328 | 329 | it('HTMLUListElement', () => { 330 | assert(type(document.createElement('UL')) === 'HTMLUListElement'); 331 | }); 332 | 333 | it('HTMLUnknownElement', () => { 334 | assert(type(document.createElement('foobarbaz')) === 'HTMLUnknownElement'); 335 | }); 336 | 337 | }); 338 | 339 | }); 340 | -------------------------------------------------------------------------------- /test/type-detect/index.js: -------------------------------------------------------------------------------- 1 | import * as chai from '../../index.js'; 2 | 3 | function assert (expr, msg) { 4 | if (!expr) { 5 | throw new Error(msg || 'Assertion Failed'); 6 | } 7 | } 8 | 9 | const type = chai.util.type 10 | 11 | describe('Generic', () => { 12 | 13 | it('array', () => { 14 | assert(type([]) === 'Array'); 15 | assert(type(new Array()) === 'Array'); 16 | }); 17 | 18 | it('regexp', () => { 19 | assert(type(/a-z/gi) === 'RegExp'); 20 | assert(type(new RegExp('a-z')) === 'RegExp'); 21 | }); 22 | 23 | it('function', () => { 24 | assert(type(() => {}) === 'Function'); 25 | }); 26 | 27 | it('arguments', function () { 28 | assert(type(arguments) === 'Arguments'); 29 | }); 30 | 31 | it('date', () => { 32 | assert(type(new Date()) === 'Date'); 33 | }); 34 | 35 | it('number', () => { 36 | assert(type(1) === 'Number'); 37 | assert(type(1.234) === 'Number'); 38 | assert(type(-1) === 'Number'); 39 | assert(type(-1.234) === 'Number'); 40 | assert(type(Infinity) === 'Number'); 41 | assert(type(NaN) === 'Number'); 42 | }); 43 | 44 | it('number objects', () => { 45 | assert(type(new Number(2)) === 'Number'); 46 | }); 47 | 48 | it('string', () => { 49 | assert(type('hello world') === 'String'); 50 | }); 51 | 52 | it('string objects', () => { 53 | assert(type(new String('hello')) === 'String'); 54 | }); 55 | 56 | it('null', () => { 57 | assert(type(null) === 'null'); 58 | assert(type(undefined) !== 'null'); 59 | }); 60 | 61 | it('undefined', () => { 62 | assert(type(undefined) === 'undefined'); 63 | assert(type(null) !== 'undefined'); 64 | }); 65 | 66 | it('object', () => { 67 | function Noop() {} 68 | assert(type({}) === 'Object'); 69 | assert(type(Noop) !== 'Object'); 70 | assert(type(new Noop()) === 'Object'); 71 | assert(type(new Object()) === 'Object'); 72 | assert(type(Object.create(null)) === 'Object'); 73 | assert(type(Object.create(Object.prototype)) === 'Object'); 74 | }); 75 | 76 | // See: https://github.com/chaijs/type-detect/pull/25 77 | it('object with .undefined property getter', () => { 78 | const foo = {}; 79 | Object.defineProperty(foo, 'undefined', { 80 | get() { 81 | throw Error('Should never happen'); 82 | }, 83 | }); 84 | assert(type(foo) === 'Object'); 85 | }); 86 | 87 | it('boolean', () => { 88 | assert(type(true) === 'Boolean'); 89 | assert(type(false) === 'Boolean'); 90 | assert(type(!0) === 'Boolean'); 91 | }); 92 | 93 | it('boolean object', () => { 94 | assert(type(new Boolean()) === 'Boolean'); 95 | }); 96 | 97 | it('error', () => { 98 | assert(type(new Error()) === 'Error'); 99 | assert(type(new TypeError()) === 'Error'); 100 | assert(type(new EvalError()) === 'Error'); 101 | assert(type(new RangeError()) === 'Error'); 102 | assert(type(new ReferenceError()) === 'Error'); 103 | assert(type(new SyntaxError()) === 'Error'); 104 | assert(type(new TypeError()) === 'Error'); 105 | assert(type(new URIError()) === 'Error'); 106 | }); 107 | 108 | it('Math', () => { 109 | assert(type(Math) === 'Math'); 110 | }); 111 | 112 | it('JSON', () => { 113 | assert(type(JSON) === 'JSON'); 114 | }); 115 | 116 | describe('Stubbed ES2015 Types', () => { 117 | const originalObjectToString = Object.prototype.toString; 118 | function stubObjectToStringOnce(staticValue) { 119 | Object.prototype.toString = function () { // eslint-disable-line no-extend-native 120 | Object.prototype.toString = originalObjectToString; // eslint-disable-line no-extend-native 121 | return staticValue; 122 | }; 123 | } 124 | function Thing() {} 125 | 126 | it('map', () => { 127 | stubObjectToStringOnce('[object Map]'); 128 | assert(type(new Thing()) === 'Map'); 129 | }); 130 | 131 | it('weakmap', () => { 132 | stubObjectToStringOnce('[object WeakMap]'); 133 | assert(type(new Thing()) === 'WeakMap'); 134 | }); 135 | 136 | it('set', () => { 137 | stubObjectToStringOnce('[object Set]'); 138 | assert(type(new Thing()) === 'Set'); 139 | }); 140 | 141 | it('weakset', () => { 142 | stubObjectToStringOnce('[object WeakSet]'); 143 | assert(type(new Thing()) === 'WeakSet'); 144 | }); 145 | 146 | it('symbol', () => { 147 | stubObjectToStringOnce('[object Symbol]'); 148 | assert(type(new Thing()) === 'Symbol'); 149 | }); 150 | 151 | it('promise', () => { 152 | stubObjectToStringOnce('[object Promise]'); 153 | assert(type(new Thing()) === 'Promise'); 154 | }); 155 | 156 | it('int8array', () => { 157 | stubObjectToStringOnce('[object Int8Array]'); 158 | assert(type(new Thing()) === 'Int8Array'); 159 | }); 160 | 161 | it('uint8array', () => { 162 | stubObjectToStringOnce('[object Uint8Array]'); 163 | assert(type(new Thing()) === 'Uint8Array'); 164 | }); 165 | 166 | it('uint8clampedarray', () => { 167 | stubObjectToStringOnce('[object Uint8ClampedArray]'); 168 | assert(type(new Thing()) === 'Uint8ClampedArray'); 169 | }); 170 | 171 | it('int16array', () => { 172 | stubObjectToStringOnce('[object Int16Array]'); 173 | assert(type(new Thing()) === 'Int16Array'); 174 | }); 175 | 176 | it('uint16array', () => { 177 | stubObjectToStringOnce('[object Uint16Array]'); 178 | assert(type(new Thing()) === 'Uint16Array'); 179 | }); 180 | 181 | it('int32array', () => { 182 | stubObjectToStringOnce('[object Int32Array]'); 183 | assert(type(new Thing()) === 'Int32Array'); 184 | }); 185 | 186 | it('uint32array', () => { 187 | stubObjectToStringOnce('[object Uint32Array]'); 188 | assert(type(new Thing()) === 'Uint32Array'); 189 | }); 190 | 191 | it('float32array', () => { 192 | stubObjectToStringOnce('[object Float32Array]'); 193 | assert(type(new Thing()) === 'Float32Array'); 194 | }); 195 | 196 | it('float64array', () => { 197 | stubObjectToStringOnce('[object Float64Array]'); 198 | assert(type(new Thing()) === 'Float64Array'); 199 | }); 200 | 201 | it('dataview', () => { 202 | stubObjectToStringOnce('[object DataView]'); 203 | assert(type(new Thing()) === 'DataView'); 204 | }); 205 | 206 | it('arraybuffer', () => { 207 | stubObjectToStringOnce('[object ArrayBuffer]'); 208 | assert(type(new Thing()) === 'ArrayBuffer'); 209 | }); 210 | 211 | it('generatorfunction', () => { 212 | stubObjectToStringOnce('[object GeneratorFunction]'); 213 | assert(type(new Thing()) === 'GeneratorFunction'); 214 | }); 215 | 216 | it('generator', () => { 217 | stubObjectToStringOnce('[object Generator]'); 218 | assert(type(new Thing()) === 'Generator'); 219 | }); 220 | 221 | it('string iterator', () => { 222 | stubObjectToStringOnce('[object String Iterator]'); 223 | assert(type(new Thing()) === 'String Iterator'); 224 | }); 225 | 226 | it('array iterator', () => { 227 | stubObjectToStringOnce('[object Array Iterator]'); 228 | assert(type(new Thing()) === 'Array Iterator'); 229 | }); 230 | 231 | it('map iterator', () => { 232 | stubObjectToStringOnce('[object Map Iterator]'); 233 | assert(type(new Thing()) === 'Map Iterator'); 234 | }); 235 | 236 | it('set iterator', () => { 237 | stubObjectToStringOnce('[object Set Iterator]'); 238 | assert(type(new Thing()) === 'Set Iterator'); 239 | }); 240 | 241 | }); 242 | 243 | describe('@@toStringTag Sham', () => { 244 | const originalObjectToString = Object.prototype.toString; 245 | before(() => { 246 | const globalObject = typeof self === 'object' ? self : global; 247 | globalObject.Symbol = globalObject.Symbol || {}; 248 | if (!Symbol.toStringTag) { 249 | Symbol.toStringTag = '__@@toStringTag__'; 250 | } 251 | const test = {}; 252 | test[Symbol.toStringTag] = function () { 253 | return 'foo'; 254 | }; 255 | if (Object.prototype.toString(test) !== '[object foo]') { 256 | Object.prototype.toString = function () { // eslint-disable-line no-extend-native 257 | if (typeof this === 'object' && typeof this[Symbol.toStringTag] === 'function') { 258 | return `[object ${ this[Symbol.toStringTag]() }]`; 259 | } 260 | return originalObjectToString.call(this); 261 | }; 262 | } 263 | }); 264 | 265 | after(() => { 266 | Object.prototype.toString = originalObjectToString; // eslint-disable-line no-extend-native 267 | }); 268 | 269 | it('plain object', () => { 270 | const obj = {}; 271 | obj[Symbol.toStringTag] = function () { 272 | return 'Foo'; 273 | }; 274 | assert(type(obj) === 'Foo', 'type(obj) === "Foo"'); 275 | }); 276 | 277 | }); 278 | 279 | }); 280 | -------------------------------------------------------------------------------- /test/type-detect/new-ecmascript-types.js: -------------------------------------------------------------------------------- 1 | import * as chai from '../../index.js'; 2 | 3 | function assert (expr, msg) { 4 | if (!expr) { 5 | throw new Error(msg || 'Assertion Failed'); 6 | } 7 | } 8 | 9 | const type = chai.util.type 10 | 11 | const symbolExists = typeof Symbol === 'function'; 12 | const setExists = typeof Set === 'function'; 13 | const mapExists = typeof Map === 'function'; 14 | let supportArrows = false; 15 | let supportGenerators = false; 16 | try { 17 | eval('function * foo () {}; foo'); // eslint-disable-line no-eval 18 | supportGenerators = true; 19 | } catch (error) { 20 | supportGenerators = false; 21 | } 22 | try { 23 | eval('() => {}'); // eslint-disable-line no-eval 24 | supportArrows = true; 25 | } catch (error) { 26 | supportArrows = false; 27 | } 28 | function itIf(condition) { 29 | return condition ? it : it.skip; 30 | } 31 | 32 | describe('ES2015 Specific', () => { 33 | itIf(symbolExists && typeof String.prototype[Symbol.iterator] === 'function')('string iterator', () => { 34 | assert(type(''[Symbol.iterator]()) === 'String Iterator'); 35 | }); 36 | 37 | itIf(symbolExists && typeof Array.prototype[Symbol.iterator] === 'function')('array iterator', () => { 38 | assert(type([][Symbol.iterator]()) === 'Array Iterator'); 39 | }); 40 | 41 | itIf(typeof Array.prototype.entries === 'function')('array iterator (entries)', () => { 42 | assert(type([].entries()) === 'Array Iterator'); 43 | }); 44 | 45 | itIf(mapExists)('map', () => { 46 | assert(type(new Map()) === 'Map'); 47 | }); 48 | 49 | itIf(symbolExists && mapExists && typeof Map.prototype[Symbol.iterator] === 'function')('map iterator', () => { 50 | assert(type(new Map()[Symbol.iterator]()) === 'Map Iterator'); 51 | }); 52 | 53 | itIf(mapExists && typeof Map.prototype.entries === 'function')('map iterator (entries)', () => { 54 | assert(type(new Map().entries()) === 'Map Iterator'); 55 | }); 56 | 57 | itIf(typeof WeakMap === 'function')('weakmap', () => { 58 | assert(type(new WeakMap()) === 'WeakMap'); 59 | }); 60 | 61 | itIf(setExists)('set', () => { 62 | assert(type(new Set()) === 'Set'); 63 | }); 64 | 65 | itIf(symbolExists && setExists && typeof Set.prototype[Symbol.iterator] === 'function')('set iterator', () => { 66 | assert(type(new Set()[Symbol.iterator]()) === 'Set Iterator'); 67 | }); 68 | 69 | itIf(setExists && typeof Set.prototype.entries === 'function')('set iterator', () => { 70 | assert(type(new Set().entries()) === 'Set Iterator'); 71 | }); 72 | 73 | itIf(typeof WeakSet === 'function')('weakset', () => { 74 | assert(type(new WeakSet()) === 'WeakSet'); 75 | }); 76 | 77 | itIf(typeof Symbol === 'function')('symbol', () => { 78 | assert(type(Symbol('foo')) === 'Symbol'); 79 | }); 80 | 81 | itIf(typeof Promise === 'function')('promise', () => { 82 | function noop() {} 83 | assert(type(new Promise(noop)) === 'Promise'); 84 | }); 85 | 86 | itIf(typeof Int8Array === 'function')('int8array', () => { 87 | assert(type(new Int8Array()) === 'Int8Array'); 88 | }); 89 | 90 | itIf(typeof Uint8Array === 'function')('uint8array', () => { 91 | assert(type(new Uint8Array()) === 'Uint8Array'); 92 | }); 93 | 94 | itIf(typeof Uint8ClampedArray === 'function')('uint8clampedarray', () => { 95 | assert(type(new Uint8ClampedArray()) === 'Uint8ClampedArray'); 96 | }); 97 | 98 | itIf(typeof Int16Array === 'function')('int16array', () => { 99 | assert(type(new Int16Array()) === 'Int16Array'); 100 | }); 101 | 102 | itIf(typeof Uint16Array === 'function')('uint16array', () => { 103 | assert(type(new Uint16Array()) === 'Uint16Array'); 104 | }); 105 | 106 | itIf(typeof Int32Array === 'function')('int32array', () => { 107 | assert(type(new Int32Array()) === 'Int32Array'); 108 | }); 109 | 110 | itIf(typeof Uint32Array === 'function')('uint32array', () => { 111 | assert(type(new Uint32Array()) === 'Uint32Array'); 112 | }); 113 | 114 | itIf(typeof Float32Array === 'function')('float32array', () => { 115 | assert(type(new Float32Array()) === 'Float32Array'); 116 | }); 117 | 118 | itIf(typeof Float64Array === 'function')('float64array', () => { 119 | assert(type(new Float64Array()) === 'Float64Array'); 120 | }); 121 | 122 | itIf(typeof DataView === 'function')('dataview', () => { 123 | const arrayBuffer = new ArrayBuffer(1); 124 | assert(type(new DataView(arrayBuffer)) === 'DataView'); 125 | }); 126 | 127 | itIf(typeof ArrayBuffer === 'function')('arraybuffer', () => { 128 | assert(type(new ArrayBuffer(1)) === 'ArrayBuffer'); 129 | }); 130 | 131 | itIf(supportArrows)('arrow function', () => { 132 | assert(type(eval('() => {}')) === 'Function'); // eslint-disable-line no-eval 133 | }); 134 | 135 | itIf(supportGenerators)('generator function', () => { 136 | assert(type(eval('function * foo () {}; foo')) === 'GeneratorFunction'); // eslint-disable-line no-eval 137 | }); 138 | 139 | itIf(supportGenerators)('generator', () => { 140 | assert(type(eval('(function * foo () {}())')) === 'Generator'); // eslint-disable-line no-eval 141 | }); 142 | 143 | }); 144 | -------------------------------------------------------------------------------- /test/type-detect/node.js: -------------------------------------------------------------------------------- 1 | import * as chai from '../../index.js'; 2 | 3 | function assert (expr, msg) { 4 | if (!expr) { 5 | throw new Error(msg || 'Assertion Failed'); 6 | } 7 | } 8 | 9 | const type = chai.util.type 10 | 11 | const isNode = typeof process !== 'undefined' && typeof process.release === 'object' && process.release.name; 12 | function describeIf(condition) { 13 | return condition ? describe : describe.skip; 14 | } 15 | describeIf(isNode)('Node Specific', () => { 16 | 17 | it('global', () => { 18 | assert(type(global) === 'global'); 19 | }); 20 | 21 | it('process', () => { 22 | assert(type(process) === 'process'); 23 | }); 24 | 25 | }); 26 | -------------------------------------------------------------------------------- /test/type-detect/tostringtag-extras.js: -------------------------------------------------------------------------------- 1 | import * as chai from '../../index.js'; 2 | 3 | function assert (expr, msg) { 4 | if (!expr) { 5 | throw new Error(msg || 'Assertion Failed'); 6 | } 7 | } 8 | 9 | const type = chai.util.type 10 | 11 | const symbolExists = typeof Symbol === 'function'; 12 | const symbolToStringTagExists = symbolExists && typeof Symbol.toStringTag !== 'undefined'; 13 | function describeIf(condition) { 14 | return condition ? describe : describe.skip; 15 | } 16 | 17 | describeIf(symbolToStringTagExists)('toStringTag extras', () => { 18 | 19 | it('supports toStringTag on arrays', () => { 20 | assert(type([]) === 'Array'); 21 | const arr = []; 22 | arr[Symbol.toStringTag] = 'foo'; 23 | assert(type(arr) === 'foo', 'type(arr) === "foo"'); 24 | }); 25 | 26 | 27 | }); 28 | -------------------------------------------------------------------------------- /test/virtual-machines.js: -------------------------------------------------------------------------------- 1 | import vm from 'node:vm'; 2 | import * as chai from '../index.js'; 3 | 4 | const {assert} = chai; 5 | const vmContext = {assert}; 6 | vm.createContext(vmContext); 7 | 8 | /** 9 | * Run the code in a virtual context 10 | * 11 | * @param {string} code Code to run 12 | */ 13 | function runCodeInVm(code) { 14 | vm.runInContext(code, vmContext); 15 | } 16 | 17 | describe('node virtual machines', function () { 18 | it('throws', function() { 19 | const shouldNotThrow = [ 20 | `assert.throws(function() { throw ''; }, /^$/);`, 21 | `assert.throws(function() { throw new Error('bleepbloop'); });`, 22 | `assert.throws(function() { throw new Error(''); });`, 23 | `assert.throws(function() { throw new Error('swoosh'); }, /swoosh/);` 24 | ]; 25 | 26 | for (const code of shouldNotThrow) { 27 | assert.doesNotThrow( 28 | () => { 29 | runCodeInVm(code); 30 | } 31 | ); 32 | } 33 | }); 34 | }); 35 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "esnext", 4 | "module": "nodenext", 5 | "moduleResolution": "nodenext", 6 | "types": [], 7 | "checkJs": true, 8 | "noEmit": true, 9 | "isolatedModules": true, 10 | "forceConsistentCasingInFileNames": true, 11 | "strict": true, 12 | "noUnusedLocals": true, 13 | "noUnusedParameters": true 14 | }, 15 | "include": [ 16 | "lib/**/*.js" 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /web-test-runner.config.js: -------------------------------------------------------------------------------- 1 | import { fromRollup } from "@web/dev-server-rollup"; 2 | import rollupCommonjs from "@rollup/plugin-commonjs"; 3 | 4 | const commonjs = fromRollup(rollupCommonjs); 5 | 6 | export default { 7 | nodeResolve: true, 8 | files: [ 9 | "test/*.js", 10 | "!test/virtual-machines.js" 11 | ], 12 | plugins: [ 13 | commonjs({ 14 | include: [ 15 | // the commonjs plugin is slow, list the required packages explicitly: 16 | "**/node_modules/type-detect/**/*", 17 | ], 18 | }), 19 | ], 20 | }; 21 | --------------------------------------------------------------------------------