Comments (8)
Hi, @ernestostifano. Thanks for reporting this!
I am frankly a bit lost in our existing cookie logic. It's been some years since it was originally written, and I'm trying to dig for the context behind every decision. I suspect that most of the things we have right now can be thrown away in v2.0.
Also, @chrisguttandin has referenced an issue in the cookie store package that may be helpful to me in uncovering the context:
I've thrown together a new implementation for the cookie store, the one that will respect path and also domain: mswjs/cookies#34. But I need to understand the logic we have in place now before making any changes to MSW.
In summary, there are two surfaces dealing with cookies:
- Response cookies. Whenever you have
Set-Cookie
on a mocked response, MSW writes those cookies todocument.cookie
. - Request cookie forwarding. When you make a request A that sets mocked cookies in the response, and then request B with a matching origin/path, MSW should include the relevant cookies in the request B.
- Response resolver
cookies
. This can be similar to no.2 but I suspect we gather these cookies just to present to the developer. Ideally, it should be the same as no.2.
from msw.
I've also discovered that JSDOM doesn't write cookies to document.cookie
if their path doesn't match the mocked location address. This is incorrect and is not how the browser behaves, after which all JSDOM behaviors must be modeled.
In the browser, you can write any cookies to document.cookie
. Then, only when making a request, given the permissive credentials
option, the browser will see which cookies from document.cookie
are relevant. You can provide this behavior by yourself by writing a cookie with a non-matching path and opening Application > Cookies in the devtools. You will see all document cookies.
In JSDOM, nothing will be written.
// jsdom
// Given location is the default location of
// http://localhost/
document.cookie = 'a=b; Path=/foo'
This makes automated testing on our end a bit problematic. We may consider switching the test suite of the cookie store to run in the actual browser. That library is still designed for Node.js as well to act as a singleton and a replacement layer for cookies in Node.js (where no document.cookie
exists).
from msw.
Opened a branch with a WIP fix/wip-cookie-path-match
.
from msw.
Released: v2.3.3 🎉
This has been released in v2.3.3!
Make sure to always update to the latest version (npm i msw@latest
) to get the newest features and bug fixes.
Predictable release automation by @ossjs/release.
from msw.
Hi @kettanaito, I just had the chance to upgrade and test this out, but unfortunately the issue is still there (2.3.3
and 2.3.5
). You can check the updated CodeSandbox.
I also noticed the following strange behaviour:
- Both cookies are visible in the cookies storage, but in the local storage, within
__msw-cookie-store__
, only the first cookie is stored.
from msw.
@kettanaito If I switch the two cookies (putting the one with the non-root path first), the behavior changes.
A) badCookie
is included in every API call:
http.get("/set-cookies", () => {
return HttpResponse.text("COOKIES SET", {
headers: [
[
"Set-Cookie",
"badCookie=badCookie; Path=/some/path; SameSite=None; Secure; Max-Age=20;",
],
[
"Set-Cookie",
"goodCookie=goodCookie; Path=/; SameSite=None; Secure; Max-Age=5;",
],
],
});
})
B) badCookie
is not included in any API call:
http.get("/set-cookies", () => {
return HttpResponse.text("COOKIES SET", {
headers: [
[
"Set-Cookie",
"goodCookie=goodCookie; Path=/; SameSite=None; Secure; Max-Age=5;",
],
[
"Set-Cookie",
"badCookie=badCookie; Path=/some/path; SameSite=None; Secure; Max-Age=20;",
],
],
});
})
In both cases, max-age
seems to get mixed between the two cookies. For example, with the B
configuration above, in the CodeSandbox, try the following:
- Click
Set Cookies
. - Wait a little over 5 seconds for
goodCookie
to expire (do not click anything in the meantime). - Click on
Get Cookies (/some)
. You will see no cookies as expected - Click on
Get Cookies (/some/path)
. You will seegoodCookie
as if it tookbadCookie
's place. It will then expire whenbadCookie
should have expired (~20s) (click onGet Cookies (/some/path)
again).
EDIT
I get different results from different combinations of the above: different order of cookies, different order of operations (setting/using cookies). Even using a cookie (making an API call) before the cookie expires and then using it again produces different results than just trying to use the cookie after it expired.
I also think there are alignment issues between the cookies storage and the local storage. For example, what if I clear the cookies storage and not the local storage? Or if I delete a specific cookie manually from the inspector.
from msw.
@kettanaito here is a similar example, emulating a real-world use-case: CodeSandbox.
-
Set the following cookies (
Set Cookies
button) (order matters, as per my previous comment):A)
- name:session-token
- valuesession-token-value
- path:/session/refresh-token
- max-age:200
B)
- name:access-token
- valueaccess-token-value
- path:/
- max-age:5
-
At this point, all API calls include both
access-token
andsession-token
, disregarding the paths (Echo Cookies
andRefresh Token
buttons). -
Wait for the second cookie to expire.
-
Make an API call to
/session/refresh-token
(Refresh Token
button). -
One would expect only the
session-token
cookie to be included, but oddlyaccess-token
is included instead, and then it disappears too (I think sometimes it doesn't), aftersession-token
expiration.
The behavior seems a bit broken. I think we should re-open this issue.
from msw.
@ernestostifano, thanks for diving deeper on this one!
I know you've done a lot already, but may I ask you to collaborate with me on turning these use cases you shown into integration tests for MSW? That would be the best way for the issue to move forward.
For example, what if I clear the cookies storage and not the local storage?
Afaik, there's no longer a concept of a cookie storage. Everything is stored either in-memory (for Node.js) or in local storage.
Or if I delete a specific cookie manually from the inspector.
Then I'd say you know what you are doing and should expect the behavior to change respectively.
I trust tough-cookie
is an extremely well-tested package, so if there are any issues, they are likely in how MSW uses it. That's where having automated tests to prove that would be great.
Test references
- https://github.com/mswjs/msw/blob/63b78315cdbe8435f9e6ec627022d67fa38a9703/test/browser/rest-api/request/request-cookies.test.ts
- https://github.com/mswjs/msw/blob/63b78315cdbe8435f9e6ec627022d67fa38a9703/test/browser/rest-api/response/response-cookies.test.ts
Important
Also please note there are two separate features related to cookies that may overlap. First, there's how MSW exposes request cookies in the cookies
argument of the response resolver. Second, is how MSW treats your mocked cookies from the Set-Cookie
mocked response header. There are flows when both are involved, and flows where only one is (the request cookies).
from msw.
Related Issues (20)
- List "graphql" as a peer dependency HOT 3
- Addition of Boundary() to setupServer() in react-native with help of AsyncLocalStorage alternative in RN HOT 1
- "TypeError: Failed to parse URL from [object Request]" with Vitest and Happy DOM HOT 1
- Cannot read properties of undefined (reading 'url') HOT 2
- v1: Add generic type to MockedRequest.passthrough() HOT 2
- Request by got + compression hangs forever HOT 1
- "Light Mode" in documentation HOT 1
- Document various URL patterns (colon, asterisk etc.) HOT 3
- Using the wildcard token to try to match random ports doesn't work if preceded by a colon HOT 1
- Error: Package subpath './browser' is not defined by "exports" (React/Vite) HOT 4
- Support option to use customized script to `server.listen()` for consistency with `worker.start()`
- Can't match the handler in tests in nuxt 3 app using useFetch composable
- Console log left over HOT 4
- Add cross platform path support to workerDirectory HOT 1
- not compatible with fetch-retry HOT 2
- MSW in fallback mode not intercepting and mocking image binary request HOT 5
- Document ListenOptions HOT 1
- Disabling "navigate" bypass causes TypeError: "Uncaught (in promise) TypeError: Failed to construct 'Request': Cannot construct a Request with a RequestInit whose mode member is set as 'navigate'."
- Requests with ".local" domain name take too long to resolve HOT 4
- Is it good practice to reference a video tutorial that is behind a pay wall? HOT 1
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from msw.