$pages */ function fakePages(array $pages): PageFetcher { return new class($pages) implements PageFetcher { /** @param array $pages */ public function __construct(private array $pages) {} public function html(string $url): string { return $this->pages[$url] ?? ''; } }; } /** * Batch-загрузчик для теста: помнит, какими пачками звали htmlBatch (проверяем параллельный путь). * * @param array $pages */ function fakeBatchPages(array $pages): BatchPageFetcher { return new class($pages) implements BatchPageFetcher { /** @var list> */ public array $batchCalls = []; /** @param array $pages */ public function __construct(private array $pages) {} public function html(string $url): string { return $this->pages[$url] ?? ''; } public function htmlBatch(array $urls): array { $this->batchCalls[] = array_values($urls); $out = []; foreach ($urls as $u) { $out[$u] = $this->pages[$u] ?? ''; } return $out; } }; } it('обходит список 2ГИС и собирает карточки филиалов с адресом', function () { $fake = fakePages([ 'https://2gis.ru/krasnoyarsk/search/X' => 'ф1ф2', 'https://2gis.ru/krasnoyarsk/firm/111' => 'КрасЛомбард, улица Весны, 7, Красноярск — 2ГИСx', 'https://2gis.ru/krasnoyarsk/firm/222' => 'КрасЛомбард, проспект Мира, 10, Красноярск — 2ГИСx', ]); $cards = (new XfetchDirectoryFetcher($fake))->directory('https://2gis.ru/krasnoyarsk/search/X'); expect($cards)->toHaveCount(2); $byNum = collect($cards)->keyBy('number'); expect($byNum['+73912550000']->office)->toBe('улица Весны, 7'); expect($byNum['+73912550000']->source)->toBe('2ГИС'); expect($byNum['+73912920000']->office)->toBe('проспект Мира, 10'); }); it('повторяет загрузку списка 2ГИС при флаки-рендере без ссылок на филиалы', function () { // 1-й рендер списка — «ошибочная оболочка» без ссылок (флак); 2-й — нормальный список. $seq = new class implements PageFetcher { public int $listCalls = 0; public function html(string $url): string { if (str_contains($url, '/branches/') || str_contains($url, '/search/')) { $this->listCalls++; return $this->listCalls === 1 ? '
Подождите, идёт загрузка…
' // флак: ссылок на филиалы нет : 'ф1'; // повтор удачен } return 'КрасЛомбард, улица Весны, 7, Красноярск — 2ГИСx'; } }; $cards = (new XfetchDirectoryFetcher($seq))->directory('https://2gis.ru/krasnoyarsk/branches/985'); expect($cards)->toHaveCount(1); expect($seq->listCalls)->toBe(2); // список перезагрузили после флака }); it('карточки филиалов грузятся ОДНОЙ пачкой (параллельно) при batch-загрузчике', function () { $fake = fakeBatchPages([ 'https://2gis.ru/krasnoyarsk/search/X' => 'ф1ф2', 'https://2gis.ru/krasnoyarsk/firm/111' => 'КрасЛомбард, улица Весны, 7, Красноярск — 2ГИСx', 'https://2gis.ru/krasnoyarsk/firm/222' => 'КрасЛомбард, проспект Мира, 10, Красноярск — 2ГИСx', ]); $cards = (new XfetchDirectoryFetcher($fake))->directory('https://2gis.ru/krasnoyarsk/search/X'); // те же карточки, что и в последовательном пути expect($cards)->toHaveCount(2); // но забраны ОДНИМ пакетным вызовом (не по одной) expect($fake->batchCalls)->toHaveCount(1) ->and($fake->batchCalls[0])->toContain('https://2gis.ru/krasnoyarsk/firm/111') ->and($fake->batchCalls[0])->toContain('https://2gis.ru/krasnoyarsk/firm/222'); }); it('site() тянет HTML конкурента через тот же загрузчик', function () { $fake = fakePages(['https://k.ru' => 'x']); $site = (new XfetchDirectoryFetcher($fake))->site('https://k.ru'); expect($site->url)->toBe('https://k.ru') ->and($site->rawHtml)->toContain('tel:+73912920000'); }); // ── Яндекс.Карты роутинг ──────────────────────────────────────────────────── it('directory(): яндекс-URL обрабатывается как одиночная карточка (не список филиалов)', function () { $yandexUrl = 'https://yandex.ru/maps/org/avtodom24/8905494012/'; $yandexHtml = <<<'HTML' Автодом24, микрофинансовая организация, Красноярск, Караульная ул., 88 — Яндекс Карты
+7 (913) 539-65-46
HTML; $fake = fakePages([$yandexUrl => $yandexHtml]); $cards = (new XfetchDirectoryFetcher($fake))->directory($yandexUrl); // должна быть хотя бы одна карточка с телефоном 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'); // siteUrl тоже должен прийти expect($cards[0]->siteUrl)->toContain('automoney-krsk.ru'); expect($cards[0]->source)->toBe('Яндекс.Карты'); }); it('directory(): 2ГИС-URL по-прежнему идёт через parseBranchList (не сломан яндекс-роутингом)', function () { $fake = fakePages([ 'https://2gis.ru/krasnoyarsk/search/Y' => 'ф1', 'https://2gis.ru/krasnoyarsk/firm/333' => 'Ломбард, ул. Мира, 5, Красноярск — 2ГИСx', ]); $cards = (new XfetchDirectoryFetcher($fake))->directory('https://2gis.ru/krasnoyarsk/search/Y'); expect($cards)->toHaveCount(1); expect($cards[0]->number)->toBe('+73912000001'); expect($cards[0]->source)->toBe('2ГИС'); });