Skip to content

Commit 3f05587

Browse files
Merge branch 'develop' into issue-9220-fix-issue-with-blank-url-in-proxy-develop
2 parents 4e9c4f8 + 570f91d commit 3f05587

File tree

4 files changed

+149
-39
lines changed

4 files changed

+149
-39
lines changed

cli/types/cypress.d.ts

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2209,12 +2209,9 @@ declare namespace Cypress {
22092209
* @see https://on.cypress.io/writefile
22102210
```
22112211
cy.writeFile('path/to/message.txt', 'Hello World')
2212-
.then((text) => {
2213-
expect(text).to.equal('Hello World') // true
2214-
})
22152212
```
22162213
*/
2217-
writeFile<C extends FileContents>(filePath: string, contents: C, encoding: Encodings): Chainable<C>
2214+
writeFile(filePath: string, contents: FileContents, encoding: Encodings): Chainable<null>
22182215
/**
22192216
* Write to a file with the specified encoding and contents.
22202217
*
@@ -2223,12 +2220,10 @@ declare namespace Cypress {
22232220
cy.writeFile('path/to/ascii.txt', 'Hello World', {
22242221
flag: 'a+',
22252222
encoding: 'ascii'
2226-
}).then((text) => {
2227-
expect(text).to.equal('Hello World') // true
22282223
})
22292224
```
22302225
*/
2231-
writeFile<C extends FileContents>(filePath: string, contents: C, options?: Partial<WriteFileOptions>): Chainable<C>
2226+
writeFile(filePath: string, contents: FileContents, options?: Partial<WriteFileOptions & Timeoutable>): Chainable<null>
22322227
/**
22332228
* Write to a file with the specified encoding and contents.
22342229
*
@@ -2238,12 +2233,10 @@ declare namespace Cypress {
22382233
```
22392234
cy.writeFile('path/to/ascii.txt', 'Hello World', 'utf8', {
22402235
flag: 'a+',
2241-
}).then((text) => {
2242-
expect(text).to.equal('Hello World') // true
22432236
})
22442237
```
22452238
*/
2246-
writeFile<C extends FileContents>(filePath: string, contents: C, encoding: Encodings, options?: Partial<WriteFileOptions>): Chainable<C>
2239+
writeFile(filePath: string, contents: FileContents, encoding: Encodings, options?: Partial<WriteFileOptions & Timeoutable>): Chainable<null>
22472240

22482241
/**
22492242
* jQuery library bound to the AUT

packages/driver/cypress/integration/commands/files_spec.js

Lines changed: 99 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,7 @@ describe('src/cy/commands/files', () => {
145145
if (attrs.name === 'readFile') {
146146
expect(log.get('state')).to.eq('pending')
147147
expect(log.get('message')).to.eq('foo.json')
148+
expect(log.get('timeout')).to.eq(Cypress.config('defaultCommandTimeout'))
148149
}
149150
})
150151

@@ -321,6 +322,54 @@ describe('src/cy/commands/files', () => {
321322

322323
cy.readFile('foo.json').should('equal', 'contents')
323324
})
325+
326+
it('throws when the read timeout expires', function (done) {
327+
Cypress.backend.callsFake(() => {
328+
return new Cypress.Promise(() => { /* Broken promise for timeout */ })
329+
})
330+
331+
cy.on('fail', (err) => {
332+
const { lastLog } = this
333+
334+
assertLogLength(this.logs, 1)
335+
expect(lastLog.get('error')).to.eq(err)
336+
expect(lastLog.get('state')).to.eq('failed')
337+
expect(err.message).to.eq(stripIndent`\
338+
\`cy.readFile("foo")\` timed out after waiting \`10ms\`.
339+
`)
340+
341+
expect(err.docsUrl).to.eq('https://on.cypress.io/readfile')
342+
343+
done()
344+
})
345+
346+
cy.readFile('foo', { timeout: 10 })
347+
})
348+
349+
it('uses defaultCommandTimeout config value if option not provided', {
350+
defaultCommandTimeout: 42,
351+
}, function (done) {
352+
Cypress.backend.callsFake(() => {
353+
return new Cypress.Promise(() => { /* Broken promise for timeout */ })
354+
})
355+
356+
cy.on('fail', (err) => {
357+
const { lastLog } = this
358+
359+
assertLogLength(this.logs, 1)
360+
expect(lastLog.get('error')).to.eq(err)
361+
expect(lastLog.get('state')).to.eq('failed')
362+
expect(err.message).to.eq(stripIndent`\
363+
\`cy.readFile("foo")\` timed out after waiting \`42ms\`.
364+
`)
365+
366+
expect(err.docsUrl).to.eq('https://on.cypress.io/readfile')
367+
368+
done()
369+
})
370+
371+
cy.readFile('foo')
372+
})
324373
})
325374
})
326375

