diff --git a/docs/src/api/class-browsercontext.md b/docs/src/api/class-browsercontext.md index 275114d1e8..aa58a05c6d 100644 --- a/docs/src/api/class-browsercontext.md +++ b/docs/src/api/class-browsercontext.md @@ -1011,19 +1011,25 @@ Creates a new page in the browser context. Returns all open pages in the context. ## async method: BrowserContext.removeCookies -* since: v1.42 +* since: v1.43 Removes cookies from context. **Usage** ```js -await browserContext.removeCookies([cookieName1, cookieName2]); +await browserContext.removeCookies({ name: 'session-id' }); +await browserContext.removeCookies({ domain: 'my-origin.com' }); +await browserContext.removeCookies({ path: '/api/v1' }); +await browserContext.removeCookies({ name: 'session-id', domain: 'my-origin.com' }); ``` -### param: BrowserContext.removeCookies.cookieNames -* since: v1.42 -- `cookieNames` <[string]|[Array]<[string]>> - list of cookie names to remove from context +### param: BrowserContext.removeCookies.cookies +* since: v1.43 +- `cookies` <[Object]> + - `name` ?<[string]> + - `domain` ?<[string]> + - `path` ?<[string]> ## property: BrowserContext.request * since: v1.16 diff --git a/packages/playwright-core/src/client/browserContext.ts b/packages/playwright-core/src/client/browserContext.ts index 3d37054415..47fbc0317e 100644 --- a/packages/playwright-core/src/client/browserContext.ts +++ b/packages/playwright-core/src/client/browserContext.ts @@ -269,11 +269,8 @@ export class BrowserContext extends ChannelOwner await this._channel.clearCookies(); } - async removeCookies(cookieNames: string | string[]): Promise { - if (typeof cookieNames === 'string') - cookieNames = [cookieNames]; - - await this._channel.removeCookies({ cookieNames: cookieNames as string[] }); + async removeCookies(cookies: network.RemoveNetworkCookieParam): Promise { + await this._channel.removeCookies({ cookies }); } async grantPermissions(permissions: string[], options?: { origin?: string }): Promise { diff --git a/packages/playwright-core/src/client/network.ts b/packages/playwright-core/src/client/network.ts index 6f35fdbc70..75cda8d207 100644 --- a/packages/playwright-core/src/client/network.ts +++ b/packages/playwright-core/src/client/network.ts @@ -58,6 +58,12 @@ export type SetNetworkCookieParam = { sameSite?: 'Strict' | 'Lax' | 'None' }; +export type RemoveNetworkCookieParam = { + name?: string, + domain?: string, + path?: string, +}; + type SerializedFallbackOverrides = { url?: string; method?: string; diff --git a/packages/playwright-core/src/protocol/validator.ts b/packages/playwright-core/src/protocol/validator.ts index d017147867..32019c67c1 100644 --- a/packages/playwright-core/src/protocol/validator.ts +++ b/packages/playwright-core/src/protocol/validator.ts @@ -829,7 +829,11 @@ scheme.BrowserContextAddInitScriptResult = tOptional(tObject({})); scheme.BrowserContextClearCookiesParams = tOptional(tObject({})); scheme.BrowserContextClearCookiesResult = tOptional(tObject({})); scheme.BrowserContextRemoveCookiesParams = tObject({ - cookieNames: tArray(tString), + cookies: tObject({ + name: tOptional(tString), + domain: tOptional(tString), + path: tOptional(tString), + }), }); scheme.BrowserContextRemoveCookiesResult = tOptional(tObject({})); scheme.BrowserContextClearPermissionsParams = tOptional(tObject({})); diff --git a/packages/playwright-core/src/server/browserContext.ts b/packages/playwright-core/src/server/browserContext.ts index abf604c668..97cd078537 100644 --- a/packages/playwright-core/src/server/browserContext.ts +++ b/packages/playwright-core/src/server/browserContext.ts @@ -276,13 +276,19 @@ export abstract class BrowserContext extends SdkObject { return await this.doGetCookies(urls as string[]); } - async removeCookies(cookieNames: string | string[]): Promise { + async removeCookies(cookies: {name?: string, domain?: string, path?: string}): Promise { const setCookies = await this.cookies(); - const newCookiesSet = setCookies.filter(c => !cookieNames.includes(c.name)); + if (!cookies.name && !cookies.domain && !cookies.path) return; + + const filteredCookies = setCookies.filter(cookie => { + return !((!cookies.name || cookies.name === cookie.name) && + (!cookies.domain || cookies.domain === cookie.domain) && + (!cookies.path || cookies.path === cookie.path)); + }); await this.clearCookies(); - await this.addCookies(newCookiesSet); + await this.addCookies(filteredCookies); } setHTTPCredentials(httpCredentials?: types.Credentials): Promise { diff --git a/packages/playwright-core/src/server/dispatchers/browserContextDispatcher.ts b/packages/playwright-core/src/server/dispatchers/browserContextDispatcher.ts index 73f03f12d9..34edf24cfb 100644 --- a/packages/playwright-core/src/server/dispatchers/browserContextDispatcher.ts +++ b/packages/playwright-core/src/server/dispatchers/browserContextDispatcher.ts @@ -225,7 +225,7 @@ export class BrowserContextDispatcher extends Dispatcher { - await this._context.removeCookies(params.cookieNames); + await this._context.removeCookies(params.cookies); } async grantPermissions(params: channels.BrowserContextGrantPermissionsParams): Promise { diff --git a/packages/playwright-core/types/types.d.ts b/packages/playwright-core/types/types.d.ts index f9841c3822..b04f16e603 100644 --- a/packages/playwright-core/types/types.d.ts +++ b/packages/playwright-core/types/types.d.ts @@ -8444,12 +8444,21 @@ export interface BrowserContext { * **Usage** * * ```js - * await browserContext.removeCookies([cookieName1, cookieName2]); + * await browserContext.removeCookies({ name: 'session-id' }); + * await browserContext.removeCookies({ domain: 'my-origin.com' }); + * await browserContext.removeCookies({ path: '/api/v1' }); + * await browserContext.removeCookies({ name: 'session-id', domain: 'my-origin.com' }); * ``` * - * @param cookieNames + * @param cookies */ - removeCookies(cookieNames: string|ReadonlyArray): Promise; + removeCookies(cookies: { + name?: string; + + domain?: string; + + path?: string; + }): Promise; /** * Routing provides the capability to modify network requests that are made by any page in the browser context. Once diff --git a/packages/protocol/src/channels.ts b/packages/protocol/src/channels.ts index b8df94ddc3..d7e5bbd1b0 100644 --- a/packages/protocol/src/channels.ts +++ b/packages/protocol/src/channels.ts @@ -1525,7 +1525,11 @@ export type BrowserContextClearCookiesParams = {}; export type BrowserContextClearCookiesOptions = {}; export type BrowserContextClearCookiesResult = void; export type BrowserContextRemoveCookiesParams = { - cookieNames: string[], + cookies: { + name?: string, + domain?: string, + path?: string, + }, }; export type BrowserContextRemoveCookiesOptions = { diff --git a/packages/protocol/src/protocol.yml b/packages/protocol/src/protocol.yml index b47f5692ee..b22c1269dd 100644 --- a/packages/protocol/src/protocol.yml +++ b/packages/protocol/src/protocol.yml @@ -1034,9 +1034,12 @@ BrowserContext: removeCookies: parameters: - cookieNames: - type: array - items: string + cookies: + type: object + properties: + name: string? + domain: string? + path: string? clearPermissions: diff --git a/tests/library/browsercontext-remove-cookies.spec.ts b/tests/library/browsercontext-remove-cookies.spec.ts index 72b8b1c8ef..9a5f295c3b 100644 --- a/tests/library/browsercontext-remove-cookies.spec.ts +++ b/tests/library/browsercontext-remove-cookies.spec.ts @@ -17,31 +17,214 @@ import { contextTest as it, expect } from '../config/browserTest'; -it('should remove cookies', async ({ context, page, server }) => { - await page.goto(server.EMPTY_PAGE); +it('should remove cookies by name', async ({ context, page, server }) => { await context.addCookies([{ - url: server.EMPTY_PAGE, name: 'cookie1', - value: '1' + value: '1', + domain: 'www.example.com', + path: '/', }, { - url: server.EMPTY_PAGE, name: 'cookie2', - value: '2' - }, - { - url: server.EMPTY_PAGE, - name: 'cookie3', - value: '3' - }]); - expect(await page.evaluate('document.cookie')).toBe('cookie1=1; cookie2=2; cookie3=3'); - await context.removeCookies(['cookie1', 'cookie2']); - expect(await page.evaluate('document.cookie')).toBe('cookie3=3'); - await page.reload(); - expect(await page.evaluate('document.cookie')).toBe('cookie3=3'); - await context.removeCookies('cookie3'); - expect(await page.evaluate('document.cookie')).toBe(''); - await page.reload(); - expect(await page.evaluate('document.cookie')).toBe(''); + value: '2', + domain: 'www.example.com', + path: '/', + } + ]); + await page.goto('https://www.example.com'); + expect(await page.evaluate('document.cookie')).toBe('cookie1=1; cookie2=2'); + await context.removeCookies({ name: 'cookie1' }); + expect(await page.evaluate('document.cookie')).toBe('cookie2=2'); }); +it('should remove cookies by domain', async ({ context, page, server }) => { + await context.addCookies([{ + name: 'cookie1', + value: '1', + domain: 'www.example.com', + path: '/', + }, + { + name: 'cookie2', + value: '2', + domain: 'www.example.org', + path: '/', + } + ]); + await page.goto('https://www.example.com'); + expect(await page.evaluate('document.cookie')).toBe('cookie1=1'); + await page.goto('https://www.example.org'); + expect(await page.evaluate('document.cookie')).toBe('cookie2=2'); + await context.removeCookies({ domain: 'www.example.org' }); + expect(await page.evaluate('document.cookie')).toBe(''); + await page.goto('https://www.example.com'); + expect(await page.evaluate('document.cookie')).toBe('cookie1=1'); +}); + +it('should remove cookies by path', async ({ context, page, server }) => { + await context.addCookies([{ + name: 'cookie1', + value: '1', + domain: 'www.example.com', + path: '/api/v1', + }, + { + name: 'cookie2', + value: '2', + domain: 'www.example.com', + path: '/api/v2', + }, + { + name: 'cookie3', + value: '3', + domain: 'www.example.com', + path: '/', + } + ]); + await page.goto('https://www.example.com/api/v1'); + expect(await page.evaluate('document.cookie')).toBe('cookie1=1; cookie3=3'); + await context.removeCookies({ path: '/api/v1' }); + expect(await page.evaluate('document.cookie')).toBe('cookie3=3'); + await page.goto('https://www.example.com/api/v2'); + expect(await page.evaluate('document.cookie')).toBe('cookie2=2; cookie3=3'); + await page.goto('https://www.example.com/'); + expect(await page.evaluate('document.cookie')).toBe('cookie3=3'); +}); + +it('should remove cookies by name and domain', async ({ context, page, server }) => { + await context.addCookies([{ + name: 'cookie1', + value: '1', + domain: 'www.example.com', + path: '/', + }, + { + name: 'cookie1', + value: '1', + domain: 'www.example.org', + path: '/', + } + ]); + await page.goto('https://www.example.com'); + expect(await page.evaluate('document.cookie')).toBe('cookie1=1'); + await context.removeCookies({ name: 'cookie1', domain: 'www.example.com' }); + expect(await page.evaluate('document.cookie')).toBe(''); + await page.goto('https://www.example.org'); + expect(await page.evaluate('document.cookie')).toBe('cookie1=1'); +}); + +it('should remove cookies by name and path', async ({ context, page, server }) => { + await context.addCookies([{ + name: 'cookie1', + value: '1', + domain: 'www.example.com', + path: '/api/v1', + }, + { + name: 'cookie1', + value: '1', + domain: 'www.example.com', + path: '/api/v2', + }, + { + name: 'cookie3', + value: '3', + domain: 'www.example.com', + path: '/', + } + ]); + await page.goto('https://www.example.com/api/v1'); + expect(await page.evaluate('document.cookie')).toBe('cookie1=1; cookie3=3'); + await context.removeCookies({ name: 'cookie1', path: '/api/v1' }); + expect(await page.evaluate('document.cookie')).toBe('cookie3=3'); + await page.goto('https://www.example.com/api/v2'); + expect(await page.evaluate('document.cookie')).toBe('cookie1=1; cookie3=3'); + await page.goto('https://www.example.com/'); + expect(await page.evaluate('document.cookie')).toBe('cookie3=3'); +}); + +it('should remove cookies by domain and path', async ({ context, page, server }) => { + await context.addCookies([{ + name: 'cookie1', + value: '1', + domain: 'www.example.com', + path: '/api/v1', + }, + { + name: 'cookie2', + value: '2', + domain: 'www.example.com', + path: '/api/v2', + }, + { + name: 'cookie3', + value: '3', + domain: 'www.example.org', + path: '/api/v1', + }, + { + name: 'cookie4', + value: '4', + domain: 'www.example.org', + path: '/api/v2', + } + ]); + await page.goto('https://www.example.com/api/v1'); + expect(await page.evaluate('document.cookie')).toBe('cookie1=1'); + await context.removeCookies({ domain: 'www.example.com', path: '/api/v1' }); + expect(await page.evaluate('document.cookie')).toBe(''); + await page.goto('https://www.example.com/api/v2'); + expect(await page.evaluate('document.cookie')).toBe('cookie2=2'); + await page.goto('https://www.example.org/api/v2'); + expect(await page.evaluate('document.cookie')).toBe('cookie4=4'); +}); + +it('should remove cookies by name, domain and path', async ({ context, page, server }) => { + await context.addCookies([{ + name: 'cookie1', + value: '1', + domain: 'www.example.com', + path: '/api/v1', + }, + { + name: 'cookie2', + value: '2', + domain: 'www.example.com', + path: '/api/v2', + }, + { + name: 'cookie1', + value: '1', + domain: 'www.example.org', + path: '/api/v1', + }, + ]); + await page.goto('https://www.example.com/api/v1'); + expect(await page.evaluate('document.cookie')).toBe('cookie1=1'); + await context.removeCookies({ name: 'cookie1', domain: 'www.example.com', path: '/api/v1' }); + expect(await page.evaluate('document.cookie')).toBe(''); + await page.goto('https://www.example.com/api/v2'); + expect(await page.evaluate('document.cookie')).toBe('cookie2=2'); + await page.goto('https://www.example.org/api/v1'); + expect(await page.evaluate('document.cookie')).toBe('cookie1=1'); +}); + +it('should not remove cookies when empty object passed', async ({ context, page, server }) => { + await context.addCookies([{ + name: 'cookie1', + value: '1', + domain: 'www.example.com', + path: '/', + }, + { + name: 'cookie2', + value: '2', + domain: 'www.example.com', + path: '/', + }, + ]); + await page.goto('https://www.example.com/'); + expect(await page.evaluate('document.cookie')).toBe('cookie1=1; cookie2=2'); + await context.removeCookies({ }); + expect(await page.evaluate('document.cookie')).toBe('cookie1=1; cookie2=2'); +});