eslint-plugin-sonarjs
Version:
147 lines (146 loc) • 5.6 kB
JavaScript
;
/*
* SonarQube JavaScript Plugin
* Copyright (C) SonarSource Sàrl
* mailto:info AT sonarsource DOT com
*
* You can redistribute and/or modify this program under the terms of
* the Sonar Source-Available License Version 1, as published by SonarSource Sàrl.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the Sonar Source-Available License for more details.
*
* You should have received a copy of the Sonar Source-Available License
* along with this program; if not, see https://sonarsource.com/license/ssal/
*/
// https://sonarsource.github.io/rspec/#/rspec/S8959/javascript
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.rule = void 0;
const ast_js_1 = require("../helpers/ast.js");
const cypress_js_1 = require("../helpers/cypress.js");
const generate_meta_js_1 = require("../helpers/generate-meta.js");
const quickfix_js_1 = require("../helpers/quickfix.js");
const test_file_pattern_js_1 = require("../helpers/test-file-pattern.js");
const meta = __importStar(require("./generated-meta.js"));
exports.rule = {
meta: (0, generate_meta_js_1.generateMeta)(meta, {
messages: {
removeDebugCommand: 'Remove this debug command from the test.',
},
fixable: 'code',
}),
create(context) {
if (!(0, test_file_pattern_js_1.isTestRelatedFile)(context.filename, context.settings?.testFileExtensions)) {
return {};
}
return {
CallExpression(node) {
const call = node;
if (!(0, ast_js_1.isMethodCall)(call)) {
return;
}
if (isUiTestDebugCommand(call.callee)) {
reportDebugCommand(context, call);
}
},
};
},
};
function isUiTestDebugCommand(callee) {
const { object, property } = callee;
switch (property.name) {
case 'pause':
return (0, cypress_js_1.chainStartsWithCy)(object) || (0, ast_js_1.isIdentifier)(object, 'page');
case 'debug':
return (0, cypress_js_1.chainStartsWithCy)(object);
default:
return false;
}
}
function reportDebugCommand(context, call) {
context.report({
node: call.callee.property,
messageId: 'removeDebugCommand',
fix: fixer => removeDebugCommand(context, call, fixer),
});
}
/**
* Removes the whole statement only for root debug calls used as standalone statements, e.g.
* `cy.pause();` or `await page.pause();`. For Cypress chains, only the `.pause()`/`.debug()`
* link is spliced out, e.g. `cy.get('x').debug().click();` -> `cy.get('x').click();` and
* `cy.get('x').pause();` -> `cy.get('x');`.
*/
function removeDebugCommand(context, call, fixer) {
const statement = unwrapToStatementExpression(call);
if (isRootDebugReceiver(call.callee.object) && (0, ast_js_1.hasParent)(statement)) {
const { parent } = statement;
if (parent.type === 'ExpressionStatement' && parent.expression === statement) {
return (0, quickfix_js_1.removeNodeWithLeadingWhitespaces)(context, parent, fixer);
}
}
const objectRange = call.callee.object.range;
const callRange = call.range;
if (!objectRange || !callRange) {
return null;
}
return fixer.removeRange([objectRange[1], callRange[1]]);
}
function unwrapToStatementExpression(node) {
let current = node;
while ((0, ast_js_1.hasParent)(current)) {
const { parent } = current;
if ((parent.type === 'ChainExpression' && parent.expression === current) ||
(parent.type === 'AwaitExpression' && parent.argument === current)) {
current = parent;
}
else {
break;
}
}
return current;
}
function isRootDebugReceiver(node) {
return (0, ast_js_1.isIdentifier)(unwrapChainExpression(node), 'cy', 'page');
}
function unwrapChainExpression(node) {
let current = node;
while (current.type === 'ChainExpression') {
current = current.expression;
}
return current;
}