UNPKG

@revoloo/cypress6

Version:

Cypress.io end to end testing tool

1,336 lines (1,094 loc) 74 kB
const _ = require('lodash') const { stripIndent } = require('common-tags') const capitalize = require('underscore.string/capitalize') const { normalizedStack } = require('./stack_utils') const divider = (num, char) => { return Array(num).join(char) } const format = (data) => { if (_.isString(data)) { return _.truncate(data, { length: 2000 }) } if (_.isObject(data)) { return JSON.stringify(data, null, 2) } return data } const formatConfigFile = (configFile) => { if (configFile === false) { return '`cypress.json` (currently disabled by --config-file=false)' } return `\`${format(configFile)}\`` } const formatRedirects = (redirects = [], listIndentSize) => { return _.map(redirects, (redirect) => { return `${' '.repeat(listIndentSize)} - ${redirect}` }) } const formatProp = (memo, field) => { const { key, value } = field if (value != null) { memo.push(`${capitalize(key)}: ${format(value)}`) } return memo } const cmd = (command, args = '') => { const prefix = command.startsWith('Cypress') ? '' : 'cy.' return `\`${prefix}${command}(${args})\`` } const getScreenshotDocsPath = (cmd) => { if (cmd === 'Cypress.Screenshot.defaults') { return 'screenshot-api' } return 'screenshot' } const getRedirects = (obj, phrase, listIndentSize) => { const redirects = obj.redirects ?? [] if (!redirects.length) { return '' } const word = redirects.length > 1 ? 'times' : 'time' const list = formatRedirects(redirects, listIndentSize) return [ `${phrase} '${redirects.length}' ${word} to:`, '', `${list.join('\n')}`, ].join('\n') } const getHttpProps = (fields = []) => { return _ .chain(fields) .reduce(formatProp, []) .join('\n') .value() } // When a function that returns a multi-line string like getHttpProps() // is used inside a template string(``) & stripIndent tag, // the returned string doesn't respect the parent indent. // It prevents `stripIndent` from stripping indent correctly. // To avoid this problem, we need to manually set the indent size to be stripped. // // cyStripIndent strips the indent in a line if it begins with that indent size or bigger. // If not, it ignores the line. const cyStripIndent = (str, indentSize) => { const indent = ' '.repeat(indentSize) return str.split('\n').map((s) => { return s.startsWith(indent) ? s.substring(indentSize) : s }).join('\n') } module.exports = { add: { type_missing: '`Cypress.add(key, fn, type)` must include a type!', }, alias: { invalid: 'Invalid alias: `{{name}}`.\nYou forgot the `@`. It should be written as: `@{{displayName}}`.', not_registered_with_available: `${cmd('{{cmd}}')} could not find a registered alias for: \`@{{displayName}}\`.\nAvailable aliases are: \`{{availableAliases}}\`.`, not_registered_without_available: `${cmd('{{cmd}}')} could not find a registered alias for: \`@{{displayName}}\`.\nYou have not aliased anything yet.`, }, as: { empty_string: { message: `${cmd('as')} cannot be passed an empty string.`, docsUrl: 'https://on.cypress.io/as', }, invalid_type: { message: `${cmd('as')} can only accept a string.`, docsUrl: 'https://on.cypress.io/as', }, invalid_first_token: { message: '`{{alias}}` cannot be named starting with the `@` symbol. Try renaming the alias to `{{suggestedName}}`, or something else that does not start with the `@` symbol.', docsUrl: 'https://on.cypress.io/as', }, reserved_word: { message: `${cmd('as')} cannot be aliased as: \`{{alias}}\`. This word is reserved.`, docsUrl: 'https://on.cypress.io/as', }, }, blur: { multiple_elements: { message: `${cmd('blur')} can only be called on a single element. Your subject contained {{num}} elements.`, docsUrl: 'https://on.cypress.io/blur', }, no_focused_element: { message: `${cmd('blur')} can only be called when there is a currently focused element.`, docsUrl: 'https://on.cypress.io/blur', }, timed_out: { message: `${cmd('blur')} timed out because your browser did not receive any \`blur\` events. This is a known bug in Chrome when it is not the currently focused window.`, docsUrl: 'https://on.cypress.io/blur', }, wrong_focused_element: { message: `${cmd('blur')} can only be called on the focused element. Currently the focused element is a: \`{{node}}\``, docsUrl: 'https://on.cypress.io/blur', }, }, browser: { invalid_arg: '{{prefix}} must be passed a string, object, or an array. You passed: `{{obj}}`', }, chai: { length_invalid_argument: 'You must provide a valid number to a `length` assertion. You passed: `{{length}}`', match_invalid_argument: '`match` requires its argument be a `RegExp`. You passed: `{{regExp}}`', invalid_jquery_obj (obj) { return stripIndent`\ You attempted to make a chai-jQuery assertion on an object that is neither a DOM object or a jQuery object. The chai-jQuery assertion you used was: > ${obj.assertion} The invalid subject you asserted on was: > ${obj.subject} To use chai-jQuery assertions your subject must be valid. This can sometimes happen if a previous assertion changed the subject.` }, }, check_uncheck: { invalid_element: { message: `${cmd('{{cmd}}')} can only be called on \`:checkbox\`{{phrase}}. Your subject {{word}} a: \`{{node}}\``, docsUrl: 'https://on.cypress.io/{{cmd}}', }, }, clear: { invalid_element: { message: stripIndent`\ ${cmd('clear')} failed because it requires a valid clearable element. The element cleared was: > \`{{node}}\` A clearable element matches one of the following selectors: 'a[href]' 'area[href]' 'input' 'select' 'textarea' 'button' 'iframe' '[tabindex]' '[contenteditable]'`, docsUrl: 'https://on.cypress.io/clear', }, }, clearCookie: { invalid_argument: { message: `${cmd('clearCookie')} must be passed a string argument for name.`, docsUrl: 'https://on.cypress.io/clearcookie', }, }, clearLocalStorage: { invalid_argument: { message: `${cmd('clearLocalStorage')} must be called with either a string or regular expression.`, docsUrl: 'https://on.cypress.io/clearlocalstorage', }, }, click: { multiple_elements: { message: `${cmd('{{cmd}}')} can only be called on a single element. Your subject contained {{num}} elements. Pass \`{ multiple: true }\` if you want to serially click each element.`, docsUrl: 'https://on.cypress.io/click', }, on_select_element: { message: `${cmd('{{cmd}}')} cannot be called on a \`<select>\` element. Use ${cmd('select')} command instead to change the value.`, docsUrl: 'https://on.cypress.io/select', }, }, clock: { invalid_1st_arg: { message: `${cmd('clock')} only accepts a number or an \`options\` object for its first argument. You passed: \`{{arg}}\``, docsUrl: 'https://on.cypress.io/clock', }, invalid_2nd_arg: { message: `${cmd('clock')} only accepts an array of function names or an \`options\` object for its second argument. You passed: \`{{arg}}\``, docsUrl: 'https://on.cypress.io/clock', }, }, contains: { empty_string: { message: `${cmd('contains')} cannot be passed an empty string.`, docsUrl: 'https://on.cypress.io/contains', }, invalid_argument: { message: `${cmd('contains')} can only accept a string, number or regular expression.`, docsUrl: 'https://on.cypress.io/contains', }, length_option: { message: `${cmd('contains')} cannot be passed a \`length\` option because it will only ever return 1 element.`, docsUrl: 'https://on.cypress.io/contains', }, regex_conflict: { message: `You passed a regular expression with the case-insensitive (_i_) flag and \`{ matchCase: true }\` to ${cmd('contains')}. Those options conflict with each other, so please choose one or the other.`, docsUrl: 'https://on.cypress.io/contains', }, }, cookies: { backend_error (obj) { return { message: [ `${cmd('{{cmd}}')} had an unexpected error {{action}} {{browserDisplayName}}.`, ``, `> {{error}}`, ``, ].join('\n'), docsUrl: `https://on.cypress.io/${_.toLower(obj.cmd)}`, } }, invalid_name (obj) { return { message: `${cmd('{{cmd}}')} must be passed an RFC-6265-compliant cookie name. You passed:\n\n\`{{name}}\``, docsUrl: `https://on.cypress.io/${_.toLower(obj.cmd)}`, } }, timed_out (obj) { return { message: `${cmd('{{cmd}}')} timed out waiting \`{{timeout}}ms\` to complete.`, docsUrl: `https://on.cypress.io/${_.toLower(obj.cmd)}`, } }, whitelist_renamed (obj) { return { message: `\`${obj.type}\` \`whitelist\` option has been renamed to \`preserve\`. Please rename \`whitelist\` to \`preserve\`.`, } }, }, dom: { animating: { message: stripIndent`\ ${cmd('{{cmd}}')} could not be issued because this element is currently animating: \`{{node}}\` You can fix this problem by: - Passing \`{force: true}\` which disables all error checking - Passing \`{waitForAnimations: false}\` which disables waiting on animations - Passing \`{animationDistanceThreshold: 20}\` which decreases the sensitivity`, docsUrl: 'https://on.cypress.io/element-is-animating', }, animation_coords_history_invalid: 'coordsHistory must be at least 2 sets of coords', animation_check_failed: 'Not enough coord points provided to calculate distance.', center_hidden: { message: stripIndent`\ ${cmd('{{cmd}}')} failed because the center of this element is hidden from view: \`{{node}}\` Fix this problem, or use \`{force: true}\` to disable error checking.`, docsUrl: 'https://on.cypress.io/element-cannot-be-interacted-with', }, covered: { message: stripIndent`\ ${cmd('{{cmd}}')} failed because this element: \`{{element1}}\` is being covered by another element: \`{{element2}}\` Fix this problem, or use {force: true} to disable error checking.`, docsUrl: 'https://on.cypress.io/element-cannot-be-interacted-with', }, pointer_events_none (obj) { return { message: stripIndent`\ ${cmd(obj.cmd)} failed because this element: \`${obj.element}\` has CSS \`pointer-events: none\`${obj.elementInherited ? `, inherited from this element:\n\n\`${obj.elementInherited}\`\n` : ''} \`pointer-events: none\` prevents user mouse interaction. Fix this problem, or use {force: true} to disable error checking.`, docsUrl: 'https://on.cypress.io/element-cannot-be-interacted-with', } }, disabled: { message: stripIndent`\ ${cmd('{{cmd}}')} failed because this element is \`disabled\`: \`{{node}}\` Fix this problem, or use \`{force: true}\` to disable error checking.`, docsUrl: 'https://on.cypress.io/element-cannot-be-interacted-with', }, invalid_position_argument: { message: 'Invalid position argument: `{{position}}`. Position may only be {{validPositions}}.', docsUrl: 'https://on.cypress.io/element-cannot-be-interacted-with', }, not_scrollable: { message: stripIndent`\ ${cmd('{{cmd}}')} failed because this element is not scrollable: \`{{node}}\` Make sure you're targeting the correct element or use \`{ensureScrollable: false}\` to disable the scrollable check.`, docsUrl: 'https://on.cypress.io/scrollto', }, not_visible: { message: stripIndent`\ ${cmd('{{cmd}}')} failed because this element is not visible: \`{{node}}\` {{reason}} Fix this problem, or use \`{force: true}\` to disable error checking.`, docsUrl: 'https://on.cypress.io/element-cannot-be-interacted-with', }, readonly: { message: stripIndent`\ ${cmd('{{cmd}}')} failed because this element is readonly: \`{{node}}\` Fix this problem, or use \`{force: true}\` to disable error checking.`, docsUrl: 'https://on.cypress.io/element-cannot-be-interacted-with', }, }, each: { invalid_argument: { message: `${cmd('each')} must be passed a callback function.`, docsUrl: 'https://on.cypress.io/each', }, non_array: { message: `${cmd('each')} can only operate on an array like subject. Your subject was: \`{{subject}}\``, docsUrl: 'https://on.cypress.io/each', }, }, exec: { failed: { message: stripIndent` ${cmd('exec', '\'{{cmd}}\'')} failed with the following error: > "{{error}}"`, docsUrl: 'https://on.cypress.io/exec', }, invalid_argument: { message: `${cmd('exec')} must be passed a non-empty string as its 1st argument. You passed: '{{cmd}}'.`, docsUrl: 'https://on.cypress.io/exec', }, non_zero_exit: { message: stripIndent`\ ${cmd('exec', '\'{{cmd}}\'')} failed because the command exited with a non-zero code. Pass \`{failOnNonZeroExit: false}\` to ignore exit code failures. Information about the failure: Code: {{code}} {{output}}`, docsUrl: 'https://on.cypress.io/exec', }, timed_out: { message: `${cmd('exec', '\'{{cmd}}\'')} timed out after waiting \`{{timeout}}ms\`.`, docsUrl: 'https://on.cypress.io/exec', }, }, files: { unexpected_error (obj) { return { message: stripIndent` ${cmd('{{cmd}}', '"{{file}}"')} failed while trying to {{action}} the file at the following path: \`{{filePath}}\` The following error occurred: > "{{error}}"`, docsUrl: `https://on.cypress.io/${_.toLower(obj.cmd)}`, } }, existent: { message: stripIndent` ${cmd('readFile', '"{{file}}"')} failed because the file exists when expected not to exist at the following path: \`{{filePath}}\``, docsUrl: 'https://on.cypress.io/readfile', }, invalid_argument (obj) { return { message: `${cmd('{{cmd}}')} must be passed a non-empty string as its 1st argument. You passed: \`{{file}}\`.`, docsUrl: `https://on.cypress.io/${_.toLower(obj.cmd)}`, } }, invalid_contents: { message: `${cmd('writeFile')} must be passed a non-empty string, an object, or an array as its 2nd argument. You passed: \`{{contents}}\`.`, docsUrl: 'https://on.cypress.io/writefile', }, nonexistent: { message: stripIndent` ${cmd('readFile', '"{{file}}"')} failed because the file does not exist at the following path: \`{{filePath}}\``, docsUrl: 'https://on.cypress.io/readfile', }, timed_out (obj) { return { message: `${cmd('{{cmd}}', '"{{file}}"')} timed out after waiting \`{{timeout}}ms\`.`, docsUrl: `https://on.cypress.io/${_.toLower(obj.cmd)}`, } }, }, fixture: { set_to_false: { message: `${cmd('fixture')} is not valid because you have configured \`fixturesFolder\` to \`false\`.`, docsUrl: 'https://on.cypress.io/fixture', }, timed_out: { message: `${cmd('fixture')} timed out waiting \`{{timeout}}ms\` to receive a fixture. No fixture was ever sent by the server.`, docsUrl: 'https://on.cypress.io/fixture', }, }, focus: { invalid_element: { message: `${cmd('focus')} can only be called on a valid focusable element. Your subject is a: \`{{node}}\``, docsUrl: 'https://on.cypress.io/focus', }, multiple_elements: { message: `${cmd('focus')} can only be called on a single element. Your subject contained {{num}} elements.`, docsUrl: 'https://on.cypress.io/focus', }, timed_out: { message: `${cmd('focus')} timed out because your browser did not receive any \`focus\` events. This is a known bug in Chrome when it is not the currently focused window.`, docsUrl: 'https://on.cypress.io/focus', }, }, get: { alias_invalid: { message: '`{{prop}}` is not a valid alias property. Only `numbers` or `all` is permitted.', docsUrl: 'https://on.cypress.io/get', }, alias_zero: { message: '`0` is not a valid alias property. Are you trying to ask for the first response? If so write `@{{alias}}.1`', docsUrl: 'https://on.cypress.io/get', }, invalid_options: { message: `${cmd('get')} only accepts an options object for its second argument. You passed {{options}}`, docsUrl: 'https://on.cypress.io/get', }, }, getCookie: { invalid_argument: { message: `${cmd('getCookie')} must be passed a string argument for name.`, docsUrl: 'https://on.cypress.io/getcookie', }, }, go: { invalid_argument: { message: `${cmd('go')} accepts only a string or number argument`, docsUrl: 'https://on.cypress.io/go', }, invalid_direction: { message: `${cmd('go')} accepts either \`forward\` or \`back\`. You passed: \`{{str}}\``, docsUrl: 'https://on.cypress.io/go', }, invalid_number: { message: `${cmd('go')} cannot accept \`0\`. The number must be greater or less than \`0\`.`, docsUrl: 'https://on.cypress.io/go', }, }, hover: { not_implemented: { message: [ `${cmd('hover')} is not currently implemented.`, 'However it is usually easy to workaround.', 'Read the following document for a detailed explanation.', ].join('\n'), docsUrl: 'https://on.cypress.io/hover', }, }, invoke: { prop_not_a_function: { message: stripIndent`\ ${cmd('invoke')} errored because the property: \`{{prop}}\` returned a \`{{type}}\` value instead of a function. ${cmd('invoke')} can only be used on properties that return callable functions. ${cmd('invoke')} waited for the specified property \`{{prop}}\` to return a function, but it never did. If you want to assert on the property's value, then switch to use ${cmd('its')} and add an assertion such as: \`cy.wrap({ foo: 'bar' }).its('foo').should('eq', 'bar')\``, docsUrl: 'https://on.cypress.io/invoke', }, subject_null_or_undefined: { message: stripIndent`\ ${cmd('invoke')} errored because your subject is: \`{{value}}\`. You cannot invoke any functions such as \`{{prop}}\` on a \`{{value}}\` value. If you expect your subject to be \`{{value}}\`, then add an assertion such as: \`cy.wrap({{value}}).should('be.{{value}}')\``, docsUrl: 'https://on.cypress.io/invoke', }, null_or_undefined_prop_value: { message: stripIndent`\ ${cmd('invoke')} errored because the property: \`{{prop}}\` is not a function, and instead returned a \`{{value}}\` value. ${cmd('invoke')} waited for the specified property \`{{prop}}\` to become a callable function, but it never did. If you expect the property \`{{prop}}\` to be \`{{value}}\`, then switch to use ${cmd('its')} and add an assertion such as: \`cy.wrap({ foo: {{value}} }).its('foo').should('be.{{value}}')\``, docsUrl: 'https://on.cypress.io/invoke', }, }, its: { subject_null_or_undefined: { message: stripIndent`\ ${cmd('its')} errored because your subject is: \`{{value}}\`. You cannot access any properties such as \`{{prop}}\` on a \`{{value}}\` value. If you expect your subject to be \`{{value}}\`, then add an assertion such as: \`cy.wrap({{value}}).should('be.{{value}}')\``, docsUrl: 'https://on.cypress.io/its', }, null_or_undefined_prop_value: { message: stripIndent`\ ${cmd('its')} errored because the property: \`{{prop}}\` returned a \`{{value}}\` value. ${cmd('its')} waited for the specified property \`{{prop}}\` to become accessible, but it never did. If you expect the property \`{{prop}}\` to be \`{{value}}\`, then add an assertion such as: \`cy.wrap({ foo: {{value}} }).its('foo').should('be.{{value}}')\``, docsUrl: 'https://on.cypress.io/its', }, }, invoke_its: { nonexistent_prop: { message: stripIndent`\ ${cmd('{{cmd}}')} errored because the property: \`{{prop}}\` does not exist on your subject. ${cmd('{{cmd}}')} waited for the specified property \`{{prop}}\` to exist, but it never did. If you do not expect the property \`{{prop}}\` to exist, then add an assertion such as: \`cy.wrap({ foo: 'bar' }).its('quux').should('not.exist')\``, docsUrl: 'https://on.cypress.io/{{cmd}}', }, previous_prop_null_or_undefined: { message: stripIndent`\ ${cmd('{{cmd}}')} errored because the property: \`{{previousProp}}\` returned a \`{{value}}\` value. The property: \`{{prop}}\` does not exist on a \`{{value}}\` value. ${cmd('{{cmd}}')} waited for the specified property \`{{prop}}\` to become accessible, but it never did. If you do not expect the property \`{{prop}}\` to exist, then add an assertion such as: \`cy.wrap({ foo: {{value}} }).its('foo.baz').should('not.exist')\``, docsUrl: 'https://on.cypress.io/{{cmd}}', }, invalid_1st_arg: { message: `${cmd('{{cmd}}')} only accepts a string as the first argument.`, docsUrl: 'https://on.cypress.io/{{cmd}}', }, invalid_num_of_args: { message: stripIndent`\ ${cmd('{{cmd}}')} does not accept additional arguments. If you want to invoke a function with arguments, use \`.invoke()\`.`, docsUrl: 'https://on.cypress.io/{{cmd}}', }, invalid_options_arg: { message: `${cmd('{{cmd}}')} only accepts an object as the options argument.`, docsUrl: 'https://on.cypress.io/{{cmd}}', }, invalid_prop_name_arg: { message: `${cmd('{{cmd}}')} only accepts a string or a number as the {{identifier}}Name argument.`, docsUrl: 'https://on.cypress.io/{{cmd}}', }, null_or_undefined_property_name: { message: `${cmd('{{cmd}}')} expects the {{identifier}}Name argument to have a value.`, docsUrl: 'https://on.cypress.io/{{cmd}}', }, timed_out: { message: stripIndent`\ ${cmd('{{cmd}}')} timed out after waiting \`{{timeout}}ms\`. Your callback function returned a promise that never resolved. The callback function was: {{func}}`, docsUrl: 'https://on.cypress.io/{{cmd}}', }, }, location: { invalid_key: { message: 'Location object does not have key: `{{key}}`', docsUrl: 'https://on.cypress.io/location', }, }, log: { invalid_argument: { message: '`Cypress.log()` can only be called with an options object. Your argument was: `{{arg}}`', docsUrl: 'https://on.cypress.io/cypress-log', }, }, miscellaneous: { returned_value_and_commands_from_custom_command (obj) { return { message: stripIndent`\ Cypress detected that you invoked one or more cy commands in a custom command but returned a different value. The custom command was: > ${cmd(obj.current)} The return value was: > {{returned}} Because cy commands are asynchronous and are queued to be run later, it doesn't make sense to return anything else. For convenience, you can also simply omit any return value or return \`undefined\` and Cypress will not error. In previous versions of Cypress we automatically detected this and forced the cy commands to be returned. To make things less magical and clearer, we are now throwing an error.` // since obj.returned can be multi-lined, it can mess up stripIndent, so we replace it after .replace('{{returned}}', obj.returned), docsUrl: 'https://on.cypress.io/returning-value-and-commands-in-custom-command', } }, returned_value_and_commands (obj) { return { message: stripIndent`\ Cypress detected that you invoked one or more cy commands but returned a different value. The return value was: > {{returned}} Because cy commands are asynchronous and are queued to be run later, it doesn't make sense to return anything else. For convenience, you can also simply omit any return value or return \`undefined\` and Cypress will not error. In previous versions of Cypress we automatically detected this and forced the cy commands to be returned. To make things less magical and clearer, we are now throwing an error.` // since obj.returned can be multi-lined, it can mess up stripIndent, so we replace it after .replace('{{returned}}', obj.returned), docsUrl: 'https://on.cypress.io/returning-value-and-commands-in-test', } }, command_returned_promise_and_commands (obj) { return { message: stripIndent`\ Cypress detected that you returned a promise from a command while also invoking one or more cy commands in that promise. The command that returned the promise was: > ${cmd(obj.current)} The cy command you invoked inside the promise was: > ${cmd(obj.called)} Because Cypress commands are already promise-like, you don't need to wrap them or return your own promise. Cypress will resolve your command with whatever the final Cypress command yields. The reason this is an error instead of a warning is because Cypress internally queues commands serially whereas Promises execute as soon as they are invoked. Attempting to reconcile this would prevent Cypress from ever resolving.`, docsUrl: 'https://on.cypress.io/returning-promise-and-commands-in-another-command', } }, mixing_promises_and_commands (obj) { return { message: stripIndent`\ Cypress detected that you returned a promise in a test, but also invoked one or more cy commands inside of that promise. The test title was: > ${obj.title} While this works in practice, it's often indicative of an anti-pattern. You almost never need to return both a promise and also invoke cy commands. Cy commands themselves are already promise like, and you can likely avoid the use of the separate Promise.`, docsUrl: 'https://on.cypress.io/returning-promise-and-commands-in-test', } }, dangling_commands: { message: stripIndent`\ Oops, Cypress detected something wrong with your test code. The test has finished but Cypress still has commands in its queue. The {{numCommands}} queued commands that have not yet run are: {{commands}} In every situation we've seen, this has been caused by programmer error. Most often this indicates a race condition due to a forgotten 'return' or from commands in a previously run test bleeding into the current test. For a much more thorough explanation including examples please review this error here:`, docsUrl: 'https://on.cypress.io/command-queue-ended-early', }, invalid_command: { message: 'Could not find a command for: `{{name}}`.\n\nAvailable commands are: {{cmds}}.\n', docsUrl: 'https://on.cypress.io/api', }, invalid_overwrite: { message: 'Cannot overwite command for: `{{name}}`. An existing command does not exist by that name.', docsUrl: 'https://on.cypress.io/api', }, invoking_child_without_parent (obj) { return stripIndent`\ Oops, it looks like you are trying to call a child command before running a parent command. You wrote code that looks like this: \`${cmd(obj.cmd, obj.args)}\` A child command must be chained after a parent because it operates on a previous subject. For example - if we were issuing the child command \`click\`... cy .get('button') // parent command must come first .click() // then child command comes second` }, no_cy: '`Cypress.cy` is `undefined`. You may be trying to query outside of a running test. Cannot call `Cypress.$()`', no_runner: 'Cannot call `Cypress#run` without a runner instance.', outside_test: { message: stripIndent`\ Cypress cannot execute commands outside a running test. This usually happens when you accidentally write commands outside an \`it(...)\` test. If that is the case, just move these commands inside an \`it(...)\` test. Check your test file for errors.`, docsUrl: 'https://on.cypress.io/cannot-execute-commands-outside-test', }, outside_test_with_cmd: { message: stripIndent`\ Cannot call ${cmd('{{cmd}}')} outside a running test. This usually happens when you accidentally write commands outside an \`it(...)\` test. If that is the case, just move these commands inside an \`it(...)\` test. Check your test file for errors.`, docsUrl: 'https://on.cypress.io/cannot-execute-commands-outside-test', }, private_custom_command_interface: 'You cannot use the undocumented private command interface: `{{method}}`', retry_timed_out ({ ms }) { return `Timed out retrying after ${ms}ms: ` }, }, mocha: { async_timed_out: 'Timed out after `{{ms}}ms`. The `done()` callback was never invoked!', invalid_interface: 'Invalid mocha interface `{{name}}`', timed_out: 'Cypress command timeout of `{{ms}}ms` exceeded.', overspecified: { message: stripIndent`\ Cypress detected that you returned a promise in a test, but also invoked a done callback. Return a promise -or- invoke a done callback, not both. Original mocha error: {{error}}`, docsUrl: 'https://on.cypress.io/returning-promise-and-invoking-done-callback', }, manually_set_retries_test: stripIndent`\ Mocha \`this.retries()\` syntax is not supported. To configure retries use the following syntax: \`\`\` it('{{title}}', { retries: {{numRetries}} }, () => { ... }) \`\`\` https://on.cypress.io/test-retries `, manually_set_retries_suite: stripIndent`\ Mocha \`this.retries()\` syntax is not supported. To configure retries use the following syntax: \`\`\` describe('{{title}}', { retries: {{numRetries}} }, () => { ... }) \`\`\` https://on.cypress.io/test-retries `, hook_registered_late: stripIndent`\ Cypress detected you registered a(n) \`{{hookTitle}}\` hook while a test was running (possibly a hook nested inside another hook). All hooks must be registered before a test begins executing. Move the \`{{hookTitle}}\` into a suite callback or the global scope. `, }, moment: { deprecated: `\`Cypress.moment\` has been deprecated and will be removed in a future release. Consider migrating to a different datetime formatter.`, }, navigation: { cross_origin ({ message, originPolicy, configFile }) { return { message: stripIndent`\ Cypress detected a cross origin error happened on page load: > ${message} Before the page load, you were bound to the origin policy: > ${originPolicy} A cross origin error happens when your application navigates to a new URL which does not match the origin policy above. A new URL does not match the origin policy if the 'protocol', 'port' (if specified), and/or 'host' (unless of the same superdomain) are different. Cypress does not allow you to navigate to a different origin URL within a single test. You may need to restructure some of your test code to avoid this problem. Alternatively you can also disable Chrome Web Security in Chromium-based browsers which will turn off this restriction by setting { chromeWebSecurity: false } in ${formatConfigFile(configFile)}.`, docsUrl: 'https://on.cypress.io/cross-origin-violation', } }, timed_out ({ ms, configFile }) { return stripIndent`\ Timed out after waiting \`${ms}ms\` for your remote page to load. Your page did not fire its \`load\` event within \`${ms}ms\`. You can try increasing the \`pageLoadTimeout\` value in ${formatConfigFile(configFile)} to wait longer. Browsers will not fire the \`load\` event until all stylesheets and scripts are done downloading. When this \`load\` event occurs, Cypress will continue running commands.` }, }, net_stubbing: { route2_renamed: `${cmd('route2')} was renamed to ${cmd('intercept')} and will be removed in a future release. Please update usages of ${cmd('route2')} to use ${cmd('intercept')} instead.`, invalid_static_response: ({ cmd, message, staticResponse }) => { return cyStripIndent(`\ An invalid StaticResponse was supplied to \`${cmd}()\`. ${message} You passed: ${format(staticResponse)}`, 8) }, intercept: { invalid_handler: ({ handler }) => { return stripIndent`\ ${cmd('intercept')}'s \`handler\` argument must be a String, StaticResponse, or HttpController function. You passed: ${format(handler)}` }, invalid_route_matcher: ({ message, matcher }) => { return stripIndent`\ An invalid RouteMatcher was supplied to ${cmd('intercept')}. ${message} You passed: ${format(matcher)}` }, }, request_handling: { cb_failed: ({ err, req, route }) => { return cyStripIndent(`\ A request callback passed to ${cmd('intercept')} threw an error while intercepting a request: ${err.message} Route: ${format(route)} Intercepted request: ${format(req)}`, 10) }, cb_timeout: ({ timeout, req, route }) => { return cyStripIndent(`\ A request callback passed to ${cmd('intercept')} timed out after returning a Promise that took more than the \`defaultCommandTimeout\` of \`${timeout}ms\` to resolve. If the request callback is expected to take longer than \`${timeout}ms\`, increase the configured \`defaultCommandTimeout\` value. Route: ${format(route)} Intercepted request: ${format(req)}`, 10) }, multiple_reply_calls: `\`req.reply()\` was called multiple times in a request handler, but a request can only be replied to once.`, reply_called_after_resolved: `\`req.reply()\` was called after the request handler finished executing, but \`req.reply()\` can not be called after the request has been passed on.`, }, request_error: { network_error: ({ innerErr, req, route }) => { return cyStripIndent(`\ \`req.reply()\` was provided a callback to intercept the upstream response, but a network error occurred while making the request: ${normalizedStack(innerErr)} Route: ${format(route)} Intercepted request: ${format(req)}`, 10) }, timeout: ({ innerErr, req, route }) => { return cyStripIndent(`\ \`req.reply()\` was provided a callback to intercept the upstream response, but the request timed out after the \`responseTimeout\` of \`${req.responseTimeout}ms\`. ${normalizedStack(innerErr)} Route: ${format(route)} Intercepted request: ${format(req)}`, 10) }, }, response_handling: { cb_failed: ({ err, req, res, route }) => { return cyStripIndent(`\ A response callback passed to \`req.reply()\` threw an error while intercepting a response: ${err.message} Route: ${format(route)} Intercepted request: ${format(req)} Intercepted response: ${format(res)}`, 10) }, cb_timeout: ({ timeout, req, res, route }) => { return cyStripIndent(`\ A response callback passed to \`req.reply()\` timed out after returning a Promise that took more than the \`defaultCommandTimeout\` of \`${timeout}ms\` to resolve. If the response callback is expected to take longer than \`${timeout}ms\`, increase the configured \`defaultCommandTimeout\` value. Route: ${format(route)} Intercepted request: ${format(req)} Intercepted response: ${format(res)}`, 10) }, multiple_send_calls: ({ res }) => { return cyStripIndent(`\ \`res.send()\` was called multiple times in a response handler, but the response can only be sent once. Response: ${format(res)}`, 10) }, send_called_after_resolved: ({ res }) => { return cyStripIndent(`\ \`res.send()\` was called after the response handler finished executing, but \`res.send()\` can not be called after the response has been passed on. Intercepted response: ${format(res)}`, 10) }, }, }, ng: { no_global: `Angular global (\`window.angular\`) was not found in your window. You cannot use ${cmd('ng')} methods without angular.`, }, proxy: { js_rewriting_failed: stripIndent`\ An error occurred in the Cypress proxy layer while rewriting your source code. This is a bug in Cypress. Open an issue if you see this message. JS URL: {{url}} Original error: {{errMessage}} {{errStack}}`, }, reload: { invalid_arguments: { message: `${cmd('reload')} can only accept a boolean or \`options\` as its arguments.`, docsUrl: 'https://on.cypress.io/reload', }, }, request: { body_circular ({ path }) { return { message: stripIndent`\ The \`body\` parameter supplied to ${cmd('request')} contained a circular reference at the path "${path.join('.')}". \`body\` can only be a string or an object with no circular references.`, docsUrl: 'https://on.cypress.io/request', } }, status_code_flags_invalid: { message: stripIndent`\ ${cmd('request')} was invoked with \`{ failOnStatusCode: false, retryOnStatusCodeFailure: true }\`. These options are incompatible with each other. - To retry on non-2xx status codes, pass \`{ failOnStatusCode: true, retryOnStatusCodeFailure: true }\`. - To not retry on non-2xx status codes, pass \`{ failOnStatusCode: true, retryOnStatusCodeFailure: true }\`. - To fail on non-2xx status codes without retrying (the default behavior), pass \`{ failOnStatusCode: true, retryOnStatusCodeFailure: false }\``, docsUrl: 'https://on.cypress.io/request', }, auth_invalid: { message: `${cmd('request')} must be passed an object literal for the \`auth\` option.`, docsUrl: 'https://on.cypress.io/request', }, encoding_invalid: { message: `${cmd('request')} was called with invalid encoding: \`{{encoding}}\`. Encoding can be: \`utf8\`, \`utf16le\`, \`latin1\`, \`base64\`, \`hex\`, \`ascii\`, \`binary\`, \`latin1\`, \`ucs2\`, \`utf16le\`, or any other encoding supported by Node\'s Buffer encoding.`, docsUrl: 'https://on.cypress.io/request', }, gzip_invalid: { message: `${cmd('request')} requires the \`gzip\` option to be a boolean.`, docsUrl: 'https://on.cypress.io/request', }, headers_invalid: { message: `${cmd('request')} requires the \`headers\` option to be an object literal.`, docsUrl: 'https://on.cypress.io/request', }, invalid_method: { message: `${cmd('request')} was called with an invalid method: \`{{method}}\`. Method can be: \`GET\`, \`POST\`, \`PUT\`, \`DELETE\`, \`PATCH\`, \`HEAD\`, \`OPTIONS\`, or any other method supported by Node's HTTP parser.`, docsUrl: 'https://on.cypress.io/request', }, form_invalid: { message: stripIndent`\ ${cmd('request')} requires the \`form\` option to be a boolean. If you're trying to send a \`x-www-form-urlencoded\` request then pass either a string or object literal to the \`body\` property.`, docsUrl: 'https://on.cypress.io/request', }, /* eslint-disable indent */ loading_failed (obj) { return { message: cyStripIndent(`\ ${cmd('request')} failed trying to load: ${obj.url} We attempted to make an http request to this URL but the request failed without a response. We received this error at the network level: > ${obj.error} ${divider(60, '-')} The request we sent was: ${getHttpProps([ { key: 'method', value: obj.method }, { key: 'URL', value: obj.url }, ])} ${divider(60, '-')} Common situations why this would fail: - you don't have internet access - you forgot to run / boot your web server - your web server isn't accessible - you have weird network configuration settings on your computer`, 10), docsUrl: 'https://on.cypress.io/request', } }, status_invalid (obj) { return { message: cyStripIndent(`\ ${cmd('request')} failed on: ${obj.url} The response we received from your web server was: > ${obj.status}: ${obj.statusText} This was considered a failure because the status code was not \`2xx\` or \`3xx\`. If you do not want status codes to cause failures pass the option: \`failOnStatusCode: false\` ${divider(60, '-')} The request we sent was: ${getHttpProps([ { key: 'method', value: obj.method }, { key: 'URL', value: obj.url }, { key: 'headers', value: obj.requestHeaders }, { key: 'body', value: obj.requestBody }, { key: 'redirects', value: obj.redirects }, ])} ${divider(60, '-')} The response we got was: ${getHttpProps([ { key: 'status', value: `${obj.status} - ${obj.statusText}` }, { key: 'headers', value: obj.responseHeaders }, { key: 'body', value: obj.responseBody }, ])} `, 10), docsUrl: 'https://on.cypress.io/request', } }, timed_out (obj) { return { message: cyStripIndent(`\ ${cmd('request')} timed out waiting \`${obj.timeout}ms\` for a response from your server. The request we sent was: ${getHttpProps([ { key: 'method', value: obj.method }, { key: 'URL', value: obj.url }, ])} No response was received within the timeout.`, 10), docsUrl: 'https://on.cypress.io/request', } }, /* eslint-enable indent */ url_missing: { message: `${cmd('request')} requires a \`url\`. You did not provide a \`url\`.`, docsUrl: 'https://on.cypress.io/request', }, url_invalid ({ configFile }) { return { message: `${cmd('request')} must be provided a fully qualified \`url\` - one that begins with \`http\`. By default ${cmd('request')} will use either the current window's origin or the \`baseUrl\` in ${formatConfigFile(configFile)}. Neither of those values were present.`, docsUrl: 'https://on.cypress.io/request', } }, url_wrong_type: { message: `${cmd('request')} requires the \`url\` to be a string.`, docsUrl: 'https://on.cypress.io/request', }, }, route: { deprecated: { message: `${cmd('route')} has been deprecated and will be moved to a plugin in a future release. Consider migrating to using ${cmd('intercept')} instead.`, docsUrl: 'https://on.cypress.io/intercept', }, failed_prerequisites: { message: `${cmd('route')} cannot be invoked before starting the ${cmd('server')}`, docsUrl: 'https://on.cypress.io/server', }, invalid_arguments: { message: `${cmd('route')} was not provided any arguments. You must provide valid arguments.`, docsUrl: 'https://on.cypress.io/route', }, method_invalid: { message: `${cmd('route')} was called with an invalid method: \`{{method}}\`. Method can be: \`GET\`, \`POST\`, \`PUT\`, \`DELETE\`, \`PATCH\`, \`HEAD\`, \`OPTIONS\`, or any other method supported by Node's HTTP parser.`, docsUrl: 'https://on.cypress.io/route', }, response_invalid: { message: `${cmd('route')} cannot accept an \`undefined\` or \`null\` response. It must be set to something, even an empty string will work.`, docsUrl: 'https://on.cypress.io/route', }, url_invalid: { message: `${cmd('route')} was called with an invalid \`url\`. \`url\` must be either a string or regular expression.`, docsUrl: 'https://on.cypress.io/route', }, url_missing: { message: `${cmd('route')} must be called with a \`url\`. It can be a string or regular expression.`, docsUrl: 'https://on.cypress.io/route', }, url_percentencoding_warning ({ decodedUrl }) { return { message: stripIndent`\ A \`url\` with percent-encoded characters was passed to ${cmd('route')}, but ${cmd('route')} expects a decoded \`url\`. Did you mean to pass "${decodedUrl}"?`, docsUrl: 'https://on.cypress.io/route', } }, }, scrollIntoView: { invalid_argument: { message: `${cmd('scrollIntoView')} can only be called with an \`options\` object. Your argument was: \`{{arg}}\``, docsUrl: 'https://on.cypress.io/scrollintoview', }, multiple_elements: { message: `${cmd('scrollIntoView')} can only be used to scroll to 1 element, you tried to scroll to {{num}} elements.\n\n`, docsUrl: 'https://on.cypress.io/scrollintoview', }, invalid_easing: { message: `${cmd('scrollIntoView')} must be called with a valid \`easing\`. Your easing was: \`{{easing}}\``, docsUrl: 'https://on.cypress.io/scrollintoview', }, invalid_duration: { message: `${cmd('scrollIntoView')} must be called with a valid \`duration\`. Duration may be either a number (ms) or a string representing a number (ms). Your duration was: \`{{duration}}\``, docsUrl: 'https://on.cypress.io/scrollintoview', }, }, scrollTo: { animation_failed: { message: `${cmd('scrollTo')} failed to scroll.`, docsUrl: 'https://on.cypress.io/scrollto', }, invalid_easing: { message: `${cmd('scrollTo')} must be called with a valid \`easing\`. Your easing was: \`{{easing}}\``, docsUrl: 'https://on.cypress.io/scrollto', }, invalid_duration: { message: `${cmd('scrollTo')} must be called with a valid \`duration\`. Duration may be either a number (ms) or a string representing a number (ms). Your duration was: \`{{duration}}\``, docsUrl: 'https://on.cypress.io/scrollto', }, invalid_target: { message: `${cmd('scrollTo')} must be called with a valid \`position\`. It can be a string, number or object. Your position was: \`{{x}}, {{y}}\``, docsUrl: 'https://on.cypress.io/scrollto', }, multiple_containers: { message: `${cmd('scrollTo')} can only be used to scroll 1 element, you tried to scroll {{num}} elements.\n\n`, docsUrl: 'https://on.cypress.io/scrollto', }, invalid_ensureScrollable: { message: `${cmd('scrollTo')} \`ensureScrollable\` option must be a boolean. You passed: \`{{ensureScrollable}}\``, docsUrl: 'https://on.cypress.io/scrollto', }, }, screenshot: { invalid_arg (obj) { return { message: `${cmd(obj.cmd)} must be called with an object. You passed: \`{{arg}}\``, docsUrl: `https://on.cypress.io/${getScreenshotDocsPath(obj.cmd)}`, } }, invalid_capture (obj) { return { message: `${cmd(obj.cmd)} \`capture\` option must be one of the following: \`fullPage\`, \`viewport\`, or \`runner\`. You passed: \`{{arg}}\``, docsUrl: `https://on.cypress.io/${getScreenshotDocsPath(obj.cmd)}`, } }, invalid_boolean (obj) { return { message: `${cmd(obj.cmd)} \`{{option}}\` option must be a boolean. You passed: \`{{arg}}\``, docsUrl: `https://on.cypress.io/${getScreenshotDocsPath(obj.cmd)}`, } }, invalid_blackout (obj) { return { message: `${cmd(obj.cmd)} \`blackout\` option must be an array of strings. You passed: \`{{arg}}\``, docsUrl: `https://on.cypress.io/${getScreenshotDocsPath(obj.cmd)}`, } }, invalid_callback (obj) { return { message: `${cmd(obj.cmd)} \`{{callback}}\` option must be a function. You passed: \`{{arg}}\``, docsUrl: `https://on.cypress.io/${getScreenshotDocsPath(obj.cmd)}`, } }, invalid_clip (obj) { return { message: `${cmd(obj.cmd)} \`clip\` option must be an object with the keys \`{ width, height, x, y }\` and number values. You passed: \`{{arg}}\``, docsUrl: `https://on.cypress.io/${getScreenshotDocsPath(obj.cmd)}`, } }, invalid_height (obj) { return { message: `${cmd('screenshot')} only works with a screenshot area with a height greater than zero.`, docsUrl: 'https://on.cypress.io/screenshot', } }, invalid_padding (obj) { return { message: `${cmd(obj.cmd)} \`padding\` option must be either a number or an array of numbers with a maximum length of 4. You passed: \`{{arg}}\``, docsUrl: `https://on.cypress.io/${getScreenshotDocsPath(obj.cmd)}`, }