Как лучше спарсить стили из scss modules с Tailwind?
По вводным: написал небольшую форму на Next.Js для того, чтобы протестировать её всеми способами. Я использовал scss modules в связке с Tailwindcss. Это выглядит примерно вот так:
import { InputHTMLAttributes, ReactHTMLElement } from "react"; import styles from "./input.module.scss"; import { FieldError } from "react-hook-form"; import clsx from "clsx"; interface Props extends InputHTMLAttributes<HTMLInputElement> { label: string; error?: FieldError; validated?: boolean | undefined; props?: ReactHTMLElement<HTMLInputElement>; } export default function Input(allProps: Props) { const { label, error, validated, placeholder, ...props } = allProps; return ( <div className={styles.container} data-testid={`Input: ${label}`}> <input className={clsx( styles.input, error !== undefined && styles.error, validated === true && styles.success )} placeholder={error === undefined ? placeholder : error.message} {...props} data-testid={`Input-input: ${label}`} /> <h3 className={clsx( styles.label, error !== undefined && styles.error, validated === true && styles.success )} data-testid={`Input-label: ${label}`} > {label} </h3> </div> ); }
@reference '../../globals.css'; .container { @apply relative w-full; } .input { @apply w-full h-13 rounded-xl px-4 text-xl text-foreground/50 whitespace-nowrap text-ellipsis overflow-hidden; outline: 1px solid rgba(var(--foreground-rgb), 0.5); transition-duration: 200ms; &:focus { @apply outline-foreground text-foreground; &::placeholder { @apply text-transparent; } } &::placeholder { @apply text-2xl text-foreground/50 whitespace-nowrap text-ellipsis overflow-hidden; } } .error { @apply outline-red-600 text-red-600!; &::placeholder { @apply text-red-600/50; } &:focus { @apply text-red-500 outline-red-500; } } .success { @apply outline-green-600 text-green-600!; &::placeholder { @apply text-green-600/50; } &:focus { @apply text-green-500 outline-green-500; } } .label { @apply bg-background rounded-sm px-1 text-[14px] text-foreground absolute left-4 -top-5 opacity-0; transition-duration: 200ms; } .input:focus + .label { @apply opacity-100 -top-2.5; }
Сейчас ситуация такая: смесь @apply и обычных стилей связана с некоторыми проблемами, возникающими при билде некоторых tailwind классов. В любом случае, к вопросу это не относится, прошу не обращать внимание.
Сейчас ситуация такая: проблема заключается в том, что, как вы видите, в классе.label прямо прописано opacity-0, однако в тесте проверка на видимость и на свойство opacity возвращает ошибку. Тесты:
test("Input: focus", async () => { const input = render(<Input label="Test" placeholder="Test placeholder" />); const label = input.getByTestId(`Input-label: Test`); expect(label).toHaveStyle("opacity: 0"); });
test("Input: focus", async () => { const input = render(<Input label="Test" placeholder="Test placeholder" />); const label = input.getByTestId(`Input-label: Test`); expect(label).toBeVisible(); });
Сейчас ситуация такая: в обоих случаях тесты не проходят. Я пытался использовать transfer библиотеки jest-css-modules-transform, jest-scss-transform, jest-transform-scss. Да, последние 2 это разные библиотеки.
Ссылка на репозиторийСейчас ситуация такая:, однако тестов там нет, так как не получилось их сделать.
Опишите проблему, и специалист поможет с настройкой, исправлением ошибки или доработкой сайта. Подберём понятный план работ без лишней переписки.
Пока нет других ответов. Будьте первым, кто поможет автору.
Ответить на вопрос
Если цель - тестировать компонент, то парсить реальные SCSS modules и Tailwind-классы обычно не нужно. В unit/integration тестах проверяют поведение компонента: label, placeholder, error state, aria-атрибуты, наличие классов состояния, а не итоговый CSS после сборки. SCSS modules в Jest/Vitest обычно мокаются, чтобы styles.input возвращал предсказуемое имя класса.
Для Jest добавьте moduleNameMapper:
module.exports = { moduleNameMapper: { '\.module\.(css|scss)$': 'identity-obj-proxy' } };module.exports = { moduleNameMapper: { '\.module\.(css|scss)$': 'identity-obj-proxy' } };
Тогда в тесте можно проверить, что при error компонент получает error-класс, но не нужно разбирать SCSS:
render(<Input label="Email" error={{ message: 'Required' }} />); const input = screen.getByPlaceholderText(/email/i); expect(input).toHaveClass('input'); expect(input).toHaveClass('error');render(<Input label="Email" error={{ message: 'Required' }} />); const input = screen.getByPlaceholderText(/email/i); expect(input).toHaveClass('input'); expect(input).toHaveClass('error');
Если вы хотите проверить именно визуальные стили, это уже не задача unit-теста. Используйте Storybook + Playwright/Cypress component testing или визуальные snapshot-тесты. Там компонент собирается настоящим сборщиком, SCSS modules и Tailwind применяются реально, и вы проверяете внешний вид через браузер.
Для Tailwind в связке с SCSS modules важно понимать: Tailwind-классы, написанные через @apply в .module.scss, попадут в CSS только если файл проходит через PostCSS/Tailwind pipeline. В тестовой среде без сборки этого не будет.
Рабочая стратегия такая:
Если нужен smoke-тест, что CSS-класс реально существует, можно проверять собранный CSS после build, но это отдельная проверка сборки, а не тест Input-компонента. В вашем случае лучше не “спарсить стили”, а разделить поведенческие тесты и визуальные тесты.