123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116 |
- "use strict";
- /**
- * @fileoverview Do not use testing library directly on stories
- * @author Yann Braga
- */
- const ast_1 = require("../utils/ast");
- const constants_1 = require("../utils/constants");
- const create_storybook_rule_1 = require("../utils/create-storybook-rule");
- module.exports = (0, create_storybook_rule_1.createStorybookRule)({
- name: 'use-storybook-testing-library',
- defaultOptions: [],
- meta: {
- type: 'suggestion',
- fixable: 'code',
- hasSuggestions: true,
- docs: {
- description: 'Do not use testing-library directly on stories',
- categories: [constants_1.CategoryId.ADDON_INTERACTIONS, constants_1.CategoryId.RECOMMENDED],
- recommended: 'error',
- },
- schema: [],
- messages: {
- updateImports: 'Update imports',
- dontUseTestingLibraryDirectly: 'Do not use `{{library}}` directly in the story. You should import the functions from `@storybook/testing-library` instead.',
- },
- },
- create(context) {
- // variables should be defined here
- //----------------------------------------------------------------------
- // Helpers
- //----------------------------------------------------------------------
- const getRangeWithoutQuotes = (source) => {
- return [
- // Not sure how to improve this. If I use node.source.range
- // it will eat the quotes and we do not want to specify whether the quotes are single or double
- source.range[0] + 1,
- source.range[1] - 1,
- ];
- };
- const hasDefaultImport = (specifiers) => specifiers.find((s) => (0, ast_1.isImportDefaultSpecifier)(s));
- const getSpecifiers = (node) => {
- const { specifiers } = node;
- if (!specifiers[0]) {
- return null;
- }
- const start = specifiers[0].range[0];
- const previousSpecifier = specifiers[specifiers.length - 1];
- if (!previousSpecifier) {
- return null;
- }
- let end = previousSpecifier.range[1];
- // this weird hack is necessary because the specifier range
- // does not include the closing brace:
- //
- // import foo, { bar } from 'baz';
- // ^ ^ end
- const fullText = context.getSourceCode().text;
- const importEnd = node.range[1];
- const closingBrace = fullText.indexOf('}', end - 1);
- if (closingBrace > -1 && closingBrace <= importEnd) {
- end = closingBrace + 1;
- }
- const text = fullText.substring(start, end);
- return { range: [start, end], text };
- };
- const fixSpecifiers = (specifiersText) => {
- const flattened = specifiersText
- .replace('{', '')
- .replace('}', '')
- .replace(/\s\s+/g, ' ')
- .trim();
- return `{ ${flattened} }`;
- };
- //----------------------------------------------------------------------
- // Public
- //----------------------------------------------------------------------
- return {
- ImportDeclaration(node) {
- if (node.source.value.includes('@testing-library')) {
- context.report({
- node,
- messageId: 'dontUseTestingLibraryDirectly',
- data: {
- library: node.source.value,
- },
- *fix(fixer) {
- yield fixer.replaceTextRange(getRangeWithoutQuotes(node.source), '@storybook/testing-library');
- if (hasDefaultImport(node.specifiers)) {
- const specifiers = getSpecifiers(node);
- if (specifiers) {
- const { range, text } = specifiers;
- yield fixer.replaceTextRange(range, fixSpecifiers(text));
- }
- }
- },
- suggest: [
- {
- messageId: 'updateImports',
- *fix(fixer) {
- yield fixer.replaceTextRange(getRangeWithoutQuotes(node.source), '@storybook/testing-library');
- if (hasDefaultImport(node.specifiers)) {
- const specifiers = getSpecifiers(node);
- if (specifiers) {
- const { range, text } = specifiers;
- yield fixer.replaceTextRange(range, fixSpecifiers(text));
- }
- }
- },
- },
- ],
- });
- }
- },
- };
- },
- });
|