@sberbusiness/triplex-next-mcp-server 0.24.0-beta1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (140) hide show
  1. package/README.md +124 -0
  2. package/bundle/.gitkeep +0 -0
  3. package/bundle/components/AbstractTree.md +198 -0
  4. package/bundle/components/AccordionBase.md +227 -0
  5. package/bundle/components/Alert.md +317 -0
  6. package/bundle/components/Amount.md +225 -0
  7. package/bundle/components/AmountField.md +289 -0
  8. package/bundle/components/Avatar.md +209 -0
  9. package/bundle/components/Badge.md +277 -0
  10. package/bundle/components/Body.md +167 -0
  11. package/bundle/components/Button.md +230 -0
  12. package/bundle/components/ButtonDropdown.md +159 -0
  13. package/bundle/components/ButtonIcon.md +122 -0
  14. package/bundle/components/Calendar.md +445 -0
  15. package/bundle/components/Card.md +374 -0
  16. package/bundle/components/CarouselExtended.md +250 -0
  17. package/bundle/components/Checkbox.md +126 -0
  18. package/bundle/components/CheckboxTree.md +280 -0
  19. package/bundle/components/CheckboxTreeExtended.md +321 -0
  20. package/bundle/components/CheckboxXGroup.md +68 -0
  21. package/bundle/components/CheckboxYGroup.md +67 -0
  22. package/bundle/components/Chip.md +404 -0
  23. package/bundle/components/ChipGroup.md +179 -0
  24. package/bundle/components/Col.md +221 -0
  25. package/bundle/components/CollapsableTree.md +255 -0
  26. package/bundle/components/Confirm.md +343 -0
  27. package/bundle/components/DateField.md +371 -0
  28. package/bundle/components/DatePickerExtended.md +342 -0
  29. package/bundle/components/DateRange.md +275 -0
  30. package/bundle/components/DesignTokens.md +385 -0
  31. package/bundle/components/Divider.md +196 -0
  32. package/bundle/components/DocumentNumberEdit.md +280 -0
  33. package/bundle/components/Dropdown.md +433 -0
  34. package/bundle/components/Ellipsis.md +208 -0
  35. package/bundle/components/EmptyView.md +99 -0
  36. package/bundle/components/ExpandAnimation.md +281 -0
  37. package/bundle/components/Footer.md +339 -0
  38. package/bundle/components/FormField.md +326 -0
  39. package/bundle/components/FormGroup.md +99 -0
  40. package/bundle/components/Gap.md +113 -0
  41. package/bundle/components/Header.md +280 -0
  42. package/bundle/components/HelpBox.md +154 -0
  43. package/bundle/components/IconWrapper.md +145 -0
  44. package/bundle/components/Island.md +197 -0
  45. package/bundle/components/IslandAccordion.md +230 -0
  46. package/bundle/components/IslandWidget.md +233 -0
  47. package/bundle/components/KeyDownListener.md +157 -0
  48. package/bundle/components/LightBox.md +322 -0
  49. package/bundle/components/Link.md +161 -0
  50. package/bundle/components/List.md +247 -0
  51. package/bundle/components/ListMaster.md +204 -0
  52. package/bundle/components/Loader.md +161 -0
  53. package/bundle/components/LoaderScreen.md +134 -0
  54. package/bundle/components/Marker.md +127 -0
  55. package/bundle/components/MarkerStatus.md +148 -0
  56. package/bundle/components/MediaWidth.md +169 -0
  57. package/bundle/components/MobileView.md +120 -0
  58. package/bundle/components/ModalWindow.md +308 -0
  59. package/bundle/components/MonthYearField.md +168 -0
  60. package/bundle/components/MultiselectField.md +213 -0
  61. package/bundle/components/Notification.md +233 -0
  62. package/bundle/components/NumberField.md +178 -0
  63. package/bundle/components/OrderedList.md +169 -0
  64. package/bundle/components/Overlay.md +237 -0
  65. package/bundle/components/Page.md +246 -0
  66. package/bundle/components/Pagination.md +298 -0
  67. package/bundle/components/Portal.md +119 -0
  68. package/bundle/components/Radio.md +174 -0
  69. package/bundle/components/Row.md +125 -0
  70. package/bundle/components/SMSField.md +160 -0
  71. package/bundle/components/SegmentedControl.md +235 -0
  72. package/bundle/components/SelectExtendedField.md +322 -0
  73. package/bundle/components/SelectField.md +213 -0
  74. package/bundle/components/Skeleton.md +129 -0
  75. package/bundle/components/Slider.md +157 -0
  76. package/bundle/components/SliderExtended.md +210 -0
  77. package/bundle/components/SliderRange.md +160 -0
  78. package/bundle/components/SmallInput.md +101 -0
  79. package/bundle/components/Spoiler.md +121 -0
  80. package/bundle/components/StatusTracker.md +151 -0
  81. package/bundle/components/Step.md +121 -0
  82. package/bundle/components/Stepper.md +163 -0
  83. package/bundle/components/Suggest.md +160 -0
  84. package/bundle/components/SuggestField.md +157 -0
  85. package/bundle/components/SwipeableArea.md +144 -0
  86. package/bundle/components/Table.md +199 -0
  87. package/bundle/components/Tabs.md +152 -0
  88. package/bundle/components/TabsExtended.md +273 -0
  89. package/bundle/components/TabsLine.md +189 -0
  90. package/bundle/components/Tag.md +135 -0
  91. package/bundle/components/TagColor.md +121 -0
  92. package/bundle/components/TagGroup.md +100 -0
  93. package/bundle/components/TextField.md +152 -0
  94. package/bundle/components/TextareaField.md +124 -0
  95. package/bundle/components/ThemeProvider.md +181 -0
  96. package/bundle/components/Tooltip.md +254 -0
  97. package/bundle/components/TopOverlay.md +145 -0
  98. package/bundle/components/TreeView.md +224 -0
  99. package/bundle/components/Triggers.md +121 -0
  100. package/bundle/guides/codestyle.md +205 -0
  101. package/bundle/guides/commits.md +166 -0
  102. package/bundle/guides/context.md +277 -0
  103. package/bundle/guides/stories.md +618 -0
  104. package/bundle/guides/template.md +154 -0
  105. package/bundle/guides/tests.md +301 -0
  106. package/bundle/icons.json +4156 -0
  107. package/bundle/manifest.json +796 -0
  108. package/bundle/release-notes/1.1.0.mdx +26 -0
  109. package/bundle/release-notes/1.1.1.mdx +14 -0
  110. package/bundle/release-notes/1.10.0.mdx +43 -0
  111. package/bundle/release-notes/1.11.0.mdx +19 -0
  112. package/bundle/release-notes/1.12.0.mdx +21 -0
  113. package/bundle/release-notes/1.13.0.mdx +43 -0
  114. package/bundle/release-notes/1.14.0.mdx +29 -0
  115. package/bundle/release-notes/1.15.0.mdx +56 -0
  116. package/bundle/release-notes/1.16.0.mdx +55 -0
  117. package/bundle/release-notes/1.17.0.mdx +15 -0
  118. package/bundle/release-notes/1.18.0.mdx +24 -0
  119. package/bundle/release-notes/1.19.0.mdx +48 -0
  120. package/bundle/release-notes/1.19.1.mdx +9 -0
  121. package/bundle/release-notes/1.2.0.mdx +80 -0
  122. package/bundle/release-notes/1.20.0.mdx +48 -0
  123. package/bundle/release-notes/1.21.0.mdx +66 -0
  124. package/bundle/release-notes/1.22.0.mdx +75 -0
  125. package/bundle/release-notes/1.23.0.mdx +50 -0
  126. package/bundle/release-notes/1.24.0.mdx +59 -0
  127. package/bundle/release-notes/1.25.0.mdx +52 -0
  128. package/bundle/release-notes/1.26.0.mdx +11 -0
  129. package/bundle/release-notes/1.3.0.mdx +21 -0
  130. package/bundle/release-notes/1.4.0.mdx +37 -0
  131. package/bundle/release-notes/1.5.0.mdx +24 -0
  132. package/bundle/release-notes/1.6.0.mdx +37 -0
  133. package/bundle/release-notes/1.6.1.mdx +11 -0
  134. package/bundle/release-notes/1.7.0.mdx +29 -0
  135. package/bundle/release-notes/1.8.0.mdx +38 -0
  136. package/bundle/release-notes/1.9.0.mdx +28 -0
  137. package/dist/index.d.ts +2 -0
  138. package/dist/index.js +324 -0
  139. package/dist/index.js.map +1 -0
  140. package/package.json +51 -0
