major update

This commit is contained in:
Mark Gregory
2026-06-10 14:56:43 +01:00
parent 0c029ff803
commit 9b8d73de5a
47 changed files with 1712 additions and 62 deletions
@@ -0,0 +1,98 @@
<?php
namespace Tests\Browser\Concerns;
use App\Models\User;
use Modules\Platform\app\Models\Company;
use Modules\Platform\app\Models\LicenseKey;
use Modules\Platform\app\Models\Payment;
use Modules\Warehouse\app\Models\Warehouse;
use Modules\Warehouse\app\Models\WarehouseValidationRule;
trait CreatesWmsBrowserData
{
/**
* Create a normal tenant user with an active licence.
*/
protected function createLicensedUser(array $attributes = []): User
{
return User::factory()->create($attributes);
}
/**
* Create an MPBA platform administrator attached to the system tenant.
*/
protected function createPlatformAdmin(array $attributes = []): User
{
return User::factory()->mpbaPlatformAdmin()->create($attributes);
}
/**
* Create a warehouse for the user's company tenant.
*/
protected function createWarehouseFor(User $user, array $attributes = []): Warehouse
{
return Warehouse::query()->create([
'company_id' => $user->company_id,
'code' => $attributes['code'] ?? 'MAIN',
'name' => $attributes['name'] ?? 'Main Warehouse',
'status' => $attributes['status'] ?? 'active',
'address' => $attributes['address'] ?? 'Unit 1, Test Estate',
'timezone' => $attributes['timezone'] ?? 'Europe/London',
'settings' => $attributes['settings'] ?? [],
]);
}
/**
* Create a validation rule for a warehouse.
*/
protected function createWarehouseRule(Warehouse $warehouse, array $attributes = []): WarehouseValidationRule
{
return WarehouseValidationRule::query()->create([
'warehouse_id' => $warehouse->id,
'name' => $attributes['name'] ?? 'Location required',
'rule_key' => $attributes['rule_key'] ?? 'stock.location.required',
'severity' => $attributes['severity'] ?? 'error',
'is_active' => $attributes['is_active'] ?? true,
'configuration' => $attributes['configuration'] ?? ['required' => true],
]);
}
/**
* Create a non-system tenant company for admin screens.
*/
protected function createTenantCompany(array $attributes = []): Company
{
$company = Company::query()->create([
'name' => $attributes['name'] ?? 'Browser Tenant Ltd',
'slug' => $attributes['slug'] ?? 'browser-tenant-ltd',
'status' => $attributes['status'] ?? 'active',
'timezone' => $attributes['timezone'] ?? 'Europe/London',
'is_system_company' => $attributes['is_system_company'] ?? false,
'billing_email' => $attributes['billing_email'] ?? 'billing@browser-tenant.example',
'settings' => $attributes['settings'] ?? [],
]);
LicenseKey::issueFor($company, 'Browser Test Licence', 'enterprise', 100);
return $company;
}
/**
* Create a payment row for platform payment tables.
*/
protected function createPaymentFor(Company $company, array $attributes = []): Payment
{
return Payment::query()->create([
'company_id' => $company->id,
'stripe_payment_intent_id' => $attributes['stripe_payment_intent_id'] ?? null,
'stripe_invoice_id' => $attributes['stripe_invoice_id'] ?? null,
'amount' => $attributes['amount'] ?? 129900,
'currency' => $attributes['currency'] ?? 'gbp',
'status' => $attributes['status'] ?? 'paid',
'description' => $attributes['description'] ?? 'Annual platform licence',
'paid_at' => $attributes['paid_at'] ?? now(),
'metadata' => $attributes['metadata'] ?? ['source' => 'browser_test'],
]);
}
}
@@ -0,0 +1,46 @@
<?php
namespace Tests\Browser\Concerns;
use Closure;
use Laravel\Dusk\Browser;
trait TestsResponsiveBreakpoints
{
/**
* @return array<string, array{width:int, height:int}>
*/
protected function responsiveBreakpoints(): array
{
return [
'desktop' => ['width' => 1440, 'height' => 900],
'laptop' => ['width' => 1280, 'height' => 800],
'tablet_portrait' => ['width' => 768, 'height' => 1024],
'tablet_landscape' => ['width' => 1024, 'height' => 768],
'mobile' => ['width' => 390, 'height' => 844],
'small_mobile' => ['width' => 360, 'height' => 740],
];
}
/**
* Run a browser assertion across every supported WMS viewport.
*
* @param Closure(Browser, string): void $callback
*/
protected function eachBreakpoint(Browser $browser, Closure $callback): void
{
foreach ($this->responsiveBreakpoints() as $name => $size) {
$browser->resize($size['width'], $size['height']);
$callback($browser, $name);
$browser->screenshot("wms-responsive-{$name}");
}
}
/**
* Decide whether the breakpoint should use the off-canvas mobile navigation.
*/
protected function isMobileNavigationBreakpoint(string $breakpoint): bool
{
return in_array($breakpoint, ['tablet_portrait', 'mobile', 'small_mobile'], true);
}
}
+95
View File
@@ -0,0 +1,95 @@
<?php
namespace Tests\Browser;
use Laravel\Dusk\Browser;
use Tests\Browser\Concerns\CreatesWmsBrowserData;
use Tests\Browser\Concerns\TestsResponsiveBreakpoints;
use Tests\DuskTestCase;
class PlatformAdminUiTest extends DuskTestCase
{
use CreatesWmsBrowserData;
use TestsResponsiveBreakpoints;
public function test_platform_admin_pages_render_at_all_breakpoints(): void
{
$admin = $this->createPlatformAdmin();
$company = $this->createTenantCompany(['name' => 'Admin Browser Co', 'slug' => 'admin-browser-co']);
$this->createWarehouseFor($admin, ['name' => 'System Warehouse', 'code' => 'SYS']);
$this->createPaymentFor($company, ['description' => 'Browser payment']);
$pages = [
['route' => 'platform.dashboard', 'heading' => 'Platform administration', 'expected' => ['Companies', 'Warehouses', 'Licences']],
['route' => 'platform.companies.index', 'heading' => 'Companies', 'expected' => ['Admin Browser Co', 'Licences']],
['route' => 'platform.licenses.index', 'heading' => 'Licence keys', 'expected' => ['Browser Test Licence', 'enterprise']],
['route' => 'platform.payments.index', 'heading' => 'Payments and overrides', 'expected' => ['Browser payment', 'GBP']],
];
$this->browse(function (Browser $browser) use ($admin, $pages): void {
$browser->loginAs($admin);
foreach ($pages as $page) {
$browser->visitRoute($page['route']);
$this->eachBreakpoint($browser, function (Browser $browser, string $breakpoint) use ($page): void {
$browser->assertSee($page['heading']);
foreach ($page['expected'] as $text) {
$browser->assertSee($text);
}
if ($this->isMobileNavigationBreakpoint($breakpoint)) {
$browser->assertVisible('[data-wms-mobile-open]');
} else {
$browser->assertVisible('[data-wms-sidebar]');
}
});
}
});
}
public function test_platform_admin_can_create_company_licence_and_payment_from_ui(): void
{
$admin = $this->createPlatformAdmin();
$this->browse(function (Browser $browser) use ($admin): void {
$browser
->loginAs($admin)
->visitRoute('platform.companies.index')
->type('name', 'UI Created Company')
->type('billing_email', 'accounts@ui-created.example')
->select('status', 'active')
->press('Create')
->waitForText('UI Created Company')
->assertSee('UI Created Company');
$companyId = (string) \Modules\Platform\app\Models\Company::query()
->where('name', 'UI Created Company')
->value('id');
$browser
->visitRoute('platform.licenses.index')
->select('company_id', $companyId)
->type('label', 'UI Enterprise Licence')
->select('plan', 'enterprise')
->type('max_users', '250')
->press('Issue')
->waitForText('New key:')
->assertSee('UI Enterprise Licence')
->assertSee('enterprise');
$browser
->visitRoute('platform.payments.index')
->select('company_id', $companyId)
->type('amount', '159900')
->type('currency', 'gbp')
->select('status', 'paid')
->type('description', 'UI manual payment')
->press('Add')
->waitForText('UI manual payment')
->assertSee('GBP 1,599.00')
->assertSee('paid');
});
}
}
+87
View File
@@ -0,0 +1,87 @@
<?php
namespace Tests\Browser;
use Laravel\Dusk\Browser;
use Tests\Browser\Concerns\CreatesWmsBrowserData;
use Tests\Browser\Concerns\TestsResponsiveBreakpoints;
use Tests\DuskTestCase;
class WarehouseUiTest extends DuskTestCase
{
use CreatesWmsBrowserData;
use TestsResponsiveBreakpoints;
public function test_dashboard_warehouse_and_rules_pages_render_at_all_breakpoints(): void
{
$user = $this->createLicensedUser(['name' => 'Tenant User']);
$warehouse = $this->createWarehouseFor($user, ['name' => 'North Warehouse', 'code' => 'NORTH']);
$this->createWarehouseRule($warehouse, ['name' => 'Batch number required']);
$pages = [
['route' => 'dashboard', 'heading' => $user->company->name, 'expected' => ['Warehouses', 'Validation rules']],
['route' => 'warehouses.index', 'heading' => 'Warehouses', 'expected' => ['North Warehouse', 'NORTH']],
['route' => 'warehouse-rules.index', 'heading' => 'Warehouse validation rules', 'expected' => ['Batch number required', 'stock.location.required']],
];
$this->browse(function (Browser $browser) use ($user, $pages): void {
$browser->loginAs($user);
foreach ($pages as $page) {
$browser->visitRoute($page['route']);
$this->eachBreakpoint($browser, function (Browser $browser, string $breakpoint) use ($page): void {
$browser->assertSee($page['heading']);
foreach ($page['expected'] as $text) {
$browser->assertSee($text);
}
if ($this->isMobileNavigationBreakpoint($breakpoint)) {
$browser->assertVisible('[data-wms-mobile-open]');
} else {
$browser->assertVisible('[data-wms-sidebar]');
}
});
}
});
}
public function test_user_can_create_a_warehouse_from_the_ui(): void
{
$user = $this->createLicensedUser();
$this->browse(function (Browser $browser) use ($user): void {
$browser
->loginAs($user)
->visitRoute('warehouses.index')
->type('code', 'PICK')
->type('name', 'Picking Warehouse')
->select('status', 'active')
->press('Create')
->waitForText('Picking Warehouse')
->assertSee('PICK')
->assertSee('Picking Warehouse');
});
}
public function test_user_can_create_a_validation_rule_from_the_ui(): void
{
$user = $this->createLicensedUser();
$warehouse = $this->createWarehouseFor($user, ['name' => 'Rules Warehouse', 'code' => 'RULES']);
$this->browse(function (Browser $browser) use ($user, $warehouse): void {
$browser
->loginAs($user)
->visitRoute('warehouse-rules.index')
->select('warehouse_id', (string) $warehouse->id)
->type('name', 'Pallet scan required')
->type('rule_key', 'pallet.scan.required')
->select('severity', 'warning')
->press('Add')
->waitForText('Pallet scan required')
->assertSee('pallet.scan.required')
->assertSee('warning');
});
}
}
+104
View File
@@ -0,0 +1,104 @@
<?php
namespace Tests\Browser;
use Laravel\Dusk\Browser;
use Tests\Browser\Concerns\CreatesWmsBrowserData;
use Tests\Browser\Concerns\TestsResponsiveBreakpoints;
use Tests\DuskTestCase;
class WmsResponsiveShellTest extends DuskTestCase
{
use CreatesWmsBrowserData;
use TestsResponsiveBreakpoints;
public function test_shell_renders_correct_desktop_tablet_and_mobile_navigation(): void
{
$user = $this->createPlatformAdmin([
'name' => 'Mark Gregory',
'email' => 'mark.gregory@example.test',
]);
$this->browse(function (Browser $browser) use ($user): void {
$browser->loginAs($user)->visitRoute('dashboard');
$this->eachBreakpoint($browser, function (Browser $browser, string $breakpoint): void {
$browser
->assertPresent('[data-wms-shell]')
->assertPresent('[data-wms-sidebar]')
->assertSee('Warehouse Management System')
->assertSee('MPBA')
->assertSee('Warehouses')
->assertSee('Validation Rules')
->assertSee('Platform Admin')
->assertSee('Mark Gregory');
if ($this->isMobileNavigationBreakpoint($breakpoint)) {
$browser
->assertVisible('[data-wms-mobile-open]')
->assertScript("document.documentElement.dataset.wmsMobileSidebar || 'closed'", 'closed')
->click('[data-wms-mobile-open]')
->waitUntil("document.documentElement.dataset.wmsMobileSidebar === 'open'")
->assertScript("document.documentElement.dataset.wmsMobileSidebar", 'open')
->click('[data-wms-mobile-close]')
->waitUntil("document.documentElement.dataset.wmsMobileSidebar === 'closed'")
->assertScript("document.documentElement.dataset.wmsMobileSidebar", 'closed');
} else {
$browser->assertVisible('[data-wms-sidebar-toggle]');
}
});
});
}
public function test_desktop_sidebar_collapses_expands_and_persists_state(): void
{
$user = $this->createPlatformAdmin();
$this->browse(function (Browser $browser) use ($user): void {
$browser
->resize(1440, 900)
->loginAs($user)
->visitRoute('dashboard')
->script("localStorage.removeItem('wms-sidebar-state'); document.documentElement.dataset.wmsSidebar = 'expanded';");
$browser
->refresh()
->waitFor('[data-wms-sidebar-toggle]')
->assertScript("document.documentElement.dataset.wmsSidebar", 'expanded')
->click('[data-wms-sidebar-toggle]')
->waitUntil("document.documentElement.dataset.wmsSidebar === 'collapsed'")
->assertScript("localStorage.getItem('wms-sidebar-state')", 'collapsed')
->assertAttribute('[data-wms-sidebar-toggle]', 'aria-expanded', 'false')
->refresh()
->waitFor('[data-wms-sidebar-toggle]')
->assertScript("document.documentElement.dataset.wmsSidebar", 'collapsed')
->click('[data-wms-sidebar-toggle]')
->waitUntil("document.documentElement.dataset.wmsSidebar === 'expanded'")
->assertScript("localStorage.getItem('wms-sidebar-state')", 'expanded')
->assertAttribute('[data-wms-sidebar-toggle]', 'aria-expanded', 'true');
});
}
public function test_mobile_overlay_and_navigation_link_close_the_menu(): void
{
$user = $this->createPlatformAdmin();
$this->browse(function (Browser $browser) use ($user): void {
$browser
->resize(390, 844)
->loginAs($user)
->visitRoute('dashboard')
->waitFor('[data-wms-mobile-open]')
->click('[data-wms-mobile-open]')
->waitUntil("document.documentElement.dataset.wmsMobileSidebar === 'open'")
->click('[data-wms-mobile-close]')
->waitUntil("document.documentElement.dataset.wmsMobileSidebar === 'closed'")
->click('[data-wms-mobile-open]')
->waitUntil("document.documentElement.dataset.wmsMobileSidebar === 'open'")
->clickLink('Warehouses')
->waitForLocation('/warehouses')
->waitUntil("document.documentElement.dataset.wmsMobileSidebar === 'closed'")
->assertSee('Warehouses');
});
}
}
+10 -19
View File
@@ -5,21 +5,16 @@ namespace Tests;
use Facebook\WebDriver\Chrome\ChromeOptions;
use Facebook\WebDriver\Remote\DesiredCapabilities;
use Facebook\WebDriver\Remote\RemoteWebDriver;
use Illuminate\Support\Collection;
use Laravel\Dusk\TestCase as BaseTestCase;
use PHPUnit\Framework\Attributes\BeforeClass;
abstract class DuskTestCase extends BaseTestCase
{
/**
* Prepare for Dusk test execution.
*/
#[BeforeClass]
public static function prepare(): void
{
if (! static::runningInSail()) {
static::startChromeDriver(['--port=9515']);
}
static::startChromeDriver();
}
/**
@@ -27,22 +22,18 @@ abstract class DuskTestCase extends BaseTestCase
*/
protected function driver(): RemoteWebDriver
{
$options = (new ChromeOptions)->addArguments(collect([
$this->shouldStartMaximized() ? '--start-maximized' : '--window-size=1920,1080',
'--disable-search-engine-choice-screen',
'--disable-smooth-scrolling',
])->unless($this->hasHeadlessDisabled(), function (Collection $items) {
return $items->merge([
'--disable-gpu',
'--headless=new',
]);
})->all());
$options = (new ChromeOptions)->addArguments(array_filter([
'--disable-gpu',
'--headless=new',
'--window-size=1440,900',
'--no-sandbox',
'--disable-dev-shm-usage',
'--ignore-certificate-errors',
]));
return RemoteWebDriver::create(
$_ENV['DUSK_DRIVER_URL'] ?? env('DUSK_DRIVER_URL') ?? 'http://localhost:9515',
DesiredCapabilities::chrome()->setCapability(
ChromeOptions::CAPABILITY, $options
)
DesiredCapabilities::chrome()->setCapability(ChromeOptions::CAPABILITY, $options)
);
}
}
@@ -0,0 +1,65 @@
<?php
namespace Tests\Feature\Modules;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;
class ModulePlaceholderPageTest extends TestCase
{
use RefreshDatabase;
/**
* @return array<string, array{route:string, heading:string, expected:array<int,string>}>
*/
public static function modulePages(): array
{
return [
'analytics' => ['route' => 'analytics.index', 'heading' => 'Analytics', 'expected' => ['KPI1 Bar', 'OTTC', 'Warehouse Performance']],
'audit' => ['route' => 'audit.index', 'heading' => 'Audit', 'expected' => ['Audit Trail', 'User Activity', 'Stock Adjustments']],
'carrier-management' => ['route' => 'carrier-management.index', 'heading' => 'Carrier Management', 'expected' => ['Carrier Master', 'Service Levels']],
'configuration' => ['route' => 'configuration.index', 'heading' => 'Configuration', 'expected' => ['System Parameters', 'Reference Data']],
'document-archive' => ['route' => 'document-archive.index', 'heading' => 'Document Archive', 'expected' => ['POD Archive', 'Document Lookup']],
'inventory-management' => ['route' => 'inventory-management.index', 'heading' => 'Inventory Management', 'expected' => ['Stock Enquiry', 'Stock Adjustments']],
'm3-integration' => ['route' => 'm3-integration.index', 'heading' => 'M3 Integration', 'expected' => ['Inbound Interfaces', 'Outbound Interfaces']],
'replenishment' => ['route' => 'replenishment.index', 'heading' => 'Replenishment', 'expected' => ['Shortage Monitor', 'Replenishment Tasks']],
'reporting' => ['route' => 'reporting.index', 'heading' => 'Reporting', 'expected' => ['Operational Reports', 'Export Packs']],
'security' => ['route' => 'security.index', 'heading' => 'Security', 'expected' => ['Users', 'Roles', 'Permissions']],
'sentinel-integration' => ['route' => 'sentinel-integration.index', 'heading' => 'Sentinel Integration', 'expected' => ['Sentinel Sync', 'Message Queue']],
'shipping' => ['route' => 'shipping.index', 'heading' => 'Shipping', 'expected' => ['Open Shipments', 'Dispatch Exceptions']],
'transportation' => ['route' => 'transportation.index', 'heading' => 'Transportation', 'expected' => ['Pending Truck Closure', 'Freight Terms']],
];
}
/**
* @dataProvider modulePages
*
* @param array<int,string> $expected
*/
public function test_module_placeholder_page_renders(string $route, string $heading, array $expected): void
{
$user = User::factory()->create();
$response = $this->actingAs($user)->get(route($route));
$response->assertOk();
$response->assertSee($heading);
$response->assertSee('Replacement module');
$response->assertSee('workbench');
foreach ($expected as $text) {
$response->assertSee($text);
}
}
/**
* @dataProvider modulePages
*
* @param array<int,string> $expected
*/
public function test_module_placeholder_page_requires_authentication(string $route, string $heading, array $expected): void
{
$this->get(route($route))->assertRedirect(route('login'));
}
}
@@ -0,0 +1,88 @@
<?php
namespace Tests\Feature\Modules;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Modules\Platform\app\Models\Company;
use Tests\TestCase;
class PlatformAdminUiFlowTest extends TestCase
{
use RefreshDatabase;
public function test_platform_admin_can_view_and_manage_platform_records(): void
{
$admin = User::factory()->mpbaPlatformAdmin()->create();
$company = Company::query()->create([
'name' => 'Feature Tenant',
'slug' => 'feature-tenant',
'status' => 'active',
'timezone' => 'Europe/London',
'is_system_company' => false,
'billing_email' => 'billing@feature-tenant.example',
]);
$this->actingAs($admin)->get(route('platform.dashboard'))
->assertOk()
->assertSee('Platform administration')
->assertSee('Feature Tenant');
$this->actingAs($admin)->post(route('platform.companies.store'), [
'name' => 'Created Feature Co',
'billing_email' => 'accounts@created-feature.example',
'status' => 'active',
])->assertRedirect();
$this->assertDatabaseHas('companies', [
'slug' => 'created-feature-co',
'billing_email' => 'accounts@created-feature.example',
]);
$this->actingAs($admin)->post(route('platform.licenses.store'), [
'company_id' => $company->id,
'label' => 'Feature Licence',
'plan' => 'enterprise',
'max_users' => 250,
])->assertSessionHas('plain_license_key');
$this->assertDatabaseHas('license_keys', [
'company_id' => $company->id,
'label' => 'Feature Licence',
'plan' => 'enterprise',
'max_users' => 250,
'is_active' => true,
]);
$this->actingAs($admin)->post(route('platform.payments.store'), [
'company_id' => $company->id,
'amount' => 99900,
'currency' => 'gbp',
'status' => 'paid',
'description' => 'Feature payment',
])->assertRedirect();
$this->assertDatabaseHas('payments', [
'company_id' => $company->id,
'amount' => 99900,
'currency' => 'gbp',
'status' => 'paid',
'description' => 'Feature payment',
]);
}
public function test_normal_user_cannot_access_platform_admin(): void
{
$user = User::factory()->create();
$this->actingAs($user)->get(route('platform.dashboard'))->assertForbidden();
$this->actingAs($user)->get(route('platform.companies.index'))->assertForbidden();
$this->actingAs($user)->get(route('platform.licenses.index'))->assertForbidden();
$this->actingAs($user)->get(route('platform.payments.index'))->assertForbidden();
}
public function test_guest_is_redirected_from_platform_admin(): void
{
$this->get(route('platform.dashboard'))->assertRedirect(route('login'));
}
}
@@ -0,0 +1,85 @@
<?php
namespace Tests\Feature\Modules;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Modules\Platform\app\Models\Company;
use Modules\Warehouse\app\Models\Warehouse;
use Tests\TestCase;
class WarehouseUiFlowTest extends TestCase
{
use RefreshDatabase;
public function test_licensed_user_can_view_dashboard_warehouses_and_rules(): void
{
$user = User::factory()->create();
$warehouse = Warehouse::query()->create([
'company_id' => $user->company_id,
'code' => 'MAIN',
'name' => 'Main Warehouse',
'status' => 'active',
'timezone' => 'Europe/London',
'settings' => [],
]);
$this->actingAs($user)->get(route('dashboard'))
->assertOk()
->assertSee($user->company->name)
->assertSee('Main Warehouse');
$this->actingAs($user)->get(route('warehouses.index'))
->assertOk()
->assertSee('Warehouses')
->assertSee('MAIN')
->assertSee('Main Warehouse');
$this->actingAs($user)->post(route('warehouse-rules.store'), [
'warehouse_id' => $warehouse->id,
'name' => 'Location required',
'rule_key' => 'stock.location.required',
'severity' => 'error',
])->assertRedirect();
$this->actingAs($user)->get(route('warehouse-rules.index'))
->assertOk()
->assertSee('Warehouse validation rules')
->assertSee('Location required')
->assertSee('stock.location.required');
}
public function test_user_can_create_warehouse_for_own_tenant_only(): void
{
$user = User::factory()->create();
$this->actingAs($user)->post(route('warehouses.store'), [
'code' => 'PICK',
'name' => 'Picking Warehouse',
'status' => 'active',
'address' => 'Rack 1',
])->assertRedirect();
$this->assertDatabaseHas('warehouses', [
'company_id' => $user->company_id,
'code' => 'PICK',
'name' => 'Picking Warehouse',
]);
}
public function test_unlicensed_tenant_gets_payment_required(): void
{
$company = Company::query()->create([
'name' => 'Unlicensed Co',
'slug' => 'unlicensed-co',
'status' => 'active',
'timezone' => 'Europe/London',
'is_system_company' => false,
]);
$user = User::factory()->create(['company_id' => $company->id]);
$this->actingAs($user)->get(route('dashboard'))->assertStatus(402);
$this->actingAs($user)->get(route('warehouses.index'))->assertStatus(402);
}
}