mirror of
https://github.com/cypress-io/cypress.git
synced 2026-02-21 22:50:49 -06:00
fix: adding timeout option to writeFile command (#19015)
This commit is contained in:
13
cli/types/cypress.d.ts
vendored
13
cli/types/cypress.d.ts
vendored
@@ -2209,12 +2209,9 @@ declare namespace Cypress {
|
||||
* @see https://on.cypress.io/writefile
|
||||
```
|
||||
cy.writeFile('path/to/message.txt', 'Hello World')
|
||||
.then((text) => {
|
||||
expect(text).to.equal('Hello World') // true
|
||||
})
|
||||
```
|
||||
*/
|
||||
writeFile<C extends FileContents>(filePath: string, contents: C, encoding: Encodings): Chainable<C>
|
||||
writeFile(filePath: string, contents: FileContents, encoding: Encodings): Chainable<null>
|
||||
/**
|
||||
* Write to a file with the specified encoding and contents.
|
||||
*
|
||||
@@ -2223,12 +2220,10 @@ declare namespace Cypress {
|
||||
cy.writeFile('path/to/ascii.txt', 'Hello World', {
|
||||
flag: 'a+',
|
||||
encoding: 'ascii'
|
||||
}).then((text) => {
|
||||
expect(text).to.equal('Hello World') // true
|
||||
})
|
||||
```
|
||||
*/
|
||||
writeFile<C extends FileContents>(filePath: string, contents: C, options?: Partial<WriteFileOptions>): Chainable<C>
|
||||
writeFile(filePath: string, contents: FileContents, options?: Partial<WriteFileOptions & Timeoutable>): Chainable<null>
|
||||
/**
|
||||
* Write to a file with the specified encoding and contents.
|
||||
*
|
||||
@@ -2238,12 +2233,10 @@ declare namespace Cypress {
|
||||
```
|
||||
cy.writeFile('path/to/ascii.txt', 'Hello World', 'utf8', {
|
||||
flag: 'a+',
|
||||
}).then((text) => {
|
||||
expect(text).to.equal('Hello World') // true
|
||||
})
|
||||
```
|
||||
*/
|
||||
writeFile<C extends FileContents>(filePath: string, contents: C, encoding: Encodings, options?: Partial<WriteFileOptions>): Chainable<C>
|
||||
writeFile(filePath: string, contents: FileContents, encoding: Encodings, options?: Partial<WriteFileOptions & Timeoutable>): Chainable<null>
|
||||
|
||||
/**
|
||||
* jQuery library bound to the AUT
|
||||
|
||||
@@ -145,6 +145,7 @@ describe('src/cy/commands/files', () => {
|
||||
if (attrs.name === 'readFile') {
|
||||
expect(log.get('state')).to.eq('pending')
|
||||
expect(log.get('message')).to.eq('foo.json')
|
||||
expect(log.get('timeout')).to.eq(Cypress.config('defaultCommandTimeout'))
|
||||
}
|
||||
})
|
||||
|
||||
@@ -321,6 +322,54 @@ describe('src/cy/commands/files', () => {
|
||||
|
||||
cy.readFile('foo.json').should('equal', 'contents')
|
||||
})
|
||||
|
||||
it('throws when the read timeout expires', function (done) {
|
||||
Cypress.backend.callsFake(() => {
|
||||
return new Cypress.Promise(() => { /* Broken promise for timeout */ })
|
||||
})
|
||||
|
||||
cy.on('fail', (err) => {
|
||||
const { lastLog } = this
|
||||
|
||||
assertLogLength(this.logs, 1)
|
||||
expect(lastLog.get('error')).to.eq(err)
|
||||
expect(lastLog.get('state')).to.eq('failed')
|
||||
expect(err.message).to.eq(stripIndent`\
|
||||
\`cy.readFile("foo")\` timed out after waiting \`10ms\`.
|
||||
`)
|
||||
|
||||
expect(err.docsUrl).to.eq('https://on.cypress.io/readfile')
|
||||
|
||||
done()
|
||||
})
|
||||
|
||||
cy.readFile('foo', { timeout: 10 })
|
||||
})
|
||||
|
||||
it('uses defaultCommandTimeout config value if option not provided', {
|
||||
defaultCommandTimeout: 42,
|
||||
}, function (done) {
|
||||
Cypress.backend.callsFake(() => {
|
||||
return new Cypress.Promise(() => { /* Broken promise for timeout */ })
|
||||
})
|
||||
|
||||
cy.on('fail', (err) => {
|
||||
const { lastLog } = this
|
||||
|
||||
assertLogLength(this.logs, 1)
|
||||
expect(lastLog.get('error')).to.eq(err)
|
||||
expect(lastLog.get('state')).to.eq('failed')
|
||||
expect(err.message).to.eq(stripIndent`\
|
||||
\`cy.readFile("foo")\` timed out after waiting \`42ms\`.
|
||||
`)
|
||||
|
||||
expect(err.docsUrl).to.eq('https://on.cypress.io/readfile')
|
||||
|
||||
done()
|
||||
})
|
||||
|
||||
cy.readFile('foo')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -394,7 +443,7 @@ describe('src/cy/commands/files', () => {
|
||||
Cypress.backend.resolves(okResponse)
|
||||
|
||||
cy.writeFile('foo.txt', 'contents').then((subject) => {
|
||||
expect(subject).to.not.exist
|
||||
expect(subject).to.eq(null)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -481,6 +530,7 @@ describe('src/cy/commands/files', () => {
|
||||
if (attrs.name === 'writeFile') {
|
||||
expect(log.get('state')).to.eq('pending')
|
||||
expect(log.get('message')).to.eq('foo.txt', 'contents')
|
||||
expect(log.get('timeout')).to.eq(Cypress.config('defaultCommandTimeout'))
|
||||
}
|
||||
})
|
||||
|
||||
@@ -601,6 +651,54 @@ describe('src/cy/commands/files', () => {
|
||||
|
||||
cy.writeFile('foo.txt', 'contents')
|
||||
})
|
||||
|
||||
it('throws when the write timeout expires', function (done) {
|
||||
Cypress.backend.callsFake(() => {
|
||||
return new Cypress.Promise(() => {})
|
||||
})
|
||||
|
||||
cy.on('fail', (err) => {
|
||||
const { lastLog } = this
|
||||
|
||||
assertLogLength(this.logs, 1)
|
||||
expect(lastLog.get('error')).to.eq(err)
|
||||
expect(lastLog.get('state')).to.eq('failed')
|
||||
expect(err.message).to.eq(stripIndent`
|
||||
\`cy.writeFile("foo.txt")\` timed out after waiting \`10ms\`.
|
||||
`)
|
||||
|
||||
expect(err.docsUrl).to.eq('https://on.cypress.io/writefile')
|
||||
|
||||
done()
|
||||
})
|
||||
|
||||
cy.writeFile('foo.txt', 'contents', { timeout: 10 })
|
||||
})
|
||||
|
||||
it('uses defaultCommandTimeout config value if option not provided', {
|
||||
defaultCommandTimeout: 42,
|
||||
}, function (done) {
|
||||
Cypress.backend.callsFake(() => {
|
||||
return new Cypress.Promise(() => { /* Broken promise for timeout */ })
|
||||
})
|
||||
|
||||
cy.on('fail', (err) => {
|
||||
const { lastLog } = this
|
||||
|
||||
assertLogLength(this.logs, 1)
|
||||
expect(lastLog.get('error')).to.eq(err)
|
||||
expect(lastLog.get('state')).to.eq('failed')
|
||||
expect(err.message).to.eq(stripIndent`
|
||||
\`cy.writeFile("foo.txt")\` timed out after waiting \`42ms\`.
|
||||
`)
|
||||
|
||||
expect(err.docsUrl).to.eq('https://on.cypress.io/writefile')
|
||||
|
||||
done()
|
||||
})
|
||||
|
||||
cy.writeFile('foo.txt', 'contents')
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
// @ts-nocheck
|
||||
import _ from 'lodash'
|
||||
import Promise from 'bluebird'
|
||||
|
||||
import $errUtils from '../../cypress/error_utils'
|
||||
|
||||
@@ -22,6 +21,7 @@ export default (Commands, Cypress, cy) => {
|
||||
// to restore the default node behavior.
|
||||
encoding: encoding === undefined ? 'utf8' : encoding,
|
||||
log: true,
|
||||
timeout: Cypress.config('defaultCommandTimeout'),
|
||||
})
|
||||
|
||||
const consoleProps = {}
|
||||
@@ -43,21 +43,33 @@ export default (Commands, Cypress, cy) => {
|
||||
})
|
||||
}
|
||||
|
||||
// We clear the default timeout so we can handle
|
||||
// the timeout ourselves
|
||||
cy.clearTimeout()
|
||||
|
||||
const verifyAssertions = () => {
|
||||
return Cypress.backend('read:file', file, _.pick(options, 'encoding'))
|
||||
return Cypress.backend('read:file', file, _.pick(options, 'encoding')).timeout(options.timeout)
|
||||
.catch((err) => {
|
||||
if (err.code === 'ENOENT') {
|
||||
return {
|
||||
contents: null,
|
||||
filePath: err.filePath,
|
||||
}
|
||||
if (err.name === 'TimeoutError') {
|
||||
return $errUtils.throwErrByPath('files.timed_out', {
|
||||
onFail: options._log,
|
||||
args: { cmd: 'readFile', file, timeout: options.timeout },
|
||||
})
|
||||
}
|
||||
|
||||
return $errUtils.throwErrByPath('files.unexpected_error', {
|
||||
onFail: options._log,
|
||||
args: { cmd: 'readFile', action: 'read', file, filePath: err.filePath, error: err.message },
|
||||
})
|
||||
}).then(({ contents, filePath }) => {
|
||||
// Non-ENOENT errors are not retried
|
||||
if (err.code !== 'ENOENT') {
|
||||
return $errUtils.throwErrByPath('files.unexpected_error', {
|
||||
onFail: options._log,
|
||||
args: { cmd: 'readFile', action: 'read', file, filePath: err.filePath, error: err.message },
|
||||
})
|
||||
}
|
||||
|
||||
return {
|
||||
contents: null,
|
||||
filePath: err.filePath,
|
||||
}
|
||||
}).then(({ filePath, contents }) => {
|
||||
// https://github.com/cypress-io/cypress/issues/1558
|
||||
// We invoke Buffer.from() in order to transform this from an ArrayBuffer -
|
||||
// which socket.io uses to transfer the file over the websocket - into a
|
||||
@@ -110,6 +122,7 @@ export default (Commands, Cypress, cy) => {
|
||||
encoding: encoding === undefined ? 'utf8' : encoding,
|
||||
flag: userOptions.flag ? userOptions.flag : 'w',
|
||||
log: true,
|
||||
timeout: Cypress.config('defaultCommandTimeout'),
|
||||
})
|
||||
|
||||
const consoleProps = {}
|
||||
@@ -117,7 +130,7 @@ export default (Commands, Cypress, cy) => {
|
||||
if (options.log) {
|
||||
options._log = Cypress.log({
|
||||
message: fileName,
|
||||
timeout: 0,
|
||||
timeout: options.timeout,
|
||||
consoleProps () {
|
||||
return consoleProps
|
||||
},
|
||||
@@ -142,19 +155,25 @@ export default (Commands, Cypress, cy) => {
|
||||
contents = JSON.stringify(contents, null, 2)
|
||||
}
|
||||
|
||||
return Cypress.backend('write:file', fileName, contents, _.pick(options, ['encoding', 'flag']))
|
||||
.then(({ contents, filePath }) => {
|
||||
// We clear the default timeout so we can handle
|
||||
// the timeout ourselves
|
||||
cy.clearTimeout()
|
||||
|
||||
return Cypress.backend('write:file', fileName, contents, _.pick(options, 'encoding', 'flag')).timeout(options.timeout)
|
||||
.then(({ filePath, contents }) => {
|
||||
consoleProps['File Path'] = filePath
|
||||
consoleProps['Contents'] = contents
|
||||
|
||||
return null
|
||||
}).catch(Promise.TimeoutError, () => {
|
||||
return $errUtils.throwErrByPath('files.timed_out', {
|
||||
onFail: options._log,
|
||||
args: { cmd: 'writeFile', file: fileName, timeout: options.timeout },
|
||||
})
|
||||
})
|
||||
.catch((err) => {
|
||||
if (err.name === 'TimeoutError') {
|
||||
return $errUtils.throwErrByPath('files.timed_out', {
|
||||
onFail: options._log,
|
||||
args: { cmd: 'writeFile', file: fileName, timeout: options.timeout },
|
||||
})
|
||||
}
|
||||
|
||||
return $errUtils.throwErrByPath('files.unexpected_error', {
|
||||
onFail: options._log,
|
||||
args: { cmd: 'writeFile', action: 'write', file: fileName, filePath: err.filePath, error: err.message },
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
import fs from 'fs'
|
||||
import buffer from 'buffer'
|
||||
import type http from 'http'
|
||||
import server, { Server as SocketIOBaseServer, ServerOptions } from 'socket.io'
|
||||
import { client } from './browser'
|
||||
|
||||
const HUNDRED_MEGABYTES = 1e8 // 100000000
|
||||
|
||||
const { version } = require('socket.io-client/package.json')
|
||||
const clientSource = require.resolve('socket.io-client/dist/socket.io.js')
|
||||
|
||||
@@ -15,13 +14,14 @@ type PatchedServerOptions = ServerOptions & { cookie: { name: string | boolean }
|
||||
|
||||
class SocketIOServer extends SocketIOBaseServer {
|
||||
constructor (srv: http.Server, opts?: Partial<PatchedServerOptions>) {
|
||||
// in socket.io v3, they reduced down the max buffer size
|
||||
// from 100mb to 1mb, so we reset it back to the previous value
|
||||
//
|
||||
// previous commit for reference:
|
||||
// https://github.com/socketio/engine.io/blame/61b949259ed966ef6fc8bfd61f14d1a2ef06d319/lib/server.js#L29
|
||||
opts = opts ?? {}
|
||||
opts.maxHttpBufferSize = opts.maxHttpBufferSize ?? HUNDRED_MEGABYTES
|
||||
|
||||
// the maxHttpBufferSize is used to limit the message size sent over
|
||||
// the socket. Small values can be used to mitigate exposure to
|
||||
// denial of service attacks; the default as of v3.0 is 1MB.
|
||||
// because our server is local, we do not need to arbitrarily limit
|
||||
// the message size and can use the theoretical maximum value.
|
||||
opts.maxHttpBufferSize = opts.maxHttpBufferSize ?? buffer.constants.MAX_LENGTH
|
||||
|
||||
super(srv, opts)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user