@@ -0,0 +1,154 @@
1
+ ---
2
+ component: ComponentName
3
+ category: Category
4
+ figma-node: "NODE_ID"
5
+ figma-file: "https://www.figma.com/design/FILE_KEY/..."
6
+ related: []
7
+ tokens: []
8
+ stories: stories/Category/ComponentName.stories.tsx
9
+ version: "1.0"
10
+ ---
11
+
12
+ <!--
13
+ ИНСТРУКЦИЯ ДЛЯ ЗАПОЛНЕНИЯ (удали этот блок после заполнения)
14
+
15
+ Этот шаблон заполняется разработчиком вместе с AI-агентом после изучения компонента.
16
+ Цель: дать агенту точный контекст для будущих изменений компонента.
17
+
18
+ Не копируй TypeScript-типы целиком — они читаются из исходного кода.
19
+ Описывай намерения и ограничения, которые не видны из кода.
20
+
21
+ Frontmatter:
22
+ - figma-node: raw-значение параметра `node-id` из URL Figma, например `1-328`
23
+ - figma-file: URL файла / design-ссылка, из которой при необходимости можно получить file key
24
+ - tokens: список CSS-переменных, которые компонент использует
25
+ - related: список связанных компонентов (строки, имена)
26
+
27
+ Таблица Stories: колонка `Example file` обязательна — её читает сборщик
28
+ mcp-data.json, чтобы инлайнить код примера. См. раздел Stories ниже.
29
+ -->
30
+
31
+ # ComponentName
32
+
33
+ ## Назначение
34
+
35
+ <!-- Одно-два предложения: что делает компонент и когда его использовать. -->
36
+
37
+ Компонент для...
38
+
39
+ Используй когда: ...
40
+ Не используй когда: ...
41
+
42
+ ---
43
+
44
+ ## Варианты и props
45
+
46
+ <!-- Перечисли смысловые группы props. Не дублируй TypeScript — опиши намерения и ограничения. -->
47
+
48
+ ### Обязательные props
49
+
50
+ | Prop | Тип | Описание |
51
+ |---|---|---|
52
+ | `theme` | `EComponentTheme` | Визуальный стиль компонента |
53
+ | `size` | `EComponentSize` | Размер: SM (28px) / MD (40px) / LG (56px) |
54
+
55
+ ### Опциональные props
56
+
57
+ | Prop | Тип | По умолчанию | Описание |
58
+ |---|---|---|---|
59
+ | `disabled` | `boolean` | `false` | Блокирует взаимодействие |
60
+
61
+ ### Ограничения по темам
62
+
63
+ <!-- Если некоторые props недоступны для определённых тем — опиши здесь. -->
64
+
65
+ - `loading` недоступен для темы `LINK`
66
+
67
+ ---
68
+
69
+ ## Дизайн-токены
70
+
71
+ <!-- Только те CSS-переменные, которые непосредственно используются в стилях компонента. -->
72
+
73
+ ```
74
+ --triplex-next-ComponentName-Variant_Background_Default
75
+ --triplex-next-ComponentName-Variant_Color_Default
76
+ --triplex-next-ComponentName-Variant_Background_Hover
77
+ --triplex-next-ComponentName-Variant_Color_Hover
78
+ --triplex-next-ComponentName-Variant_Background_Active
79
+ --triplex-next-ComponentName-Variant_Color_Active
80
+ --triplex-next-ComponentName-Variant_Shadow_Focus
81
+ --triplex-next-ComponentName-Variant_Background_Disabled
82
+ --triplex-next-ComponentName-Variant_Color_Disabled
83
+ ```
84
+
85
+ ---
86
+
87
+ ## Инварианты
88
+
89
+ <!-- Что НЕЛЬЗЯ изменять без явного обсуждения с командой. -->
90
+
91
+ - `forwardRef` на компоненте — не убирать
92
+ - Prop `X` изменить нельзя — он часть публичного API с версии N
93
+ - CSS-класс `Y` используется в тестах — не переименовывать
94
+
95
+ ---
96
+
97
+ ## Accessibility
98
+
99
+ <!-- Заполни, если компонент имеет нетривиальное поведение с точки зрения доступности. -->
100
+
101
+ <!--
102
+ Примеры того, что стоит описать:
103
+ - Управление фокусом: при открытии фокус уходит в контент, при закрытии — возвращается на триггер
104
+ - ARIA-роль компонента (combobox, listbox, dialog, alertdialog, tooltip...)
105
+ - Обязательные aria-атрибуты (aria-expanded, aria-haspopup, aria-controls, aria-labelledby...)
106
+ - Keyboard navigation: какие клавиши обрабатываются и что делают
107
+ - aria-live region, если компонент динамически обновляет содержимое
108
+ - Какие aria-атрибуты потребитель ОБЯЗАН передать сам (например, aria-label на кнопке-иконке)
109
+ — компонент не хардкодит текст, библиотека мультиязычная
110
+ -->
111
+
112
+ ---
113
+
114
+ ## Связанные компоненты
115
+
116
+ <!-- Компоненты, которые часто используются вместе или являются вариантами. -->
117
+
118
+ - `RelatedComponent` — описание связи
119
+ - `AnotherComponent` — описание связи
120
+
121
+ ---
122
+
123
+ ## Stories
124
+
125
+ Основные истории: `stories/Category/ComponentName.stories.tsx`
126
+ Файлы примеров: `stories/Category/examples/ComponentName/`
127
+
128
+ <!--
129
+ Колонка `Example file` — имя файла внутри `stories/Category/examples/ComponentName/`.
130
+ Именно этот файл бандлится в mcp-data.json как код примера, поэтому ссылка обязательна.
131
+ Если у story нет отдельного example-файла (редкий случай) — поставь `—`.
132
+
133
+ Стори `Playground` и `VisualTests` в bundle НЕ попадают (скрипт generateMcpData
134
+ фильтрует их по имени): Playground — интерактивная песочница без показа кода,
135
+ VisualTests — скриншот-регрессия. Но в таблице их всё равно оставляй —
136
+ таблица описывает все stories компонента для человека, а не только то,
137
+ что уходит в MCP.
138
+ -->
139
+
140
+ | Story | Example file | Что демонстрирует |
141
+ |---|---|---|
142
+ | `Playground` | `PlaygroundExample.tsx` | Интерактивный контроль всех props |
143
+ | `Themes` | `ThemesExample.tsx` | Все визуальные темы |
144
+ | `Sizes` | `SizesExample.tsx` | Размеры SM / MD / LG |
145
+
146
+ ---
147
+
148
+ ## История изменений
149
+
150
+ <!-- Краткий лог значимых изменений компонента. Агент дописывает при каждом изменении. -->
151
+
152
+ | Дата | Изменение |
153
+ |---|---|
154
+ | YYYY-MM-DD | Создан документ |
@@ -0,0 +1,301 @@
1
+ # Тестирование — Triplex-Next
2
+
3
+ ---
4
+
5
+ ## Обзор: три уровня тестирования
6
+
7
+ | Уровень | Инструмент | Расположение | Когда запускать |
8
+ |---|---|---|---|
9
+ | Unit | Vitest + Testing Library | `src/components/**/__tests__/` | `npm run test-unit` |
10
+ | Visual regression | Storybook test-runner + jest-image-snapshot | `__screenshots__/` | CI (PR) |
11
+ | E2E | Playwright | `e2e/tests/` | `npm run test-e2e` |
12
+
13
+ ---
14
+
15
+ ## Unit-тесты (Vitest + Testing Library)
16
+
17
+ ### Когда писать
18
+
19
+ - **При изменении компонента:** добавить тест-кейс на новый функционал.
20
+ - **При создании нового компонента:** полное покрытие: все props, состояния, события, forwardRef.
21
+ - Баги исправляй с тестом, который воспроизводит баг.
22
+
23
+ ### Расположение
24
+
25
+ ```text
26
+ src/components/{ComponentName}/__tests__/{ComponentName}.test.tsx
27
+ ```
28
+
29
+ Предпочтительный путь для новых тестов — `__tests__/`.
30
+
31
+ В текущем репозитории уже встречаются и другие локальные паттерны:
32
+ - `__tests__/`
33
+ - `__test__/`
34
+ - `tests/`
35
+
36
+ Если у компонента уже используется локальный паттерн, следуй ему и не делай
37
+ отдельную миграцию структуры без задачи.
38
+
39
+ Один файл на компонент. Если компонент имеет субкомпоненты (ButtonIcon, ButtonDropdown), тесты субкомпонентов — в отдельных файлах той же локальной папки тестов.
40
+
41
+ ### Импорты
42
+
43
+ ```typescript
44
+ import React from "react";
45
+ import { render, screen, waitFor, fireEvent } from "@testing-library/react";
46
+ import userEvent from "@testing-library/user-event";
47
+ import { describe, it, expect, vi } from "vitest";
48
+ import { ComponentName } from "../ComponentName";
49
+ // Следуй локальному паттерну файла: для unit-тестов обычно используются локальные импорты
50
+ ```
51
+
52
+ **`userEvent` vs `fireEvent`:**
53
+ - Предпочитай `userEvent` — он точнее симулирует реальные события браузера (pointer events, keyboard, focus/blur цепочки).
54
+ - `fireEvent` допустим для простых случаев (триггер события без цепочки браузерных эффектов) или когда следуешь паттерну существующего файла теста.
55
+
56
+ ```typescript
57
+ // ✅ Предпочтительно для пользовательского взаимодействия
58
+ const user = userEvent.setup();
59
+ await user.click(screen.getByRole("button"));
60
+ await user.type(screen.getByRole("textbox"), "hello");
61
+ await user.keyboard("{Enter}");
62
+
63
+ // ✅ Допустимо для простых низкоуровневых событий
64
+ fireEvent.change(input, { target: { value: "hello" } });
65
+ ```
66
+
67
+ Правило:
68
+ - В unit-тестах предпочитай локальные импорты для компонента под тестом и ближайших внутренних типов/утилит.
69
+ - В репозитории есть и package-style тесты. Не делай отдельную миграцию import-style без задачи на cleanup.
70
+ - Если тест проверяет именно публичный entrypoint библиотеки, импорт через `@sberbusiness/triplex-next` допустим.
71
+
72
+ ### Структура
73
+
74
+ ```typescript
75
+ describe("ComponentName", () => {
76
+ // Хелпер для повторяющегося рендера
77
+ const renderComponent = (props = {}) =>
78
+ render(<ComponentName theme={ETheme.DEFAULT} {...props} />);
79
+
80
+ it("renders without errors", () => {
81
+ renderComponent();
82
+ expect(screen.getByRole("button")).toBeInTheDocument();
83
+ });
84
+
85
+ it("applies correct class for size prop", () => {
86
+ renderComponent({ size: EComponentSize.SM });
87
+ expect(screen.getByTestId("component-name")).toHaveClass("sm");
88
+ });
89
+
90
+ it("calls onChange on click", () => {
91
+ const onChange = vi.fn();
92
+ renderComponent({ onChange });
93
+ fireEvent.click(screen.getByRole("button"));
94
+ expect(onChange).toHaveBeenCalledTimes(1);
95
+ });
96
+
97
+ it("forwards ref to root element", () => {
98
+ const ref = React.createRef<HTMLButtonElement>();
99
+ render(<ComponentName theme={ETheme.DEFAULT} ref={ref} />);
100
+ expect(ref.current).toBeInstanceOf(HTMLButtonElement);
101
+ });
102
+
103
+ it("is disabled when disabled prop is set", () => {
104
+ renderComponent({ disabled: true });
105
+ expect(screen.getByRole("button")).toBeDisabled();
106
+ });
107
+ });
108
+ ```
109
+
110
+ ### Приоритет запросов (Testing Library)
111
+
112
+ Используй запросы в этом порядке:
113
+ 1. `getByRole` — семантические запросы (кнопки, инпуты, чекбоксы)
114
+ 2. `getByLabelText` — для полей с подписями
115
+ 3. `getByText` — для текстового содержимого
116
+ 4. `getByTestId` — только если нет семантической альтернативы
117
+
118
+ **Async-варианты:** используй `findBy*` или `waitFor` для элементов, которые появляются после асинхронной операции:
119
+
120
+ ```typescript
121
+ // ✅ findBy* — сокращение для waitFor + getBy*
122
+ const successMessage = await screen.findByRole("alert");
123
+
124
+ // ✅ waitFor — для проверки условия, которое станет истинным со временем
125
+ await waitFor(() => {
126
+ expect(screen.getByRole("button")).toBeEnabled();
127
+ });
128
+
129
+ // ❌ не используй фиксированные таймауты
130
+ await new Promise((r) => setTimeout(r, 500));
131
+ ```
132
+
133
+ ```typescript
134
+ // ✅ Предпочтительно
135
+ screen.getByRole("button", { name: "Submit" })
136
+ screen.getByRole("checkbox")
137
+ screen.getByLabelText("Email")
138
+
139
+ // ✅ Когда нет семантики
140
+ screen.getByTestId("loader-spinner")
141
+
142
+ // ❌ Избегать
143
+ container.querySelector(".button--loading") // тестирует реализацию
144
+ ```
145
+
146
+ ### Что тестировать
147
+
148
+ **Обязательно для каждого компонента:**
149
+ - [ ] Рендерится без ошибок с обязательными пропами
150
+ - [ ] `forwardRef` — ref передаётся в правильный DOM-элемент
151
+ - [ ] Все значения enum-props применяют корректный CSS-класс или атрибут
152
+ - [ ] `disabled` — компонент отключён и не вызывает обработчики
153
+ - [ ] Обработчики событий (onChange, onClick) вызываются корректно
154
+ - [ ] `className` пробрасывается в корневой элемент
155
+
156
+ **Для новых props (при изменении компонента):**
157
+ - [ ] Новый prop применяет ожидаемый класс / атрибут / поведение
158
+ - [ ] Пограничные значения (undefined, null, крайние значения enum)
159
+
160
+ **Не нужно тестировать:**
161
+ - Визуальное отображение (это задача visual regression тестов)
162
+ - Детали реализации LESS-классов, которые могут меняться
163
+ - Внутреннее состояние компонента (тестируй поведение, не state)
164
+
165
+ **Допустимо, когда это часть публичного состояния:**
166
+ - Проверять CSS-класс, если именно он кодирует публичный вариант или состояние (`size`, `theme`, `loading`, `expanded`)
167
+ - Проверять `aria-*`, `disabled`, `tabIndex`, текст и callback-контракт как часть публичного поведения
168
+
169
+ ### Минимальная проверка после изменений
170
+
171
+ - Правки только документации/конфигов тестов: проверь актуальность команд, путей и примеров, отдельный прогон тестов не обязателен.
172
+ - Правки компонента с изменением публичного поведения: предпочитай focused unit test на затронутый компонент.
173
+ - Правки багфикса: сначала добавь или обнови тест, который воспроизводит сценарий.
174
+ - Правки accessibility, focus management, keyboard navigation, dropdown/dialog behavior: тест обязателен, если поведение нетривиальное или уже ломалось раньше.
175
+ - Если локально не запускал релевантный тест, явно укажи это в результате.
176
+
177
+ ### Полезные матчеры (jest-dom)
178
+
179
+ ```typescript
180
+ expect(el).toBeInTheDocument()
181
+ expect(el).toBeVisible()
182
+ expect(el).toBeDisabled()
183
+ expect(el).toBeChecked()
184
+ expect(el).toHaveClass("sm")
185
+ expect(el).toHaveAttribute("aria-expanded", "true")
186
+ expect(el).toHaveTextContent("Submit")
187
+ expect(el).toHaveFocus()
188
+ ```
189
+
190
+ ### Мокирование
191
+
192
+ ```typescript
193
+ // Мок функции
194
+ const onChangeMock = vi.fn();
195
+
196
+ // Мок модуля
197
+ vi.mock("../utils", () => ({
198
+ formatDate: vi.fn(() => "01.01.2024"),
199
+ }));
200
+
201
+ // Очистка после теста (если нужно)
202
+ afterEach(() => {
203
+ vi.clearAllMocks();
204
+ });
205
+ ```
206
+
207
+ ---
208
+
209
+ ## Visual regression тесты (Storybook test-runner)
210
+
211
+ ### Как работает
212
+
213
+ - **Каждая story** автоматически снимается на двух viewport'ах: `xs` (575px) и `xl` (1200px).
214
+ - Скриншоты сравниваются с baseline в `__screenshots__/`.
215
+ - Baseline **только из Docker/CI** (Linux). Никогда не коммить скриншоты с локальной macOS.
216
+
217
+ ### Когда нужны Visual tests
218
+
219
+ Создавай отдельную `VisualTests` story если:
220
+ - Компонент требует взаимодействия перед скриншотом (открыть dropdown, hover, focus).
221
+ - Нужно покрыть состояние, которого нет в документационных stories.
222
+ - Нужно объединить несколько вариантов в один скриншот.
223
+
224
+ ### Обновление baseline
225
+
226
+ ```text
227
+ GitHub → Actions → "Update Visual Snapshots" → Run workflow → выбери ветку
228
+ ```
229
+
230
+ Workflow сам закоммитит обновлённые скриншоты в ветку.
231
+
232
+ ### Локальная проверка (опционально)
233
+
234
+ ```bash
235
+ npm run test-visual:docker # прогон через Docker
236
+ npm run test-visual:docker:update # обновление baseline через Docker
237
+ ```
238
+
239
+ ### Что делать при упавших visual тестах
240
+
241
+ 1. Проверить, ожидаемо ли изменение (намеренное изменение дизайна?).
242
+ 2. Если да — запустить `Update Visual Snapshots` workflow.
243
+ 3. Если нет — найти и исправить визуальную регрессию в коде.
244
+
245
+ ---
246
+
247
+ ## E2E тесты (Playwright)
248
+
249
+ ### Когда писать
250
+
251
+ - Критичные пользовательские флоу, затрагивающие несколько компонентов.
252
+ - Взаимодействия, которые нельзя протестировать в unit-тестах (например, drag-and-drop, clipboard).
253
+ - Не нужен для каждого компонента — только для сложных флоу.
254
+
255
+ ### Расположение
256
+
257
+ ```text
258
+ e2e/tests/{componentName}.spec.ts
259
+ ```
260
+
261
+ ### Структура
262
+
263
+ ```typescript
264
+ import { test, expect, Page } from "@playwright/test";
265
+
266
+ test.describe("ComponentName", () => {
267
+ test("should do something on user action", async ({ page }) => {
268
+ await page.goto("http://localhost:6006/iframe.html?id=category--story-name");
269
+
270
+ const element = page.getByRole("button", { name: "Submit" });
271
+ await expect(element).toBeVisible();
272
+ await element.click();
273
+
274
+ await expect(page.getByText("Success")).toBeVisible();
275
+ });
276
+ });
277
+ ```
278
+
279
+ ### Правила
280
+
281
+ - **Не используй фиксированные таймауты** (`page.waitForTimeout`) как основной способ синхронизации. Используй `waitFor`, `toBeVisible`, `toBeEnabled`.
282
+ - Исключение: короткий `waitForTimeout(100-300ms)` для ожидания CSS-анимации допустим, если иначе состояние надёжно не поймать. Такой таймаут должен быть коротким и обоснованным.
283
+ - Тесты должны быть **независимы** друг от друга (не полагаться на порядок выполнения).
284
+ - URL для теста — iframe Storybook конкретной story.
285
+ - Не переименовывай story export/name без синхронного обновления e2e-URL и visual baseline.
286
+
287
+ ---
288
+
289
+ ## Запуск тестов
290
+
291
+ ```bash
292
+ # Unit
293
+ npm run test-unit # одноразовый прогон
294
+ npm run test-unit:watch # watch-режим
295
+
296
+ # Visual (требует запущенного Storybook на :6006)
297
+ npm run test-visual:docker
298
+
299
+ # E2E (требует запущенного Storybook)
300
+ npm run test-e2e
301
+ ```