consistent-data-testid.js 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", { value: true });
  3. exports.RULE_NAME = void 0;
  4. const create_testing_library_rule_1 = require("../create-testing-library-rule");
  5. const node_utils_1 = require("../node-utils");
  6. exports.RULE_NAME = 'consistent-data-testid';
  7. const FILENAME_PLACEHOLDER = '{fileName}';
  8. exports.default = (0, create_testing_library_rule_1.createTestingLibraryRule)({
  9. name: exports.RULE_NAME,
  10. meta: {
  11. type: 'suggestion',
  12. docs: {
  13. description: 'Ensures consistent usage of `data-testid`',
  14. recommendedConfig: {
  15. dom: false,
  16. angular: false,
  17. react: false,
  18. vue: false,
  19. marko: false,
  20. },
  21. },
  22. messages: {
  23. consistentDataTestId: '`{{attr}}` "{{value}}" should match `{{regex}}`',
  24. consistentDataTestIdCustomMessage: '`{{message}}`',
  25. },
  26. schema: [
  27. {
  28. type: 'object',
  29. default: {},
  30. additionalProperties: false,
  31. required: ['testIdPattern'],
  32. properties: {
  33. testIdPattern: {
  34. type: 'string',
  35. },
  36. testIdAttribute: {
  37. default: 'data-testid',
  38. oneOf: [
  39. {
  40. type: 'string',
  41. },
  42. {
  43. type: 'array',
  44. items: {
  45. type: 'string',
  46. },
  47. },
  48. ],
  49. },
  50. customMessage: {
  51. default: undefined,
  52. type: 'string',
  53. },
  54. },
  55. },
  56. ],
  57. },
  58. defaultOptions: [
  59. {
  60. testIdPattern: '',
  61. testIdAttribute: 'data-testid',
  62. customMessage: undefined,
  63. },
  64. ],
  65. detectionOptions: {
  66. skipRuleReportingCheck: true,
  67. },
  68. create: (context, [options]) => {
  69. const { getFilename } = context;
  70. const { testIdPattern, testIdAttribute: attr, customMessage } = options;
  71. function getFileNameData() {
  72. var _a;
  73. const splitPath = getFilename().split('/');
  74. const fileNameWithExtension = (_a = splitPath.pop()) !== null && _a !== void 0 ? _a : '';
  75. if (fileNameWithExtension.includes('[') ||
  76. fileNameWithExtension.includes(']')) {
  77. return { fileName: undefined };
  78. }
  79. const parent = splitPath.pop();
  80. const fileName = fileNameWithExtension.split('.').shift();
  81. return {
  82. fileName: fileName === 'index' ? parent : fileName,
  83. };
  84. }
  85. function getTestIdValidator(fileName) {
  86. return new RegExp(testIdPattern.replace(FILENAME_PLACEHOLDER, fileName));
  87. }
  88. function isTestIdAttribute(name) {
  89. var _a;
  90. if (typeof attr === 'string') {
  91. return attr === name;
  92. }
  93. else {
  94. return (_a = attr === null || attr === void 0 ? void 0 : attr.includes(name)) !== null && _a !== void 0 ? _a : false;
  95. }
  96. }
  97. function getErrorMessageId() {
  98. if (customMessage === undefined) {
  99. return 'consistentDataTestId';
  100. }
  101. return 'consistentDataTestIdCustomMessage';
  102. }
  103. return {
  104. JSXIdentifier: (node) => {
  105. if (!node.parent ||
  106. !(0, node_utils_1.isJSXAttribute)(node.parent) ||
  107. !(0, node_utils_1.isLiteral)(node.parent.value) ||
  108. !isTestIdAttribute(node.name)) {
  109. return;
  110. }
  111. const value = node.parent.value.value;
  112. const { fileName } = getFileNameData();
  113. const regex = getTestIdValidator(fileName !== null && fileName !== void 0 ? fileName : '');
  114. if (value && typeof value === 'string' && !regex.test(value)) {
  115. context.report({
  116. node,
  117. messageId: getErrorMessageId(),
  118. data: {
  119. attr: node.name,
  120. value,
  121. regex,
  122. message: customMessage,
  123. },
  124. });
  125. }
  126. },
  127. };
  128. },
  129. });