diff --git a/experimental/puppeteer-firefox/lib/Page.js b/experimental/puppeteer-firefox/lib/Page.js index 63a70861769..d6ae8969445 100644 --- a/experimental/puppeteer-firefox/lib/Page.js +++ b/experimental/puppeteer-firefox/lib/Page.js @@ -8,6 +8,7 @@ const util = require('util'); const EventEmitter = require('events'); const {createHandle} = require('./JSHandle'); const {Events} = require('./Events'); +const {Connection} = require('./Connection'); const {FrameManager, normalizeWaitUntil} = require('./FrameManager'); const {NetworkManager} = require('./NetworkManager'); const {TimeoutSettings} = require('./TimeoutSettings'); @@ -80,6 +81,67 @@ class Page extends EventEmitter { }); } + /** + * @param {!Array} urls + * @return {!Promise>} + */ + async cookies(...urls) { + const connection = Connection.fromSession(this._session); + return (await connection.send('Browser.getCookies', { + browserContextId: this._target._context._browserContextId, + urls: urls.length ? urls : [this.url()] + })).cookies; + } + + /** + * @param {Array} cookies + */ + async deleteCookie(...cookies) { + 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, + cookies: items, + }); + } + + /** + * @param {Array} cookies + */ + async setCookie(...cookies) { + 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, + cookies: items + }); + } + } + async setRequestInterception(enabled) { await this._networkManager.setRequestInterception(enabled); } diff --git a/experimental/puppeteer-firefox/package.json b/experimental/puppeteer-firefox/package.json index 9b8fe136bdc..a7461aeacb9 100644 --- a/experimental/puppeteer-firefox/package.json +++ b/experimental/puppeteer-firefox/package.json @@ -9,7 +9,7 @@ "node": ">=8.9.4" }, "puppeteer": { - "firefox_revision": "d69636bbb91f42286e81ef673b33a1459bcdfcea" + "firefox_revision": "fd63770c54de8a6e4ac28bd9f010405c12105d63" }, "scripts": { "install": "node install.js", diff --git a/lib/Page.js b/lib/Page.js index b2704fdca13..2f9510aa84b 100644 --- a/lib/Page.js +++ b/lib/Page.js @@ -388,14 +388,8 @@ class Page extends EventEmitter { 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}"` - ); + 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); diff --git a/test/browsercontext.spec.js b/test/browsercontext.spec.js index 5c365334fc8..54ed4afab00 100644 --- a/test/browsercontext.spec.js +++ b/test/browsercontext.spec.js @@ -18,7 +18,7 @@ const utils = require('./utils'); module.exports.addTests = function({testRunner, expect, puppeteer, Errors}) { const {describe, xdescribe, fdescribe} = testRunner; - const {it, fit, xit, it_fails_ffox} = testRunner; + const {it, fit, xit} = testRunner; const {beforeAll, beforeEach, afterAll, afterEach} = testRunner; const {TimeoutError} = Errors; diff --git a/test/cookies.spec.js b/test/cookies.spec.js index 5148162a63a..35e90b41f0b 100644 --- a/test/cookies.spec.js +++ b/test/cookies.spec.js @@ -15,14 +15,17 @@ */ module.exports.addTests = function({testRunner, expect}) { - const {describe, xdescribe, fdescribe, describe_fails_ffox} = testRunner; + const {describe, xdescribe, fdescribe} = testRunner; const {it, fit, xit} = testRunner; const {beforeAll, beforeEach, afterAll, afterEach} = testRunner; - describe_fails_ffox('Cookies', function() { - it('should set and get cookies', async({page, server}) => { - await page.goto(server.PREFIX + '/grid.html'); + describe('Page.cookies', function() { + it('should return no cookies in pristine browser context', async({page, server}) => { + await page.goto(server.EMPTY_PAGE); expect(await page.cookies()).toEqual([]); + }); + it('should get a cookie', async({page, server}) => { + await page.goto(server.EMPTY_PAGE); await page.evaluate(() => { document.cookie = 'username=John Doe'; }); @@ -35,13 +38,144 @@ module.exports.addTests = function({testRunner, expect}) { size: 16, httpOnly: false, secure: false, - session: true } + session: true + }]); + }); + it('should get multiple cookies', async({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(); + cookies.sort((a, b) => a.name.localeCompare(b.name)); + expect(cookies).toEqual([ + { + name: 'password', + value: '1234', + domain: 'localhost', + path: '/', + expires: -1, + size: 12, + httpOnly: false, + secure: false, + session: true + }, + { + name: 'username', + value: 'John Doe', + domain: 'localhost', + path: '/', + expires: -1, + size: 16, + httpOnly: false, + secure: false, + session: true + }, ]); + }); + it('should get cookies from multiple urls', async({page, server}) => { + await page.setCookie({ + url: 'https://foo.com', + name: 'doggo', + value: 'woofs', + }, { + url: 'https://bar.com', + name: 'catto', + value: 'purrs', + }, { + url: 'https://baz.com', + name: 'birdo', + value: 'tweets', + }); + const cookies = await page.cookies('https://foo.com', 'https://baz.com'); + cookies.sort((a, b) => a.name.localeCompare(b.name)); + expect(cookies).toEqual([{ + name: 'birdo', + value: 'tweets', + domain: 'baz.com', + path: '/', + expires: -1, + size: 11, + httpOnly: false, + secure: true, + session: true + }, { + name: 'doggo', + value: 'woofs', + domain: 'foo.com', + path: '/', + expires: -1, + size: 10, + httpOnly: false, + secure: true, + session: true + }]); + }); + }); + + describe('Page.setCookie', function() { + it('should work', async({page, server}) => { + await page.goto(server.EMPTY_PAGE); + await page.setCookie({ + name: 'password', + value: '123456' + }); + expect(await page.evaluate(() => document.cookie)).toEqual('password=123456'); + }); + it('should isolate cookies in browser contexts', async({page, server, browser}) => { + const anotherContext = await browser.createIncognitoBrowserContext(); + const anotherPage = await anotherContext.newPage(); + + 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(); + expect(cookies1.length).toBe(1); + expect(cookies2.length).toBe(1); + expect(cookies1[0].name).toBe('page1cookie'); + expect(cookies1[0].value).toBe('page1value'); + expect(cookies2[0].name).toBe('page2cookie'); + expect(cookies2[0].value).toBe('page2value'); + await anotherContext.close(); + }); + it('should set multiple cookies', async({page, server}) => { + await page.goto(server.EMPTY_PAGE); + await page.setCookie({ + name: 'password', + value: '123456' + }, { + name: 'foo', + value: 'bar' + }); + expect(await page.evaluate(() => { + const cookies = document.cookie.split(';'); + return cookies.map(cookie => cookie.trim()).sort(); + })).toEqual([ + 'foo=bar', + 'password=123456', + ]); + }); + it('should have |expires| set to |-1| for session cookies', async({page, server}) => { + await page.goto(server.EMPTY_PAGE); + await page.setCookie({ + name: 'password', + value: '123456' + }); + const cookies = await page.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({ name: 'password', value: '123456' }); - expect(await page.evaluate('document.cookie')).toBe('username=John Doe; password=123456'); const cookies = await page.cookies(); expect(cookies.sort((a, b) => a.name.localeCompare(b.name))).toEqual([{ name: 'password', @@ -53,19 +187,8 @@ module.exports.addTests = function({testRunner, expect}) { httpOnly: false, secure: false, session: true - }, { - name: 'username', - value: 'John Doe', - domain: 'localhost', - path: '/', - expires: -1, - size: 16, - httpOnly: false, - secure: false, - session: true }]); }); - it('should set a cookie with a path', async({page, server}) => { await page.goto(server.PREFIX + '/grid.html'); await page.setCookie({ @@ -85,46 +208,25 @@ module.exports.addTests = function({testRunner, expect}) { session: true }]); expect(await page.evaluate('document.cookie')).toBe('gridcookie=GRID'); - await page.goto(server.PREFIX + '/empty.html'); + 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 delete a cookie', async({page, server}) => { - await page.goto(server.PREFIX + '/grid.html'); - await page.setCookie({ - 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'); - }); - it('should not set a cookie on a blank page', async function({page}) { - let error = null; await page.goto('about:blank'); + let error = null; try { await page.setCookie({name: 'example-cookie', value: 'best'}); } catch (e) { error = e; } - expect(error).toBeTruthy(); - expect(error.message).toEqual('Protocol error (Network.deleteCookies): At least one of the url and domain needs to be specified'); + 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.PREFIX + '/grid.html'); + await page.goto(server.EMPTY_PAGE); try { await page.setCookie( {name: 'example-cookie', value: 'best'}, @@ -133,12 +235,10 @@ module.exports.addTests = function({testRunner, expect}) { } catch (e) { error = e; } - expect(error).toBeTruthy(); expect(error.message).toEqual( `Blank page can not have cookie "example-cookie-blank"` ); }); - it('should not set a cookie on a data URL page', async function({page}) { let error = null; await page.goto('data:,Hello%2C%20World!'); @@ -147,15 +247,37 @@ module.exports.addTests = function({testRunner, expect}) { } catch (e) { error = e; } - expect(error).toBeTruthy(); - expect(error.message).toEqual( - 'Protocol error (Network.deleteCookies): At least one of the url and domain needs to be specified' - ); + expect(error.message).toContain('At least one of the url and domain needs to be specified'); + }); + it('should default to setting secure cookie for HTTPS websites', async({page, server}) => { + await page.goto(server.EMPTY_PAGE); + const SECURE_URL = 'https://example.com'; + await page.setCookie({ + url: SECURE_URL, + name: 'foo', + value: 'bar', + }); + const [cookie] = await page.cookies(SECURE_URL); + expect(cookie.secure).toBe(true); + }); + it('should be able to set unsecure cookie for HTTPS website', async({page, server}) => { + await page.goto(server.EMPTY_PAGE); + const SECURE_URL = 'http://example.com'; + await page.setCookie({ + url: SECURE_URL, + name: 'foo', + value: 'bar', + }); + const [cookie] = await page.cookies(SECURE_URL); + expect(cookie.secure).toBe(false); }); - it('should set a cookie on a different domain', async({page, server}) => { - await page.goto(server.PREFIX + '/grid.html'); - await page.setCookie({name: 'example-cookie', value: 'best', url: 'https://www.example.com'}); + await page.goto(server.EMPTY_PAGE); + await page.setCookie({ + 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([{ @@ -170,7 +292,6 @@ module.exports.addTests = function({testRunner, expect}) { session: true }]); }); - it('should set cookies from a frame', async({page, server}) => { await page.goto(server.PREFIX + '/grid.html'); await page.setCookie({name: 'localhost-cookie', value: 'best'}); @@ -210,7 +331,25 @@ module.exports.addTests = function({testRunner, expect}) { secure: false, session: true }]); + }); + }); + describe('Page.deleteCookie', function() { + it('should work', async({page, server}) => { + await page.goto(server.EMPTY_PAGE); + await page.setCookie({ + 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'); }); }); }; diff --git a/test/network.spec.js b/test/network.spec.js index 1aa37f049c7..116a895f70c 100644 --- a/test/network.spec.js +++ b/test/network.spec.js @@ -236,6 +236,17 @@ module.exports.addTests = function({testRunner, expect, CHROME}) { }); }); + describe('Response.statusText', function() { + it('should work', async({page, server}) => { + server.setRoute('/cool', (req, res) => { + res.writeHead(200, 'cool!'); + res.end(); + }); + const response = await page.goto(server.PREFIX + '/cool'); + expect(response.statusText()).toBe('cool!'); + }); + }); + describe('Network Events', function() { it('Page.Events.Request', async({page, server}) => { const requests = []; @@ -264,15 +275,6 @@ module.exports.addTests = function({testRunner, expect, CHROME}) { expect(remoteAddress.port).toBe(server.PORT); }); - it('Response.statusText', async({page, server}) => { - server.setRoute('/cool', (req, res) => { - res.writeHead(200, 'cool!'); - res.end(); - }); - const response = await page.goto(server.PREFIX + '/cool'); - expect(response.statusText()).toBe('cool!'); - }); - it('Page.Events.RequestFailed', async({page, server}) => { await page.setRequestInterception(true); page.on('request', request => { @@ -422,7 +424,7 @@ module.exports.addTests = function({testRunner, expect, CHROME}) { expect(requests[1].url()).toContain('/one-style.css'); expect(requests[1].headers().referer).toContain('/one-style.html'); }); - it_fails_ffox('should properly return navigation response when URL has cookies', async({page, server}) => { + it('should properly return navigation response when URL has cookies', async({page, server}) => { // Setup cookie. await page.goto(server.EMPTY_PAGE); await page.setCookie({ name: 'foo', value: 'bar'});