diff --git a/packages/server/lib/browsers/cdp-protocol/cdp_automation.ts b/packages/server/lib/browsers/cdp-protocol/cdp_automation.ts index 68ea29bd72..036388d965 100644 --- a/packages/server/lib/browsers/cdp-protocol/cdp_automation.ts +++ b/packages/server/lib/browsers/cdp-protocol/cdp_automation.ts @@ -1,7 +1,6 @@ /// import _ from 'lodash' -import Bluebird from 'bluebird' import type { Protocol } from 'devtools-protocol' import type ProtocolMapping from 'devtools-protocol/types/protocol-mapping' import { isLocalhost as isLocalhostNetworkTools } from '@packages/network-tools' @@ -262,52 +261,49 @@ export class CdpAutomation implements CDPClient, AutomationMiddleware { } } - private getAllCookies = (filter: CyCookieFilter) => { - return this.sendDebuggerCommandFn('Network.getAllCookies') - .then((result: Protocol.Network.GetAllCookiesResponse) => { - return normalizeGetCookies(result.cookies) - .filter((cookie: CyCookie) => { - const matches = cookieMatches(cookie, filter) + private getAllCookies = async (filter: CyCookieFilter) => { + const result: Protocol.Network.GetAllCookiesResponse = await this.sendDebuggerCommandFn('Network.getAllCookies') - debugVerbose('cookie matches filter? %o', { matches, cookie, filter }) + return normalizeGetCookies(result.cookies) + .filter((cookie: CyCookie) => { + const matches = cookieMatches(cookie, filter) - return matches - }) + debugVerbose('cookie matches filter? %o', { matches, cookie, filter }) + + return matches }) } - private getCookiesByUrl = (url): Promise => { - return this.sendDebuggerCommandFn('Network.getCookies', { + private getCookiesByUrl = async (url): Promise => { + const result: Protocol.Network.GetCookiesResponse = await this.sendDebuggerCommandFn('Network.getCookies', { urls: [url], }) - .then((result: Protocol.Network.GetCookiesResponse) => { - const isLocalhost = isLocalhostNetworkTools(new URL(url)) - - return normalizeGetCookies(result.cookies) - .filter((cookie) => { - // Chrome returns all cookies for a URL, even if they wouldn't normally - // be sent with a request. This standardizes it by filtering out ones - // that are secure but not on a secure context - - // localhost is considered a secure context (even when http:) - // and it's required for cross origin support when visiting a secondary - // origin so that all its cookies are sent. - return !(cookie.secure && url.startsWith('http:') && !isLocalhost) - }) + + const isLocalhost = isLocalhostNetworkTools(new URL(url)) + + return normalizeGetCookies(result.cookies) + .filter((cookie) => { + // Chrome returns all cookies for a URL, even if they wouldn't normally + // be sent with a request. This standardizes it by filtering out ones + // that are secure but not on a secure context + + // localhost is considered a secure context (even when http:) + // and it's required for cross origin support when visiting a secondary + // origin so that all its cookies are sent. + return !(cookie.secure && url.startsWith('http:') && !isLocalhost) }) } - private getCookie = (filter: CyCookieFilter): Promise => { - return this.getAllCookies(filter) - .then((cookies) => { - return _.get(cookies, 0, null) - }) + private getCookie = async (filter: CyCookieFilter): Promise => { + const cookies = await this.getAllCookies(filter) + + return _.get(cookies, 0, null) } private _updateFrameTree = (client: CriClient, eventName) => async () => { debugVerbose(`update frame tree for ${eventName}`) - this.gettingFrameTree = new Promise(async (resolve) => { + this.gettingFrameTree = (async () => { try { this.frameTree = (await client.send('Page.getFrameTree')).frameTree debugVerbose('frame tree updated') @@ -315,10 +311,8 @@ export class CdpAutomation implements CDPClient, AutomationMiddleware { debugVerbose('failed to update frame tree:', err.stack) } finally { this.gettingFrameTree = null - - resolve() } - }) + })() } private _continueRequest = (client, params, header?) => { @@ -459,19 +453,19 @@ export class CdpAutomation implements CDPClient, AutomationMiddleware { return this.getAllCookies(data) case 'get:cookie': return this.getCookie(data) - case 'set:cookie': + case 'set:cookie': { setCookie = normalizeSetCookieProps(data) - return this.sendDebuggerCommandFn('Network.setCookie', setCookie) - .then((result: Protocol.Network.SetCookieResponse) => { - if (!result.success) { - // i wish CDP provided some more detail here, but this is really it in v1.3 - // @see https://chromedevtools.github.io/devtools-protocol/tot/Network/#method-setCookie - throw new Error(`Network.setCookie failed to set cookie: ${JSON.stringify(setCookie)}`) - } + const result: Protocol.Network.SetCookieResponse = await this.sendDebuggerCommandFn('Network.setCookie', setCookie) - return this.getCookie(data) - }) + if (!result.success) { + // i wish CDP provided some more detail here, but this is really it in v1.3 + // @see https://chromedevtools.github.io/devtools-protocol/tot/Network/#method-setCookie + throw new Error(`Network.setCookie failed to set cookie: ${JSON.stringify(setCookie)}`) + } + + return this.getCookie(data) + } case 'add:cookies': setCookie = data.map((cookie) => normalizeSetCookieProps(cookie)) as Protocol.Network.SetCookieRequest[] @@ -481,46 +475,52 @@ export class CdpAutomation implements CDPClient, AutomationMiddleware { case 'set:cookies': setCookie = data.map((cookie) => normalizeSetCookieProps(cookie)) - return this.sendDebuggerCommandFn('Network.clearBrowserCookies') - .then(() => { - return this.sendDebuggerCommandFn('Network.setCookies', { cookies: setCookie }) - }) + await this.sendDebuggerCommandFn('Network.clearBrowserCookies') - case 'clear:cookie': - return this.getCookie(data) + return this.sendDebuggerCommandFn('Network.setCookies', { cookies: setCookie }) + + case 'clear:cookie': { // always resolve with the value of the removed cookie. also, getting // the cookie via CDP first will ensure that we send a cookie `domain` // to CDP that matches the cookie domain that is really stored - .then((cookieToBeCleared) => { - if (!cookieToBeCleared) { - return cookieToBeCleared - } + const cookieToBeCleared = await this.getCookie(data) - return this.sendDebuggerCommandFn('Network.deleteCookies', _.pick(cookieToBeCleared, 'name', 'domain')) - .then(() => { - return cookieToBeCleared - }) - }) + if (!cookieToBeCleared) { + return cookieToBeCleared + } + + await this.sendDebuggerCommandFn('Network.deleteCookies', _.pick(cookieToBeCleared, 'name', 'domain')) + + return cookieToBeCleared + } + + case 'clear:cookies': { + const clearedCookies: (CyCookie | undefined)[] = [] - case 'clear:cookies': - return Bluebird.mapSeries(data as CyCookieFilter[], async (cookie) => { + for (const cookie of data as CyCookieFilter[]) { // resolve with the value of the removed cookie // also, getting the cookie via CDP first will ensure that we send a cookie `domain` to CDP // that matches the cookie domain that is really stored const cookieToBeCleared = await this.getCookie(cookie) - if (!cookieToBeCleared) return + if (!cookieToBeCleared) { + clearedCookies.push(undefined) + continue + } await this.sendDebuggerCommandFn('Network.deleteCookies', _.pick(cookieToBeCleared, 'name', 'domain')) - return cookieToBeCleared - }) + clearedCookies.push(cookieToBeCleared) + } + + return clearedCookies + } case 'is:automation:client:connected': return true case 'remote:debugger:protocol': return this.sendDebuggerCommandFn(data.command, data.params, data.sessionId) - case 'take:screenshot': + case 'take:screenshot': { debugVerbose('capturing screenshot') if (this.focusTabOnScreenshot) { @@ -531,13 +531,16 @@ export class CdpAutomation implements CDPClient, AutomationMiddleware { } } - return this.sendDebuggerCommandFn('Page.captureScreenshot', { format: 'png' }) - .catch((err) => { + let screenshot: Protocol.Page.CaptureScreenshotResponse + + try { + screenshot = await this.sendDebuggerCommandFn('Page.captureScreenshot', { format: 'png' }) + } catch (err) { throw new Error(`The browser responded with an error when Cypress attempted to take a screenshot.\n\nDetails:\n${err.message}`) - }) - .then(({ data }) => { - return `data:image/png;base64,${data}` - }) + } + + return `data:image/png;base64,${screenshot.data}` + } case 'reset:browser:state': return Promise.all([ // Note that we are omitting `file_systems` as it is very non-performant to clear: