337f6b9897
Дата старта (решение владельца 06.08.2026). Портал не передаёт кабинету МТС ни одной даты — их ставит кабинет своими умолчаниями. Живой прогон показал: старт оказался ЗАВТРАШНИМ, тогда как экран обещал клиенту показы «7 дней», подразумевая сегодня. Робот читает день начала из ТОЙ ЖЕ строки списка, куда и так ходит за вердиктом — ни одного лишнего захода в кабинет; портал хранит его в client_tg_campaigns.starts_on и показывает клиенту «Показы начнутся 7 августа». Проверено на ЖИВОМ кабинете: три задания подряд вернули startDate 2026-08-07 по кампании МТС 2237821. Мастер перестал молчать о том, что день начала ставит кабинет, а не мы. Ф-2, карточка приёмки Т-Ф4. У кампаний в движении видно «Проверяли 5 минут назад». Считаются только ЗАКОНЧЕННЫЕ проверки, включая неудачные: задание в очереди работой не является, а неудачная проверка — всё равно признак жизни. Именно в такой тишине владелец 36 часов не знал, что робот вообще не может войти в кабинет. Ф-3. Имена полей в ошибках формы по-русски: «Лимит на объявление не может быть меньше 1 ₽» вместо «Поле budget cap rub должно быть не меньше 1». Серая кнопка «Запустить» называет причину и шаг, куда вернуться, а не гаснет молча. Карточки Т-Р3 и Т-Р4 закрыты тестами (живьём не воспроизвести). Попутно найдено: обе защиты, стерегущие ЕДИНСТВЕННОЕ место траты живых денег роботом, были без единого теста. Сторожа доказаны вырезанием. Тесты: ClientTg 378 зелёных, экраны 2158, робот 197. Статанализ 0, стиль 0, типы чисто. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
135 lines
5.6 KiB
TypeScript
135 lines
5.6 KiB
TypeScript
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
||
import { mount, flushPromises } from '@vue/test-utils';
|
||
import { createVuetify } from 'vuetify';
|
||
|
||
/**
|
||
* Дата старта показов на экране клиента.
|
||
*
|
||
* 🔴 Откуда взялось. Портал не передаёт кабинету МТС ни одной даты — их ставит сам
|
||
* кабинет своими умолчаниями. Живьём 06.08.2026 (кампания 2237821) старт оказался
|
||
* ЗАВТРАШНИМ, а экран обещал показы «7 дней», подразумевая сегодня. Клиент ждёт
|
||
* рекламу сегодня, её нет, и объяснить нечем.
|
||
*
|
||
* Дату теперь привозит робот вместе с вердиктом модерации. Экран обязан её показать —
|
||
* и обязан МОЛЧАТЬ, пока даты нет: выдуманная дата хуже отсутствующей.
|
||
*/
|
||
vi.mock('vue-router', () => ({
|
||
useRouter: vi.fn(() => ({ push: vi.fn() })),
|
||
}));
|
||
|
||
const { fetchTelegramMock } = vi.hoisted(() => ({
|
||
fetchTelegramMock: vi.fn(),
|
||
}));
|
||
|
||
vi.mock('../../resources/js/api/telegram', () => ({
|
||
fetchTelegram: fetchTelegramMock,
|
||
createTelegram: vi.fn(),
|
||
fetchTelegramCampaign: vi.fn(() => Promise.resolve({ campaign: { id: 1 } })),
|
||
launchTelegram: vi.fn(),
|
||
resubmitTelegram: vi.fn(),
|
||
uploadTelegramMedia: vi.fn(),
|
||
estimateTelegram: vi.fn(() =>
|
||
Promise.resolve({ planned_count: 512, estimated_cost_rub: '430.08', min_count: 367, enough: true }),
|
||
),
|
||
uploadAutoRuleMedia: vi.fn(),
|
||
MEDIA_ACCEPT: 'image/jpeg,image/png,video/mp4',
|
||
MEDIA_TREBOVANIYA: 'Картинка: JPEG или PNG, до 25 МБ, от 640×360 до 5120×2880.',
|
||
MEDIA_CENA: 'С картинкой показ стоит дороже: 600 ₽ за тысячу, с видео — 680 ₽.',
|
||
fetchAutoRule: vi.fn(() =>
|
||
Promise.resolve({
|
||
enabled: false,
|
||
ad_text: '',
|
||
ad_link: '',
|
||
ad_headline: '',
|
||
media_path: null,
|
||
ord_category: 'Размещение рекламы',
|
||
budget_cap_rub: '0.00',
|
||
daily_limit_rub: '0.00',
|
||
batch_threshold: null,
|
||
}),
|
||
),
|
||
saveAutoRule: vi.fn(),
|
||
}));
|
||
|
||
import AdvertisingTelegramView from '../../resources/js/views/advertising/AdvertisingTelegramView.vue';
|
||
|
||
function otvet(kampaniya: Record<string, unknown>) {
|
||
return {
|
||
campaigns: [kampaniya],
|
||
sandbox: false,
|
||
balance_rub: '1000.00',
|
||
frozen_rub: '0.00',
|
||
};
|
||
}
|
||
|
||
const BAZA = {
|
||
id: 40,
|
||
ad_text: 'Приходите к нам',
|
||
ad_link: 'https://t.me/x',
|
||
planned_count: 400,
|
||
estimated_cost_rub: '268.80',
|
||
};
|
||
|
||
function smontirovat() {
|
||
return mount(AdvertisingTelegramView, { global: { plugins: [createVuetify()] } });
|
||
}
|
||
|
||
describe('дата старта показов на экране клиента', () => {
|
||
beforeEach(() => {
|
||
vi.clearAllMocks();
|
||
});
|
||
|
||
it('пока идёт модерация, видно с какого дня пойдут показы', async () => {
|
||
fetchTelegramMock.mockResolvedValue(
|
||
otvet({ ...BAZA, status: 'moderating', starts_on: '2026-08-07' }),
|
||
);
|
||
|
||
const wrapper = smontirovat();
|
||
await flushPromises();
|
||
|
||
const stroka = wrapper.find('[data-testid="tg-start-date-40"]');
|
||
expect(stroka.exists()).toBe(true);
|
||
// По-человечески, а не «2026-08-07»: заказчик не программист.
|
||
expect(stroka.text()).toContain('7 августа');
|
||
});
|
||
|
||
it('у запущенной кампании дата тоже видна', async () => {
|
||
fetchTelegramMock.mockResolvedValue(
|
||
otvet({ ...BAZA, status: 'launched', starts_on: '2026-12-31' }),
|
||
);
|
||
|
||
const wrapper = smontirovat();
|
||
await flushPromises();
|
||
|
||
expect(wrapper.find('[data-testid="tg-start-date-40"]').text()).toContain('31 декабря');
|
||
});
|
||
|
||
// 🪤 Робот ещё не сходил в кабинет — даты нет. Показать вместо неё «сегодня» или
|
||
// пустое место со словом «начнётся» значило бы соврать клиенту.
|
||
it('даты нет — строки нет вовсе, ничего не выдумываем', async () => {
|
||
fetchTelegramMock.mockResolvedValue(
|
||
otvet({ ...BAZA, status: 'moderating', starts_on: null }),
|
||
);
|
||
|
||
const wrapper = smontirovat();
|
||
await flushPromises();
|
||
|
||
expect(wrapper.find('[data-testid="tg-start-date-40"]').exists()).toBe(false);
|
||
// Подсказка про модерацию при этом никуда не делась.
|
||
expect(wrapper.find('[data-testid="tg-moderating-hint-40"]').exists()).toBe(true);
|
||
});
|
||
|
||
// 🪤 Старое поле могло приехать битым (кабинет сменил формат, чужая правка).
|
||
// Битую дату не показываем — но и экран из-за неё не роняем.
|
||
it('битая дата не показывается и экран не роняет', async () => {
|
||
fetchTelegramMock.mockResolvedValue(
|
||
otvet({ ...BAZA, status: 'moderating', starts_on: 'завтра' }),
|
||
);
|
||
|
||
const wrapper = smontirovat();
|
||
await flushPromises();
|
||
|
||
expect(wrapper.find('[data-testid="tg-start-date-40"]').exists()).toBe(false);
|
||
});
|
||
});
|