Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: Updates based on PR feedback #21137

Merged
merged 46 commits into from Apr 22, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
0c363df
add generic to cy.origin type
chrisbreiding Apr 19, 2022
76e5178
fix log type, update/add comments
chrisbreiding Apr 19, 2022
c486640
fix comment indentation
chrisbreiding Apr 19, 2022
2d756cf
specific generic
chrisbreiding Apr 19, 2022
7407dcb
move RemoteState to internal types
chrisbreiding Apr 19, 2022
b23ddf4
add on links to experimental flag descriptions
chrisbreiding Apr 19, 2022
985f5eb
chore: reduce nesting by flipping condition
chrisbreiding Apr 19, 2022
6a421f3
fix test title
chrisbreiding Apr 19, 2022
e043431
simplify failing log
chrisbreiding Apr 19, 2022
7500d53
rename variable
chrisbreiding Apr 19, 2022
43dfc2a
delete error property
chrisbreiding Apr 19, 2022
856f1b7
fix types
chrisbreiding Apr 19, 2022
ffaebd0
fix type
chrisbreiding Apr 19, 2022
49f7b9c
remove unnecessary todo
chrisbreiding Apr 19, 2022
2f1dfde
update wait test
chrisbreiding Apr 19, 2022
9a5aecb
jquery -> this
chrisbreiding Apr 19, 2022
17ed708
update comment
chrisbreiding Apr 19, 2022
02a63ed
remove vestigial autoRun
chrisbreiding Apr 19, 2022
348302a
use finally
chrisbreiding Apr 19, 2022
2f7d248
re-throw non-security errors
chrisbreiding Apr 19, 2022
5efb24a
move back getting index
chrisbreiding Apr 19, 2022
6850b68
add new state types
chrisbreiding Apr 20, 2022
6c80197
remove unnecessary export
chrisbreiding Apr 20, 2022
10e87fb
startsWith -> includes
chrisbreiding Apr 20, 2022
6c58d3d
it -> them
chrisbreiding Apr 20, 2022
992d629
update system test
chrisbreiding Apr 20, 2022
35ccc28
remove use of promise constructor
chrisbreiding Apr 20, 2022
d96baa5
Revert "remove use of promise constructor"
chrisbreiding Apr 20, 2022
11d6330
log errors from Page.getFrameTree
chrisbreiding Apr 20, 2022
ac1307f
test if anything breaks when removing optional chaining operator
chrisbreiding Apr 20, 2022
9e3750b
remove vestigial file
chrisbreiding Apr 20, 2022
f19402c
handle queue ending in cross-origin driver
chrisbreiding Apr 20, 2022
1f43423
fix coordinates spec
chrisbreiding Apr 20, 2022
9cd2490
improve chrome/firefox check in extension
chrisbreiding Apr 21, 2022
1a2167d
improve secure cookie regex
chrisbreiding Apr 21, 2022
ebd40c7
use production mode for cross-origin driver bundle
chrisbreiding Apr 21, 2022
7c8d698
adding remoteStates.getPrimary
mschile Apr 21, 2022
890b827
catch and ignore queue errors
chrisbreiding Apr 22, 2022
054680c
Merge branch 'md-pr-feedback' of github.com:cypress-io/cypress into m…
chrisbreiding Apr 22, 2022
2ae28a0
remove optional chaining in postMessage handler
chrisbreiding Apr 22, 2022
793cac1
removed unnecessary async
chrisbreiding Apr 22, 2022
02f7fd8
update frame tree on cri client reconnect
chrisbreiding Apr 22, 2022
0569c91
fix formatting
chrisbreiding Apr 22, 2022
5e71789
Merge branch 'feature-multidomain' into md-pr-feedback
chrisbreiding Apr 22, 2022
6ee45ac
renaming remoteStates variable
mschile Apr 22, 2022
fde3acf
prevent requests from being paused if experimentalSessionAndOrigin fl…
chrisbreiding Apr 22, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion cli/schema/cypress.schema.json
Expand Up @@ -256,7 +256,7 @@
"experimentalSessionAndOrigin": {
"type": "boolean",
"default": false,
"description": "Enables cross-origin and improved session support, including the `cy.origin` and `cy.session` commands."
"description": "Enables cross-origin and improved session support, including the `cy.origin` and `cy.session` commands. See https://on.cypress.io/origin and https://on.cypress.io/session."
},
"experimentalSourceRewriting": {
"type": "boolean",
Expand Down
23 changes: 7 additions & 16 deletions cli/types/cypress.d.ts
Expand Up @@ -56,15 +56,6 @@ declare namespace Cypress {
password: string
}

interface RemoteState {
auth?: Auth
domainName: string
strategy: 'file' | 'http'
origin: string
fileServer: string | null
props: Record<string, any>
}

interface Backend {
/**
* Firefox only: Force Cypress to run garbage collection routines.
Expand Down Expand Up @@ -1430,7 +1421,7 @@ declare namespace Cypress {
* cy.get('h1').should('equal', 'Example Domain')
* })
*/
origin(urlOrDomain: string, fn: () => void): Chainable
origin<T extends any>(urlOrDomain: string, fn: () => void): Chainable<T>

/**
* Enables running Cypress commands in a secondary origin.
Expand All @@ -1441,9 +1432,9 @@ declare namespace Cypress {
* expect(foo).to.equal('foo')
* })
*/
origin<T>(urlOrDomain: string, options: {
origin<T, S extends any>(urlOrDomain: string, options: {
args: T
}, fn: (args: T) => void): Chainable
}, fn: (args: T) => void): Chainable<S>

/**
* Get the parent DOM element of a set of DOM elements.
Expand Down Expand Up @@ -2846,7 +2837,7 @@ declare namespace Cypress {
*/
experimentalInteractiveRunEvents: boolean
/**
* Enables cross-origin and improved session support, including the `cy.origin` and `cy.session` commands.
* Enables cross-origin and improved session support, including the `cy.origin` and `cy.session` commands. See https://on.cypress.io/origin and https://on.cypress.io/session.
* @default false
*/
experimentalSessionAndOrigin: boolean
Expand Down Expand Up @@ -2981,7 +2972,6 @@ declare namespace Cypress {
projectName: string
projectRoot: string
proxyUrl: string
remote: RemoteState
report: boolean
reporterRoute: string
reporterUrl: string
Expand Down Expand Up @@ -5766,7 +5756,8 @@ declare namespace Cypress {
}

interface LogConfig extends Timeoutable {
id: number
/** Unique id for the log, in the form of '<origin>-<number>' */
id: string
/** The JQuery element for the command. This will highlight the command in the main window when debugging */
$el: JQuery
/** The scope of the log entry. If child, will appear nested below parents, prefixed with '-' */
Expand All @@ -5779,7 +5770,7 @@ declare namespace Cypress {
message: any
/** Set to false if you want to control the finishing of the command in the log yourself */
autoEnd: boolean
/** Set to false if you want to control the finishing of the command in the log yourself */
/** Set to true to immediately finish the log */
end: boolean
/** Return an object that will be printed in the dev tools console */
consoleProps(): ObjectLike
Expand Down
2 changes: 0 additions & 2 deletions packages/driver/cypress/fixtures/auth/index.html
Expand Up @@ -70,8 +70,6 @@

} else {
const token = JSON.parse(cypressAuthToken)
// ToDo, check for expiry maybe?

// If the token exists, hooray, give them a logout button to destroy the token and refresh.
const tag = document.createElement("p");
const text = document.createTextNode(`Welcome ${token.body.username}`);
Expand Down
Expand Up @@ -2344,9 +2344,9 @@ describe('src/cy/commands/navigation', () => {
}

cy.on('command:queue:before:end', () => {
// force us to become unstable immediately
// else the beforeunload event fires at the end
// of the tick which is too late
// force us to become unstable immediately
// else the beforeunload event fires at the end
// of the tick which is too late
cy.isStable(false, 'testing')

win.location.href = '/timeout?ms=100'
Expand Down
2 changes: 1 addition & 1 deletion packages/driver/cypress/integration/cypress/log_spec.js
Expand Up @@ -91,7 +91,7 @@ describe('src/cypress/log', function () {
expect(LogUtils.countLogsByTests(tests)).to.equal(6)
})

it('returns zero if there are no agents routes or commands', () => {
it('returns zero if there are no agents, routes, or commands', () => {
const tests = {
a: {
notAThing: true,
Expand Down
9 changes: 6 additions & 3 deletions packages/driver/cypress/integration/dom/coordinates_spec.ts
Expand Up @@ -238,15 +238,18 @@ describe('src/dom/coordinates', () => {
}

it('returns true if parent is a window and not an iframe', () => {
const win = getWindowLikeObject()
const win = cy.state('window')

expect(isAUTFrame(win)).to.be.true
})

it('returns true if parent is a window and getting its frameElement property throws an error', () => {
it('returns true if parent is a window and getting its frameElement property throws a cross-origin error', () => {
const win = getWindowLikeObject()
const err = new Error('cross-origin error')

err.name = 'SecurityError'

cy.stub($elements, 'getNativeProp').throws('cross-origin error')
cy.stub($elements, 'getNativeProp').throws(err)

expect(isAUTFrame(win)).to.be.true
})
Expand Down
Expand Up @@ -16,7 +16,7 @@ context('cy.origin log', () => {
})

it('logs in primary and secondary origins', () => {
cy.origin('http://foobar.com:3500', () => {
cy.origin<string>('http://foobar.com:3500', () => {
mschile marked this conversation as resolved.
Show resolved Hide resolved
const afterLogAdded = new Promise<void>((resolve) => {
const listener = (attrs) => {
if (attrs.message === 'test log in cy.origin') {
Expand Down
Expand Up @@ -8,7 +8,11 @@ context('cy.origin waiting', () => {

it('.wait()', () => {
cy.origin('http://foobar.com:3500', () => {
cy.wait(500)
const delay = cy.spy(Cypress.Promise, 'delay')

cy.wait(50).then(() => {
expect(delay).to.be.calledWith(50, 'wait')
})
})
})

Expand Down
Expand Up @@ -118,7 +118,7 @@ describe('cy.origin yields', () => {
done()
})

cy.origin('http://foobar.com:3500', () => {
cy.origin<JQuery>('http://foobar.com:3500', () => {
mschile marked this conversation as resolved.
Show resolved Hide resolved
cy.get('[data-cy="dom-check"]')
})
.then((subject) => subject.text())
Expand All @@ -134,7 +134,7 @@ describe('cy.origin yields', () => {
done()
})

cy.origin('http://foobar.com:3500', () => {
cy.origin<{ key: Function }>('http://foobar.com:3500', () => {
cy.wrap({
key: () => {
return 'whoops'
Expand Down
82 changes: 41 additions & 41 deletions packages/driver/src/cy/commands/navigation.ts
Expand Up @@ -13,7 +13,7 @@ import debugFn from 'debug'
const debug = debugFn('cypress:driver:navigation')

let id = null
let previousUrlVisited: LocationObject | undefined
let previouslyVisitedLocation: LocationObject | undefined
let hasVisitedAboutBlank: boolean = false
let currentlyVisitingAboutBlank: boolean = false
let knownCommandCausedInstability: boolean = false
Expand All @@ -30,7 +30,7 @@ const reset = (test: any = {}) => {

// continuously reset this
// before each test run!
previousUrlVisited = undefined
previouslyVisitedLocation = undefined

// make sure we reset that we haven't
// visited about blank again
Expand All @@ -53,33 +53,7 @@ const timedOutWaitingForPageLoad = (ms, log) => {
const anticipatedCrossOriginHref = cy.state('anticipatingCrossOriginResponse')?.href

// Were we anticipating a cross origin page when we timed out?
if (anticipatedCrossOriginHref) {
// We remain in an anticipating state until either a load even happens or a timeout.
cy.isAnticipatingCrossOriginResponseFor(undefined)

// By default origins is just this location.
let originPolicies = [$Location.create(location.href).originPolicy]

const currentCommand = cy.queue.state('current')

if (currentCommand?.get('name') === 'origin') {
// If the current command is a cy.origin command, we should have gotten a request on the origin it expects.
originPolicies = [cy.state('latestActiveOriginPolicy')]
} else if (Cypress.isCrossOriginSpecBridge && cy.queue.isOnLastCommand()) {
// If this is a cross origin spec bridge and we're on the last command, we should have gotten a request on the origin of one of the parents.
originPolicies = cy.state('parentOriginPolicies')
}

$errUtils.throwErrByPath('navigation.cross_origin_load_timed_out', {
args: {
configFile: Cypress.config('configFile'),
ms,
crossOriginUrl: $Location.create(anticipatedCrossOriginHref),
originPolicies,
},
onFail: log,
})
} else {
if (!anticipatedCrossOriginHref) {
$errUtils.throwErrByPath('navigation.timed_out', {
args: {
configFile: Cypress.config('configFile'),
Expand All @@ -88,9 +62,35 @@ const timedOutWaitingForPageLoad = (ms, log) => {
onFail: log,
})
}

// We remain in an anticipating state until either a load even happens or a timeout.
cy.isAnticipatingCrossOriginResponseFor(undefined)

// By default origins is just this location.
let originPolicies = [$Location.create(location.href).originPolicy]

const currentCommand = cy.queue.state('current')

if (currentCommand?.get('name') === 'origin') {
// If the current command is a cy.origin command, we should have gotten a request on the origin it expects.
originPolicies = [cy.state('latestActiveOriginPolicy')]
} else if (Cypress.isCrossOriginSpecBridge && cy.queue.isOnLastCommand()) {
// If this is a cross origin spec bridge and we're on the last command, we should have gotten a request on the origin of one of the parents.
originPolicies = cy.state('parentOriginPolicies')
}

$errUtils.throwErrByPath('navigation.cross_origin_load_timed_out', {
args: {
configFile: Cypress.config('configFile'),
ms,
crossOriginUrl: $Location.create(anticipatedCrossOriginHref),
originPolicies,
},
onFail: log,
})
}

const cannotVisitDifferentOrigin = ({ remote, existing, originalUrl, previousUrlVisited, log, isCrossOriginSpecBridge = false }) => {
const cannotVisitDifferentOrigin = ({ remote, existing, originalUrl, previouslyVisitedLocation, log, isCrossOriginSpecBridge = false }) => {
const differences: string[] = []

if (remote.protocol !== existing.protocol) {
Expand All @@ -109,7 +109,7 @@ const cannotVisitDifferentOrigin = ({ remote, existing, originalUrl, previousUrl
onFail: log,
args: {
differences: differences.join(', '),
previousUrl: previousUrlVisited,
previousUrl: previouslyVisitedLocation,
attemptedUrl: remote,
originalUrl,
isCrossOriginSpecBridge,
Expand All @@ -123,12 +123,12 @@ const cannotVisitDifferentOrigin = ({ remote, existing, originalUrl, previousUrl
$errUtils.throwErrByPath('visit.cannot_visit_different_origin', errOpts)
}

const cannotVisitPreviousOrigin = ({ remote, originalUrl, previousUrlVisited, log }) => {
const cannotVisitPreviousOrigin = ({ remote, originalUrl, previouslyVisitedLocation, log }) => {
const errOpts = {
onFail: log,
args: {
attemptedUrl: remote,
previousUrl: previousUrlVisited,
previousUrl: previouslyVisitedLocation,
originalUrl,
},
errProps: {
Expand Down Expand Up @@ -434,9 +434,7 @@ const stabilityChanged = (Cypress, state, config, stable) => {
}

const onCrossOriginFailure = (err) => {
options._log.set('message', '--page loaded--').snapshot().end()
options._log.set('state', 'failed')
options._log.set('error', err)
options._log.set('message', '--page loaded--').snapshot().error(err)

resolve()
}
Expand Down Expand Up @@ -526,6 +524,7 @@ type InvalidContentTypeError = Error & {

interface InternalVisitOptions extends Partial<Cypress.VisitOptions> {
_log?: Log
hasAlreadyVisitedUrl: boolean
}

export default (Commands, Cypress, cy, state, config) => {
Expand Down Expand Up @@ -852,7 +851,7 @@ export default (Commands, Cypress, cy, state, config) => {
onLoad () {},
})

options.hasAlreadyVisitedUrl = !!previousUrlVisited
options.hasAlreadyVisitedUrl = !!previouslyVisitedLocation

if (!_.isUndefined(options.qs) && !_.isObject(options.qs)) {
$errUtils.throwErrByPath('visit.invalid_qs', { args: { qs: String(options.qs) } })
Expand Down Expand Up @@ -1125,7 +1124,7 @@ export default (Commands, Cypress, cy, state, config) => {
// if the origin currently matches
// then go ahead and change the iframe's src
if (remote.originPolicy === existing.originPolicy) {
previousUrlVisited = remote
previouslyVisitedLocation = remote

url = $Location.fullyQualifyUrl(url)

Expand All @@ -1138,10 +1137,10 @@ export default (Commands, Cypress, cy, state, config) => {
// if we've already cy.visit'ed in the test and we are visiting a new origin,
// throw an error, else we'd be in a endless loop,
// we also need to disable retries to prevent the endless loop
if (previousUrlVisited) {
if (previouslyVisitedLocation) {
$utils.getTestFromRunnable(state('runnable'))._retries = 0

const params = { remote, existing, originalUrl, previousUrlVisited, log: options._log }
const params = { remote, existing, originalUrl, previouslyVisitedLocation, log: options._log }

return cannotVisitDifferentOrigin(params)
}
Expand All @@ -1151,7 +1150,7 @@ export default (Commands, Cypress, cy, state, config) => {
// origin which isn't allowed within a cy.origin block
if (Cypress.isCrossOriginSpecBridge) {
const existingAutOrigin = win ? $Location.create(win.location.href) : $Location.create(Cypress.state('currentActiveOriginPolicy'))
const params = { remote, existing, originalUrl, previousUrlVisited: existingAutOrigin, log: options._log, isCrossOriginSpecBridge: true, isPrimaryOrigin }
const params = { remote, existing, originalUrl, previouslyVisitedLocation: existingAutOrigin, log: options._log, isCrossOriginSpecBridge: true, isPrimaryOrigin }

return isPrimaryOrigin ? cannotVisitPreviousOrigin(params) : cannotVisitDifferentOrigin(params)
}
Expand Down Expand Up @@ -1238,6 +1237,7 @@ export default (Commands, Cypress, cy, state, config) => {
// not a network failure, and we should throw the original error
if (err.isCallbackError || err.isCrossOrigin) {
delete err.isCallbackError
delete err.isCrossOrigin
throw err
}

Expand Down
2 changes: 1 addition & 1 deletion packages/driver/src/cy/location.ts
Expand Up @@ -15,7 +15,7 @@ export const create = (state) => ({
return location
} catch (e) {
// it is possible we do not have access to the location
// for example, if the app has redirected to a 2nd origin
// for example, if the app has redirected to a different origin
return ''
}
},
Expand Down
6 changes: 3 additions & 3 deletions packages/driver/src/cypress/command_queue.ts
Expand Up @@ -262,15 +262,15 @@ export class CommandQueue extends Queue<Command> {
// @ts-ignore
run () {
const next = () => {
// start at 0 index if one is not already set
let index = this.state('index') || this.state('index', 0)

// bail if we've been told to abort in case
// an old command continues to run after
if (this.stopped) {
return
}

// start at 0 index if one is not already set
let index = this.state('index') || this.state('index', 0)

const command = this.at(index)

// if the command should be skipped, just bail and increment index
Expand Down