mirror of
https://github.com/cypress-io/cypress.git
synced 2026-02-21 14:41:00 -06:00
* fix: update newProject ref when switching between organizations in SelectCloudProjectModal (#25730) * chore: debug page tooltip distance and artifact border (#25727) * misc: debug page tooltip distance and artifact border * add changelog entry * fix CT test * fix: Improve error handling around calls to `this.next` in middleware (#25702) * chore: update changelog validation example (#25742) * misc: improve debug loading text wrap responsiveness (#25703) * misc: Increase max failures in IATR badge to 99 (#25737) * chore: exclude collaborator issues/PRs from triage project (#25769) * feat: add --auto-cancel-after-failures flag (#25237) Co-authored-by: Emily Rohrbough <emilyrohrbough@users.noreply.github.com> Co-authored-by: Matt Schile <mschile@cypress.io> Co-authored-by: Ryan Pei <ryanppei@gmail.com> Co-authored-by: Emily Rohrbough <emilyrohrbough@yahoo.com> * chore: Update v8 snapshot cache (#25592) * chore: updating v8 snapshot cache * chore: updating v8 snapshot cache * chore: updating v8 snapshot cache * chore: updating v8 snapshot cache * chore: updating v8 snapshot cache * chore: updating v8 snapshot cache * chore: updating v8 snapshot cache * chore: updating v8 snapshot cache * chore: updating v8 snapshot cache * chore: updating v8 snapshot cache * chore: updating v8 snapshot cache * chore: updating v8 snapshot cache * chore: updating v8 snapshot cache * chore: updating v8 snapshot cache * chore: updating v8 snapshot cache * chore: updating v8 snapshot cache * chore: updating v8 snapshot cache * chore: updating v8 snapshot cache * chore: updating v8 snapshot cache * chore: updating v8 snapshot cache * chore: updating v8 snapshot cache * chore: updating v8 snapshot cache * chore: updating v8 snapshot cache * chore: updating v8 snapshot cache * chore: updating v8 snapshot cache * chore: updating v8 snapshot cache * chore: updating v8 snapshot cache * chore: updating v8 snapshot cache * chore: updating v8 snapshot cache * Update update_v8_snapshot_cache.yml * chore: updating v8 snapshot cache * chore: updating v8 snapshot cache * chore: updating v8 snapshot cache * chore: updating v8 snapshot cache * chore: updating v8 snapshot cache * chore: updating v8 snapshot cache * chore: updating v8 snapshot cache * chore: updating v8 snapshot cache * chore: updating v8 snapshot cache * chore: updating v8 snapshot cache * chore: updating v8 snapshot cache * chore: updating v8 snapshot cache * chore: updating v8 snapshot cache * chore: updating v8 snapshot cache * chore: updating v8 snapshot cache * chore: updating v8 snapshot cache --------- Co-authored-by: cypress-bot[bot] <2f0651858c6e38e0+cypress-bot[bot]@users.noreply.github.com> Co-authored-by: Ryan Manuel <ryanm@cypress.io> Co-authored-by: cypress-bot[bot] <47117332+cypress-bot[bot]@users.noreply.github.com> * fix: implement new graphql fields for spec counts (#25757) Co-authored-by: Stokes Player <stokes@cypress.io> Co-authored-by: Mike Plummer <mike-plummer@users.noreply.github.com> * feat: Bundle cy.origin() dependencies at runtime (#25626) Co-authored-by: cypress-bot[bot] <2f0651858c6e38e0+cypress-bot[bot]@users.noreply.github.com> Co-authored-by: Ryan Manuel <ryanm@cypress.io> * chore: remove zenhub from release process (#25701) Co-authored-by: Matt Schile <mschile@cypress.io> * feat: add Cypress.Commands.overwriteQuery (#25674) * feat: add Cypress.Commands.overwriteQuery Co-authored-by: Emily Rohrbough <emilyrohrbough@users.noreply.github.com> Co-authored-by: Zach Bloomquist <git@chary.us> * fix: spawn child process with process.env in macOS arm64 (#25753) Co-authored-by: Matt Schile <mschile@cypress.io> Co-authored-by: Emily Rohrbough <emilyrohrbough@users.noreply.github.com> Co-authored-by: Zach Bloomquist <github@chary.us> * chore: lint system tests in CI (#25673) * fix: Suppress filesystem errors during glob search (#25774) * chore: issue with ts-loader missing in binary and problematic esbuild norewrite construct (#25797) * chore: update changelog linting (#25809) * docs(guides): add more detail to code-signing (#25794) Co-authored-by: Emily Rohrbough <emilyrohrbough@users.noreply.github.com> * chore: update workflows.yml to include the v8 snapshot update branch (#25784) Co-authored-by: cypress-bot[bot] <+cypress-bot[bot]@users.noreply.github.com> * chore: internal request preflight (#25772) --------- Co-authored-by: Emily Rohrbough <emilyrohrbough@users.noreply.github.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: cypress-bot[bot] <2f0651858c6e38e0+cypress-bot[bot]@users.noreply.github.com> Co-authored-by: Ryan Manuel <ryanm@cypress.io> Co-authored-by: Matt Henkes <mjhenkes@gmail.com> Co-authored-by: Zach Bloomquist <git@chary.us> * chore: bump for 12.6.0 release (#25812) * chore: release @cypress/webpack-batteries-included-preprocessor-v2.4.0 [skip ci] * chore: release @cypress/webpack-preprocessor-v5.17.0 [skip ci] * test: skip flaky GitDataSource test (#25825) * chore: making our add-to-triage-board workflow reusable within the Cypress-io org (#25820) * chore: Making our add to triage workflow callable from other projects inside the Cypress-io org in Github * chore: updated cypress-example-kitchensink version (#25828) * fix: duplicate and expired cookies (#25761) * chore: add regression tests for duplicate cookies and bad expiry times * avoid prepending domain with dot for cookies that are set with the server side jar. This is to avoid the cookie being duplicated if it is set or overridden in a different context (request that can actually set the cookie or via document.domain) * feat: use cookie.toString() in the cookie patch to more accurately set cookies on the document, which should include other properties besides key=value * fix: add logic to handle expired cookies in the document.cookie patch, as well as in CDP * chore: build binary for cookie fixes for users to test * chore: change name of fixture to something more accurate * chore: comment why we are using the toughCookie toString method in the patch * [run ci] * chore: add changelog entry * [run ci] * fix: revert back to key=value when getting document.cookie as those are the only values are displayed (oversight on my end) * [run ci] * chore: make compatible with cypress.require * fix: add tests for hostOnly/non hostOnly cookies to make sure property gets sent up to automation client correctly. No longer need custom cookie prop to determine destination * [run ci] * fix: stale unit test * chore: adjust comments * [run ci] * fix: bad domain logic * [run ci] * chore: remove irrelevant comment * [run ci] * fix: adjust cookie login text to spec hostOnly cookie within the cookie patch. This should yield the same behavior as we are bound to same origin within the spec bridge * [run ci] * [run ci] * fix: allow for cookies on request of same key to take precedence over cookies in the jar, regardless of how many hierachy cookies exist in the jar * chore: fix cookie misc tests for cy.origin (dont run cy.origin) * [run ci] * chore: skip misc cookie tests in webkit as headless behavior doesn't clear cookies between tests correctly * Revert "fix: allow for cookies on request of same key to take precedence over cookies in the jar, regardless of how many hierachy cookies exist in the jar" This reverts commit17de1883ab. * [run ci] * chore: split changelog entry into two parts * chore: update logic to remove else statement and add comments * [run ci] * chore: readd windows snapshot branch in workflows * [run ci] * chore: fix workflows from bad merge * [run ci] * Revert "chore: split changelog entry into two parts" This reverts commit4352ef5f3e. * [run ci] * fix: Fix type definitions for cy.reload() (#25779) Co-authored-by: Emily Rohrbough <emilyrohrbough@users.noreply.github.com> * misc: Debug header updates (#25823) * fix: allow running tests outside Vite project root folder (#25801) * fix: allow running tests outside Vite project root folder * update snapshots * add changelog entry --------- Co-authored-by: Lachlan Miller <lachlan.miller.1990@outlook.com> * fix: mount component in [data-cy-root] (#25807) * fix(angular): mount component in [data-cy-root] * fix e2e test * add changelog entry * changelog [skip ci] * changelog --------- Co-authored-by: Lachlan Miller <lachlan.miller.1990@outlook.com> * chore: updating add to triage baord github action to use org secret (#25868) * chore: updating add to triage board github action to use org secret * chore: release @cypress/angular-v2.0.2 [skip ci] * chore: release @cypress/vite-dev-server-v5.0.3 [skip ci] * chore: Update v8 snapshot cache (#25822) Co-authored-by: cypress-bot[bot] <+cypress-bot[bot]@users.noreply.github.com> Co-authored-by: Ryan Manuel <ryanm@cypress.io> * feat: support host only cookies (#25853) * feat: allow setCookie API to take a hostOnly option * chore: add jsdoc/typescript description to render to users * chore: add changelog entry * [run ci] * chore: fix types * chore: fix cookie login tests * chore: update e2e cookie system tests * [run ci] * chore: fix cookie command tests. localhost cookies are calculated as hostOnly, which is consistent with how cypress works today * chore: fix system tests for cookies. * [run ci] * chore: fix system tests * chore: skip hostOnly assertions in webkit (for now) * [run ci] * chore: add property definitions to setCookieOptions * [run ci] * chore: add comments to hostOnly prop in firefox when setting a cookie * fix(webpack-dev-server): touch component-index during onSpecsChange to avoid writing to app file (#25861) * testing: try disabling uTimesSync and see what happens * build binaries [run ci] * fix: touch component index file instead of browser.js * build binaries [run ci] * update test * fix test * add test for custom HTML file in config * use existing component index in webpack-dev-server unit tests --------- Co-authored-by: Lachlan Miller <lachlan.miller.1990@outlook.com> * chore: release @cypress/webpack-dev-server-v3.2.4 [skip ci] * chore: improve types for server automation cookie client (#25836) * chore: improve types for automation cookies * [run ci] * fix: the cookie_behavior tests by syncing cookies immediately if … (#25855) * fix: fix the cookie_behavior tests by syncing cookies immediately if the application is already stable * chore: add changelog entry * [run ci] * chore: address comments from code review * feat: Public API for CT Framework Definitions (#25780) * chore: rework component onboarding in launchpad (#25713) * chore: refactoring and types * rework source of frameworks * revert rename * fix tests * fix more tests * types * update code * use same public API internally * rename interfaces * rename * work on dev server api * fix types * fix test * attempt to support getDevServerConfig * tests * add function to define framework [skip ci] * rework a lot of types * fix test * update tests and types * refactor * revert changes * lint * fix test * revert * remove * add "community" label [skip ci] * refactor * types * lint * fix bug * update function name * address feedback * improve types with Pick * refactor using type guard * correct label --------- Co-authored-by: Zachary Williams <ZachJW34@gmail.com> * chore: typing error * feat: scan for 3rd party ct plugins (#25749) * chore: refactoring and types * rework source of frameworks * revert rename * fix tests * fix more tests * types * update code * use same public API internally * rename interfaces * rename * work on dev server api * fix types * fix test * attempt to support getDevServerConfig * tests * add function to define framework [skip ci] * rework a lot of types * fix test * update tests and types * refactor * revert changes * lint * fix test * revert * remove * add "community" label [skip ci] * refactor * types * lint * fix bug * update function name * address feedback * feat: scan for 3rd party ct plugins * add e2e test * unit tests [run ci] * tweak resolution * rebase, address comments * fix windows paths * remove .gitignore * fix test --------- Co-authored-by: Lachlan Miller <lachlan.miller.1990@outlook.com> * lint config * spacing * try fix race cond * fix import error * build binary * try update snapshot * try using require * support namespaced definitions (#25804) * remove category * add icon prop * support esm -> cjs compiled typescript * fix test * misc: add CTA footer to launchpad framework dropdown (#25831) * remove test project dependencies * rebase * windows * windows again * add changelog entry * changelog * revert workflow * remove worklfow --------- Co-authored-by: Zachary Williams <ZachJW34@gmail.com> Co-authored-by: Adam Stone-Lord <adams@cypress.io> * chore: release @cypress/webpack-dev-server-v3.3.0 [skip ci] * fix: Add missing error message when `req.continue` is used incorrectly (#25884) --------- Co-authored-by: Adam Stone-Lord <adams@cypress.io> Co-authored-by: Zachary Williams <ZachJW34@gmail.com> Co-authored-by: Mike Plummer <mike-plummer@users.noreply.github.com> Co-authored-by: Matt Schile <mschile@cypress.io> Co-authored-by: Alejandro Estrada <estrada9166@gmail.com> Co-authored-by: Emily Rohrbough <emilyrohrbough@users.noreply.github.com> Co-authored-by: Ryan Pei <ryanppei@gmail.com> Co-authored-by: Emily Rohrbough <emilyrohrbough@yahoo.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: cypress-bot[bot] <2f0651858c6e38e0+cypress-bot[bot]@users.noreply.github.com> Co-authored-by: Ryan Manuel <ryanm@cypress.io> Co-authored-by: cypress-bot[bot] <47117332+cypress-bot[bot]@users.noreply.github.com> Co-authored-by: Mark Noonan <mark@cypress.io> Co-authored-by: Stokes Player <stokes@cypress.io> Co-authored-by: Chris Breiding <chrisbreiding@users.noreply.github.com> Co-authored-by: Zach Bloomquist <git@chary.us> Co-authored-by: willmsC <50909991+willmsC@users.noreply.github.com> Co-authored-by: Zach Bloomquist <github@chary.us> Co-authored-by: cypress-bot[bot] <+cypress-bot[bot]@users.noreply.github.com> Co-authored-by: Tim Griesser <tgriesser10@gmail.com> Co-authored-by: Matt Henkes <mjhenkes@gmail.com> Co-authored-by: semantic-release-bot <semantic-release-bot@martynus.net> Co-authored-by: Ben M <benm@cypress.io> Co-authored-by: Bill Glesias <bglesias@gmail.com> Co-authored-by: Podles <78863563+podlesny-j@users.noreply.github.com> Co-authored-by: Paolo Caleffi <p.caleffi@dreamonkey.com> Co-authored-by: Lachlan Miller <lachlan.miller.1990@outlook.com>
431 lines
14 KiB
TypeScript
431 lines
14 KiB
TypeScript
import Bluebird from 'bluebird'
|
|
import Debug from 'debug'
|
|
import isHtml from 'is-html'
|
|
import _ from 'lodash'
|
|
import stream from 'stream'
|
|
import url from 'url'
|
|
import httpsProxy from '@packages/https-proxy'
|
|
import { getRoutesForRequest } from '@packages/net-stubbing'
|
|
import { concatStream, cors } from '@packages/network'
|
|
import { graphqlWS } from '@packages/graphql/src/makeGraphQLServer'
|
|
|
|
import * as errors from './errors'
|
|
import fileServer from './file_server'
|
|
import { OpenServerOptions, ServerBase } from './server-base'
|
|
import type { SocketE2E } from './socket-e2e'
|
|
import appData from './util/app_data'
|
|
import * as ensureUrl from './util/ensure-url'
|
|
import headersUtil from './util/headers'
|
|
import statusCode from './util/status_code'
|
|
import type { Cfg } from './project-base'
|
|
|
|
type WarningErr = Record<string, any>
|
|
|
|
const fullyQualifiedRe = /^https?:\/\//
|
|
const htmlContentTypesRe = /^(text\/html|application\/xhtml)/i
|
|
|
|
const debug = Debug('cypress:server:server-e2e')
|
|
|
|
const isResponseHtml = function (contentType, responseBuffer) {
|
|
if (contentType) {
|
|
// want to match anything starting with 'text/html'
|
|
// including 'text/html;charset=utf-8' and 'Text/HTML'
|
|
// https://github.com/cypress-io/cypress/issues/8506
|
|
return htmlContentTypesRe.test(contentType)
|
|
}
|
|
|
|
const body = _.invoke(responseBuffer, 'toString')
|
|
|
|
if (body) {
|
|
return isHtml(body)
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
export class ServerE2E extends ServerBase<SocketE2E> {
|
|
private _urlResolver: Bluebird<Record<string, any>> | null
|
|
// the initialization of this variable is only precautionary as the actual config value is applied when the server is created
|
|
private skipDomainInjectionForDomains: string[] | null = null
|
|
|
|
constructor () {
|
|
super()
|
|
|
|
this._urlResolver = null
|
|
}
|
|
|
|
open (config: Cfg, options: OpenServerOptions) {
|
|
return super.open(config, { ...options, testingType: 'e2e' })
|
|
}
|
|
|
|
createServer (app, config, onWarning): Bluebird<[number, WarningErr?]> {
|
|
return new Bluebird((resolve, reject) => {
|
|
const { port, fileServerFolder, socketIoRoute, baseUrl, experimentalSkipDomainInjection } = config
|
|
|
|
this._server = this._createHttpServer(app)
|
|
this.skipDomainInjectionForDomains = experimentalSkipDomainInjection
|
|
const onError = (err) => {
|
|
// if the server bombs before starting
|
|
// and the err no is EADDRINUSE
|
|
// then we know to display the custom err message
|
|
if (err.code === 'EADDRINUSE') {
|
|
return reject(this.portInUseErr(port))
|
|
}
|
|
}
|
|
|
|
debug('createServer connecting to server')
|
|
|
|
this.server.on('connect', this.onConnect.bind(this))
|
|
this.server.on('upgrade', (req, socket, head) => this.onUpgrade(req, socket, head, socketIoRoute))
|
|
this.server.once('error', onError)
|
|
|
|
this._graphqlWS = graphqlWS(this.server, `${socketIoRoute}-graphql`)
|
|
|
|
return this._listen(port, (err) => {
|
|
// if the server bombs before starting
|
|
// and the err no is EADDRINUSE
|
|
// then we know to display the custom err message
|
|
if (err.code === 'EADDRINUSE') {
|
|
return reject(this.portInUseErr(port))
|
|
}
|
|
})
|
|
.then((port) => {
|
|
return Bluebird.all([
|
|
httpsProxy.create(appData.path('proxy'), port, {
|
|
onRequest: this.callListeners.bind(this),
|
|
onUpgrade: this.onSniUpgrade.bind(this),
|
|
}),
|
|
|
|
fileServer.create(fileServerFolder),
|
|
])
|
|
.spread((httpsProxy, fileServer) => {
|
|
this._httpsProxy = httpsProxy
|
|
this._fileServer = fileServer
|
|
|
|
// if we have a baseUrl let's go ahead
|
|
// and make sure the server is connectable!
|
|
if (baseUrl) {
|
|
this._baseUrl = baseUrl
|
|
|
|
if (config.isTextTerminal) {
|
|
return this._retryBaseUrlCheck(baseUrl, onWarning)
|
|
.return(null)
|
|
.catch((e) => {
|
|
debug(e)
|
|
|
|
return reject(errors.get('CANNOT_CONNECT_BASE_URL'))
|
|
})
|
|
}
|
|
|
|
return ensureUrl.isListening(baseUrl)
|
|
.return(null)
|
|
.catch((err) => {
|
|
debug('ensuring baseUrl (%s) errored: %o', baseUrl, err)
|
|
|
|
return errors.get('CANNOT_CONNECT_BASE_URL_WARNING', baseUrl)
|
|
})
|
|
}
|
|
}).then((warning) => {
|
|
// once we open set the domain to root by default
|
|
// which prevents a situation where navigating
|
|
// to http sites redirects to /__/ cypress
|
|
this._remoteStates.set(baseUrl != null ? baseUrl : '<root>')
|
|
|
|
return resolve([port, warning])
|
|
})
|
|
})
|
|
})
|
|
}
|
|
|
|
startWebsockets (automation, config, options: Record<string, unknown> = {}) {
|
|
options.onResolveUrl = this._onResolveUrl.bind(this)
|
|
|
|
return super.startWebsockets(automation, config, options)
|
|
}
|
|
|
|
_onResolveUrl (urlStr, headers, automationRequest, options: Record<string, any> = { headers: {} }) {
|
|
let p
|
|
|
|
debug('resolving visit %o', {
|
|
url: urlStr,
|
|
headers,
|
|
options,
|
|
})
|
|
|
|
// always clear buffers - reduces the possibility of a random HTTP request
|
|
// accidentally retrieving buffered content at the wrong time
|
|
this._networkProxy?.reset()
|
|
|
|
const startTime = Date.now()
|
|
|
|
// if we have an existing url resolver
|
|
// in flight then cancel it
|
|
if (this._urlResolver) {
|
|
this._urlResolver.cancel()
|
|
}
|
|
|
|
const request = this.request
|
|
|
|
let handlingLocalFile = false
|
|
const previousRemoteState = this._remoteStates.current()
|
|
const previousRemoteStateIsPrimary = this._remoteStates.isPrimarySuperDomainOrigin(previousRemoteState.origin)
|
|
const primaryRemoteState = this._remoteStates.getPrimary()
|
|
|
|
// nuke any hashes from our url since
|
|
// those those are client only and do
|
|
// not apply to http requests
|
|
urlStr = url.parse(urlStr)
|
|
urlStr.hash = null
|
|
urlStr = urlStr.format()
|
|
|
|
const originalUrl = urlStr
|
|
|
|
let reqStream = null
|
|
let currentPromisePhase = null
|
|
|
|
const runPhase = (fn) => {
|
|
return currentPromisePhase = fn()
|
|
}
|
|
|
|
const matchesNetStubbingRoute = (requestOptions) => {
|
|
const proxiedReq = {
|
|
proxiedUrl: requestOptions.url,
|
|
resourceType: 'document',
|
|
..._.pick(requestOptions, ['headers', 'method']),
|
|
// TODO: add `body` here once bodies can be statically matched
|
|
}
|
|
|
|
// @ts-ignore
|
|
const iterator = getRoutesForRequest(this.netStubbingState?.routes, proxiedReq)
|
|
// If the iterator is exhausted (done) on the first try, then 0 matches were found
|
|
const zeroMatches = iterator.next().done
|
|
|
|
return !zeroMatches
|
|
}
|
|
|
|
return this._urlResolver = (p = new Bluebird<Record<string, any>>((resolve, reject, onCancel) => {
|
|
let urlFile
|
|
|
|
onCancel?.(() => {
|
|
p.currentPromisePhase = currentPromisePhase
|
|
p.reqStream = reqStream
|
|
|
|
_.invoke(reqStream, 'abort')
|
|
|
|
return _.invoke(currentPromisePhase, 'cancel')
|
|
})
|
|
|
|
const redirects: any[] = []
|
|
let newUrl: string | null = null
|
|
|
|
if (!fullyQualifiedRe.test(urlStr)) {
|
|
handlingLocalFile = true
|
|
|
|
options.headers['x-cypress-authorization'] = this._fileServer?.token
|
|
|
|
const state = this._remoteStates.set(urlStr, options)
|
|
|
|
// TODO: Update url.resolve signature to not use deprecated methods
|
|
urlFile = url.resolve(state.fileServer as string, urlStr)
|
|
urlStr = url.resolve(state.origin as string, urlStr)
|
|
}
|
|
|
|
const onReqError = (err) => {
|
|
// only restore the previous state
|
|
// if our promise is still pending
|
|
if (p.isPending()) {
|
|
restorePreviousRemoteState(previousRemoteState, previousRemoteStateIsPrimary)
|
|
}
|
|
|
|
return reject(err)
|
|
}
|
|
|
|
const onReqStreamReady = (str) => {
|
|
reqStream = str
|
|
|
|
return str
|
|
.on('error', onReqError)
|
|
.on('response', (incomingRes) => {
|
|
debug(
|
|
'resolve:url headers received, buffering response %o',
|
|
_.pick(incomingRes, 'headers', 'statusCode'),
|
|
)
|
|
|
|
if (newUrl == null) {
|
|
newUrl = urlStr
|
|
}
|
|
|
|
return runPhase(() => {
|
|
// get the cookies that would be sent with this request so they can be rehydrated
|
|
return automationRequest('get:cookies', {
|
|
domain: cors.getSuperDomain(newUrl),
|
|
})
|
|
.then((cookies) => {
|
|
const statusIs2xxOrAllowedFailure = () => {
|
|
// is our status code in the 2xx range, or have we disabled failing
|
|
// on status code?
|
|
return statusCode.isOk(incomingRes.statusCode) || options.failOnStatusCode === false
|
|
}
|
|
|
|
const isOk = statusIs2xxOrAllowedFailure()
|
|
const contentType = headersUtil.getContentType(incomingRes)
|
|
|
|
const details: Record<string, unknown> = {
|
|
isOkStatusCode: isOk,
|
|
contentType,
|
|
url: newUrl,
|
|
status: incomingRes.statusCode,
|
|
cookies,
|
|
statusText: statusCode.getText(incomingRes.statusCode),
|
|
redirects,
|
|
originalUrl,
|
|
}
|
|
|
|
// does this response have this cypress header?
|
|
const fp = incomingRes.headers['x-cypress-file-path']
|
|
|
|
if (fp) {
|
|
// if so we know this is a local file request
|
|
details.filePath = fp
|
|
}
|
|
|
|
debug('setting details resolving url %o', details)
|
|
|
|
const concatStr = concatStream((responseBuffer) => {
|
|
// buffer the entire response before resolving.
|
|
// this allows us to detect & reject ETIMEDOUT errors
|
|
// where the headers have been sent but the
|
|
// connection hangs before receiving a body.
|
|
|
|
// if there is not a content-type, try to determine
|
|
// if the response content is HTML-like
|
|
// https://github.com/cypress-io/cypress/issues/1727
|
|
details.isHtml = isResponseHtml(contentType, responseBuffer)
|
|
|
|
debug('resolve:url response ended, setting buffer %o', { newUrl, details })
|
|
|
|
details.totalTime = Date.now() - startTime
|
|
|
|
// buffer the response and set the remote state if this is a successful html response
|
|
// TODO: think about moving this logic back into the frontend so that the driver can be in control
|
|
// of when to buffer and set the remote state
|
|
if (isOk && details.isHtml) {
|
|
const urlDoesNotMatchPolicyBasedOnDomain = options.hasAlreadyVisitedUrl
|
|
&& !cors.urlMatchesPolicyBasedOnDomain(primaryRemoteState.origin, newUrl || '', { skipDomainInjectionForDomains: this.skipDomainInjectionForDomains })
|
|
|| options.isFromSpecBridge
|
|
|
|
if (!handlingLocalFile) {
|
|
this._remoteStates.set(newUrl as string, options, !urlDoesNotMatchPolicyBasedOnDomain)
|
|
}
|
|
|
|
const responseBufferStream = new stream.PassThrough({
|
|
highWaterMark: Number.MAX_SAFE_INTEGER,
|
|
})
|
|
|
|
responseBufferStream.end(responseBuffer)
|
|
|
|
this._networkProxy?.setHttpBuffer({
|
|
url: newUrl,
|
|
stream: responseBufferStream,
|
|
details,
|
|
originalUrl,
|
|
response: incomingRes,
|
|
urlDoesNotMatchPolicyBasedOnDomain,
|
|
})
|
|
} else {
|
|
// TODO: move this logic to the driver too for
|
|
// the same reasons listed above
|
|
restorePreviousRemoteState(previousRemoteState, previousRemoteStateIsPrimary)
|
|
}
|
|
|
|
details.isPrimarySuperDomainOrigin = this._remoteStates.isPrimarySuperDomainOrigin(newUrl!)
|
|
|
|
return resolve(details)
|
|
})
|
|
|
|
return str.pipe(concatStr)
|
|
}).catch(onReqError)
|
|
})
|
|
})
|
|
}
|
|
|
|
const restorePreviousRemoteState = (previousRemoteState: Cypress.RemoteState, previousRemoteStateIsPrimary: boolean) => {
|
|
this._remoteStates.set(previousRemoteState, {}, previousRemoteStateIsPrimary)
|
|
}
|
|
|
|
// if they're POSTing an object, querystringify their POST body
|
|
if ((options.method === 'POST') && _.isObject(options.body)) {
|
|
options.form = options.body
|
|
delete options.body
|
|
}
|
|
|
|
_.assign(options, {
|
|
// turn off gzip since we need to eventually
|
|
// rewrite these contents
|
|
gzip: false,
|
|
url: urlFile != null ? urlFile : urlStr,
|
|
headers: _.assign({
|
|
accept: 'text/html,*/*',
|
|
}, options.headers),
|
|
onBeforeReqInit: runPhase,
|
|
followRedirect (incomingRes) {
|
|
const status = incomingRes.statusCode
|
|
const next = incomingRes.headers.location
|
|
|
|
const curr = newUrl != null ? newUrl : urlStr
|
|
|
|
newUrl = url.resolve(curr, next)
|
|
|
|
redirects.push([status, newUrl].join(': '))
|
|
|
|
return true
|
|
},
|
|
})
|
|
|
|
if (matchesNetStubbingRoute(options)) {
|
|
// TODO: this is being used to force cy.visits to be interceptable by network stubbing
|
|
// however, network errors will be obsfucated by the proxying so this is not an ideal solution
|
|
_.merge(options, {
|
|
proxy: `http://127.0.0.1:${this._port()}`,
|
|
agent: null,
|
|
headers: {
|
|
'x-cypress-resolving-url': '1',
|
|
},
|
|
})
|
|
}
|
|
|
|
debug('sending request with options %o', options)
|
|
|
|
return runPhase(() => {
|
|
// @ts-ignore
|
|
return request.sendStream(headers, automationRequest, options)
|
|
.then((createReqStream) => {
|
|
const stream = createReqStream()
|
|
|
|
return onReqStreamReady(stream)
|
|
}).catch(onReqError)
|
|
})
|
|
}))
|
|
}
|
|
|
|
onTestFileChange (filePath) {
|
|
return this.socket.onTestFileChange(filePath)
|
|
}
|
|
|
|
_retryBaseUrlCheck (baseUrl, onWarning) {
|
|
return ensureUrl.retryIsListening(baseUrl, {
|
|
retryIntervals: [3000, 3000, 4000],
|
|
onRetry ({ attempt, delay, remaining }) {
|
|
const warning = errors.get('CANNOT_CONNECT_BASE_URL_RETRYING', {
|
|
remaining,
|
|
attempt,
|
|
delay,
|
|
baseUrl,
|
|
})
|
|
|
|
return onWarning(warning)
|
|
},
|
|
})
|
|
}
|
|
}
|