Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: implement cookie persistence using tough-cookie #2206

Merged
merged 10 commits into from
Jul 23, 2024
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -136,18 +136,19 @@
"@bundled-es-modules/cookie": "^2.0.0",
"@bundled-es-modules/statuses": "^1.0.1",
"@inquirer/confirm": "^3.0.0",
"@mswjs/cookies": "^1.1.0",
"@mswjs/interceptors": "^0.29.0",
"@open-draft/until": "^2.1.0",
"@types/cookie": "^0.6.0",
"@types/statuses": "^2.0.4",
"@types/tough-cookie": "^4.0.5",
"chalk": "^4.1.2",
"graphql": "^16.8.1",
"headers-polyfill": "^4.0.2",
"is-node-process": "^1.2.0",
"outvariant": "^1.4.2",
"path-to-regexp": "^6.2.0",
"strict-event-emitter": "^0.5.1",
"tough-cookie": "^4.1.4",
"type-fest": "^4.9.0",
"yargs": "^17.7.2"
},
Expand Down
31 changes: 13 additions & 18 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

48 changes: 32 additions & 16 deletions src/core/utils/HttpResponse/decorators.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import statuses from '@bundled-es-modules/statuses'
import type { HttpResponseInit } from '../../HttpResponse'
import { Headers as HeadersPolyfill } from 'headers-polyfill'
import type { HttpResponseInit } from '../../HttpResponse'

const { message } = statuses

export const kSetCookie = Symbol('kSetCookie')

export interface HttpResponseDecoratedInit extends HttpResponseInit {
status: number
statusText: string
Expand Down Expand Up @@ -38,21 +40,35 @@ export function decorateResponse(
})
}

// Cookie forwarding is only relevant in the browser.
if (typeof document !== 'undefined') {
// Write the mocked response cookies to the document.
// Use `headers-polyfill` to get the Set-Cookie header value correctly.
// This is an alternative until TypeScript 5.2
// and Node.js v20 become the minimum supported version
// and getSetCookie in Headers can be used directly.
const responseCookies = HeadersPolyfill.prototype.getSetCookie.call(
init.headers,
)

for (const cookieString of responseCookies) {
// No need to parse the cookie headers because it's defined
// as the valid cookie string to begin with.
document.cookie = cookieString
const responseCookies = init.headers.get('set-cookie')

if (responseCookies) {
// Record the raw "Set-Cookie" response header provided
// in the HeadersInit. This is later used to store these cookies
// in cookie jar and return the right cookies in the "cookies"
// response resolver argument.
Object.defineProperty(response, kSetCookie, {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would love to store the cookies here directly but tough-cookie needs a URL to associate the cookies with. response.url is not always defined (this may be easier to solve). I don't like introducing a symbol just to pass the data around.

value: responseCookies,
enumerable: false,
writable: false,
})

// Cookie forwarding is only relevant in the browser.
if (typeof document !== 'undefined') {
// Write the mocked response cookies to the document.
// Use `headers-polyfill` to get the Set-Cookie header value correctly.
// This is an alternative until TypeScript 5.2
// and Node.js v20 become the minimum supported version
// and getSetCookie in Headers can be used directly.
const responseCookiePairs = HeadersPolyfill.prototype.getSetCookie.call(
init.headers,
)

for (const cookieString of responseCookiePairs) {
// No need to parse the cookie headers because it's defined
// as the valid cookie string to begin with.
document.cookie = cookieString
}
}
}

Expand Down
5 changes: 5 additions & 0 deletions src/core/utils/cookieStore.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import * as toughCookie from 'tough-cookie'

const { CookieJar } = toughCookie

export const cookieStore = new CookieJar()
6 changes: 3 additions & 3 deletions src/core/utils/handleRequest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { LifeCycleEventsMap, SharedOptions } from '../sharedOptions'
import { RequiredDeep } from '../typeUtils'
import { HandlersExecutionResult, executeHandlers } from './executeHandlers'
import { onUnhandledRequest } from './request/onUnhandledRequest'
import { readResponseCookies } from './request/readResponseCookies'
import { storeResponseCookies } from './request/storeResponseCookies'

export interface HandleRequestOptions {
/**
Expand Down Expand Up @@ -110,8 +110,8 @@ export async function handleRequest(
return
}

// Store all the received response cookies in the virtual cookie store.
readResponseCookies(request, response)
// Store all the received response cookies in the cookie jar.
storeResponseCookies(request, response)

emitter.emit('request:match', { request, requestId })

Expand Down
29 changes: 0 additions & 29 deletions src/core/utils/request/getRequestCookies.node.test.ts

This file was deleted.

64 changes: 0 additions & 64 deletions src/core/utils/request/getRequestCookies.test.ts

This file was deleted.

Loading
Loading