refactor: shared routing for E2E and CT (#27618)

* remove server e2e

* missing functions

* fixing tests

* minimize server-ct

* linting

* trying to fix

* remove unused file

* routes

* update usage

* remove routes

* remove ct specific route

* revert code
This commit is contained in:
Lachlan Miller
2023-08-25 07:47:42 +10:00
committed by GitHub
parent 4288634e0b
commit 65cb16f900
19 changed files with 560 additions and 724 deletions

View File

@@ -20,7 +20,7 @@ import type { BrowserInstance } from './browsers/types'
const debug = Debug('cypress:server:open_project')
export class OpenProject {
private projectBase: ProjectBase<any> | null = null
private projectBase: ProjectBase | null = null
relaunchBrowser: (() => Promise<BrowserInstance | null>) = () => {
throw new Error('bad relaunch')
}
@@ -243,7 +243,7 @@ export class OpenProject {
debug(`New url is ${newSpecUrl}`)
this.projectBase.server._socket.changeToUrl(newSpecUrl)
this.projectBase.server.socket.changeToUrl(newSpecUrl)
}
changeUrlToDebug (runNumber: number) {
@@ -259,7 +259,7 @@ export class OpenProject {
debug(`New url is ${newUrl}`)
this.projectBase.server._socket.changeToUrl(newUrl)
this.projectBase.server.socket.changeToUrl(newUrl)
}
/**
@@ -268,7 +268,7 @@ export class OpenProject {
* @returns
*/
updateTelemetryContext (context: string) {
return this.projectBase?.server._socket.updateTelemetryContext(context)
return this.projectBase?.server.socket.updateTelemetryContext(context)
}
// close existing open project if it exists, for example

View File

@@ -13,8 +13,6 @@ import preprocessor from './plugins/preprocessor'
import runEvents from './plugins/run_events'
import Reporter from './reporter'
import * as savedState from './saved_state'
import { ServerCt } from './server-ct'
import { ServerE2E } from './server-e2e'
import { SocketCt } from './socket-ct'
import { SocketE2E } from './socket-e2e'
import { ensureProp } from './util/class-helpers'
@@ -23,11 +21,13 @@ import system from './util/system'
import type { BannersState, FoundBrowser, FoundSpec, OpenProjectLaunchOptions, ReceivedCypressOptions, ResolvedConfigurationOptions, TestingType, VideoRecording } from '@packages/types'
import { DataContext, getCtx } from '@packages/data-context'
import { createHmac } from 'crypto'
import { ServerBase } from './server-base'
export interface Cfg extends ReceivedCypressOptions {
projectId?: string
projectRoot: string
proxyServer?: Cypress.RuntimeConfigOptions['proxyUrl']
fileServerFolder?: Cypress.ResolvedConfigOptions['fileServerFolder']
testingType: TestingType
exit?: boolean
state?: {
@@ -48,15 +48,13 @@ const debug = Debug('cypress:server:project')
type StartWebsocketOptions = Pick<Cfg, 'socketIoCookie' | 'namespace' | 'screenshotsFolder' | 'report' | 'reporter' | 'reporterOptions' | 'projectRoot'>
export type Server = ServerE2E | ServerCt
export class ProjectBase<TServer extends Server> extends EE {
export class ProjectBase extends EE {
// id is sha256 of projectRoot
public id: string
protected ctx: DataContext
protected _cfg?: Cfg
protected _server?: TServer
protected _server?: ServerBase<any>
protected _automation?: Automation
private _recordTests?: any = null
private _isServerOpen: boolean = false
@@ -137,12 +135,6 @@ export class ProjectBase<TServer extends Server> extends EE {
return this._server?.remoteStates
}
createServer (testingType: Cypress.TestingType) {
return testingType === 'e2e'
? new ServerE2E() as TServer
: new ServerCt() as TServer
}
async open () {
debug('opening project instance %s', this.projectRoot)
debug('project open options %o', this.options)
@@ -151,7 +143,7 @@ export class ProjectBase<TServer extends Server> extends EE {
process.chdir(this.projectRoot)
this._server = this.createServer(this.testingType)
this._server = new ServerBase()
const [port, warning] = await this._server.open(cfg, {
getCurrentBrowser: () => this.browser,

View File

@@ -1,61 +0,0 @@
import Debug from 'debug'
import { Request, Response, Router } from 'express'
import type { InitializeRoutes } from './routes'
import send from 'send'
import { getPathToDist } from '@packages/resolve-dist'
const debug = Debug('cypress:server:routes-ct')
const serveChunk = (req: Request, res: Response, clientRoute: string) => {
let pathToFile = getPathToDist('runner', req.originalUrl.replace(clientRoute, ''))
return send(req, pathToFile).pipe(res)
}
export const createRoutesCT = ({
config,
nodeProxy,
}: InitializeRoutes) => {
const routesCt = Router()
routesCt.get(`/${config.namespace}/static/*`, (req, res) => {
debug(`proxying to %s/static, originalUrl %s`, config.namespace, req.originalUrl)
const pathToFile = getPathToDist('static', req.params[0])
return send(req, pathToFile)
.pipe(res)
})
// user app code + spec code
// default mounted to /__cypress/src/*
routesCt.get(`${config.devServerPublicPathRoute}*`, (req, res) => {
debug(`proxying to %s, originalUrl %s`, config.devServerPublicPathRoute, req.originalUrl)
// user the node proxy here instead of the network proxy
// to avoid the user accidentally intercepting and modifying
// their own app.js files + spec.js files
nodeProxy.web(req, res, {}, (e) => {
if (e) {
// eslint-disable-next-line
debug('Proxy request error. This is likely the socket hangup issue, we can basically ignore this because the stream will automatically continue once the asset will be available', e)
}
})
})
const clientRoute = config.clientRoute
if (!clientRoute) {
throw Error(`clientRoute is required. Received ${clientRoute}`)
}
// enables runner to make a dynamic import
routesCt.get([
`${clientRoute}ctChunk-*`,
`${clientRoute}vendors~ctChunk-*`,
], (req, res) => {
debug('Serving Cypress front-end chunk by requested URL:', req.url)
serveChunk(req, res, clientRoute)
})
return routesCt
}

View File

@@ -1,113 +0,0 @@
import bodyParser from 'body-parser'
import Debug from 'debug'
import { Router } from 'express'
import path from 'path'
import AppData from './util/app_data'
import CacheBuster from './util/cache_buster'
import specController from './controllers/spec'
import reporter from './controllers/reporter'
import client from './controllers/client'
import files from './controllers/files'
import type { InitializeRoutes } from './routes'
import * as plugins from './plugins'
import { privilegedCommandsManager } from './privileged-commands/privileged-commands-manager'
const debug = Debug('cypress:server:routes-e2e')
export const createRoutesE2E = ({
config,
networkProxy,
onError,
}: InitializeRoutes) => {
const routesE2E = Router()
// routing for the actual specs which are processed automatically
// this could be just a regular .js file or a .coffee file
routesE2E.get(`/${config.namespace}/tests`, (req, res, next) => {
// slice out the cache buster
const test = CacheBuster.strip(req.query.p)
specController.handle(test, req, res, config, next, onError)
})
routesE2E.post(`/${config.namespace}/process-origin-callback`, bodyParser.json(), async (req, res) => {
try {
const { file, fn, projectRoot } = req.body
debug('process origin callback: %s', fn)
const contents = await plugins.execute('_process:cross:origin:callback', { file, fn, projectRoot })
res.json({ contents })
} catch (err) {
const errorMessage = `Processing the origin callback errored:\n\n${err.stack}`
debug(errorMessage)
res.json({
error: errorMessage,
})
}
})
routesE2E.get(`/${config.namespace}/socket.io.js`, (req, res) => {
client.handle(req, res)
})
routesE2E.get(`/${config.namespace}/reporter/*`, (req, res) => {
reporter.handle(req, res)
})
routesE2E.get(`/${config.namespace}/automation/getLocalStorage`, (req, res) => {
res.sendFile(path.join(__dirname, './html/get-local-storage.html'))
})
routesE2E.get(`/${config.namespace}/automation/setLocalStorage`, (req, res) => {
const origin = req.originalUrl.slice(req.originalUrl.indexOf('?') + 1)
networkProxy.http.getRenderedHTMLOrigins()[origin] = true
res.sendFile(path.join(__dirname, './html/set-local-storage.html'))
})
routesE2E.get(`/${config.namespace}/source-maps/:id.map`, (req, res) => {
networkProxy.handleSourceMapRequest(req, res)
})
// special fallback - serve dist'd (bundled/static) files from the project path folder
routesE2E.get(`/${config.namespace}/bundled/*`, (req, res) => {
const file = AppData.getBundledFilePath(config.projectRoot, path.join('src', req.params[0]))
debug(`Serving dist'd bundle at file path: %o`, { path: file, url: req.url })
res.sendFile(file, { etag: false })
})
// TODO: The below route is not technically correct for cypress in cypress tests.
// We should be using 'config.namespace' to provide the namespace instead of hard coding __cypress, however,
// In the runner when we create the spec bridge we have no knowledge of the namespace used by the server so
// we create a spec bridge for the namespace of the server specified in the config, but that server hasn't been created.
// To fix this I think we need to find a way to listen in the cypress in cypress server for routes from the server the
// cypress instance thinks should exist, but that's outside the current scope.
routesE2E.get('/__cypress/spec-bridge-iframes', (req, res) => {
debug('handling cross-origin iframe for domain: %s', req.hostname)
// Chrome plans to make document.domain immutable in Chrome 109, with the default value
// of the Origin-Agent-Cluster header becoming 'true'. We explicitly disable this header
// in the spec-bridge-iframe to allow setting document.domain to the bare domain
// to guarantee the spec bridge can communicate with the injected code.
// @see https://github.com/cypress-io/cypress/issues/25010
res.setHeader('Origin-Agent-Cluster', '?0')
files.handleCrossOriginIframe(req, res, config)
})
routesE2E.post(`/${config.namespace}/add-verified-command`, bodyParser.json(), (req, res) => {
privilegedCommandsManager.addVerifiedCommand(req.body)
res.sendStatus(204)
})
return routesE2E
}

View File

@@ -3,7 +3,6 @@ import Debug from 'debug'
import { ErrorRequestHandler, Request, Router } from 'express'
import send from 'send'
import { getPathToDist } from '@packages/resolve-dist'
import type { NetworkProxy } from '@packages/proxy'
import type { Cfg } from './project-base'
import xhrs from './controllers/xhrs'
@@ -13,6 +12,16 @@ import type { FoundSpec } from '@packages/types'
import { getCtx } from '@packages/data-context'
import { graphQLHTTP } from '@packages/graphql/src/makeGraphQLServer'
import type { RemoteStates } from './remote_states'
import bodyParser from 'body-parser'
import path from 'path'
import AppData from './util/app_data'
import CacheBuster from './util/cache_buster'
import specController from './controllers/spec'
import reporter from './controllers/reporter'
import client from './controllers/client'
import files from './controllers/files'
import * as plugins from './plugins'
import { privilegedCommandsManager } from './privileged-commands/privileged-commands-manager'
const debug = Debug('cypress:server:routes')
@@ -33,10 +42,96 @@ export const createCommonRoutes = ({
getSpec,
remoteStates,
nodeProxy,
onError,
}: InitializeRoutes) => {
const router = Router()
const { clientRoute, namespace } = config
router.get(`/${config.namespace}/tests`, (req, res, next) => {
// slice out the cache buster
const test = CacheBuster.strip(req.query.p)
specController.handle(test, req, res, config, next, onError)
})
router.post(`/${config.namespace}/process-origin-callback`, bodyParser.json(), async (req, res) => {
try {
const { file, fn, projectRoot } = req.body
debug('process origin callback: %s', fn)
const contents = await plugins.execute('_process:cross:origin:callback', { file, fn, projectRoot })
res.json({ contents })
} catch (err) {
const errorMessage = `Processing the origin callback errored:\n\n${err.stack}`
debug(errorMessage)
res.json({
error: errorMessage,
})
}
})
router.get(`/${config.namespace}/socket.io.js`, (req, res) => {
client.handle(req, res)
})
router.get(`/${config.namespace}/reporter/*`, (req, res) => {
reporter.handle(req, res)
})
router.get(`/${config.namespace}/automation/getLocalStorage`, (req, res) => {
res.sendFile(path.join(__dirname, './html/get-local-storage.html'))
})
router.get(`/${config.namespace}/automation/setLocalStorage`, (req, res) => {
const origin = req.originalUrl.slice(req.originalUrl.indexOf('?') + 1)
networkProxy.http.getRenderedHTMLOrigins()[origin] = true
res.sendFile(path.join(__dirname, './html/set-local-storage.html'))
})
router.get(`/${config.namespace}/source-maps/:id.map`, (req, res) => {
networkProxy.handleSourceMapRequest(req, res)
})
// special fallback - serve dist'd (bundled/static) files from the project path folder
router.get(`/${config.namespace}/bundled/*`, (req, res) => {
const file = AppData.getBundledFilePath(config.projectRoot, path.join('src', req.params[0]))
debug(`Serving dist'd bundle at file path: %o`, { path: file, url: req.url })
res.sendFile(file, { etag: false })
})
// TODO: The below route is not technically correct for cypress in cypress tests.
// We should be using 'config.namespace' to provide the namespace instead of hard coding __cypress, however,
// In the runner when we create the spec bridge we have no knowledge of the namespace used by the server so
// we create a spec bridge for the namespace of the server specified in the config, but that server hasn't been created.
// To fix this I think we need to find a way to listen in the cypress in cypress server for routes from the server the
// cypress instance thinks should exist, but that's outside the current scope.
router.get('/__cypress/spec-bridge-iframes', (req, res) => {
debug('handling cross-origin iframe for domain: %s', req.hostname)
// Chrome plans to make document.domain immutable in Chrome 109, with the default value
// of the Origin-Agent-Cluster header becoming 'true'. We explicitly disable this header
// in the spec-bridge-iframe to allow setting document.domain to the bare domain
// to guarantee the spec bridge can communicate with the injected code.
// @see https://github.com/cypress-io/cypress/issues/25010
res.setHeader('Origin-Agent-Cluster', '?0')
files.handleCrossOriginIframe(req, res, config)
})
router.post(`/${config.namespace}/add-verified-command`, bodyParser.json(), (req, res) => {
privilegedCommandsManager.addVerifiedCommand(req.body)
res.sendStatus(204)
})
if (process.env.CYPRESS_INTERNAL_VITE_DEV) {
const proxy = httpProxy.createProxyServer({
target: `http://localhost:${process.env.CYPRESS_INTERNAL_VITE_APP_PORT}/`,
@@ -102,6 +197,24 @@ export const createCommonRoutes = ({
return send(req, pathToFile).pipe(res)
})
// user app code + spec code
// default mounted to /__cypress/src/*
// TODO: Remove this - only needed for Cy in Cy testing for unknown reasons.
if (process.env.CYPRESS_INTERNAL_E2E_TESTING_SELF) {
router.get(`${config.devServerPublicPathRoute}*`, (req, res) => {
debug(`proxying to %s, originalUrl %s`, config.devServerPublicPathRoute, req.originalUrl)
// user the node proxy here instead of the network proxy
// to avoid the user accidentally intercepting and modifying
// their own app.js files + spec.js files
nodeProxy.web(req, res, {}, (e) => {
if (e) {
// eslint-disable-next-line
debug('Proxy request error. This is likely the socket hangup issue, we can basically ignore this because the stream will automatically continue once the asset will be available', e)
}
})
})
}
router.all('*', (req, res) => {
networkProxy.handleHttpRequest(req, res)
})

View File

@@ -4,6 +4,7 @@ import compression from 'compression'
import Debug from 'debug'
import EventEmitter from 'events'
import evilDns from 'evil-dns'
import * as ensureUrl from './util/ensure-url'
import express, { Express } from 'express'
import http from 'http'
import httpProxy from 'http-proxy'
@@ -11,9 +12,9 @@ import _ from 'lodash'
import type { AddressInfo } from 'net'
import url from 'url'
import la from 'lazy-ass'
import type httpsProxy from '@packages/https-proxy'
import { netStubbingState, NetStubbingState } from '@packages/net-stubbing'
import { agent, clientCertificates, cors, httpUtils, uri } from '@packages/network'
import httpsProxy from '@packages/https-proxy'
import { getRoutesForRequest, netStubbingState, NetStubbingState } from '@packages/net-stubbing'
import { agent, clientCertificates, cors, httpUtils, uri, concatStream } from '@packages/network'
import { NetworkProxy, BrowserPreRequest } from '@packages/proxy'
import type { SocketCt } from './socket-ct'
import * as errors from './errors'
@@ -27,16 +28,41 @@ import { createInitialWorkers } from '@packages/rewriter'
import type { Cfg } from './project-base'
import type { Browser } from '@packages/server/lib/browsers/types'
import { InitializeRoutes, createCommonRoutes } from './routes'
import { createRoutesE2E } from './routes-e2e'
import { createRoutesCT } from './routes-ct'
import type { FoundSpec } from '@packages/types'
import type { FoundSpec, TestingType } from '@packages/types'
import type { Server as WebSocketServer } from 'ws'
import { RemoteStates } from './remote_states'
import { cookieJar, SerializableAutomationCookie } from './util/cookies'
import { resourceTypeAndCredentialManager, ResourceTypeAndCredentialManager } from './util/resourceTypeAndCredentialManager'
import fileServer from './file_server'
import appData from './util/app_data'
import { graphqlWS } from '@packages/graphql/src/makeGraphQLServer'
import statusCode from './util/status_code'
import headersUtil from './util/headers'
import stream from 'stream'
import isHtml from 'is-html'
const debug = Debug('cypress:server:server-base')
const fullyQualifiedRe = /^https?:\/\//
const htmlContentTypesRe = /^(text\/html|application\/xhtml)/i
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
}
const _isNonProxiedRequest = (req) => {
// proxied HTTP requests have a URL like: "http://example.com/foo"
// non-proxied HTTP requests have a URL like: "/foo"
@@ -111,7 +137,7 @@ export interface OpenServerOptions {
shouldCorrelatePreRequests: () => boolean
}
export abstract class ServerBase<TSocket extends SocketE2E | SocketCt> {
export class ServerBase<TSocket extends SocketE2E | SocketCt> {
private _middleware
protected request: Request
protected isListening: boolean
@@ -129,6 +155,9 @@ export abstract class ServerBase<TSocket extends SocketE2E | SocketCt> {
protected _eventBus: EventEmitter
protected _remoteStates: RemoteStates
private getCurrentBrowser: undefined | (() => Browser)
private skipDomainInjectionForDomains: string[] | null = null
private _urlResolver: Bluebird<Record<string, any>> | null = null
private testingType?: TestingType
constructor () {
this.isListening = false
@@ -192,11 +221,90 @@ export abstract class ServerBase<TSocket extends SocketE2E | SocketCt> {
this.socket.localBus.on('request:sent:with:credentials', this.resourceTypeAndCredentialManager.set)
}
abstract createServer (
createServer (
app: Express,
config: Cfg,
onWarning: unknown,
): Bluebird<[number, WarningErr?]>
): 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])
})
})
})
}
open (config: Cfg, {
getSpec,
@@ -209,11 +317,12 @@ export abstract class ServerBase<TSocket extends SocketE2E | SocketCt> {
exit,
}: OpenServerOptions) {
debug('server open')
this.testingType = testingType
la(_.isPlainObject(config), 'expected plain config object', config)
if (!config.baseUrl && testingType === 'component') {
throw new Error('ServerCt#open called without config.baseUrl.')
throw new Error('Server#open called without config.baseUrl.')
}
const app = this.createExpressApp(config)
@@ -253,11 +362,6 @@ export abstract class ServerBase<TSocket extends SocketE2E | SocketCt> {
this.setupCrossOriginRequestHandling()
const runnerSpecificRouter = testingType === 'e2e'
? createRoutesE2E(routeOptions)
: createRoutesCT(routeOptions)
app.use(runnerSpecificRouter)
app.use(createCommonRoutes(routeOptions))
return this.createServer(app, config, onWarning)
@@ -345,6 +449,9 @@ export abstract class ServerBase<TSocket extends SocketE2E | SocketCt> {
}
startWebsockets (automation, config, options: Record<string, unknown> = {}) {
// e2e only?
options.onResolveUrl = this._onResolveUrl.bind(this)
options.onRequest = this._onRequest.bind(this)
options.netStubbingState = this.netStubbingState
options.getRenderedHTMLOrigins = this._networkProxy?.http.getRenderedHTMLOrigins
@@ -574,4 +681,293 @@ export abstract class ServerBase<TSocket extends SocketE2E | SocketCt> {
return this.httpsProxy.connect(req, socket, head)
}
_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)
},
})
}
_onResolveUrl (urlStr, headers, automationRequest, options: Record<string, any> = { headers: {} }) {
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
}
let p
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)
})
}))
}
destroyAut () {
if (this.testingType === 'component' && 'destroyAut' in this.socket) {
return this.socket.destroyAut()
}
return
}
}

View File

@@ -1,55 +0,0 @@
import Bluebird from 'bluebird'
import httpsProxy from '@packages/https-proxy'
import { OpenServerOptions, ServerBase } from '@packages/server/lib/server-base'
import appData from '@packages/server/lib/util/app_data'
import type { SocketCt } from './socket-ct'
import type { Cfg } from '@packages/server/lib/project-base'
import { graphqlWS } from '@packages/graphql/src/makeGraphQLServer'
type WarningErr = Record<string, any>
export class ServerCt extends ServerBase<SocketCt> {
open (config: Cfg, options: OpenServerOptions) {
return super.open(config, { ...options, testingType: 'component' })
}
createServer (app, config, onWarning): Bluebird<[number, WarningErr?]> {
return new Bluebird((resolve, reject) => {
const { port, baseUrl, socketIoRoute } = config
this._server = this._createHttpServer(app)
this.server.on('connect', this.onConnect.bind(this))
this.server.on('upgrade', (req, socket, head) => this.onUpgrade(req, socket, head, socketIoRoute))
this._graphqlWS = graphqlWS(this.server, `${socketIoRoute}-graphql`)
return this._listen(port, (err) => {
if (err.code === 'EADDRINUSE') {
reject(`Port ${port} is already in use`)
}
reject(err)
})
.then((port) => {
httpsProxy.create(appData.path('proxy'), port, {
onRequest: this.callListeners.bind(this),
onUpgrade: this.onSniUpgrade.bind(this),
})
.then((httpsProxy) => {
this._httpsProxy = httpsProxy
// 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)
return resolve([port])
})
})
})
}
destroyAut () {
return this.socket.destroyAut()
}
}

View File

@@ -1,430 +0,0 @@
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)
},
})
}
}

