chore: (multi-domain) update to support path and hash (#20953)

This commit is contained in:
Matt Schile
2022-04-07 14:23:24 -06:00
committed by GitHub
parent 8a78b5c127
commit 782d89f94a
7 changed files with 125 additions and 65 deletions
+2 -2
View File
@@ -1906,7 +1906,7 @@ declare namespace Cypress {
* cy.get('h1').should('equal', 'Example Domain')
* })
*/
origin(originOrDomain: string, fn: () => void): Chainable
origin(urlOrDomain: string, fn: () => void): Chainable
// TODO: when we find other options to put into the 'data' argument of cy.origin, we may want to overload this type with
// a 'data' parameter that contains all data options, including args, and one that contains all data options, excluding args.
@@ -1920,7 +1920,7 @@ declare namespace Cypress {
* expect(foo).to.equal('foo')
* })
*/
origin<T>(originOrDomain: string, options: {
origin<T>(urlOrDomain: string, options: {
args: T
}, fn: (args: T) => void): Chainable
@@ -113,20 +113,38 @@ context('cy.origin navigation', () => {
cy.get('[data-cy="dom-check"]').should('have.text', 'From a secondary origin')
cy.visit('http://www.foobar.com:3500/fixtures/dom.html')
cy.location('href').should('equal', 'http://www.foobar.com:3500/fixtures/dom.html')
})
})
it('supports relative urls within secondary', () => {
cy.visit('/fixtures/multi-domain.html')
cy.get('a[data-cy="cross-origin-secondary-link"]').click()
cy.origin('http://www.foobar.com:3500', () => {
cy.visit('/fixtures/dom.html')
cy.location('href').should('equal', 'http://www.foobar.com:3500/fixtures/dom.html')
})
})
it('supports relative urls with path within secondary', () => {
cy.origin('http://www.foobar.com:3500/fixtures', () => {
cy.visit('/dom.html')
cy.location('href').should('equal', 'http://www.foobar.com:3500/fixtures/dom.html')
})
})
it('supports relative urls with hash within secondary', () => {
cy.origin('http://www.foobar.com:3500/#hash', () => {
cy.visit('/more-hash')
cy.location('href').should('equal', 'http://www.foobar.com:3500/#hash/more-hash')
})
})
it('supports relative urls with path and hash within secondary', () => {
cy.origin('http://www.foobar.com:3500/welcome/#hash', () => {
cy.visit('/more-hash')
cy.location('href').should('equal', 'http://www.foobar.com:3500/welcome/#hash/more-hash')
})
})
it('supports hash change within secondary', () => {
cy.visit('/fixtures/multi-domain.html')
@@ -42,7 +42,7 @@ describe('cy.origin', () => {
})
it('succeeds on a complete origin', () => {
cy.origin('http://foobar1.com:3500', () => {})
cy.origin('http://foobar1.com:3500', () => undefined)
cy.then(() => {
const expectedSrc = `http://foobar1.com:3500/__cypress/multi-domain-iframes`
const iframe = window.top?.document.getElementById('Spec\ Bridge:\ http://foobar1.com:3500') as HTMLIFrameElement
@@ -52,7 +52,7 @@ describe('cy.origin', () => {
})
it('succeeds on a complete origin using https', () => {
cy.origin('https://foobar2.com:3500', () => {})
cy.origin('https://foobar2.com:3500', () => undefined)
cy.then(() => {
const expectedSrc = `https://foobar2.com:3500/__cypress/multi-domain-iframes`
const iframe = window.top?.document.getElementById('Spec\ Bridge:\ https://foobar2.com:3500') as HTMLIFrameElement
@@ -62,7 +62,7 @@ describe('cy.origin', () => {
})
it('succeeds on a hostname and port', () => {
cy.origin('foobar3.com:3500', () => {})
cy.origin('foobar3.com:3500', () => undefined)
cy.then(() => {
const expectedSrc = `https://foobar3.com:3500/__cypress/multi-domain-iframes`
const iframe = window.top?.document.getElementById('Spec\ Bridge:\ https://foobar3.com:3500') as HTMLIFrameElement
@@ -72,7 +72,7 @@ describe('cy.origin', () => {
})
it('succeeds on a protocol and hostname', () => {
cy.origin('http://foobar4.com', () => {})
cy.origin('http://foobar4.com', () => undefined)
cy.then(() => {
const expectedSrc = `http://foobar4.com/__cypress/multi-domain-iframes`
const iframe = window.top?.document.getElementById('Spec\ Bridge:\ http://foobar4.com') as HTMLIFrameElement
@@ -82,7 +82,7 @@ describe('cy.origin', () => {
})
it('succeeds on a subdomain', () => {
cy.origin('app.foobar5.com', () => {})
cy.origin('app.foobar5.com', () => undefined)
cy.then(() => {
const expectedSrc = `https://foobar5.com/__cypress/multi-domain-iframes`
const iframe = window.top?.document.getElementById('Spec\ Bridge:\ https://foobar5.com') as HTMLIFrameElement
@@ -101,8 +101,68 @@ describe('cy.origin', () => {
})
})
it('succeeds on a url with path', () => {
cy.origin('http://www.foobar7.com/login', () => undefined)
cy.then(() => {
const expectedSrc = `http://foobar7.com/__cypress/multi-domain-iframes`
const iframe = window.top?.document.getElementById('Spec\ Bridge:\ http://foobar7.com') as HTMLIFrameElement
expect(iframe.src).to.equal(expectedSrc)
})
})
it('succeeds on a url with a hash', () => {
cy.origin('http://www.foobar8.com/#hash', () => undefined)
cy.then(() => {
const expectedSrc = `http://foobar8.com/__cypress/multi-domain-iframes`
const iframe = window.top?.document.getElementById('Spec\ Bridge:\ http://foobar8.com') as HTMLIFrameElement
expect(iframe.src).to.equal(expectedSrc)
})
})
it('succeeds on a url with a path and hash', () => {
cy.origin('http://www.foobar9.com/login/#hash', () => undefined)
cy.then(() => {
const expectedSrc = `http://foobar9.com/__cypress/multi-domain-iframes`
const iframe = window.top?.document.getElementById('Spec\ Bridge:\ http://foobar9.com') as HTMLIFrameElement
expect(iframe.src).to.equal(expectedSrc)
})
})
it('succeeds on a domain with path', () => {
cy.origin('foobar10.com/login', () => undefined)
cy.then(() => {
const expectedSrc = `https://foobar10.com/__cypress/multi-domain-iframes`
const iframe = window.top?.document.getElementById('Spec\ Bridge:\ https://foobar10.com') as HTMLIFrameElement
expect(iframe.src).to.equal(expectedSrc)
})
})
it('succeeds on a domain with a hash', () => {
cy.origin('foobar11.com/#hash', () => undefined)
cy.then(() => {
const expectedSrc = `https://foobar11.com/__cypress/multi-domain-iframes`
const iframe = window.top?.document.getElementById('Spec\ Bridge:\ https://foobar11.com') as HTMLIFrameElement
expect(iframe.src).to.equal(expectedSrc)
})
})
it('succeeds on a domain with a path and hash', () => {
cy.origin('foobar12.com/login/#hash', () => undefined)
cy.then(() => {
const expectedSrc = `https://foobar12.com/__cypress/multi-domain-iframes`
const iframe = window.top?.document.getElementById('Spec\ Bridge:\ https://foobar12.com') as HTMLIFrameElement
expect(iframe.src).to.equal(expectedSrc)
})
})
it('succeeds on a public suffix with a subdomain', () => {
cy.origin('app.foobar.herokuapp.com', () => {})
cy.origin('app.foobar.herokuapp.com', () => undefined)
cy.then(() => {
const expectedSrc = `https://foobar.herokuapp.com/__cypress/multi-domain-iframes`
const iframe = window.top?.document.getElementById('Spec\ Bridge:\ https://foobar.herokuapp.com') as HTMLIFrameElement
@@ -122,7 +182,7 @@ describe('cy.origin', () => {
})
it('finds the right spec bridge with a subdomain', () => {
cy.visit('/fixtures/auth/index.html') // Establishes primary origin
cy.visit('/fixtures/auth/index.html')
cy.window().then((win) => {
win.location.href = 'http://baz.foobar.com:3500/fixtures/auth/idp.html'
})
@@ -138,14 +198,13 @@ describe('cy.origin', () => {
})
it('uses cy.origin twice', () => {
cy.visit('/fixtures/auth/index.html') // Establishes primary origin
cy.get('[data-cy="login-idp"]').click() // Takes you to idp.com
cy.visit('/fixtures/auth/index.html')
cy.get('[data-cy="login-idp"]').click()
cy.origin('http://idp.com:3500', () => {
cy.get('[data-cy="username"]').type('BJohnson')
cy.get('[data-cy="login"]').click()
}) // Trailing edge wait, waiting to return to the primary origin
})
// Verify that the user has logged in on /siteA
cy.get('[data-cy="welcome"]')
.invoke('text')
.should('equal', 'Welcome BJohnson')
@@ -159,16 +218,15 @@ describe('cy.origin', () => {
cy.origin('http://foobar.com:3500', () => {
cy.get('[data-cy="username"]').type('TJohnson')
cy.get('[data-cy="login"]').click()
}) // Trailing edge wait, waiting to return to the primary origin
})
// Verify that the user has logged in on /siteA
cy.get('[data-cy="welcome"]')
.invoke('text')
.should('equal', 'Welcome TJohnson')
})
it('creates a spec bridge for https://idp.com:3502', () => {
cy.visit('/fixtures/auth/index.html') // Establishes primary origin
cy.visit('/fixtures/auth/index.html')
cy.origin('idp.com:3502', () => {
cy.visit('https://www.idp.com:3502/fixtures/auth/index.html')
cy.get('[data-cy="login-idp"]').invoke('text').should('equal', 'Login IDP')
@@ -176,7 +234,7 @@ describe('cy.origin', () => {
})
it('creates a spec bridge for http://idp.com:3500', () => {
cy.visit('/fixtures/auth/index.html') // Establishes primary origin
cy.visit('/fixtures/auth/index.html')
cy.origin('http://idp.com:3500', () => {
cy.visit('http://www.idp.com:3500/fixtures/auth/index.html')
cy.get('[data-cy="login-idp"]').invoke('text').should('equal', 'Login IDP')
@@ -199,7 +257,7 @@ describe('cy.origin', () => {
it('errors if passed a non-string for the origin argument', (done) => {
cy.on('fail', (err) => {
expect(err.message).to.equal('`cy.origin()` requires the first argument to be either an origin (\'https://app.example.com\') or a domain name (\'example.com\'). The origin or domain name must not contain a path, hash, or query parameters. You passed: ``')
expect(err.message).to.equal('`cy.origin()` requires the first argument to be either a url (`https://www.example.com/path`) or a domain name (`example.com`). Query parameters are not allowed. You passed: ``')
done()
})
@@ -210,32 +268,12 @@ describe('cy.origin', () => {
it('errors if query params are provided', (done) => {
cy.on('fail', (err) => {
expect(err.message).to.equal('`cy.origin()` requires the first argument to be either an origin (\'https://app.example.com\') or a domain name (\'example.com\'). The origin or domain name must not contain a path, hash, or query parameters. You passed: `foobar.com?foo=bar`')
expect(err.message).to.equal('`cy.origin()` requires the first argument to be either a url (`https://www.example.com/path`) or a domain name (`example.com`). Query parameters are not allowed. You passed: `http://www.foobar.com?foo=bar`')
done()
})
cy.origin('foobar.com?foo=bar', () => undefined)
})
it('errors if passed a domain name with a path', (done) => {
cy.on('fail', (err) => {
expect(err.message).to.equal('`cy.origin()` requires the first argument to be either an origin (\'https://app.example.com\') or a domain name (\'example.com\'). The origin or domain name must not contain a path, hash, or query parameters. You passed: `foobar.com/login`')
done()
})
cy.origin('foobar.com/login', () => undefined)
})
it('errors if passed a domain name with a hash', (done) => {
cy.on('fail', (err) => {
expect(err.message).to.equal('`cy.origin()` requires the first argument to be either an origin (\'https://app.example.com\') or a domain name (\'example.com\'). The origin or domain name must not contain a path, hash, or query parameters. You passed: `foobar.com/#hash`')
done()
})
cy.origin('foobar.com/#hash', () => undefined)
cy.origin('http://www.foobar.com?foo=bar', () => undefined)
})
it('errors passing non-array to callback function', (done) => {
@@ -37,6 +37,10 @@ const createApp = (port) => {
return res.sendStatus(200)
})
app.get('/', (req, res) => {
return res.send('<html><body>root page</body></html>')
})
app.get('/timeout', (req, res) => {
return Promise
.delay(req.query.ms || 0)
+10 -10
View File
@@ -9,12 +9,12 @@ import { LogUtils } from '../../cypress/log'
const reHttp = /^https?:\/\//
const normalizeOrigin = (originOrDomain) => {
let origin = originOrDomain
const normalizeOrigin = (urlOrDomain) => {
let origin = urlOrDomain
// If just a domain, convert it to an origin by adding the protocol
if (!reHttp.test(originOrDomain)) {
origin = `https://${originOrDomain}`
if (!reHttp.test(urlOrDomain)) {
origin = `https://${urlOrDomain}`
}
return $Location.normalize(origin)
@@ -50,7 +50,7 @@ export function addCommands (Commands, Cypress: Cypress.Cypress, cy: Cypress.cy,
})
Commands.addAll({
origin<T> (originOrDomain: string, optionsOrFn: { args: T } | (() => {}), fn?: (args?: T) => {}) {
origin<T> (urlOrDomain: string, optionsOrFn: { args: T } | (() => {}), fn?: (args?: T) => {}) {
// store the invocation stack in the case that `cy.origin` errors
communicator.userInvocationStack = state('current').get('userInvocationStack')
@@ -79,7 +79,7 @@ export function addCommands (Commands, Cypress: Cypress.Cypress, cy: Cypress.cy,
const log = Cypress.log({
name: 'origin',
type: 'parent',
message: originOrDomain,
message: urlOrDomain,
end: true,
})
@@ -93,14 +93,14 @@ export function addCommands (Commands, Cypress: Cypress.Cypress, cy: Cypress.cy,
validator.validate({
callbackFn,
options,
originOrDomain,
urlOrDomain,
})
// use URL to ensure unicode characters are correctly handled
const url = new URL(normalizeOrigin(originOrDomain)).toString()
const url = new URL(normalizeOrigin(urlOrDomain)).toString()
const location = $Location.create(url)
validator.validateLocation(location, originOrDomain)
validator.validateLocation(location, urlOrDomain)
const originPolicy = location.originPolicy
@@ -221,7 +221,7 @@ export function addCommands (Commands, Cypress: Cypress.Cypress, cy: Cypress.cy,
runnable: serializeRunnable(Cypress.state('runnable')),
duringUserTestExecution: Cypress.state('duringUserTestExecution'),
hookId: Cypress.state('hookId'),
originCommandBaseUrl: location.origin,
originCommandBaseUrl: location.href,
parentOriginPolicies: [cy.getRemoteLocation('originPolicy')],
isStable: Cypress.state('isStable'),
autOrigin: Cypress.state('autOrigin'),
@@ -13,13 +13,13 @@ export class Validator {
this.onFailure = onFailure
}
validate ({ callbackFn, options, originOrDomain }) {
if (!isString(originOrDomain)) {
validate ({ callbackFn, options, urlOrDomain }) {
if (!isString(urlOrDomain)) {
this.onFailure()
$errUtils.throwErrByPath('origin.invalid_origin_argument', {
$errUtils.throwErrByPath('origin.invalid_url_argument', {
onFail: this.log,
args: { arg: $utils.stringify(originOrDomain) },
args: { arg: $utils.stringify(urlOrDomain) },
})
}
@@ -58,14 +58,14 @@ export class Validator {
}
}
validateLocation (location, originOrDomain) {
// we don't support query params, hashes, or paths (except for '/')
if (location.search.length > 0 || location.pathname.length > 1 || location.hash.length > 0) {
validateLocation (location, urlOrDomain) {
// we don't support query params
if (location.search.length > 0) {
this.onFailure()
$errUtils.throwErrByPath('origin.invalid_origin_argument', {
$errUtils.throwErrByPath('origin.invalid_url_argument', {
onFail: this.log,
args: { arg: $utils.stringify(originOrDomain) },
args: { arg: $utils.stringify(urlOrDomain) },
})
}
}
@@ -1730,8 +1730,8 @@ export default {
experiment_not_enabled: {
message: `${cmd('origin')} requires enabling the experimentalLoginFlows flag`,
},
invalid_origin_argument: {
message: `${cmd('origin')} requires the first argument to be either an origin ('https://app.example.com') or a domain name ('example.com'). The origin or domain name must not contain a path, hash, or query parameters. You passed: \`{{arg}}\``,
invalid_url_argument: {
message: `${cmd('origin')} requires the first argument to be either a url (\`https://www.example.com/path\`) or a domain name (\`example.com\`). Query parameters are not allowed. You passed: \`{{arg}}\``,
},
invalid_options_argument: {
message: `${cmd('origin')} requires the 'options' argument to be an object. You passed: \`{{arg}}\``,