Files
portal/app/tests/Feature/AdminVisitorsTest.php
T

88 lines
3.1 KiB
PHP
Raw Normal View History

<?php
declare(strict_types=1);
use Illuminate\Foundation\Testing\DatabaseTransactions;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;
use Tests\Concerns\SharesAdminPdo;
use Tests\Concerns\SharesSupplierPdo;
uses(DatabaseTransactions::class);
uses(SharesSupplierPdo::class);
uses(SharesAdminPdo::class);
/** Гость + его шаги. */
function visitor(string $channel, bool $human, array $events, string $device = 'phone'): string
{
$id = (string) Str::uuid();
DB::table('site_visitors')->insert([
'id' => $id, 'first_seen_at' => now(), 'last_seen_at' => now(),
'channel' => $channel, 'device' => $device, 'is_human' => $human, 'is_datacenter' => false,
]);
foreach ($events as $e) {
DB::table('site_events')->insert([
'visitor_id' => $id, 'event' => $e, 'occurred_at' => now(), 'meta' => '{}',
]);
}
return $id;
}
beforeEach(function () {
DB::table('site_events')->delete();
DB::table('site_visitors')->delete();
});
test('воронка считает шаги и конверсии', function () {
visitor('sms', true, ['view', 'alive']);
visitor('sms', true, ['view', 'alive', 'cta_click', 'register_open', 'register_done', 'login_done']);
visitor('direct', false, ['view']); // браузер, но не человек
$r = $this->getJson('/api/admin/visitors/funnel?period=7d');
$r->assertStatus(200);
expect($r->json('funnel.view'))->toBe(3);
expect($r->json('funnel.alive'))->toBe(2);
expect($r->json('funnel.cta_click'))->toBe(1);
expect($r->json('funnel.register_done'))->toBe(1);
expect($r->json('funnel.login_done'))->toBe(1);
});
test('таблица каналов группирует людей по источнику', function () {
visitor('sms', true, ['view', 'alive']);
visitor('sms', true, ['view', 'alive', 'cta_click']);
visitor('hh', true, ['view', 'alive', 'cta_click', 'register_done']);
$r = $this->getJson('/api/admin/visitors/channels?period=7d');
$r->assertStatus(200);
$sms = collect($r->json('channels'))->firstWhere('channel', 'sms');
expect($sms['people'])->toBe(2);
expect($sms['cta_clicks'])->toBe(1);
expect($sms['registrations'])->toBe(0);
$hh = collect($r->json('channels'))->firstWhere('channel', 'hh');
expect($hh['registrations'])->toBe(1);
});
test('список визитов отдаёт цепочку шагов и пометку VPN', function () {
$id = visitor('direct', true, ['view', 'alive', 'cta_click']);
DB::table('site_visitors')->where('id', $id)->update([
'is_datacenter' => true, 'city' => 'Amsterdam', 'asn_org' => 'NordVPN', 'ip' => '1.2.3.4',
]);
$r = $this->getJson('/api/admin/visitors/visits?period=7d');
$r->assertStatus(200);
$v = $r->json('visits.0');
expect($v['is_datacenter'])->toBeTrue();
expect($v['steps'])->toContain('cta_click');
});
test('пустой период — нули, а не ошибка', function () {
$r = $this->getJson('/api/admin/visitors/funnel?period=today');
$r->assertStatus(200);
expect($r->json('funnel.view'))->toBe(0);
});