From 9229b229a794e964c6c87ce41a1237348fc8ec55 Mon Sep 17 00:00:00 2001 From: David Date: Wed, 3 Oct 2018 20:24:10 +0200 Subject: [PATCH] Allow for multiple onReadyStateChange calls (#1195) * Closes #1150 * Closes #2374 * Allow for multiple onReadyStateChange calls by implementing new recursive prevention --- packages/driver/src/cypress/server.coffee | 37 ++++++++----------- .../integration/commands/task_spec.coffee | 2 +- .../integration/commands/xhr_spec.coffee | 21 +++++++++++ packages/driver/test/cypress/plugins/index.js | 12 ++++++ 4 files changed, 49 insertions(+), 23 deletions(-) diff --git a/packages/driver/src/cypress/server.coffee b/packages/driver/src/cypress/server.coffee index 7041be5ea820..45aaa400abd6 100644 --- a/packages/driver/src/cypress/server.coffee +++ b/packages/driver/src/cypress/server.coffee @@ -359,17 +359,20 @@ create = (options = {}) -> xhr = @ fns = {} - called = {} overrides = {} - readyStates = {} - onLoadFn = -> - ## bail if we've already been called to prevent - ## infinite recursion - return if called.onload + bailIfRecursive = (fn) -> + isCalled = false - called.onload = true + return () -> + return if isCalled + isCalled = true + try + return fn.apply(arguments) + finally + isCalled = false + onLoadFn = -> proxy._setDuration(timeStart) proxy._setStatus() proxy._setResponseHeaders() @@ -391,12 +394,6 @@ create = (options = {}) -> options.onAnyResponse(route, proxy) onErrorFn = -> - ## bail if we've already been called to prevent - ## infinite recursion - return if called.onerror - - called.onerror = true - ## its possible our real onerror handler ## throws so we need to catch those errors too try @@ -407,12 +404,6 @@ create = (options = {}) -> options.onError(proxy, err) onReadyStateFn = -> - ## bail if we've already been called with this - ## readyState to prevent infinite recursions - return if readyStates[@readyState] - - readyStates[@readyState] = true - ## catch synchronous errors caused ## by the onreadystatechange function try @@ -423,9 +414,11 @@ create = (options = {}) -> xhr.onreadystatechange = null options.onError(proxy, err) - overrides.onload = onLoadFn - overrides.onerror = onErrorFn - overrides.onreadystatechange = onReadyStateFn + ## bail if eventhandlers have already been called to prevent + ## infinite recursion + overrides.onload = bailIfRecursive(onLoadFn) + overrides.onerror = bailIfRecursive(onErrorFn) + overrides.onreadystatechange = bailIfRecursive(onReadyStateFn) props.forEach (prop) -> ## if we currently have one of these properties then diff --git a/packages/driver/test/cypress/integration/commands/task_spec.coffee b/packages/driver/test/cypress/integration/commands/task_spec.coffee index c597f5e1c831..b69c9a1748ac 100644 --- a/packages/driver/test/cypress/integration/commands/task_spec.coffee +++ b/packages/driver/test/cypress/integration/commands/task_spec.coffee @@ -161,7 +161,7 @@ describe "src/cy/commands/task", -> expect(lastLog.get("error")).to.eq(err) expect(lastLog.get("state")).to.eq("failed") - expect(err.message).to.eq("cy.task('bar') failed with the following error:\n\nThe task 'bar' was not handled in the plugins file. The following tasks are registered: return:arg, wait\n\nFix this in your plugins file here:\n#{Cypress.config('pluginsFile')}\n\nhttps://on.cypress.io/api/task") + expect(err.message).to.eq("cy.task('bar') failed with the following error:\n\nThe task 'bar' was not handled in the plugins file. The following tasks are registered: return:arg, wait, create:long:file\n\nFix this in your plugins file here:\n#{Cypress.config('pluginsFile')}\n\nhttps://on.cypress.io/api/task") done() cy.task("bar") diff --git a/packages/driver/test/cypress/integration/commands/xhr_spec.coffee b/packages/driver/test/cypress/integration/commands/xhr_spec.coffee index 95b80058827c..86bf6192598e 100644 --- a/packages/driver/test/cypress/integration/commands/xhr_spec.coffee +++ b/packages/driver/test/cypress/integration/commands/xhr_spec.coffee @@ -69,6 +69,27 @@ describe "src/cy/commands/xhr", -> expect(onreadystatechanged).to.be.true expect(xhr.status).to.eq(404) + it "allows multiple readystatechange calls", -> + responseText = null + responseStatuses = 0 + + cy + .server() + .route({ url: /longtext.txt/ }).as("getLongText") + .task('create:long:file') + .window().then (win) -> + xhr = new win.XMLHttpRequest() + xhr.onreadystatechange = -> + responseText = xhr.responseText + if xhr.readyState == 3 + responseStatuses++ + xhr.open("GET", "/_test-output/longtext.txt?" + Cypress._.random(0, 1e6)) + xhr.send() + null + .wait("@getLongText").then (xhr) -> + expect(responseStatuses).to.be.gt(1) + expect(xhr.status).to.eq(200) + it "works with jquery too", -> failed = false onloaded = false diff --git a/packages/driver/test/cypress/plugins/index.js b/packages/driver/test/cypress/plugins/index.js index 40bcc91b2826..f053bb92b8e0 100644 --- a/packages/driver/test/cypress/plugins/index.js +++ b/packages/driver/test/cypress/plugins/index.js @@ -1,3 +1,6 @@ +const _ = require('lodash') +const path = require('path') +const fs = require('fs-extra') const Promise = require('bluebird') module.exports = (on) => { @@ -8,5 +11,14 @@ module.exports = (on) => { 'wait' () { return Promise.delay(2000) }, + 'create:long:file' () { + const filePath = path.join(__dirname, '..', '_test-output', 'longtext.txt') + const longText = _.times(2000).map(() => { + return _.times(20).map(() => Math.random()).join(' ') + }).join('\n\n') + + fs.outputFileSync(filePath, longText) + return null + }, }) }