Skip to content

Commit 4b3bf68

Browse files
Merge branch 'develop' into ryanm/fix/error-message-when-downloading-cy-prompt
2 parents 703ab84 + 7b6ad1d commit 4b3bf68

File tree

94 files changed

+1570
-1217
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

94 files changed

+1570
-1217
lines changed

.circleci/src/pipeline/@pipeline.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1785,7 +1785,7 @@ jobs:
17851785
source ./scripts/ensure-node.sh
17861786
yarn lerna run types
17871787
- sanitize-verify-and-store-mocha-results:
1788-
expectedResultCount: 5
1788+
expectedResultCount: 3
17891789

17901790
verify-release-readiness:
17911791
<<: *defaults

cli/CHANGELOG.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,10 @@ _Released 10/20/2025 (PENDING)_
99

1010
**Bugfixes:**
1111

12-
- Fixes an issue where grouped command text jumps up and down when expanding and collapsing in the command log. Addressed in [#32757](https://github.com/cypress-io/cypress/pull/32757).
13-
- Added more context to the error message shown when `cy.prompt()` fails to download. Addressed in [#](https://github.com/cypress-io/cypress/pull/).
12+
- Fixed an issue where grouped command text jumps up and down when expanding and collapsing in the command log. Addressed in [#32757](https://github.com/cypress-io/cypress/pull/32757).
13+
- Fixed an issue where command snapshots were not correctly displayed in Studio. Addressed in [#32808](https://github.com/cypress-io/cypress/pull/32808).
14+
- Fixed an issue with grouped console prop items having a hard to read blue color in the console log and duplicate `:` characters being displayed. Addressed in [#32776](https://github.com/cypress-io/cypress/pull/32776).
15+
- Added more context to the error message shown when `cy.prompt()` fails to download. Addressed in [#32822](https://github.com/cypress-io/cypress/pull/32822).
1416

1517
**Misc:**
1618

guides/esm-migration.md

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,11 @@
1212

1313
#### Notes
1414

15-
When migrating some of these projects away from the `ts-node` entry [see `@packages/scaffold-config` example](https://github.com/cypress-io/cypress/blob/v15.2.0/packages/scaffold-config/index.js), it is somewhat difficult to make separate browser/node entries as the v8-snapshot [tsconfig.json](https://github.com/cypress-io/cypress/blob/v15.2.0/tooling/v8-snapshot/tsconfig.json) is using an older style of module resolution where the `exports` key inside a package's `package.json` is not well supported. Because of this, we need to find ways to bundle code that is needed internally in the browser vs in node without them being a part of the same bundle. This is a temporary work around until we are able to get every package being able to build as an ES Module, which as that point we can re assess how the Cypress binary is being built as well as v8-snapshots, and will allow us to reconfigure this packages to export content in a more proper fashion.
15+
When migrating some of these projects away from the `ts-node` entry [see `@packages/scaffold-config` example](https://github.com/cypress-io/cypress/blob/v15.2.0/packages/scaffold-config/index.js), it is somewhat difficult to make separate browser/node entries as the v8-snapshot [tsconfig.json](https://github.com/cypress-io/cypress/blob/v15.2.0/tooling/v8-snapshot/tsconfig.json) is using an older style of module resolution where the `exports` key inside a package's `package.json` is not well supported. Because of this, we need to find ways to bundle code that is needed internally in the browser vs in node without them being a part of the same bundle. This is a temporary work around until we are able to get every package being able to build as an ES Module, which as that point we can re assess how the Cypress binary is being built as well as v8-snapshots, and will allow us to reconfigure this packages to export content in a more proper fashion. We are currently doing something similar in the following packages:
16+
17+
* `@packages/scaffold-config`
18+
* `@packages/socket`
19+
* `@packages/telemetry`
1620

1721
#### Status
1822

@@ -44,7 +48,7 @@ When migrating some of these projects away from the `ts-node` entry [see `@packa
4448
- [x] packages/error ✅ **COMPLETED**
4549
- [x] packages/eslint-config ✅ **COMPLETED**
4650
- [ ] packages/example
47-
- [ ] packages/extension
51+
- [x] packages/extension**COMPLETED**
4852
- [ ] packages/frontend-shared **PARTIAL** - entry point is JS
4953
- [x] packages/electron ✅ **COMPLETED**
5054
- [x] packages/https-proxy - ✅ **COMPLETED**
@@ -63,7 +67,7 @@ When migrating some of these projects away from the `ts-node` entry [see `@packa
6367
- [x] packages/runner ✅ **COMPLETED**
6468
- [x] packages/scaffold-config ✅ **COMPLETED**
6569
- [ ] packages/server **PARTIAL** - many source/test files in JS. highest priority
66-
- [ ] packages/socket **PARTIAL** - entry point is JS. Tests are JS
70+
- [x] packages/socket **COMPLETED**
6771
- [x] packages/stderr-filtering ✅ **COMPLETED**
6872
- [x] packages/telemetry ✅ **COMPLETED**
6973
- [ ] packages/ts **PARTIAL** - ultimate goal is removal and likely not worth the effort to convert
@@ -82,7 +86,7 @@ When migrating some of these projects away from the `ts-node` entry [see `@packa
8286
- [x] npm/cypress-schematic ✅ **COMPLETED**
8387
- [ ] npm/eslint-plugin-dev
8488
- [x] npm/grep ✅ **COMPLETED**
85-
- [ ] npm/puppeteer
89+
- [x] npm/puppeteer**COMPLETED**
8690
- [x] npm/vite-dev-server ✅ **COMPLETED**
8791
- [ ] npm/webpack-batteries-included-preprocessor
8892
- [ ] npm/webpack-dev-server
@@ -95,15 +99,15 @@ When migrating some of these projects away from the `ts-node` entry [see `@packa
9599
- [x] packages/driver ✅ **COMPLETED**
96100
- [x] packages/electron ✅ **COMPLETED**
97101
- [x] packages/error ✅ **COMPLETED**
98-
- [ ] packages/extension
102+
- [x] packages/extension**COMPLETED**
99103
- [x] packages/https-proxy ✅ **COMPLETED**
100104
- [x] packages/electron ✅ **COMPLETED**
101105
- [x] packages/icons ✅ **COMPLETED**
102106
- [x] packages/launcher ✅ **COMPLETED**
103107
- [x] packages/net-stubbing ✅ **COMPLETED**
104108
- [x] packages/network ✅ **COMPLETED**
105109
- [x] packages/network-tools ✅ **COMPLETED**
106-
- [ ] packages/packherd-require
110+
- [x] packages/packherd-require**COMPLETED**
107111
- [x] packages/proxy ✅ **COMPLETED**
108112
- [x] packages/rewriter ✅ **COMPLETED**
109113
- [x] packages/scaffold-config ✅ **COMPLETED**

npm/puppeteer/.mocharc.json

Lines changed: 0 additions & 7 deletions
This file was deleted.

npm/puppeteer/eslint.config.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
import { baseConfig } from '@packages/eslint-config'
22

3-
const allowDefaultProject = ['cypress.config.ts', 'eslint.config.ts', 'support/index.js']
3+
const allowDefaultProject = ['cypress.config.ts', 'eslint.config.ts', 'support/index.js', 'vitest.config.ts']
44

55
export default [
66
...baseConfig,
77
{
88
languageOptions: {
99
parserOptions: {
10+
tsconfigRootDir: __dirname,
1011
projectService: {
1112
allowDefaultProject,
1213
},

npm/puppeteer/package.json

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,9 @@
1212
"cypress:run": "node ../../scripts/cypress.js run --browser chrome",
1313
"lint": "eslint",
1414
"semantic-release": "semantic-release",
15-
"test": "mocha test/unit/*.spec.ts",
16-
"test-watch": "yarn test & chokidar '**/*.ts' 'test/unit/*.ts' -c 'yarn test'",
15+
"test-debug": "vitest --inspect-brk --no-file-parallelism --test-timeout=0 --hook-timeout=0",
16+
"test": "vitest run",
17+
"test-watch": "vitest --watch",
1718
"watch": "rimraf dist && tsc --watch"
1819
},
1920
"dependencies": {
@@ -23,17 +24,13 @@
2324
"devDependencies": {
2425
"@packages/eslint-config": "0.0.0-development",
2526
"@types/node": "^22.18.7",
26-
"chai-as-promised": "^7.1.1",
2727
"chokidar": "^3.5.3",
2828
"eslint": "^9.31.0",
2929
"express": "4.21.0",
30-
"mocha": "^9.2.2",
3130
"rimraf": "^6.0.1",
3231
"semantic-release": "22.0.12",
33-
"sinon": "^13.0.1",
34-
"sinon-chai": "^3.7.0",
35-
"ts-node": "^10.9.2",
36-
"typescript": "5.4.5"
32+
"typescript": "5.4.5",
33+
"vitest": "^3.2.4"
3734
},
3835
"peerDependencies": {
3936
"cypress": ">=13.6.0"

npm/puppeteer/test/tsconfig.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
"compilerOptions": {
33
"allowJs": true,
44
"noEmit": true,
5-
"types": ["mocha"]
65
},
76
"include": ["**/*.ts"],
87
}
Lines changed: 43 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,8 @@
1-
import { expect, use } from 'chai'
2-
import chaiAsPromised from 'chai-as-promised'
3-
import sinon from 'sinon'
4-
import sinonChai from 'sinon-chai'
1+
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest'
52
import type { Browser, Page } from 'puppeteer-core'
63
import { activateMainTab, ACTIVATION_TIMEOUT } from '../../src/plugin/activateMainTab'
74

8-
use(chaiAsPromised)
9-
use(sinonChai)
10-
115
describe('activateMainTab', () => {
12-
let clock: sinon.SinonFakeTimers
136
let prevWin: Window
147
let prevDoc: Document
158
let prevTop: Window & typeof globalThis
@@ -22,14 +15,12 @@ describe('activateMainTab', () => {
2215
let mockPage: Partial<Page>
2316

2417
beforeEach(() => {
25-
clock = sinon.useFakeTimers()
26-
18+
vi.useFakeTimers()
2719
window = {
28-
addEventListener: sinon.stub(),
29-
removeEventListener: sinon.stub(),
20+
addEventListener: vi.fn(),
21+
removeEventListener: vi.fn(),
3022

31-
// @ts-ignore sinon gets confused about postMessage type declaration
32-
postMessage: sinon.stub(),
23+
postMessage: vi.fn(),
3324
}
3425

3526
mockDocument = {
@@ -41,60 +32,66 @@ describe('activateMainTab', () => {
4132
// activateMainTab is eval'd in browser context, but the tests exec in a
4233
// node context. We don't necessarily need to do this swap, but it makes the
4334
// tests more portable.
44-
// @ts-ignore
4535
prevWin = global.window
4636
prevDoc = global.document
47-
// @ts-ignore
37+
// @ts-expect-error
4838
prevTop = global.top
49-
//@ts-ignore
39+
// @ts-expect-error
5040
global.window = window
5141
global.document = mockDocument as Document
52-
//@ts-ignore
42+
// @ts-expect-error
5343
global.top = mockTop
5444

5545
mockPage = {
56-
evaluate: sinon.stub().callsFake((fn, ...args) => fn(...args)),
46+
evaluate: vi.fn().mockImplementation((fn, ...args) => fn(...args)),
5747
}
5848

5949
mockBrowser = {
60-
pages: sinon.stub(),
50+
pages: vi.fn(),
6151
}
6252
})
6353

6454
afterEach(() => {
65-
clock.restore()
66-
// @ts-ignore
55+
vi.clearAllTimers()
56+
// @ts-expect-error
6757
global.window = prevWin
68-
// @ts-ignore
6958
global.top = prevTop
7059
global.document = prevDoc
7160
})
7261

7362
it('sends a tab activation request to the plugin, and resolves when the ack event is received', async () => {
74-
const pagePromise = Promise.resolve([mockPage])
75-
76-
;(mockBrowser.pages as sinon.SinonStub).returns(pagePromise)
77-
const p = activateMainTab(mockBrowser as Browser)
63+
vi.mocked(mockBrowser.pages).mockResolvedValue([mockPage] as Page[])
64+
vi.mocked(window.addEventListener).mockImplementation((event, listener) => {
65+
if (event === 'message') {
66+
// @ts-expect-error
67+
listener({ data: { message: 'cypress:extension:main:tab:activated' } })
68+
}
69+
})
7870

79-
await pagePromise
80-
// @ts-ignore
81-
window.addEventListener.withArgs('message').yield({ data: { message: 'cypress:extension:main:tab:activated' } })
82-
expect(window.postMessage).to.be.calledWith({ message: 'cypress:extension:activate:main:tab' })
71+
await activateMainTab(mockBrowser as Browser)
8372

84-
expect(p).to.eventually.be.true
73+
expect(window.postMessage).toHaveBeenCalledExactlyOnceWith({ message: 'cypress:extension:activate:main:tab' })
8574
})
8675

8776
it('sends a tab activation request to the plugin, and rejects if it times out', async () => {
88-
const pagePromise = Promise.resolve([mockPage])
89-
90-
;(mockBrowser.pages as sinon.SinonStub).returns(pagePromise)
91-
await pagePromise
92-
93-
const p = activateMainTab(mockBrowser as Browser)
94-
95-
clock.tick(ACTIVATION_TIMEOUT + 1)
96-
97-
expect(p).to.be.rejected
77+
vi.mocked(mockBrowser.pages).mockResolvedValue([mockPage] as Page[])
78+
79+
return new Promise<void>(async (resolve) => {
80+
mockPage.evaluate = vi.fn().mockImplementation(async (fn, ...args) => {
81+
try {
82+
await fn(...args)
83+
} catch (error) {
84+
expect(window.removeEventListener).toHaveBeenCalledExactlyOnceWith('message', expect.any(Function))
85+
expect(error).toBeUndefined()
86+
resolve()
87+
}
88+
})
89+
90+
const activationPromise = activateMainTab(mockBrowser as Browser)
91+
92+
await vi.advanceTimersByTimeAsync(ACTIVATION_TIMEOUT + 1)
93+
await activationPromise
94+
})
9895
})
9996

10097
describe('when cy in cy', () => {
@@ -103,16 +100,11 @@ describe('activateMainTab', () => {
103100
})
104101

105102
it('does not try to send tab activation message', async () => {
106-
const pagePromise = Promise.resolve([mockPage])
107-
108-
;(mockBrowser.pages as sinon.SinonStub).returns(pagePromise)
109-
110-
const p = activateMainTab(mockBrowser as Browser)
103+
vi.mocked(mockBrowser.pages).mockResolvedValue([mockPage] as Page[])
104+
await activateMainTab(mockBrowser as Browser)
111105

112-
await pagePromise
113-
expect(window.postMessage).not.to.be.called
114-
expect(window.addEventListener).not.to.be.called
115-
await p
106+
expect(window.postMessage).not.toHaveBeenCalled()
107+
expect(window.addEventListener).not.toHaveBeenCalled()
116108
})
117109
})
118110
})
Lines changed: 19 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,53 +1,49 @@
1-
import { expect, use } from 'chai'
2-
import chaiAsPromised from 'chai-as-promised'
3-
import sinon from 'sinon'
4-
import sinonChai from 'sinon-chai'
5-
1+
import { describe, it, expect, vi } from 'vitest'
62
import { retry } from '../../src/plugin'
73

8-
use(chaiAsPromised)
9-
use(sinonChai)
10-
114
describe('#retry', () => {
125
it('returns result of passing 1st attempt', async () => {
13-
const fn = sinon.stub().returns('passes 1st attempt')
6+
const fn = vi.fn().mockReturnValue('passes 1st attempt')
147
const result = await retry(fn)
158

16-
expect(fn).to.be.calledOnce
17-
expect(result).to.equal('passes 1st attempt')
9+
expect(fn).toHaveBeenCalledOnce()
10+
expect(result).toEqual('passes 1st attempt')
1811
})
1912

2013
it('retries after delay and returns result of subsequent passing attempt', async () => {
21-
const fn = sinon.stub()
14+
const fn = vi.fn()
2215

23-
fn.onFirstCall().throws('fail')
24-
fn.onSecondCall().returns('passes 2nd attempt')
16+
fn.mockImplementationOnce(() => { throw new Error('fail') })
17+
fn.mockImplementationOnce(() => { return 'passes 2nd attempt' })
2518

2619
const result = await retry(fn, { delayBetweenTries: 1 })
2720

28-
expect(fn).to.be.calledTwice
29-
expect(result).to.equal('passes 2nd attempt')
21+
expect(fn).toHaveBeenCalledTimes(2)
22+
expect(result).toEqual('passes 2nd attempt')
3023
})
3124

3225
it('retries up to timeout and returns result of subsequent passing attempt', async () => {
33-
const fn = sinon.stub()
26+
const fn = vi.fn()
3427

35-
fn.throws('fail')
36-
fn.onCall(5).returns('passes 5th attempt')
28+
fn.mockImplementationOnce(() => { throw new Error('fail') })
29+
fn.mockImplementationOnce(() => { throw new Error('fail') })
30+
fn.mockImplementationOnce(() => { throw new Error('fail') })
31+
fn.mockImplementationOnce(() => { throw new Error('fail') })
32+
fn.mockImplementationOnce(() => { return 'passes 5th attempt' })
3733

3834
const result = await retry(fn, { delayBetweenTries: 1 })
3935

40-
expect(fn.callCount).to.equal(6)
41-
expect(result).to.equal('passes 5th attempt')
36+
expect(fn).toHaveBeenCalledTimes(5)
37+
expect(result).toEqual('passes 5th attempt')
4238
})
4339

4440
it('fails if function does not pass before timeout', async () => {
45-
const fn = sinon.stub().callsFake(() => {
41+
const fn = vi.fn().mockImplementation(() => {
4642
throw new Error('fail')
4743
})
4844

4945
await expect(
5046
retry(fn, { timeout: 5, delayBetweenTries: 1 }),
51-
).to.be.rejectedWith('Failed retrying after 5ms: fail')
47+
).rejects.toThrow('Failed retrying after 5ms: fail')
5248
})
5349
})

0 commit comments

Comments
 (0)