View File

@@ -31,7 +31,7 @@ const cache = require(`../../lib/cache`)
const errors = require(`../../lib/errors`)
const cypress = require(`../../lib/cypress`)
const ProjectBase = require(`../../lib/project-base`).ProjectBase
const { ServerE2E } = require(`../../lib/server-e2e`)
const { ServerBase } = require(`../../lib/server-base`)
const Reporter = require(`../../lib/reporter`)
const browsers = require(`../../lib/browsers`)
const videoCapture = require(`../../lib/video_capture`)
@@ -174,7 +174,7 @@ describe('lib/cypress', () => {
sinon.stub(extension, 'setHostAndPath').resolves()
sinon.stub(detect, 'detect').resolves(TYPICAL_BROWSERS)
sinon.stub(process, 'exit')
sinon.stub(ServerE2E.prototype, 'reset')
sinon.stub(ServerBase.prototype, 'reset')
sinon.stub(errors, 'warning')
.callThrough()
.withArgs('INVOKED_BINARY_OUTSIDE_NPM_MODULE')
@@ -1102,7 +1102,7 @@ describe('lib/cypress', () => {
it('can change the default port to 5544', function () {
const listen = sinon.spy(http.Server.prototype, 'listen')
const open = sinon.spy(ServerE2E.prototype, 'open')
const open = sinon.spy(ServerBase.prototype, 'open')
return cypress.start([`--run-project=${this.todosPath}`, '--port=5544'])
.then(() => {
@@ -1777,7 +1777,7 @@ describe('lib/cypress', () => {
sinon.stub(electron.app, 'on').withArgs('ready').yieldsAsync()
sinon.stub(Windows, 'open').resolves(this.win)
sinon.stub(ServerE2E.prototype, 'startWebsockets')
sinon.stub(ServerBase.prototype, 'startWebsockets')
sinon.stub(electron.ipcMain, 'on')
})
@@ -1798,7 +1798,7 @@ describe('lib/cypress', () => {
// TODO: fix failing test https://github.com/cypress-io/cypress/issues/23149
it.skip('passes filtered options to Project#open and sets cli config', async function () {
const open = sinon.stub(ServerE2E.prototype, 'open').resolves([])
const open = sinon.stub(ServerBase.prototype, 'open').resolves([])
sinon.stub(interactiveMode, 'ready')

View File

@@ -19,7 +19,7 @@ const SseStream = require('ssestream')
const EventSource = require('eventsource')
const { setupFullConfigWithDefaults } = require('@packages/config')
const config = require(`../../lib/config`)
const { ServerE2E } = require(`../../lib/server-e2e`)
const { ServerBase } = require(`../../lib/server-base`)
const pluginsModule = require(`../../lib/plugins`)
const preprocessor = require(`../../lib/plugins/preprocessor`)
const resolve = require(`../../lib/util/resolve`)
@@ -87,7 +87,7 @@ describe('Routes', () => {
process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0'
sinon.stub(CacheBuster, 'get').returns('-123')
sinon.stub(ServerE2E.prototype, 'reset')
sinon.stub(ServerBase.prototype, 'reset')
sinon.stub(pluginsModule, 'has').returns(false)
nock.enableNetConnect()
@@ -161,7 +161,7 @@ describe('Routes', () => {
httpsServer.start(8443),
// and open our cypress server
(this.server = new ServerE2E()),
(this.server = new ServerBase()),
this.server.open(cfg, {
SocketCtor: SocketE2E,

View File

@@ -8,7 +8,7 @@ const evilDns = require('evil-dns')
const { setupFullConfigWithDefaults } = require('@packages/config')
const httpsServer = require(`@packages/https-proxy/test/helpers/https_server`)
const config = require(`../../lib/config`)
const { ServerE2E } = require(`../../lib/server-e2e`)
const { ServerBase } = require(`../../lib/server-base`)
const { SocketE2E } = require(`../../lib/socket-e2e`)
const Fixtures = require('@tooling/system-tests')
const { createRoutes } = require(`../../lib/routes`)
@@ -26,7 +26,7 @@ describe('Server', () => {
require('mocha-banner').register()
beforeEach(() => {
return sinon.stub(ServerE2E.prototype, 'reset')
return sinon.stub(ServerBase.prototype, 'reset')
})
context('resolving url', () => {
@@ -83,7 +83,7 @@ describe('Server', () => {
httpsServer.start(8443),
// and open our cypress server
(this.server = new ServerE2E()),
(this.server = new ServerBase()),
this.server.open(cfg, {
SocketCtor: SocketE2E,

View File

@@ -8,7 +8,7 @@ const Promise = require('bluebird')
const socketIo = require(`@packages/socket/lib/browser`)
const httpsServer = require(`@packages/https-proxy/test/helpers/https_server`)
const config = require(`../../lib/config`)
const { ServerE2E } = require(`../../lib/server-e2e`)
const { ServerBase } = require(`../../lib/server-base`)
const { SocketE2E } = require(`../../lib/socket-e2e`)
const { Automation } = require(`../../lib/automation`)
const Fixtures = require('@tooling/system-tests')
@@ -38,7 +38,7 @@ describe('Web Sockets', () => {
this.cfg = cfg
this.ws = new ws.Server({ port: wsPort })
this.server = new ServerE2E()
this.server = new ServerBase()
return this.server.open(this.cfg, {
SocketCtor: SocketE2E,

View File

@@ -22,7 +22,7 @@ process.env.CYPRESS_INTERNAL_ENV = 'development'
const CA = require('@packages/https-proxy').CA
const { setupFullConfigWithDefaults } = require('@packages/config')
const { ServerE2E } = require('../../lib/server-e2e')
const { ServerBase } = require('../../lib/server-base')
const { SocketE2E } = require('../../lib/socket-e2e')
const { _getArgs } = require('../../lib/browsers/chrome')
@@ -361,7 +361,7 @@ describe('Proxy Performance', function () {
// turn off morgan
config.morgan = false
cyServer = new ServerE2E()
cyServer = new ServerBase()
return cyServer.open(config, {
SocketCtor: SocketE2E,

View File

@@ -6,7 +6,7 @@ const pkg = require('@packages/root')
const Fixtures = require('@tooling/system-tests')
const { sinon } = require('../spec_helper')
const config = require(`../../lib/config`)
const { ServerE2E } = require(`../../lib/server-e2e`)
const { ServerBase } = require(`../../lib/server-base`)
const { ProjectBase } = require(`../../lib/project-base`)
const { Automation } = require(`../../lib/automation`)
const savedState = require(`../../lib/saved_state`)
@@ -224,8 +224,8 @@ This option will not have an effect in Some-other-name. Tests that rely on web s
beforeEach(function () {
sinon.stub(this.project, 'startWebsockets')
sinon.stub(this.project, 'getConfig').returns(this.config)
sinon.stub(ServerE2E.prototype, 'open').resolves([])
sinon.stub(ServerE2E.prototype, 'reset')
sinon.stub(ServerBase.prototype, 'open').resolves([])
sinon.stub(ServerBase.prototype, 'reset')
})
it('calls #startWebsockets with options + config', function () {

View File

@@ -6,7 +6,7 @@ const express = require('express')
const Promise = require('bluebird')
const { connect } = require('@packages/network')
const { setupFullConfigWithDefaults } = require('@packages/config')
const { ServerE2E } = require(`../../lib/server-e2e`)
const { ServerBase } = require(`../../lib/server-base`)
const { SocketE2E } = require(`../../lib/socket-e2e`)
const fileServer = require(`../../lib/file_server`)
const ensureUrl = require(`../../lib/util/ensure-url`)
@@ -20,7 +20,7 @@ mockery.registerMock('morgan', () => {
describe('lib/server', () => {
beforeEach(function () {
this.server = new ServerE2E()
this.server = new ServerBase()
return setupFullConfigWithDefaults({ projectRoot: '/foo/bar/', config: { supportFile: false } }, getCtx().file.getFilesByGlob)
.then((cfg) => {
@@ -54,7 +54,7 @@ describe.skip('lib/server', () => {
return setupFullConfigWithDefaults({ projectRoot: '/foo/bar/' }, getCtx().file.getFilesByGlob)
.then((cfg) => {
this.config = cfg
this.server = new ServerE2E()
this.server = new ServerBase()
this.oldFileServer = this.server._fileServer
this.server._fileServer = this.fileServer

View File

@@ -9,7 +9,7 @@ const Fixtures = require('@tooling/system-tests')
const errors = require('../../lib/errors')
const { SocketE2E } = require('../../lib/socket-e2e')
const { ServerE2E } = require('../../lib/server-e2e')
const { ServerBase } = require('../../lib/server-base')
const { Automation } = require('../../lib/automation')
const preprocessor = require('../../lib/plugins/preprocessor')
const { fs } = require('../../lib/util/fs')
@@ -37,7 +37,7 @@ describe('lib/socket', () => {
this.todosPath = Fixtures.projectPath('todos')
this.server = new ServerE2E()
this.server = new ServerBase()
await ctx.actions.project.setCurrentProjectAndTestingTypeForTestSetup(this.todosPath)

View File

@@ -4073,13 +4073,11 @@
"./packages/server/lib/project_utils.ts",
"./packages/server/lib/remote_states.ts",
"./packages/server/lib/request.js",
"./packages/server/lib/routes-ct.ts",
"./packages/server/lib/routes-e2e.ts",
"./packages/server/lib/routes.ts",
"./packages/server/lib/saved_state.ts",
"./packages/server/lib/server-base.ts",
"./packages/server/lib/server-ct.ts",
"./packages/server/lib/server-e2e.ts",
"./packages/server/lib/server-base.ts",
"./packages/server/lib/session.ts",
"./packages/server/lib/socket-base.ts",
"./packages/server/lib/socket-e2e.ts",

View File

@@ -4072,13 +4072,11 @@
"./packages/server/lib/project_utils.ts",
"./packages/server/lib/remote_states.ts",
"./packages/server/lib/request.js",
"./packages/server/lib/routes-ct.ts",
"./packages/server/lib/routes-e2e.ts",
"./packages/server/lib/routes.ts",
"./packages/server/lib/saved_state.ts",
"./packages/server/lib/server-base.ts",
"./packages/server/lib/server-ct.ts",
"./packages/server/lib/server-e2e.ts",
"./packages/server/lib/server-base.ts",
"./packages/server/lib/session.ts",
"./packages/server/lib/socket-base.ts",
"./packages/server/lib/socket-e2e.ts",

View File

@@ -4070,13 +4070,11 @@
"./packages/server/lib/project_utils.ts",
"./packages/server/lib/remote_states.ts",
"./packages/server/lib/request.js",
"./packages/server/lib/routes-ct.ts",
"./packages/server/lib/routes-e2e.ts",
"./packages/server/lib/routes.ts",
"./packages/server/lib/saved_state.ts",
"./packages/server/lib/server-base.ts",
"./packages/server/lib/server-ct.ts",
"./packages/server/lib/server-e2e.ts",
"./packages/server/lib/server-base.ts",
"./packages/server/lib/session.ts",
"./packages/server/lib/socket-base.ts",
"./packages/server/lib/socket-e2e.ts",