diff --git a/lib/rules/a11y-audit-called.js b/lib/rules/a11y-audit-called.js new file mode 100644 index 0000000..2006758 --- /dev/null +++ b/lib/rules/a11y-audit-called.js @@ -0,0 +1,79 @@ +"use strict"; + +const utils = require("../utils"); + +/** + * @fileoverview Enforce importing and calling a11yAudit at least once in a file. + * @author Buck Doyle + */ + +//------------------------------------------------------------------------------ +// Requirements +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +// Rule Definition +//------------------------------------------------------------------------------ + +module.exports = { + meta: { + type: "problem", + + description: "enforce a11yAudit must be called at least once", + category: "Accessibility", + recommended: true, + url: + "https://github.com/a11y-tool-sandbox/eslint-plugin-ember-a11y-testing/blob/master/lib/rules/a11y-audit-called.js", + + schema: [], + + messages: { + a11yAuditCalled: "`{{name}}` must be called", + a11yAuditImported: "a11y audit helper must be imported", + }, + }, + + create(context) { + const settings = utils.extractSettings(context); + + const a11yImportDeclaration = utils.findA11yAuditImportDeclaration( + context, + settings + ); + + if (a11yImportDeclaration) { + const a11yAuditIdentifier = a11yImportDeclaration.local.name; + let a11yAuditCalled = false; + + return { + "Program:exit": function (node) { + if (!a11yAuditCalled) { + context.report({ + node, + messageId: "a11yAuditCalled", + data: { + name: a11yAuditIdentifier, + }, + }); + } + }, + + CallExpression(node) { + let identifier = node.callee.type === "Identifier" && node.callee; + if (!identifier || identifier.name !== a11yAuditIdentifier) return; + + a11yAuditCalled = true; + }, + }; + } else { + return { + Program: function (node) { + context.report({ + node, + messageId: "a11yAuditImported", + }); + }, + }; + } + }, +}; diff --git a/tests/lib/rules/a11y-audit-called.js b/tests/lib/rules/a11y-audit-called.js new file mode 100644 index 0000000..7f9ada3 --- /dev/null +++ b/tests/lib/rules/a11y-audit-called.js @@ -0,0 +1,105 @@ +"use strict"; + +/** + * @fileoverview Tests for a11y-audit-called rule. + * @author Buck Doyle + */ + +//------------------------------------------------------------------------------ +// Requirements +//------------------------------------------------------------------------------ + +const rule = require("../../../lib/rules/a11y-audit-called"); +const { RuleTester } = require("eslint/lib/rule-tester"); +const { stripIndents: code } = require("common-tags"); + +// Tests +//------------------------------------------------------------------------------ + +const ruleTester = new RuleTester(); + +function runWithModernSyntax(testName, rule, options) { + const makeTestModern = (testCase) => { + const defaultParserOptions = { + parserOptions: { + ecmaVersion: 2018, + sourceType: "module", + }, + }; + return { + ...defaultParserOptions, + ...testCase, + }; + }; + const validCases = (options.valid || []).map(makeTestModern); + const invalidCases = (options.invalid || []).map(makeTestModern); + return ruleTester.run(testName, rule, { + ...options, + valid: validCases, + invalid: invalidCases, + }); +} + +runWithModernSyntax("a11y-audit-called", rule, { + valid: [ + { + code: code`import a11yAudit from 'ember-a11y-testing/test-support/audit'; + a11yAudit();`, + }, + { + code: code`import a11yAudit from 'ember-a11y-testing/test-support/audit'; + import { visit } from '@ember/test-helpers'; + + function test1() { + visit(); + a11yAudit(); + } + + function test2() { + visit(); + }`, + }, + { + code: `import a11yAudit2 from 'custom-module'; a11yAudit2();`, + parserOptions: { + ecmaVersion: "2018", + sourceType: "module", + }, + settings: { + "ember-a11y-testing": { + auditModule: { + package: "custom-module", + exportName: "default", + }, + }, + }, + }, + ], + invalid: [ + { + code: code` + import { fillIn } from "@ember/test-helpers"; + import a11yAudit from 'ember-a11y-testing/test-support/audit'; + async function doStuff() { + return fillIn('#hi'); + }`, + errors: [{ messageId: "a11yAuditCalled" }], + }, + { + code: `import audit from 'custom-module'; a11yAudit();`, + errors: [{ messageId: "a11yAuditCalled" }], + settings: { + "ember-a11y-testing": { + auditModule: { + package: "custom-module", + exportName: "default", + }, + }, + }, + }, + { + code: `a11yAudit();`, + errors: [{ messageId: "a11yAuditImported" }], + }, + ], +});