From 86676e051599fbd77f4f0ffa9891c69317fa31e5 Mon Sep 17 00:00:00 2001 From: astone123 Date: Thu, 21 Aug 2025 10:04:26 -0400 Subject: [PATCH 01/16] feat: (studio) support `@cypress/grep` package in Cypress Studio --- packages/app/src/store/studio-store.ts | 70 ++++++++++++++++++++++ packages/driver/src/cypress/stack_utils.ts | 25 ++++++-- 2 files changed, 89 insertions(+), 6 deletions(-) diff --git a/packages/app/src/store/studio-store.ts b/packages/app/src/store/studio-store.ts index 9e70ee26b05..f8973b57253 100644 --- a/packages/app/src/store/studio-store.ts +++ b/packages/app/src/store/studio-store.ts @@ -111,6 +111,7 @@ interface StudioRecorderState { sessionId?: string _isStudioCreatedTest: boolean newTestLineNumber?: number + _originalGrepSettings: Record } function getUrlParams () { @@ -148,6 +149,7 @@ export const useStudioStore = defineStore('studioRecorder', { sessionId: persistedSessionId, newTestLineNumber: undefined, _isStudioCreatedTest: false, + _originalGrepSettings: {}, } }, @@ -254,6 +256,12 @@ export const useStudioStore = defineStore('studioRecorder', { this.sessionId = studio.sessionId } + if (studio.newTestLineNumber || studio.testId) { + if (this.detectAndStoreGrepSettings()) { + this.clearGrepSettings() + } + } + // if we have an existing test or are creating a new test, we need to start loading // otherwise if we have a suite, we can just set the studio active if (this.testId || studio.newTestLineNumber) { @@ -276,6 +284,63 @@ export const useStudioStore = defineStore('studioRecorder', { } }, + detectAndStoreGrepSettings () { + const grepEnvVars = [ + 'grep', + 'grepTags', 'grep-tags', + 'grepBurn', 'grep-burn', 'burn', + 'grepUntagged', 'grep-untagged', + 'grepOmitFiltered', 'grep-omit-filtered', + ] + + this._originalGrepSettings = {} + + try { + const cypress = getCypress() + + for (const envVar of grepEnvVars) { + const value = cypress.env(envVar) + + if (value != null) { + this._originalGrepSettings[envVar] = value + } + } + + return Object.keys(this._originalGrepSettings).length > 0 + } catch { + return false + } + }, + + clearGrepSettings () { + try { + const cypress = getCypress() + + for (const envVar of Object.keys(this._originalGrepSettings)) { + cypress.env(envVar, null) + } + } catch { + // Cypress not ready, skip + } + }, + + restoreGrepSettings () { + // Only restore if we have settings to restore + if (Object.keys(this._originalGrepSettings).length === 0) { + return + } + + try { + const cypress = getCypress() + + for (const [envVar, value] of Object.entries(this._originalGrepSettings)) { + cypress.env(envVar, value) + } + } catch { + // Cypress not ready, skip + } + }, + interceptTest (test) { // if this test is the one we created, we can just set the test id if ((this.newTestLineNumber && test.invocationDetails?.line === this.newTestLineNumber) || (this.suiteId && this._hasStarted)) { @@ -319,6 +384,8 @@ export const useStudioStore = defineStore('studioRecorder', { reset () { this.stop() + this.restoreGrepSettings() + this.logs = [] this.url = undefined this._hasStarted = false @@ -326,11 +393,14 @@ export const useStudioStore = defineStore('studioRecorder', { this.isFailed = false this.showUrlPrompt = true this._isStudioCreatedTest = false + this._originalGrepSettings = {} this._maybeResetRunnables() }, cancel () { + this.restoreGrepSettings() + this.reset() this.clearRunnableIds() this._removeUrlParams() diff --git a/packages/driver/src/cypress/stack_utils.ts b/packages/driver/src/cypress/stack_utils.ts index ae4875a4eaf..12bce66ac94 100644 --- a/packages/driver/src/cypress/stack_utils.ts +++ b/packages/driver/src/cypress/stack_utils.ts @@ -131,11 +131,6 @@ const getInvocationDetails = (specWindow, config): InvocationDetails | undefined if (specWindow.Error) { let stack = (new specWindow.Error()).stack - // note: specWindow.Cypress can be undefined or null - // if the user quickly reloads the tests multiple times - - // firefox and chrome throw stacks that include lines from cypress - // So we drop the lines until we get to the spec stackframe (includes __cypress) if (specWindow.Cypress) { // The stack includes frames internal to cypress, after the spec stackframe. In order // to determine the invocation details, the stack needs to be parsed and trimmed. @@ -149,7 +144,22 @@ const getInvocationDetails = (specWindow, config): InvocationDetails | undefined } } - const details: Omit = getSourceDetailsForFirstLine(stack, config('projectRoot')) || {}; + // Find the first stack line that is not a grep wrapper function + const stackLines = getStackLines(stack) + let targetLine = stackLines[0] // fallback to first line if no non-grep line found + + for (const line of stackLines) { + // Skip lines that contain grep wrapper function names + if (line.includes('itGrep') || line.includes('describeGrep')) { + continue + } + + // This should be the actual test invocation line + targetLine = line + break + } + + const details: Omit = (getSourceDetailsForLine(config('projectRoot'), targetLine) as StackLineDetail) || {}; (details as any).stack = stack @@ -405,8 +415,11 @@ const getSourceDetailsForLine = (projectRoot, line): MessageLineDetail | StackLi } const getSourceDetailsForFirstLine = (stack, projectRoot) => { + // console.log('getSourceDetailsForFirstLine', stack, projectRoot) const line = getStackLines(stack)[0] + // console.log('line', line) + if (!line) return return getSourceDetailsForLine(projectRoot, line) as StackLineDetail From 1e6139fa4187bc612c7bdd5ab483af2b6617098e Mon Sep 17 00:00:00 2001 From: astone123 Date: Thu, 21 Aug 2025 10:09:54 -0400 Subject: [PATCH 02/16] add comment back --- packages/driver/src/cypress/stack_utils.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/driver/src/cypress/stack_utils.ts b/packages/driver/src/cypress/stack_utils.ts index 12bce66ac94..52312f0d762 100644 --- a/packages/driver/src/cypress/stack_utils.ts +++ b/packages/driver/src/cypress/stack_utils.ts @@ -131,6 +131,11 @@ const getInvocationDetails = (specWindow, config): InvocationDetails | undefined if (specWindow.Error) { let stack = (new specWindow.Error()).stack + // note: specWindow.Cypress can be undefined or null + // if the user quickly reloads the tests multiple times + + // firefox and chrome throw stacks that include lines from cypress + // So we drop the lines until we get to the spec stackframe (includes __cypress) if (specWindow.Cypress) { // The stack includes frames internal to cypress, after the spec stackframe. In order // to determine the invocation details, the stack needs to be parsed and trimmed. From 973940f7c24571348b4dbc0ec894c0862dcc45cd Mon Sep 17 00:00:00 2001 From: astone123 Date: Thu, 21 Aug 2025 10:10:27 -0400 Subject: [PATCH 03/16] remove logs --- packages/driver/src/cypress/stack_utils.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/packages/driver/src/cypress/stack_utils.ts b/packages/driver/src/cypress/stack_utils.ts index 52312f0d762..3f10c22f1bd 100644 --- a/packages/driver/src/cypress/stack_utils.ts +++ b/packages/driver/src/cypress/stack_utils.ts @@ -420,11 +420,8 @@ const getSourceDetailsForLine = (projectRoot, line): MessageLineDetail | StackLi } const getSourceDetailsForFirstLine = (stack, projectRoot) => { - // console.log('getSourceDetailsForFirstLine', stack, projectRoot) const line = getStackLines(stack)[0] - // console.log('line', line) - if (!line) return return getSourceDetailsForLine(projectRoot, line) as StackLineDetail From 7c8eae64e7d13217e9319c3aaa9bcc604c370df8 Mon Sep 17 00:00:00 2001 From: astone123 Date: Thu, 21 Aug 2025 10:11:25 -0400 Subject: [PATCH 04/16] remove unnecessary comments --- packages/driver/src/cypress/stack_utils.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/driver/src/cypress/stack_utils.ts b/packages/driver/src/cypress/stack_utils.ts index 3f10c22f1bd..6dc6767afc4 100644 --- a/packages/driver/src/cypress/stack_utils.ts +++ b/packages/driver/src/cypress/stack_utils.ts @@ -149,9 +149,8 @@ const getInvocationDetails = (specWindow, config): InvocationDetails | undefined } } - // Find the first stack line that is not a grep wrapper function const stackLines = getStackLines(stack) - let targetLine = stackLines[0] // fallback to first line if no non-grep line found + let targetLine = stackLines[0] for (const line of stackLines) { // Skip lines that contain grep wrapper function names @@ -159,7 +158,6 @@ const getInvocationDetails = (specWindow, config): InvocationDetails | undefined continue } - // This should be the actual test invocation line targetLine = line break } From b7124aa85a27504ce08a8a6b6f22883447478bff Mon Sep 17 00:00:00 2001 From: astone123 Date: Fri, 22 Aug 2025 10:34:31 -0400 Subject: [PATCH 05/16] guard line replace --- packages/driver/src/cypress/stack_utils.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/driver/src/cypress/stack_utils.ts b/packages/driver/src/cypress/stack_utils.ts index 6dc6767afc4..5e0cea449d2 100644 --- a/packages/driver/src/cypress/stack_utils.ts +++ b/packages/driver/src/cypress/stack_utils.ts @@ -361,14 +361,14 @@ interface StackLineDetail { whitespace: any } -const getSourceDetailsForLine = (projectRoot, line): MessageLineDetail | StackLineDetail => { +const getSourceDetailsForLine = (projectRoot: string, line: string): MessageLineDetail | StackLineDetail => { const whitespace = getWhitespace(line) const generatedDetails = parseLine(line) // if it couldn't be parsed, it's a message line if (!generatedDetails) { return { - message: line.replace(whitespace, ''), // strip leading whitespace + message: line ? line.replace(whitespace, '') : '', // strip leading whitespace whitespace, } } From dbfef2fa0f1dcecb76a6df06e5c0402dd42402dd Mon Sep 17 00:00:00 2001 From: astone123 Date: Fri, 22 Aug 2025 12:18:13 -0400 Subject: [PATCH 06/16] add unit test --- .../test/unit/cypress/stack_utils.spec.ts | 80 +++++++++++++++++++ 1 file changed, 80 insertions(+) diff --git a/packages/driver/test/unit/cypress/stack_utils.spec.ts b/packages/driver/test/unit/cypress/stack_utils.spec.ts index f9d9970657e..150f4e0a946 100644 --- a/packages/driver/test/unit/cypress/stack_utils.spec.ts +++ b/packages/driver/test/unit/cypress/stack_utils.spec.ts @@ -64,5 +64,85 @@ describe('stack_utils', () => { }) }) } + + describe('@cypress/grep support', () => { + it('skips grep wrapper functions and finds actual test invocation', () => { + // Stack trace that includes cypress-grep wrapper functions + stack = `Error + at Object.getInvocationDetails (cypress:///../driver/src/cypress/stack_utils.ts:130:17) + at Suite.addTest (cypress:///../driver/src/cypress/mocha.ts:462:85) + at itGrep (https://example.cypress.io/__cypress/tests?p=cypress/support/e2e.js:391:14) + at Suite.eval (https://example.cypress.io/__cypress/tests?p=cypress/e2e/spec.cy.js:19:3) + at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:141:19) + at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27) + at createRunnable (cypress:///../driver/src/cypress/mocha.ts:126:31)` + + stack_utils.getInvocationDetails( + { Error: MockError, Cypress: {} }, + config, + ) + + // Should skip the itGrep line and use the actual test line + expect(source_map_utils.getSourcePosition).toHaveBeenCalledWith( + 'https://example.cypress.io/__cypress/tests?p=cypress/e2e/spec.cy.js', + expect.objectContaining({ + file: 'https://example.cypress.io/__cypress/tests?p=cypress/e2e/spec.cy.js', + line: 19, + column: 3, + }), + ) + }) + + it('skips describe grep wrapper functions and finds actual test invocation', () => { + // Stack trace that includes describeGrep wrapper function + stack = `Error + at Object.getInvocationDetails (cypress:///../driver/src/cypress/stack_utils.ts:130:17) + at Suite.addTest (cypress:///../driver/src/cypress/mocha.ts:462:85) + at describeGrep (https://example.cypress.io/__cypress/tests?p=cypress/support/e2e.js:144:14) + at Suite.eval (https://example.cypress.io/__cypress/tests?p=cypress/e2e/spec.cy.js:25:5) + at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:141:19)` + + stack_utils.getInvocationDetails( + { Error: MockError, Cypress: {} }, + config, + ) + + // Should skip the describeGrep line and use the actual test line + expect(source_map_utils.getSourcePosition).toHaveBeenCalledWith( + 'https://example.cypress.io/__cypress/tests?p=cypress/e2e/spec.cy.js', + expect.objectContaining({ + file: 'https://example.cypress.io/__cypress/tests?p=cypress/e2e/spec.cy.js', + line: 25, + column: 5, + }), + ) + }) + + it('handles multiple grep wrapper functions in stack', () => { + // Stack trace with both itGrep and describeGrep + stack = `Error + at Object.getInvocationDetails (cypress:///../driver/src/cypress/stack_utils.ts:130:17) + at Suite.addTest (cypress:///../driver/src/cypress/mocha.ts:462:85) + at itGrep (https://example.cypress.io/__cypress/tests?p=cypress/support/e2e.js:391:14) + at describeGrep (https://example.cypress.io/__cypress/tests?p=cypress/support/e2e.js:144:14) + at Suite.eval (https://example.cypress.io/__cypress/tests?p=cypress/e2e/spec.cy.js:10:7) + at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:141:19)` + + stack_utils.getInvocationDetails( + { Error: MockError, Cypress: {} }, + config, + ) + + // Should skip both grep lines and use the actual test line + expect(source_map_utils.getSourcePosition).toHaveBeenCalledWith( + 'https://example.cypress.io/__cypress/tests?p=cypress/e2e/spec.cy.js', + expect.objectContaining({ + file: 'https://example.cypress.io/__cypress/tests?p=cypress/e2e/spec.cy.js', + line: 10, + column: 7, + }), + ) + }) + }) }) }) From b1d1aec42ec403ded8b730148e9be584942ae4b2 Mon Sep 17 00:00:00 2001 From: astone123 Date: Fri, 22 Aug 2025 12:41:20 -0400 Subject: [PATCH 07/16] send features object --- packages/app/src/studio/StudioPanel.vue | 3 +++ packages/app/src/studio/studio-app-types.ts | 3 +++ 2 files changed, 6 insertions(+) diff --git a/packages/app/src/studio/StudioPanel.vue b/packages/app/src/studio/StudioPanel.vue index 2db464af37a..00f80abdc82 100644 --- a/packages/app/src/studio/StudioPanel.vue +++ b/packages/app/src/studio/StudioPanel.vue @@ -79,6 +79,9 @@ const maybeRenderReactComponent = () => { onStudioPanelClose: props.onStudioPanelClose, studioSessionId: props.cloudStudioSessionId, autUrlSelector: props.autUrlSelector, + features: { + supportsCypressGrep: true, + }, }) // Store the react root in a weak map keyed by the container. We do this so that we have a reference diff --git a/packages/app/src/studio/studio-app-types.ts b/packages/app/src/studio/studio-app-types.ts index 68fec5e8fb4..ad75c4eac8c 100644 --- a/packages/app/src/studio/studio-app-types.ts +++ b/packages/app/src/studio/studio-app-types.ts @@ -13,6 +13,9 @@ export interface StudioPanelProps { useCypress?: CypressShape autUrlSelector?: string studioAiAvailable?: boolean + features?: { + supportsCypressGrep?: boolean + } } export type StudioPanelShape = (props: StudioPanelProps) => JSX.Element From 50891be5ac18e339e763061a541be7fd6e6d1e37 Mon Sep 17 00:00:00 2001 From: astone123 Date: Fri, 22 Aug 2025 14:21:10 -0400 Subject: [PATCH 08/16] changelog --- cli/CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/cli/CHANGELOG.md b/cli/CHANGELOG.md index ee18e029ca8..c35c9ca2e19 100644 --- a/cli/CHANGELOG.md +++ b/cli/CHANGELOG.md @@ -3,6 +3,10 @@ _Released 08/26/2025 (PENDING)_ +**Features:** + +- Added support for using [@cypress/grep](https://www.npmjs.com/package/@cypress/grep) with Cypress Studio. Addresses [#32292](https://github.com/cypress-io/cypress/issues/32292). + **Bugfixes:** - Fixed an issue where the open Studio button would incorrectly show for component tests. Addressed in [#32315](https://github.com/cypress-io/cypress/pull/32315). From 2f5809f0b259e6d53ed78072e4ddefa9b02f69b5 Mon Sep 17 00:00:00 2001 From: astone123 Date: Wed, 27 Aug 2025 16:39:00 -0400 Subject: [PATCH 09/16] remove stack changes from app --- packages/app/src/store/studio-store.ts | 1 - packages/driver/src/cypress.ts | 3 +++ packages/driver/src/cypress/stack_utils.ts | 19 +++---------------- 3 files changed, 6 insertions(+), 17 deletions(-) diff --git a/packages/app/src/store/studio-store.ts b/packages/app/src/store/studio-store.ts index f8973b57253..558ad55d648 100644 --- a/packages/app/src/store/studio-store.ts +++ b/packages/app/src/store/studio-store.ts @@ -288,7 +288,6 @@ export const useStudioStore = defineStore('studioRecorder', { const grepEnvVars = [ 'grep', 'grepTags', 'grep-tags', - 'grepBurn', 'grep-burn', 'burn', 'grepUntagged', 'grep-untagged', 'grepOmitFiltered', 'grep-omit-filtered', ] diff --git a/packages/driver/src/cypress.ts b/packages/driver/src/cypress.ts index 8cfc86ce728..8ef3b25634f 100644 --- a/packages/driver/src/cypress.ts +++ b/packages/driver/src/cypress.ts @@ -30,6 +30,7 @@ import $SetterGetter from './cypress/setter_getter' import { validateConfig } from './util/config' import $utils from './cypress/utils' +import $stackUtils from './cypress/stack_utils' import { $Chainer } from './cypress/chainer' import { $Cookies, ICookies } from './cypress/cookies' import { $Command } from './cypress/command' @@ -126,6 +127,7 @@ class $Cypress { specBridgeCommunicator: SpecBridgeCommunicator isCrossOriginSpecBridge: boolean on: any + stackUtils: any // attach to $Cypress to access // all of the constructors @@ -360,6 +362,7 @@ class $Cypress { this.mocha = $Mocha.create(specWindow, this, this.config) this.runner = $Runner.create(specWindow, this.mocha, this, this.cy, this.state) this.downloads = $Downloads.create(this) + this.stackUtils = $stackUtils // wire up command create to cy // @ts-expect-error diff --git a/packages/driver/src/cypress/stack_utils.ts b/packages/driver/src/cypress/stack_utils.ts index 5e0cea449d2..ae4875a4eaf 100644 --- a/packages/driver/src/cypress/stack_utils.ts +++ b/packages/driver/src/cypress/stack_utils.ts @@ -149,20 +149,7 @@ const getInvocationDetails = (specWindow, config): InvocationDetails | undefined } } - const stackLines = getStackLines(stack) - let targetLine = stackLines[0] - - for (const line of stackLines) { - // Skip lines that contain grep wrapper function names - if (line.includes('itGrep') || line.includes('describeGrep')) { - continue - } - - targetLine = line - break - } - - const details: Omit = (getSourceDetailsForLine(config('projectRoot'), targetLine) as StackLineDetail) || {}; + const details: Omit = getSourceDetailsForFirstLine(stack, config('projectRoot')) || {}; (details as any).stack = stack @@ -361,14 +348,14 @@ interface StackLineDetail { whitespace: any } -const getSourceDetailsForLine = (projectRoot: string, line: string): MessageLineDetail | StackLineDetail => { +const getSourceDetailsForLine = (projectRoot, line): MessageLineDetail | StackLineDetail => { const whitespace = getWhitespace(line) const generatedDetails = parseLine(line) // if it couldn't be parsed, it's a message line if (!generatedDetails) { return { - message: line ? line.replace(whitespace, '') : '', // strip leading whitespace + message: line.replace(whitespace, ''), // strip leading whitespace whitespace, } } From a9606da25510ce4d5cf6182c71361bbc5410c131 Mon Sep 17 00:00:00 2001 From: astone123 Date: Wed, 27 Aug 2025 16:44:00 -0400 Subject: [PATCH 10/16] remove test --- .../test/unit/cypress/stack_utils.spec.ts | 80 ------------------- 1 file changed, 80 deletions(-) diff --git a/packages/driver/test/unit/cypress/stack_utils.spec.ts b/packages/driver/test/unit/cypress/stack_utils.spec.ts index 150f4e0a946..f9d9970657e 100644 --- a/packages/driver/test/unit/cypress/stack_utils.spec.ts +++ b/packages/driver/test/unit/cypress/stack_utils.spec.ts @@ -64,85 +64,5 @@ describe('stack_utils', () => { }) }) } - - describe('@cypress/grep support', () => { - it('skips grep wrapper functions and finds actual test invocation', () => { - // Stack trace that includes cypress-grep wrapper functions - stack = `Error - at Object.getInvocationDetails (cypress:///../driver/src/cypress/stack_utils.ts:130:17) - at Suite.addTest (cypress:///../driver/src/cypress/mocha.ts:462:85) - at itGrep (https://example.cypress.io/__cypress/tests?p=cypress/support/e2e.js:391:14) - at Suite.eval (https://example.cypress.io/__cypress/tests?p=cypress/e2e/spec.cy.js:19:3) - at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:141:19) - at context.describe.context.context (cypress:///../driver/node_modules/mocha/lib/interfaces/bdd.js:42:27) - at createRunnable (cypress:///../driver/src/cypress/mocha.ts:126:31)` - - stack_utils.getInvocationDetails( - { Error: MockError, Cypress: {} }, - config, - ) - - // Should skip the itGrep line and use the actual test line - expect(source_map_utils.getSourcePosition).toHaveBeenCalledWith( - 'https://example.cypress.io/__cypress/tests?p=cypress/e2e/spec.cy.js', - expect.objectContaining({ - file: 'https://example.cypress.io/__cypress/tests?p=cypress/e2e/spec.cy.js', - line: 19, - column: 3, - }), - ) - }) - - it('skips describe grep wrapper functions and finds actual test invocation', () => { - // Stack trace that includes describeGrep wrapper function - stack = `Error - at Object.getInvocationDetails (cypress:///../driver/src/cypress/stack_utils.ts:130:17) - at Suite.addTest (cypress:///../driver/src/cypress/mocha.ts:462:85) - at describeGrep (https://example.cypress.io/__cypress/tests?p=cypress/support/e2e.js:144:14) - at Suite.eval (https://example.cypress.io/__cypress/tests?p=cypress/e2e/spec.cy.js:25:5) - at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:141:19)` - - stack_utils.getInvocationDetails( - { Error: MockError, Cypress: {} }, - config, - ) - - // Should skip the describeGrep line and use the actual test line - expect(source_map_utils.getSourcePosition).toHaveBeenCalledWith( - 'https://example.cypress.io/__cypress/tests?p=cypress/e2e/spec.cy.js', - expect.objectContaining({ - file: 'https://example.cypress.io/__cypress/tests?p=cypress/e2e/spec.cy.js', - line: 25, - column: 5, - }), - ) - }) - - it('handles multiple grep wrapper functions in stack', () => { - // Stack trace with both itGrep and describeGrep - stack = `Error - at Object.getInvocationDetails (cypress:///../driver/src/cypress/stack_utils.ts:130:17) - at Suite.addTest (cypress:///../driver/src/cypress/mocha.ts:462:85) - at itGrep (https://example.cypress.io/__cypress/tests?p=cypress/support/e2e.js:391:14) - at describeGrep (https://example.cypress.io/__cypress/tests?p=cypress/support/e2e.js:144:14) - at Suite.eval (https://example.cypress.io/__cypress/tests?p=cypress/e2e/spec.cy.js:10:7) - at Object.create (cypress:///../driver/node_modules/mocha/lib/interfaces/common.js:141:19)` - - stack_utils.getInvocationDetails( - { Error: MockError, Cypress: {} }, - config, - ) - - // Should skip both grep lines and use the actual test line - expect(source_map_utils.getSourcePosition).toHaveBeenCalledWith( - 'https://example.cypress.io/__cypress/tests?p=cypress/e2e/spec.cy.js', - expect.objectContaining({ - file: 'https://example.cypress.io/__cypress/tests?p=cypress/e2e/spec.cy.js', - line: 10, - column: 7, - }), - ) - }) - }) }) }) From f31a08dec5e4a2bf47cfbd4f42b16ef5e0d8d7cf Mon Sep 17 00:00:00 2001 From: astone123 Date: Wed, 27 Aug 2025 16:44:50 -0400 Subject: [PATCH 11/16] fix types --- packages/app/src/studio/studio-app-types.ts | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/packages/app/src/studio/studio-app-types.ts b/packages/app/src/studio/studio-app-types.ts index ad75c4eac8c..c8a529f0811 100644 --- a/packages/app/src/studio/studio-app-types.ts +++ b/packages/app/src/studio/studio-app-types.ts @@ -13,9 +13,6 @@ export interface StudioPanelProps { useCypress?: CypressShape autUrlSelector?: string studioAiAvailable?: boolean - features?: { - supportsCypressGrep?: boolean - } } export type StudioPanelShape = (props: StudioPanelProps) => JSX.Element @@ -34,6 +31,13 @@ CyEventEmitter & { getRootSuite: () => Suite } areSourceMapsAvailable?: boolean + stackUtils?: { + getSourceDetailsForFirstLine: (stack: string, projectRoot: string) => { + line: number + column: number + file: string + } + } } export interface TestBlock { From c49f93190d8dae22a529076279ad41e12ad65bd7 Mon Sep 17 00:00:00 2001 From: astone123 Date: Wed, 27 Aug 2025 16:46:41 -0400 Subject: [PATCH 12/16] revert changes --- packages/app/src/studio/StudioPanel.vue | 3 --- 1 file changed, 3 deletions(-) diff --git a/packages/app/src/studio/StudioPanel.vue b/packages/app/src/studio/StudioPanel.vue index 00f80abdc82..2db464af37a 100644 --- a/packages/app/src/studio/StudioPanel.vue +++ b/packages/app/src/studio/StudioPanel.vue @@ -79,9 +79,6 @@ const maybeRenderReactComponent = () => { onStudioPanelClose: props.onStudioPanelClose, studioSessionId: props.cloudStudioSessionId, autUrlSelector: props.autUrlSelector, - features: { - supportsCypressGrep: true, - }, }) // Store the react root in a weak map keyed by the container. We do this so that we have a reference From 87ac4472baf2ead7115ba652befc818338903b74 Mon Sep 17 00:00:00 2001 From: astone123 Date: Wed, 3 Sep 2025 09:34:04 -0400 Subject: [PATCH 13/16] comment --- packages/app/src/store/studio-store.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/app/src/store/studio-store.ts b/packages/app/src/store/studio-store.ts index 558ad55d648..01e7809ca94 100644 --- a/packages/app/src/store/studio-store.ts +++ b/packages/app/src/store/studio-store.ts @@ -256,6 +256,8 @@ export const useStudioStore = defineStore('studioRecorder', { this.sessionId = studio.sessionId } + // if the user has any settings related to @cypress/grep, we need to temporarily remove them + // so that studio can run all of the tests regardless of whether they match the grep filters if (studio.newTestLineNumber || studio.testId) { if (this.detectAndStoreGrepSettings()) { this.clearGrepSettings() From adab0349f94b77564aa24e6f205c20b1a1e67d28 Mon Sep 17 00:00:00 2001 From: astone123 Date: Wed, 3 Sep 2025 09:35:16 -0400 Subject: [PATCH 14/16] update changelog --- cli/CHANGELOG.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/cli/CHANGELOG.md b/cli/CHANGELOG.md index 9ce1b57ff74..a5143b839a5 100644 --- a/cli/CHANGELOG.md +++ b/cli/CHANGELOG.md @@ -3,6 +3,10 @@ _Released 09/16/2025 (PENDING)_ +**Features:** + +- Added support for using [@cypress/grep](https://www.npmjs.com/package/@cypress/grep) with Cypress Studio. Addresses [#32292](https://github.com/cypress-io/cypress/issues/32292). + **Dependency Updates:** - Updated [`better-sqlite3`](https://www.npmjs.com/package/better-sqlite3) from `11.9.1` to `11.10.0`. Addressed in [#32404](https://github.com/cypress-io/cypress/pull/32404). @@ -15,10 +19,6 @@ _Released 09/02/2025_ - Expanded `cy.press()` to support more key types. Addresses [#31051](https://github.com/cypress-io/cypress/issues/31051) and [#31488](https://github.com/cypress-io/cypress/issues/31488). Addressed in [#31496](https://github.com/cypress-io/cypress/pull/31496). -**Features:** - -- Added support for using [@cypress/grep](https://www.npmjs.com/package/@cypress/grep) with Cypress Studio. Addresses [#32292](https://github.com/cypress-io/cypress/issues/32292). - **Bugfixes:** - Fixed an issue where OS distributions and releases were sometimes not properly populated for Module API results and Cloud recordings. Fixes [#30533](https://github.com/cypress-io/cypress/issues/30533). Addressed in [#32283](https://github.com/cypress-io/cypress/pull/32283). From 8a8f8a9c65ef4d0532a87d4f50283612350386ec Mon Sep 17 00:00:00 2001 From: astone123 Date: Wed, 3 Sep 2025 17:36:08 -0400 Subject: [PATCH 15/16] add type for stackUtils --- packages/driver/src/cypress.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/driver/src/cypress.ts b/packages/driver/src/cypress.ts index 8ef3b25634f..ab2aee41c6d 100644 --- a/packages/driver/src/cypress.ts +++ b/packages/driver/src/cypress.ts @@ -127,7 +127,7 @@ class $Cypress { specBridgeCommunicator: SpecBridgeCommunicator isCrossOriginSpecBridge: boolean on: any - stackUtils: any + stackUtils: typeof $stackUtils | null = null // attach to $Cypress to access // all of the constructors From 11d8ef9ed51d699dda1aee941acf1f98b900695e Mon Sep 17 00:00:00 2001 From: Adam Stone-Lord Date: Thu, 4 Sep 2025 11:02:28 -0400 Subject: [PATCH 16/16] Update packages/app/src/store/studio-store.ts Co-authored-by: Matt Schile --- packages/app/src/store/studio-store.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/app/src/store/studio-store.ts b/packages/app/src/store/studio-store.ts index 01e7809ca94..654b3b75061 100644 --- a/packages/app/src/store/studio-store.ts +++ b/packages/app/src/store/studio-store.ts @@ -400,8 +400,6 @@ export const useStudioStore = defineStore('studioRecorder', { }, cancel () { - this.restoreGrepSettings() - this.reset() this.clearRunnableIds() this._removeUrlParams()