diff --git a/cli/schema/cypress.schema.json b/cli/schema/cypress.schema.json index 0d21f27285cc..ca8253d11f72 100644 --- a/cli/schema/cypress.schema.json +++ b/cli/schema/cypress.schema.json @@ -234,11 +234,6 @@ "default": false, "description": "Enables AST-based JS/HTML rewriting. This may fix issues caused by the existing regex-based JS/HTML replacement algorithm." }, - "experimentalNetworkStubbing": { - "type": "boolean", - "default": false, - "description": "Enables `cy.http()`, which can be used to dynamically intercept/stub/await any HTTP request or response (XHRs, fetch, beacons, etc.)" - }, "experimentalFetchPolyfill": { "type": "boolean", "default": false, diff --git a/cli/types/cypress.d.ts b/cli/types/cypress.d.ts index 3462eb0dbf19..e7dab01e3a82 100644 --- a/cli/types/cypress.d.ts +++ b/cli/types/cypress.d.ts @@ -2576,11 +2576,6 @@ declare namespace Cypress { * @default false */ experimentalSourceRewriting: boolean - /** - * Enables `cy.http()`, which can be used to dynamically intercept/stub/await any HTTP request or response (XHRs, fetch, beacons, etc.) - * @default false - */ - experimentalNetworkStubbing: boolean /** * Number of times to retry a failed test. * If a number is set, tests will retry in both runMode and openMode. diff --git a/packages/desktop-gui/cypress.json b/packages/desktop-gui/cypress.json index 7e0f4fa4d945..6940475ea6b1 100644 --- a/packages/desktop-gui/cypress.json +++ b/packages/desktop-gui/cypress.json @@ -10,7 +10,6 @@ "nodeVersion": "system", "testFiles": "**/*_spec.{js,jsx}", "experimentalComponentTesting": true, - "experimentalNetworkStubbing": true, "componentFolder": "src", "reporter": "../../node_modules/cypress-multi-reporters/index.js", "reporterOptions": { diff --git a/packages/driver/cypress.json b/packages/driver/cypress.json index 7d63ba98810c..8a0a973e1beb 100644 --- a/packages/driver/cypress.json +++ b/packages/driver/cypress.json @@ -7,6 +7,5 @@ "reporter": "cypress-multi-reporters", "reporterOptions": { "configFile": "../../mocha-reporter-config.json" - }, - "experimentalNetworkStubbing": true + } } diff --git a/packages/driver/cypress/integration/commands/net_stubbing_spec.ts b/packages/driver/cypress/integration/commands/net_stubbing_spec.ts index 78d09b2aa533..b52b79c8a94f 100644 --- a/packages/driver/cypress/integration/commands/net_stubbing_spec.ts +++ b/packages/driver/cypress/integration/commands/net_stubbing_spec.ts @@ -309,20 +309,6 @@ describe('network stubbing', { retries: 2 }, function () { }) }) - it('if experimentalNetworkStubbing is falsy', function (done) { - sinon.stub(Cypress, 'config').callThrough() - // @ts-ignore - .withArgs('experimentalNetworkStubbing').returns(false) - - cy.on('fail', (err) => { - expect(err.message).to.contain('`cy.http()` requires experimental network mocking to be enabled.') - sinon.restore() - done() - }) - - cy.http('', '') - }) - it('url must be a string or regexp', function (done) { cy.on('fail', function (err) { expect(err.message).to.include('`url` must be a string or a regular expression') diff --git a/packages/driver/cypress/integration/commands/xhr_spec.js b/packages/driver/cypress/integration/commands/xhr_spec.js index 8fc7816d9f11..76238f293f84 100644 --- a/packages/driver/cypress/integration/commands/xhr_spec.js +++ b/packages/driver/cypress/integration/commands/xhr_spec.js @@ -1081,6 +1081,15 @@ describe('src/cy/commands/xhr', () => { }) context('#server', () => { + it('logs deprecation warning', () => { + cy.stub(Cypress.utils, 'warning') + + cy.server() + .then(function () { + expect(Cypress.utils.warning).to.be.calledWithMatch(/^`cy\.server\(\)` has been deprecated and will be moved to a plugin in a future release\. Consider migrating to using `cy\.http\(\)` instead\./) + }) + }) + it('sets serverIsStubbed', () => { cy.server().then(() => { expect(cy.state('serverIsStubbed')).to.be.true @@ -1261,6 +1270,15 @@ describe('src/cy/commands/xhr', () => { }) }) + it('logs deprecation warning', () => { + cy.stub(Cypress.utils, 'warning') + + cy.route('*') + .then(function () { + expect(Cypress.utils.warning).to.be.calledWithMatch(/^`cy\.route\(\)` has been deprecated and will be moved to a plugin in a future release\. Consider migrating to using `cy\.http\(\)` instead\./) + }) + }) + it('accepts url, response', () => { cy.route('/foo', {}).then(function () { this.expectOptionsToBe({ @@ -1642,7 +1660,7 @@ describe('src/cy/commands/xhr', () => { cy.route('GET', 'http://example.com/%E0%A4%A') .then(() => { - expect(Cypress.utils.warning).to.not.be.called + expect(Cypress.utils.warning).to.not.be.calledWithMatch(/percent\-encoded characters/) }) }) @@ -1654,19 +1672,6 @@ describe('src/cy/commands/xhr', () => { }) }) - describe('deprecations', () => { - beforeEach(function () { - this.warn = cy.spy(window.top.console, 'warn') - }) - - it('does not log on {force404: true}', () => { - cy.server({ force404: true }) - .then(function () { - expect(this.warn).not.to.be.called - }) - }) - }) - describe('request response alias', () => { it('matches xhrs with lowercase methods', () => { cy diff --git a/packages/driver/src/cy/commands/navigation.js b/packages/driver/src/cy/commands/navigation.js index f2bf0c46f78d..15f67911e3ec 100644 --- a/packages/driver/src/cy/commands/navigation.js +++ b/packages/driver/src/cy/commands/navigation.js @@ -17,7 +17,7 @@ let hasVisitedAboutBlank = null let currentlyVisitingAboutBlank = null let knownCommandCausedInstability = null -const REQUEST_URL_OPTS = 'auth failOnStatusCode retryOnNetworkFailure retryOnStatusCodeFailure method body headers selfProxy' +const REQUEST_URL_OPTS = 'auth failOnStatusCode retryOnNetworkFailure retryOnStatusCodeFailure method body headers' .split(' ') const VISIT_OPTS = 'url log onBeforeLoad onLoad timeout requestTimeout' @@ -877,11 +877,6 @@ module.exports = (Commands, Cypress, cy, state, config) => { url = url.replace(`${existingAuth}@`, '') } - // hack to make cy.visits interceptable by network stubbing - if (Cypress.config('experimentalNetworkStubbing')) { - options.selfProxy = true - } - return requestUrl(url, options) .then((resp = {}) => { let { url, originalUrl, cookies, redirects, filePath } = resp diff --git a/packages/driver/src/cy/commands/waiting.js b/packages/driver/src/cy/commands/waiting.js index 785f507cf099..f284983a1b0c 100644 --- a/packages/driver/src/cy/commands/waiting.js +++ b/packages/driver/src/cy/commands/waiting.js @@ -25,8 +25,8 @@ const throwErr = (arg) => { module.exports = (Commands, Cypress, cy, state) => { const isDynamicAliasingPossible = () => { - // dynamic aliasing is possible if cy.http is enabled and a route with dynamic interception has been defined - return Cypress.config('experimentalNetworkStubbing') && _.find(state('routes'), (route) => { + // dynamic aliasing is possible if a route with dynamic interception has been defined + return _.find(state('routes'), (route) => { return _.isFunction(route.handler) }) } @@ -75,12 +75,11 @@ module.exports = (Commands, Cypress, cy, state) => { options.type = type - if (Cypress.config('experimentalNetworkStubbing')) { - const req = waitForRoute(alias, state, type) + // check cy.http routes + const req = waitForRoute(alias, state, type) - if (req) { - return req - } + if (req) { + return req } // append .type to the alias diff --git a/packages/driver/src/cy/commands/xhr.js b/packages/driver/src/cy/commands/xhr.js index e15d0721308f..4fb8d4c34ea1 100644 --- a/packages/driver/src/cy/commands/xhr.js +++ b/packages/driver/src/cy/commands/xhr.js @@ -329,6 +329,8 @@ module.exports = (Commands, Cypress, cy, state, config) => { return Commands.addAll({ server (options) { + $errUtils.warnByPath('server.deprecated') + let userOptions = options if (arguments.length === 0) { @@ -351,6 +353,8 @@ module.exports = (Commands, Cypress, cy, state, config) => { }, route (...args) { + $errUtils.warnByPath('route.deprecated') + // TODO: // if we return a function which returns a promise // then we should be handling potential timeout issues diff --git a/packages/driver/src/cy/net-stubbing/add-command.ts b/packages/driver/src/cy/net-stubbing/add-command.ts index a7ec2a207658..6394efcf433d 100644 --- a/packages/driver/src/cy/net-stubbing/add-command.ts +++ b/packages/driver/src/cy/net-stubbing/add-command.ts @@ -271,10 +271,6 @@ export function addCommand (Commands, Cypress: Cypress.Cypress, cy: Cypress.cy, } function http (matcher: RouteMatcher, handler?: RouteHandler | StringMatcher, arg2?: RouteHandler) { - if (!Cypress.config('experimentalNetworkStubbing')) { - return $errUtils.throwErrByPath('net_stubbing.http.needs_experimental') - } - function getMatcherOptions (): RouteMatcherOptions { if (_.isString(matcher) && $utils.isValidHttpMethod(matcher) && isStringMatcher(handler)) { // method, url, handler diff --git a/packages/driver/src/cypress/error_messages.js b/packages/driver/src/cypress/error_messages.js index 8311650ee357..6e79f01677ea 100644 --- a/packages/driver/src/cypress/error_messages.js +++ b/packages/driver/src/cypress/error_messages.js @@ -917,12 +917,6 @@ module.exports = { You passed: ${format(staticResponse)}`, 8) }, http: { - needs_experimental: stripIndent`\ - ${cmd('http')} requires experimental network mocking to be enabled. - - Set the \`experimentalNetworkStubbing\` config value to \`true\` to access this command. - - Read more: https://on.cypress.io/experiments`, invalid_handler: ({ handler }) => { return stripIndent`\ ${cmd('http')}'s \`handler\` argument must be a String, StaticResponse, or HttpController function. @@ -1200,6 +1194,10 @@ module.exports = { }, route: { + deprecated: { + message: `${cmd('route')} has been deprecated and will be moved to a plugin in a future release. Consider migrating to using ${cmd('http')} instead.`, + docsUrl: 'https://on.cypress.io/http', + }, failed_prerequisites: { message: `${cmd('route')} cannot be invoked before starting the ${cmd('server')}`, docsUrl: 'https://on.cypress.io/server', @@ -1391,6 +1389,10 @@ module.exports = { }, server: { + deprecated: { + message: `${cmd('server')} has been deprecated and will be moved to a plugin in a future release. Consider migrating to using ${cmd('http')} instead.`, + docsUrl: 'https://on.cypress.io/http', + }, invalid_argument: { message: `${cmd('server')} accepts only an object literal as its argument.`, docsUrl: 'https://on.cypress.io/server', diff --git a/packages/net-stubbing/lib/external-types.ts b/packages/net-stubbing/lib/external-types.ts index b039d9940c2e..05dfe74899bf 100644 --- a/packages/net-stubbing/lib/external-types.ts +++ b/packages/net-stubbing/lib/external-types.ts @@ -334,9 +334,6 @@ declare global { /** * Use `cy.http()` to stub and intercept HTTP requests and responses. * - * Note: this command is only available if you have set the `experimentalNetworkStubbing` - * configuration option to `true`. - * * @see https://on.cypress.io/http * @example * cy.http('https://localhost:7777/users', [{id: 1, name: 'Pat'}]) @@ -355,9 +352,6 @@ declare global { /** * Use `cy.http()` to stub and intercept HTTP requests and responses. * - * Note: this command is only available if you have set the `experimentalNetworkStubbing` - * configuration option to `true`. - * * @see https://on.cypress.io/http * @example * cy.http('GET', 'http://foo.com/fruits', ['apple', 'banana', 'cherry']) diff --git a/packages/net-stubbing/lib/server/index.ts b/packages/net-stubbing/lib/server/index.ts index ec0f054dc8f0..b62564c27745 100644 --- a/packages/net-stubbing/lib/server/index.ts +++ b/packages/net-stubbing/lib/server/index.ts @@ -8,6 +8,8 @@ export { InterceptResponse } from './intercept-response' export { NetStubbingState } from './types' +export { getRouteForRequest } from './route-matching' + import { state } from './state' export const netStubbingState = state diff --git a/packages/net-stubbing/lib/server/intercept-request.ts b/packages/net-stubbing/lib/server/intercept-request.ts index ac12cb471138..ab443f769b77 100644 --- a/packages/net-stubbing/lib/server/intercept-request.ts +++ b/packages/net-stubbing/lib/server/intercept-request.ts @@ -1,7 +1,6 @@ import _ from 'lodash' import concatStream from 'concat-stream' import Debug from 'debug' -import minimatch from 'minimatch' import url from 'url' import { @@ -14,124 +13,16 @@ import { NetStubbingState, } from './types' import { - RouteMatcherOptions, CyHttpMessages, NetEventFrames, SERIALIZABLE_REQ_PROPS, } from '../types' -import { getAllStringMatcherFields, sendStaticResponse, emit, setResponseFromFixture } from './util' +import { getRouteForRequest } from './route-matching' +import { sendStaticResponse, emit, setResponseFromFixture } from './util' import CyServer from '@packages/server' const debug = Debug('cypress:net-stubbing:server:intercept-request') -/** - * Returns `true` if `req` matches all supplied properties on `routeMatcher`, `false` otherwise. - */ -export function _doesRouteMatch (routeMatcher: RouteMatcherOptions, req: CypressIncomingRequest) { - const matchable = _getMatchableForRequest(req) - - // get a list of all the fields which exist where a rule needs to be succeed - const stringMatcherFields = getAllStringMatcherFields(routeMatcher) - const booleanFields = _.filter(_.keys(routeMatcher), _.partial(_.includes, ['https', 'webSocket'])) - const numberFields = _.filter(_.keys(routeMatcher), _.partial(_.includes, ['port'])) - - for (let i = 0; i < stringMatcherFields.length; i++) { - const field = stringMatcherFields[i] - const matcher = _.get(routeMatcher, field) - let value = _.get(matchable, field, '') - - if (typeof value !== 'string') { - value = String(value) - } - - if (matcher.test) { - if (!matcher.test(value)) { - return false - } - - continue - } - - if (field === 'url') { - if (value.includes(matcher)) { - continue - } - } - - if (!minimatch(value, matcher, { matchBase: true })) { - return false - } - } - - for (let i = 0; i < booleanFields.length; i++) { - const field = booleanFields[i] - const matcher = _.get(routeMatcher, field) - const value = _.get(matchable, field) - - if (matcher !== value) { - return false - } - } - - for (let i = 0; i < numberFields.length; i++) { - const field = numberFields[i] - const matcher = _.get(routeMatcher, field) - const value = _.get(matchable, field) - - if (matcher.length) { - if (!matcher.includes(value)) { - return false - } - - continue - } - - if (matcher !== value) { - return false - } - } - - return true -} - -export function _getMatchableForRequest (req: CypressIncomingRequest) { - let matchable: any = _.pick(req, ['headers', 'method', 'webSocket']) - - const authorization = req.headers['authorization'] - - if (authorization) { - const [mechanism, credentials] = authorization.split(' ', 2) - - if (mechanism && credentials && mechanism.toLowerCase() === 'basic') { - const [username, password] = Buffer.from(credentials, 'base64').toString().split(':', 2) - - matchable.auth = { username, password } - } - } - - const proxiedUrl = url.parse(req.proxiedUrl, true) - - _.assign(matchable, _.pick(proxiedUrl, ['hostname', 'path', 'pathname', 'port', 'query'])) - - matchable.url = req.proxiedUrl - - matchable.https = proxiedUrl.protocol && (proxiedUrl.protocol.indexOf('https') === 0) - - if (!matchable.port) { - matchable.port = matchable.https ? 443 : 80 - } - - return matchable -} - -function _getRouteForRequest (routes: BackendRoute[], req: CypressIncomingRequest, prevRoute?: BackendRoute) { - const possibleRoutes = prevRoute ? routes.slice(_.findIndex(routes, prevRoute) + 1) : routes - - return _.find(possibleRoutes, (route) => { - return _doesRouteMatch(route.routeMatcher, req) - }) -} - /** * Called when a new request is received in the proxy layer. * @param project @@ -140,7 +31,7 @@ function _getRouteForRequest (routes: BackendRoute[], req: CypressIncomingReques * @param cb Can be called to resume the proxy's normal behavior. If `res` is not handled and this is not called, the request will hang. */ export const InterceptRequest: RequestMiddleware = function () { - const route = _getRouteForRequest(this.netStubbingState.routes, this.req) + const route = getRouteForRequest(this.netStubbingState.routes, this.req) if (!route) { // not intercepted, carry on normally... @@ -241,7 +132,7 @@ function getNextRoute (state: NetStubbingState, req: CypressIncomingRequest, pre return } - return _getRouteForRequest(state.routes, req, prevRoute) + return getRouteForRequest(state.routes, req, prevRoute) } export async function onRequestContinue (state: NetStubbingState, frame: NetEventFrames.HttpRequestContinue, socket: CyServer.Socket) { diff --git a/packages/net-stubbing/lib/server/route-matching.ts b/packages/net-stubbing/lib/server/route-matching.ts new file mode 100644 index 000000000000..c6a612448c2d --- /dev/null +++ b/packages/net-stubbing/lib/server/route-matching.ts @@ -0,0 +1,116 @@ +import _ from 'lodash' +import minimatch from 'minimatch' +import url from 'url' + +import { CypressIncomingRequest } from '@packages/proxy' +import { BackendRoute } from './types' +import { RouteMatcherOptions } from '../types' +import { getAllStringMatcherFields } from './util' + +/** + * Returns `true` if `req` matches all supplied properties on `routeMatcher`, `false` otherwise. + */ +export function _doesRouteMatch (routeMatcher: RouteMatcherOptions, req: CypressIncomingRequest) { + const matchable = _getMatchableForRequest(req) + + // get a list of all the fields which exist where a rule needs to be succeed + const stringMatcherFields = getAllStringMatcherFields(routeMatcher) + const booleanFields = _.filter(_.keys(routeMatcher), _.partial(_.includes, ['https'])) + const numberFields = _.filter(_.keys(routeMatcher), _.partial(_.includes, ['port'])) + + for (let i = 0; i < stringMatcherFields.length; i++) { + const field = stringMatcherFields[i] + const matcher = _.get(routeMatcher, field) + let value = _.get(matchable, field, '') + + if (typeof value !== 'string') { + value = String(value) + } + + if (matcher.test) { + if (!matcher.test(value)) { + return false + } + + continue + } + + if (field === 'url') { + if (value.includes(matcher)) { + continue + } + } + + if (!minimatch(value, matcher, { matchBase: true })) { + return false + } + } + + for (let i = 0; i < booleanFields.length; i++) { + const field = booleanFields[i] + const matcher = _.get(routeMatcher, field) + const value = _.get(matchable, field) + + if (matcher !== value) { + return false + } + } + + for (let i = 0; i < numberFields.length; i++) { + const field = numberFields[i] + const matcher = _.get(routeMatcher, field) + const value = _.get(matchable, field) + + if (matcher.length) { + if (!matcher.includes(value)) { + return false + } + + continue + } + + if (matcher !== value) { + return false + } + } + + return true +} + +export function _getMatchableForRequest (req: CypressIncomingRequest) { + let matchable: any = _.pick(req, ['headers', 'method']) + + const authorization = req.headers['authorization'] + + if (authorization) { + const [mechanism, credentials] = authorization.split(' ', 2) + + if (mechanism && credentials && mechanism.toLowerCase() === 'basic') { + const [username, password] = Buffer.from(credentials, 'base64').toString().split(':', 2) + + matchable.auth = { username, password } + } + } + + const proxiedUrl = url.parse(req.proxiedUrl, true) + + _.assign(matchable, _.pick(proxiedUrl, ['hostname', 'path', 'pathname', 'port', 'query'])) + + matchable.url = req.proxiedUrl + + matchable.https = proxiedUrl.protocol && (proxiedUrl.protocol.indexOf('https') === 0) + + if (!matchable.port) { + matchable.port = matchable.https ? 443 : 80 + } + + return matchable +} + +export function getRouteForRequest (routes: BackendRoute[], req: CypressIncomingRequest, prevRoute?: BackendRoute) { + const possibleRoutes = prevRoute ? routes.slice(_.findIndex(routes, prevRoute) + 1) : routes + + return _.find(possibleRoutes, (route) => { + return _doesRouteMatch(route.routeMatcher, req) + }) +} diff --git a/packages/net-stubbing/test/unit/intercept-request-spec.ts b/packages/net-stubbing/test/unit/route-matching-spec.ts similarity index 98% rename from packages/net-stubbing/test/unit/intercept-request-spec.ts rename to packages/net-stubbing/test/unit/route-matching-spec.ts index 4190d294208c..2ec032a795b9 100644 --- a/packages/net-stubbing/test/unit/intercept-request-spec.ts +++ b/packages/net-stubbing/test/unit/route-matching-spec.ts @@ -1,7 +1,7 @@ import { _doesRouteMatch, _getMatchableForRequest, -} from '../../lib/server/intercept-request' +} from '../../lib/server/route-matching' import { expect } from 'chai' import { CypressIncomingRequest } from '@packages/proxy' diff --git a/packages/server/lib/config_options.ts b/packages/server/lib/config_options.ts index 4887a4095821..d58e3eb664e5 100644 --- a/packages/server/lib/config_options.ts +++ b/packages/server/lib/config_options.ts @@ -74,11 +74,6 @@ export const options = [ defaultValue: false, validation: v.isBoolean, isExperimental: true, - }, { - name: 'experimentalNetworkStubbing', - defaultValue: false, - validation: v.isBoolean, - isExperimental: true, }, { name: 'fileServerFolder', defaultValue: '', @@ -289,5 +284,9 @@ export const breakingOptions = [ name: 'experimentalShadowDomSupport', errorKey: 'EXPERIMENTAL_SHADOW_DOM_REMOVED', isWarning: true, + }, { + name: 'experimentalNetworkStubbing', + errorKey: 'EXPERIMENTAL_NETWORK_STUBBING_REMOVED', + isWarning: true, }, ] diff --git a/packages/server/lib/errors.js b/packages/server/lib/errors.js index 8aee69e7a0cf..77a22117f72e 100644 --- a/packages/server/lib/errors.js +++ b/packages/server/lib/errors.js @@ -900,6 +900,12 @@ const getMsgByType = function (type, arg1 = {}, arg2, arg3) { return stripIndent`\ The \`experimentalShadowDomSupport\` configuration option was removed in Cypress version \`5.2.0\`. It is no longer necessary when utilizing the \`includeShadowDom\` option. + You can safely remove this option from your config.` + case 'EXPERIMENTAL_NETWORK_STUBBING_REMOVED': + return stripIndent`\ + The \`experimentalNetworkStubbing\` configuration option was removed in Cypress version \`6.0.0\`. + It is no longer necessary for using \`cy.http()\` (formerly \`cy.route2()\`). + You can safely remove this option from your config.` case 'INCOMPATIBLE_PLUGIN_RETRIES': return stripIndent`\ diff --git a/packages/server/lib/experiments.ts b/packages/server/lib/experiments.ts index 26a65fd2e045..3fd64d5895a9 100644 --- a/packages/server/lib/experiments.ts +++ b/packages/server/lib/experiments.ts @@ -52,7 +52,6 @@ interface StringValues { */ const _summaries: StringValues = { experimentalComponentTesting: 'Framework-specific component testing, uses `componentFolder` to load component specs', - experimentalNetworkStubbing: 'Enables `cy.http()`, which can be used to dynamically intercept/stub/await any HTTP request or response (XHRs, fetch, beacons, etc.)', experimentalSourceRewriting: 'Enables AST-based JS/HTML rewriting. This may fix issues caused by the existing regex-based JS/HTML replacement algorithm.', experimentalFetchPolyfill: 'Polyfills `window.fetch` to enable Network spying and stubbing', } @@ -69,7 +68,6 @@ const _summaries: StringValues = { */ const _names: StringValues = { experimentalComponentTesting: 'Component Testing', - experimentalNetworkStubbing: 'Experimental network mocking', experimentalSourceRewriting: 'Improved source rewriting', experimentalFetchPolyfill: 'Fetch polyfill', } diff --git a/packages/server/lib/server.js b/packages/server/lib/server.js index f2a9f0f63b99..ce021bd582b7 100644 --- a/packages/server/lib/server.js +++ b/packages/server/lib/server.js @@ -20,7 +20,10 @@ const { uri, } = require('@packages/network') const { NetworkProxy } = require('@packages/proxy') -const { netStubbingState } = require('@packages/net-stubbing') +const { + netStubbingState, + getRouteForRequest, +} = require('@packages/net-stubbing') const { createInitialWorkers } = require('@packages/rewriter') const origin = require('./util/origin') const ensureUrl = require('./util/ensure-url') @@ -453,6 +456,16 @@ class Server { return currentPromisePhase = fn() } + const matchesNetStubbingRoute = (requestOptions) => { + const proxiedReq = { + proxiedUrl: requestOptions.url, + ..._.pick(requestOptions, ['headers', 'method']), + // TODO: add `body` here once bodies can be statically matched + } + + return !!getRouteForRequest(this._netStubbingState.routes, proxiedReq) + } + return this._urlResolver = (p = new Promise((resolve, reject, onCancel) => { let urlFile @@ -642,7 +655,7 @@ class Server { }, }) - if (options.selfProxy) { + 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 _.assign(options, { diff --git a/packages/server/test/unit/config_spec.js b/packages/server/test/unit/config_spec.js index a8f782b8ab90..45fa622c9b16 100644 --- a/packages/server/test/unit/config_spec.js +++ b/packages/server/test/unit/config_spec.js @@ -1142,6 +1142,17 @@ describe('lib/config', () => { expect(warning).to.be.calledWith('EXPERIMENTAL_SHADOW_DOM_REMOVED') }) + // @see https://github.com/cypress-io/cypress/pull/9185 + it('warns if experimentalNetworkStubbing is passed', async function () { + const warning = sinon.spy(errors, 'warning') + + await this.defaults('experimentalNetworkStubbing', true, { + experimentalNetworkStubbing: true, + }) + + expect(warning).to.be.calledWith('EXPERIMENTAL_NETWORK_STUBBING_REMOVED') + }) + describe('.resolved', () => { it('sets reporter and port to cli', () => { const obj = { @@ -1167,7 +1178,6 @@ describe('lib/config', () => { execTimeout: { value: 60000, from: 'default' }, experimentalComponentTesting: { value: false, from: 'default' }, experimentalFetchPolyfill: { value: false, from: 'default' }, - experimentalNetworkStubbing: { value: false, from: 'default' }, experimentalSourceRewriting: { value: false, from: 'default' }, fileServerFolder: { value: '', from: 'default' }, firefoxGcInterval: { value: { openMode: null, runMode: 1 }, from: 'default' }, @@ -1245,7 +1255,6 @@ describe('lib/config', () => { execTimeout: { value: 60000, from: 'default' }, experimentalComponentTesting: { value: false, from: 'default' }, experimentalFetchPolyfill: { value: false, from: 'default' }, - experimentalNetworkStubbing: { value: false, from: 'default' }, experimentalSourceRewriting: { value: false, from: 'default' }, env: { foo: {