Files
ladill-pos/tests/Feature/CustomerDisplayTest.php
T
isaacclad 38b7f96714
Deploy Ladill POS / deploy (push) Successful in 56s
Add dual-screen customer display for the register.
Token-gated full-screen customer view shows order, payment QR, tips,
signature, receipt, and promo phases while the till keeps operator UI.
2026-07-15 15:35:44 +00:00

147 lines
4.6 KiB
PHP

<?php
namespace Tests\Feature;
use App\Http\Middleware\EnsurePlatformSession;
use App\Models\PosCustomerDisplay;
use App\Models\PosLocation;
use App\Models\User;
use App\Services\Pos\CustomerDisplayService;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;
class CustomerDisplayTest extends TestCase
{
use RefreshDatabase;
private function owner(): User
{
return User::create([
'public_id' => 'owner-'.uniqid(),
'name' => 'Owner',
'email' => uniqid().'@owner.example.com',
]);
}
private function locationFor(User $owner): PosLocation
{
return PosLocation::create([
'owner_ref' => $owner->public_id,
'name' => 'Front counter',
'currency' => 'GHS',
'is_default' => true,
]);
}
protected function setUp(): void
{
parent::setUp();
$this->withoutMiddleware(EnsurePlatformSession::class);
$this->withoutVite();
}
public function test_operator_can_push_cart_and_public_screen_reads_it(): void
{
$owner = $this->owner();
$this->locationFor($owner);
$this->actingAs($owner)
->postJson(route('pos.customer-display.push'), [
'phase' => 'cart',
'lines' => [
[
'name' => 'Blue Shirt',
'quantity' => 2,
'unit_price_minor' => 5000,
'discount_minor' => 500,
],
],
'discount_minor' => 0,
'loyalty' => [
'label' => 'Rewards',
'points_balance' => 120,
'points_earn' => 9,
],
])
->assertOk()
->assertJsonPath('phase', 'cart');
$display = PosCustomerDisplay::query()->first();
$this->assertNotNull($display);
$this->getJson(route('pos.customer-display.state', ['token' => $display->token]))
->assertOk()
->assertJsonPath('phase', 'cart')
->assertJsonPath('lines.0.name', 'Blue Shirt')
->assertJsonPath('total_minor', 9500)
->assertJsonPath('loyalty.points_balance', 120);
}
public function test_customer_can_submit_tip_and_signature_actions(): void
{
$owner = $this->owner();
$location = $this->locationFor($owner);
$display = app(CustomerDisplayService::class)->pushCart($location, [
['name' => 'Lunch', 'quantity' => 1, 'unit_price_minor' => 10000],
], [
'tip' => [
'enabled' => true,
'options_percent' => [10, 15, 20],
'custom_allowed' => true,
'base_minor' => 10000,
],
]);
app(CustomerDisplayService::class)->push($location, PosCustomerDisplay::PHASE_TIP, [
'lines' => $display->fresh()->payload['lines'] ?? [],
'total_minor' => 10000,
'tip' => [
'enabled' => true,
'options_percent' => [10, 15, 20],
'custom_allowed' => true,
'base_minor' => 10000,
],
]);
$token = $display->fresh()->token;
$this->postJson(route('pos.customer-display.action', ['token' => $token]), [
'type' => 'tip',
'tip_percent' => 15,
'tip_minor' => 1500,
])->assertOk()->assertJsonPath('ok', true);
$this->actingAs($owner)
->getJson(route('pos.customer-display.actions'))
->assertOk()
->assertJsonPath('action.type', 'tip')
->assertJsonPath('action.tip_minor', 1500);
$this->postJson(route('pos.customer-display.action', ['token' => $token]), [
'type' => 'signature',
'signature_data_url' => 'data:image/png;base64,abc',
])->assertOk();
$this->get(route('pos.customer-display.show', ['token' => $token]))
->assertOk()
->assertSee('Order summary', false);
}
public function test_invalid_token_is_not_found(): void
{
$this->get(route('pos.customer-display.show', ['token' => str_repeat('a', 48)]))
->assertNotFound();
}
public function test_operator_info_returns_display_url(): void
{
$owner = $this->owner();
$this->locationFor($owner);
$this->actingAs($owner)
->getJson(route('pos.customer-display.info'))
->assertOk()
->assertJsonStructure(['url', 'token', 'phase']);
}
}