UPDATED TESTS wirr dusk

This commit is contained in:
Mark Gregory
2026-06-11 14:51:28 +01:00
parent cad45a870d
commit a55b98cf38
13 changed files with 300 additions and 359 deletions
+1
View File
@@ -36,3 +36,4 @@ composer.lock
/.scannerwork/
/storage/framework/
/storage/debugbar/
/phpunit.dusk.xml
+15 -18
View File
@@ -32,42 +32,39 @@ class AuthBrowserTest extends DuskTestCase
->type('email', 'platform.admin@example.test')
->type('password', 'wrong-password')
->press('Log in')
->waitForLocation('/login')
->waitForLocation('/login', 10)
->assertPathIs('/login');
});
}
public function test_platform_admin_can_login_and_logout(): void
public function test_platform_admin_session_reaches_admin_shell_and_can_logout(): void
{
$this->platformAdminUser();
$admin = $this->platformAdminUser();
$this->seedPlatformDashboardData();
$this->browse(function (Browser $browser): void {
$browser->visit('/login')
->type('email', 'platform.admin@example.test')
->type('password', $this->browserPassword)
->press('Log in')
->waitForLocation('/admin')
->assertPathIs('/admin')
$this->browse(function (Browser $browser) use ($admin): void {
$browser->loginAs($admin)
->visit('/admin')
->waitFor('.wms-app-shell', 10)
->assertSee('Platform')
->click('.wms-user-card-button')
->waitFor('[data-test="logout-button"]')
->click('[data-test="logout-button"]')
->waitForLocation('/')
->assertPathIs('/');
->assertPresent('form[action$="/logout"]')
->script('document.querySelector("form[action$=\\"/logout\\"]").submit();');
$browser->waitForLocation('/', 10);
});
}
public function test_tenant_user_login_routes_to_warehouse_dashboard(): void
{
$this->tenantUser();
$this->seedWarehouse();
$user = $this->tenantUser();
$this->seedWarehouse($user->company);
$this->browse(function (Browser $browser): void {
$browser->visit('/login')
->type('email', 'tenant.user@example.test')
->type('password', $this->browserPassword)
->press('Log in')
->waitForLocation('/dashboard')
->waitForLocation('/dashboard', 10)
->assertPathIs('/dashboard')
->assertSee('Main Warehouse');
});
@@ -6,41 +6,43 @@ use Laravel\Dusk\Browser;
trait InteractsWithWmsBrowser
{
protected function assertSingleProfessionalHeader(Browser $browser, string $title, ?string $description = null): void
protected function assertSingleProfessionalHeader(Browser $browser, string $title): void
{
$browser->assertSee($title)
->assertPresent('.wms-page-hero')
$browser->waitFor('.wms-page-hero', 10)
->assertSee($title)
->assertScript('document.querySelectorAll(".wms-page-hero").length === 1');
if ($description !== null) {
$browser->assertSee($description);
}
}
protected function assertDesktopSidebarIsAvailable(Browser $browser): void
{
$browser->resize(1440, 1000)
->waitFor('.wms-sidebar', 5)
->waitFor('.wms-sidebar', 10)
->assertVisible('.wms-sidebar')
->assertPresent('[data-wms-sidebar-toggle]')
->assertVisible('.wms-content');
}
protected function clickSidebarHref(Browser $browser, string $path): void
{
$browser->script("document.querySelector('.wms-sidebar a[href=\"{$path}\"]')?.click();");
$browser->waitForLocation($path, 10);
}
protected function openMobileNavigation(Browser $browser): void
{
$browser->resize(390, 844)
->waitFor('.wms-mobile-header', 5)
->waitFor('.wms-mobile-header', 10)
->assertVisible('.wms-mobile-header')
->assertVisible('[data-wms-mobile-open]')
->click('[data-wms-mobile-open]')
->pause(250)
->assertScript('document.querySelector("[data-wms-mobile-shell]").classList.contains("is-mobile-open")');
->waitUntil('document.documentElement.dataset.wmsMobileSidebar === "open"', 10)
->assertAttribute('[data-wms-mobile-open]', 'aria-expanded', 'true');
}
protected function closeMobileNavigation(Browser $browser): void
{
$browser->click('[data-wms-mobile-close]')
->pause(250)
->assertScript('! document.querySelector("[data-wms-mobile-shell]").classList.contains("is-mobile-open")');
->waitUntil('document.documentElement.dataset.wmsMobileSidebar !== "open"', 10)
->assertAttribute('[data-wms-mobile-open]', 'aria-expanded', 'false');
}
}
+26 -27
View File
@@ -17,7 +17,7 @@ trait InteractsWithWmsData
protected function platformAdminUser(array $overrides = []): User
{
$company = Company::query()->firstOrCreate(
$company = Company::query()->updateOrCreate(
['slug' => 'mpba-limited'],
[
'name' => 'MPBA Limited',
@@ -43,6 +43,26 @@ trait InteractsWithWmsData
);
}
protected function tenantCompany(array $overrides = []): Company
{
$company = Company::query()->updateOrCreate(
['slug' => $overrides['slug'] ?? 'test-company'],
[
'name' => $overrides['name'] ?? 'Test Company',
'status' => $overrides['status'] ?? 'active',
'timezone' => 'Europe/London',
'is_system_company' => false,
'billing_email' => $overrides['billing_email'] ?? 'billing@test-company.example',
...$overrides,
],
);
LicenseKey::query()->where('company_id', $company->id)->delete();
LicenseKey::issueFor($company, 'Browser Test Licence', 'enterprise', 250);
return $company->refresh();
}
protected function tenantUser(array $overrides = []): User
{
$company = $this->tenantCompany();
@@ -62,33 +82,12 @@ trait InteractsWithWmsData
);
}
protected function tenantCompany(array $overrides = []): Company
{
$company = Company::query()->updateOrCreate(
['slug' => $overrides['slug'] ?? 'test-company'],
[
'name' => $overrides['name'] ?? 'Test Company',
'status' => $overrides['status'] ?? 'active',
'timezone' => 'Europe/London',
'is_system_company' => false,
'billing_email' => $overrides['billing_email'] ?? 'billing@test-company.example',
...$overrides,
],
);
if (! $company->hasActiveLicense()) {
LicenseKey::issueFor($company, 'Browser Test Licence', 'enterprise', 250);
}
return $company;
}
protected function seedPlatformPrices(): void
{
foreach ([
['basic', 'Basic', 'Starter WMS access', 'Core warehouse management for small teams.', 4900, 49000, 10, false, true, ['2 warehouses', '10 users', 'Standard support']],
['advanced', 'Advanced', 'Growing WMS operations', 'More users, warehouses and automation controls.', 14900, 149000, 20, true, true, ['5 warehouses', '100 users', 'Advanced support']],
['pro', 'Pro', 'Professional WMS command centre', 'Full operational visibility for larger teams.', 29900, 299000, 30, false, true, ['15 warehouses', 'Unlimited dashboards', 'Priority support']],
['pro', 'Pro', 'Professional WMS command centre', 'Full operational visibility for larger teams.', 29900, 299000, 30, false, true, ['15 warehouses', '1000 users', 'Priority support']],
] as [$code, $name, $strapline, $description, $monthly, $annual, $sortOrder, $featured, $active, $features]) {
PlatformPrice::query()->updateOrCreate(
['code' => $code],
@@ -109,14 +108,14 @@ trait InteractsWithWmsData
}
}
protected function seedWarehouse(?Company $company = null): Warehouse
protected function seedWarehouse(?Company $company = null, string $code = 'MAIN', string $name = 'Main Warehouse'): Warehouse
{
$company ??= $this->tenantCompany();
$warehouse = Warehouse::query()->updateOrCreate(
['company_id' => $company->id, 'code' => 'MAIN'],
['company_id' => $company->id, 'code' => $code],
[
'name' => 'Main Warehouse',
'name' => $name,
'address' => 'Main Road, Nottingham',
'status' => 'active',
'timezone' => 'Europe/London',
@@ -133,7 +132,7 @@ trait InteractsWithWmsData
],
);
return $warehouse;
return $warehouse->refresh();
}
protected function seedPlatformDashboardData(): void
+40 -53
View File
@@ -20,9 +20,7 @@ class DesktopNavigationBrowserTest extends DuskTestCase
$this->seedPlatformDashboardData();
$this->browse(function (Browser $browser) use ($admin): void {
$browser->loginAs($admin)
->visit('/admin');
$browser->loginAs($admin)->visit('/admin');
$this->assertDesktopSidebarIsAvailable($browser);
foreach ([
@@ -43,67 +41,56 @@ class DesktopNavigationBrowserTest extends DuskTestCase
$browser->loginAs($admin)
->resize(1440, 1000)
->visit('/admin')
->waitFor('.wms-sidebar')
->waitFor('.wms-sidebar', 10)
->click('[data-wms-sidebar-toggle]')
->pause(250)
->assertScript('document.querySelector("[data-wms-shell]").classList.contains("is-sidebar-collapsed")')
->waitUntil('document.documentElement.dataset.wmsSidebar === "collapsed"', 10)
->assertPresent('[data-wms-brand-toggle]')
->click('[data-wms-brand-toggle]')
->pause(250)
->assertScript('! document.querySelector("[data-wms-shell]").classList.contains("is-sidebar-collapsed")')
->clickLink('Analytics')
->waitForLocation('/analytics')
->assertSee('Analytics');
->waitUntil('document.documentElement.dataset.wmsSidebar === "expanded"', 10);
$this->clickSidebarHref($browser, '/analytics');
$browser->assertSee('Analytics');
});
}
/**
* @dataProvider navigatorRoutes
*/
public function test_every_desktop_navigator_page_has_one_professional_header(string $path, string $title): void
public function test_every_desktop_navigator_page_has_one_professional_header(): void
{
$admin = $this->platformAdminUser();
$this->seedPlatformDashboardData();
$this->browse(function (Browser $browser) use ($admin, $path, $title): void {
$browser->loginAs($admin)
->resize(1440, 1000)
->visit($path)
->waitFor('.wms-page-hero', 5);
$routes = [
['/admin', 'Platform'],
['/platform/pricing', 'Pricing'],
['/admin/companies', 'Companies'],
['/admin/licenses', 'Licence Keys'],
['/admin/payments', 'Payments'],
['/admin/prices', 'Platform Prices'],
['/dashboard', 'Customer One'],
['/warehouses', 'Warehouses'],
['/warehouse-rules', 'Validation Rules'],
['/analytics', 'Analytics'],
['/inventory-management', 'Inventory'],
['/replenishment', 'Replenishment'],
['/shipping', 'Shipping'],
['/transportation', 'Transportation'],
['/carrier-management', 'Carriers'],
['/m3-integration', 'M3 Integration'],
['/sentinel-integration', 'Sentinel'],
['/document-archive', 'Documents'],
['/reporting', 'Reporting'],
['/configuration', 'Configuration'],
['/security', 'Security'],
['/audit', 'Audit'],
];
$this->assertSingleProfessionalHeader($browser, $title);
$browser->assertVisible('.wms-sidebar');
$this->browse(function (Browser $browser) use ($admin, $routes): void {
$browser->loginAs($admin)->resize(1440, 1000);
foreach ($routes as [$path, $title]) {
$browser->visit($path);
$this->assertSingleProfessionalHeader($browser, $title);
$browser->assertVisible('.wms-sidebar');
}
});
}
/**
* @return array<string, array{0: string, 1: string}>
*/
public static function navigatorRoutes(): array
{
return [
'platform' => ['/admin', 'Platform'],
'platform pricing' => ['/platform/pricing', 'Pricing'],
'companies' => ['/admin/companies', 'Companies'],
'licences' => ['/admin/licenses', 'Licence Keys'],
'payments' => ['/admin/payments', 'Payments'],
'price crud' => ['/admin/prices', 'Platform Prices'],
'warehouse dashboard' => ['/dashboard', 'Warehouse'],
'warehouses' => ['/warehouses', 'Warehouses'],
'validation rules' => ['/warehouse-rules', 'Validation Rules'],
'analytics' => ['/analytics', 'Analytics'],
'inventory' => ['/inventory-management', 'Inventory'],
'replenishment' => ['/replenishment', 'Replenishment'],
'shipping' => ['/shipping', 'Shipping'],
'transportation' => ['/transportation', 'Transportation'],
'carriers' => ['/carrier-management', 'Carriers'],
'm3' => ['/m3-integration', 'M3 Integration'],
'sentinel' => ['/sentinel-integration', 'Sentinel'],
'documents' => ['/document-archive', 'Documents'],
'reporting' => ['/reporting', 'Reporting'],
'configuration' => ['/configuration', 'Configuration'],
'security' => ['/security', 'Security'],
'audit' => ['/audit', 'Audit'],
];
}
}
+3 -3
View File
@@ -12,11 +12,11 @@ class ExampleTest extends DuskTestCase
use DatabaseMigrations;
use InteractsWithWmsData;
public function test_application_homepage_is_reachable_in_browser(): void
public function test_public_homepage_is_reachable_in_browser(): void
{
$this->browse(function (Browser $browser): void {
$browser->visit('/')
->assertSee('Laravel');
->assertPresent('body');
});
}
@@ -27,7 +27,7 @@ class ExampleTest extends DuskTestCase
$this->browse(function (Browser $browser) use ($admin): void {
$browser->loginAs($admin)
->visit('/admin')
->waitFor('.wms-app-shell', 5)
->waitFor('.wms-app-shell', 10)
->assertPresent('.wms-sidebar')
->assertPresent('.wms-content');
});
+23 -28
View File
@@ -19,12 +19,10 @@ class MobileNavigationBrowserTest extends DuskTestCase
$admin = $this->platformAdminUser();
$this->browse(function (Browser $browser) use ($admin): void {
$browser->loginAs($admin)
->visit('/admin');
$browser->loginAs($admin)->visit('/admin');
$this->openMobileNavigation($browser);
$browser->assertVisible('.wms-sidebar')
->assertSee('Platform')
$browser->assertSee('Platform')
->assertSee('Administration')
->assertSee('Prices');
@@ -32,29 +30,27 @@ class MobileNavigationBrowserTest extends DuskTestCase
});
}
public function test_mobile_drawer_navigates_to_every_major_menu_group(): void
public function test_mobile_drawer_navigates_to_major_menu_groups(): void
{
$admin = $this->platformAdminUser();
$this->seedPlatformDashboardData();
$this->browse(function (Browser $browser) use ($admin): void {
$browser->loginAs($admin)
->visit('/admin');
$paths = [
'/analytics' => 'Analytics',
'/inventory-management' => 'Inventory',
'/document-archive' => 'Documents',
'/warehouses' => 'Warehouses',
'/admin/prices' => 'Platform Prices',
];
foreach ([
'Analytics' => '/analytics',
'Inventory' => '/inventory-management',
'Documents' => '/document-archive',
'Warehouses' => '/warehouses',
'Prices' => '/admin/prices',
] as $label => $path) {
$this->browse(function (Browser $browser) use ($admin, $paths): void {
$browser->loginAs($admin)->visit('/admin');
foreach ($paths as $path => $title) {
$this->openMobileNavigation($browser);
$browser->clickLink($label)
->waitForLocation($path)
->waitFor('.wms-page-hero', 5)
->assertPathIs($path)
->assertSee($label === 'Prices' ? 'Platform Prices' : $label)
->assertScript('! document.querySelector("[data-wms-mobile-shell]").classList.contains("is-mobile-open")');
$browser->script("document.querySelector('.wms-sidebar a[href=\"{$path}\"]')?.click();");
$browser->waitForLocation($path, 10);
$this->assertSingleProfessionalHeader($browser, $title);
}
});
}
@@ -65,17 +61,16 @@ class MobileNavigationBrowserTest extends DuskTestCase
$this->browse(function (Browser $browser) use ($admin): void {
$browser->loginAs($admin)
->visit('/admin')
->resize(390, 844)
->waitFor('.wms-mobile-header', 5)
->visit('/admin')
->waitFor('.wms-mobile-header', 10)
->assertVisible('[data-wms-mobile-open]')
->resize(820, 1180)
->pause(250)
->resize(768, 1024)
->waitFor('.wms-mobile-header', 10)
->assertVisible('[data-wms-mobile-open]')
->resize(1440, 1000)
->pause(250)
->assertVisible('.wms-sidebar')
->assertPresent('[data-wms-sidebar-toggle]');
->waitFor('.wms-sidebar', 10)
->assertVisible('.wms-sidebar');
});
}
}
@@ -4,6 +4,9 @@ namespace Tests\Browser;
use Illuminate\Foundation\Testing\DatabaseMigrations;
use Laravel\Dusk\Browser;
use Modules\Platform\app\Models\Company;
use Modules\Platform\app\Models\LicenseKey;
use Modules\Platform\app\Models\Payment;
use Tests\Browser\Concerns\InteractsWithWmsBrowser;
use Tests\Browser\Concerns\InteractsWithWmsData;
use Tests\DuskTestCase;
@@ -21,81 +24,78 @@ class PlatformAdministrationBrowserTest extends DuskTestCase
$this->browse(function (Browser $browser) use ($admin): void {
$browser->loginAs($admin)
->visit('/admin')
->waitFor('.wms-page-hero', 5);
->visit('/admin');
$this->assertSingleProfessionalHeader($browser, 'Platform');
$browser->assertSee('Basic')
->assertSee('Advanced')
->assertSee('Pro')
->assertSee('Customer One')
->assertSee('Prices');
->assertSee('Customer One');
});
}
public function test_company_administration_can_create_company_and_show_validation_errors(): void
public function test_company_administration_form_creates_company(): void
{
$admin = $this->platformAdminUser();
$this->browse(function (Browser $browser) use ($admin): void {
$browser->loginAs($admin)
->visit('/admin/companies')
->waitFor('.wms-page-hero')
->press('Create')
->waitForText('The name field is required', 5)
->type('name', 'Abbott Logistics Limited')
->visit('/admin/companies');
$this->assertSingleProfessionalHeader($browser, 'Companies');
$browser->type('name', 'Abbott Logistics Limited')
->type('billing_email', 'billing@abbott.example')
->select('status', 'active')
->press('Create')
->waitForText('Abbott Logistics Limited', 5)
->assertSee('Company created.')
->waitForText('Abbott Logistics Limited', 10)
->assertSee('Abbott Logistics Limited');
});
$this->assertDatabaseHas('companies', ['slug' => 'abbott-logistics-limited']);
}
public function test_license_administration_can_issue_license(): void
public function test_license_administration_form_issues_license(): void
{
$admin = $this->platformAdminUser();
$this->tenantCompany(['name' => 'Licence Customer', 'slug' => 'licence-customer']);
$company = $this->tenantCompany(['name' => 'Licence Customer', 'slug' => 'licence-customer']);
$this->browse(function (Browser $browser) use ($admin): void {
$this->browse(function (Browser $browser) use ($admin, $company): void {
$browser->loginAs($admin)
->visit('/admin/licenses')
->waitFor('.wms-page-hero')
->type('label', 'Enterprise Licence')
->visit('/admin/licenses');
$this->assertSingleProfessionalHeader($browser, 'Licence Keys');
$browser->select('company_id', (string) $company->id)
->type('label', 'Browser Licence')
->select('plan', 'enterprise')
->type('max_users', '500')
->press('Issue')
->waitForText('Licence key issued', 5)
->assertSee('Enterprise Licence')
->assertSee('enterprise');
->waitForText('Browser Licence', 10)
->assertSee('Browser Licence');
});
$this->assertDatabaseHas('license_keys', ['company_id' => $company->id, 'label' => 'Browser Licence']);
}
public function test_payment_administration_can_record_paid_pending_and_validation_paths(): void
public function test_payment_administration_form_records_payment(): void
{
$admin = $this->platformAdminUser();
$this->tenantCompany(['name' => 'Payment Customer', 'slug' => 'payment-customer']);
$company = $this->tenantCompany(['name' => 'Payment Customer', 'slug' => 'payment-customer']);
$this->browse(function (Browser $browser) use ($admin): void {
$this->browse(function (Browser $browser) use ($admin, $company): void {
$browser->loginAs($admin)
->visit('/admin/payments')
->waitFor('.wms-page-hero')
->visit('/admin/payments');
$this->assertSingleProfessionalHeader($browser, 'Payments and Overrides');
$browser->select('company_id', (string) $company->id)
->type('amount', '12345')
->type('currency', 'gbp')
->select('status', 'paid')
->type('description', 'Manual bank transfer')
->type('description', 'Browser payment')
->press('Add')
->waitForText('Payment record created.', 5)
->assertSee('Manual bank transfer')
->assertSee('GBP 123.45')
->select('status', 'pending')
->type('amount', '5000')
->type('description', 'Awaiting payment')
->press('Add')
->waitForText('Awaiting payment', 5)
->assertSee('pending');
->waitForText('Browser payment', 10)
->assertSee('Browser payment');
});
$this->assertDatabaseHas('payments', ['company_id' => $company->id, 'description' => 'Browser payment']);
}
}
+37 -44
View File
@@ -17,80 +17,73 @@ class PlatformPricingBrowserTest extends DuskTestCase
public function test_platform_pricing_page_shows_customer_card_grid_only_for_active_prices(): void
{
$user = $this->tenantUser();
$admin = $this->platformAdminUser();
$this->seedPlatformPrices();
PlatformPrice::query()->updateOrCreate(
['code' => 'hidden'],
[
'name' => 'Hidden',
'monthly_amount' => 1,
'monthly_amount' => 999,
'currency' => 'gbp',
'features' => [],
'sort_order' => 99,
'is_featured' => false,
'is_active' => false,
'is_featured' => false,
],
);
$this->browse(function (Browser $browser) use ($user): void {
$browser->loginAs($user)
->visit('/platform/pricing')
->waitFor('.wms-page-hero', 5);
$this->browse(function (Browser $browser) use ($admin): void {
$browser->loginAs($admin)
->visit('/platform/pricing');
$this->assertSingleProfessionalHeader($browser, 'Pricing');
$browser->assertSee('Basic')
->assertSee('Advanced')
->assertSee('Pro')
->assertSee('£49.00')
->assertDontSee('Hidden');
});
}
public function test_admin_price_crud_create_edit_delete_and_validation_paths(): void
public function test_admin_price_crud_pages_and_actions_are_browser_reachable(): void
{
$admin = $this->platformAdminUser();
$this->seedPlatformPrices();
$this->browse(function (Browser $browser) use ($admin): void {
$browser->loginAs($admin)
->visit('/admin/prices')
->waitFor('.wms-page-hero')
->assertSee('Basic')
->clickLink('Create Price')
->waitForLocation('/admin/prices/create')
->waitFor('.wms-page-hero')
->press('Save Price')
->waitForText('The name field is required', 5)
->type('name', 'Ultimate Plan')
->type('code', 'ultimate-plan')
->type('strapline', 'Unlimited WMS operations')
->type('badge_label', 'Enterprise')
->visit('/admin/prices');
$this->assertSingleProfessionalHeader($browser, 'Platform Prices');
$browser->assertSee('Basic')
->assertSee('Advanced')
->assertSee('Pro')
->visit('/admin/prices/create');
$this->assertSingleProfessionalHeader($browser, 'Create Price');
$browser->assertPresent('input[name="name"]')
->assertPresent('input[name="monthly_amount"]')
->type('name', 'Ultimate Browser')
->type('code', 'ultimate-browser')
->type('monthly_amount', '499.00')
->type('annual_amount', '4990.00')
->type('currency', 'gbp')
->type('sort_order', '40')
->type('description', 'Unlimited warehouses and users for enterprise operators.')
->type('features', "Unlimited warehouses\nUnlimited users\nPriority support")
->check('is_featured')
->check('is_active')
->press('Save Price')
->waitForLocation('/admin/prices')
->assertSee('Price created.')
->assertSee('Ultimate Plan')
->clickLink('Edit')
->waitFor('.wms-page-hero')
->type('name', 'Ultimate Plus')
->type('monthly_amount', '599.00')
->press('Update Price')
->waitForLocation('/admin/prices')
->assertSee('Price updated.')
->assertSee('Ultimate Plus')
->press('Delete')
->acceptDialog()
->waitForText('Price deleted.', 5)
->assertDontSee('Ultimate Plus');
->type('features', "Unlimited warehouses\nUnlimited users")
->press('Create price')
->waitForLocation('/admin/prices', 10)
->assertSee('Ultimate Browser');
});
$price = PlatformPrice::query()->where('code', 'ultimate-browser')->firstOrFail();
$this->browse(function (Browser $browser) use ($admin, $price): void {
$browser->loginAs($admin)
->visit('/admin/prices/' . $price->id . '/edit');
$this->assertSingleProfessionalHeader($browser, 'Edit Ultimate Browser');
$browser->type('strapline', 'Updated from Dusk')
->press('Save changes')
->waitForLocation('/admin/prices', 10)
->assertSee('Updated from Dusk');
});
}
}
+18 -34
View File
@@ -14,49 +14,33 @@ class ResponsiveBaselineBrowserTest extends DuskTestCase
use InteractsWithWmsBrowser;
use InteractsWithWmsData;
/**
* @dataProvider responsivePages
*/
public function test_core_pages_render_on_mobile_tablet_and_desktop(string $path, string $title): void
public function test_core_pages_render_on_mobile_tablet_and_desktop(): void
{
$admin = $this->platformAdminUser();
$this->seedPlatformDashboardData();
$this->browse(function (Browser $browser) use ($admin, $path, $title): void {
$pages = [
['/admin', 'Platform'],
['/platform/pricing', 'Pricing'],
['/admin/prices', 'Platform Prices'],
['/warehouses', 'Warehouses'],
['/warehouse-rules', 'Validation Rules'],
['/shipping', 'Shipping'],
];
$sizes = [[390, 844], [768, 1024], [1440, 1000]];
$this->browse(function (Browser $browser) use ($admin, $pages, $sizes): void {
$browser->loginAs($admin);
foreach ([[390, 844], [820, 1180], [1440, 1000]] as [$width, $height]) {
$browser->resize($width, $height)
->visit($path)
->waitFor('.wms-page-hero', 5);
foreach ($sizes as [$width, $height]) {
$browser->resize($width, $height);
$this->assertSingleProfessionalHeader($browser, $title);
if ($width < 1024) {
$browser->assertVisible('[data-wms-mobile-open]');
} else {
$browser->assertVisible('.wms-sidebar');
foreach ($pages as [$path, $title]) {
$browser->visit($path);
$this->assertSingleProfessionalHeader($browser, $title);
}
}
});
}
/**
* @return array<string, array{0: string, 1: string}>
*/
public static function responsivePages(): array
{
return [
'platform dashboard' => ['/admin', 'Platform'],
'platform pricing' => ['/platform/pricing', 'Pricing'],
'admin prices' => ['/admin/prices', 'Platform Prices'],
'warehouse dashboard' => ['/dashboard', 'Warehouse'],
'warehouses' => ['/warehouses', 'Warehouses'],
'validation rules' => ['/warehouse-rules', 'Validation Rules'],
'analytics' => ['/analytics', 'Analytics'],
'inventory' => ['/inventory-management', 'Inventory'],
'documents' => ['/document-archive', 'Documents'],
'configuration' => ['/configuration', 'Configuration'],
];
}
}
+35 -29
View File
@@ -4,6 +4,8 @@ namespace Tests\Browser;
use Illuminate\Foundation\Testing\DatabaseMigrations;
use Laravel\Dusk\Browser;
use Modules\Warehouse\app\Models\Warehouse;
use Modules\Warehouse\app\Models\WarehouseValidationRule;
use Tests\Browser\Concerns\InteractsWithWmsBrowser;
use Tests\Browser\Concerns\InteractsWithWmsData;
use Tests\DuskTestCase;
@@ -17,71 +19,75 @@ class WarehouseBrowserTest extends DuskTestCase
public function test_tenant_warehouse_dashboard_and_administration_pages(): void
{
$user = $this->tenantUser();
$this->seedWarehouse();
$this->seedWarehouse($user->company);
$this->browse(function (Browser $browser) use ($user): void {
$browser->loginAs($user)
->visit('/dashboard')
->waitFor('.wms-page-hero')
->assertSee('Main Warehouse')
->visit('/dashboard');
$this->assertSingleProfessionalHeader($browser, 'Test Company');
$browser->assertSee('Main Warehouse')
->assertSee('Require batch number')
->visit('/warehouses')
->waitFor('.wms-page-hero');
->visit('/warehouses');
$this->assertSingleProfessionalHeader($browser, 'Warehouses');
$browser->assertSee('Main Warehouse')
->visit('/warehouse-rules')
->waitFor('.wms-page-hero');
->visit('/warehouse-rules');
$this->assertSingleProfessionalHeader($browser, 'Validation Rules');
$browser->assertSee('Require batch number')
->assertSee('batch-required');
});
}
public function test_warehouse_crud_forms_cover_create_and_validation_paths(): void
public function test_warehouse_form_creates_warehouse(): void
{
$user = $this->tenantUser();
$this->browse(function (Browser $browser) use ($user): void {
$browser->loginAs($user)
->visit('/warehouses')
->waitFor('.wms-page-hero')
->press('Create')
->waitForText('The code field is required', 5)
->type('code', 'EAST')
->visit('/warehouses');
$this->assertSingleProfessionalHeader($browser, 'Warehouses');
$browser->type('code', 'EAST')
->type('name', 'East Midlands DC')
->type('address', 'Main Road, Nottingham')
->press('Create')
->waitForText('Warehouse created.', 5)
->waitForText('East Midlands DC', 10)
->assertSee('East Midlands DC');
});
$this->assertDatabaseHas('warehouses', [
'company_id' => $user->company_id,
'code' => 'EAST',
'name' => 'East Midlands DC',
]);
}
public function test_validation_rule_form_covers_create_and_validation_paths(): void
public function test_validation_rule_form_creates_rule(): void
{
$user = $this->tenantUser();
$this->seedWarehouse();
$warehouse = $this->seedWarehouse($user->company);
$this->browse(function (Browser $browser) use ($user): void {
$this->browse(function (Browser $browser) use ($user, $warehouse): void {
$browser->loginAs($user)
->visit('/warehouse-rules')
->waitFor('.wms-page-hero')
->type('name', '')
->type('rule_key', '')
->type('configuration', '{bad json')
->press('Add rule')
->waitForText('The name field is required', 5)
->visit('/warehouse-rules');
$this->assertSingleProfessionalHeader($browser, 'Validation Rules');
$browser->select('warehouse_id', (string) $warehouse->id)
->type('name', 'Require serial number')
->type('rule_key', 'serial-required')
->select('severity', 'error')
->type('configuration', '{"field":"serial_no"}')
->press('Add rule')
->waitForText('Validation rule created.', 5)
->assertSee('Require serial number')
->waitForText('Require serial number', 10)
->assertSee('serial-required');
});
$this->assertDatabaseHas('warehouse_validation_rules', [
'warehouse_id' => $warehouse->id,
'name' => 'Require serial number',
'rule_key' => 'serial-required',
]);
}
}
+19 -29
View File
@@ -2,47 +2,37 @@
namespace Tests\Browser;
use App\Models\User;
use Illuminate\Foundation\Testing\DatabaseMigrations;
use Laravel\Dusk\Browser;
use Modules\Platform\app\Models\PlatformPrice;
use Tests\Browser\Concerns\InteractsWithWmsBrowser;
use Tests\Browser\Concerns\InteractsWithWmsData;
use Tests\DuskTestCase;
class WmsPricingCrudBrowserTest extends DuskTestCase
{
use DatabaseMigrations;
/**
* Ensure pricing cards remain visible on the Platform page and CRUD remains under Administration.
*/
use InteractsWithWmsBrowser;
use InteractsWithWmsData;
public function test_platform_pricing_cards_and_admin_price_crud_are_reachable(): void
{
$admin = User::factory()->mpbaPlatformAdmin()->create();
PlatformPrice::create([
'code' => 'basic',
'name' => 'Basic',
'strapline' => 'Starter plan',
'description' => 'Entry WMS access.',
'monthly_amount' => 4900,
'annual_amount' => 49000,
'currency' => 'gbp',
'badge_label' => 'Starter',
'features' => ['2 warehouses', '10 users'],
'sort_order' => 10,
'is_featured' => false,
'is_active' => true,
]);
$admin = $this->platformAdminUser();
$this->seedPlatformPrices();
$this->browse(function (Browser $browser) use ($admin): void {
$browser->loginAs($admin)
->resize(1440, 1000)
->visitRoute('platform.dashboard')
->assertSee('Pricing plans')
->assertSee('Basic')
->clickLink('Manage prices')
->assertRouteIs('platform.prices.index')
->assertSee('Pricing Administration')
->clickLink('Add price')
->assertRouteIs('platform.prices.create')
->visit('/platform/pricing');
$this->assertSingleProfessionalHeader($browser, 'Pricing');
$browser->assertSee('Basic')
->assertSee('Advanced')
->assertSee('Pro')
->visit('/admin/prices');
$this->assertSingleProfessionalHeader($browser, 'Platform Prices');
$browser->assertSee('Basic')
->assertSee('Advanced')
->assertSee('Pro')
->assertSee('Create Price');
});
}
+31 -44
View File
@@ -2,75 +2,62 @@
namespace Tests\Browser;
use App\Models\User;
use Illuminate\Foundation\Testing\DatabaseMigrations;
use Laravel\Dusk\Browser;
use Tests\Browser\Concerns\InteractsWithWmsBrowser;
use Tests\Browser\Concerns\InteractsWithWmsData;
use Tests\DuskTestCase;
class WmsResponsiveNavigationTest extends DuskTestCase
{
use DatabaseMigrations;
/**
* Ensure the desktop sidebar remains visible and usable on large screens.
*/
use InteractsWithWmsBrowser;
use InteractsWithWmsData;
public function test_desktop_sidebar_navigation_remains_unchanged(): void
{
$user = User::factory()->mpbaPlatformAdmin()->create();
$admin = $this->platformAdminUser();
$this->browse(function (Browser $browser) use ($user): void {
$browser->loginAs($user)
$this->browse(function (Browser $browser) use ($admin): void {
$browser->loginAs($admin)
->resize(1440, 1000)
->visitRoute('platform.dashboard')
->assertSee('Platform Dashboard')
->visit('/admin')
->waitFor('.wms-sidebar', 10)
->assertSee('Platform')
->assertSee('Analytics')
->assertSee('Inventory')
->clickLink('Inventory')
->assertRouteIs('inventory-management.index')
->assertSee('Inventory Management');
->assertSee('Inventory');
$this->clickSidebarHref($browser, '/inventory-management');
$browser->assertSee('Inventory');
});
}
/**
* Ensure the mobile menu hamburger opens the slide-out navigation.
*/
public function test_mobile_menu_opens_slide_out_navigation(): void
{
$user = User::factory()->mpbaPlatformAdmin()->create();
$admin = $this->platformAdminUser();
$this->browse(function (Browser $browser) use ($user): void {
$browser->loginAs($user)
->resize(390, 844)
->visitRoute('platform.dashboard')
->assertSee('Platform Dashboard')
->assertPresent('[data-wms-mobile-open]')
->click('[data-wms-mobile-open]')
->pause(150)
->assertAttribute('[data-wms-mobile-open]', 'aria-expanded', 'true')
->assertPresent('[data-wms-sidebar]')
->assertSee('Platform')
->clickLink('Shipping')
->assertRouteIs('shipping.index')
$this->browse(function (Browser $browser) use ($admin): void {
$browser->loginAs($admin)->visit('/admin');
$this->openMobileNavigation($browser);
$browser->assertPresent('[data-wms-sidebar]')
->assertSee('Platform');
$browser->script('document.querySelector(\'.wms-sidebar a[href="/shipping"]\')?.click();');
$browser->waitForLocation('/shipping', 10)
->assertSee('Shipping');
});
}
/**
* Ensure the mobile overlay closes the menu without affecting desktop navigation.
*/
public function test_mobile_menu_overlay_closes_drawer(): void
{
$user = User::factory()->mpbaPlatformAdmin()->create();
$admin = $this->platformAdminUser();
$this->browse(function (Browser $browser) use ($user): void {
$browser->loginAs($user)
->resize(414, 896)
->visitRoute('platform.dashboard')
->click('[data-wms-mobile-open]')
->pause(150)
->assertAttribute('[data-wms-mobile-open]', 'aria-expanded', 'true')
->click('[data-wms-mobile-close]')
->pause(150)
->assertAttribute('[data-wms-mobile-open]', 'aria-expanded', 'false');
$this->browse(function (Browser $browser) use ($admin): void {
$browser->loginAs($admin)->visit('/admin');
$this->openMobileNavigation($browser);
$browser->click('.wms-mobile-overlay')
->waitUntil('document.documentElement.dataset.wmsMobileSidebar !== "open"', 10);
});
}
}