Files
portal/app/tests/Frontend/WarmingBadges.spec.ts
T
Дмитрий 80c9ca7e67 feat(витрина B1,B2,B4,B5): летопись эпизодов прогрева + значки в воронке; уборка мёртвых скоупов
Кусок B «витрина прогрева» (спека 2026-07-21 §6), решение владельца — полная летопись:
- B1: таблица sales_ad_audience_warming_episodes + модель + WarmingEpisodeRecorder
  (open/close/record идемпотентно). Схема v8.85, бэкфилл из firm_channels(warming)
  и боевых СМС, guard по источнику. rls-reviewer OK 8/8.
- B2: «Греть»/«Убрать» на площадке открывают/закрывают эпизоды канала.
- B4: warmingByProspect считает значки из летописи (live/count вместо массива каналов),
  тип WarmingBadgeState в sales.ts.
- B5: единый компонент WarmingBadges.vue (идёт/грели раньше/×N) в канбане;
  осиротевший WarmingChannelIcons удалён.
Уборка: убраны мёртвые скоупы forYandex/Vk/Mts + их импорт + тест (боевых вызовов нет).
cspell: +5 пре-существующих слов CHANGELOG в словарь (apk/cvtjpq/hgq/sar/sca).

Проверено: Sales 427/427, composer stan 0, pint/prettier чисто, весь Vue-набор зелёный.
B3 (СМС→эпизод) — отдельным коммитом (СМС-блок правит и параллельная сессия).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-23 13:54:09 +03:00

55 lines
2.6 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { mount } from '@vue/test-utils';
import { createVuetify } from 'vuetify';
import { describe, it, expect } from 'vitest';
import WarmingBadges from '../../resources/js/components/sales/WarmingBadges.vue';
import type { WarmingBadgeState } from '../../resources/js/api/sales';
/**
* Единый компонент значков витрины прогрева (кусок B, B5). Объединяет бейдж
* состояния (Греется/Прогрет) и значки каналов с признаком «идёт/грели раньше»
* (data-live) и счётчиком «×N». Монтируем с реальным Vuetify — как WarmingChannelIcons.spec.
*/
const vuetify = createVuetify();
function mountBadges(warming: WarmingBadgeState) {
return mount(WarmingBadges, { props: { warming }, global: { plugins: [vuetify] } });
}
describe('WarmingBadges', () => {
it('живой канал: «Греется» и яркий значок (data-live=1)', () => {
const w = mountBadges({ active: true, channels: { yandex: { live: true, count: 1 } } });
expect(w.find('[data-testid="warming-state-badge"]').text()).toBe('Греется');
const y = w.find('[data-testid="warming-badge-yandex"]');
expect(y.exists()).toBe(true);
expect(y.attributes('data-live')).toBe('1');
});
it('грели раньше: «Прогрет» и серый значок (data-live=0)', () => {
const w = mountBadges({ active: false, channels: { yandex: { live: false, count: 1 } } });
expect(w.find('[data-testid="warming-state-badge"]').text()).toBe('Прогрет');
expect(w.find('[data-testid="warming-badge-yandex"]').attributes('data-live')).toBe('0');
});
it('несколько эпизодов канала → «×N» (СМС ×2)', () => {
const w = mountBadges({ active: false, channels: { sms: { live: false, count: 2 } } });
const s = w.find('[data-testid="warming-badge-sms"]');
expect(s.exists()).toBe(true);
expect(s.text()).toContain('×2');
});
it('единственный эпизод — счётчик не показываем', () => {
const w = mountBadges({ active: true, channels: { yandex: { live: true, count: 1 } } });
expect(w.find('[data-testid="warming-badge-yandex"]').text()).not.toContain('×');
});
it('пустые каналы — прочерк', () => {
const w = mountBadges({ active: false, channels: {} });
expect(w.find('[data-testid="warming-badges"]').text()).toContain('—');
});
});