Skip to content

Commit f1b0e55

Browse files
authored
feat: openInIDE for failed debug spec (#25691)
1 parent 0fb06a4 commit f1b0e55

File tree

5 files changed

+158
-20
lines changed

5 files changed

+158
-20
lines changed

cli/CHANGELOG.md

+4
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@
33

44
_Released 02/14/2023 (PENDING)_
55

6+
**Features:**
7+
8+
- Added the "Open in IDE" feature for failed tests reported from the Debug page. Addressed in [#25691](https://github.com/cypress-io/cypress/pull/25691).
9+
610
**Misc:**
711

812
- Improved the layout of the Debug Page on smaller viewports when there is a pending run. Addresses [#25664](https://github.com/cypress-io/cypress/issues/25664).

packages/app/cypress/e2e/debug.cy.ts

+33-12
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import type { OpenFileInIdeQuery } from '../../src/generated/graphql-test'
12
import RelevantRunsDataSource_RunsByCommitShas from '../fixtures/gql-RelevantRunsDataSource_RunsByCommitShas.json'
23

34
Cypress.on('window:before:load', (win) => {
@@ -31,27 +32,27 @@ describe('App - Debug Page', () => {
3132
it('all tests passed', () => {
3233
// This mocks all the responses so we can get deterministic
3334
// results to test the debug page.
34-
cy.intercept('POST', '/__cypress/graphql/query-Debug', {
35+
cy.intercept('query-Debug', {
3536
fixture: 'debug-Passing/gql-Debug.json',
3637
})
3738

38-
cy.intercept('POST', '/__cypress/graphql/query-CloudViewerAndProject_RequiredData', {
39+
cy.intercept('query-CloudViewerAndProject_RequiredData', {
3940
fixture: 'debug-Passing/gql-CloudViewerAndProject_RequiredData.json',
4041
})
4142

42-
cy.intercept('POST', '/__cypress/graphql/query-MainAppQuery', {
43+
cy.intercept('query-MainAppQuery', {
4344
fixture: 'debug-Passing/gql-MainAppQuery.json',
4445
})
4546

46-
cy.intercept('POST', '/__cypress/graphql/query-SideBarNavigationContainer', {
47+
cy.intercept('query-SideBarNavigationContainer', {
4748
fixture: 'debug-Passing/gql-SideBarNavigationContainer',
4849
})
4950

50-
cy.intercept('POST', '/__cypress/graphql/query-HeaderBar_HeaderBarQuery', {
51+
cy.intercept('query-HeaderBar_HeaderBarQuery', {
5152
fixture: 'debug-Passing/gql-HeaderBar_HeaderBarQuery',
5253
})
5354

54-
cy.intercept('POST', '/__cypress/graphql/query-SpecsPageContainer', {
55+
cy.intercept('query-SpecsPageContainer', {
5556
fixture: 'debug-Passing/gql-SpecsPageContainer',
5657
})
5758

@@ -86,30 +87,44 @@ describe('App - Debug Page', () => {
8687
})
8788

8889
it('shows information about a failed spec', () => {
89-
cy.intercept('POST', '/__cypress/graphql/query-Debug', {
90+
cy.intercept('query-Debug', {
9091
fixture: 'debug-Failing/gql-Debug.json',
9192
})
9293

93-
cy.intercept('POST', '/__cypress/graphql/query-CloudViewerAndProject_RequiredData', {
94+
cy.intercept('query-CloudViewerAndProject_RequiredData', {
9495
fixture: 'debug-Failing/gql-CloudViewerAndProject_RequiredData.json',
9596
})
9697

97-
cy.intercept('POST', '/__cypress/graphql/query-MainAppQuery', {
98+
cy.intercept('query-MainAppQuery', {
9899
fixture: 'debug-Failing/gql-MainAppQuery.json',
99100
})
100101

101-
cy.intercept('POST', '/__cypress/graphql/query-SideBarNavigationContainer', {
102+
cy.intercept('query-SideBarNavigationContainer', {
102103
fixture: 'debug-Failing/gql-SideBarNavigationContainer',
103104
})
104105

105-
cy.intercept('POST', '/__cypress/graphql/query-HeaderBar_HeaderBarQuery', {
106+
cy.intercept('query-HeaderBar_HeaderBarQuery', {
106107
fixture: 'debug-Failing/gql-HeaderBar_HeaderBarQuery',
107108
})
108109

109-
cy.intercept('POST', '/__cypress/graphql/query-SpecsPageContainer', {
110+
cy.intercept('query-SpecsPageContainer', {
110111
fixture: 'debug-Failing/gql-SpecsPageContainer',
111112
})
112113

114+
cy.intercept('query-OpenFileInIDE', (req) => {
115+
req.on('response', (res) => {
116+
const gqlData = res.body.data as OpenFileInIdeQuery
117+
118+
gqlData.localSettings.preferences.preferredEditorBinary = 'code'
119+
})
120+
})
121+
122+
cy.intercept('mutation-OpenFileInIDE_Mutation').as('openFileInIDE')
123+
124+
cy.withCtx((ctx, { sinon }) => {
125+
sinon.stub(ctx.actions.file, 'openFile')
126+
})
127+
113128
cy.visitApp()
114129

115130
cy.findByTestId('sidebar-link-debug-page').click()
@@ -147,5 +162,11 @@ describe('App - Debug Page', () => {
147162
cy.findByTestId('test-row').contains('InfoPanel')
148163
cy.findByTestId('test-row').contains('renders')
149164
cy.findByTestId('run-failures').should('exist').should('have.attr', 'href', '#/specs/runner?file=src/components/InfoPanel/InfoPanel.cy.ts&mode=debug')
165+
166+
cy.findByLabelText('Open in IDE').click()
167+
cy.wait('@openFileInIDE')
168+
cy.withCtx((ctx) => {
169+
expect(ctx.actions.file.openFile).to.have.been.calledWith('src/components/InfoPanel/InfoPanel.cy.ts', 1, 1)
170+
})
150171
})
151172
})

packages/app/src/debug/DebugSpec.cy.tsx

+49
Original file line numberDiff line numberDiff line change
@@ -530,3 +530,52 @@ describe('Run Failures button', () => {
530530
.and('not.have.attr', 'aria-disabled')
531531
})
532532
})
533+
534+
describe('Open in IDE', () => {
535+
const spec = {
536+
id: '8879798756s88d',
537+
path: 'cypress/tests/',
538+
fileName: 'auth',
539+
fileExtension: '.spec.ts',
540+
fullPath: 'cypress/tests/auth.spec.ts',
541+
testsPassed: resultCounts(22, 22),
542+
testsFailed: resultCounts(2, 2),
543+
testsPending: resultCounts(1, 1),
544+
specDuration: {
545+
min: 143000,
546+
max: 143000,
547+
},
548+
}
549+
550+
const renderDebugSpec = ({ foundLocally } = { foundLocally: true }) => cy.mount(() =>
551+
<div class="px-24px">
552+
<DebugSpec spec={spec}
553+
testResults={testResultSingleGroup}
554+
groups={singleGroup}
555+
testingType={'e2e'}
556+
foundLocally={foundLocally}
557+
matchesCurrentTestingType={true}
558+
/>
559+
</div>)
560+
561+
it('shows openInIDE if file is found locally', () => {
562+
renderDebugSpec()
563+
564+
cy.findByLabelText(defaultMessages.debugPage.openFile.openInIDE).as('openInIDE').realHover()
565+
cy.findByTestId('open-in-ide-tooltip').should('be.visible').and('contain', defaultMessages.debugPage.openFile.openInIDE)
566+
cy.percySnapshot()
567+
568+
cy.get('@openInIDE').click()
569+
570+
cy.findByLabelText('External editor preferences').should('be.visible')
571+
cy.findByLabelText('Close').click()
572+
})
573+
574+
it('shows disabled openInIDE if file is not found locally', () => {
575+
renderDebugSpec({ foundLocally: false })
576+
577+
cy.findByLabelText(defaultMessages.debugPage.openFile.notFoundLocally).as('openInIDE').realHover()
578+
cy.findByTestId('open-in-ide-disabled-tooltip').should('be.visible').and('contain', defaultMessages.debugPage.openFile.notFoundLocally)
579+
cy.percySnapshot()
580+
})
581+
})

packages/app/src/debug/DebugSpec.vue

+68-8
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,72 @@
1616
class="flex w-full grid px-18px gap-y-8px items-center"
1717
>
1818
<div class="flex-grow flex w-full gap-x-2 truncate items-center">
19-
<IconDocumentText
20-
stroke-color="gray-500"
21-
fill-color="gray-100"
22-
size="16"
23-
class="min-w-16px"
24-
/>
25-
19+
<Tooltip
20+
v-if="foundLocally"
21+
placement="bottom"
22+
color="dark"
23+
:distance="8"
24+
>
25+
<OpenFileInIDE
26+
v-slot="{onClick}"
27+
:file-path="specData.fullPath"
28+
>
29+
<button
30+
class="rounded-md border-1px border-gray-100 p-4px group hocus:border-indigo-200"
31+
:aria-label="t('debugPage.openFile.openInIDE')"
32+
@click="onClick"
33+
>
34+
<IconDocumentText
35+
stroke-color="gray-500"
36+
fill-color="gray-100"
37+
hocus-stroke-color="indigo-400"
38+
hocus-fill-color="indigo-200"
39+
size="16"
40+
interactive-colors-on-group
41+
class="min-w-16px"
42+
/>
43+
</button>
44+
</OpenFileInIDE>
45+
<template
46+
#popper
47+
>
48+
<div
49+
class="text-center text-sm max-w-240px"
50+
data-cy="open-in-ide-tooltip"
51+
>
52+
{{ t('debugPage.openFile.openInIDE') }}
53+
</div>
54+
</template>
55+
</Tooltip>
56+
<Tooltip
57+
v-else
58+
placement="bottom"
59+
color="dark"
60+
:distance="8"
61+
>
62+
<button
63+
aria-disabled
64+
:aria-label="t('debugPage.openFile.notFoundLocally')"
65+
class="rounded-md border-1px border-gray-100 p-4px"
66+
>
67+
<IconDocumentMinus
68+
stroke-color="gray-500"
69+
fill-color="gray-100"
70+
size="16"
71+
class="min-w-16px"
72+
/>
73+
</button>
74+
<template
75+
#popper
76+
>
77+
<div
78+
class="text-center text-sm max-w-240px"
79+
data-cy="open-in-ide-disabled-tooltip"
80+
>
81+
{{ t('debugPage.openFile.notFoundLocally') }}
82+
</div>
83+
</template>
84+
</Tooltip>
2685
<div
2786
data-cy="spec-path"
2887
class="flex-grow text-base non-italic truncate"
@@ -144,7 +203,7 @@
144203
<script lang="ts" setup>
145204
146205
import { computed, unref } from 'vue'
147-
import { IconActionRefresh, IconDocumentText } from '@cypress-design/vue-icon'
206+
import { IconActionRefresh, IconDocumentText, IconDocumentMinus } from '@cypress-design/vue-icon'
148207
import type { SpecDataAggregate, CloudRunInstance } from '@packages/data-context/src/gen/graphcache-config.gen'
149208
import DebugFailedTest from './DebugFailedTest.vue'
150209
import StatsMetaData from './StatsMetadata.vue'
@@ -156,6 +215,7 @@ import { useI18n } from '@cy/i18n'
156215
import { useDurationFormat } from '../composables/useDurationFormat'
157216
import { posixify } from '../paths'
158217
import type { StatsMetadata_GroupsFragment, TestingTypeEnum } from '../generated/graphql'
218+
import OpenFileInIDE from '@cy/gql-components/OpenFileInIDE.vue'
159219
160220
export interface Spec {
161221
id: string

packages/frontend-shared/src/locales/en-US.json

+4
Original file line numberDiff line numberDiff line change
@@ -650,6 +650,10 @@
650650
}
651651
},
652652
"debugPage": {
653+
"openFile": {
654+
"openInIDE": "Open in IDE",
655+
"notFoundLocally": "Opening in IDE is disabled because the spec is not found in this project"
656+
},
653657
"limit": {
654658
"title": "Cypress renders up to 100 failed test results",
655659
"message": "This run has {n} failed tests | This run has {n} failed test | This run has {n} failed tests",

0 commit comments

Comments
 (0)