自定义 ESLint 规则中的异步代码

Asynchronous code in custom ESLint rules

故事与动机:

我们有一个相当庞大的端到端 Protractor 测试代码库。有时,测试会等待特定修复的实施——通常作为 TDD 方法的一部分,并演示问题是如何重现的以及预期的行为是什么。我们目前正在做的是使用 Jasmine 的 pending(),里面有一个 Jira 问题编号。示例:

pending("Missing functionality (AP-1234)", function () {
    // some testing is done here
});

现在,我们想知道何时可以将 pending() 重命名为 it() 和 运行 测试。或者,换句话说,当问题 AP-1234 已解决或发送至测试时。

当前方法:

目前,我正在尝试用 custom ESLint rule, jira NodeJS module, and Q 来解决它。自定义 ESLint 规则搜索带有至少一个参数的 pending() 调用。以 AP- 后跟 4 位数字的格式提取票号,并使用 jira.findIssue() 检查其在 Jira 中的状态。如果状态为 Resolved - 报告错误。

这是我目前得到的:

"use strict";

var JiraApi = require("jira").JiraApi,
    Q = require('q');
var jira = new JiraApi("https",
    "jira.url.com",
    "443",
    "user",
    "password",
    "2");

module.exports = function (context) {
    var jiraTicketRegex = /AP\-\d+/g;

    return {
        CallExpression: function (node) {
            if (node.callee.name === "pending" && node.arguments.length > 0) {
                var match = node.arguments[0].value.match(jiraTicketRegex);

                if (match) {
                    match.forEach(function(ticket) {
                        console.log(ticket);  // I see the ticket numbers printed
                        getTicket(ticket).then(function (status) {
                            console.log(status);  // I don't see statuses printed
                            if (status === "Resolved") {
                                context.report(node, 'Ticket {{ticket}} is already resolved.', {
                                    ticket: ticket
                                })
                            }
                        });
                    });

                }
            }
        }
    }
};

其中 getTicket() 定义为:

function getTicket(ticket) {
    var deferred = Q.defer();

    jira.findIssue(ticket, function(error, issue) {
        if (error) {
            deferred.reject(new Error(error));
        } else {
            deferred.resolve(issue.fields.status.name);
        }
    });

    return deferred.promise;
}

问题是: 目前,它成功地从 pending() 调用中提取票号,但不打印票状态。不过没有错误。

问题:

我想一般的问题是:我可以使用异步代码块、等待回调、解决自定义 ESLint 规则中的承诺吗?如果没有,我有什么选择?

一个更具体的问题是:我做错了什么以及如何将 Node.js jira 模块与 ESLint 一起使用?

将不胜感激任何见解或替代方法。

简短的回答是 - 不,您不能在规则内使用异步代码。 ESLint 是同步的,并且在遍历 AST 时严重依赖 EventEmitter。将 ESLint 代码修改为异步非常困难,但同时又要保证事件将以正确的顺序发出。 我认为您唯一的选择可能是编写一个同步规则,将足够的信息输出到错误消息中,然后使用 JSONUNIX 等可解析格式化程序之一,然后创建另一个可以通过管道传输 ESLint 的应用程序根据错误消息输出并在 Jira 中执行异步查找。

注意:它没有回答关于在 ESLint 自定义规则中支持异步代码的原始问题,而是提供了该问题的替代解决方案。

我个人不会在这种情况下使用 ESLint,它应该用于检查您的代码是否编写正确以及您是否遵循风格指南;从我的角度来看,缺少测试不是代码检查的一部分,它更像是您的团队内部流程。此外,这种请求可能会显着降低您的 ESLint 执行速度,如果有人在他们的编辑器中实时运行它,调用将会非常频繁并且会减慢整个检查。我会让这个 JIRA 检查 Protractor 流程的一部分,所以如果问题单得到解决,你将得到一个失败的 Protractor 规格。 (从 复制来使答案完整)

Jasmine 允许使用 xit(). I am not sure about pending() though, it works weird in Protractor. Also, Jasmine allows to call pending() inside a spec, so it will be marked as pending, but it is not implemented for Protractor yet (see issue) 将规范标记为待定。知道这一点后,我会使用自定义助手来定义 "pending specs",应该检查 JIRA 问题状态。我想你仍然可以使用 Q 来处理承诺,我只是 post 一个使用 WebDriver 承诺的替代方案,没有外部依赖。这是 getTicket() 的修改版本:

function getTicketStatus(ticket) {

    // Using WebDriver promises
    var deferred = protractor.promise.defer();

    jira.findIssue(ticket, function(error, issue) {
        if (error) {
            deferred.reject(new Error(error));
        } else {
            deferred.fulfill(issue.fields.status.name);
        }
    });

    return deferred.promise;
}

然后就是自定义辅助函数:

function jira(name) {
    // Display as pending in reporter results, remove when pending() is supported
    xit(name);

    // Using Jasmine Async API because Jira request is not a part of Control Flow
    it(name, function (done) {

        getTicketStatus().then(function (status) {
            if (status === 'Resolved') {
                done.fail('Ticket "' + name + '" is already resolved.');
            } else {
                done();
                // pending() is not supported yet https://github.com/angular/protractor/issues/2454
                // pending();
            }
        }, function (error) {
            done.fail(error);
        });
    });

}

用法示例:

jira('Missing functionality (AP-1234)', function () {
    //
});

jira('Missing functionality (AP-1235)');

如果对 JIRA 的请求失败或问题的状态为 Resolved,您将得到一个失败的规范(使用 茉莉 async API). In all situations you will still have this spec duplicated as pending in reporter results. I hope it can be improved, when pending() functionality inside a spec is implemented.

这些答案在 2018 年仍然有效。

有关 eslint 开发人员的一些见解,请参阅 this conversation 我们在他们的邮件列表中。

举一个有效的例子,在我的 "pseudo eslint plugin" 中,我选择使用昂贵但同步的 API,并警告用户如何最好地在他们的 CI 过程中使用 "plugin"。