feat: strip FuelFinder to layout shell, wire sub-components
This commit is contained in:
@@ -1,223 +1,9 @@
|
||||
<?php
|
||||
|
||||
use App\Livewire\Public\FuelFinder;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use Livewire\Livewire;
|
||||
|
||||
uses(RefreshDatabase::class);
|
||||
|
||||
it('renders the fuel finder page', function () {
|
||||
it('renders the fuel finder shell', function () {
|
||||
Livewire::test(FuelFinder::class)
|
||||
->assertStatus(200)
|
||||
->assertSeeHtml('name="search"');
|
||||
});
|
||||
|
||||
it('has default property values', function () {
|
||||
Livewire::test(FuelFinder::class)
|
||||
->assertSet('search', '')
|
||||
->assertSet('fuelType', 'petrol')
|
||||
->assertSet('radius', 5)
|
||||
->assertSet('sort', 'reliable')
|
||||
->assertSet('results', [])
|
||||
->assertSet('meta', [])
|
||||
->assertSet('prediction', null)
|
||||
->assertSet('apiError', null)
|
||||
->assertSet('hasSearched', false);
|
||||
});
|
||||
|
||||
it('validates search is required', function () {
|
||||
Livewire::test(FuelFinder::class)
|
||||
->call('findStations')
|
||||
->assertHasErrors(['search' => 'required']);
|
||||
});
|
||||
|
||||
it('validates fuelType is required', function () {
|
||||
Livewire::test(FuelFinder::class)
|
||||
->set('search', 'SW1A 1AA')
|
||||
->set('fuelType', '')
|
||||
->call('findStations')
|
||||
->assertHasErrors(['fuelType' => 'required']);
|
||||
});
|
||||
|
||||
it('populates results, meta, and prediction on successful search', function () {
|
||||
Http::fake([
|
||||
'*/api/stations*' => Http::response([
|
||||
'data' => [
|
||||
[
|
||||
'station_id' => 'abc123',
|
||||
'name' => 'BP Garage',
|
||||
'brand' => 'BP',
|
||||
'is_supermarket' => false,
|
||||
'address' => '1 High Street',
|
||||
'postcode' => 'SW1A 1AA',
|
||||
'lat' => 51.5074,
|
||||
'lng' => -0.1278,
|
||||
'distance_km' => 1.5,
|
||||
'fuel_type' => 'e10',
|
||||
'price_pence' => 14390,
|
||||
'price' => 143.9,
|
||||
'price_updated_at' => '2026-04-05T08:00:00.000Z',
|
||||
'price_classification' => 'current',
|
||||
'price_classification_label' => 'Current',
|
||||
],
|
||||
],
|
||||
'meta' => [
|
||||
'count' => 1,
|
||||
'lowest_pence' => 14390,
|
||||
'avg_pence' => 14390.0,
|
||||
],
|
||||
], 200),
|
||||
'*/api/prediction*' => Http::response([
|
||||
'action' => 'fill_now',
|
||||
'confidence_score' => 80.0,
|
||||
'confidence_label' => 'high',
|
||||
'reasoning' => 'Prices rising.',
|
||||
'predicted_direction' => 'up',
|
||||
'predicted_change_pence' => 3.5,
|
||||
], 200),
|
||||
]);
|
||||
|
||||
Livewire::test(FuelFinder::class)
|
||||
->set('search', 'SW1A 1AA')
|
||||
->set('fuelType', 'petrol')
|
||||
->call('findStations')
|
||||
->assertSet('hasSearched', true)
|
||||
->assertSet('apiError', null)
|
||||
->assertSet('results', fn (array $r) => count($r) === 1 && $r[0]['name'] === 'BP Garage')
|
||||
->assertSet('meta', fn (array $m) => $m['count'] === 1)
|
||||
->assertSet('prediction', fn (?array $p) => $p !== null && $p['action'] === 'fill_now');
|
||||
});
|
||||
|
||||
it('sets apiError from 422 station response and leaves prediction null', function () {
|
||||
Http::fake([
|
||||
'*/api/stations*' => Http::response([
|
||||
'errors' => ['postcode' => ['Postcode not found.']],
|
||||
], 422),
|
||||
]);
|
||||
|
||||
Livewire::test(FuelFinder::class)
|
||||
->set('search', 'ZZ99 9ZZ')
|
||||
->set('fuelType', 'petrol')
|
||||
->call('findStations')
|
||||
->assertSet('results', [])
|
||||
->assertSet('meta', [])
|
||||
->assertSet('prediction', null)
|
||||
->assertSet('hasSearched', false)
|
||||
->assertSet('apiError', 'Postcode not found.');
|
||||
});
|
||||
|
||||
it('sets generic apiError on server error', function () {
|
||||
Http::fake([
|
||||
'*/api/stations*' => Http::response([], 500),
|
||||
]);
|
||||
|
||||
Livewire::test(FuelFinder::class)
|
||||
->set('search', 'SW1A 1AA')
|
||||
->set('fuelType', 'petrol')
|
||||
->call('findStations')
|
||||
->assertSet('apiError', 'Unable to fetch stations. Please try again.');
|
||||
});
|
||||
|
||||
it('converts radius from miles to km in the outgoing stations request', function () {
|
||||
Http::fake([
|
||||
'*/api/stations*' => Http::response(['data' => [], 'meta' => ['count' => 0]], 200),
|
||||
'*/api/prediction*' => Http::response(['action' => 'no_signal', 'confidence_score' => 0, 'confidence_label' => 'low', 'reasoning' => '', 'predicted_direction' => 'stable', 'predicted_change_pence' => 0], 200),
|
||||
]);
|
||||
|
||||
Livewire::test(FuelFinder::class)
|
||||
->set('search', 'SW1A 1AA')
|
||||
->set('fuelType', 'petrol')
|
||||
->set('radius', 5)
|
||||
->call('findStations');
|
||||
|
||||
Http::assertSent(function ($request) {
|
||||
if (! str_contains($request->url(), 'api/stations')) {
|
||||
return false;
|
||||
}
|
||||
$data = $request->data();
|
||||
|
||||
return isset($data['radius']) && abs((float) $data['radius'] - 8.05) < 0.01;
|
||||
});
|
||||
});
|
||||
|
||||
it('resets state before each new search', function () {
|
||||
Http::fake([
|
||||
'*/api/stations*' => Http::response(['data' => [], 'meta' => ['count' => 0]], 200),
|
||||
'*/api/prediction*' => Http::response(['action' => 'no_signal', 'confidence_score' => 0, 'confidence_label' => 'low', 'reasoning' => '', 'predicted_direction' => 'stable', 'predicted_change_pence' => 0], 200),
|
||||
]);
|
||||
|
||||
Livewire::test(FuelFinder::class)
|
||||
->set('search', 'SW1A 1AA')
|
||||
->set('fuelType', 'petrol')
|
||||
->set('results', [['name' => 'Old Result']])
|
||||
->set('apiError', 'Old error')
|
||||
->call('findStations')
|
||||
->assertSet('apiError', null)
|
||||
->assertSet('results', []);
|
||||
});
|
||||
|
||||
it('does not call findStations on updatedFuelType if not yet searched', function () {
|
||||
Http::fake();
|
||||
|
||||
Livewire::test(FuelFinder::class)
|
||||
->set('fuelType', 'diesel');
|
||||
|
||||
Http::assertNothingSent();
|
||||
});
|
||||
|
||||
it('re-runs findStations on updatedFuelType when already searched', function () {
|
||||
Http::fake([
|
||||
'*/api/stations*' => Http::response(['data' => [], 'meta' => ['count' => 0]], 200),
|
||||
'*/api/prediction*' => Http::response(['action' => 'no_signal', 'confidence_score' => 0, 'confidence_label' => 'low', 'reasoning' => '', 'predicted_direction' => 'stable', 'predicted_change_pence' => 0], 200),
|
||||
]);
|
||||
|
||||
Livewire::test(FuelFinder::class)
|
||||
->set('hasSearched', true)
|
||||
->set('search', 'SW1A 1AA')
|
||||
->set('fuelType', 'diesel');
|
||||
|
||||
Http::assertSentCount(2); // stations + prediction
|
||||
});
|
||||
|
||||
it('re-runs findStations on updatedRadius when already searched', function () {
|
||||
Http::fake([
|
||||
'*/api/stations*' => Http::response(['data' => [], 'meta' => ['count' => 0]], 200),
|
||||
'*/api/prediction*' => Http::response(['action' => 'no_signal', 'confidence_score' => 0, 'confidence_label' => 'low', 'reasoning' => '', 'predicted_direction' => 'stable', 'predicted_change_pence' => 0], 200),
|
||||
]);
|
||||
|
||||
Livewire::test(FuelFinder::class)
|
||||
->set('hasSearched', true)
|
||||
->set('search', 'SW1A 1AA')
|
||||
->set('radius', 10);
|
||||
|
||||
Http::assertSentCount(2);
|
||||
});
|
||||
|
||||
it('re-runs findStations on updatedSort when already searched', function () {
|
||||
Http::fake([
|
||||
'*/api/stations*' => Http::response(['data' => [], 'meta' => ['count' => 0]], 200),
|
||||
'*/api/prediction*' => Http::response(['action' => 'no_signal', 'confidence_score' => 0, 'confidence_label' => 'low', 'reasoning' => '', 'predicted_direction' => 'stable', 'predicted_change_pence' => 0], 200),
|
||||
]);
|
||||
|
||||
Livewire::test(FuelFinder::class)
|
||||
->set('hasSearched', true)
|
||||
->set('search', 'SW1A 1AA')
|
||||
->set('sort', 'price');
|
||||
|
||||
Http::assertSentCount(2);
|
||||
});
|
||||
|
||||
it('prediction remains null when prediction api fails', function () {
|
||||
Http::fake([
|
||||
'*/api/stations*' => Http::response(['data' => [], 'meta' => ['count' => 0]], 200),
|
||||
'*/api/prediction*' => Http::response([], 500),
|
||||
]);
|
||||
|
||||
Livewire::test(FuelFinder::class)
|
||||
->set('search', 'SW1A 1AA')
|
||||
->set('fuelType', 'petrol')
|
||||
->call('findStations')
|
||||
->assertSet('hasSearched', true)
|
||||
->assertSet('prediction', null);
|
||||
->assertStatus(200);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user