@@ -394,7 +443,7 @@ describe('src/cy/commands/files', () => {
394443
Cypress.backend.resolves(okResponse)
395444

396445
cy.writeFile('foo.txt', 'contents').then((subject) => {
397-
expect(subject).to.not.exist
446+
expect(subject).to.eq(null)
398447
})
399448
})
400449

@@ -481,6 +530,7 @@ describe('src/cy/commands/files', () => {
481530
if (attrs.name === 'writeFile') {
482531
expect(log.get('state')).to.eq('pending')
483532
expect(log.get('message')).to.eq('foo.txt', 'contents')
533+
expect(log.get('timeout')).to.eq(Cypress.config('defaultCommandTimeout'))
484534
}
485535
})
486536

@@ -601,6 +651,54 @@ describe('src/cy/commands/files', () => {
601651

602652
cy.writeFile('foo.txt', 'contents')
603653
})
654+
655+
it('throws when the write timeout expires', function (done) {
656+
Cypress.backend.callsFake(() => {
657+
return new Cypress.Promise(() => {})
658+
})
659+
660+
cy.on('fail', (err) => {
661+
const { lastLog } = this
662+
663+
assertLogLength(this.logs, 1)
664+
expect(lastLog.get('error')).to.eq(err)
665+
expect(lastLog.get('state')).to.eq('failed')
666+
expect(err.message).to.eq(stripIndent`
667+
\`cy.writeFile("foo.txt")\` timed out after waiting \`10ms\`.
668+
`)
669+
670+
expect(err.docsUrl).to.eq('https://on.cypress.io/writefile')
671+
672+
done()
673+
})
674+
675+
cy.writeFile('foo.txt', 'contents', { timeout: 10 })
676+
})
677+
678+
it('uses defaultCommandTimeout config value if option not provided', {
679+
defaultCommandTimeout: 42,
680+
}, function (done) {
681+
Cypress.backend.callsFake(() => {
682+
return new Cypress.Promise(() => { /* Broken promise for timeout */ })
683+
})
684+
685+
cy.on('fail', (err) => {
686+
const { lastLog } = this
687+
688+
assertLogLength(this.logs, 1)
689+
expect(lastLog.get('error')).to.eq(err)
690+
expect(lastLog.get('state')).to.eq('failed')
691+
expect(err.message).to.eq(stripIndent`
692+
\`cy.writeFile("foo.txt")\` timed out after waiting \`42ms\`.
693+
`)
694+
695+
expect(err.docsUrl).to.eq('https://on.cypress.io/writefile')
696+
697+
done()
698+
})
699+
700+
cy.writeFile('foo.txt', 'contents')
701+
})
604702
})
605703
})
606704
})

packages/driver/src/cy/commands/files.ts

Lines changed: 39 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
// @ts-nocheck
22
import _ from 'lodash'
3-
import Promise from 'bluebird'
43

54
import $errUtils from '../../cypress/error_utils'
65

@@ -22,6 +21,7 @@ export default (Commands, Cypress, cy) => {
2221
// to restore the default node behavior.
2322
encoding: encoding === undefined ? 'utf8' : encoding,
2423
log: true,
24+
timeout: Cypress.config('defaultCommandTimeout'),
2525
})
2626

2727
const consoleProps = {}
@@ -43,21 +43,33 @@ export default (Commands, Cypress, cy) => {
4343
})
4444
}
4545

