Как лучше спарсить стили из scss modules с Tailwind?

Ссылка скопирована
1 ответ

По вводным: написал небольшую форму на 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 это разные библиотеки.

Ссылка на репозиторийСейчас ситуация такая:, однако тестов там нет, так как не получилось их сделать.

Нужно решить такую задачу?

Опишите проблему, и специалист поможет с настройкой, исправлением ошибки или доработкой сайта. Подберём понятный план работ без лишней переписки.

Заказать помощь
Лучший ответ
1
Алексей Денисов Ответ

Если цель - тестировать компонент, то парсить реальные 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(&lt;Input label="Email" error={{ message: 'Required' }} /&gt;);
 
const input = screen.getByPlaceholderText(/email/i);
expect(input).toHaveClass('input');
expect(input).toHaveClass('error');

render(&lt;Input label="Email" error={{ message: 'Required' }} /&gt;); 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. В тестовой среде без сборки этого не будет.

Рабочая стратегия такая:

  • Unit-тесты: mock CSS modules, проверять DOM, aria, тексты, классы состояний.
  • Component/e2e-тесты: запускать в браузере и проверять реальный внешний вид.
  • Не парсить SCSS вручную в тестах, это хрупко и мало что доказывает.

Если нужен smoke-тест, что CSS-класс реально существует, можно проверять собранный CSS после build, но это отдельная проверка сборки, а не тест Input-компонента. В вашем случае лучше не “спарсить стили”, а разделить поведенческие тесты и визуальные тесты.

Другие ответы (0)

Пока нет других ответов. Будьте первым, кто поможет автору.

Ответить на вопрос

комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Вам также может быть интересно