{"id":35209077,"url":"https://github.com/mercadona/wrapito","last_synced_at":"2026-02-20T13:02:20.587Z","repository":{"id":38461803,"uuid":"201032780","full_name":"mercadona/wrapito","owner":"mercadona","description":"🌯 Yummy React tests","archived":false,"fork":false,"pushed_at":"2026-02-05T10:47:07.000Z","size":1886,"stargazers_count":31,"open_issues_count":26,"forks_count":5,"subscribers_count":15,"default_branch":"master","last_synced_at":"2026-02-05T22:09:57.454Z","etag":null,"topics":["jest","react","testing","type-library"],"latest_commit_sha":null,"homepage":"https://www.npmjs.com/package/wrapito","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/mercadona.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":".github/CODEOWNERS","security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2019-08-07T11:02:01.000Z","updated_at":"2026-02-05T10:47:09.000Z","dependencies_parsed_at":"2025-12-10T18:01:04.411Z","dependency_job_id":null,"html_url":"https://github.com/mercadona/wrapito","commit_stats":{"total_commits":155,"total_committers":14,"mean_commits":"11.071428571428571","dds":0.567741935483871,"last_synced_commit":"08c8a8e1fc02f368ef5e4a4e039741badf6c5832"},"previous_names":[],"tags_count":110,"template":false,"template_full_name":null,"purl":"pkg:github/mercadona/wrapito","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mercadona%2Fwrapito","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mercadona%2Fwrapito/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mercadona%2Fwrapito/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mercadona%2Fwrapito/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mercadona","download_url":"https://codeload.github.com/mercadona/wrapito/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mercadona%2Fwrapito/sbom","scorecard":{"id":636788,"data":{"date":"2025-08-11","repo":{"name":"github.com/mercadona/wrapito","commit":"d82fec480b34fd1ba12c750f71bbf34ddca3c504"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":4,"checks":[{"name":"Code-Review","score":8,"reason":"Found 20/24 approved changesets -- score normalized to 8","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Dangerous-Workflow","score":10,"reason":"no dangerous workflow patterns detected","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Maintained","score":1,"reason":"2 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 1","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Token-Permissions","score":0,"reason":"detected GitHub workflow tokens with excessive permissions","details":["Warn: no topLevel permission defined: .github/workflows/integration.yml:1","Warn: no topLevel permission defined: .github/workflows/npm-publish.yml:1","Info: no jobLevel write permissions found"],"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Branch-Protection","score":-1,"reason":"internal error: error during branchesHandler.setup: internal error: githubv4.Query: Resource not accessible by integration","details":null,"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: MIT License: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"Pinned-Dependencies","score":5,"reason":"dependency not pinned by hash detected -- score normalized to 5","details":["Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/integration.yml:14: update your workflow using https://app.stepsecurity.io/secureworkflow/mercadona/wrapito/integration.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/integration.yml:15: update your workflow using https://app.stepsecurity.io/secureworkflow/mercadona/wrapito/integration.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/npm-publish.yml:14: update your workflow using https://app.stepsecurity.io/secureworkflow/mercadona/wrapito/npm-publish.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/npm-publish.yml:15: update your workflow using https://app.stepsecurity.io/secureworkflow/mercadona/wrapito/npm-publish.yml/master?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/npm-publish.yml:32: update your workflow using https://app.stepsecurity.io/secureworkflow/mercadona/wrapito/npm-publish.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/npm-publish.yml:37: update your workflow using https://app.stepsecurity.io/secureworkflow/mercadona/wrapito/npm-publish.yml/master?enable=pin","Info:   0 out of   5 GitHub-owned GitHubAction dependencies pinned","Info:   0 out of   1 third-party GitHubAction dependencies pinned","Info:   2 out of   2 npmCommand dependencies pinned"],"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 27 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}},{"name":"Vulnerabilities","score":0,"reason":"12 existing vulnerabilities detected","details":["Warn: Project is vulnerable to: GHSA-968p-4wvh-cqc8","Warn: Project is vulnerable to: GHSA-v6h2-p8h4-qcjw","Warn: Project is vulnerable to: GHSA-67mh-4wv8-2f99","Warn: Project is vulnerable to: GHSA-fjxv-7rqg-78g4","Warn: Project is vulnerable to: GHSA-mwcw-c2x4-8c55","Warn: Project is vulnerable to: GHSA-vg6x-rcgg-rjx6","Warn: Project is vulnerable to: GHSA-x574-m823-4x7w","Warn: Project is vulnerable to: GHSA-4r4m-qw57-chr8","Warn: Project is vulnerable to: GHSA-xcj6-pq6g-qj4x","Warn: Project is vulnerable to: GHSA-356w-63v5-8wf4","Warn: Project is vulnerable to: GHSA-859w-5945-r5v3","Warn: Project is vulnerable to: GHSA-9crc-q9x8-hgqq"],"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}}]},"last_synced_at":"2025-08-21T09:24:31.199Z","repository_id":38461803,"created_at":"2025-08-21T09:24:31.199Z","updated_at":"2025-08-21T09:24:31.199Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29651971,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-20T09:27:29.698Z","status":"ssl_error","status_checked_at":"2026-02-20T09:26:12.373Z","response_time":59,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["jest","react","testing","type-library"],"created_at":"2025-12-29T16:36:32.134Z","updated_at":"2026-02-20T13:02:20.580Z","avatar_url":"https://github.com/mercadona.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# 🌯 wrapito\n\nWrap you tests so that you can test both behaviour and components with less effort.\n\n## IMPORTANT\n\nThis version is agnostic and compatible with both [jest](https://jestjs.io/) and [vitest](https://vitest.dev/).\n\n### Note:\n\nFrom the version 13 wrapito is compatible with the new version of React and requires such versions of dependencies:\n\n```\n\"peerDependencies\": {\n    \"@testing-library/jest-dom\": \"\u003e=5.16.4\",\n    \"@testing-library/react\": \"\u003e=14.0.0\",\n    \"react-dom\": \"\u003e=18.0.0\",\n    \"react\": \"\u003e=18.0.0\"\n  }\n```\n\nIf your project uses React \u003c=18.0.0, you can use wrapito \u003c=12, but we extremely recommend to migrate to newest versions, because we are not maintaining legacy dependencies.\n\n## 🎯 Motivation\n\nAs we are more focused on user interactions than implementation details. In order to test all the user interactions that\ncan be done at a certain point of our app, the upper the component we render in the test, the better.\n\n## 💡 The idea\n\nAs we test our app we will be in two different scenarios where:\n\n- We will need to test that the user interactions cause the proper side effects such as making http calls or refreshing\n  the UI.\n- In case we have a components library, we will need to test these by passing the needed props and checking that it\n  returns (renders) the expected result.\n\nIn general, if you want to test behaviour, you need to simulate external actions from user or from http responses.\nUnfortunately, there aren't so many options when it comes to manage http requests and responses in the tests.\nTo give the mounted component context about which path is the current path where the app should be mounted, what props\ndoes the component receive, what http requests will respond with which results or where should the portal be mounted we\nhave used the builder pattern that makes tests much more semantic.\n\n## 🔧 Installing\n\nUsing npm:\n\n```sh\n$ npm install wrapito\n```\n\n## 👩‍💻 Basic usage\n\n```js\nconst MyComponent = () =\u003e \u003cspan\u003eJust a component\u003c/span\u003e\n\nconst myWrappedComponent = wrap(MyComponent).mount()\n```\n\n## 👣 Initial setup\n\nIn the latest version of 🌯 `wrapito` passing the rendering/mounting function is optional, because we use `render` from `@testing-library/react` by default.\n\nIf one or more of your components use a `react portal` in any way, you will need to specify the `id` of the node where\nit will be added.\n\nTo configure wrapito we recommend adding a setupTests.tsx file and adding there all your custom configs and extensions.\n\n```js\nimport { configure } from 'wrapito'\n\nconfigure({\n  defaultHost: 'your-host-path',\n  portal: 'modal-root',\n  extend: {\n    /* Here you can group network calls to reuse them in your tests */\n  },\n})\n```\n\nAdd this line in your project setup (vite/cra):\n\n```js\nsetupFiles: ['./src/setupTests.tsx']\n```\n\n## 🏰 Builder API\n\n#### withMocks (Deprecated)\n\nIt has the same API than the withNetwork builder. The main difference between them is that withMocks will fail if a\ngiven request, done by the production code, is not set up in the `responses object`.\n\n#### withNetwork\n\nBy using this feature you can configure the responses for your `http requests`. If your component is making a request\nthat is not set up in the `responses object`, it will not be validated and it will return an empty response with code 200.\n\n```js\nimport { wrap } from 'wrapito'\n\nconst responses = {\n  host: 'my-host',\n  method: 'get',\n  path: '/path/to/get/a/single/product/',\n  responseBody: { id: 1, name: 'hummus' },\n  status: 200,\n  catchParams: true,\n  delay: 500,\n}\n\nwrap(MyComponent).withNetwork(responses).mount()\n```\n\nYou can specify the default `host` via configuration:\n\n```js\nimport { configure } from 'wrapito'\n\nconst { API_HOST, API_VERSION } = process.env\nconfigure({ defaultHost: `${API_HOST}${API_VERSION}` })\n```\n\nIn addition, `wrapito` defaults the `method` to `'get'` and `status` to `200`. This means one can use `withNetwork` like\nthis:\n\n```js\nimport { wrap } from 'wrapito'\n\nconst responses = {\n  path: '/path/to/get/a/single/product/,\n  responseBody: { id: 1, name: 'hummus' },\n}\n\nwrap(MyComponent)\n  .withNetwork(responses)\n  .mount()\n```\n\nNow, you might need to mock several `http responses` at the same time and that's why you can also pass an array of\nresponses instead if you wish:\n\n```js\nimport { wrap } from 'wrapito'\n\nconst responses = [\n  {\n    path: '/path/to/get/the/products/list/',\n    responseBody: [\n      { id: 1, name: 'hummus' },\n      { id: 2, name: 'guacamole' },\n    ],\n  },\n  {\n    path: '/path/to/get/a/single/product/',\n    responseBody: { id: 1, name: 'hummus' },\n  },\n]\n\nwrap(MyComponent).withNetwork(responses).mount()\n```\n\nThere might be cases where one request is called several times and we want it to return different responses. An example\nof this could be an app that shows a list of products that may be updated over time and for this propose the app has a\nrefresh button that will request the list again in order to update its content.\n\nWell, it can be solved by specifying the response as multiple using `multipleResponse` as follows:\n\n```js\nconst responses = {\n  path: '/path/to/get/the/products/list/,\n  multipleResponses: [\n    {\n      responseBody: [\n        { id: 1, name: 'hummus' },\n        { id: 2, name: 'guacamole' },\n      ]\n    },\n    {\n      responseBody: [\n        { id: 1, name: 'hummus' },\n        { id: 2, name: 'guacamole' },\n        { id: 3, name: 'peanut butter' },\n      ]\n    },\n  ],\n}\n```\n\n`multipleResponses` receives an array of responses where one set the `responseBody`, `status` or `headers` for every\nresponse.\n\nWhen `multipleResponses` is present, 🌯 `wrapito` will ignore the `responseBody` at the root of the mock and will return\none response per request made at the same time that sets the returned response as `hasBeenReturned`, which means it can\nbe returned again, until all the array of responses is returned. In that case an exception will be raised.\n\nThis behaviour differs from using a single response for a given request as single response for a given request will\nreturn the response no matter how many times the request is called.\n\n#### atPath\n\nWhen mounting the whole app, it will be done at the default route passing the default path. But a specific route might\nneed to be mounted and for that reason a path to match that route should be pass here.\n\n```js\nimport { wrap } from 'wrapito'\n\nwrap(MyComponent)\n  .atPath(`/the/path/to/match/a/route/that/mounts/my/component/3`)\n  .mount()\n```\n\nBy default it will use the native javascript history API, but you can provide a method to be called for change the app\nroute with [`changeRoute`](#changeRoute):\n\n```js\nimport { configure } from 'wrapito'\nimport { history } from 'app.js'\n\nconfigure({\n  ..configuration,\n  changeRoute: (route) =\u003e history.push(route)\n})\n```\n\n#### withProps\n\nPass down the props to the wrapped component:\n\n```js\nimport { wrap } from 'wrapito'\n\nconst props = { message: 'MyComponent will receive this as prop' }\n\nwrap(MyComponent).withProps(props).mount()\n```\n\n#### composing\n\nAs it is basically a builder, all the above functions can be used at the same time and these will be composed\nunderneath:\n\n```js\nimport { wrap } from 'wrapito'\n\nconst props = { message: 'MyComponent will receive this as prop' }\nconst responses = {\n  path: '/path/to/get/a/single/product/by/id/1/,\n  responseBody: { id: 1, name: 'hummus' },\n}\n\nwrap(PreparationContainer)\n  .atPath('/products/1')\n  .withNetwork(responses)\n  .withProps()\n  .mount()\n```\n\n## ✨ Utils\n\n#### toHaveBeenFetched\n\nSome times checking only the visual side effects in the UI it's not enough. In the case that we want to check if a\nparticular network side effect is happening, this assertion will come in handy.\n\n```js\nwrap(MyComponentMakingHttpCalls).withNetwork(responses).mount()\n\nexpect('/some/path').toHaveBeenFetched()\n```\n\n#### toHaveBeenFetchedWith\n\nThis assertion is an extension of `toHaveBeenFetched` but we will use it if we want to check the properties.\n\n```js\nimport { wrap, assertions } from 'wrapito'\n\nwrap(MyComponentMakingHttpCalls).withNetwork(responses).mount()\n\nexpect('/some/path').toHaveBeenFetchedWith({\n  method: 'POST',\n  body: { key: 'value' },\n})\n```\n\n#### toHaveBeenFetchedTimes\n\nThis assertion is to check how many times an API url is called.\n\n```js\nimport { wrap, assertions } from 'wrapito'\n\nexpect.extend(assertions)\n\nconst responses = [{ path: '/path/to/get/quantity/' }]\n\nwrap(MyComponentMakingHttpCalls).withNetwork(responses).mount()\n\nexpect('/path/to/get/quantity/').toHaveBeenFetchedTimes(1)\n```\n\n## 🔧 Development\n\nIn order to test the library in a project without releasing the library:\n\n- `npm run build`\n- This will generate a local build in the `dist` folder\n- Copy the content of that folder in `node_modules/wrapito` in your project\n\n## Deploy new version in npm\n\nYou need to create a new tag for the project. E.g:\n\n```\ngit tag v1.0.5\ngit push origin v1.0.5\n```\n\nThis will run a workflow in github that will publish this version for you.\n\n### Release beta versions\n\nWARNING: DO NOT MERGE YOUR PR IF YOU WANT TO DO A BETA RELEASE, SINCE THE CHANGES ARE NOT FULLY TRUSTED THEY SHOULD NOT GO TO MASTER\n\nIf you need to release beta versions to test things, you may do so with the -beta tag. E.g:\n\n```\ngit tag v1.0.5-beta1\ngit push origin v1.0.5-beta1\n```\n\nThis will run a workflow in github that will publish this version for you as a pre-release.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmercadona%2Fwrapito","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmercadona%2Fwrapito","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmercadona%2Fwrapito/lists"}