UNPKG

@yasanchezz/eslint-plugin-vue-data-testid

Version:

An `eslint` plugin for checking accessibility rules from within `.vue` files. Add `data-testid` to use better behavior testing

75 lines (74 loc) 3.67 kB
import { getAttributeNames, getAttributeValue, calculateAttributeNames } from "../utils/index.js"; import * as path from 'node:path'; export default function createRule({ attributeName = 'data-testid', buildDataTestid, ignoreNode = () => false, }) { return { meta: { docs: { description: 'should add data-testid attribute' }, messages: { add: 'add data-testid="{{attribute}}".' }, type: 'suggestion', fixable: 'code', hasSuggestions: true, schema: [], // no options }, create(context) { const filename = path.basename(context.filename); const extname = path.extname(context.filename); if (extname !== '.vue') { return {}; } return context.sourceCode.parserServices.defineTemplateBodyVisitor({ VElement(node) { const isRoot = node.parent.parent?.type === 'VDocumentFragment'; const classNames = Array.from(getAttributeValue(node, 'class')) .flatMap(className => className.split(' ')); const option = { nodeName: node.rawName, filepath: context.filename, filename, isRoot, classNames, }; const userAttributeName = typeof attributeName === 'string' ? attributeName : attributeName(option); const searchedAttributeNames = calculateAttributeNames(userAttributeName); const attributeNames = getAttributeNames(node); const hasDataTestIdAttribute = attributeNames.intersection(searchedAttributeNames).size > 0; const [startRange] = node.startTag.range; const nameLength = node.rawName.length; if (hasDataTestIdAttribute) { return {}; } const classNameOptions = classNames.map(className => ({ ...option, className, })); const options = classNameOptions.length ? classNameOptions : [option]; const filteredOptions = options.filter(option => !ignoreNode(option)); const dataTestids = filteredOptions .map(buildDataTestid) .filter(dataTestid => dataTestid != null) .filter(dataTestid => dataTestid.trim()); const firstDataTestid = dataTestids.at(0); if (!firstDataTestid) { return {}; } function fix(dataTestid, fixer) { const message = ` ${userAttributeName}="${dataTestid}"`; return fixer.insertTextAfterRange([startRange, startRange + nameLength + 1], message); } context.report({ node: node, message: `should add ${userAttributeName} for further behavior testing`, fix: fix.bind(undefined, firstDataTestid), suggest: dataTestids.map(dataTestid => ({ data: { attribute: dataTestid }, messageId: 'add', fix: fix.bind(undefined, dataTestid) })) }); } }); } }; }