|
'use strict' |
|
|
|
const assertionCommands = [ |
|
|
|
'should', |
|
'and', |
|
'contains', |
|
|
|
|
|
'get', |
|
|
|
|
|
'scrollIntoView', |
|
'scrollTo', |
|
] |
|
|
|
module.exports = { |
|
meta: { |
|
type: 'problem', |
|
docs: { |
|
description: 'require screenshots to be preceded by an assertion', |
|
category: 'Possible Errors', |
|
recommended: false, |
|
url: 'https://github.com/cypress-io/eslint-plugin-cypress/blob/master/docs/rules/assertion-before-screenshot.md', |
|
}, |
|
schema: [], |
|
messages: { |
|
unexpected: 'Make an assertion on the page state before taking a screenshot', |
|
}, |
|
}, |
|
create (context) { |
|
return { |
|
CallExpression (node) { |
|
if (isCallingCyScreenshot(node) && !isPreviousAnAssertion(node)) { |
|
context.report({ node, messageId: 'unexpected' }) |
|
} |
|
}, |
|
} |
|
}, |
|
} |
|
|
|
function isRootCypress (node) { |
|
while (node.type === 'CallExpression') { |
|
if (node.callee.type !== 'MemberExpression') return false |
|
|
|
if (node.callee.object.type === 'Identifier' && |
|
node.callee.object.name === 'cy') { |
|
return true |
|
} |
|
|
|
node = node.callee.object |
|
} |
|
|
|
return false |
|
} |
|
|
|
function getPreviousInChain (node) { |
|
return node.type === 'CallExpression' && |
|
node.callee.type === 'MemberExpression' && |
|
node.callee.object.type === 'CallExpression' && |
|
node.callee.object.callee.type === 'MemberExpression' && |
|
node.callee.object.callee.property.type === 'Identifier' && |
|
node.callee.object.callee.property.name |
|
} |
|
|
|
function getCallExpressionCypressCommand (node) { |
|
return isRootCypress(node) && |
|
node.callee.property.type === 'Identifier' && |
|
node.callee.property.name |
|
} |
|
|
|
function isCallingCyScreenshot (node) { |
|
return getCallExpressionCypressCommand(node) === 'screenshot' |
|
} |
|
|
|
function getPreviousCypressCommand (node) { |
|
const previousInChain = getPreviousInChain(node) |
|
|
|
if (previousInChain) { |
|
return previousInChain |
|
} |
|
|
|
while (node.parent && !node.parent.body) { |
|
node = node.parent |
|
} |
|
|
|
if (!node.parent || !node.parent.body) return null |
|
|
|
const body = node.parent.body.type === 'BlockStatement' ? node.parent.body.body : node.parent.body |
|
|
|
const index = body.indexOf(node) |
|
|
|
|
|
if (index < 0) return null |
|
|
|
if (index === 0) return getPreviousCypressCommand(node.parent) |
|
|
|
const previousStatement = body[index - 1] |
|
|
|
if (previousStatement.type !== 'ExpressionStatement' || |
|
previousStatement.expression.type !== 'CallExpression') { |
|
return null |
|
} |
|
|
|
return getCallExpressionCypressCommand(previousStatement.expression) |
|
} |
|
|
|
function isPreviousAnAssertion (node) { |
|
const previousCypressCommand = getPreviousCypressCommand(node) |
|
|
|
return assertionCommands.indexOf(previousCypressCommand) >= 0 |
|
} |
|
|