wrapito 13.0.0-beta2 → 13.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +39 -38
- package/dist/index.d.cts +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +3 -3
- package/dist/index.mjs +3 -3
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -6,6 +6,21 @@ Wrap you tests so that you can test both behaviour and components with less effo
|
|
|
6
6
|
|
|
7
7
|
This version is agnostic and compatible with both [jest](https://jestjs.io/) and [vitest](https://vitest.dev/).
|
|
8
8
|
|
|
9
|
+
### Note:
|
|
10
|
+
|
|
11
|
+
From the version 13 wrapito is compatible with the new version of React and requires such versions of dependencies:
|
|
12
|
+
|
|
13
|
+
```
|
|
14
|
+
"peerDependencies": {
|
|
15
|
+
"@testing-library/jest-dom": ">=5.16.4",
|
|
16
|
+
"@testing-library/react": ">=14.0.0",
|
|
17
|
+
"react-dom": ">=18.0.0",
|
|
18
|
+
"react": ">=18.0.0"
|
|
19
|
+
}
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
If your project uses React <=18.0.0, you can use wrapito <=12, but we extremely recommend to migrate to newest versions, because we are not maintaining legacy dependencies.
|
|
23
|
+
|
|
9
24
|
## 🎯 Motivation
|
|
10
25
|
|
|
11
26
|
As we are more focused on user interactions than implementation details. In order to test all the user interactions that
|
|
@@ -21,8 +36,6 @@ As we test our app we will be in two different scenarios where:
|
|
|
21
36
|
returns (renders) the expected result.
|
|
22
37
|
|
|
23
38
|
In general, if you want to test behaviour, you need to simulate external actions from user or from http responses.
|
|
24
|
-
Most of the existing testing libraries give you control of the user actions and thats why we just ask you to set in the
|
|
25
|
-
config what is the `render` function of your testing library.
|
|
26
39
|
Unfortunately, there aren't so many options when it comes to manage http requests and responses in the tests.
|
|
27
40
|
To give the mounted component context about which path is the current path where the app should be mounted, what props
|
|
28
41
|
does the component receive, what http requests will respond with which results or where should the portal be mounted we
|
|
@@ -46,40 +59,29 @@ const myWrappedComponent = wrap(MyComponent).mount()
|
|
|
46
59
|
|
|
47
60
|
## 👣 Initial setup
|
|
48
61
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
62
|
+
In the latest version of 🌯 `wrapito` passing the rendering/mounting function is optional, because we use `render` from `@testing-library/react` by default.
|
|
63
|
+
|
|
64
|
+
If one or more of your components use a `react portal` in any way, you will need to specify the `id` of the node where
|
|
65
|
+
it will be added.
|
|
66
|
+
|
|
67
|
+
To configure wrapito we recommend adding a setupTests.tsx file and adding there all your custom configs and extensions.
|
|
52
68
|
|
|
53
69
|
```js
|
|
54
|
-
import { render } from '@testing-library/react'
|
|
55
70
|
import { configure } from 'wrapito'
|
|
56
71
|
|
|
57
72
|
configure({
|
|
58
|
-
|
|
73
|
+
defaultHost: 'your-host-path',
|
|
74
|
+
portal: 'modal-root',
|
|
75
|
+
extend: {
|
|
76
|
+
/* Here you can group network calls to reuse them in your tests */
|
|
77
|
+
},
|
|
59
78
|
})
|
|
60
79
|
```
|
|
61
80
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
```js
|
|
65
|
-
"setupFiles"
|
|
66
|
-
:
|
|
67
|
-
[
|
|
68
|
-
"<rootDir>/config/jest/setup.wrapito.js"
|
|
69
|
-
],
|
|
70
|
-
```
|
|
71
|
-
|
|
72
|
-
If one or more of your components use a `react portal` in any way, you will need to specify the `id` of the node where
|
|
73
|
-
it will be added:
|
|
81
|
+
Add this line in your project setup (vite/cra):
|
|
74
82
|
|
|
75
83
|
```js
|
|
76
|
-
|
|
77
|
-
import { configure } from 'wrapito'
|
|
78
|
-
|
|
79
|
-
configure({
|
|
80
|
-
mount: render,
|
|
81
|
-
portal: 'modal-root',
|
|
82
|
-
})
|
|
84
|
+
setupFiles: ['./src/setupTests.tsx']
|
|
83
85
|
```
|
|
84
86
|
|
|
85
87
|
## 🏰 Builder API
|
|
@@ -92,8 +94,7 @@ given request, done by the production code, is not set up in the `responses obje
|
|
|
92
94
|
#### withNetwork
|
|
93
95
|
|
|
94
96
|
By using this feature you can configure the responses for your `http requests`. If your component is making a request
|
|
95
|
-
that is not set up in the `responses object`, it will not be validated and it will return an empty response with code
|
|
96
|
-
200.
|
|
97
|
+
that is not set up in the `responses object`, it will not be validated and it will return an empty response with code 200.
|
|
97
98
|
|
|
98
99
|
```js
|
|
99
100
|
import { wrap } from 'wrapito'
|
|
@@ -101,16 +102,14 @@ import { wrap } from 'wrapito'
|
|
|
101
102
|
const responses = {
|
|
102
103
|
host: 'my-host',
|
|
103
104
|
method: 'get',
|
|
104
|
-
path: '/path/to/get/a/single/product
|
|
105
|
+
path: '/path/to/get/a/single/product/',
|
|
105
106
|
responseBody: { id: 1, name: 'hummus' },
|
|
106
107
|
status: 200,
|
|
107
108
|
catchParams: true,
|
|
108
109
|
delay: 500,
|
|
109
110
|
}
|
|
110
111
|
|
|
111
|
-
wrap(MyComponent)
|
|
112
|
-
.withNetwork(responses)
|
|
113
|
-
.mount()
|
|
112
|
+
wrap(MyComponent).withNetwork(responses).mount()
|
|
114
113
|
```
|
|
115
114
|
|
|
116
115
|
You can specify the default `host` via configuration:
|
|
@@ -146,21 +145,19 @@ import { wrap } from 'wrapito'
|
|
|
146
145
|
|
|
147
146
|
const responses = [
|
|
148
147
|
{
|
|
149
|
-
path: '/path/to/get/the/products/list
|
|
148
|
+
path: '/path/to/get/the/products/list/',
|
|
150
149
|
responseBody: [
|
|
151
150
|
{ id: 1, name: 'hummus' },
|
|
152
151
|
{ id: 2, name: 'guacamole' },
|
|
153
|
-
]
|
|
152
|
+
],
|
|
154
153
|
},
|
|
155
154
|
{
|
|
156
|
-
path: '/path/to/get/a/single/product
|
|
155
|
+
path: '/path/to/get/a/single/product/',
|
|
157
156
|
responseBody: { id: 1, name: 'hummus' },
|
|
158
157
|
},
|
|
159
158
|
]
|
|
160
159
|
|
|
161
|
-
wrap(MyComponent)
|
|
162
|
-
.withNetwork(responses)
|
|
163
|
-
.mount()
|
|
160
|
+
wrap(MyComponent).withNetwork(responses).mount()
|
|
164
161
|
```
|
|
165
162
|
|
|
166
163
|
There might be cases where one request is called several times and we want it to return different responses. An example
|
|
@@ -322,6 +319,10 @@ git push origin v1.0.5
|
|
|
322
319
|
|
|
323
320
|
This will run a workflow in github that will publish this version for you.
|
|
324
321
|
|
|
322
|
+
### Release beta versions
|
|
323
|
+
|
|
324
|
+
WARNING: 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
|
|
325
|
+
|
|
325
326
|
If you need to release beta versions to test things, you may do so with the -beta tag. E.g:
|
|
326
327
|
|
|
327
328
|
```
|
package/dist/index.d.cts
CHANGED
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
"use strict";var
|
|
1
|
+
"use strict";var me=Object.create;var T=Object.defineProperty;var de=Object.getOwnPropertyDescriptor;var he=Object.getOwnPropertyNames;var fe=Object.getPrototypeOf,ge=Object.prototype.hasOwnProperty;var ye=(e,t)=>{for(var s in t)T(e,s,{get:t[s],enumerable:!0})},$=(e,t,s,n)=>{if(t&&typeof t=="object"||typeof t=="function")for(let o of he(t))!ge.call(e,o)&&o!==s&&T(e,o,{get:()=>t[o],enumerable:!(n=de(t,o))||n.enumerable});return e};var E=(e,t,s)=>(s=e!=null?me(fe(e)):{},$(t||!e||!e.__esModule?T(s,"default",{value:e,enumerable:!0}):s,e)),Re=e=>$(T({},"__esModule",{value:!0}),e);var Ye={};ye(Ye,{assertions:()=>Je,configure:()=>we,getConfig:()=>d,matchers:()=>Je,wrap:()=>Ae});module.exports=Re(Ye);var J=E(require("react"),1);var f=E(require("chalk"),1);var H=E(require("object-hash"),1);var D=require("@testing-library/react"),C={defaultHost:"",extend:{},mount:D.render,changeRoute:e=>window.history.replaceState(null,"",e)};function we(e){C={...C,...e}}var d=()=>({...C});var U=e=>t=>{let{method:s="GET",path:n,host:o=d().defaultHost,requestBody:r=void 0,catchParams:a}=t,l=o+n,i=!d().handleQueryParams||a,u=(0,H.default)({url:i?l:l.split("?")[0],method:s.toUpperCase(),requestBody:r}),p;return"_bodyInit"in e&&e._bodyInit!==void 0&&(p=JSON.parse(e._bodyInit)),(0,H.default)({url:i?e.url:e.url.split("?")[0],method:e.method,requestBody:p})===u};function P(e,t){if(!e)throw new Error(t)}function b(e,t){return typeof t===e}function be(e){return e instanceof Promise}function B(e,t,s){Object.defineProperty(e,t,s)}function k(e,t,s){Object.defineProperty(e,t,{value:s})}var M=Symbol.for("tinyspy:spy"),ke=new Set,xe=e=>{e.called=!1,e.callCount=0,e.calls=[],e.results=[],e.next=[]},Me=e=>(B(e,M,{value:{reset:()=>xe(e[M])}}),e[M]),O=e=>e[M]||Me(e);function Te(e){P(b("function",e)||b("undefined",e),"cannot spy on a non-function value");let t=function(...n){let o=O(t);o.called=!0,o.callCount++,o.calls.push(n);let r=o.next.shift();if(r){o.results.push(r);let[u,p]=r;if(u==="ok")return p;throw p}let a,l="ok";if(o.impl)try{new.target?a=Reflect.construct(o.impl,n,new.target):a=o.impl.apply(this,n),l="ok"}catch(u){throw a=u,l="error",o.results.push([l,u]),u}let i=[l,a];if(be(a)){let u=a.then(p=>i[1]=p).catch(p=>{throw i[0]="error",i[1]=p,p});Object.assign(u,a),a=u}return o.results.push(i),a};k(t,"_isMockFunction",!0),k(t,"length",e?e.length:0),k(t,"name",e&&e.name||"spy");let s=O(t);return s.reset(),s.impl=e,t}var F=(e,t)=>Object.getOwnPropertyDescriptor(e,t),L=(e,t)=>{t!=null&&typeof t=="function"&&t.prototype!=null&&Object.setPrototypeOf(e.prototype,t.prototype)};function _(e,t,s){P(!b("undefined",e),"spyOn could not find an object to spy upon"),P(b("object",e)||b("function",e),"cannot spyOn on a primitive value");let[n,o]=(()=>{if(!b("object",t))return[t,"value"];if("getter"in t&&"setter"in t)throw new Error("cannot spy on both getter and setter");if("getter"in t)return[t.getter,"get"];if("setter"in t)return[t.setter,"set"];throw new Error("specify getter or setter to spy on")})(),r=F(e,n),a=Object.getPrototypeOf(e),l=a&&F(a,n),i=r||l;P(i||n in e,`${String(n)} does not exist`);let u=!1;o==="value"&&i&&!i.value&&i.get&&(o="get",u=!0,s=i.get());let p;i?p=i[o]:o!=="value"?p=()=>e[n]:p=e[n],s||(s=p);let m=Te(s);o==="value"&&L(m,p);let c=w=>{let{value:W,...A}=i||{configurable:!0,writable:!0};o!=="value"&&delete A.writable,A[o]=w,B(e,n,A)},R=()=>i?B(e,n,i):c(p),h=m[M];return k(h,"restore",R),k(h,"getOriginal",()=>u?p():p),k(h,"willCall",w=>(h.impl=w,m)),c(u?()=>(L(m,s),m):m),ke.add(m),m}var Pe=new Set,Oe=0;function qe(e){let t=e,s,n=[],o=[],r=O(e),a={get calls(){return r.calls},get instances(){return n},get invocationCallOrder(){return o},get results(){return r.results.map(([c,R])=>({type:c==="error"?"throw":"return",value:R}))},get lastCall(){return r.calls[r.calls.length-1]}},l=[],i=!1;function u(...c){return n.push(this),o.push(++Oe),(i?s:l.shift()||s||r.getOriginal()||(()=>{})).apply(this,c)}let p=t.name;t.getMockName=()=>p||"vi.fn()",t.mockName=c=>(p=c,t),t.mockClear=()=>(r.reset(),n=[],o=[],t),t.mockReset=()=>(t.mockClear(),s=()=>{},l=[],t),t.mockRestore=()=>(t.mockReset(),r.restore(),s=void 0,t),t.getMockImplementation=()=>s,t.mockImplementation=c=>(s=c,r.willCall(u),t),t.mockImplementationOnce=c=>(l.push(c),t);function m(c,R){let h=s;s=c,r.willCall(u),i=!0;let w=()=>{s=h,i=!1},W=R();return W instanceof Promise?W.then(()=>(w(),t)):(w(),t)}return t.withImplementation=m,t.mockReturnThis=()=>t.mockImplementation(function(){return this}),t.mockReturnValue=c=>t.mockImplementation(()=>c),t.mockReturnValueOnce=c=>t.mockImplementationOnce(()=>c),t.mockResolvedValue=c=>t.mockImplementation(()=>Promise.resolve(c)),t.mockResolvedValueOnce=c=>t.mockImplementationOnce(()=>Promise.resolve(c)),t.mockRejectedValue=c=>t.mockImplementation(()=>Promise.reject(c)),t.mockRejectedValueOnce=c=>t.mockImplementationOnce(()=>Promise.reject(c)),Object.defineProperty(t,"mock",{get:()=>a}),r.willCall(u),Pe.add(t),t}var q=e=>qe(_({spy:e||(()=>{})},"spy"));beforeEach(()=>{global.window.fetch=q()});afterEach(()=>{global.window.fetch.mockReset()});var Ie=async()=>{let e={json:()=>Promise.resolve(),status:200,ok:!0,headers:new Headers({"Content-Type":"application/json"})};return Promise.resolve(e)},G=async e=>{let{responseBody:t,status:s=200,headers:n={},delay:o}=e,r={json:()=>Promise.resolve(t),status:s,ok:s>=200&&s<=299,headers:new Headers({"Content-Type":"application/json",...n})};return o?new Promise(a=>setTimeout(()=>a(r),o)):Promise.resolve(r)},ve=e=>console.warn(`
|
|
2
2
|
${f.default.white.bold.bgRed("wrapito")} ${f.default.redBright.bold("cannot find any mock matching:")}
|
|
3
3
|
${f.default.greenBright(`URL: ${e.url}`)}
|
|
4
4
|
${f.default.greenBright(`METHOD: ${e.method?.toLowerCase()}`)}
|
|
5
5
|
${f.default.greenBright(`REQUEST BODY: ${e._bodyInit}`)}
|
|
6
|
-
`),V=async(e,t,
|
|
6
|
+
`),V=async(e,t,s)=>{let n=e.find(U(t));if(!n)return s&&ve(t),Ie();let{multipleResponses:o}=n;if(!o)return G(n);let r=o.find(a=>!a.hasBeenReturned);if(!r){s&&We(n);return}return r.hasBeenReturned=!0,G(r)},Q=(e=[],t=!1)=>{global.window.fetch.mockImplementation((n,o)=>{if(typeof n=="string"){let a=new Request(n,o);return V(e,a,t)}return V(e,n,t)})},We=e=>{let t=`\u{1F32F} Wrapito: Missing response in the multipleResponses array for path ${e.path} and method ${e.method}.`,s=f.default.greenBright(t);console.warn(s)};var j,g=e=>{j={...j,...e}},y=()=>({...j});beforeEach(()=>{global.fetch=q()});afterEach(()=>{global.fetch.mockReset()});var Ae=e=>(g({Component:e,responses:[],props:{},path:"",hasPath:!1,debug:process.env.npm_config_debugRequests==="true"}),x()),x=()=>{let e=je();return{withProps:Ne,withNetwork:Se,atPath:$e,debugRequests:De,mount:Ue,...e}},Ce=e=>{let t=y(),s=[...t.responses,...e];g({...t,responses:s})},He=(e,t)=>(t({addResponses:Ce},e),x()),Be=(e,t)=>{let{extend:s}=d(),n=s[t];return{...e,[t]:(...o)=>He(o,n)}},je=()=>{let{extend:e}=d();return Object.keys(e).reduce(Be,{})},Ne=e=>{let t=y();return g({...t,props:e}),x()},Se=(e=[])=>{let t=y(),s=Array.isArray(e)?e:[e];return g({...t,responses:[...t.responses,...s]}),x()},$e=(e,t)=>{let s=y();return g({...s,historyState:t,path:e,hasPath:!0}),x()},De=()=>{let e=y();return g({...e,debug:!0}),x()},Ue=()=>{let{portal:e,portals:t,changeRoute:s,history:n,mount:o}=d(),{Component:r,props:a,responses:l,path:i,hasPath:u,debug:p,historyState:m}=y(),c=r;return e&&Y(e),t&&Fe(t),u&&n&&(console.warn("wrapito WARNING: history is DEPRECATED. Pass a changeRoute function to the config instead."),console.warn("Read about changeRoute in: https://github.com/mercadona/wrapito#changeRoute"),n.push(i,m)),u&&!n&&s(i),Q(l,p),o(J.createElement(c,{...a}))},Y=e=>{if(document.getElementById(e))return;let t=document.createElement("div");t.setAttribute("id",e),t.setAttribute("data-testid",e),document.body.appendChild(t)},Fe=e=>{e.forEach(t=>{Y(t)})};var z=require("jest-diff"),N=(e,t)=>{let s=t?.host?`\u{1F32F} Wrapito: ${t?.host}${e} ain't got called`:`\u{1F32F} Wrapito: ${e} ain't got called`;return{pass:!1,message:()=>s}},K=(e,t,s)=>({pass:!1,message:()=>`\u{1F32F} Wrapito: ${e} is called ${s} times, you expected ${t} times`}),X=(e,t)=>({pass:!1,message:()=>`\u{1F32F} Wrapito: Fetch method does not match, expected ${e} received ${t??"none"}`}),Z=(e,t)=>{let s=t.map(n=>(0,z.diff)(e,n)).join(`
|
|
7
7
|
|
|
8
8
|
`);return{pass:!1,message:()=>`\u{1F32F} Wrapito: Fetch body does not match.
|
|
9
|
-
${n}`}},
|
|
9
|
+
${s}`}},ee=(e,t)=>{let s=t.find(n=>e!==n);return{pass:!1,message:()=>`\u{1F32F} Wrapito: Host request does not match, expected ${e} received ${s}`}},te=()=>({pass:!1,message:()=>"\u{1F32F} Wrapito: Unable to find body."}),S=()=>({pass:!0,message:()=>"Test passing"}),se=(e,t)=>{let s=t?.host?`\u{1F32F} Wrapito: ${t.host}${e} is called`:`\u{1F32F} Wrapito: ${e} is called`;return{pass:!0,message:()=>s}};var ne=E(require("deep-equal"),1);var oe=()=>{let e=d().defaultHost;return e?.includes("http")?e:"https://default.com"},Le=(e="",t,s)=>t.includes(s)?t:e+t,I=e=>e instanceof Request,_e=e=>I(e)?e.url:e,v=(e,t={method:"GET"})=>fetch.mock.calls.filter(([n])=>{let o=_e(n),r=oe(),a=new URL(o,r),l=Le(t?.host,e,r),i=t?.host||r,u=new URL(l,i),p=a.pathname===u.pathname,m=a.search===u.search,c=a.host===u.host;return u.search?p&&m:t?.host?p&&c:p}),re=e=>e.flat(1).filter(I).map(t=>t.method),ae=e=>e.flat(1).filter(I).map(t=>t._bodyInit?JSON.parse(t._bodyInit):{}),ie=e=>e.flat(1).filter(I).map(t=>new URL(t.url,oe()).hostname),ce=(e,t)=>e&&!t.includes(e),pe=(e,t)=>t.map(n=>(0,ne.default)(e,n)).every(n=>n===!1),ue=(e,t)=>t.every(n=>n!==e),le=e=>e.length===0;var Ge=(e,t)=>{let s=v(e);if(le(s))return N(e);let n=re(s),o=t?.method;if(ce(o,n))return X(o,n);let r=ae(s),a=t?.body;if(!a)return te();if(pe(a,r))return Z(a,r);let l=ie(s),i=t?.host;return i&&ue(i,l)?ee(i,l):S()},Ve=(e,t={method:"GET"})=>v(e,t).length?se(e,t):N(e,t),Qe=(e,t,s={method:"GET"})=>{let n=v(e,s);return n.length!==t?K(e,t,n.length):S()},Je={toHaveBeenFetched:Ve,toHaveBeenFetchedWith:Ge,toHaveBeenFetchedTimes:Qe};0&&(module.exports={assertions,configure,getConfig,matchers,wrap});
|
package/dist/index.mjs
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import*as _ from"react";import k from"chalk";import
|
|
1
|
+
import*as _ from"react";import k from"chalk";import j from"object-hash";import{render as ae}from"@testing-library/react";var W={defaultHost:"",extend:{},mount:ae,changeRoute:e=>window.history.replaceState(null,"",e)};function Ne(e){W={...W,...e}}var d=()=>({...W});var N=e=>t=>{let{method:s="GET",path:n,host:o=d().defaultHost,requestBody:r=void 0,catchParams:a}=t,l=o+n,i=!d().handleQueryParams||a,u=j({url:i?l:l.split("?")[0],method:s.toUpperCase(),requestBody:r}),p;return"_bodyInit"in e&&e._bodyInit!==void 0&&(p=JSON.parse(e._bodyInit)),j({url:i?e.url:e.url.split("?")[0],method:e.method,requestBody:p})===u};function T(e,t){if(!e)throw new Error(t)}function w(e,t){return typeof t===e}function ie(e){return e instanceof Promise}function A(e,t,s){Object.defineProperty(e,t,s)}function b(e,t,s){Object.defineProperty(e,t,{value:s})}var M=Symbol.for("tinyspy:spy"),ce=new Set,pe=e=>{e.called=!1,e.callCount=0,e.calls=[],e.results=[],e.next=[]},ue=e=>(A(e,M,{value:{reset:()=>pe(e[M])}}),e[M]),E=e=>e[M]||ue(e);function le(e){T(w("function",e)||w("undefined",e),"cannot spy on a non-function value");let t=function(...n){let o=E(t);o.called=!0,o.callCount++,o.calls.push(n);let r=o.next.shift();if(r){o.results.push(r);let[u,p]=r;if(u==="ok")return p;throw p}let a,l="ok";if(o.impl)try{new.target?a=Reflect.construct(o.impl,n,new.target):a=o.impl.apply(this,n),l="ok"}catch(u){throw a=u,l="error",o.results.push([l,u]),u}let i=[l,a];if(ie(a)){let u=a.then(p=>i[1]=p).catch(p=>{throw i[0]="error",i[1]=p,p});Object.assign(u,a),a=u}return o.results.push(i),a};b(t,"_isMockFunction",!0),b(t,"length",e?e.length:0),b(t,"name",e&&e.name||"spy");let s=E(t);return s.reset(),s.impl=e,t}var S=(e,t)=>Object.getOwnPropertyDescriptor(e,t),$=(e,t)=>{t!=null&&typeof t=="function"&&t.prototype!=null&&Object.setPrototypeOf(e.prototype,t.prototype)};function D(e,t,s){T(!w("undefined",e),"spyOn could not find an object to spy upon"),T(w("object",e)||w("function",e),"cannot spyOn on a primitive value");let[n,o]=(()=>{if(!w("object",t))return[t,"value"];if("getter"in t&&"setter"in t)throw new Error("cannot spy on both getter and setter");if("getter"in t)return[t.getter,"get"];if("setter"in t)return[t.setter,"set"];throw new Error("specify getter or setter to spy on")})(),r=S(e,n),a=Object.getPrototypeOf(e),l=a&&S(a,n),i=r||l;T(i||n in e,`${String(n)} does not exist`);let u=!1;o==="value"&&i&&!i.value&&i.get&&(o="get",u=!0,s=i.get());let p;i?p=i[o]:o!=="value"?p=()=>e[n]:p=e[n],s||(s=p);let m=le(s);o==="value"&&$(m,p);let c=R=>{let{value:I,...v}=i||{configurable:!0,writable:!0};o!=="value"&&delete v.writable,v[o]=R,A(e,n,v)},y=()=>i?A(e,n,i):c(p),h=m[M];return b(h,"restore",y),b(h,"getOriginal",()=>u?p():p),b(h,"willCall",R=>(h.impl=R,m)),c(u?()=>($(m,s),m):m),ce.add(m),m}var de=new Set,he=0;function fe(e){let t=e,s,n=[],o=[],r=E(e),a={get calls(){return r.calls},get instances(){return n},get invocationCallOrder(){return o},get results(){return r.results.map(([c,y])=>({type:c==="error"?"throw":"return",value:y}))},get lastCall(){return r.calls[r.calls.length-1]}},l=[],i=!1;function u(...c){return n.push(this),o.push(++he),(i?s:l.shift()||s||r.getOriginal()||(()=>{})).apply(this,c)}let p=t.name;t.getMockName=()=>p||"vi.fn()",t.mockName=c=>(p=c,t),t.mockClear=()=>(r.reset(),n=[],o=[],t),t.mockReset=()=>(t.mockClear(),s=()=>{},l=[],t),t.mockRestore=()=>(t.mockReset(),r.restore(),s=void 0,t),t.getMockImplementation=()=>s,t.mockImplementation=c=>(s=c,r.willCall(u),t),t.mockImplementationOnce=c=>(l.push(c),t);function m(c,y){let h=s;s=c,r.willCall(u),i=!0;let R=()=>{s=h,i=!1},I=y();return I instanceof Promise?I.then(()=>(R(),t)):(R(),t)}return t.withImplementation=m,t.mockReturnThis=()=>t.mockImplementation(function(){return this}),t.mockReturnValue=c=>t.mockImplementation(()=>c),t.mockReturnValueOnce=c=>t.mockImplementationOnce(()=>c),t.mockResolvedValue=c=>t.mockImplementation(()=>Promise.resolve(c)),t.mockResolvedValueOnce=c=>t.mockImplementationOnce(()=>Promise.resolve(c)),t.mockRejectedValue=c=>t.mockImplementation(()=>Promise.reject(c)),t.mockRejectedValueOnce=c=>t.mockImplementationOnce(()=>Promise.reject(c)),Object.defineProperty(t,"mock",{get:()=>a}),r.willCall(u),de.add(t),t}var P=e=>fe(D({spy:e||(()=>{})},"spy"));beforeEach(()=>{global.window.fetch=P()});afterEach(()=>{global.window.fetch.mockReset()});var ge=async()=>{let e={json:()=>Promise.resolve(),status:200,ok:!0,headers:new Headers({"Content-Type":"application/json"})};return Promise.resolve(e)},U=async e=>{let{responseBody:t,status:s=200,headers:n={},delay:o}=e,r={json:()=>Promise.resolve(t),status:s,ok:s>=200&&s<=299,headers:new Headers({"Content-Type":"application/json",...n})};return o?new Promise(a=>setTimeout(()=>a(r),o)):Promise.resolve(r)},ye=e=>console.warn(`
|
|
2
2
|
${k.white.bold.bgRed("wrapito")} ${k.redBright.bold("cannot find any mock matching:")}
|
|
3
3
|
${k.greenBright(`URL: ${e.url}`)}
|
|
4
4
|
${k.greenBright(`METHOD: ${e.method?.toLowerCase()}`)}
|
|
5
5
|
${k.greenBright(`REQUEST BODY: ${e._bodyInit}`)}
|
|
6
|
-
`),
|
|
6
|
+
`),F=async(e,t,s)=>{let n=e.find(N(t));if(!n)return s&&ye(t),ge();let{multipleResponses:o}=n;if(!o)return U(n);let r=o.find(a=>!a.hasBeenReturned);if(!r){s&&Re(n);return}return r.hasBeenReturned=!0,U(r)},L=(e=[],t=!1)=>{global.window.fetch.mockImplementation((n,o)=>{if(typeof n=="string"){let a=new Request(n,o);return F(e,a,t)}return F(e,n,t)})},Re=e=>{let t=`\u{1F32F} Wrapito: Missing response in the multipleResponses array for path ${e.path} and method ${e.method}.`,s=k.greenBright(t);console.warn(s)};var C,f=e=>{C={...C,...e}},g=()=>({...C});beforeEach(()=>{global.fetch=P()});afterEach(()=>{global.fetch.mockReset()});var Xe=e=>(f({Component:e,responses:[],props:{},path:"",hasPath:!1,debug:process.env.npm_config_debugRequests==="true"}),x()),x=()=>{let e=xe();return{withProps:Me,withNetwork:Te,atPath:Ee,debugRequests:Pe,mount:Oe,...e}},we=e=>{let t=g(),s=[...t.responses,...e];f({...t,responses:s})},be=(e,t)=>(t({addResponses:we},e),x()),ke=(e,t)=>{let{extend:s}=d(),n=s[t];return{...e,[t]:(...o)=>be(o,n)}},xe=()=>{let{extend:e}=d();return Object.keys(e).reduce(ke,{})},Me=e=>{let t=g();return f({...t,props:e}),x()},Te=(e=[])=>{let t=g(),s=Array.isArray(e)?e:[e];return f({...t,responses:[...t.responses,...s]}),x()},Ee=(e,t)=>{let s=g();return f({...s,historyState:t,path:e,hasPath:!0}),x()},Pe=()=>{let e=g();return f({...e,debug:!0}),x()},Oe=()=>{let{portal:e,portals:t,changeRoute:s,history:n,mount:o}=d(),{Component:r,props:a,responses:l,path:i,hasPath:u,debug:p,historyState:m}=g(),c=r;return e&&G(e),t&&qe(t),u&&n&&(console.warn("wrapito WARNING: history is DEPRECATED. Pass a changeRoute function to the config instead."),console.warn("Read about changeRoute in: https://github.com/mercadona/wrapito#changeRoute"),n.push(i,m)),u&&!n&&s(i),L(l,p),o(_.createElement(c,{...a}))},G=e=>{if(document.getElementById(e))return;let t=document.createElement("div");t.setAttribute("id",e),t.setAttribute("data-testid",e),document.body.appendChild(t)},qe=e=>{e.forEach(t=>{G(t)})};import{diff as Ie}from"jest-diff";var H=(e,t)=>{let s=t?.host?`\u{1F32F} Wrapito: ${t?.host}${e} ain't got called`:`\u{1F32F} Wrapito: ${e} ain't got called`;return{pass:!1,message:()=>s}},V=(e,t,s)=>({pass:!1,message:()=>`\u{1F32F} Wrapito: ${e} is called ${s} times, you expected ${t} times`}),Q=(e,t)=>({pass:!1,message:()=>`\u{1F32F} Wrapito: Fetch method does not match, expected ${e} received ${t??"none"}`}),J=(e,t)=>{let s=t.map(n=>Ie(e,n)).join(`
|
|
7
7
|
|
|
8
8
|
`);return{pass:!1,message:()=>`\u{1F32F} Wrapito: Fetch body does not match.
|
|
9
|
-
${n}`}},
|
|
9
|
+
${s}`}},Y=(e,t)=>{let s=t.find(n=>e!==n);return{pass:!1,message:()=>`\u{1F32F} Wrapito: Host request does not match, expected ${e} received ${s}`}},z=()=>({pass:!1,message:()=>"\u{1F32F} Wrapito: Unable to find body."}),B=()=>({pass:!0,message:()=>"Test passing"}),K=(e,t)=>{let s=t?.host?`\u{1F32F} Wrapito: ${t.host}${e} is called`:`\u{1F32F} Wrapito: ${e} is called`;return{pass:!0,message:()=>s}};import ve from"deep-equal";var X=()=>{let e=d().defaultHost;return e?.includes("http")?e:"https://default.com"},We=(e="",t,s)=>t.includes(s)?t:e+t,O=e=>e instanceof Request,Ae=e=>O(e)?e.url:e,q=(e,t={method:"GET"})=>fetch.mock.calls.filter(([n])=>{let o=Ae(n),r=X(),a=new URL(o,r),l=We(t?.host,e,r),i=t?.host||r,u=new URL(l,i),p=a.pathname===u.pathname,m=a.search===u.search,c=a.host===u.host;return u.search?p&&m:t?.host?p&&c:p}),Z=e=>e.flat(1).filter(O).map(t=>t.method),ee=e=>e.flat(1).filter(O).map(t=>t._bodyInit?JSON.parse(t._bodyInit):{}),te=e=>e.flat(1).filter(O).map(t=>new URL(t.url,X()).hostname),se=(e,t)=>e&&!t.includes(e),ne=(e,t)=>t.map(n=>ve(e,n)).every(n=>n===!1),oe=(e,t)=>t.every(n=>n!==e),re=e=>e.length===0;var Ce=(e,t)=>{let s=q(e);if(re(s))return H(e);let n=Z(s),o=t?.method;if(se(o,n))return Q(o,n);let r=ee(s),a=t?.body;if(!a)return z();if(ne(a,r))return J(a,r);let l=te(s),i=t?.host;return i&&oe(i,l)?Y(i,l):B()},He=(e,t={method:"GET"})=>q(e,t).length?K(e,t):H(e,t),Be=(e,t,s={method:"GET"})=>{let n=q(e,s);return n.length!==t?V(e,t,n.length):B()},it={toHaveBeenFetched:He,toHaveBeenFetchedWith:Ce,toHaveBeenFetchedTimes:Be};export{it as assertions,Ne as configure,d as getConfig,it as matchers,Xe as wrap};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "wrapito",
|
|
3
|
-
"version": "13.0.0
|
|
3
|
+
"version": "13.0.0",
|
|
4
4
|
"description": "🌯 🌯 Wrap you tests so that you can test both behaviour and components with less effort.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -52,7 +52,7 @@
|
|
|
52
52
|
},
|
|
53
53
|
"homepage": "https://github.com/mercadona/wrapito#readme",
|
|
54
54
|
"dependencies": {
|
|
55
|
-
"chalk": "^
|
|
55
|
+
"chalk": "^5.6.2",
|
|
56
56
|
"deep-equal": "^2.2.3",
|
|
57
57
|
"jest-diff": "^29.7.0",
|
|
58
58
|
"object-hash": "^3.0.0",
|