Skip to content

Commit d1d0215

Browse files
Merge branch 'feat/cy-prompt' into alejandro/chore/handle-cy-prompt-error
2 parents 2acc165 + 15c0396 commit d1d0215

File tree

7 files changed

+95
-18
lines changed

7 files changed

+95
-18
lines changed

.circleci/workflows.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
version: 2.1
22

3-
chrome-stable-version: &chrome-stable-version "138.0.7204.92"
3+
chrome-stable-version: &chrome-stable-version "138.0.7204.100"
44
chrome-beta-version: &chrome-beta-version "139.0.7258.5"
55
firefox-stable-version: &firefox-stable-version "137.0"
66

cli/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ _Released 7/15/2025 (PENDING)_
66
**Bugfixes:**
77

88
- Fixed a regression introduced in [`14.5.0`](https://docs.cypress.io/guides/references/changelog#14-5-0) where the Stop button would not immediately stop the spec timer. Addresses [#31920](https://github.com/cypress-io/cypress/issues/31920).
9+
- Fixed an issue with the `CloudRequest` where it used the wrong port for `https` requests. Addressed in [#31992](https://github.com/cypress-io/cypress/pull/31992).
910

1011
## 14.5.1
1112

packages/app/src/prompt/PromptGetCodeModal.vue

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -46,11 +46,11 @@ const closeModal = () => {
4646
const container = ref<HTMLDivElement | null>(null)
4747
const error = ref<string | null>(null)
4848
const ReactGetCodeModalContents = ref<GetCodeModalContentsShape | null>(null)
49-
const reactRoot = ref<Root | null>(null)
49+
const containerReactRootMap = new WeakMap<HTMLElement, Root>()
5050
const promptStore = usePromptStore()
5151
5252
const maybeRenderReactComponent = () => {
53-
if (!ReactGetCodeModalContents.value || !!error.value) {
53+
if (!ReactGetCodeModalContents.value || !!error.value || !container.value) {
5454
return
5555
}
5656
@@ -63,19 +63,32 @@ const maybeRenderReactComponent = () => {
6363
},
6464
})
6565
66-
if (!reactRoot.value) {
67-
reactRoot.value = window.UnifiedRunner.ReactDOM.createRoot(container.value)
66+
// Store the react root in a weak map keyed by the container. We do this so that we have a reference
67+
// to it that's tied to the container value but absolutely do not want to use vue to do the tracking.
68+
// If vue tracks it (e.g. using a ref) it creates proxies that do not play nicely with React in
69+
// production
70+
let reactRoot = containerReactRootMap.get(container.value)
71+
72+
if (!reactRoot) {
73+
reactRoot = window.UnifiedRunner.ReactDOM.createRoot(container.value) as Root
74+
containerReactRootMap.set(container.value, reactRoot)
6875
}
6976
70-
reactRoot.value?.render(panel)
77+
reactRoot.render(panel)
7178
}
7279
7380
const unmountReactComponent = () => {
7481
if (!ReactGetCodeModalContents.value || !container.value) {
7582
return
7683
}
7784
78-
reactRoot.value?.unmount()
85+
const reactRoot = containerReactRootMap.get(container.value)
86+
87+
if (!reactRoot) {
88+
return
89+
}
90+
91+
reactRoot.unmount()
7992
}
8093
8194
onMounted(maybeRenderReactComponent)

packages/app/src/prompt/PromptMoreInfoNeededModal.vue

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -46,11 +46,11 @@ const closeModal = () => {
4646
const container = ref<HTMLDivElement | null>(null)
4747
const error = ref<string | null>(null)
4848
const ReactMoreInfoNeededModalContents = ref<MoreInfoNeededModalContentsShape | null>(null)
49-
const reactRoot = ref<Root | null>(null)
49+
const containerReactRootMap = new WeakMap<HTMLElement, Root>()
5050
const promptStore = usePromptStore()
5151
5252
const maybeRenderReactComponent = () => {
53-
if (!ReactMoreInfoNeededModalContents.value || !!error.value) {
53+
if (!ReactMoreInfoNeededModalContents.value || !!error.value || !container.value) {
5454
return
5555
}
5656
@@ -65,19 +65,32 @@ const maybeRenderReactComponent = () => {
6565
},
6666
})
6767
68-
if (!reactRoot.value) {
69-
reactRoot.value = window.UnifiedRunner.ReactDOM.createRoot(container.value)
68+
// Store the react root in a weak map keyed by the container. We do this so that we have a reference
69+
// to it that's tied to the container value but absolutely do not want to use vue to do the tracking.
70+
// If vue tracks it (e.g. using a ref) it creates proxies that do not play nicely with React in
71+
// production
72+
let reactRoot = containerReactRootMap.get(container.value)
73+
74+
if (!reactRoot) {
75+
reactRoot = window.UnifiedRunner.ReactDOM.createRoot(container.value) as Root
76+
containerReactRootMap.set(container.value, reactRoot)
7077
}
7178
72-
reactRoot.value?.render(panel)
79+
reactRoot.render(panel)
7380
}
7481
7582
const unmountReactComponent = () => {
7683
if (!ReactMoreInfoNeededModalContents.value || !container.value) {
7784
return
7885
}
7986
80-
reactRoot.value?.unmount()
87+
const reactRoot = containerReactRootMap.get(container.value)
88+
89+
if (!reactRoot) {
90+
return
91+
}
92+
93+
reactRoot.unmount()
8194
}
8295
8396
onMounted(maybeRenderReactComponent)

packages/driver/cypress/e2e/commands/prompt/prompt.cy.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,13 @@ describe('src/cy/commands/prompt', () => {
1212
// TODO: add more tests when cy.prompt is built out, but for now this just
1313
// verifies that the command executes without throwing an error
1414
// @ts-expect-error - this will not error when we actually release the experimentalPromptCommand flag
15-
cy.prompt('Hello, world!')
15+
cy.prompt(['Hello, world!'])
1616

1717
cy.visit('http://www.barbaz.com:3500/fixtures/dom.html')
1818

1919
cy.origin('http://www.barbaz.com:3500', () => {
2020
// @ts-expect-error - this will not error when we actually release the experimentalPromptCommand flag
21-
cy.prompt('Hello, world!')
21+
cy.prompt(['Hello, world!'])
2222
})
2323
})
2424

@@ -37,6 +37,6 @@ describe('src/cy/commands/prompt', () => {
3737

3838
cy.visit('http://www.foobar.com:3500/fixtures/dom.html')
3939
// @ts-expect-error - this will not error when we actually release the experimentalPromptCommand flag
40-
cy.prompt('Hello, world!')
40+
cy.prompt(['Hello, world!'])
4141
})
4242
})

packages/network/lib/agent.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ export const createProxySock = (opts: CreateProxySockOpts, cb: CreateProxySockCb
126126

127127
export const isRequestHttps = (options: http.RequestOptions) => {
128128
// WSS connections will not have an href, but you can tell protocol from the defaultAgent
129-
return _.get(options, '_defaultAgent.protocol') === 'https:' || (options.href || '').slice(0, 6) === 'https:'
129+
return _.get(options, '_defaultAgent.protocol') === 'https:' || options.protocol === 'https:' || (options.href || '').slice(0, 6) === 'https:'
130130
}
131131

132132
export const isResponseStatusCode200 = (head: string) => {
@@ -226,6 +226,11 @@ export class CombinedAgent {
226226

227227
const isHttps = isRequestHttps(options)
228228

229+
// Ensure that HTTPS requests are using 443
230+
if (isHttps && options.port === 80) {
231+
options.port = 443
232+
}
233+
229234
if (!options.href) {
230235
// options.path can contain query parameters, which url.format will not-so-kindly urlencode for us...
231236
// so just append it to the resultant URL string

packages/server/test/unit/cloud/api/cloud_request_spec.ts

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,17 @@ import agent from '@packages/network/lib/agent'
66
import axios, { CreateAxiosDefaults, AxiosInstance } from 'axios'
77
import debugLib from 'debug'
88
import stripAnsi from 'strip-ansi'
9-
import { createCloudRequest } from '../../../../lib/cloud/api/cloud_request'
9+
import { CloudRequest, createCloudRequest } from '../../../../lib/cloud/api/cloud_request'
1010
import cloudApi from '../../../../lib/cloud/api'
1111
import app_config from '../../../../config/app.json'
1212
import os from 'os'
1313
import pkg from '@packages/root'
1414
import { transformError } from '../../../../lib/cloud/api/axios_middleware/transform_error'
1515
import { DestroyableProxy, fakeServer, fakeProxy } from './utils/fake_proxy_server'
1616
import dedent from 'dedent'
17+
import { PassThrough } from 'stream'
18+
import fetch from 'cross-fetch'
19+
import nock from 'nock'
1720

1821
chai.use(sinonChai)
1922

@@ -444,6 +447,48 @@ describe('CloudRequest', () => {
444447
})
445448
})
446449

450+
describe('https requests', () => {
451+
it('handles https requests properly', async () => {
452+
nock.restore()
453+
454+
// @ts-ignore
455+
const addRequestSpy = sinon.stub(agent.httpsAgent, 'addRequest').callsFake((req, options) => {
456+
// fake IncomingMessage
457+
const res = new PassThrough() as any
458+
459+
res.statusCode = 200
460+
res.headers = {
461+
'content-type': 'application/json',
462+
}
463+
464+
process.nextTick(() => {
465+
req.emit('response', res)
466+
res.write(JSON.stringify({ ok: options.port === 443 && options.protocol === 'https:' }))
467+
res.end()
468+
})
469+
})
470+
471+
const result1 = await CloudRequest.post('https://cloud.cypress.io/ping', {})
472+
473+
expect(result1.data).to.eql({ ok: true })
474+
475+
const result2 = await createCloudRequest({ baseURL: 'https://api.cypress.io' }).post('/ping', {})
476+
477+
expect(result2.data).to.eql({ ok: true })
478+
479+
const result3 = await fetch('https://cloud.cypress.io/ping', {
480+
method: 'POST',
481+
body: '{}',
482+
// @ts-expect-error - this is supported
483+
agent,
484+
})
485+
486+
expect(await result3.json()).to.eql({ ok: true })
487+
488+
expect(addRequestSpy).to.have.been.calledThrice
489+
})
490+
})
491+
447492
;[undefined, 'development', 'test', 'staging', 'production'].forEach((env) => {
448493
describe(`base url for CYPRESS_CONFIG_ENV "${env}"`, () => {
449494
let prevEnv

0 commit comments

Comments
 (0)