no-unnecessary-waiting.js 2.5 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586
  1. 'use strict'
  2. module.exports = {
  3. meta: {
  4. docs: {
  5. description: 'Prevent waiting for arbitrary time periods',
  6. category: 'Possible Errors',
  7. recommended: true,
  8. url: 'https://on.cypress.io/best-practices#Unnecessary-Waiting',
  9. },
  10. schema: [],
  11. messages: {
  12. unexpected: 'Do not wait for arbitrary time periods',
  13. },
  14. },
  15. create (context) {
  16. return {
  17. CallExpression (node) {
  18. if (isCallingCyWait(node)) {
  19. const scope = context.getScope()
  20. if (isIdentifierNumberConstArgument(node, scope) || isNumberArgument(node)) {
  21. context.report({ node, messageId: 'unexpected' })
  22. }
  23. }
  24. },
  25. }
  26. },
  27. }
  28. function nodeIsCalledByCy (node) {
  29. if (node.type === 'Identifier' && node.name === 'cy') return true
  30. if (typeof node.callee === 'undefined' || typeof node.callee.object === 'undefined') {
  31. return false
  32. }
  33. return nodeIsCalledByCy(node.callee.object)
  34. }
  35. function isCallingCyWait (node) {
  36. return node.callee.type === 'MemberExpression' &&
  37. nodeIsCalledByCy(node) &&
  38. node.callee.property.type === 'Identifier' &&
  39. node.callee.property.name === 'wait'
  40. }
  41. function isNumberArgument (node) {
  42. return node.arguments.length > 0 &&
  43. node.arguments[0].type === 'Literal' &&
  44. typeof (node.arguments[0].value) === 'number'
  45. }
  46. function isIdentifierNumberConstArgument (node, scope) {
  47. if (node.arguments.length === 0) return false
  48. if (node.arguments[0].type !== 'Identifier') return false
  49. const identifier = node.arguments[0]
  50. const resolvedIdentifier = scope.references.find((ref) => ref.identifier === identifier).resolved
  51. const definition = resolvedIdentifier.defs[0]
  52. const isVariable = definition.type === 'Variable'
  53. // const amount = 1000 or const amount = '@alias'
  54. // cy.wait(amount)
  55. if (isVariable) {
  56. if (!definition.node.init) return false
  57. return typeof definition.node.init.value === 'number'
  58. }
  59. // import { WAIT } from './constants'
  60. // cy.wait(WAIT)
  61. // we don't know if WAIT is a number or alias '@someRequest', so don't fail
  62. if (definition.type === 'ImportBinding') return false
  63. const param = definition.node.params[definition.index]
  64. // function wait (amount) { cy.wait(amount) }
  65. // we can't know the type of value, so don't fail
  66. if (!param || param.type !== 'AssignmentPattern') return false
  67. // function wait (amount = 1) { cy.wait(amount) } or
  68. // function wait (amount = '@alias') { cy.wait(amount) }
  69. return typeof param.right.value === 'number'
  70. }