Ф1Ф2'
.'дубльне филиал';
$links = (new DirectoryParser)->parseBranchList($html);
expect($links)->toBe(['/krasnoyarsk/firm/111', '/krasnoyarsk/firm/222']);
});
it('берёт ссылки на филиалы, даже когда они в данных страницы, а не в href', function () {
// 2ГИС иногда рендерит карточки без чистого href — ссылка лежит в JSON-состоянии страницы
$html = '{"items":[{"link":"/krasnoyarsk/firm/111"},{"link":"/krasnoyarsk/firm/222"}]}'
.'дубль';
$links = (new DirectoryParser)->parseBranchList($html);
expect($links)->toBe(['/krasnoyarsk/firm/111', '/krasnoyarsk/firm/222']);
});
it('из карточки 2ГИС берёт телефон из tel: и адрес из заголовка', function () {
$html = '
КрасЛомбард, улица Ладо Кецховели, 30, Красноярск — 2ГИС'
.'КрасЛомбард
+7 (391) 2...';
$cards = (new DirectoryParser)->parseFirmCard($html, 'https://2gis.ru/krasnoyarsk/firm/111', '2ГИС');
expect($cards)->toHaveCount(1);
expect($cards[0]->number)->toBe('+73912920000');
expect($cards[0]->office)->toBe('улица Ладо Кецховели, 30');
expect($cards[0]->url)->toBe('https://2gis.ru/krasnoyarsk/firm/111');
expect($cards[0]->source)->toBe('2ГИС');
});
it('схлопывает неразрывные/двойные пробелы в адресе из заголовка', function () {
$html = "КрасЛомбард,\u{00A0}улица Весны,\u{00A0}7а, Красноярск — 2ГИС"
.'x';
$cards = (new DirectoryParser)->parseFirmCard($html, 'u', '2ГИС');
expect($cards[0]->office)->toBe('улица Весны, 7а');
});
// ── Яндекс.Карты ────────────────────────────────────────────────────────────
it('parseYandexCard: извлекает телефон из div orgpage-phones-view и сайт из business-urls-link', function () {
$html = <<<'HTML'
Автодом24, микрофинансовая организация, Красноярск, Караульная ул., 88 — Яндекс Карты
+7 (913) 539-65-46
HTML;
$cards = (new DirectoryParser)->parseYandexCard($html, 'https://yandex.ru/maps/org/avtodom24/8905494012/');
expect($cards)->not->toBeEmpty();
// Номер хранится в сыром виде «+7 (913) 539-65-46», проверяем по цифрам
$digitsAll = implode('', array_map(fn ($c) => preg_replace('/\D/', '', $c->number), $cards));
expect($digitsAll)->toContain('9135396546');
expect($cards[0]->siteUrl)->toContain('automoney-krsk.ru');
expect($cards[0]->source)->toBe('Яндекс.Карты');
});
it('parseYandexCard: дедуп телефонов — wa.me не дублирует основной номер', function () {
$html = <<<'HTML'
Автодом24, микрофинансовая организация, Красноярск, Красная ул., 1 — Яндекс Карты
+7 (913) 539-65-46
HTML;
$cards = (new DirectoryParser)->parseYandexCard($html, 'https://yandex.ru/maps/org/avtodom24/1/');
// wa.me не должен дублировать тот же номер
expect($cards)->toHaveCount(1);
expect($cards[0]->siteUrl)->toContain('automoney-krsk.ru');
});
it('parseYandexCard: если телефонов нет но сайт есть — одна карточка с пустым number', function () {
$html = <<<'HTML'
Тест, Красноярск — Яндекс Карты
HTML;
$cards = (new DirectoryParser)->parseYandexCard($html, 'https://yandex.ru/maps/org/test/1/');
expect($cards)->toHaveCount(1);
expect($cards[0]->number)->toBe('');
expect($cards[0]->siteUrl)->toContain('example.com');
expect($cards[0]->source)->toBe('Яндекс.Карты');
});
it('parseYandexCard: нет ни телефона, ни сайта — возвращает пустой массив', function () {
$html = 'Тест — Яндекс Картынет контактов
';
$cards = (new DirectoryParser)->parseYandexCard($html, 'https://yandex.ru/maps/org/test/2/');
expect($cards)->toBe([]);
});
it('parseYandexCard: адрес офиса берётся из заголовка страницы Яндекса', function () {
$html = <<<'HTML'
Автодом24, микрофинансовая организация, Красноярск, Караульная ул., 88 — Яндекс Карты
+7 (913) 539-65-46
HTML;
$cards = (new DirectoryParser)->parseYandexCard($html, 'https://yandex.ru/maps/org/avtodom24/1/');
expect($cards)->not->toBeEmpty();
// officeFromTitle должен отрезать «Автодом24» и «Яндекс Карты», оставить «микрофинансовая организация, Красноярск, Караульная ул., 88»
// Точнее: первый кусок = имя, последний = город; середина = адрес
expect($cards[0]->office)->not->toBeNull();
});
// ── 2ГИС «вскрыть всё»: сайт + все телефоны + описание из карточки ───────────
it('parse2gisCard: сайт берёт из поля url объекта-контакта website, НЕ из value-редиректа', function () {
// Контакты 2ГИС — JSON-массив объектов. У сайта настоящий адрес в "url",
// а в "value" — редирект link.2gis.ru (его брать нельзя).
$html = 'AutoMoney, Красноярск, Ленина, 1 — 2ГИС'
.'{"url":"http://automoney-krsk.ru","text":"automoney-krsk.ru","code":"70000001093406076","type":"website","value":"http://link.2gis.ru/redirect/xxx"}'
.'{"contact_text":"+7‒913‒539‒65‒46","type":"phone","value":"+79135396546"}';
$cards = (new DirectoryParser)->parse2gisCard($html, 'https://2gis.ru/krasnoyarsk/firm/111');
expect($cards)->not->toBeEmpty();
expect($cards[0]->siteUrl)->toContain('automoney-krsk.ru');
expect($cards[0]->siteUrl)->not->toContain('link.2gis.ru');
expect($cards[0]->source)->toBe('2ГИС');
});
it('parse2gisCard: берёт ВСЕ телефоны из value объектов phone, без дублей', function () {
$html = 'Тест, Красноярск — 2ГИС'
.'{"contact_text":"+7 391 279-33-79","type":"phone","value":"+73912793379"}'
.'{"contact_text":"+7 391 296-01-11","type":"phone","value":"+73912960111"}'
.'{"contact_text":"+7 391 240-00-68","type":"phone","value":"+73912400068"}'
.'{"type":"phone","value":"+73912793379"}'; // дубль первого
$cards = (new DirectoryParser)->parse2gisCard($html, 'https://2gis.ru/krasnoyarsk/firm/222');
$digits = array_map(fn ($c) => preg_replace('/\D/', '', $c->number), $cards);
expect($digits)->toContain('73912793379');
expect($digits)->toContain('73912960111');
expect($digits)->toContain('73912400068');
expect(array_unique($digits))->toHaveCount(3);
});
it('parse2gisCard: достаёт описание «чем занимается» из og:description', function () {
$html = 'AutoMoney, Красноярск — 2ГИС'
.''
.'{"contact_text":"+7 913 539-65-46","type":"phone","value":"+79135396546"}';
$cards = (new DirectoryParser)->parse2gisCard($html, 'https://2gis.ru/krasnoyarsk/firm/333');
expect($cards)->not->toBeEmpty();
expect($cards[0]->description)->toContain('Займы под залог');
});
it('parse2gisCard: НЕ берёт служебную заглушку 2ГИС в описание (оценка/фото/отзывы)', function () {
$html = 'AutoMoney, Красноярск — 2ГИС'
.''
.'{"contact_text":"+7 913 539-65-46","type":"phone","value":"+79135396546"}';
$cards = (new DirectoryParser)->parse2gisCard($html, 'https://2gis.ru/krasnoyarsk/firm/xx');
expect($cards)->not->toBeEmpty();
expect($cards[0]->description)->toBeNull(); // заглушку не пишем
});
it('parse2gisCard: рубрика из заголовка идёт в описание, когда своего описания нет', function () {
// og-описания нет → «чем занимается» берём из рубрики (2-й кусок заголовка): «Имя, РУБРИКА, адрес…, Город».
$html = 'Ломбарджини, ломбард, Взлётная улица, 6а, Красноярск — 2ГИС'
.'{"url":"http://lombardzhini.ru","text":"x","code":"1","type":"website"}';
$cards = (new DirectoryParser)->parse2gisCard($html, 'https://2gis.ru/krasnoyarsk/firm/1');
expect($cards[0]->description)->toBe('ломбард');
});
it('parse2gisCard: 2-й кусок-адрес НЕ считается рубрикой (описание пустое)', function () {
// «КрасЛомбард, улица Ладо Кецховели, 30, Красноярск» — 2-й кусок это АДРЕС, не рубрика → описания нет.
$html = 'КрасЛомбард, улица Ладо Кецховели, 30, Красноярск — 2ГИС'
.'{"url":"http://k.ru","text":"x","code":"1","type":"website"}';
$cards = (new DirectoryParser)->parse2gisCard($html, 'https://2gis.ru/krasnoyarsk/firm/2');
expect($cards[0]->description)->toBeNull();
});
it('parseYandexCard: рубрика из заголовка идёт в описание', function () {
$html = 'Твои Займы, микрофинансовая организация, Ястынская ул., 19А, Красноярск — Яндекс Карты'
.'';
$cards = (new DirectoryParser)->parseYandexCard($html, 'https://yandex.ru/maps/org/tvoi_zaymy/1/');
expect($cards[0]->description)->toBe('микрофинансовая организация');
});
it('parse2gisCard: мусор-домены из '
.''
.'{"url":"http://realsite.рф","text":"realsite.рф","code":"1","type":"website","value":"http://link.2gis.ru/y"}'
.'{"contact_text":"+7 391 200-00-00","type":"phone","value":"+73912000000"}';
$cards = (new DirectoryParser)->parse2gisCard($html, 'https://2gis.ru/krasnoyarsk/firm/444');
expect($cards[0]->siteUrl)->toContain('realsite');
expect($cards[0]->siteUrl)->not->toContain('top100');
expect($cards[0]->siteUrl)->not->toContain('mc.yandex');
});
it('parse2gisCard: телефонов нет, но сайт есть — одна карточка с пустым number', function () {
$html = 'Тест, Красноярск — 2ГИС'
.'{"url":"http://onlysite.ru","text":"onlysite.ru","code":"1","type":"website","value":"http://link.2gis.ru/z"}';
$cards = (new DirectoryParser)->parse2gisCard($html, 'https://2gis.ru/krasnoyarsk/firm/555');
expect($cards)->toHaveCount(1);
expect($cards[0]->number)->toBe('');
expect($cards[0]->siteUrl)->toContain('onlysite.ru');
});
it('parse2gisCard: нет ни телефона, ни сайта — пустой массив', function () {
$html = 'Тест — 2ГИСнет контактов
';
$cards = (new DirectoryParser)->parse2gisCard($html, 'https://2gis.ru/krasnoyarsk/firm/666');
expect($cards)->toBe([]);
});
it('parseYandexCard: достаёт ВСЕ три телефона из скрытого JSON, без дублей', function () {
// Яндекс показывает только первый телефон в видимом блоке,
// остальные прячет за раскрывашкой — но все три лежат в JSON на той же странице.
$html = <<<'HTML'
Автокредит24, Красноярск, Взлётная ул., 5 — Яндекс Карты
+7 (391) 279-33-79
HTML;
$cards = (new DirectoryParser)->parseYandexCard($html, 'https://yandex.ru/maps/org/autocredit24/1/');
// Должны получить ровно 3 карточки (по одной на телефон)
expect($cards)->toHaveCount(3);
// Проверяем по цифрам (без +, скобок и т.п.)
$allDigits = array_map(fn ($c) => preg_replace('/\D/', '', $c->number), $cards);
expect($allDigits)->toContain('73912793379');
expect($allDigits)->toContain('73912960111');
expect($allDigits)->toContain('73912400068');
// Нет дублей
expect(array_unique($allDigits))->toHaveCount(3);
// Сайт присутствует у каждой карточки
foreach ($cards as $card) {
expect($card->siteUrl)->toContain('autocredit24.ru');
}
});
it('parse2gisCard: НЕ берёт рекламный телефон-кнопку (caption/platforms), только настоящий контакт', function () {
// Живой случай: на карточке «Ломбард Бодрый» 2ГИС показал рекламный блок «Связаться с
// менеджером» — это кнопка-действие ЧУЖОЙ фирмы (Ломбард Айклауд). У неё caption/platforms и
// НЕТ code/print_text. Её номер брать нельзя: иначе чужой телефон попадает в контакты Бодрого
// и на «Предложениях» даёт ложное «объединить?».
$html = 'Ломбард Бодрый, ломбард, Красноярск — 2ГИС'
// настоящий контакт фирмы: с code (id) и print_text
.'{"text":"+7 (391) 296-78-88","code":"70000001085640830","print_text":"+7 (391) 296-78-88","type":"phone","value":"+73912967888"}'
// рекламная кнопка чужой фирмы: caption + platforms, без code/print_text
.'{"caption":"Связаться с менеджером","name":"Связаться с менеджером","platforms":["ios","android","online"],"type":"phone","value":"+79237575575"}';
$cards = (new DirectoryParser)->parse2gisCard($html, 'https://2gis.ru/krasnoyarsk/firm/70000001085640828');
$digits = array_map(fn ($c) => preg_replace('/\D/', '', $c->number), $cards);
expect($digits)->toContain('73912967888'); // настоящий телефон Бодрого — берём
expect($digits)->not->toContain('79237575575'); // рекламный номер Айклауда — НЕ берём
});