Skip to content

Commit 26edc21

Browse files
authored
fix: studio save to definitions with config and surface save errs (#15069)
1 parent 9c64236 commit 26edc21

File tree

8 files changed

+206
-21
lines changed

8 files changed

+206
-21
lines changed

packages/runner/src/lib/event-manager.js

+3-3
Original file line numberDiff line numberDiff line change
@@ -243,9 +243,9 @@ const eventManager = {
243243
})
244244

245245
localBus.on('studio:save', (saveInfo) => {
246-
ws.emit('studio:save', saveInfo, (success) => {
247-
if (!success) {
248-
reporterBus.emit('test:set:state', studioRecorder.saveError, _.noop)
246+
ws.emit('studio:save', saveInfo, (err) => {
247+
if (err) {
248+
reporterBus.emit('test:set:state', studioRecorder.saveError(err), _.noop)
249249
}
250250
})
251251
})

packages/runner/src/studio/studio-recorder.js

+11-8
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,14 @@ import $driverUtils from '@packages/driver/src/cypress/utils'
44

55
import eventManager from '../lib/event-manager'
66

7-
const saveErrorMessage = `\
7+
const saveErrorMessage = (message) => {
8+
return `\
9+
${message}\n\n\
810
Cypress was unable to save these commands to your spec file. \
911
Cypress Studio is still in beta and the team is working hard to \
1012
resolve issues like this. To help us fix this issue more quickly, \
1113
you can provide us with more information by clicking 'Learn more' below.`
14+
}
1215

1316
const eventTypes = [
1417
'click',
@@ -75,21 +78,21 @@ export class StudioRecorder {
7578
}
7679
}
7780

78-
@computed get saveError () {
81+
get Cypress () {
82+
return eventManager.getCypress()
83+
}
84+
85+
saveError (err) {
7986
return {
8087
id: this.testId,
8188
err: {
82-
name: 'CypressError',
83-
message: saveErrorMessage,
89+
...err,
90+
message: saveErrorMessage(err.message),
8491
docsUrl: 'https://on.cypress.io/studio-beta',
8592
},
8693
}
8794
}
8895

89-
get Cypress () {
90-
return eventManager.getCypress()
91-
}
92-
9396
@action setTestId = (testId) => {
9497
this.testId = testId
9598
}

packages/server/__snapshots__/spec_writer_spec.ts.js

+132
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,10 @@ describe('top level suite', () => {
5050
it.only('test with it.only', () => {
5151
cy.get('.btn').click()
5252
})
53+
54+
it('test with config', { responseTimeout: 60000 }, () => {
55+
cy.get('.btn').click()
56+
})
5357
})
5458
5559
context('inner suite with context', () => {
@@ -60,6 +64,10 @@ describe('top level suite', () => {
6064
describe.only('inner suite with describe.only', () => {
6165
6266
})
67+
68+
describe('suite with config', { responseTimeout: 60000 }, () => {
69+
70+
})
6371
})
6472
6573
`
@@ -83,6 +91,10 @@ describe('top level suite', () => {
8391
it.only('test with it.only', () => {
8492
cy.get('.btn').click()
8593
})
94+
95+
it('test with config', { responseTimeout: 60000 }, () => {
96+
cy.get('.btn').click()
97+
})
8698
})
8799
88100
context('inner suite with context', () => {
@@ -93,6 +105,10 @@ describe('top level suite', () => {
93105
describe.only('inner suite with describe.only', () => {
94106
95107
})
108+
109+
describe('suite with config', { responseTimeout: 60000 }, () => {
110+
111+
})
96112
})
97113
98114
`
@@ -113,6 +129,10 @@ describe('top level suite', () => {
113129
cy.get('.btn').click()
114130
})
115131
132+
it('test with config', { responseTimeout: 60000 }, () => {
133+
cy.get('.btn').click()
134+
})
135+
116136
/* === Test Created with Cypress Studio === */
117137
it('test added to describe', function() {
118138
/* ==== Generated with Cypress Studio ==== */
@@ -130,6 +150,10 @@ describe('top level suite', () => {
130150
describe.only('inner suite with describe.only', () => {
131151
132152
})
153+
154+
describe('suite with config', { responseTimeout: 60000 }, () => {
155+
156+
})
133157
})
134158
135159
`
@@ -149,6 +173,10 @@ describe('top level suite', () => {
149173
it.only('test with it.only', () => {
150174
cy.get('.btn').click()
151175
})
176+
177+
it('test with config', { responseTimeout: 60000 }, () => {
178+
cy.get('.btn').click()
179+
})
152180
})
153181
154182
context('inner suite with context', () => {
@@ -165,6 +193,10 @@ describe('top level suite', () => {
165193
describe.only('inner suite with describe.only', () => {
166194
167195
})
196+
197+
describe('suite with config', { responseTimeout: 60000 }, () => {
198+
199+
})
168200
})
169201
170202
`
@@ -188,6 +220,10 @@ describe('top level suite', () => {
188220
cy.get('.btn').click();
189221
/* ==== End Cypress Studio ==== */
190222
})
223+
224+
it('test with config', { responseTimeout: 60000 }, () => {
225+
cy.get('.btn').click()
226+
})
191227
})
192228
193229
context('inner suite with context', () => {
@@ -198,6 +234,10 @@ describe('top level suite', () => {
198234
describe.only('inner suite with describe.only', () => {
199235
200236
})
237+
238+
describe('suite with config', { responseTimeout: 60000 }, () => {
239+
240+
})
201241
})
202242
203243
`
@@ -217,6 +257,10 @@ describe('top level suite', () => {
217257
it.only('test with it.only', () => {
218258
cy.get('.btn').click()
219259
})
260+
261+
it('test with config', { responseTimeout: 60000 }, () => {
262+
cy.get('.btn').click()
263+
})
220264
})
221265
222266
context('inner suite with context', () => {
@@ -233,6 +277,94 @@ describe('top level suite', () => {
233277
/* ==== End Cypress Studio ==== */
234278
});
235279
})
280+
281+
describe('suite with config', { responseTimeout: 60000 }, () => {
282+
283+
})
284+
})
285+
286+
`
287+
288+
exports['lib/util/spec_writer #appendCommandsToTest can add commands to an existing test with config 1'] = `
289+
describe('top level suite', () => {
290+
describe('inner suite with describe', () => {
291+
it('test with it', () => {
292+
cy.get('.btn').click()
293+
})
294+
295+
specify('test with specify', () => {
296+
cy.get('.btn').click()
297+
})
298+
299+
// eslint-disable-next-line mocha/no-exclusive-tests
300+
it.only('test with it.only', () => {
301+
cy.get('.btn').click()
302+
})
303+
304+
it('test with config', { responseTimeout: 60000 }, () => {
305+
cy.get('.btn').click()
306+
/* ==== Generated with Cypress Studio ==== */
307+
cy.get('.input').type('typed text');
308+
cy.get('.btn').click();
309+
/* ==== End Cypress Studio ==== */
310+
})
311+
})
312+
313+
context('inner suite with context', () => {
314+
315+
})
316+
317+
// eslint-disable-next-line mocha/no-exclusive-tests
318+
describe.only('inner suite with describe.only', () => {
319+
320+
})
321+
322+
describe('suite with config', { responseTimeout: 60000 }, () => {
323+
324+
})
325+
})
326+
327+
`
328+
329+
exports['lib/util/spec_writer #createNewTestInSuite can create a new test in a suite with config 1'] = `
330+
describe('top level suite', () => {
331+
describe('inner suite with describe', () => {
332+
it('test with it', () => {
333+
cy.get('.btn').click()
334+
})
335+
336+
specify('test with specify', () => {
337+
cy.get('.btn').click()
338+
})
339+
340+
// eslint-disable-next-line mocha/no-exclusive-tests
341+
it.only('test with it.only', () => {
342+
cy.get('.btn').click()
343+
})
344+
345+
it('test with config', { responseTimeout: 60000 }, () => {
346+
cy.get('.btn').click()
347+
})
348+
})
349+
350+
context('inner suite with context', () => {
351+
352+
})
353+
354+
// eslint-disable-next-line mocha/no-exclusive-tests
355+
describe.only('inner suite with describe.only', () => {
356+
357+
})
358+
359+
describe('suite with config', { responseTimeout: 60000 }, () => {
360+
/* === Test Created with Cypress Studio === */
361+
it('test added to describe with config', function() {
362+
/* ==== Generated with Cypress Studio ==== */
363+
cy.get('.input').type('typed text');
364+
cy.get('.btn').click();
365+
/* ==== End Cypress Studio ==== */
366+
});
367+
})
236368
})
237369
238370
`

packages/server/lib/socket-e2e.ts

+7-5
Original file line numberDiff line numberDiff line change
@@ -114,17 +114,19 @@ export class SocketE2E extends SocketBase {
114114
})
115115

116116
socket.on('studio:save', (saveInfo, cb) => {
117-
// even if the user has turned off file watching
118-
// we want to force a reload on save
117+
// even if the user has turned off file watching
118+
// we want to force a reload on save
119119
if (!config.watchForFileChanges) {
120120
preprocessor.emitter.on('file:updated', this.onStudioTestFileChange)
121121
}
122122

123123
studio.save(saveInfo)
124-
.then((success) => {
125-
cb(success)
124+
.then((err) => {
125+
cb(err)
126126

127-
if (!success && !config.watchForFileChanges) {
127+
// onStudioTestFileChange will remove itself after being called
128+
// but if there's an error, it never gets called so we manually remove it
129+
if (err && !config.watchForFileChanges) {
128130
this.removeOnStudioTestFileChange()
129131
}
130132
})

packages/server/lib/studio.ts

+24-2
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,15 @@ interface SaveInfo {
88
testName?: string
99
}
1010

11+
class StudioSaveError extends Error {
12+
static errMessage = (isSuite) => `Studio was unable to find your ${isSuite ? 'suite' : 'test'} in the spec file.`
13+
14+
constructor (isSuite) {
15+
super(StudioSaveError.errMessage(isSuite))
16+
this.name = 'StudioSaveError'
17+
}
18+
}
19+
1120
export const setStudioModalShown = () => {
1221
return savedState.create()
1322
.then((state) => {
@@ -35,7 +44,20 @@ export const save = (saveInfo: SaveInfo) => {
3544
return saveToFile()
3645
.then((success) => {
3746
return setStudioModalShown()
38-
.then(() => success)
47+
.then(() => {
48+
if (!success) {
49+
throw new StudioSaveError(isSuite)
50+
}
51+
52+
return null
53+
})
54+
})
55+
.catch((err) => {
56+
return {
57+
type: err.type,
58+
name: err.name,
59+
message: err.message,
60+
stack: err.stack,
61+
}
3962
})
40-
.catch(() => false)
4163
}

packages/server/lib/util/spec_writer.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,9 @@ export const generateAstRules = (fileDetails: { line: number, column: number },
130130
const columnEnd = identifier.loc.end.column + 2
131131

132132
if (fnNames.includes(identifier.name) && identifier.loc.start.line === line && columnStart <= column && column <= columnEnd) {
133-
const fn = node.arguments[1] as n.FunctionExpression
133+
const arg1 = node.arguments[1]
134+
135+
const fn = (arg1.type === 'ObjectExpression' ? node.arguments[2] : arg1) as n.FunctionExpression
134136

135137
if (!fn) {
136138
return false

packages/server/test/support/fixtures/projects/studio/cypress/integration/unwritten.spec.js

+8
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@ describe('top level suite', () => {
1212
it.only('test with it.only', () => {
1313
cy.get('.btn').click()
1414
})
15+
16+
it('test with config', { responseTimeout: 60000 }, () => {
17+
cy.get('.btn').click()
18+
})
1519
})
1620

1721
context('inner suite with context', () => {
@@ -22,4 +26,8 @@ describe('top level suite', () => {
2226
describe.only('inner suite with describe.only', () => {
2327

2428
})
29+
30+
describe('suite with config', { responseTimeout: 60000 }, () => {
31+
32+
})
2533
})

0 commit comments

Comments
 (0)