From aa15b73ed5546c024c80f889c82a637f6d01d3f0 Mon Sep 17 00:00:00 2001 From: Lila Conlee Date: Tue, 19 Mar 2019 19:42:41 -0500 Subject: [PATCH 1/4] Update error utils to retrieve object, strings, or fns --- .../driver/src/cypress/error_messages.coffee | 3 +- packages/driver/src/cypress/utils.coffee | 58 ++++++++++++++----- 2 files changed, 47 insertions(+), 14 deletions(-) diff --git a/packages/driver/src/cypress/error_messages.coffee b/packages/driver/src/cypress/error_messages.coffee index 9713158dca03..dcd7347d4526 100644 --- a/packages/driver/src/cypress/error_messages.coffee +++ b/packages/driver/src/cypress/error_messages.coffee @@ -59,7 +59,8 @@ module.exports = { not_registered_without_available: "#{cmd('{{cmd}}')} could not find a registered alias for: '@{{displayName}}'.\nYou have not aliased anything yet." as: - empty_string: "#{cmd('as')} cannot be passed an empty string." + empty_string: + message: "#{cmd('as')} cannot be passed an empty string." invalid_type: "#{cmd('as')} can only accept a string." invalid_first_token: "'{{alias}}' cannot be named starting with the '@' symbol. Try renaming the alias to '{{suggestedName}}', or something else that does not start with the '@' symbol." reserved_word: "#{cmd('as')} cannot be aliased as: '{{alias}}'. This word is reserved." diff --git a/packages/driver/src/cypress/utils.coffee b/packages/driver/src/cypress/utils.coffee index 7e14f7fb35ea..c468743ad0ea 100644 --- a/packages/driver/src/cypress/utils.coffee +++ b/packages/driver/src/cypress/utils.coffee @@ -84,11 +84,14 @@ module.exports = { err2.name = obj.name err2.stack = obj.stack - for own prop, val of obj - if not err2[prop] - err2[prop] = val + return @copyProperties(obj, err2) - return err2 + copyProperties: (originalObj, newObj) -> + for own prop, val of originalObj + if not newObj[prop] + newObj[prop] = val + + return newObj throwErr: (err, options = {}) -> if _.isString(err) @@ -111,7 +114,7 @@ module.exports = { throwErrByPath: (errPath, options = {}) -> err = try - @errMessageByPath errPath, options.args + @errByPath errPath, options.args catch e err = @internalErr e @@ -123,15 +126,17 @@ module.exports = { err cypressErr: (err) -> - err = new Error(err) - err.name = "CypressError" - err + if _.isString(err) + err = new Error(err) + err.name = "CypressError" + err - errMessageByPath: (errPath, args) -> - if not errMessage = @getObjValueByPath($errorMessages, errPath) - throw new Error "Error message path '#{errPath}' does not exist" + newErr = new Error(err.message) + newErr.name = "CypressError" + @copyProperties(err, newErr) - getMsg = -> + formatErrorMessage: (errMessage, args) -> + getMessage = -> if _.isFunction(errMessage) errMessage(args) else @@ -142,12 +147,39 @@ module.exports = { ## normalize two or more new lines ## into only exactly two new lines _ - .chain(getMsg()) + .chain(getMessage()) .split(twoOrMoreNewLinesRe) .compact() .join('\n\n') .value() + getErrorValue: (errPath) -> + if not err = @getObjValueByPath($errorMessages, errPath) + throw new Error "Error path '#{errPath}' does not exist" + + return err + + errByPath: (errPath, args) -> + err = @getErrorValue(errPath) + + if _.isString(err) or _.isFunction(err) + message = @formatErrorMessage(err, args) + return @cypressErr(message) + + if !err.message + throw new Error "Error message path '#{errPath}' does not have a message field" + return @cypressErr(err) + + errMessageByPath: (errPath, args) -> + errMessage = @getErrorValue(errPath) + + if _.isObject(errMessage) and not _.isFunction(errMessage) + if !errMessage.message? + throw new Error "Error message path '#{errPath}' does not have a message field" + errMessage = errMessage.message + + @formatErrorMessage(errMessage, args) + normalizeObjWithLength: (obj) -> ## lodash shits the bed if our object has a 'length' ## property so we have to normalize that From ad06557bed8d6ec4b3e9d75e7bc1051d85fd6f66 Mon Sep 17 00:00:00 2001 From: Lila Conlee Date: Wed, 20 Mar 2019 09:35:21 -0500 Subject: [PATCH 2/4] Update stack traces in snapshots --- .../1_caught_uncaught_hook_errors_spec.coffee.js | 1 + .../__snapshots__/1_commands_outside_of_test_spec.coffee.js | 1 + packages/server/__snapshots__/2_config_spec.coffee.js | 1 + packages/server/__snapshots__/3_issue_173_spec.coffee.js | 1 + packages/server/__snapshots__/4_request_spec.coffee.js | 2 ++ packages/server/__snapshots__/4_return_value_spec.coffee.js | 1 + packages/server/__snapshots__/5_spec_isolation_spec.coffee.js | 2 +- packages/server/__snapshots__/5_stdout_spec.coffee.js | 2 ++ .../server/__snapshots__/5_task_not_registered_spec.coffee.js | 1 + packages/server/__snapshots__/6_task_spec.coffee.js | 2 ++ packages/server/__snapshots__/6_visit_spec.coffee.js | 4 ++++ packages/server/__snapshots__/6_web_security_spec.coffee.js | 3 +++ 12 files changed, 20 insertions(+), 1 deletion(-) diff --git a/packages/server/__snapshots__/1_caught_uncaught_hook_errors_spec.coffee.js b/packages/server/__snapshots__/1_caught_uncaught_hook_errors_spec.coffee.js index ae3b6add2118..ab3926553984 100644 --- a/packages/server/__snapshots__/1_caught_uncaught_hook_errors_spec.coffee.js +++ b/packages/server/__snapshots__/1_caught_uncaught_hook_errors_spec.coffee.js @@ -57,6 +57,7 @@ Because this error occurred during a 'before each' hook we are skipping the rema at stack trace line at stack trace line at stack trace line + at stack trace line 2) s3a "before all" hook for "t8a": Error: s3a before hook failed diff --git a/packages/server/__snapshots__/1_commands_outside_of_test_spec.coffee.js b/packages/server/__snapshots__/1_commands_outside_of_test_spec.coffee.js index 9eabcb080251..dfc9f8b9d14b 100644 --- a/packages/server/__snapshots__/1_commands_outside_of_test_spec.coffee.js +++ b/packages/server/__snapshots__/1_commands_outside_of_test_spec.coffee.js @@ -47,6 +47,7 @@ We dynamically generated a new test to display this failure. at stack trace line at stack trace line at stack trace line + at stack trace line diff --git a/packages/server/__snapshots__/2_config_spec.coffee.js b/packages/server/__snapshots__/2_config_spec.coffee.js index 4fa3114c5629..42d0ea3add47 100644 --- a/packages/server/__snapshots__/2_config_spec.coffee.js +++ b/packages/server/__snapshots__/2_config_spec.coffee.js @@ -105,6 +105,7 @@ exports['e2e config fails 1'] = ` at stack trace line at stack trace line at stack trace line + at stack trace line diff --git a/packages/server/__snapshots__/3_issue_173_spec.coffee.js b/packages/server/__snapshots__/3_issue_173_spec.coffee.js index 5815ca18250d..95fa3b37eca6 100644 --- a/packages/server/__snapshots__/3_issue_173_spec.coffee.js +++ b/packages/server/__snapshots__/3_issue_173_spec.coffee.js @@ -39,6 +39,7 @@ exports['e2e issue 173 failing 1'] = ` at stack trace line at stack trace line at stack trace line + at stack trace line diff --git a/packages/server/__snapshots__/4_request_spec.coffee.js b/packages/server/__snapshots__/4_request_spec.coffee.js index f56e8a37cc71..4485111f10f9 100644 --- a/packages/server/__snapshots__/4_request_spec.coffee.js +++ b/packages/server/__snapshots__/4_request_spec.coffee.js @@ -156,6 +156,7 @@ RequestError: Error: connect ECONNREFUSED 127.0.0.1:16795 at stack trace line at stack trace line at stack trace line + at stack trace line @@ -279,6 +280,7 @@ Body: Service Unavailable at stack trace line at stack trace line at stack trace line + at stack trace line diff --git a/packages/server/__snapshots__/4_return_value_spec.coffee.js b/packages/server/__snapshots__/4_return_value_spec.coffee.js index 9db8da1481dd..29b17e8118e1 100644 --- a/packages/server/__snapshots__/4_return_value_spec.coffee.js +++ b/packages/server/__snapshots__/4_return_value_spec.coffee.js @@ -84,6 +84,7 @@ https://on.cypress.io/returning-value-and-commands-in-custom-command at stack trace line at stack trace line at stack trace line + at stack trace line diff --git a/packages/server/__snapshots__/5_spec_isolation_spec.coffee.js b/packages/server/__snapshots__/5_spec_isolation_spec.coffee.js index 2519db703d25..02bbe6e96012 100644 --- a/packages/server/__snapshots__/5_spec_isolation_spec.coffee.js +++ b/packages/server/__snapshots__/5_spec_isolation_spec.coffee.js @@ -274,7 +274,7 @@ exports['e2e spec_isolation failing 1'] = { ], "state": "failed", "body": "function () {\n return cy.wrap(true, {\n timeout: 100\n }).should(\"be.false\");\n }", - "stack": "CypressError: Timed out retrying: expected true to be false\n at stack trace line\n at stack trace line\n at stack trace line\n at stack trace line\n at stack trace line\n at stack trace line\n at stack trace line\n at stack trace line\n at stack trace line\n at stack trace line\n at stack trace line\n at stack trace line\n at stack trace line\n at stack trace line", + "stack": "CypressError: Timed out retrying: expected true to be false\n at stack trace line\n at stack trace line\n at stack trace line\n at stack trace line\n at stack trace line\n at stack trace line\n at stack trace line\n at stack trace line\n at stack trace line\n at stack trace line\n at stack trace line\n at stack trace line\n at stack trace line\n at stack trace line\n at stack trace line", "error": "Timed out retrying: expected true to be false", "timings": { "lifecycle": 100, diff --git a/packages/server/__snapshots__/5_stdout_spec.coffee.js b/packages/server/__snapshots__/5_stdout_spec.coffee.js index 3cbaab1866ac..80391b52f09d 100644 --- a/packages/server/__snapshots__/5_stdout_spec.coffee.js +++ b/packages/server/__snapshots__/5_stdout_spec.coffee.js @@ -63,6 +63,7 @@ Because this error occurred during a 'before each' hook we are skipping the rema at stack trace line at stack trace line at stack trace line + at stack trace line 3) stdout_failing_spec passing hook is failing: CypressError: cy.visit() failed trying to load: @@ -91,6 +92,7 @@ The internal Cypress web server responded with: at stack trace line at stack trace line at stack trace line + at stack trace line diff --git a/packages/server/__snapshots__/5_task_not_registered_spec.coffee.js b/packages/server/__snapshots__/5_task_not_registered_spec.coffee.js index f9135f634900..3b8ac5116c49 100644 --- a/packages/server/__snapshots__/5_task_not_registered_spec.coffee.js +++ b/packages/server/__snapshots__/5_task_not_registered_spec.coffee.js @@ -44,6 +44,7 @@ https://on.cypress.io/api/task at stack trace line at stack trace line at stack trace line + at stack trace line diff --git a/packages/server/__snapshots__/6_task_spec.coffee.js b/packages/server/__snapshots__/6_task_spec.coffee.js index 87373537a423..7523dea28d5b 100644 --- a/packages/server/__snapshots__/6_task_spec.coffee.js +++ b/packages/server/__snapshots__/6_task_spec.coffee.js @@ -109,6 +109,7 @@ https://on.cypress.io/api/task at stack trace line at stack trace line at stack trace line + at stack trace line 2) includes stack trace in error: CypressError: cy.task('errors') failed with the following error: @@ -145,6 +146,7 @@ https://on.cypress.io/api/task at stack trace line at stack trace line at stack trace line + at stack trace line diff --git a/packages/server/__snapshots__/6_visit_spec.coffee.js b/packages/server/__snapshots__/6_visit_spec.coffee.js index 19661cea2189..0a8f8f94e526 100644 --- a/packages/server/__snapshots__/6_visit_spec.coffee.js +++ b/packages/server/__snapshots__/6_visit_spec.coffee.js @@ -141,6 +141,7 @@ Error: connect ECONNREFUSED 127.0.0.1:16795 at stack trace line at stack trace line at stack trace line + at stack trace line @@ -238,6 +239,7 @@ If you do not want status codes to cause failures pass the option: 'failOnStatus at stack trace line at stack trace line at stack trace line + at stack trace line @@ -335,6 +337,7 @@ The internal Cypress web server responded with: at stack trace line at stack trace line at stack trace line + at stack trace line @@ -434,6 +437,7 @@ cy.request() will automatically get and set cookies and enable you to parse resp at stack trace line at stack trace line at stack trace line + at stack trace line diff --git a/packages/server/__snapshots__/6_web_security_spec.coffee.js b/packages/server/__snapshots__/6_web_security_spec.coffee.js index 925119bbe4c6..0717813952b4 100644 --- a/packages/server/__snapshots__/6_web_security_spec.coffee.js +++ b/packages/server/__snapshots__/6_web_security_spec.coffee.js @@ -57,6 +57,7 @@ https://on.cypress.io/cross-origin-violation at stack trace line at stack trace line at stack trace line + at stack trace line 2) web security fails when submitted a form and being redirected to another origin: CypressError: Cypress detected a cross origin error happened on page load: @@ -89,6 +90,7 @@ https://on.cypress.io/cross-origin-violation at stack trace line at stack trace line at stack trace line + at stack trace line 3) web security fails when using a javascript redirect to another origin: CypressError: Cypress detected a cross origin error happened on page load: @@ -121,6 +123,7 @@ https://on.cypress.io/cross-origin-violation at stack trace line at stack trace line at stack trace line + at stack trace line From 619c403bde79536c36360290fb573b9d7690d311 Mon Sep 17 00:00:00 2001 From: Lila Conlee Date: Mon, 25 Mar 2019 08:56:48 -0700 Subject: [PATCH 3/4] Support error objects in uncaught errors --- packages/driver/package.json | 2 +- packages/driver/src/cy/errors.coffee | 2 +- .../driver/src/cypress/error_messages.coffee | 8 ++++---- packages/driver/src/cypress/utils.coffee | 4 ++++ .../integration/cypress/utils_spec.coffee | 16 ++++++++++++++++ .../integration/e2e/uncaught_errors_spec.coffee | 2 +- 6 files changed, 27 insertions(+), 7 deletions(-) diff --git a/packages/driver/package.json b/packages/driver/package.json index cb6c2179ee6c..5bec48712c36 100644 --- a/packages/driver/package.json +++ b/packages/driver/package.json @@ -45,8 +45,8 @@ "jsdom": "13.2.0", "lodash": "4.17.11", "lolex": "3.1.0", - "methods": "1.1.2", "method-override": "2.3.10", + "methods": "1.1.2", "minimatch": "3.0.4", "minimist": "1.2.0", "mocha": "cypress-io/mocha#58f6eac05e664fc6b69aa9fba70f1f6b5531a900", diff --git a/packages/driver/src/cy/errors.coffee b/packages/driver/src/cy/errors.coffee index beb2d1bcbd21..b56cfa4f0389 100644 --- a/packages/driver/src/cy/errors.coffee +++ b/packages/driver/src/cy/errors.coffee @@ -49,7 +49,7 @@ create = (state, config, log) -> when "app" then "uncaught.fromApp" when "spec" then "uncaught.fromSpec" - err = $utils.appendErrMsg(err, $utils.errMessageByPath(suffixMsg)) + err = $utils.appendErrMsg(err, $utils.errByPath(suffixMsg)) err.onFail = -> if l = current and current.getLastLog() diff --git a/packages/driver/src/cypress/error_messages.coffee b/packages/driver/src/cypress/error_messages.coffee index dcd7347d4526..361754bf7a17 100644 --- a/packages/driver/src/cypress/error_messages.coffee +++ b/packages/driver/src/cypress/error_messages.coffee @@ -822,15 +822,15 @@ module.exports = { msg + if source and lineno then " (#{source}:#{lineno})" else "" - fromApp: """ + fromApp: + message: """ This error originated from your application code, not from Cypress. When Cypress detects uncaught errors originating from your application it will automatically fail the current test. This behavior is configurable, and you can choose to turn this off by listening to the 'uncaught:exception' event. - - https://on.cypress.io/uncaught-exception-from-application - """ + """, + docs: "https://on.cypress.io/uncaught-exception-from-application" fromSpec: """ This error originated from your test code, not from Cypress. diff --git a/packages/driver/src/cypress/utils.coffee b/packages/driver/src/cypress/utils.coffee index c468743ad0ea..6908d97a6f4c 100644 --- a/packages/driver/src/cypress/utils.coffee +++ b/packages/driver/src/cypress/utils.coffee @@ -62,6 +62,10 @@ module.exports = { ## not be evaluated later stack = err.stack + if _.isObject(message) + err = @copyProperties(message, err) + message = message.message + ## preserve message ## and toString msg = err.message diff --git a/packages/driver/test/cypress/integration/cypress/utils_spec.coffee b/packages/driver/test/cypress/integration/cypress/utils_spec.coffee index 030ab01f4647..ca0dc45a6cae 100644 --- a/packages/driver/test/cypress/integration/cypress/utils_spec.coffee +++ b/packages/driver/test/cypress/integration/cypress/utils_spec.coffee @@ -58,3 +58,19 @@ describe "driver/src/cypress/utils", -> expect(err2.message).to.eq("\n\nbar") expect(err2.stack).to.eq("Error: \n\nbar\n" + stack) + + it "handles error messages as objects", -> + err = new Error("foo") + + obj = { + message: "bar", + docs: "baz" + } + + stack = err.stack.split("\n").slice(1).join("\n") + + err2 = $utils.appendErrMsg(err, obj) + expect(err2.message).to.eq("foo\n\nbar") + expect(err2.docs).to.eq("baz") + + expect(err2.stack).to.eq("Error: foo\n\nbar\n" + stack) diff --git a/packages/driver/test/cypress/integration/e2e/uncaught_errors_spec.coffee b/packages/driver/test/cypress/integration/e2e/uncaught_errors_spec.coffee index 0ad5c6cda273..855a8bc9d6db 100644 --- a/packages/driver/test/cypress/integration/e2e/uncaught_errors_spec.coffee +++ b/packages/driver/test/cypress/integration/e2e/uncaught_errors_spec.coffee @@ -38,7 +38,7 @@ describe "uncaught errors", -> expect(err.name).to.eq("Uncaught ReferenceError") expect(err.message).to.include("foo is not defined") expect(err.message).to.include("This error originated from your application code, not from Cypress.") - expect(err.message).to.include("https://on.cypress.io/uncaught-exception-from-application") + expect(err.docs).to.eq("https://on.cypress.io/uncaught-exception-from-application") expect(runnable is r).to.be.true return false From 059abaee33983dcb80ffb76565769862f3580cc5 Mon Sep 17 00:00:00 2001 From: Lila Conlee Date: Mon, 25 Mar 2019 13:36:07 -0700 Subject: [PATCH 4/4] Add code frame util --- packages/driver/package.json | 1 + packages/driver/src/cypress/utils.coffee | 15 +++++++ .../integration/cypress/utils_spec.coffee | 45 +++++++++++++++++++ 3 files changed, 61 insertions(+) diff --git a/packages/driver/package.json b/packages/driver/package.json index 5bec48712c36..58df13df429d 100644 --- a/packages/driver/package.json +++ b/packages/driver/package.json @@ -17,6 +17,7 @@ "lib" ], "devDependencies": { + "@babel/code-frame": "^7.0.0", "@cypress/bower-kendo-ui": "0.0.2", "@cypress/sinon-chai": "1.1.0", "@cypress/underscore.inflection": "1.0.1", diff --git a/packages/driver/src/cypress/utils.coffee b/packages/driver/src/cypress/utils.coffee index 6908d97a6f4c..e53427f3f945 100644 --- a/packages/driver/src/cypress/utils.coffee +++ b/packages/driver/src/cypress/utils.coffee @@ -3,6 +3,7 @@ _ = require("lodash") methods = require("methods") moment = require("moment") Promise = require("bluebird") +codeFrameColumns = require("@babel/code-frame").codeFrameColumns $jquery = require("../dom/jquery") $Location = require("./location") @@ -184,6 +185,20 @@ module.exports = { @formatErrorMessage(errMessage, args) + getCodeFrame: (source, path, lineNumber, columnNumber) -> + location = { start: { line: lineNumber, column: columnNumber } } + options = { + highlightCode: true, + forceColor: true + } + + return { + frame: codeFrameColumns(source, location, options), + path: path, + lineNumber: lineNumber, + columnNumber: columnNumber + } + normalizeObjWithLength: (obj) -> ## lodash shits the bed if our object has a 'length' ## property so we have to normalize that diff --git a/packages/driver/test/cypress/integration/cypress/utils_spec.coffee b/packages/driver/test/cypress/integration/cypress/utils_spec.coffee index ca0dc45a6cae..87d92810066a 100644 --- a/packages/driver/test/cypress/integration/cypress/utils_spec.coffee +++ b/packages/driver/test/cypress/integration/cypress/utils_spec.coffee @@ -74,3 +74,48 @@ describe "driver/src/cypress/utils", -> expect(err2.docs).to.eq("baz") expect(err2.stack).to.eq("Error: foo\n\nbar\n" + stack) + + context ".getCodeFrame", -> + it "returns a code frame with syntax highlighting", -> + path = "foo/bar/baz" + line = 5 + column = 6 + src = """ + + + + + + + """ + + { frame, path, lineNumber, columnNumber } = $utils.getCodeFrame(src, path, line, column) + + expect(frame).to.contain("foo") + expect(frame).to.contain("bar()") + expect(frame).to.contain("[0m") + expect(path).to.eq("foo/bar/baz") + expect(lineNumber).to.eq(5) + expect(columnNumber).to.eq(6) + + ## TODO determine if we want more failure cases covered + it "fails gracefully", -> + path = "foo/bar/baz" + line = 100 + column = 6 + src = """ + + + + + + + """ + + { frame } = $utils.getCodeFrame(src, path, line, column) + + expect(frame).to.eq("")