From 5774b631a53260ca3493b44d813a1ab3d6f59e13 Mon Sep 17 00:00:00 2001 From: Artem Zakharchenko Date: Fri, 19 Jul 2024 16:13:36 +0200 Subject: [PATCH] test: add request cookies tests --- .../rest-api/cookies-http-only.mocks.ts | 29 --- .../rest-api/cookies-http-only.test.ts | 17 -- .../rest-api/cookies-inheritance.mocks.ts | 27 --- .../rest-api/cookies-inheritance.test.ts | 21 -- .../browser/rest-api/cookies-request.mocks.ts | 12 -- test/browser/rest-api/cookies-request.test.ts | 85 -------- .../rest-api/request/request-cookies.mocks.ts | 17 ++ .../rest-api/request/request-cookies.test.ts | 199 ++++++++++++++++++ 8 files changed, 216 insertions(+), 191 deletions(-) delete mode 100644 test/browser/rest-api/cookies-http-only.mocks.ts delete mode 100644 test/browser/rest-api/cookies-http-only.test.ts delete mode 100644 test/browser/rest-api/cookies-inheritance.mocks.ts delete mode 100644 test/browser/rest-api/cookies-inheritance.test.ts delete mode 100644 test/browser/rest-api/cookies-request.mocks.ts delete mode 100644 test/browser/rest-api/cookies-request.test.ts create mode 100644 test/browser/rest-api/request/request-cookies.mocks.ts create mode 100644 test/browser/rest-api/request/request-cookies.test.ts diff --git a/test/browser/rest-api/cookies-http-only.mocks.ts b/test/browser/rest-api/cookies-http-only.mocks.ts deleted file mode 100644 index f9ac6401b..000000000 --- a/test/browser/rest-api/cookies-http-only.mocks.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { http, HttpResponse } from 'msw' -import { setupWorker } from 'msw/browser' - -const worker = setupWorker( - http.post('/login', () => { - return HttpResponse.text(null, { - headers: { - 'Set-Cookie': 'authToken=abc-123; HttpOnly', - }, - }) - }), - http.get('/user', ({ cookies }) => { - if (cookies.authToken == null) { - throw HttpResponse.json( - { - error: 'Not authenticated', - }, - { status: 403 }, - ) - } - - return HttpResponse.json({ - firstName: 'John', - lastName: 'Maverick', - }) - }), -) - -worker.start() diff --git a/test/browser/rest-api/cookies-http-only.test.ts b/test/browser/rest-api/cookies-http-only.test.ts deleted file mode 100644 index 3aa5fd9b1..000000000 --- a/test/browser/rest-api/cookies-http-only.test.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { test, expect } from '../playwright.extend' - -test('inherits cookies set on a preceeding request', async ({ - loadExample, - fetch, -}) => { - await loadExample(require.resolve('./cookies-http-only.mocks.ts')) - - await fetch('/login', { method: 'POST' }) - const response = await fetch('/user') - - await expect(response.json()).resolves.toEqual({ - firstName: 'John', - lastName: 'Maverick', - }) - expect(response.status()).toBe(200) -}) diff --git a/test/browser/rest-api/cookies-inheritance.mocks.ts b/test/browser/rest-api/cookies-inheritance.mocks.ts deleted file mode 100644 index 674ba07c1..000000000 --- a/test/browser/rest-api/cookies-inheritance.mocks.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { http, HttpResponse } from 'msw' -import { setupWorker } from 'msw/browser' - -const worker = setupWorker( - http.post('/login', () => { - return HttpResponse.text(null, { - headers: { - 'Set-Cookie': 'authToken=abc-123', - }, - }) - }), - http.get('/user', ({ cookies }) => { - if (cookies.authToken == null) { - return HttpResponse.json( - { error: 'Auth token not found' }, - { status: 403 }, - ) - } - - return HttpResponse.json({ - firstName: 'John', - lastName: 'Maverick', - }) - }), -) - -worker.start() diff --git a/test/browser/rest-api/cookies-inheritance.test.ts b/test/browser/rest-api/cookies-inheritance.test.ts deleted file mode 100644 index 519d0444d..000000000 --- a/test/browser/rest-api/cookies-inheritance.test.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { test, expect } from '../playwright.extend' - -test('inherits cookies set on a preceeding request', async ({ - loadExample, - fetch, -}) => { - await loadExample(require.resolve('./cookies-inheritance.mocks.ts')) - - const res = await fetch('/login', { method: 'POST' }).then(() => { - return fetch('/user') - }) - - const status = res.status() - const json = await res.json() - - expect(status).toBe(200) - expect(json).toEqual({ - firstName: 'John', - lastName: 'Maverick', - }) -}) diff --git a/test/browser/rest-api/cookies-request.mocks.ts b/test/browser/rest-api/cookies-request.mocks.ts deleted file mode 100644 index 18b89e3e2..000000000 --- a/test/browser/rest-api/cookies-request.mocks.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { http, HttpResponse } from 'msw' -import { setupWorker } from 'msw/browser' - -const worker = setupWorker( - // Use wildcard so that we intercept any "GET /user" requests - // regardless of the origin, and can assert "same-origin" credentials. - http.get('*/user', ({ cookies }) => { - return HttpResponse.json({ cookies }) - }), -) - -worker.start() diff --git a/test/browser/rest-api/cookies-request.test.ts b/test/browser/rest-api/cookies-request.test.ts deleted file mode 100644 index 8b641d39f..000000000 --- a/test/browser/rest-api/cookies-request.test.ts +++ /dev/null @@ -1,85 +0,0 @@ -import { Page } from '@playwright/test' -import { test, expect } from '../playwright.extend' - -const EXAMPLE_PATH = require.resolve('./cookies-request.mocks.ts') - -async function bakeCookies(page: Page) { - await page.evaluate(() => { - document.cookie = 'auth-token=abc-123;' - document.cookie = 'custom-cookie=yes;' - }) -} - -test('exposes all document cookies if request "credentials" is "include"', async ({ - loadExample, - fetch, - page, -}) => { - await loadExample(EXAMPLE_PATH) - await bakeCookies(page) - - const response = await fetch('/user', { - credentials: 'include', - }) - - await expect(response.json()).resolves.toEqual({ - cookies: { - 'auth-token': 'abc-123', - 'custom-cookie': 'yes', - }, - }) -}) - -test('exposes document cookies if request "credentials" is "same-origin" for same-origin request', async ({ - loadExample, - fetch, - page, -}) => { - await loadExample(EXAMPLE_PATH) - await bakeCookies(page) - - const response = await fetch('/user', { - credentials: 'same-origin', - }) - - await expect(response.json()).resolves.toEqual({ - cookies: { - 'auth-token': 'abc-123', - 'custom-cookie': 'yes', - }, - }) -}) - -test('exposes no cookies if request "credentials" is "same-origin" for a cross-origin request', async ({ - loadExample, - fetch, - page, -}) => { - await loadExample(EXAMPLE_PATH) - await bakeCookies(page) - - const response = await fetch('https://test.mswjs.io/user', { - credentials: 'same-origin', - }) - - await expect(response.json()).resolves.toEqual({ - cookies: {}, - }) -}) - -test('exposes no cookies if request "credentials" is "omit"', async ({ - loadExample, - fetch, - page, -}) => { - await loadExample(EXAMPLE_PATH) - await bakeCookies(page) - - const response = await fetch('/user', { - credentials: 'omit', - }) - - await expect(response.json()).resolves.toEqual({ - cookies: {}, - }) -}) diff --git a/test/browser/rest-api/request/request-cookies.mocks.ts b/test/browser/rest-api/request/request-cookies.mocks.ts new file mode 100644 index 000000000..59856827f --- /dev/null +++ b/test/browser/rest-api/request/request-cookies.mocks.ts @@ -0,0 +1,17 @@ +import { http, HttpResponse } from 'msw' +import { setupWorker } from 'msw/browser' + +const worker = setupWorker( + http.get('*/cookies', ({ cookies }) => { + return HttpResponse.json(cookies) + }), + http.post('/set-cookies', async ({ request }) => { + return new HttpResponse(null, { + headers: { + 'Set-Cookie': await request.clone().text(), + }, + }) + }), +) + +worker.start() diff --git a/test/browser/rest-api/request/request-cookies.test.ts b/test/browser/rest-api/request/request-cookies.test.ts new file mode 100644 index 000000000..a617ef901 --- /dev/null +++ b/test/browser/rest-api/request/request-cookies.test.ts @@ -0,0 +1,199 @@ +import type { Page } from '@playwright/test' +import { test, expect } from '../../playwright.extend' + +async function bakeCookies(page: Page, cookies: Array) { + await page.evaluate((cookies) => { + cookies.forEach((cookie) => { + document.cookie = cookie + }) + }, cookies) +} + +test('returns empty object if document has no cookies', async ({ + loadExample, + fetch, + page, +}) => { + await loadExample(require.resolve('./request-cookies.mocks.ts')) + const response = await fetch('/cookies') + const documentCookies = await page.evaluate(() => document.cookie) + + expect(response.status()).toBe(200) + await expect(response.json()).resolves.toEqual({}) + expect(documentCookies).toBe('') +}) + +test('returns empty object for request with "credentials: omit"', async ({ + loadExample, + fetch, + page, +}) => { + await loadExample(require.resolve('./request-cookies.mocks.ts')) + await bakeCookies(page, ['documentCookie=value']) + const response = await fetch('/cookies', { credentials: 'omit' }) + + expect(response.status()).toBe(200) + await expect(response.json()).resolves.toEqual({}) +}) + +test('returns empty object for cross-origin request with "credentials: same-origin"', async ({ + loadExample, + fetch, + page, +}) => { + await loadExample(require.resolve('./request-cookies.mocks.ts')) + await bakeCookies(page, ['documentCookie=value']) + const response = await fetch('https://example.com/cookies', { + credentials: 'same-origin', + }) + + expect(response.status()).toBe(200) + await expect(response.json()).resolves.toEqual({}) +}) + +test('returns cookies for same-origin request with "credentials: same-origin"', async ({ + loadExample, + fetch, + page, +}) => { + await loadExample(require.resolve('./request-cookies.mocks.ts')) + await bakeCookies(page, ['documentCookie=value']) + const response = await fetch('/cookies', { + credentials: 'same-origin', + }) + + expect(response.status()).toBe(200) + await expect(response.json()).resolves.toEqual({ + documentCookie: 'value', + }) +}) + +test('returns cookies for same-origin request with "credentials: include"', async ({ + loadExample, + fetch, + page, +}) => { + await loadExample(require.resolve('./request-cookies.mocks.ts')) + await bakeCookies(page, ['firstCookie=value', 'secondCookie=anotherValue']) + const response = await fetch('/cookies', { + credentials: 'include', + }) + + expect(response.status()).toBe(200) + await expect(response.json()).resolves.toEqual({ + firstCookie: 'value', + secondCookie: 'anotherValue', + }) +}) + +test('returns cookies for cross-origin request with "credentials: include"', async ({ + loadExample, + fetch, + page, +}) => { + await loadExample(require.resolve('./request-cookies.mocks.ts')) + await bakeCookies(page, ['documentCookie=value']) + const response = await fetch('https://example.com/cookies', { + credentials: 'include', + }) + + expect(response.status()).toBe(200) + await expect(response.json()).resolves.toEqual({ + documentCookie: 'value', + }) +}) + +test('inherits mocked cookies', async ({ loadExample, fetch, page }) => { + await loadExample(require.resolve('./request-cookies.mocks.ts')) + await bakeCookies(page, ['documentCookie=value']) + + // Make a request that sends mocked cookies. + await fetch('/set-cookies', { + method: 'POST', + body: 'mockedCookie=mockedValue', + }) + const response = await fetch('/cookies', { + credentials: 'include', + }) + + expect(response.status()).toBe(200) + await expect(response.json()).resolves.toEqual({ + documentCookie: 'value', + mockedCookie: 'mockedValue', + }) +}) + +test('inherits mocked cookies after page reload', async ({ + loadExample, + fetch, + page, +}) => { + await loadExample(require.resolve('./request-cookies.mocks.ts')) + await bakeCookies(page, ['documentCookie=value']) + + await fetch('/set-cookies', { + method: 'POST', + body: 'mockedCookie=mockedValue', + }) + // Reload the page to ensure that the mocked cookies persist. + await page.reload({ waitUntil: 'networkidle' }) + + const response = await fetch('/cookies', { + credentials: 'include', + }) + + expect(response.status()).toBe(200) + await expect(response.json()).resolves.toEqual({ + documentCookie: 'value', + mockedCookie: 'mockedValue', + }) +}) + +test('inherits mocked "HttpOnly" cookies', async ({ + loadExample, + fetch, + page, +}) => { + await loadExample(require.resolve('./request-cookies.mocks.ts')) + await bakeCookies(page, ['documentCookie=value']) + + await fetch('/set-cookies', { + method: 'POST', + body: 'mockedCookie=mockedValue; HttpOnly', + }) + await page.reload({ waitUntil: 'networkidle' }) + + const response = await fetch('/cookies', { + credentials: 'include', + }) + + expect(response.status()).toBe(200) + await expect(response.json()).resolves.toEqual({ + documentCookie: 'value', + mockedCookie: 'mockedValue', + }) +}) + +test('respects cookie "Path" when exposing cookies', async ({ + loadExample, + fetch, + page, +}) => { + const { compilation } = await loadExample( + require.resolve('./request-cookies.mocks.ts'), + ) + await bakeCookies(page, [ + `documentCookie=value; Path=${compilation.previewRoute}`, + ]) + + const nonMatchingResponse = await fetch('/cookies') + // Must not return cookies for the request under a different path. + await expect(nonMatchingResponse.json()).resolves.toEqual({}) + + const matchingResponse = await fetch( + new URL('./cookies', compilation.previewUrl).href, + ) + await expect(matchingResponse.json()).resolves.toEqual({ + documentCookie: 'value', + }) +})