diff --git a/docs/api.md b/docs/api.md index 76669b7414..0e8527094a 100644 --- a/docs/api.md +++ b/docs/api.md @@ -50,11 +50,14 @@ * [event: 'targetcreated'](#event-targetcreated-1) * [event: 'targetdestroyed'](#event-targetdestroyed-1) * [browserContext.browser()](#browsercontextbrowser) + * [browserContext.clearCookies()](#browsercontextclearcookies) * [browserContext.close()](#browsercontextclose) + * [browserContext.cookies([...urls])](#browsercontextcookiesurls) * [browserContext.isIncognito()](#browsercontextisincognito) * [browserContext.newPage()](#browsercontextnewpage) * [browserContext.pages()](#browsercontextpages) * [browserContext.permissions](#browsercontextpermissions) + * [browserContext.setCookies(cookies)](#browsercontextsetcookiescookies) * [browserContext.targets()](#browsercontexttargets) * [browserContext.waitForTarget(predicate[, options])](#browsercontextwaitfortargetpredicate-options) - [class: Geolocation](#class-geolocation) @@ -91,10 +94,8 @@ * [page.click(selector[, options])](#pageclickselector-options) * [page.close([options])](#pagecloseoptions) * [page.content()](#pagecontent) - * [page.cookies([...urls])](#pagecookiesurls) * [page.coverage](#pagecoverage) * [page.dblclick(selector[, options])](#pagedblclickselector-options) - * [page.deleteCookie(...cookies)](#pagedeletecookiecookies) * [page.emulate(options)](#pageemulateoptions) * [page.emulateMedia(options)](#pageemulatemediaoptions) * [page.emulateTimezone(timezoneId)](#pageemulatetimezonetimezoneid) @@ -122,7 +123,6 @@ * [page.setBypassCSP(enabled)](#pagesetbypasscspenabled) * [page.setCacheEnabled([enabled])](#pagesetcacheenabledenabled) * [page.setContent(html[, options])](#pagesetcontenthtml-options) - * [page.setCookie(...cookies)](#pagesetcookiecookies) * [page.setDefaultNavigationTimeout(timeout)](#pagesetdefaultnavigationtimeouttimeout) * [page.setDefaultTimeout(timeout)](#pagesetdefaulttimeouttimeout) * [page.setExtraHTTPHeaders(headers)](#pagesetextrahttpheadersheaders) @@ -757,6 +757,11 @@ Emitted when a target inside the browser context is destroyed, for example when The browser this browser context belongs to. +#### browserContext.clearCookies() +- returns: <[Promise]> + +Clears context bookies. + #### browserContext.close() - returns: <[Promise]> @@ -765,6 +770,23 @@ will be closed. > **NOTE** only incognito browser contexts can be closed. +#### browserContext.cookies([...urls]) +- `...urls` <...[string]> +- returns: <[Promise]<[Array]<[Object]>>> + - `name` <[string]> + - `value` <[string]> + - `domain` <[string]> + - `path` <[string]> + - `expires` <[number]> Unix time in seconds. + - `size` <[number]> + - `httpOnly` <[boolean]> + - `secure` <[boolean]> + - `session` <[boolean]> + - `sameSite` <"Strict"|"Lax"|"None"> + +If no URLs are specified, this method returns all cookies. +If URLs are specified, only cookies that affect those URLs are returned. + #### browserContext.isIncognito() - returns: <[boolean]> @@ -786,6 +808,23 @@ An array of all pages inside the browser context. #### browserContext.permissions - returns: <[Permissions]> +#### browserContext.setCookies(cookies) +- `cookies` <[Array]<[Object]>> + - `name` <[string]> **required** + - `value` <[string]> **required** + - `url` <[string]> either url or domain / path are **required** + - `domain` <[string]> either url or domain / path are **required** + - `path` <[string]> either url or domain / path are **required** + - `expires` <[number]> Unix time in seconds. + - `httpOnly` <[boolean]> + - `secure` <[boolean]> + - `sameSite` <"Strict"|"Lax"|"None"> +- returns: <[Promise]> + +```js +await browserContext.setCookies([cookieObject1, cookieObject2]); +``` + #### browserContext.targets() - returns: <[Array]<[Target]>> @@ -1145,23 +1184,6 @@ By default, `page.close()` **does not** run beforeunload handlers. Gets the full HTML contents of the page, including the doctype. -#### page.cookies([...urls]) -- `...urls` <...[string]> -- returns: <[Promise]<[Array]<[Object]>>> - - `name` <[string]> - - `value` <[string]> - - `domain` <[string]> - - `path` <[string]> - - `expires` <[number]> Unix time in seconds. - - `size` <[number]> - - `httpOnly` <[boolean]> - - `secure` <[boolean]> - - `session` <[boolean]> - - `sameSite` <"Strict"|"Lax"|"Extended"|"None"> - -If no URLs are specified, this method returns cookies for the current page URL. -If URLs are specified, only cookies for those URLs are returned. - #### page.coverage - returns: <[Coverage]> @@ -1186,14 +1208,6 @@ Bear in mind that if the first click of the `dblclick()` triggers a navigation e Shortcut for [page.mainFrame().dblclick(selector[, options])](#framedblclickselector-options). -#### page.deleteCookie(...cookies) -- `...cookies` <...[Object]> - - `name` <[string]> **required** - - `url` <[string]> - - `domain` <[string]> - - `path` <[string]> -- returns: <[Promise]> - #### page.emulate(options) - `options` <[Object]> - `viewport` <[Object]> @@ -1614,23 +1628,6 @@ Toggles ignoring cache for each request based on the enabled state. By default, - `networkidle2` - consider setting content to be finished when there are no more than 2 network connections for at least `500` ms. - returns: <[Promise]> -#### page.setCookie(...cookies) -- `...cookies` <...[Object]> - - `name` <[string]> **required** - - `value` <[string]> **required** - - `url` <[string]> - - `domain` <[string]> - - `path` <[string]> - - `expires` <[number]> Unix time in seconds. - - `httpOnly` <[boolean]> - - `secure` <[boolean]> - - `sameSite` <"Strict"|"Lax"> -- returns: <[Promise]> - -```js -await page.setCookie(cookieObject1, cookieObject2); -``` - #### page.setDefaultNavigationTimeout(timeout) - `timeout` <[number]> Maximum navigation time in milliseconds diff --git a/package.json b/package.json index b73098ac3c..e04564c8a3 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,7 @@ }, "playwright": { "chromium_revision": "718525", - "firefox_revision": "1003", + "firefox_revision": "1004", "webkit_revision": "2" }, "scripts": { diff --git a/src/chromium/Browser.ts b/src/chromium/Browser.ts index 15db04a22f..3a07f7be5d 100644 --- a/src/chromium/Browser.ts +++ b/src/chromium/Browser.ts @@ -33,7 +33,7 @@ export class Browser extends EventEmitter { private _process: childProcess.ChildProcess; private _screenshotTaskQueue = new TaskQueue(); private _connection: Connection; - private _client: CDPSession; + _client: CDPSession; private _closeCallback: () => Promise; private _defaultContext: BrowserContext; private _contexts = new Map(); @@ -146,7 +146,7 @@ export class Browser extends EventEmitter { async _createPageInContext(contextId: string | null): Promise { const { targetId } = await this._client.send('Target.createTarget', { url: 'about:blank', browserContextId: contextId || undefined }); - const target = await this._targets.get(targetId); + const target = this._targets.get(targetId); assert(await target._initializedPromise, 'Failed to create target for page'); const page = await target.page(); return page; diff --git a/src/chromium/BrowserContext.ts b/src/chromium/BrowserContext.ts index 66b99d8b6f..5d5c950aa8 100644 --- a/src/chromium/BrowserContext.ts +++ b/src/chromium/BrowserContext.ts @@ -17,6 +17,7 @@ import { EventEmitter } from 'events'; import { assert } from '../helper'; +import { NetworkCookie, SetNetworkCookieParam, filterCookies } from '../network'; import { Browser } from './Browser'; import { CDPSession } from './Connection'; import { Permissions } from './features/permissions'; @@ -65,6 +66,25 @@ export class BrowserContext extends EventEmitter { return this._browser; } + async cookies(...urls: string[]): Promise { + const { cookies } = await this._browser._client.send('Storage.getCookies', { browserContextId: this._id || undefined }); + return filterCookies(cookies.map(c => ({ sameSite: 'None', ...c })), urls); + } + + async clearCookies() { + await this._browser._client.send('Storage.clearCookies', { browserContextId: this._id || undefined }); + } + + async setCookies(cookies: SetNetworkCookieParam[]) { + const items = cookies.map(cookie => { + const item = Object.assign({}, cookie); + assert(item.url !== 'about:blank', `Blank page can not have cookie "${item.name}"`); + assert(!String.prototype.startsWith.call(item.url || '', 'data:'), `Data URL page can not have cookie "${item.name}"`); + return item; + }); + await this._browser._client.send('Storage.setCookies', { cookies: items, browserContextId: this._id || undefined }); + } + async close() { assert(this._id, 'Non-incognito profiles cannot be closed!'); await this._browser._disposeContext(this._id); diff --git a/src/chromium/Page.ts b/src/chromium/Page.ts index 3f0d46a9e1..2e7b76c180 100644 --- a/src/chromium/Page.ts +++ b/src/chromium/Page.ts @@ -254,40 +254,6 @@ export class Page extends EventEmitter { return this.mainFrame().$x(expression); } - async cookies(...urls: string[]): Promise { - const {cookies} = await this._client.send('Network.getCookies', { - urls: urls.length ? urls : [this.url()] - }); - // Chromiums's cookies are missing sameSite when it is 'None' - return cookies.map(cookie => ({sameSite: 'None', ...cookie})); - } - - async deleteCookie(...cookies: Protocol.Network.deleteCookiesParameters[]) { - const pageURL = this.url(); - for (const cookie of cookies) { - const item = Object.assign({}, cookie); - if (!cookie.url && pageURL.startsWith('http')) - item.url = pageURL; - await this._client.send('Network.deleteCookies', item); - } - } - - async setCookie(...cookies: NetworkCookieParam[]) { - const pageURL = this.url(); - const startsWithHTTP = pageURL.startsWith('http'); - const items = cookies.map(cookie => { - const item = Object.assign({}, cookie); - if (!item.url && startsWithHTTP) - item.url = pageURL; - assert(item.url !== 'about:blank', `Blank page can not have cookie "${item.name}"`); - assert(!String.prototype.startsWith.call(item.url || '', 'data:'), `Data URL page can not have cookie "${item.name}"`); - return item; - }); - await this.deleteCookie(...items); - if (items.length) - await this._client.send('Network.setCookies', { cookies: items }); - } - async addScriptTag(options: { url?: string; path?: string; content?: string; type?: string; }): Promise { return this.mainFrame().addScriptTag(options); } @@ -747,31 +713,6 @@ type MediaFeature = { value: string } -type NetworkCookie = { - name: string, - value: string, - domain: string, - path: string, - expires: number, - size: number, - httpOnly: boolean, - secure: boolean, - session: boolean, - sameSite: 'Strict'|'Lax'|'Extended'|'None' -}; - -type NetworkCookieParam = { - name: string, - value: string, - url?: string, - domain?: string, - path?: string, - expires?: number, - httpOnly?: boolean, - secure?: boolean, - sameSite?: 'Strict'|'Lax' -}; - type ConsoleMessageLocation = { url?: string, lineNumber?: number, diff --git a/src/chromium/features/coverage.ts b/src/chromium/features/coverage.ts index 34846d1f3a..93616d2cf5 100644 --- a/src/chromium/features/coverage.ts +++ b/src/chromium/features/coverage.ts @@ -56,8 +56,6 @@ export class Coverage { } } -module.exports = {Coverage}; - class JSCoverage { _client: CDPSession; _enabled: boolean; diff --git a/src/firefox/Browser.ts b/src/firefox/Browser.ts index 84449410af..1e2ec8437e 100644 --- a/src/firefox/Browser.ts +++ b/src/firefox/Browser.ts @@ -16,9 +16,10 @@ */ import { EventEmitter } from 'events'; -import { Events } from './events'; import { assert, helper, RegisteredListener } from '../helper'; +import { filterCookies, NetworkCookie, SetNetworkCookieParam } from '../network'; import { Connection, ConnectionEvents } from './Connection'; +import { Events } from './events'; import { Permissions } from './features/permissions'; import { Page, Viewport } from './Page'; @@ -289,10 +290,34 @@ export class BrowserContext extends EventEmitter { return this._browser; } + async cookies(...urls: string[]): Promise { + const { cookies } = await this._connection.send('Browser.getCookies', { + browserContextId: this._browserContextId || undefined + }); + return filterCookies(cookies, urls); + } + + async clearCookies() { + await this._connection.send('Browser.clearCookies', { + browserContextId: this._browserContextId || undefined, + }); + } + + async setCookies(cookies: SetNetworkCookieParam[]) { + const items = cookies.map(cookie => { + const item = Object.assign({}, cookie); + assert(item.url !== 'about:blank', `Blank page can not have cookie "${item.name}"`); + assert(!String.prototype.startsWith.call(item.url || '', 'data:'), `Data URL page can not have cookie "${item.name}"`); + return item; + }); + await this._connection.send('Browser.setCookies', { + browserContextId: this._browserContextId || undefined, + cookies + }); + } + async close() { assert(this._browserContextId, 'Non-incognito contexts cannot be closed!'); await this._browser._disposeContext(this._browserContextId); } } - -module.exports = {Browser, BrowserContext, Target}; diff --git a/src/firefox/Page.ts b/src/firefox/Page.ts index 3ade807478..33dc91c795 100644 --- a/src/firefox/Page.ts +++ b/src/firefox/Page.ts @@ -94,59 +94,6 @@ export class Page extends EventEmitter { }); } - async cookies(...urls: Array): Promise> { - const connection = Connection.fromSession(this._session); - const {cookies} = await connection.send('Browser.getCookies', { - browserContextId: this._target._context._browserContextId || undefined, - urls: urls.length ? urls : [this.url()] - }); - // Firefox's cookies are missing sameSite when it is 'None' - return cookies.map(cookie => ({sameSite: 'None', ...cookie})); - } - - async deleteCookie(...cookies: Array) { - const pageURL = this.url(); - const items = []; - for (const cookie of cookies) { - const item = { - url: cookie.url, - domain: cookie.domain, - path: cookie.path, - name: cookie.name, - }; - if (!item.url && pageURL.startsWith('http')) - item.url = pageURL; - items.push(item); - } - - const connection = Connection.fromSession(this._session); - await connection.send('Browser.deleteCookies', { - browserContextId: this._target._context._browserContextId || undefined, - cookies: items, - }); - } - - async setCookie(...cookies: Array) { - const pageURL = this.url(); - const startsWithHTTP = pageURL.startsWith('http'); - const items = cookies.map(cookie => { - const item = Object.assign({}, cookie); - if (!item.url && startsWithHTTP) - item.url = pageURL; - assert(item.url !== 'about:blank', `Blank page can not have cookie "${item.name}"`); - assert(!String.prototype.startsWith.call(item.url || '', 'data:'), `Data URL page can not have cookie "${item.name}"`); - return item; - }); - await this.deleteCookie(...items); - if (items.length) { - const connection = Connection.fromSession(this._session); - await connection.send('Browser.setCookies', { - browserContextId: this._target._context._browserContextId || undefined, - cookies: items - }); - } - } - async setExtraHTTPHeaders(headers) { await this._networkManager.setExtraHTTPHeaders(headers); } diff --git a/src/network.ts b/src/network.ts new file mode 100644 index 0000000000..f95870db08 --- /dev/null +++ b/src/network.ts @@ -0,0 +1,46 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +export type NetworkCookie = { + name: string, + value: string, + domain: string, + path: string, + expires: number, + size: number, + httpOnly: boolean, + secure: boolean, + session: boolean, + sameSite: 'Strict' | 'Lax' | 'None' +}; + +export type SetNetworkCookieParam = { + name: string, + value: string, + url?: string, + domain?: string, + path?: string, + expires?: number, + httpOnly?: boolean, + secure?: boolean, + sameSite?: 'Strict' | 'Lax' | 'None' +}; + +export function filterCookies(cookies: NetworkCookie[], urls: string[]) { + const parsedURLs = urls.map(s => new URL(s)); + // Chromiums's cookies are missing sameSite when it is 'None' + return cookies.filter(c => { + if (!parsedURLs.length) + return true; + for (const parsedURL of parsedURLs) { + if (parsedURL.hostname !== c.domain) + continue; + if (!parsedURL.pathname.startsWith(c.path)) + continue; + if ((parsedURL.protocol === 'https:') !== c.secure) + continue; + return true; + } + return false; + }); +} diff --git a/src/webkit/Browser.ts b/src/webkit/Browser.ts index e5e63eded9..dc9169a6dd 100644 --- a/src/webkit/Browser.ts +++ b/src/webkit/Browser.ts @@ -17,9 +17,10 @@ import * as childProcess from 'child_process'; import { EventEmitter } from 'events'; +import { assert, helper, RegisteredListener } from '../helper'; +import { filterCookies, NetworkCookie } from '../network'; import { Connection } from './Connection'; import { Events } from './events'; -import { RegisteredListener, assert, helper } from '../helper'; import { Page, Viewport } from './Page'; import { Target } from './Target'; import { TaskQueue } from './TaskQueue'; @@ -255,4 +256,30 @@ export class BrowserContext extends EventEmitter { assert(this._id, 'Non-incognito profiles cannot be closed!'); await this._browser._disposeContext(this._id); } + + async cookies(...urls: string[]): Promise { + const page = (await this.pages())[0]; + const response = await page._session.send('Page.getCookies'); + const cookies = response.cookies.map(cookie => { + // Webkit returns 0 for a cookie without an expiration + if (cookie.expires === 0) + cookie.expires = -1; + return cookie; + }); + return filterCookies(cookies, urls); + } + + async clearCookies() { + const page = (await this.pages())[0]; + const response = await page._session.send('Page.getCookies'); + const promises = []; + for (const cookie of response.cookies) { + const item = { + cookieName: cookie.name, + url: (cookie.secure ? 'https://' : 'http://') + cookie.domain + cookie.path + }; + promises.push(page._session.send('Page.deleteCookie', item)); + } + await Promise.all(promises); + } } diff --git a/src/webkit/Page.ts b/src/webkit/Page.ts index 80839d8440..711acc1ec0 100644 --- a/src/webkit/Page.ts +++ b/src/webkit/Page.ts @@ -44,7 +44,7 @@ export type Viewport = { export class Page extends EventEmitter { private _closed = false; - private _session: TargetSession; + _session: TargetSession; private _target: Target; private _keyboard: input.Keyboard; private _mouse: input.Mouse; @@ -222,29 +222,6 @@ export class Page extends EventEmitter { return this.mainFrame().$x(expression); } - async cookies(...urls: string[]): Promise { - const response = await this._session.send('Page.getCookies'); - return response.cookies.map(cookie => { - // Webkit returns 0 for a cookie without an expiration - if (cookie.expires === 0) - cookie.expires = -1; - return cookie; - }); - } - - async deleteCookie(...cookies: DeleteNetworkCookieParam[]) { - const pageURL = this.url(); - for (const cookie of cookies) { - const item = { - cookieName: cookie.name, - url: cookie.url - }; - if (!cookie.url && pageURL.startsWith('http')) - item.url = pageURL; - await this._session.send('Page.deleteCookie', item).catch(e => debugError('deleting ' + JSON.stringify(item) + ' => ' + e)); - } - } - async addScriptTag(options: { url?: string; path?: string; content?: string; type?: string; }): Promise { return this.mainFrame().addScriptTag(options); } @@ -525,24 +502,6 @@ type ScreenshotOptions = { encoding?: string, } -type NetworkCookie = { - name: string, - value: string, - domain: string, - path: string, - expires: number, - size: number, - httpOnly: boolean, - secure: boolean, - session: boolean, - sameSite?: 'Strict'|'Lax'|'Extended'|'None' -}; - -type DeleteNetworkCookieParam = { - name: string, - url?: string, -}; - type ConsoleMessageLocation = { url?: string, lineNumber?: number, diff --git a/test/cookies.spec.js b/test/cookies.spec.js index 72ba2507e0..0e6976db7c 100644 --- a/test/cookies.spec.js +++ b/test/cookies.spec.js @@ -19,17 +19,17 @@ module.exports.addTests = function({testRunner, expect, FFOX, CHROME, WEBKIT}) { const {it, fit, xit} = testRunner; const {beforeAll, beforeEach, afterAll, afterEach} = testRunner; - describe('Page.cookies', function() { - it('should return no cookies in pristine browser context', async({page, server}) => { + describe('BrowserContext.cookies', function() { + it('should return no cookies in pristine browser context', async({context, page, server}) => { await page.goto(server.EMPTY_PAGE); - expect(await page.cookies()).toEqual([]); + expect(await context.cookies()).toEqual([]); }); - it('should get a cookie', async({page, server}) => { + it('should get a cookie', async({context, page, server}) => { await page.goto(server.EMPTY_PAGE); await page.evaluate(() => { document.cookie = 'username=John Doe'; }); - expect(await page.cookies()).toEqual([{ + expect(await context.cookies()).toEqual([{ name: 'username', value: 'John Doe', domain: 'localhost', @@ -42,43 +42,43 @@ module.exports.addTests = function({testRunner, expect, FFOX, CHROME, WEBKIT}) { sameSite: 'None', }]); }); - it('should properly report httpOnly cookie', async({page, server}) => { + it('should properly report httpOnly cookie', async({context, page, server}) => { server.setRoute('/empty.html', (req, res) => { res.setHeader('Set-Cookie', ';HttpOnly; Path=/'); res.end(); }); await page.goto(server.EMPTY_PAGE); - const cookies = await page.cookies(); + const cookies = await context.cookies(); expect(cookies.length).toBe(1); expect(cookies[0].httpOnly).toBe(true); }); - it.skip(FFOX || WEBKIT)('should properly report "Strict" sameSite cookie', async({page, server}) => { + it.skip(WEBKIT)('should properly report "Strict" sameSite cookie', async({context, page, server}) => { server.setRoute('/empty.html', (req, res) => { res.setHeader('Set-Cookie', ';SameSite=Strict'); res.end(); }); await page.goto(server.EMPTY_PAGE); - const cookies = await page.cookies(); + const cookies = await context.cookies(); expect(cookies.length).toBe(1); expect(cookies[0].sameSite).toBe('Strict'); }); - it.skip(FFOX || WEBKIT)('should properly report "Lax" sameSite cookie', async({page, server}) => { + it.skip(WEBKIT)('should properly report "Lax" sameSite cookie', async({context, page, server}) => { server.setRoute('/empty.html', (req, res) => { res.setHeader('Set-Cookie', ';SameSite=Lax'); res.end(); }); await page.goto(server.EMPTY_PAGE); - const cookies = await page.cookies(); + const cookies = await context.cookies(); expect(cookies.length).toBe(1); expect(cookies[0].sameSite).toBe('Lax'); }); - it('should get multiple cookies', async({page, server}) => { + it('should get multiple cookies', async({context, page, server}) => { await page.goto(server.EMPTY_PAGE); await page.evaluate(() => { document.cookie = 'username=John Doe'; document.cookie = 'password=1234'; }); - const cookies = await page.cookies(); + const cookies = await context.cookies(); cookies.sort((a, b) => a.name.localeCompare(b.name)); expect(cookies).toEqual([ { @@ -107,8 +107,8 @@ module.exports.addTests = function({testRunner, expect, FFOX, CHROME, WEBKIT}) { }, ]); }); - it.skip(WEBKIT)('should get cookies from multiple urls', async({page, server}) => { - await page.setCookie({ + it.skip(WEBKIT)('should get cookies from multiple urls', async({context}) => { + await context.setCookies([{ url: 'https://foo.com', name: 'doggo', value: 'woofs', @@ -120,8 +120,8 @@ module.exports.addTests = function({testRunner, expect, FFOX, CHROME, WEBKIT}) { url: 'https://baz.com', name: 'birdo', value: 'tweets', - }); - const cookies = await page.cookies('https://foo.com', 'https://baz.com'); + }]); + const cookies = await context.cookies('https://foo.com', 'https://baz.com'); cookies.sort((a, b) => a.name.localeCompare(b.name)); expect(cookies).toEqual([{ name: 'birdo', @@ -149,27 +149,23 @@ module.exports.addTests = function({testRunner, expect, FFOX, CHROME, WEBKIT}) { }); }); - describe.skip(WEBKIT)('Page.setCookie', function() { - it('should work', async({page, server}) => { + describe.skip(WEBKIT)('BrowserContext.setCookies', function() { + it('should work', async({context, page, server}) => { await page.goto(server.EMPTY_PAGE); - await page.setCookie({ + await context.setCookies([{ + url: server.EMPTY_PAGE, name: 'password', value: '123456' - }); + }]); expect(await page.evaluate(() => document.cookie)).toEqual('password=123456'); }); - it('should isolate cookies in browser contexts', async({page, server, browser}) => { + it('should isolate cookies in browser contexts', async({context, server, browser}) => { const anotherContext = await browser.createIncognitoBrowserContext(); - const anotherPage = await anotherContext.newPage(); + await context.setCookies([{url: server.EMPTY_PAGE, name: 'page1cookie', value: 'page1value'}]); + await anotherContext.setCookies([{url: server.EMPTY_PAGE, name: 'page2cookie', value: 'page2value'}]); - await page.goto(server.EMPTY_PAGE); - await anotherPage.goto(server.EMPTY_PAGE); - - await page.setCookie({name: 'page1cookie', value: 'page1value'}); - await anotherPage.setCookie({name: 'page2cookie', value: 'page2value'}); - - const cookies1 = await page.cookies(); - const cookies2 = await anotherPage.cookies(); + const cookies1 = await context.cookies(); + const cookies2 = await anotherContext.cookies(); expect(cookies1.length).toBe(1); expect(cookies2.length).toBe(1); expect(cookies1[0].name).toBe('page1cookie'); @@ -178,15 +174,17 @@ module.exports.addTests = function({testRunner, expect, FFOX, CHROME, WEBKIT}) { expect(cookies2[0].value).toBe('page2value'); await anotherContext.close(); }); - it('should set multiple cookies', async({page, server}) => { + it('should set multiple cookies', async({context, page, server}) => { await page.goto(server.EMPTY_PAGE); - await page.setCookie({ + await context.setCookies([{ + url: server.EMPTY_PAGE, name: 'password', value: '123456' }, { + url: server.EMPTY_PAGE, name: 'foo', value: 'bar' - }); + }]); expect(await page.evaluate(() => { const cookies = document.cookie.split(';'); return cookies.map(cookie => cookie.trim()).sort(); @@ -195,23 +193,23 @@ module.exports.addTests = function({testRunner, expect, FFOX, CHROME, WEBKIT}) { 'password=123456', ]); }); - it('should have |expires| set to |-1| for session cookies', async({page, server}) => { - await page.goto(server.EMPTY_PAGE); - await page.setCookie({ + it('should have |expires| set to |-1| for session cookies', async({context, server}) => { + await context.setCookies([{ + url: server.EMPTY_PAGE, name: 'password', value: '123456' - }); - const cookies = await page.cookies(); + }]); + const cookies = await context.cookies(); expect(cookies[0].session).toBe(true); expect(cookies[0].expires).toBe(-1); }); - it('should set cookie with reasonable defaults', async({page, server}) => { - await page.goto(server.EMPTY_PAGE); - await page.setCookie({ + it('should set cookie with reasonable defaults', async({context, server}) => { + await context.setCookies([{ + url: server.EMPTY_PAGE, name: 'password', value: '123456' - }); - const cookies = await page.cookies(); + }]); + const cookies = await context.cookies(); expect(cookies.sort((a, b) => a.name.localeCompare(b.name))).toEqual([{ name: 'password', value: '123456', @@ -225,14 +223,15 @@ module.exports.addTests = function({testRunner, expect, FFOX, CHROME, WEBKIT}) { sameSite: 'None', }]); }); - it('should set a cookie with a path', async({page, server}) => { + it('should set a cookie with a path', async({context, page, server}) => { await page.goto(server.PREFIX + '/grid.html'); - await page.setCookie({ + await context.setCookies([{ + domain: 'localhost', + path: '/grid.html', name: 'gridcookie', value: 'GRID', - path: '/grid.html' - }); - expect(await page.cookies()).toEqual([{ + }]); + expect(await context.cookies()).toEqual([{ name: 'gridcookie', value: 'GRID', domain: 'localhost', @@ -246,29 +245,17 @@ module.exports.addTests = function({testRunner, expect, FFOX, CHROME, WEBKIT}) { }]); expect(await page.evaluate('document.cookie')).toBe('gridcookie=GRID'); await page.goto(server.EMPTY_PAGE); - expect(await page.cookies()).toEqual([]); expect(await page.evaluate('document.cookie')).toBe(''); await page.goto(server.PREFIX + '/grid.html'); expect(await page.evaluate('document.cookie')).toBe('gridcookie=GRID'); }); - it('should not set a cookie on a blank page', async function({page}) { - await page.goto('about:blank'); + it('should not set a cookie with blank page URL', async function({context, server}) { let error = null; try { - await page.setCookie({name: 'example-cookie', value: 'best'}); - } catch (e) { - error = e; - } - expect(error.message).toContain('At least one of the url and domain needs to be specified'); - }); - it('should not set a cookie with blank page URL', async function({page, server}) { - let error = null; - await page.goto(server.EMPTY_PAGE); - try { - await page.setCookie( - {name: 'example-cookie', value: 'best'}, + await context.setCookies([ + {url: server.EMPTY_PAGE, name: 'example-cookie', value: 'best'}, {url: 'about:blank', name: 'example-cookie-blank', value: 'best'} - ); + ]); } catch (e) { error = e; } @@ -276,48 +263,46 @@ module.exports.addTests = function({testRunner, expect, FFOX, CHROME, WEBKIT}) { `Blank page can not have cookie "example-cookie-blank"` ); }); - it('should not set a cookie on a data URL page', async function({page}) { + it('should not set a cookie on a data URL page', async function({context}) { let error = null; - await page.goto('data:,Hello%2C%20World!'); try { - await page.setCookie({name: 'example-cookie', value: 'best'}); + await context.setCookies([{url: 'data:,Hello%2C%20World!', name: 'example-cookie', value: 'best'}]); } catch (e) { error = e; } - expect(error.message).toContain('At least one of the url and domain needs to be specified'); + expect(error.message).toContain('Data URL page can not have cookie "example-cookie"'); }); - it('should default to setting secure cookie for HTTPS websites', async({page, server}) => { + it('should default to setting secure cookie for HTTPS websites', async({context, page, server}) => { await page.goto(server.EMPTY_PAGE); const SECURE_URL = 'https://example.com'; - await page.setCookie({ + await context.setCookies([{ url: SECURE_URL, name: 'foo', value: 'bar', - }); - const [cookie] = await page.cookies(SECURE_URL); + }]); + const [cookie] = await context.cookies(SECURE_URL); expect(cookie.secure).toBe(true); }); - it('should be able to set unsecure cookie for HTTP website', async({page, server}) => { + it('should be able to set unsecure cookie for HTTP website', async({context, page, server}) => { await page.goto(server.EMPTY_PAGE); const HTTP_URL = 'http://example.com'; - await page.setCookie({ + await context.setCookies([{ url: HTTP_URL, name: 'foo', value: 'bar', - }); - const [cookie] = await page.cookies(HTTP_URL); + }]); + const [cookie] = await context.cookies(HTTP_URL); expect(cookie.secure).toBe(false); }); - it('should set a cookie on a different domain', async({page, server}) => { + it('should set a cookie on a different domain', async({context, page, server}) => { await page.goto(server.EMPTY_PAGE); - await page.setCookie({ + await context.setCookies([{ url: 'https://www.example.com', name: 'example-cookie', value: 'best', - }); + }]); expect(await page.evaluate('document.cookie')).toBe(''); - expect(await page.cookies()).toEqual([]); - expect(await page.cookies('https://www.example.com')).toEqual([{ + expect(await context.cookies('https://www.example.com')).toEqual([{ name: 'example-cookie', value: 'best', domain: 'www.example.com', @@ -330,9 +315,12 @@ module.exports.addTests = function({testRunner, expect, FFOX, CHROME, WEBKIT}) { sameSite: 'None', }]); }); - it('should set cookies from a frame', async({page, server}) => { + it('should set cookies from a frame', async({context, page, server}) => { await page.goto(server.PREFIX + '/grid.html'); - await page.setCookie({name: 'localhost-cookie', value: 'best'}); + await context.setCookies([ + {url: server.PREFIX, name: 'localhost-cookie', value: 'best'}, + {url: server.CROSS_PROCESS_PREFIX, name: '127-cookie', value: 'worst'} + ]); await page.evaluate(src => { let fulfill; const promise = new Promise(x => fulfill = x); @@ -342,11 +330,10 @@ module.exports.addTests = function({testRunner, expect, FFOX, CHROME, WEBKIT}) { iframe.src = src; return promise; }, server.CROSS_PROCESS_PREFIX); - await page.setCookie({name: '127-cookie', value: 'worst', url: server.CROSS_PROCESS_PREFIX}); expect(await page.evaluate('document.cookie')).toBe('localhost-cookie=best'); expect(await page.frames()[1].evaluate('document.cookie')).toBe('127-cookie=worst'); - expect(await page.cookies()).toEqual([{ + expect(await context.cookies(server.PREFIX)).toEqual([{ name: 'localhost-cookie', value: 'best', domain: 'localhost', @@ -359,7 +346,7 @@ module.exports.addTests = function({testRunner, expect, FFOX, CHROME, WEBKIT}) { sameSite: 'None', }]); - expect(await page.cookies(server.CROSS_PROCESS_PREFIX)).toEqual([{ + expect(await context.cookies(server.CROSS_PROCESS_PREFIX)).toEqual([{ name: '127-cookie', value: 'worst', domain: '127.0.0.1', @@ -374,22 +361,35 @@ module.exports.addTests = function({testRunner, expect, FFOX, CHROME, WEBKIT}) { }); }); - describe('Page.deleteCookie', function() { - it.skip(WEBKIT)('should work', async({page, server}) => { + describe.skip(WEBKIT)('BrowserContext.setCookies', function() { + it.skip(WEBKIT)('should clear cookies', async({context, page, server}) => { await page.goto(server.EMPTY_PAGE); - await page.setCookie({ + await context.setCookies([{ + url: server.EMPTY_PAGE, name: 'cookie1', value: '1' - }, { - name: 'cookie2', - value: '2' - }, { - name: 'cookie3', - value: '3' - }); - expect(await page.evaluate('document.cookie')).toBe('cookie1=1; cookie2=2; cookie3=3'); - await page.deleteCookie({name: 'cookie2'}); - expect(await page.evaluate('document.cookie')).toBe('cookie1=1; cookie3=3'); + }]); + expect(await page.evaluate('document.cookie')).toBe('cookie1=1'); + await context.clearCookies(); + expect(await page.evaluate('document.cookie')).toBe(''); + }); + it('should isolate cookies when clearing', async({context, server, browser}) => { + const anotherContext = await browser.createIncognitoBrowserContext(); + await context.setCookies([{url: server.EMPTY_PAGE, name: 'page1cookie', value: 'page1value'}]); + await anotherContext.setCookies([{url: server.EMPTY_PAGE, name: 'page2cookie', value: 'page2value'}]); + + expect((await context.cookies()).length).toBe(1); + expect((await anotherContext.cookies()).length).toBe(1); + + await context.clearCookies(); + expect((await context.cookies()).length).toBe(0); + expect((await anotherContext.cookies()).length).toBe(1); + + await anotherContext.clearCookies(); + expect((await context.cookies()).length).toBe(0); + expect((await anotherContext.cookies()).length).toBe(0); + + await anotherContext.close(); }); }); };