diff --git a/.circleci/config.yml b/.circleci/config.yml index 7e4f7cca19e2..81f6beaae83c 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -47,6 +47,7 @@ linuxArm64WorkflowFilters: &linux-arm64-workflow-filters when: or: - equal: [ develop, << pipeline.git.branch >> ] + - equal: [ 'webkit-multidomain', << pipeline.git.branch >> ] - equal: [ 'issue-23843_electron_21_upgrade', << pipeline.git.branch >> ] - matches: pattern: "-release$" @@ -1856,7 +1857,7 @@ jobs: name: Build command: yarn workspace @cypress/mount-utils build - store-npm-logs - + npm-xpath: <<: *defaults resource_class: small @@ -2437,11 +2438,10 @@ linux-x64-workflow: &linux-x64-workflow context: test-runner:cypress-record-key requires: - build - # TODO: Implement WebKit network automation to fix the majority of these tests before re-enabling - # - driver-integration-tests-webkit-experimentalSessionAndOrigin: - # context: test-runner:cypress-record-key - # requires: - # - build + - driver-integration-tests-webkit-experimentalSessionAndOrigin: + context: test-runner:cypress-record-key + requires: + - build - run-frontend-shared-component-tests-chrome: context: [test-runner:cypress-record-key, test-runner:launchpad-tests, test-runner:percy] percy: true diff --git a/packages/app/src/runner/index.ts b/packages/app/src/runner/index.ts index 54b16d42e082..ace31aa4a8f3 100644 --- a/packages/app/src/runner/index.ts +++ b/packages/app/src/runner/index.ts @@ -22,7 +22,7 @@ import { getRunnerElement, empty } from './utils' import { IframeModel } from './iframe-model' import { AutIframe } from './aut-iframe' import { EventManager } from './event-manager' -import { client } from '@packages/socket/lib/browser' +import { createWebsocket as createWebsocketIo } from '@packages/socket/lib/browser' import { decodeBase64Unicode } from '@packages/frontend-shared/src/utils/base64' import type { AutomationElementId } from '@packages/types/src' import { useSnapshotStore } from './snapshot-store' @@ -31,11 +31,7 @@ import { useStudioStore } from '../store/studio-store' let _eventManager: EventManager | undefined export function createWebsocket (config: Cypress.Config) { - const ws = client({ - path: config.socketIoRoute, - // TODO(webkit): the websocket socket.io transport is busted in WebKit, need polling - transports: config.browser.family === 'webkit' ? ['polling'] : ['websocket'], - }) + const ws = createWebsocketIo({ path: config.socketIoRoute, browserFamily: config.browser.family }) ws.on('connect', () => { ws.emit('runner:connected') diff --git a/packages/driver/cypress/e2e/commands/cookies.cy.js b/packages/driver/cypress/e2e/commands/cookies.cy.js index b739a8a7af8b..5b4872a65765 100644 --- a/packages/driver/cypress/e2e/commands/cookies.cy.js +++ b/packages/driver/cypress/e2e/commands/cookies.cy.js @@ -2,6 +2,25 @@ const { assertLogLength } = require('../../support/utils') const { stripIndent } = require('common-tags') const { Promise } = Cypress +describe('src/cy/commands/cookies - no stub', () => { + it('clears all cookies', () => { + cy.setCookie('foo', 'bar') + cy.getCookies().should('have.length', 1) + cy.clearCookies() + cy.getCookies().should('have.length', 0) + }) + + it('clears a single cookie', () => { + cy.setCookie('foo', 'bar') + cy.setCookie('key', 'val') + cy.getCookies().should('have.length', 2) + cy.clearCookie('foo') + cy.getCookies().should('have.length', 1).then((cookies) => { + expect(cookies[0].name).to.eq('key') + }) + }) +}) + describe('src/cy/commands/cookies', () => { beforeEach(() => { // call through normally on everything diff --git a/packages/driver/cypress/e2e/commands/sessions/sessions.cy.js b/packages/driver/cypress/e2e/commands/sessions/sessions.cy.js index c64965f21e2d..c48f706dea7c 100644 --- a/packages/driver/cypress/e2e/commands/sessions/sessions.cy.js +++ b/packages/driver/cypress/e2e/commands/sessions/sessions.cy.js @@ -24,6 +24,27 @@ const clearAllSavedSessions = () => { }) } +// In webkit, the clear page and clear cookies, etc log messages may be reversed. This isn't an issue, but we just want to test we have both messages. +const validateClearLogs = (logs, sessionGroupId) => { + let clearPageLogIndex = 0 + let clearCookiesIndex = 1 + + if (logs[1].get('name') === 'Clear page') { + clearPageLogIndex = 1 + clearCookiesIndex = 0 + } + + expect(logs[clearPageLogIndex].get()).to.contain({ + name: 'Clear page', + group: sessionGroupId, + }) + + expect(logs[clearCookiesIndex].get()).to.contain({ + displayName: 'Clear cookies, localStorage and sessionStorage', + group: sessionGroupId, + }) +} + describe('cy.session', { retries: 0 }, () => { describe('args', () => { it('accepts string as id', () => { @@ -200,15 +221,7 @@ describe('cy.session', { retries: 0 }, () => { }, }) - expect(logs[1].get()).to.contain({ - name: 'Clear page', - group: sessionGroupId, - }) - - expect(logs[2].get()).to.contain({ - displayName: 'Clear cookies, localStorage and sessionStorage', - group: sessionGroupId, - }) + validateClearLogs([logs[1], logs[2]], sessionGroupId) const createNewSessionGroup = logs[3].get() @@ -282,15 +295,7 @@ describe('cy.session', { retries: 0 }, () => { }, }) - expect(logs[1].get()).to.contain({ - name: 'Clear page', - group: sessionGroupId, - }) - - expect(logs[2].get()).to.contain({ - displayName: 'Clear cookies, localStorage and sessionStorage', - group: sessionGroupId, - }) + validateClearLogs([logs[1], logs[2]], sessionGroupId) const createNewSessionGroup = logs[3].get() @@ -344,15 +349,7 @@ describe('cy.session', { retries: 0 }, () => { }, }) - expect(logs[1].get()).to.contain({ - name: 'Clear page', - group: sessionGroupId, - }) - - expect(logs[2].get()).to.contain({ - displayName: 'Clear cookies, localStorage and sessionStorage', - group: sessionGroupId, - }) + validateClearLogs([logs[1], logs[2]], sessionGroupId) const createNewSessionGroup = logs[3].get() @@ -437,15 +434,7 @@ describe('cy.session', { retries: 0 }, () => { }, }) - expect(logs[1].get()).to.contain({ - name: 'Clear page', - group: sessionGroupId, - }) - - expect(logs[2].get()).to.contain({ - displayName: 'Clear cookies, localStorage and sessionStorage', - group: sessionGroupId, - }) + validateClearLogs([logs[1], logs[2]], sessionGroupId) const restoreSavedSessionGroup = logs[3].get() @@ -500,15 +489,7 @@ describe('cy.session', { retries: 0 }, () => { }, }) - expect(logs[1].get()).to.contain({ - name: 'Clear page', - group: sessionGroupId, - }) - - expect(logs[2].get()).to.contain({ - displayName: 'Clear cookies, localStorage and sessionStorage', - group: sessionGroupId, - }) + validateClearLogs([logs[1], logs[2]], sessionGroupId) const restoreSavedSessionGroup = logs[3].get() @@ -582,15 +563,7 @@ describe('cy.session', { retries: 0 }, () => { }, }) - expect(logs[1].get()).to.contain({ - name: 'Clear page', - group: sessionGroupId, - }) - - expect(logs[2].get()).to.contain({ - displayName: 'Clear cookies, localStorage and sessionStorage', - group: sessionGroupId, - }) + validateClearLogs([logs[1], logs[2]], sessionGroupId) const restoreSavedSessionGroup = logs[3].get() @@ -618,15 +591,7 @@ describe('cy.session', { retries: 0 }, () => { expect(logs[6].get('error').message).to.eq('Your `cy.session` **validate** callback returned false.') - expect(logs[7].get()).to.contain({ - name: 'Clear page', - group: sessionGroupId, - }) - - expect(logs[8].get()).to.contain({ - displayName: 'Clear cookies, localStorage and sessionStorage', - group: sessionGroupId, - }) + validateClearLogs([logs[7], logs[8]], sessionGroupId) const createNewSessionGroup = logs[9].get() @@ -692,15 +657,7 @@ describe('cy.session', { retries: 0 }, () => { }, }) - expect(logs[1].get()).to.contain({ - name: 'Clear page', - group: sessionGroupId, - }) - - expect(logs[2].get()).to.contain({ - displayName: 'Clear cookies, localStorage and sessionStorage', - group: sessionGroupId, - }) + validateClearLogs([logs[1], logs[2]], sessionGroupId) const restoreSavedSessionGroup = logs[3].get() @@ -728,15 +685,7 @@ describe('cy.session', { retries: 0 }, () => { expect(logs[6].get('error').message).to.eq('Your `cy.session` **validate** callback returned false.') - expect(logs[7].get()).to.contain({ - name: 'Clear page', - group: sessionGroupId, - }) - - expect(logs[8].get()).to.contain({ - displayName: 'Clear cookies, localStorage and sessionStorage', - group: sessionGroupId, - }) + validateClearLogs([logs[7], logs[8]], sessionGroupId) const createNewSessionGroup = logs[9].get() @@ -1046,7 +995,10 @@ describe('cy.session', { retries: 0 }, () => { cy.once('fail', (err) => { expect(err.message).contain('Expected to find element: `#does_not_exist`') expect(err.message).contain(errorHookMessage) - expect(err.codeFrame).exist + // TODO: Webkit does not have correct stack traces on errors currently + if (Cypress.isBrowser('!webkit')) { + expect(err.codeFrame).exist + } done() }) @@ -1064,7 +1016,11 @@ describe('cy.session', { retries: 0 }, () => { cy.once('fail', (err) => { expect(err.message).contain('validate error') expect(err.message).contain(errorHookMessage) - expect(err.codeFrame).exist + // TODO: Webkit does not have correct stack traces on errors currently + if (Cypress.isBrowser('!webkit')) { + expect(err.codeFrame).exist + } + done() }) @@ -1081,7 +1037,10 @@ describe('cy.session', { retries: 0 }, () => { cy.once('fail', (err) => { expect(err.message).contain('validate error') expect(err.message).contain(errorHookMessage) - expect(err.codeFrame).exist + // TODO: Webkit does not have correct stack traces on errors currently + if (Cypress.isBrowser('!webkit')) { + expect(err.codeFrame).exist + } done() }) @@ -1099,7 +1058,10 @@ describe('cy.session', { retries: 0 }, () => { cy.once('fail', (err) => { expect(err.message).to.contain('Your `cy.session` **validate** callback returned false.') expect(err.message).contain(errorHookMessage) - expect(err.codeFrame).exist + // TODO: Webkit does not have correct stack traces on errors currently + if (Cypress.isBrowser('!webkit')) { + expect(err.codeFrame).exist + } done() }) @@ -1117,7 +1079,11 @@ describe('cy.session', { retries: 0 }, () => { cy.once('fail', (err) => { expect(err.message).to.contain('Your `cy.session` **validate** callback resolved false.') expect(err.message).contain(errorHookMessage) - expect(err.codeFrame).exist + // TODO: Webkit does not have correct stack traces on errors currently + if (Cypress.isBrowser('!webkit')) { + expect(err.codeFrame).exist + } + done() }) diff --git a/packages/driver/cypress/e2e/e2e/origin/basic_login.cy.ts b/packages/driver/cypress/e2e/e2e/origin/basic_login.cy.ts index f70894cf2c46..458797b62eb4 100644 --- a/packages/driver/cypress/e2e/e2e/origin/basic_login.cy.ts +++ b/packages/driver/cypress/e2e/e2e/origin/basic_login.cy.ts @@ -1,4 +1,4 @@ -describe('basic login', () => { +describe('basic login', { browser: '!webkit' }, () => { // Scenario, Token based auth. Visit site, redirect to IDP hosted on secondary origin, login and redirect back to site. describe('visit primary first', () => { it('logs in with idp redirect', () => { @@ -148,7 +148,7 @@ describe('basic login', () => { }) }) -describe('Multi-step Auth', () => { +describe('Multi-step Auth', { browser: '!webkit' }, () => { // TODO: cy.origin does not work in cy.origin yet. it.skip('final auth redirects back to localhost - nested', () => { cy.visit('/fixtures/auth/index.html') diff --git a/packages/driver/cypress/e2e/e2e/origin/commands/actions.cy.ts b/packages/driver/cypress/e2e/e2e/origin/commands/actions.cy.ts index ac5324b31d31..2dde6a666876 100644 --- a/packages/driver/cypress/e2e/e2e/origin/commands/actions.cy.ts +++ b/packages/driver/cypress/e2e/e2e/origin/commands/actions.cy.ts @@ -1,6 +1,6 @@ import { findCrossOriginLogs } from '../../../../support/utils' -context('cy.origin actions', () => { +context('cy.origin actions', { browser: '!webkit' }, () => { beforeEach(() => { cy.visit('/fixtures/primary-origin.html') }) diff --git a/packages/driver/cypress/e2e/e2e/origin/commands/aliasing.cy.ts b/packages/driver/cypress/e2e/e2e/origin/commands/aliasing.cy.ts index 2d6f77ebbc9d..4c7557279a6a 100644 --- a/packages/driver/cypress/e2e/e2e/origin/commands/aliasing.cy.ts +++ b/packages/driver/cypress/e2e/e2e/origin/commands/aliasing.cy.ts @@ -1,6 +1,6 @@ import { findCrossOriginLogs } from '../../../../support/utils' -context('cy.origin aliasing', () => { +context('cy.origin aliasing', { browser: '!webkit' }, () => { beforeEach(() => { cy.visit('/fixtures/primary-origin.html') }) diff --git a/packages/driver/cypress/e2e/e2e/origin/commands/assertions.cy.ts b/packages/driver/cypress/e2e/e2e/origin/commands/assertions.cy.ts index 6e12063ae998..5556787f8e79 100644 --- a/packages/driver/cypress/e2e/e2e/origin/commands/assertions.cy.ts +++ b/packages/driver/cypress/e2e/e2e/origin/commands/assertions.cy.ts @@ -1,6 +1,6 @@ import { findCrossOriginLogs } from '../../../../support/utils' -context('cy.origin assertions', () => { +context('cy.origin assertions', { browser: '!webkit' }, () => { beforeEach(() => { cy.visit('/fixtures/primary-origin.html') cy.get('a[data-cy="dom-link"]').click() diff --git a/packages/driver/cypress/e2e/e2e/origin/commands/connectors.cy.ts b/packages/driver/cypress/e2e/e2e/origin/commands/connectors.cy.ts index ab8690605a3b..0565a2b702d6 100644 --- a/packages/driver/cypress/e2e/e2e/origin/commands/connectors.cy.ts +++ b/packages/driver/cypress/e2e/e2e/origin/commands/connectors.cy.ts @@ -1,6 +1,6 @@ import { findCrossOriginLogs } from '../../../../support/utils' -context('cy.origin connectors', () => { +context('cy.origin connectors', { browser: '!webkit' }, () => { beforeEach(() => { cy.visit('/fixtures/primary-origin.html') cy.get('a[data-cy="dom-link"]').click() diff --git a/packages/driver/cypress/e2e/e2e/origin/commands/cookies.cy.ts b/packages/driver/cypress/e2e/e2e/origin/commands/cookies.cy.ts index 4887980fdc22..6585492eb5c5 100644 --- a/packages/driver/cypress/e2e/e2e/origin/commands/cookies.cy.ts +++ b/packages/driver/cypress/e2e/e2e/origin/commands/cookies.cy.ts @@ -1,6 +1,6 @@ import { findCrossOriginLogs, assertLogLength } from '../../../../support/utils' -describe('cy.origin cookies', () => { +describe('cy.origin cookies', { browser: '!webkit' }, () => { context('client side', () => { beforeEach(() => { cy.visit('/fixtures/primary-origin.html') diff --git a/packages/driver/cypress/e2e/e2e/origin/commands/files.cy.ts b/packages/driver/cypress/e2e/e2e/origin/commands/files.cy.ts index ec90c2d88038..d708b9b9fc94 100644 --- a/packages/driver/cypress/e2e/e2e/origin/commands/files.cy.ts +++ b/packages/driver/cypress/e2e/e2e/origin/commands/files.cy.ts @@ -1,6 +1,6 @@ import { findCrossOriginLogs } from '../../../../support/utils' -context('cy.origin files', () => { +context('cy.origin files', { browser: '!webkit' }, () => { beforeEach(() => { cy.visit('/fixtures/primary-origin.html') cy.get('a[data-cy="cross-origin-secondary-link"]').click() diff --git a/packages/driver/cypress/e2e/e2e/origin/commands/local_storage.cy.ts b/packages/driver/cypress/e2e/e2e/origin/commands/local_storage.cy.ts index 7debeaa90432..9b5137ea27d8 100644 --- a/packages/driver/cypress/e2e/e2e/origin/commands/local_storage.cy.ts +++ b/packages/driver/cypress/e2e/e2e/origin/commands/local_storage.cy.ts @@ -1,6 +1,6 @@ import { findCrossOriginLogs } from '../../../../support/utils' -context('cy.origin local storage', () => { +context('cy.origin local storage', { browser: '!webkit' }, () => { beforeEach(() => { cy.visit('/fixtures/primary-origin.html') cy.get('a[data-cy="cross-origin-secondary-link"]').click() diff --git a/packages/driver/cypress/e2e/e2e/origin/commands/location.cy.ts b/packages/driver/cypress/e2e/e2e/origin/commands/location.cy.ts index 9072399b4139..3d6869c28980 100644 --- a/packages/driver/cypress/e2e/e2e/origin/commands/location.cy.ts +++ b/packages/driver/cypress/e2e/e2e/origin/commands/location.cy.ts @@ -1,6 +1,6 @@ import { findCrossOriginLogs } from '../../../../support/utils' -context('cy.origin location', () => { +context('cy.origin location', { browser: '!webkit' }, () => { beforeEach(() => { cy.visit('/fixtures/primary-origin.html') cy.get('a[data-cy="cross-origin-secondary-link"]').click() diff --git a/packages/driver/cypress/e2e/e2e/origin/commands/log.cy.ts b/packages/driver/cypress/e2e/e2e/origin/commands/log.cy.ts index 2db1a85c41a7..93b3e8eacdff 100644 --- a/packages/driver/cypress/e2e/e2e/origin/commands/log.cy.ts +++ b/packages/driver/cypress/e2e/e2e/origin/commands/log.cy.ts @@ -1,6 +1,6 @@ import { assertLogLength } from '../../../../support/utils' -context('cy.origin log', () => { +context('cy.origin log', { browser: '!webkit' }, () => { let logs: any = [] let lastTestLogId = '' diff --git a/packages/driver/cypress/e2e/e2e/origin/commands/misc.cy.ts b/packages/driver/cypress/e2e/e2e/origin/commands/misc.cy.ts index cce9a63d8eb9..a58f90aca3c2 100644 --- a/packages/driver/cypress/e2e/e2e/origin/commands/misc.cy.ts +++ b/packages/driver/cypress/e2e/e2e/origin/commands/misc.cy.ts @@ -1,6 +1,6 @@ import { findCrossOriginLogs } from '../../../../support/utils' -context('cy.origin misc', () => { +context('cy.origin misc', { browser: '!webkit' }, () => { beforeEach(() => { cy.visit('/fixtures/primary-origin.html') cy.get('a[data-cy="dom-link"]').click() diff --git a/packages/driver/cypress/e2e/e2e/origin/commands/navigation.cy.ts b/packages/driver/cypress/e2e/e2e/origin/commands/navigation.cy.ts index 5d631cb481e0..3119226458d1 100644 --- a/packages/driver/cypress/e2e/e2e/origin/commands/navigation.cy.ts +++ b/packages/driver/cypress/e2e/e2e/origin/commands/navigation.cy.ts @@ -1,7 +1,7 @@ const { stripIndent } = require('common-tags') import { findCrossOriginLogs } from '../../../../support/utils' -context('cy.origin navigation', () => { +context('cy.origin navigation', { browser: '!webkit' }, () => { it('.go()', () => { cy.visit('/fixtures/primary-origin.html') cy.get('a[data-cy="cross-origin-secondary-link"]').click() diff --git a/packages/driver/cypress/e2e/e2e/origin/commands/network_requests.cy.ts b/packages/driver/cypress/e2e/e2e/origin/commands/network_requests.cy.ts index 7e05cfa03a90..c52a81b96597 100644 --- a/packages/driver/cypress/e2e/e2e/origin/commands/network_requests.cy.ts +++ b/packages/driver/cypress/e2e/e2e/origin/commands/network_requests.cy.ts @@ -1,6 +1,6 @@ import { findCrossOriginLogs } from '../../../../support/utils' -context('cy.origin network requests', () => { +context('cy.origin network requests', { browser: '!webkit' }, () => { beforeEach(() => { cy.visit('/fixtures/primary-origin.html') cy.get('a[data-cy="request-link"]').click() diff --git a/packages/driver/cypress/e2e/e2e/origin/commands/querying.cy.ts b/packages/driver/cypress/e2e/e2e/origin/commands/querying.cy.ts index 615d5fdadd64..adf072a16a47 100644 --- a/packages/driver/cypress/e2e/e2e/origin/commands/querying.cy.ts +++ b/packages/driver/cypress/e2e/e2e/origin/commands/querying.cy.ts @@ -1,6 +1,6 @@ import { findCrossOriginLogs } from '../../../../support/utils' -context('cy.origin querying', () => { +context('cy.origin querying', { browser: '!webkit' }, () => { beforeEach(() => { cy.visit('/fixtures/primary-origin.html') cy.get('a[data-cy="dom-link"]').click() diff --git a/packages/driver/cypress/e2e/e2e/origin/commands/querying_shadow.cy.ts b/packages/driver/cypress/e2e/e2e/origin/commands/querying_shadow.cy.ts index 60077167cb1a..d6dfde203219 100644 --- a/packages/driver/cypress/e2e/e2e/origin/commands/querying_shadow.cy.ts +++ b/packages/driver/cypress/e2e/e2e/origin/commands/querying_shadow.cy.ts @@ -1,6 +1,6 @@ import { findCrossOriginLogs } from '../../../../support/utils' -context('cy.origin shadow dom', () => { +context('cy.origin shadow dom', { browser: '!webkit' }, () => { beforeEach(() => { cy.visit('/fixtures/primary-origin.html') cy.get('a[data-cy="shadow-dom-link"]').click() diff --git a/packages/driver/cypress/e2e/e2e/origin/commands/screenshot.cy.ts b/packages/driver/cypress/e2e/e2e/origin/commands/screenshot.cy.ts index e39368c6bff5..d87d49429a23 100644 --- a/packages/driver/cypress/e2e/e2e/origin/commands/screenshot.cy.ts +++ b/packages/driver/cypress/e2e/e2e/origin/commands/screenshot.cy.ts @@ -1,4 +1,4 @@ -context('cy.origin screenshot', () => { +context('cy.origin screenshot', { browser: '!webkit' }, () => { const { devicePixelRatio } = window context('set viewport', () => { diff --git a/packages/driver/cypress/e2e/e2e/origin/commands/spies_stubs_clocks.cy.ts b/packages/driver/cypress/e2e/e2e/origin/commands/spies_stubs_clocks.cy.ts index 3634e120004c..74f821c86127 100644 --- a/packages/driver/cypress/e2e/e2e/origin/commands/spies_stubs_clocks.cy.ts +++ b/packages/driver/cypress/e2e/e2e/origin/commands/spies_stubs_clocks.cy.ts @@ -1,6 +1,6 @@ import { findCrossOriginLogs } from '../../../../support/utils' -context('cy.origin spies, stubs, and clock', () => { +context('cy.origin spies, stubs, and clock', { browser: '!webkit' }, () => { beforeEach(() => { cy.visit('/fixtures/primary-origin.html') cy.get('a[data-cy="cross-origin-secondary-link"]').click() diff --git a/packages/driver/cypress/e2e/e2e/origin/commands/traversal.cy.ts b/packages/driver/cypress/e2e/e2e/origin/commands/traversal.cy.ts index af34204be873..8ca80e6720c1 100644 --- a/packages/driver/cypress/e2e/e2e/origin/commands/traversal.cy.ts +++ b/packages/driver/cypress/e2e/e2e/origin/commands/traversal.cy.ts @@ -1,6 +1,6 @@ import { findCrossOriginLogs } from '../../../../support/utils' -context('cy.origin traversal', () => { +context('cy.origin traversal', { browser: '!webkit' }, () => { beforeEach(() => { cy.visit('/fixtures/primary-origin.html') cy.get('a[data-cy="dom-link"]').click() diff --git a/packages/driver/cypress/e2e/e2e/origin/commands/unsupported_commands.cy.ts b/packages/driver/cypress/e2e/e2e/origin/commands/unsupported_commands.cy.ts index c10d24347bf8..2d87ce6735d0 100644 --- a/packages/driver/cypress/e2e/e2e/origin/commands/unsupported_commands.cy.ts +++ b/packages/driver/cypress/e2e/e2e/origin/commands/unsupported_commands.cy.ts @@ -1,4 +1,4 @@ -context('cy.origin unsupported commands', () => { +context('cy.origin unsupported commands', { browser: '!webkit' }, () => { beforeEach(() => { cy.visit('/fixtures/primary-origin.html') cy.get('a[data-cy="cross-origin-secondary-link"]').click() diff --git a/packages/driver/cypress/e2e/e2e/origin/commands/viewport.cy.ts b/packages/driver/cypress/e2e/e2e/origin/commands/viewport.cy.ts index 75ed312a0ee4..cd935c46adda 100644 --- a/packages/driver/cypress/e2e/e2e/origin/commands/viewport.cy.ts +++ b/packages/driver/cypress/e2e/e2e/origin/commands/viewport.cy.ts @@ -1,6 +1,6 @@ import { findCrossOriginLogs } from '../../../../support/utils' -context('cy.origin viewport', () => { +context('cy.origin viewport', { browser: '!webkit' }, () => { it('syncs the viewport from the primary to secondary', () => { // change the viewport in the primary first cy.viewport(320, 480) diff --git a/packages/driver/cypress/e2e/e2e/origin/commands/waiting.cy.ts b/packages/driver/cypress/e2e/e2e/origin/commands/waiting.cy.ts index 9e8c2572a11f..f6fbea48f06a 100644 --- a/packages/driver/cypress/e2e/e2e/origin/commands/waiting.cy.ts +++ b/packages/driver/cypress/e2e/e2e/origin/commands/waiting.cy.ts @@ -22,7 +22,7 @@ const abortRequests = () => { reqQueue = [] } -context('cy.origin waiting', () => { +context('cy.origin waiting', { browser: '!webkit' }, () => { before(() => { cy.origin('http://www.foobar.com:3500', () => { let reqQueue: XMLHttpRequest[] = [] diff --git a/packages/driver/cypress/e2e/e2e/origin/commands/window.cy.ts b/packages/driver/cypress/e2e/e2e/origin/commands/window.cy.ts index 2f7a4d835c0d..47cb717ffb87 100644 --- a/packages/driver/cypress/e2e/e2e/origin/commands/window.cy.ts +++ b/packages/driver/cypress/e2e/e2e/origin/commands/window.cy.ts @@ -1,6 +1,6 @@ import { findCrossOriginLogs } from '../../../../support/utils' -context('cy.origin window', () => { +context('cy.origin window', { browser: '!webkit' }, () => { beforeEach(() => { cy.visit('/fixtures/primary-origin.html') cy.get('a[data-cy="dom-link"]').click() diff --git a/packages/driver/cypress/e2e/e2e/origin/config_env.cy.ts b/packages/driver/cypress/e2e/e2e/origin/config_env.cy.ts index a6df37a4b7a2..58b06e242096 100644 --- a/packages/driver/cypress/e2e/e2e/origin/config_env.cy.ts +++ b/packages/driver/cypress/e2e/e2e/origin/config_env.cy.ts @@ -1,5 +1,5 @@ ['config', 'env'].forEach((fnName) => { - describe(`cy.origin- Cypress.${fnName}()`, () => { + describe(`cy.origin- Cypress.${fnName}()`, { browser: '!webkit' }, () => { const USED_KEYS = { foo: 'cy-origin-foo', bar: 'cy-origin-bar', diff --git a/packages/driver/cypress/e2e/e2e/origin/cookie_behavior.cy.ts b/packages/driver/cypress/e2e/e2e/origin/cookie_behavior.cy.ts index 370853db3575..91f3decaa515 100644 --- a/packages/driver/cypress/e2e/e2e/origin/cookie_behavior.cy.ts +++ b/packages/driver/cypress/e2e/e2e/origin/cookie_behavior.cy.ts @@ -1,4 +1,4 @@ -describe('Cookie Behavior with experimentalSessionAndOrigin=true', () => { +describe('Cookie Behavior with experimentalSessionAndOrigin=true', { browser: '!webkit' }, () => { const makeRequest = ( win: Cypress.AUTWindow, url: string, diff --git a/packages/driver/cypress/e2e/e2e/origin/cookie_login.cy.ts b/packages/driver/cypress/e2e/e2e/origin/cookie_login.cy.ts index b56425d24dd8..aa6fe91bc2f3 100644 --- a/packages/driver/cypress/e2e/e2e/origin/cookie_login.cy.ts +++ b/packages/driver/cypress/e2e/e2e/origin/cookie_login.cy.ts @@ -1,6 +1,6 @@ import dayjs from 'dayjs' -describe('cy.origin - cookie login', () => { +describe('cy.origin - cookie login', { browser: '!webkit' }, () => { const { _ } = Cypress // ensures unique username so there's no risk of false positives from // test pollution @@ -71,7 +71,7 @@ describe('cy.origin - cookie login', () => { • displays "Welcome, " ****************************************************************************/ - describe('general behavior', () => { + describe('general behavior', { browser: '!webkit' }, () => { let username beforeEach(() => { diff --git a/packages/driver/cypress/e2e/e2e/origin/cypress_api.cy.ts b/packages/driver/cypress/e2e/e2e/origin/cypress_api.cy.ts index 2885165f439f..22f15eca9a7e 100644 --- a/packages/driver/cypress/e2e/e2e/origin/cypress_api.cy.ts +++ b/packages/driver/cypress/e2e/e2e/origin/cypress_api.cy.ts @@ -1,6 +1,6 @@ const { assertLogLength } = require('../../../support/utils') -describe('cy.origin Cypress API', () => { +describe('cy.origin Cypress API', { browser: '!webkit' }, () => { beforeEach(() => { cy.visit('/fixtures/primary-origin.html') cy.get('a[data-cy="cross-origin-secondary-link"]').click() diff --git a/packages/driver/cypress/e2e/e2e/origin/dependencies.cy.jsx b/packages/driver/cypress/e2e/e2e/origin/dependencies.cy.jsx index 309e4fb43ad9..872827dcfadc 100644 --- a/packages/driver/cypress/e2e/e2e/origin/dependencies.cy.jsx +++ b/packages/driver/cypress/e2e/e2e/origin/dependencies.cy.jsx @@ -1,4 +1,4 @@ -describe('cy.origin dependencies - jsx', () => { +describe('cy.origin dependencies - jsx', { browser: '!webkit' }, () => { beforeEach(() => { cy.visit('/fixtures/primary-origin.html') cy.get('a[data-cy="cross-origin-secondary-link"]').click() diff --git a/packages/driver/cypress/e2e/e2e/origin/dependencies.cy.ts b/packages/driver/cypress/e2e/e2e/origin/dependencies.cy.ts index 860af3a5f930..f1ff753741c8 100644 --- a/packages/driver/cypress/e2e/e2e/origin/dependencies.cy.ts +++ b/packages/driver/cypress/e2e/e2e/origin/dependencies.cy.ts @@ -1,4 +1,4 @@ -describe('cy.origin dependencies', () => { +describe('cy.origin dependencies', { browser: '!webkit' }, () => { beforeEach(() => { cy.visit('/fixtures/primary-origin.html') cy.get('a[data-cy="cross-origin-secondary-link"]').click() diff --git a/packages/driver/cypress/e2e/e2e/origin/events.cy.ts b/packages/driver/cypress/e2e/e2e/origin/events.cy.ts index 4889eb7ea7ea..e0c80a28bdb8 100644 --- a/packages/driver/cypress/e2e/e2e/origin/events.cy.ts +++ b/packages/driver/cypress/e2e/e2e/origin/events.cy.ts @@ -1,4 +1,4 @@ -describe('cy.origin', () => { +describe('cy.origin', { browser: '!webkit' }, () => { it('window:before:load event', () => { cy.visit('/fixtures/primary-origin.html') cy.on('window:before:load', (win: {testPrimaryOriginBeforeLoad: boolean}) => { diff --git a/packages/driver/cypress/e2e/e2e/origin/integrity.cy.ts b/packages/driver/cypress/e2e/e2e/origin/integrity.cy.ts index bb1f048eefe5..46f3802fb382 100644 --- a/packages/driver/cypress/e2e/e2e/origin/integrity.cy.ts +++ b/packages/driver/cypress/e2e/e2e/origin/integrity.cy.ts @@ -4,7 +4,7 @@ import type { TemplateExecutor } from 'lodash' // NOTE: in order to run these tests, the following config flags need to be set // experimentalSessionAndOrigin=true // experimentalModifyObstructiveThirdPartyCode=true -describe('Integrity Preservation', () => { +describe('Integrity Preservation', { browser: '!webkit' }, () => { // Add common SRI hashes used when setting script/link integrity. // These are the ones supported by SRI (see https://developer.mozilla.org/en-US/docs/Web/Security/Subresource_Integrity#using_subresource_integrity) // For our tests, we will use CryptoJS to calculate these hashes as they can regenerate the integrity without us having to do it manually every diff --git a/packages/driver/cypress/e2e/e2e/origin/logging.cy.ts b/packages/driver/cypress/e2e/e2e/origin/logging.cy.ts index 7e0214dd563f..3fa2ca3f34b4 100644 --- a/packages/driver/cypress/e2e/e2e/origin/logging.cy.ts +++ b/packages/driver/cypress/e2e/e2e/origin/logging.cy.ts @@ -1,4 +1,4 @@ -describe('cy.origin logging', () => { +describe('cy.origin logging', { browser: '!webkit' }, () => { const { _ } = Cypress it('groups callback commands on a passing test', () => { diff --git a/packages/driver/cypress/e2e/e2e/origin/navigation.cy.ts b/packages/driver/cypress/e2e/e2e/origin/navigation.cy.ts index 8d12f03066e3..835f253b19fb 100644 --- a/packages/driver/cypress/e2e/e2e/origin/navigation.cy.ts +++ b/packages/driver/cypress/e2e/e2e/origin/navigation.cy.ts @@ -11,7 +11,7 @@ const reifyLogs = (logs) => { }) } -describe('navigation events', () => { +describe('navigation events', { browser: '!webkit' }, () => { let logs: any = [] beforeEach(() => { @@ -185,7 +185,7 @@ describe('navigation events', () => { }) // @ts-ignore / session support is needed for visiting about:blank between tests -describe('event timing', () => { +describe('event timing', { browser: '!webkit' }, () => { it('does not timeout when receiving a delaying:html event after cy.origin has started, but before the spec bridge is ready', () => { cy.visit('/fixtures/primary-origin.html') cy.get('a[data-cy="cross-origin-secondary-link"]').click() @@ -206,7 +206,7 @@ describe('event timing', () => { }) // @ts-ignore / session support is needed for visiting about:blank between tests -describe('delayed navigation', { defaultCommandTimeout: 2000 }, () => { +describe('delayed navigation', { browser: '!webkit' }, { defaultCommandTimeout: 2000 }, () => { it('localhost -> localhost', () => { cy.visit('/fixtures/auth/delayedNavigate.html') cy.get('[data-cy="to-localhost"]').click() @@ -245,7 +245,7 @@ describe('delayed navigation', { defaultCommandTimeout: 2000 }, () => { }) // @ts-ignore / session support is needed for visiting about:blank between tests -describe('errors', () => { +describe('errors', { browser: '!webkit' }, () => { it('never calls cy.origin', { defaultCommandTimeout: 50 }, (done) => { cy.on('fail', (err) => { expect(err.message).to.include(`Timed out retrying after 50ms:`) diff --git a/packages/driver/cypress/e2e/e2e/origin/origin.cy.ts b/packages/driver/cypress/e2e/e2e/origin/origin.cy.ts index aa696056f255..9f200bd79e8a 100644 --- a/packages/driver/cypress/e2e/e2e/origin/origin.cy.ts +++ b/packages/driver/cypress/e2e/e2e/origin/origin.cy.ts @@ -1,4 +1,4 @@ -describe('cy.origin', () => { +describe('cy.origin', { browser: '!webkit' }, () => { it('passes viewportWidth/Height state to the secondary origin', () => { const expectedViewport = [320, 480] @@ -224,7 +224,9 @@ describe('cy.origin', () => { describe('errors', () => { it('propagates secondary origin errors to the primary that occur within the test', (done) => { cy.on('fail', (err) => { - expect(err.message).to.include('variable is not defined') + const undefinedMessage = Cypress.isBrowser('webkit') ? 'Can\'t find variable: variable' : 'variable is not defined' + + expect(err.message).to.include(undefinedMessage) expect(err.message).to.include(`Variables must either be defined within the \`cy.origin()\` command or passed in using the args option.`) expect(err.stack).to.include(`Variables must either be defined within the \`cy.origin()\` command or passed in using the args option.`) // make sure that the secondary origin failures do NOT show up as spec failures or AUT failures diff --git a/packages/driver/cypress/e2e/e2e/origin/patches.cy.ts b/packages/driver/cypress/e2e/e2e/origin/patches.cy.ts index d87511a5fedd..d917b1fdfabe 100644 --- a/packages/driver/cypress/e2e/e2e/origin/patches.cy.ts +++ b/packages/driver/cypress/e2e/e2e/origin/patches.cy.ts @@ -1,4 +1,4 @@ -describe('src/cross-origin/patches', () => { +describe('src/cross-origin/patches', { browser: '!webkit' }, () => { context('submit', () => { beforeEach(() => { cy.visit('/fixtures/primary-origin.html') diff --git a/packages/driver/cypress/e2e/e2e/origin/rerun.cy.ts b/packages/driver/cypress/e2e/e2e/origin/rerun.cy.ts index 75a0e5e2bdd8..641119c8a641 100644 --- a/packages/driver/cypress/e2e/e2e/origin/rerun.cy.ts +++ b/packages/driver/cypress/e2e/e2e/origin/rerun.cy.ts @@ -1,5 +1,5 @@ // @ts-ignore -describe('cy.origin - rerun', { }, () => { +describe('cy.origin - rerun', { browser: '!webkit' }, () => { beforeEach(() => { cy.visit('/fixtures/primary-origin.html') cy.get('a[data-cy="cross-origin-secondary-link"]').click() diff --git a/packages/driver/cypress/e2e/e2e/origin/snapshots.cy.ts b/packages/driver/cypress/e2e/e2e/origin/snapshots.cy.ts index 957fc57b0d3d..49da844ba0f6 100644 --- a/packages/driver/cypress/e2e/e2e/origin/snapshots.cy.ts +++ b/packages/driver/cypress/e2e/e2e/origin/snapshots.cy.ts @@ -1,7 +1,7 @@ // import to bind shouldWithTimeout into global cy commands import '../../../support/utils' -describe('cy.origin - snapshots', () => { +describe('cy.origin - snapshots', { browser: '!webkit' }, () => { const findLog = (logMap: Map, displayName: string, url: string) => { return Array.from(logMap.values()).find((log: any) => { const props = log.get() diff --git a/packages/driver/cypress/e2e/e2e/origin/spec_bridge.cy.ts b/packages/driver/cypress/e2e/e2e/origin/spec_bridge.cy.ts index 5c885b25bb04..2cab63f6f229 100644 --- a/packages/driver/cypress/e2e/e2e/origin/spec_bridge.cy.ts +++ b/packages/driver/cypress/e2e/e2e/origin/spec_bridge.cy.ts @@ -1,4 +1,4 @@ -it('visits foobar.com and types foobar inside an input', () => { +it('visits foobar.com and types foobar inside an input', { browser: '!webkit' }, () => { cy.visit('/fixtures/primary-origin.html') cy.get('[data-cy="cross-origin-secondary-link"]').click() diff --git a/packages/driver/cypress/e2e/e2e/origin/uncaught_errors.cy.ts b/packages/driver/cypress/e2e/e2e/origin/uncaught_errors.cy.ts index 6efefc99b559..fa5007da7b34 100644 --- a/packages/driver/cypress/e2e/e2e/origin/uncaught_errors.cy.ts +++ b/packages/driver/cypress/e2e/e2e/origin/uncaught_errors.cy.ts @@ -1,4 +1,4 @@ -describe('cy.origin - uncaught errors', () => { +describe('cy.origin - uncaught errors', { browser: '!webkit' }, () => { beforeEach(() => { cy.visit('/fixtures/primary-origin.html') cy.get('a[data-cy="errors-link"]').click() diff --git a/packages/driver/cypress/e2e/e2e/origin/validation.cy.ts b/packages/driver/cypress/e2e/e2e/origin/validation.cy.ts index 58e32b1368b1..8cc7320fffa4 100644 --- a/packages/driver/cypress/e2e/e2e/origin/validation.cy.ts +++ b/packages/driver/cypress/e2e/e2e/origin/validation.cy.ts @@ -1,4 +1,4 @@ -describe('cy.origin', () => { +describe('cy.origin', { browser: '!webkit' }, () => { describe('successes', () => { beforeEach(() => { // TODO: There seems to be a limit of 15 active spec bridges during a given test. diff --git a/packages/driver/cypress/e2e/e2e/origin/yield.cy.ts b/packages/driver/cypress/e2e/e2e/origin/yield.cy.ts index 953bade551bf..572d69ea94cb 100644 --- a/packages/driver/cypress/e2e/e2e/origin/yield.cy.ts +++ b/packages/driver/cypress/e2e/e2e/origin/yield.cy.ts @@ -1,6 +1,6 @@ import { assertLogLength } from '../../../support/utils' -describe('cy.origin yields', () => { +describe('cy.origin yields', { browser: '!webkit' }, () => { let logs: any = [] beforeEach(() => { diff --git a/packages/driver/cypress/e2e/webkit.cy.ts b/packages/driver/cypress/e2e/webkit.cy.ts index 36da5ab9c4d5..8a6bb4e269c1 100644 --- a/packages/driver/cypress/e2e/webkit.cy.ts +++ b/packages/driver/cypress/e2e/webkit.cy.ts @@ -9,17 +9,7 @@ describe('WebKit-specific behavior', { browser: 'webkit' }, () => { cy.origin('foo', () => {}) }) - it('cy.session() is disabled', (done) => { - cy.on('fail', (err) => { - expect(err.message).to.equal('`cy.session()` is not currently supported in experimental WebKit.') - expect(err.docsUrl).to.equal('https://on.cypress.io/webkit-experiment') - done() - }) - - cy.session('foo', () => {}) - }) - - it('cy.session() is disabled', (done) => { + it('forceNetworkError intercept option is disabled', (done) => { cy.on('fail', (err) => { expect(err.message).to.include('`forceNetworkError` was passed, but it is not currently supported in experimental WebKit.') expect(err.docsUrl).to.equal('https://on.cypress.io/intercept') diff --git a/packages/driver/src/cross-origin/events/socket.ts b/packages/driver/src/cross-origin/events/socket.ts index 52e28b54f0e7..05663ff13d0e 100644 --- a/packages/driver/src/cross-origin/events/socket.ts +++ b/packages/driver/src/cross-origin/events/socket.ts @@ -1,10 +1,9 @@ -import { client } from '@packages/socket/lib/browser' +import { createWebsocket } from '@packages/socket/lib/browser' export const handleSocketEvents = (Cypress) => { - const webSocket = client({ - path: Cypress.config('socketIoRoute'), - transports: ['websocket'], - }).connect() + const webSocket = createWebsocket({ path: Cypress.config('socketIoRoute'), browserFamily: Cypress.config('browser').family }) + + webSocket.connect() const onBackendRequest = (...args) => { webSocket.emit('backend:request', ...args) diff --git a/packages/driver/src/cy/commands/sessions/index.ts b/packages/driver/src/cy/commands/sessions/index.ts index 2296ea77e077..51629c51746d 100644 --- a/packages/driver/src/cy/commands/sessions/index.ts +++ b/packages/driver/src/cy/commands/sessions/index.ts @@ -25,10 +25,6 @@ export default function (Commands, Cypress, cy) { // @ts-ignore function throwIfNoSessionSupport () { - if (Cypress.isBrowser('webkit')) { - $errUtils.throwErrByPath('webkit.session') - } - if (!Cypress.config('experimentalSessionAndOrigin')) { $errUtils.throwErrByPath('sessions.experimentNotEnabled', { args: { diff --git a/packages/driver/src/cypress/cy.ts b/packages/driver/src/cypress/cy.ts index 9cb52cb136e6..a33ab0e9958b 100644 --- a/packages/driver/src/cypress/cy.ts +++ b/packages/driver/src/cypress/cy.ts @@ -551,22 +551,6 @@ export class $Cy extends EventEmitter2 implements ITimeouts, IStability, IAssert let isRunnerAbleToCommunicateWithAUT: boolean - if (this.Cypress.isBrowser('webkit')) { - // WebKit's unhandledrejection event will sometimes not fire within the AUT - // due to a documented bug: https://bugs.webkit.org/show_bug.cgi?id=187822 - // To ensure that the event will always fire (and always report these - // unhandled rejections to the user), we patch the AUT's Error constructor - // to enqueue a no-op microtask when executed, which ensures that the unhandledrejection - // event handler will be executed if this Error is uncaught. - const originalError = autWindow.Error - - autWindow.Error = function __CyWebKitError (...args) { - autWindow.queueMicrotask(() => {}) - - return originalError.apply(this, args) - } - } - try { // Test to see if we can communicate with the AUT. autWindow.location.href @@ -581,6 +565,22 @@ export class $Cy extends EventEmitter2 implements ITimeouts, IStability, IAssert // If the runner can communicate, we should setup all events, otherwise just setup the window and fire the load event. if (isRunnerAbleToCommunicateWithAUT) { + if (this.Cypress.isBrowser('webkit')) { + // WebKit's unhandledrejection event will sometimes not fire within the AUT + // due to a documented bug: https://bugs.webkit.org/show_bug.cgi?id=187822 + // To ensure that the event will always fire (and always report these + // unhandled rejections to the user), we patch the AUT's Error constructor + // to enqueue a no-op microtask when executed, which ensures that the unhandledrejection + // event handler will be executed if this Error is uncaught. + const originalError = autWindow.Error + + autWindow.Error = function __CyWebKitError (...args) { + autWindow.queueMicrotask(() => {}) + + return originalError.apply(this, args) + } + } + setWindowDocumentProps(autWindow, this.state) // we may need to update the url now diff --git a/packages/driver/src/cypress/error_messages.ts b/packages/driver/src/cypress/error_messages.ts index 0bf0366a21e0..652b9a6ae674 100644 --- a/packages/driver/src/cypress/error_messages.ts +++ b/packages/driver/src/cypress/error_messages.ts @@ -2352,7 +2352,6 @@ export default { webkit: { docsUrl: 'https://on.cypress.io/webkit-experiment', origin: '`cy.origin()` is not currently supported in experimental WebKit.', - session: '`cy.session()` is not currently supported in experimental WebKit.', }, window: { diff --git a/packages/server/lib/browsers/webkit-automation.ts b/packages/server/lib/browsers/webkit-automation.ts index bc6b21e94726..cd8dd6129744 100644 --- a/packages/server/lib/browsers/webkit-automation.ts +++ b/packages/server/lib/browsers/webkit-automation.ts @@ -30,22 +30,26 @@ function convertSameSiteExtensionToCypress (str: CyCookie['sameSite']): 'None' | return str ? extensionMap[str] : undefined } -const normalizeGetCookieProps = (cookie: any): CyCookie => { - if (cookie.expires === -1) { - delete cookie.expires +const normalizeGetCookieProps = ({ name, value, domain, path, secure, httpOnly, sameSite, expires }: playwright.Cookie): CyCookie => { + const cyCookie: CyCookie = { + name, + value, + domain, + path, + secure, + httpOnly, + hostOnly: false, + // Use expirationDate instead of expires + ...expires !== -1 ? { expirationDate: expires } : {}, } - // Use expirationDate instead of expires 🤷‍♀️ - cookie.expirationDate = cookie.expires - delete cookie.expires - - if (cookie.sameSite === 'None') { - cookie.sameSite = 'no_restriction' - } else if (cookie.sameSite) { - cookie.sameSite = cookie.sameSite.toLowerCase() + if (sameSite === 'None') { + cyCookie.sameSite = 'no_restriction' + } else if (sameSite) { + cyCookie.sameSite = sameSite.toLowerCase() as CyCookie['sameSite'] } - return cookie as CyCookie + return cyCookie } const normalizeSetCookieProps = (cookie: CyCookie): playwright.Cookie => { @@ -88,17 +92,33 @@ let requestIdCounter = 1 const requestIdMap = new WeakMap() let downloadIdCounter = 1 +type WebKitAutomationOpts = { + automation: Automation + browser: playwright.Browser + shouldMarkAutIframeRequests: boolean + initialUrl: string + downloadsFolder: string + videoApi?: RunModeVideoApi +} + export class WebKitAutomation { + automation: Automation + private browser: playwright.Browser private context!: playwright.BrowserContext private page!: playwright.Page + private shouldMarkAutIframeRequests: boolean - private constructor (public automation: Automation, private browser: playwright.Browser) {} + private constructor (opts: WebKitAutomationOpts) { + this.automation = opts.automation + this.browser = opts.browser + this.shouldMarkAutIframeRequests = opts.shouldMarkAutIframeRequests + } // static initializer to avoid "not definitively declared" - static async create (automation: Automation, browser: playwright.Browser, initialUrl: string, downloadsFolder: string, videoApi?: RunModeVideoApi) { - const wkAutomation = new WebKitAutomation(automation, browser) + static async create (opts: WebKitAutomationOpts) { + const wkAutomation = new WebKitAutomation(opts) - await wkAutomation.reset({ downloadsFolder, newUrl: initialUrl, videoApi }) + await wkAutomation.reset({ downloadsFolder: opts.downloadsFolder, newUrl: opts.initialUrl, videoApi: opts.videoApi }) return wkAutomation } @@ -127,6 +147,9 @@ export class WebKitAutomation { let promises: Promise[] = [] + // TODO: remove with experimentalSessionAndOrigin + if (this.shouldMarkAutIframeRequests) promises.push(this.markAutIframeRequests()) + if (oldPwPage) promises.push(oldPwPage.context().close()) if (options.newUrl) promises.push(this.page.goto(options.newUrl)) @@ -168,6 +191,28 @@ export class WebKitAutomation { }) } + private async markAutIframeRequests () { + function isAutIframeRequest (request: playwright.Request) { + // is an iframe + return (request.resourceType() === 'document') + // is a top-level iframe (only 1 parent in chain) + && request.frame().parentFrame() && !request.frame().parentFrame()?.parentFrame() + // is not the runner itself + && !request.url().includes('__cypress') + } + + await this.context.route('**', (route, request) => { + if (!isAutIframeRequest(request)) return route.continue() + + return route.continue({ + headers: { + ...request.headers(), + 'X-Cypress-Is-AUT-Frame': 'true', + }, + }) + }) + } + private handleDownloadEvents (downloadsFolder: string) { this.page.on('download', async (download) => { const id = downloadIdCounter++ @@ -234,7 +279,7 @@ export class WebKitAutomation { }) } - private async getCookies () { + private async getCookies (): Promise { const cookies = await this.context.cookies() return cookies.map(normalizeGetCookieProps) @@ -256,8 +301,12 @@ export class WebKitAutomation { return normalizeGetCookieProps(cookie) } - - private async clearCookie (filter: CookieFilter) { + /** + * Clears one specific cookie + * @param filter the cookie to be cleared + * @returns the cleared cookie + */ + private async clearCookie (filter: CookieFilter): Promise { const allCookies = await this.context.cookies() const persistCookies = allCookies.filter((cookie) => { return !_cookieMatches(cookie, filter) @@ -265,6 +314,20 @@ export class WebKitAutomation { await this.context.clearCookies() if (persistCookies.length) await this.context.addCookies(persistCookies) + + return filter + } + + /** + * Clear all cookies + * @returns cookies cleared + */ + private async clearCookies (): Promise { + const allCookies = await this.getCookies() + + await this.context.clearCookies() + + return allCookies } private async takeScreenshot (data) { @@ -293,7 +356,7 @@ export class WebKitAutomation { case 'set:cookies': return await this.context.addCookies(data.map(normalizeSetCookieProps)) case 'clear:cookies': - return await this.context.clearCookies() + return await this.clearCookies() case 'clear:cookie': return await this.clearCookie(data) case 'take:screenshot': diff --git a/packages/server/lib/browsers/webkit.ts b/packages/server/lib/browsers/webkit.ts index 534cff80347d..a7b1a80da3a5 100644 --- a/packages/server/lib/browsers/webkit.ts +++ b/packages/server/lib/browsers/webkit.ts @@ -92,7 +92,15 @@ export async function open (browser: Browser, url: string, options: BrowserLaunc const pwBrowser = await pw.webkit.connect(pwServer.wsEndpoint()) - wkAutomation = await WebKitAutomation.create(automation, pwBrowser, url, options.downloadsFolder, options.videoApi) + wkAutomation = await WebKitAutomation.create({ + automation, + browser: pwBrowser, + initialUrl: url, + downloadsFolder: options.downloadsFolder, + shouldMarkAutIframeRequests: !!options.experimentalSessionAndOrigin, + videoApi: options.videoApi, + }) + automation.use(wkAutomation) class WkInstance extends EventEmitter implements BrowserInstance { diff --git a/packages/socket/lib/browser.ts b/packages/socket/lib/browser.ts index 82c85f81f0af..5bcc7dd07e28 100644 --- a/packages/socket/lib/browser.ts +++ b/packages/socket/lib/browser.ts @@ -5,3 +5,12 @@ export type { Socket } from 'socket.io-client' export { io as client, } + +export function createWebsocket ({ path, browserFamily }: { path: string, browserFamily: string}) { + return io({ + path, + // TODO(webkit): the websocket socket.io transport is busted in WebKit, need polling + // https://github.com/cypress-io/cypress/issues/23807 + transports: browserFamily === 'webkit' ? ['polling'] : ['websocket'], + }) +} diff --git a/packages/socket/test/socket_spec.js b/packages/socket/test/socket_spec.js index e4e95ec64256..74fd372f5855 100644 --- a/packages/socket/test/socket_spec.js +++ b/packages/socket/test/socket_spec.js @@ -21,6 +21,24 @@ describe('Socket', function () { expect(browserLib.client).to.eq(client) }) + it('exports createWebSocket from lib/browser', function () { + expect(browserLib.createWebsocket).to.be.defined + }) + + it('creates a websocket for non webkit browsers', function () { + const socket = browserLib.createWebsocket({ path: '/path', browserFamily: 'chromium' }) + + expect(socket.io.opts.path).to.eq('/path') + expect(socket.io.opts.transports[0]).to.eq('websocket') + }) + + it('creates a websocket for non webkit browsers', function () { + const socket = browserLib.createWebsocket({ path: '/path', browserFamily: 'webkit' }) + + expect(socket.io.opts.path).to.eq('/path') + expect(socket.io.opts.transports[0]).to.eq('polling') + }) + context('.getPathToClientSource', function () { it('returns path to socket.io.js', function () { const clientPath = path.join(resolvePkg('socket.io-client'), 'dist', 'socket.io.js')