Car-Repairs-Shop/tests/Feature/EstimateModuleTest.php
sackey 5403c3591d
Some checks are pending
linter / quality (push) Waiting to run
tests / ci (push) Waiting to run
feat: Enhance job card workflow with diagnosis actions and technician assignment modal
- Added buttons for assigning diagnosis and starting diagnosis based on job card status in the job card view.
- Implemented a modal for assigning technicians for diagnosis, including form validation and technician selection.
- Updated routes to include a test route for job cards.
- Created a new Blade view for testing inspection inputs.
- Developed comprehensive feature tests for the estimate module, including creation, viewing, editing, and validation of estimates.
- Added tests for estimate model relationships and statistics calculations.
- Introduced a basic feature test for job cards index.
2025-08-15 08:37:45 +00:00

357 lines
12 KiB
PHP

<?php
namespace Tests\Feature;
use App\Livewire\Estimates\Create;
use App\Livewire\Estimates\Edit;
use App\Livewire\Estimates\Show;
use App\Models\Branch;
use App\Models\Customer;
use App\Models\Diagnosis;
use App\Models\Estimate;
use App\Models\JobCard;
use App\Models\User;
use App\Models\Vehicle;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Livewire\Livewire;
use Tests\TestCase;
class EstimateModuleTest extends TestCase
{
use RefreshDatabase;
private User $user;
private Branch $branch;
private Customer $customer;
private Vehicle $vehicle;
private JobCard $jobCard;
private Diagnosis $diagnosis;
protected function setUp(): void
{
parent::setUp();
// Create test data using factories
$this->user = User::factory()->create([
'role' => 'service_coordinator',
]);
// Give user required permissions for estimates
$permissions = [
['name' => 'service-orders.view', 'display_name' => 'View Service Orders', 'module' => 'service-orders'],
['name' => 'service-orders.create', 'display_name' => 'Create Service Orders', 'module' => 'service-orders'],
['name' => 'service-orders.update', 'display_name' => 'Update Service Orders', 'module' => 'service-orders'],
];
foreach ($permissions as $permissionData) {
$permission = \App\Models\Permission::firstOrCreate(
['name' => $permissionData['name']],
[
'display_name' => $permissionData['display_name'],
'description' => $permissionData['display_name'],
'module' => $permissionData['module'],
]
);
$this->user->permissions()->attach($permission);
}
$this->branch = Branch::create([
'name' => 'Main Branch',
'code' => 'MAIN',
'address' => '123 Test St',
'phone' => '555-0123',
'email' => 'main@test.com',
]);
$this->customer = Customer::factory()->create();
$this->vehicle = Vehicle::factory()->create([
'customer_id' => $this->customer->id,
]);
$this->jobCard = JobCard::factory()->create([
'customer_id' => $this->customer->id,
'vehicle_id' => $this->vehicle->id,
'branch_code' => $this->branch->code,
'status' => 'in_diagnosis',
'service_advisor_id' => $this->user->id,
]);
$this->diagnosis = Diagnosis::create([
'job_card_id' => $this->jobCard->id,
'service_coordinator_id' => $this->user->id,
'diagnostic_findings' => 'Worn brake pads detected',
'labor_operations' => [
[
'operation' => 'Replace front brake pads',
'estimated_hours' => 2.0,
'labor_rate' => 85.00,
],
],
'parts_required' => [
[
'part_name' => 'Front brake pads',
'part_number' => 'BP-TOY-001',
'quantity' => 1,
'estimated_cost' => 75.00,
],
],
'estimated_repair_time' => 2.5,
'diagnosis_status' => 'completed',
]);
$this->actingAs($this->user);
}
/** @test */
public function it_can_create_an_estimate_from_diagnosis()
{
Livewire::test(Create::class, ['diagnosis' => $this->diagnosis])
->assertSet('diagnosis.id', $this->diagnosis->id)
->assertViewHas('diagnosis')
->call('save')
->assertHasNoErrors()
->assertRedirect();
$this->assertDatabaseHas('estimates', [
'job_card_id' => $this->jobCard->id,
'diagnosis_id' => $this->diagnosis->id,
'prepared_by_id' => $this->user->id,
'status' => 'draft',
]);
// Check that line items were created
$estimate = Estimate::latest()->first();
$this->assertGreaterThan(0, $estimate->lineItems()->count());
// Check that totals were calculated
$this->assertGreaterThan(0, $estimate->total_amount);
}
/** @test */
public function it_calculates_totals_correctly()
{
$component = Livewire::test(Create::class, ['diagnosis' => $this->diagnosis]);
// Verify initial line items from diagnosis
$lineItems = $component->get('lineItems');
$this->assertNotEmpty($lineItems);
// Check that totals are calculated
$subtotal = $component->get('subtotal');
$taxAmount = $component->get('tax_amount');
$totalAmount = $component->get('total_amount');
$this->assertGreaterThan(0, $subtotal);
$this->assertGreaterThan(0, $taxAmount);
$this->assertGreaterThan(0, $totalAmount);
// Verify total calculation: subtotal + tax = total
$expectedTotal = $subtotal + $taxAmount;
$this->assertEquals($expectedTotal, $totalAmount);
}
/** @test */
public function it_can_add_and_remove_line_items()
{
$component = Livewire::test(Create::class, ['diagnosis' => $this->diagnosis]);
$initialCount = count($component->get('lineItems'));
// Add a line item
$component->call('addLineItem');
$this->assertCount($initialCount + 1, $component->get('lineItems'));
// Remove a line item
$component->call('removeLineItem', 0);
$this->assertCount($initialCount, $component->get('lineItems'));
}
/** @test */
public function it_validates_required_fields()
{
Livewire::test(Create::class, ['diagnosis' => $this->diagnosis])
->set('terms_and_conditions', '')
->set('validity_period_days', 0)
->set('tax_rate', -1)
->call('save')
->assertHasErrors([
'terms_and_conditions',
'validity_period_days',
'tax_rate',
]);
}
/** @test */
public function it_can_view_an_estimate()
{
$estimate = Estimate::create([
'estimate_number' => 'MAIN/EST0001',
'job_card_id' => $this->jobCard->id,
'diagnosis_id' => $this->diagnosis->id,
'prepared_by_id' => $this->user->id,
'labor_cost' => 170.00,
'parts_cost' => 90.00,
'subtotal' => 260.00,
'tax_rate' => 8.25,
'tax_amount' => 21.45,
'total_amount' => 281.45,
'validity_period_days' => 30,
'terms_and_conditions' => 'Test terms',
'status' => 'draft',
]);
Livewire::test(Show::class, ['estimate' => $estimate])
->assertSet('estimate.id', $estimate->id)
->assertSee($estimate->estimate_number)
->assertSee('$281.45')
->assertViewHas('estimate');
}
/** @test */
public function it_can_edit_an_estimate()
{
$estimate = Estimate::factory()->create([
'job_card_id' => $this->jobCard->id,
'terms_and_conditions' => 'Original terms',
'validity_period_days' => 30,
]);
// Test the component functionality directly without layout rendering
$component = Livewire::test(\App\Livewire\Estimates\Edit::class, ['estimate' => $estimate])
->set('terms_and_conditions', 'Updated terms and conditions')
->set('validity_period_days', 45);
// Assert the properties are set correctly
$component->assertSet('terms_and_conditions', 'Updated terms and conditions')
->assertSet('validity_period_days', 45);
// Call save method
$component->call('save')
->assertHasNoErrors();
$this->assertDatabaseHas('estimates', [
'id' => $estimate->id,
'terms_and_conditions' => 'Updated terms and conditions',
'validity_period_days' => 45,
]);
}
/** @test */
public function it_generates_unique_estimate_numbers()
{
$initialCount = Estimate::count();
// Create first estimate
Livewire::test(Create::class, ['diagnosis' => $this->diagnosis])
->call('save');
$this->assertEquals($initialCount + 1, Estimate::count());
$firstEstimate = Estimate::latest()->first();
// Create another diagnosis for second estimate
$diagnosis2 = Diagnosis::create([
'job_card_id' => $this->jobCard->id,
'service_coordinator_id' => $this->user->id,
'diagnostic_findings' => 'Different issue',
'diagnosis_status' => 'completed',
]);
// Create second estimate
Livewire::test(Create::class, ['diagnosis' => $diagnosis2])
->call('save');
$this->assertEquals($initialCount + 2, Estimate::count());
$estimates = Estimate::latest()->take(2)->get();
$secondEstimate = $estimates->first();
$firstEstimate = $estimates->last();
$this->assertNotEquals($firstEstimate->estimate_number, $secondEstimate->estimate_number);
$this->assertStringContainsString('MAIN/EST', $firstEstimate->estimate_number);
$this->assertStringContainsString('MAIN/EST', $secondEstimate->estimate_number);
}
/** @test */
public function it_updates_job_card_status_when_estimate_created()
{
$this->assertEquals('in_diagnosis', $this->jobCard->status);
Livewire::test(Create::class, ['diagnosis' => $this->diagnosis])
->call('save');
$this->jobCard->refresh();
$this->assertEquals('estimate_prepared', $this->jobCard->status);
}
/** @test */
public function estimate_routes_are_accessible()
{
// Skip permission checks for this test by using an admin role
$this->user->update(['role' => 'admin']);
$estimate = Estimate::create([
'estimate_number' => 'MAIN/EST0001',
'job_card_id' => $this->jobCard->id,
'diagnosis_id' => $this->diagnosis->id,
'prepared_by_id' => $this->user->id,
'labor_cost' => 170.00,
'parts_cost' => 90.00,
'subtotal' => 260.00,
'tax_rate' => 8.25,
'tax_amount' => 21.45,
'total_amount' => 281.45,
'validity_period_days' => 30,
'terms_and_conditions' => 'Test terms',
'status' => 'draft',
]);
// Test index route
$this->actingAs($this->user)
->get('/estimates')
->assertStatus(200);
// Test show route
$this->actingAs($this->user)
->get("/estimates/{$estimate->id}")
->assertStatus(200);
// Test edit route
$this->get("/estimates/{$estimate->id}/edit")
->assertStatus(200);
// Test create route
$this->get("/estimates/create/{$this->diagnosis->id}")
->assertStatus(200);
}
/** @test */
public function it_can_calculate_estimate_expiration()
{
$estimate = Estimate::create([
'estimate_number' => 'MAIN/EST0001',
'job_card_id' => $this->jobCard->id,
'diagnosis_id' => $this->diagnosis->id,
'prepared_by_id' => $this->user->id,
'labor_cost' => 170.00,
'parts_cost' => 90.00,
'subtotal' => 260.00,
'tax_rate' => 8.25,
'tax_amount' => 21.45,
'total_amount' => 281.45,
'validity_period_days' => 30,
'terms_and_conditions' => 'Test terms',
'status' => 'draft',
]);
$expectedValidUntil = $estimate->created_at->addDays(30);
$this->assertEquals($expectedValidUntil->format('Y-m-d'), $estimate->valid_until->format('Y-m-d'));
// Test if estimate is not expired (fresh estimate)
$this->assertFalse($estimate->is_expired);
}
}