blob: 4d050a5a1778307cfc6b48136813a2613ddc3681 [file]
// Copyright 2023 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
'use strict';
// Rule Definition
// ------------------------------------------------------------------------------
module.exports = {
meta: {
type: 'problem',
docs: {
description: 'Ensure trace engine tests are defined as functions for test timeouts.',
category: 'Possible Errors',
},
fixable: 'code',
messages: {
'needsFunction': 'Test should use a function keyword, not an arrow function.'
},
schema: [] // no options
},
create: function(context) {
const MOCHA_CALLS_TO_CHECK = new Set([
'it',
'before',
'beforeEach',
'after',
'afterEach',
]);
function walkUpTreeToFindMochaFunctionCall(node) {
if(node.type === 'CallExpression' && node.callee.type === 'Identifier' && MOCHA_CALLS_TO_CHECK.has(node.callee.name)) {
return node;
}
if(!node || !node.parent) {
return null;
}
return walkUpTreeToFindMochaFunctionCall(node.parent);
}
return {
MemberExpression(node) {
const objectIsTraceLoader = node.object.type === 'Identifier' && node.object.name === 'TraceLoader';
if(!objectIsTraceLoader) {
return;
}
// Find out if this is an await call (which needs the additional test timeout).
const callExpression = node.parent;
const isAwait = callExpression.parent && callExpression.parent.type === 'AwaitExpression';
if(!isAwait) {
return;
}
// We now know that we have await TraceLoader.[something]();
// Now we need to walk up the tree to find the it() call. If we do,
// we can then check that its function is defined via a function
// and not as an arrow function.
const mochaFunctionCall = walkUpTreeToFindMochaFunctionCall(node);
if(!mochaFunctionCall) {
return;
}
// This code is within a mocha call. If the call is an `it`, we need
// the second argument, otherwise we use the first argument (Mocha
// functions like `beforeEach` take only a function as the argument.)
const functionArg = mochaFunctionCall.callee.name === 'it' ?
mochaFunctionCall.arguments[1] :
mochaFunctionCall.arguments[0];
if(!functionArg) {
// The node unexpectedly does not have a function passed. The
// developer is probably in the middle of writing it, so we should
// just stop and leave them to it.
return;
}
if(functionArg.type === 'ArrowFunctionExpression') {
context.report({
node: functionArg,
messageId: 'needsFunction',
fix(fixer) {
const rangeToReplace = [
// We want to replace `async () =>` [11 characters] with
// `function()`. So we replace the first 11 characters of
// the function.
functionArg.range[0],
functionArg.range[0] + 11,
];
return fixer.replaceTextRange(rangeToReplace, 'async function()');
}
});
}
}
};
}
};