diff --git a/packages/app/src/runner/event-manager.ts b/packages/app/src/runner/event-manager.ts index 427a9c9b10b4..e2f84f11780e 100644 --- a/packages/app/src/runner/event-manager.ts +++ b/packages/app/src/runner/event-manager.ts @@ -3,7 +3,6 @@ import { EventEmitter } from 'events' import type { MobxRunnerStore } from '@packages/app/src/store/mobx-runner-store' import type MobX from 'mobx' import type { LocalBusEmitsMap, LocalBusEventMap, DriverToLocalBus, SocketToDriverMap } from './event-manager-types' - import type { RunState, CachedTestState, AutomationElementId, FileDetails, ReporterStartInfo, ReporterRunState } from '@packages/types' import { logger } from './logger' @@ -40,7 +39,7 @@ const driverToSocketEvents = 'backend:request automation:request mocha recorder: const driverTestEvents = 'test:before:run:async test:after:run'.split(' ') const driverToLocalEvents = 'viewport:changed config stop url:changed page:loading visit:failed visit:blank cypress:in:cypress:runner:event'.split(' ') const socketRerunEvents = 'runner:restart watched:file:changed'.split(' ') -const socketToDriverEvents = 'net:stubbing:event request:event script:error cross:origin:automation:cookies'.split(' ') +const socketToDriverEvents = 'net:stubbing:event request:event script:error cross:origin:cookies'.split(' ') const localToReporterEvents = 'reporter:log:add reporter:log:state:changed reporter:log:remove'.split(' ') /** @@ -698,32 +697,18 @@ export class EventManager { log?.set(attrs) }) - // This message comes from the AUT, not the spec bridge. - // This is called in the event that cookies are set in a cross origin AUT prior to attaching a spec bridge. - Cypress.primaryOriginCommunicator.on('aut:set:cookie', ({ cookie, href }, _origin, source) => { - const { superDomain } = Cypress.Location.create(href) - const automationCookie = Cypress.Cookies.toughCookieToAutomationCookie(Cypress.Cookies.parse(cookie), superDomain) - - Cypress.automation('set:cookie', automationCookie).then(() => { - // It's possible the source has already unloaded before this event has been processed. - source?.postMessage({ event: 'cross:origin:aut:set:cookie' }, '*') - }) - .catch(() => { - // unlikely there will be errors, but ignore them in any case, since - // they're not user-actionable - }) - }) - - // This message comes from the AUT, not the spec bridge. - // This is called in the event that cookies are retrieved in a cross origin AUT prior to attaching a spec bridge. - Cypress.primaryOriginCommunicator.on('aut:get:cookie', async ({ href }, _origin, source) => { - const { superDomain } = Cypress.Location.create(href) - - const cookies = await Cypress.automation('get:cookies', { superDomain }) - - // It's possible the source has already unloaded before this event has been processed. - source?.postMessage({ event: 'cross:origin:aut:get:cookie', cookies }, '*') - }) + // This message comes from the AUT, not the spec bridge. This is called in + // the event that cookies are set via document.cookie in a cross origin + // AUT prior to attaching a spec bridge. + Cypress.primaryOriginCommunicator.on( + 'aut:set:cookie', + (options: { cookie, url: string, sameSiteContext: string }) => { + // unlikely there will be errors, but ignore them in any case, since + // they're not user-actionable + Cypress.automation('set:cookie', options.cookie).catch(() => {}) + Cypress.backend('cross:origin:set:cookie', options).catch(() => {}) + }, + ) // The window.top should not change between test reloads, and we only need to bind the message event when Cypress is recreated // Forward all message events to the current instance of the multi-origin communicator 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 aa6fe91bc2f3..73c1e3cae4ea 100644 --- a/packages/driver/cypress/e2e/e2e/origin/cookie_login.cy.ts +++ b/packages/driver/cypress/e2e/e2e/origin/cookie_login.cy.ts @@ -453,12 +453,13 @@ describe('cy.origin - cookie login', { browser: '!webkit' }, () => { const expires = (new Date()).toUTCString() cy.get('[data-cy="username"]').type(username) - cy.get('[data-cy="localhostCookieProps"]').type(`Expires=${expires}`) + cy.get('[data-cy="cookieProps"]').type(`Expires=${expires}`) cy.get('[data-cy="login"]').click() }) - cy.origin('http://www.idp.com:3500', () => { - cy.clearCookie('user') + cy.origin('http://www.idp.com:3501', () => { + cy.wait(1000) // give cookie time to expire + cy.reload() cy.document().its('cookie').should('not.include', 'user=') }) }) @@ -502,15 +503,18 @@ describe('cy.origin - cookie login', { browser: '!webkit' }, () => { cy.getCookie('user').should('be.null') }) - it('past max-age -> not accessible via document.cookie', () => { + // expiring cookies set by automation don't seem to get unset appropriately + // in Firefox. this issue doesn't seem to be specific to cross-origin tests, + // as it happens even using cy.setCookie() + it('past max-age -> not accessible via document.cookie', { browser: '!firefox' }, () => { cy.get('[data-cy="cookie-login-land-on-idp"]').click() cy.origin('http://www.foobar.com:3500', { args: { username } }, ({ username }) => { cy.get('[data-cy="username"]').type(username) - cy.get('[data-cy="localhostCookieProps"]').type('Max-Age=1') + cy.get('[data-cy="cookieProps"]').type('Max-Age=1') cy.get('[data-cy="login"]').click() }) - cy.origin('http://www.idp.com:3500', () => { + cy.origin('http://www.idp.com:3501', () => { cy.wait(1500) // give cookie time to expire cy.reload() cy.document().its('cookie').should('not.include', 'user=') @@ -664,14 +668,15 @@ describe('cy.origin - cookie login', { browser: '!webkit' }, () => { }) it('gets cookie set by http request', () => { - cy.get('[data-cy="cookie-login-land-on-idp"]').click() + cy.get('[data-cy="cookie-login-land-on-document-cookie"]').click() cy.origin('http://www.foobar.com:3500', { args: { username } }, ({ username }) => { cy.get('[data-cy="username"]').type(username) cy.get('[data-cy="login"]').click() }) - cy.origin('http://www.idp.com:3500', { args: { username } }, ({ username }) => { - cy.document().its('cookie').should('include', `user=${username}`) + cy.origin('http://www.idp.com:3501', { args: { username } }, ({ username }) => { + cy.get('[data-cy="doc-cookie"]').invoke('text') + .should('include', `user=${username}`) }) }) @@ -698,20 +703,20 @@ describe('cy.origin - cookie login', { browser: '!webkit' }, () => { }) it('cookie properties are preserved when set via automation', () => { - cy.get('[data-cy="cross-origin-secondary-link"]').click() - cy.origin('http://www.foobar.com:3500', () => { + cy.get('[data-cy="cookie-https"]').click() + cy.origin('https://www.foobar.com:3502', () => { cy.document().then((doc) => { - doc.cookie = 'key=value; SameSite=Strict; Path=/foo' + doc.cookie = 'key=value; SameSite=Strict; Secure; Path=/fixtures' }) cy.getCookie('key').then((cookie) => { expect(Cypress._.omit(cookie, 'expiry')).to.deep.equal({ - domain: '.foobar.com', + domain: '.www.foobar.com', httpOnly: false, name: 'key', - path: '/foo', + path: '/fixtures', sameSite: 'strict', - secure: false, + secure: true, value: 'value', }) }) @@ -743,7 +748,7 @@ describe('cy.origin - cookie login', { browser: '!webkit' }, () => { doc.cookie = 'key2=value2' }) - cy.document().its('cookie').should('equal', 'key2=value2; key1=value1') + cy.document().its('cookie').should('equal', 'key1=value1; key2=value2') }) }) @@ -754,10 +759,29 @@ describe('cy.origin - cookie login', { browser: '!webkit' }, () => { doc.cookie = 'key=value' }) + // it can take a small amount of time for the cookie to make it to + // automation, but it's unlikely a user will encounter this issue + // since they'd pretty much have to write this exact test. making it + // wait a second is probably overkill, but purposefully keeping the + // wait long to avoid this test becoming flaky + cy.wait(1000) cy.getCookie('key').its('value').should('equal', 'value') }) }) + it('returns cookie set by cy.setCookie()', () => { + cy.get('[data-cy="cookie-login-land-on-idp"]').click() + cy.origin('http://www.foobar.com:3500', { args: { username } }, ({ username }) => { + cy.get('[data-cy="username"]').type(username) + cy.get('[data-cy="login"]').click() + }) + + cy.origin('http://www.idp.com:3501', () => { + cy.setCookie('foo', 'bar') + cy.document().its('cookie').should('include', 'foo=bar') + }) + }) + it('no longer returns cookie after cy.clearCookie()', () => { cy.get('[data-cy="cookie-login-land-on-idp"]').click() cy.origin('http://www.foobar.com:3500', { args: { username } }, ({ username }) => { @@ -765,20 +789,20 @@ describe('cy.origin - cookie login', { browser: '!webkit' }, () => { cy.get('[data-cy="login"]').click() }) - cy.origin('http://www.idp.com:3500', () => { + cy.origin('http://www.idp.com:3501', () => { cy.clearCookie('user') cy.document().its('cookie').should('equal', '') }) }) - it('no longer returns cookie after cy.clearCookies()', () => { + it('no longer returns cookies after cy.clearCookies()', () => { cy.get('[data-cy="cookie-login-land-on-idp"]').click() cy.origin('http://www.foobar.com:3500', { args: { username } }, ({ username }) => { cy.get('[data-cy="username"]').type(username) cy.get('[data-cy="login"]').click() }) - cy.origin('http://www.idp.com:3500', () => { + cy.origin('http://www.idp.com:3501', () => { cy.clearCookies() cy.document().its('cookie').should('equal', '') }) @@ -791,7 +815,7 @@ describe('cy.origin - cookie login', { browser: '!webkit' }, () => { cy.get('[data-cy="login"]').click() }) - cy.origin('http://www.idp.com:3500', { args: { username } }, ({ username }) => { + cy.origin('http://www.idp.com:3501', { args: { username } }, ({ username }) => { cy.document().then((doc) => { doc.cookie = 'key=value' }) @@ -810,12 +834,12 @@ describe('cy.origin - cookie login', { browser: '!webkit' }, () => { }) cy.get('[data-cy="document-cookie"]').click() - cy.origin('http://www.foobar.com:3500', { args: { username } }, ({ username }) => { + cy.origin('http://www.foobar.com:3500', () => { cy.document().its('cookie').should('include', 'name=value') cy.get('[data-cy="doc-cookie"]').invoke('text').should('equal', 'name=value') cy.getCookie('name').then((cookie) => { expect(Cypress._.omit(cookie, 'expiry')).to.deep.equal({ - domain: '.foobar.com', + domain: '.www.foobar.com', httpOnly: false, name: 'name', path: '/', @@ -826,5 +850,59 @@ describe('cy.origin - cookie login', { browser: '!webkit' }, () => { }) }) }) + + it('preserves duplicate cookie keys', () => { + cy.get('[data-cy="cookie-login-land-on-document-cookie"]').click() + cy.origin('http://www.foobar.com:3500', { args: { username } }, ({ username }) => { + cy.get('[data-cy="username"]').type(username) + cy.get('[data-cy="login"]').click() + }) + + cy.origin('http://www.idp.com:3501', () => { + // ensure we've redirected to the right page + cy.url().should('not.include', 'http://www.idp.com:3501/verify-cookie-login') + cy.document().then((doc) => { + doc.cookie = 'key=value1; domain=www.idp.com' + doc.cookie = 'key=value2; domain=idp.com' + }) + + // order of the cookies differs depending on browser, so just + // ensure that each one is there + cy.document().its('cookie').should('include', 'key=value1') + cy.document().its('cookie').should('include', 'key=value2') + }) + }) + + it('setting cookie preserves cookies on subsequent page loads', () => { + cy.get('[data-cy="cross-origin-secondary-link"]').click() + cy.origin('http://www.foobar.com:3500', () => { + cy.document().then((doc) => { + doc.cookie = 'key=value' + }) + + cy.document().its('cookie').should('equal', 'key=value') + cy.wait(500) + cy.reload() + cy.document().its('cookie').should('equal', 'key=value') + }) + }) + + // the spec bridge will likely already exist in this spec when running + // all the tests together, but this ensures the behavior in case it's run + // alone or if we implement spec bridge removal in the future + it('works when spec bridge is set up prior to page load', () => { + cy.origin('http://www.idp.com:3501', () => {}) + + cy.get('[data-cy="cookie-login-land-on-document-cookie"]').click() + cy.origin('http://www.foobar.com:3500', { args: { username } }, ({ username }) => { + cy.get('[data-cy="username"]').type(username) + cy.get('[data-cy="login"]').click() + }) + + cy.origin('http://www.idp.com:3501', { args: { username } }, ({ username }) => { + cy.get('[data-cy="doc-cookie"]').invoke('text') + .should('include', `user=${username}`) + }) + }) }) }) diff --git a/packages/driver/cypress/fixtures/auth/document-cookie.html b/packages/driver/cypress/fixtures/auth/document-cookie.html index c36fa837157c..69b48c122ceb 100644 --- a/packages/driver/cypress/fixtures/auth/document-cookie.html +++ b/packages/driver/cypress/fixtures/auth/document-cookie.html @@ -3,11 +3,14 @@
-should be replaced
++ document.cookie: + should be replaced +