Files
portal/bots/mts-telegram-ads/test/task.test.js
T
Дмитрий 32df332901 fix(телеграм-реклама): заголовок объявления для рекламы сайта — кампания больше не встаёт
Приёмка глазами вскрыла: кабинет МТС требует «Заголовок объявления» (до 40 знаков),
когда в объявлении ссылка на САЙТ, а не на телеграм-канал. Робот про это поле не знал,
«Продолжить» молча не срабатывало, кампания вставала на шаге «Объявление» — в бою уже
ПОСЛЕ списания денег. Проверено живьём: 2234454 (сайт — встала) против 2234462 (канал —
дошла до подтверждения) и 2234490 (сайт с заголовком — дошла).

Портал спрашивает заголовок заранее, на создании черновика: обязателен только для
не-телеграмной ссылки (App\Support\TelegramLink), колонка ad_headline varchar(40),
поле на экране появляется по той же развилке. Робот заполняет его в кабинете.

Три ловушки, добытые живыми прогонами (описаны в коде):
- поле дорисовывается в ОТВЕТ на ссылку, с задержкой — надо ждать, а не спрашивать;
- под описание подходит несколько элементов — нужен .first();
- серая надпись внутри поля НЕ placeholder, а нарисованная подпись: поиск по атрибуту
  давал ноль совпадений при видимом на снимке поле. Опознаём по видимой надписи.

Тесты: робот 130/130, ClientTg 250/250, экран 23/23. Полный прогон бэкенда — те же
13 падающих классов до и после правки (ни одного в телеграм-части). В baseline
статанализа добавлен известный ложный класс Pest для нового файла тестов.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-01 11:42:46 +03:00

74 lines
3.0 KiB
JavaScript

import { test } from 'node:test';
import assert from 'node:assert/strict';
import { parseTask } from '../src/task.js';
const base = {
phonesFile: 'phones.txt', adText: 'Короткий текст',
budgetRub: 1000, buttonUrl: 'https://site.ru',
mode: 'draft',
};
test('parseTask принимает корректное задание', () => {
const t = parseTask(base);
assert.equal(t.mode, 'draft');
assert.equal(t.budgetRub, 1000);
});
test('parseTask отвергает неизвестный режим', () => {
assert.throws(() => parseTask({ ...base, mode: 'xxx' }), /mode/);
});
test('parseTask требует phonesFile', () => {
const { phonesFile, ...rest } = base;
assert.throws(() => parseTask(rest), /phonesFile/);
});
test('parseTask отвергает пустой текст', () => {
assert.throws(() => parseTask({ ...base, adText: '' }), /adText/);
});
test('parseTask требует budgetRub > 0', () => {
assert.throws(() => parseTask({ ...base, budgetRub: 0 }), /budgetRub/);
});
test('parseTask подставляет дефолт ordCategory', () => {
const t = parseTask(base);
assert.equal(t.ordCategory, 'Размещение рекламы');
});
test('parseTask пробрасывает заданную ordCategory', () => {
const t = parseTask({ ...base, ordCategory: 'Другая категория' });
assert.equal(t.ordCategory, 'Другая категория');
});
test('parseTask НЕ требует adTitle/ordCompanyName (полей в кабинете нет)', () => {
// Задание без adTitle/ordCompanyName/cpmRub должно проходить.
const t = parseTask(base);
assert.equal(t.adTitle, undefined);
assert.equal(t.ordCompanyName, undefined);
assert.equal(t.cpmRub, undefined);
});
test('parseTask пробрасывает moderatorFile (документ модератору, пересдача 3.6)', () => {
const t = parseTask({ ...base, moderatorFile: 'C:/tmp/licenziya.pdf' });
assert.equal(t.moderatorFile, 'C:/tmp/licenziya.pdf');
});
test('parseTask: без moderatorFile → null (документ не приложили)', () => {
const t = parseTask(base);
assert.equal(t.moderatorFile, null);
});
// 31.07.2026, приёмка живьём: кабинет МТС показывает ОБЯЗАТЕЛЬНОЕ поле «Заголовок
// объявления» (до 40 знаков) и «Текст на кнопке», когда рекламируется САЙТ, а не
// телеграм-канал. Со ссылкой на сайт кампания вставала на шаге «Объявление».
test('parseTask пробрасывает adHeadline', () => {
const t = parseTask({ ...base, adHeadline: 'Лидерра' });
assert.equal(t.adHeadline, 'Лидерра');
});
test('parseTask: без заголовка → null (телеграм-ссылке он не нужен)', () => {
const t = parseTask(base);
assert.equal(t.adHeadline, null);
});