Setting cookies in context does not appear to be working
I am trying to set and test cookies. I have a simple test that loads a page where the only content is the value of a cookie. In the browser, it works even with server-side rendering, but in the test, I am not able to get the cookie value to appear.
I have tried using the Playwright context, hoping that the Nuxt test utils would recognize it somehow. I have also tried using the storageState.cookies to set the cookies, but the test always fails.
Can anyone point me in the right direction?
Example using context:
it('renders cookie value', async () => {
await createBrowser()
const browser = await getBrowser()
const context = await browser.newContext()
await context.addCookies([
{
name: 'test',
value: 'test cookie',
path: '/',
domain: 'localhost'
}
])
const page = await createPage('/')
expect(await page.innerHTML('body')).toContain('test cookie')
await context.close()
await browser.close()
})
Example using storageState.cookies
it('renders cookie value', async () => {
const page = await createPage('/', {
storageState: {
cookies: [
{
name: 'test',
value: 'test cookie',
path: '/',
domain: 'localhost',
expires: -1,
httpOnly: false,
secure: false,
sameSite: 'None'
}
],
origins: []
}
})
expect(await page.innerHTML('body')).toContain('test cookie')
})
You can pass a options object to createPage which includes a way to configure cookies (via storageState.cookies).
That's what I tried, in the second example above, test still fails. Is there any documentation for this? Thanks
Ah, serves me right for not reading to end. ๐
I would guess the issue is that you haven't specified the port in the domain. You can get a full URL from url('/') and extract the domain from that.
Will try that, thanks!
Unfortunately still no success. this should set the domain as localhost:PORT but test still fails. No idea why
it('renders test cookie value', async () => {
const page = await createPage('/', {
storageState: {
cookies: [
{
name: 'test',
value: 'test-cookie',
path: '/',
domain: new URL(url('/')).host,
expires: -1,
httpOnly: false,
secure: false,
sameSite: 'None'
}
],
origins: []
}
})
expect(await page.innerHTML('body')).toContain('test-cookie')
})
This is the frontend, but again, everything works in the browser when I set the cookie manually and refresh page.
<script setup lang="ts">
import { useCookie } from '#imports'
const test = useCookie<string>('test')
</script>
<template>
<div>
{{ test }}
</div>
</template>
I tried to put together a reproduction example on StackBlitz, but they may be blocking cookies or something because I could not make it work.
If you have any ideas, please let me know. I'm starting to get kind of desperate ๐ .
Reopened - I'll try to have a look later.
Wait, just to be clear - you are using Nuxt 3, right? (This is an old repo we should probably close.)
yes, I'm using Nuxt3.
Running into the same issue unfortunately. This makes it impossible to test with cookies. Would greatly appreciate if this get fixed! ๐
Yep same issue here, setting a cookie using
const page = await createPage("/", {
// @ts-ignore
storageState: {
cookies: [
// @ts-ignore
{
name: "accessToken",
value: "test",
domain: "localhost",
path: "/",
},
],
},
})
But when useCookie("accessToken") is used in my middleware, it shows it as undefined.
Yep same issue here, setting a cookie using
const page = await createPage("/", { // @ts-ignore storageState: { cookies: [ // @ts-ignore { name: "accessToken", value: "test", domain: "localhost", path: "/", }, ], }, })But when
useCookie("accessToken")is used in my middleware, it shows it as undefined.
It is working for me. You are doing it wrong. The setup in test-utils use 127.0.0.1 as domain. My suggestion is to use addCookies with name and value only. Then debug page.context().cookies() and copy paste the values. One wrong value makes it appear as it is not working. But it is.
Yep same issue here, setting a cookie using
const page = await createPage("/", { // @ts-ignore storageState: { cookies: [ // @ts-ignore { name: "accessToken", value: "test", domain: "localhost", path: "/", }, ], }, })But when
useCookie("accessToken")is used in my middleware, it shows it as undefined.It is working for me. You are doing it wrong. The setup in test-utils use 127.0.0.1 as domain. My suggestion is to use addCookies with name and value only. Then debug page.context().cookies() and copy paste the values. One wrong value makes it appear as it is not working. But it is.
Thanks for the suggestions, but this still isn't working for me. Not including a domain/path I get an error:
If I set domain to 127.0.0.1, still the same issue. Cookie is not set :(
Unfortunately still no success. this should set the domain as
localhost:PORTbut test still fails. No idea whyit('renders test cookie value', async () => { const page = await createPage('/', { storageState: { cookies: [ { name: 'test', value: 'test-cookie', path: '/', domain: new URL(url('/')).host, expires: -1, httpOnly: false, secure: false, sameSite: 'None' } ], origins: [] } }) expect(await page.innerHTML('body')).toContain('test-cookie') })This is the frontend, but again, everything works in the browser when I set the cookie manually and refresh page.
<script setup lang="ts"> import { useCookie } from '#imports' const test = useCookie<string>('test') </script> <template> <div> {{ test }} </div> </template>I tried to put together a reproduction example on StackBlitz, but they may be blocking cookies or something because I could not make it work.
If you have any ideas, please let me know. I'm starting to get kind of desperate ๐ .
I think for domain you should pass the hostname instead of host like this: domain: new URL(url('/')).hostname as host also includes the port but for the cookie it should only be the domain excluding the port.
Investigation:
(TL;DR: at the example at the end works)
In the original bug report:
const context = await browser.newContext()
await context.addCookies(...)
const page = await createPage('/')
is used, which won't work, because this will result in two isolated browser contexts:
- The one you create in line 1 + 2 where you set the actual cookie
- The one which
await createPagecreates internally (it uses browser.newPage() under the hood which is a helper which creates a browser context and a page on it).
If you do:
const page = await createPage('/')
await page.context().addCookies(...)
it's too late, because createPage() internally already has navigated. So you could do another await page.reload() afterwards, which is not ideal and only works in some scenarios (cookie overrides?).
I saw above that storageState works sometimes, this is correct when passing it to createPage(). storageState is not really a developer experience optimised API (it was intended to be generated via page.context().storageState()), hence either declaring a lot of defaults or // @ts-ignore is needed. The following example works tho if someone looks for workarounds:
const page = await createPage('/', {
storageState: {
cookies: [
{
name: 'counter',
value: '1337',
domain: new URL(url('/')).hostname,
path: '/',
expires: -1,
httpOnly: false,
secure: false,
sameSite: 'Lax',
}
],
origins: []
}
})
expect(await page.innerHTML('body')).toContain('Counter: 1337')
This will only create 1 browser context and set the cookie when creating the page before the navigation.
Really good explanation @mxschmitt.
I will just add an example here.
import {setup} from '@nuxt/test-utils/e2e'
import {fileURLToPath} from 'node:url'
import type {Page} from "playwright-core";
import {afterEach, assert, beforeEach, describe, expect, it} from 'vitest'
import {createPage, url} from "@nuxt/test-utils";
import {fail} from "node:assert";
describe('Cookies Test Suite', async () => {
await setup({
rootDir: fileURLToPath(new URL('./fixtures/basic', import.meta.url))
})
describe('With cookies in storage', () => {
let page: Page | undefined = undefined;
beforeEach(async () => {
page = await createPage('/', {
storageState: {
cookies: [
{
path: "/",
domain: "127.0.0.1",
expires: -1,
name: "my_cookie",
secure: false,
value: "12345",
sameSite: "Lax",
httpOnly: false
}
],
origins: []
},
})
})
afterEach(async () => {
if (page) {
await page.context().clearCookies();
await page.close();
}
})
it('should have cookie when page is created', async () => {
if (page === undefined) assert.fail("Page never became an instance of Page")
const cookies = await (page as Page).context().cookies();
expect(cookies[0].name).toEqual("my_cookie");
expect(cookies[0].value).toEqual("12345");
});
})
describe("Without Cookies in storage", () => {
let page: Page | undefined = undefined;
beforeEach(async () => {
page = await createPage('/');
})
afterEach(async () => {
if (page) {
await page.context().clearCookies();
await page.close();
}
})
it('should add cookie later on', async () => {
if (!page) return fail("Page is not an instance of Page");
await page.context().addCookies([
{
name: "my_cookie2",
value: "12345",
url: url("/")
}
]);
const cookies = await page.context().cookies();
expect(cookies[0].name).toEqual("my_cookie2");
expect(cookies[0].value).toEqual("12345");
});
})
})
I wanted to add it in the stackblitz but im not sure how to do it.
My issue was that I was not set up correctly with test-utils - I had only added the npm package and not added it as a module, or set vitest's environment to Nuxt - just in case someone makes the same mistake as me. The docs for getting set up with testing are actually there now which helped a lot, when I had set this up originally they were not there.