46+
// We clear the default timeout so we can handle
47+
// the timeout ourselves
48+
cy.clearTimeout()
49+
4650
const verifyAssertions = () => {
47-
return Cypress.backend('read:file', file, _.pick(options, 'encoding'))
51+
return Cypress.backend('read:file', file, _.pick(options, 'encoding')).timeout(options.timeout)
4852
.catch((err) => {
49-
if (err.code === 'ENOENT') {
50-
return {
51-
contents: null,
52-
filePath: err.filePath,
53-
}
53+
if (err.name === 'TimeoutError') {
54+
return $errUtils.throwErrByPath('files.timed_out', {
55+
onFail: options._log,
56+
args: { cmd: 'readFile', file, timeout: options.timeout },
57+
})
5458
}
5559

56-
return $errUtils.throwErrByPath('files.unexpected_error', {
57-
onFail: options._log,
58-
args: { cmd: 'readFile', action: 'read', file, filePath: err.filePath, error: err.message },
59-
})
60-
}).then(({ contents, filePath }) => {
60+
// Non-ENOENT errors are not retried
61+
if (err.code !== 'ENOENT') {
62+
return $errUtils.throwErrByPath('files.unexpected_error', {
63+
onFail: options._log,
64+
args: { cmd: 'readFile', action: 'read', file, filePath: err.filePath, error: err.message },
65+
})
66+
}
67+
68+
return {
69+
contents: null,
70+
filePath: err.filePath,
71+
}
72+
}).then(({ filePath, contents }) => {
6173
// https://github.com/cypress-io/cypress/issues/1558
6274
// We invoke Buffer.from() in order to transform this from an ArrayBuffer -
6375
// which socket.io uses to transfer the file over the websocket - into a
@@ -110,14 +122,15 @@ export default (Commands, Cypress, cy) => {
110122
encoding: encoding === undefined ? 'utf8' : encoding,
111123
flag: userOptions.flag ? userOptions.flag : 'w',
112124
log: true,
125+
timeout: Cypress.config('defaultCommandTimeout'),
113126
})
114127

115128
const consoleProps = {}
116129

117130
if (options.log) {
118131
options._log = Cypress.log({
119132
message: fileName,
120-
timeout: 0,
133+
timeout: options.timeout,
121134
consoleProps () {
122135
return consoleProps
123136
},
@@ -142,19 +155,25 @@ export default (Commands, Cypress, cy) => {
142155
contents = JSON.stringify(contents, null, 2)
143156
}
144157

145-
return Cypress.backend('write:file', fileName, contents, _.pick(options, ['encoding', 'flag']))
146-
.then(({ contents, filePath }) => {
158+
// We clear the default timeout so we can handle
159+
// the timeout ourselves
160+
cy.clearTimeout()
161+
162+
return Cypress.backend('write:file', fileName, contents, _.pick(options, 'encoding', 'flag')).timeout(options.timeout)
163+
.then(({ filePath, contents }) => {
147164
consoleProps['File Path'] = filePath
148165
consoleProps['Contents'] = contents
149166

150167
return null
151-
}).catch(Promise.TimeoutError, () => {
152-
return $errUtils.throwErrByPath('files.timed_out', {
153-
onFail: options._log,
154-
args: { cmd: 'writeFile', file: fileName, timeout: options.timeout },
155-
})
156168
})
157169
.catch((err) => {
170+
if (err.name === 'TimeoutError') {
171+
return $errUtils.throwErrByPath('files.timed_out', {
172+
onFail: options._log,
173+
args: { cmd: 'writeFile', file: fileName, timeout: options.timeout },
174+
})
175+
}
176+
158177
return $errUtils.throwErrByPath('files.unexpected_error', {
159178
onFail: options._log,
160179
args: { cmd: 'writeFile', action: 'write', file: fileName, filePath: err.filePath, error: err.message },

packages/socket/lib/socket.ts

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
11
import fs from 'fs'
2+
import buffer from 'buffer'
23
import type http from 'http'
34
import server, { Server as SocketIOBaseServer, ServerOptions } from 'socket.io'
45
import { client } from './browser'
56

6-
const HUNDRED_MEGABYTES = 1e8 // 100000000
7-
87
const { version } = require('socket.io-client/package.json')
98
const clientSource = require.resolve('socket.io-client/dist/socket.io.js')
109

@@ -15,13 +14,14 @@ type PatchedServerOptions = ServerOptions & { cookie: { name: string | boolean }
1514

1615
class SocketIOServer extends SocketIOBaseServer {
1716
constructor (srv: http.Server, opts?: Partial<PatchedServerOptions>) {
18-
// in socket.io v3, they reduced down the max buffer size
19-
// from 100mb to 1mb, so we reset it back to the previous value
20-
//
21-
// previous commit for reference:
22-
// https://github.com/socketio/engine.io/blame/61b949259ed966ef6fc8bfd61f14d1a2ef06d319/lib/server.js#L29
2317
opts = opts ?? {}
24-
opts.maxHttpBufferSize = opts.maxHttpBufferSize ?? HUNDRED_MEGABYTES
18+
19+
// the maxHttpBufferSize is used to limit the message size sent over
20+
// the socket. Small values can be used to mitigate exposure to
21+
// denial of service attacks; the default as of v3.0 is 1MB.
22+
// because our server is local, we do not need to arbitrarily limit
23+
// the message size and can use the theoretical maximum value.
24+
opts.maxHttpBufferSize = opts.maxHttpBufferSize ?? buffer.constants.MAX_LENGTH
2525

2626
super(srv, opts)
2727
}

0 commit comments

Comments
 (0)