Skip to content

Commit 8bd5462

Browse files
nielsdrost7claude
andcommitted
chore: port test-infra fixes, tighten .gitignore, document PHP 8.3+
Ports the standing fixes that so far only lived on the fork's develop branch, so every future PR can be cut from this repo directly: - Panel test base classes seed Spatie roles/permissions in setUp — without this every company-panel Livewire test 403s. Also fixes testLivewire(), which imported the Filament schema component instead of the Livewire test facade and chained withSession onto the wrong object (it could never have run). - RolesSeeder: restore company CRUD permissions for client_admin. - Login page: build the throttled notification inline (Filament version compatibility) and label the email/password fields. - Invoices/Quotes feature test adjustments that go with the seeding. .gitignore now excludes local tooling that kept leaking into pull requests (/automation/, /.claude/fable5/, pint_output.log, audit-report.json) and drops the Modules controller ignores that hid real source files. README: the resolved dependency tree requires PHP >= 8.3 (composer's platform check rejects 8.2), so the badge and requirements now say 8.3+. composer.json still declares ^8.2 — bumping it needs a lock re-resolve that currently fails because roave/security-advisories conflicts with the locked guzzlehttp/guzzle < 7.12.1; a follow-up `composer update -W guzzlehttp/guzzle` would fix the constraint and the three open guzzle advisories together. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 parent ae7c593 commit 8bd5462

8 files changed

Lines changed: 66 additions & 28 deletions

File tree

.gitignore

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,6 @@
1212
!.env.testing.example
1313
/storage/*.key
1414

15-
# Laravel – Modules (ignored generated sources)
16-
Modules/**/Controllers/
17-
Modules/**/Http/Controllers/
18-
Modules/**/Http/Requests/
19-
2015
# Vite / Filament / Livewire
2116
/.vite
2217
/livewire-tmp/
@@ -40,6 +35,7 @@ package-lock.json
4035

4136
# Pint
4237
/pint.txt
38+
/pint_output.log
4339

4440
# PHPUnit
4541
/.phpunit.cache
@@ -78,5 +74,9 @@ package-lock.json
7874
/olddocs
7975
/.php-cs-fixer.cache
8076
*.sqlite
77+
/audit-report.json
8178
/failures.txt
8279
/yarnpack.txt
80+
/automation/
81+
/.claude/fable5/
82+
upd.sh

Modules/Core/Database/Seeders/RolesSeeder.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,7 @@ function ($p) use ($customerResources) {
120120
$isBasicAction = str_starts_with($p, 'view-')
121121
|| str_starts_with($p, 'create-')
122122
|| str_starts_with($p, 'edit-')
123+
|| str_starts_with($p, 'delete-')
123124
|| str_starts_with($p, 'export-')
124125
|| str_starts_with($p, 'duplicate-');
125126
$isCustomerResource = (bool) array_filter(

Modules/Core/Filament/Pages/Auth/Login.php

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,17 @@ public function authenticate(): ?LoginResponse
1717
try {
1818
$this->rateLimit(5);
1919
} catch (TooManyRequestsException $exception) {
20-
$this->getRateLimitedNotification($exception)?->send();
20+
Notification::make()
21+
->title(trans('filament-panels::pages/auth/login.notifications.throttled.title', [
22+
'seconds' => $exception->secondsUntilAvailable,
23+
'minutes' => ceil($exception->secondsUntilAvailable / 60),
24+
]))
25+
->body(array_key_exists('body', trans('filament-panels::pages/auth/login.notifications.throttled') ?: []) ? trans('filament-panels::pages/auth/login.notifications.throttled.body', [
26+
'seconds' => $exception->secondsUntilAvailable,
27+
'minutes' => ceil($exception->secondsUntilAvailable / 60),
28+
]) : null)
29+
->danger()
30+
->send();
2131

2232
return null;
2333
}
@@ -52,6 +62,7 @@ public function authenticate(): ?LoginResponse
5262
protected function getEmailFormComponent(): Component
5363
{
5464
return TextInput::make('email')
65+
->label(trans('filament-panels::pages/auth/login.form.email.label'))
5566
->email()
5667
->required()
5768
->autocomplete()
@@ -62,6 +73,7 @@ protected function getEmailFormComponent(): Component
6273
protected function getPasswordFormComponent(): Component
6374
{
6475
return TextInput::make('password')
76+
->label(trans('filament-panels::pages/auth/login.form.password.label'))
6577
->password()
6678
->revealable(filament()->arePasswordsRevealable())
6779
->autocomplete('current-password')

Modules/Core/Tests/AbstractAdminPanelTestCase.php

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@
55
use Illuminate\Foundation\Testing\RefreshDatabase;
66
use Illuminate\Foundation\Testing\TestCase as BaseTestCase;
77
use Illuminate\Support\Carbon;
8+
use Modules\Core\Database\Seeders\PermissionsSeeder;
9+
use Modules\Core\Database\Seeders\RolesSeeder;
10+
use Modules\Core\Enums\UserRole;
811
use Modules\Core\Models\Company;
912
use Modules\Core\Models\User;
1013

@@ -34,6 +37,14 @@ protected function setUp(): void
3437

3538
session(['current_company_id' => $this->company->id]);
3639

40+
/*
41+
* Admin resources gate every page on Spatie permissions (canViewAny
42+
* etc.), so the test user needs the seeded super_admin permission set.
43+
*/
44+
(new PermissionsSeeder())->run();
45+
(new RolesSeeder())->run();
46+
$this->superAdmin->assignRole(UserRole::SUPER_ADMIN->value);
47+
3748
$this->withoutExceptionHandling();
3849
}
3950

Modules/Core/Tests/AbstractCompanyPanelTestCase.php

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,13 @@
33
namespace Modules\Core\Tests;
44

55
use Filament\Facades\Filament;
6-
use Filament\Schemas\Components\Livewire;
6+
use Livewire\Livewire;
77
use Illuminate\Foundation\Testing\RefreshDatabase;
88
use Illuminate\Foundation\Testing\TestCase as BaseTestCase;
99
use Illuminate\Support\Carbon;
10+
use Modules\Core\Database\Seeders\PermissionsSeeder;
11+
use Modules\Core\Database\Seeders\RolesSeeder;
12+
use Modules\Core\Enums\UserRole;
1013
use Modules\Core\Models\Company;
1114
use Modules\Core\Models\User;
1215

@@ -44,6 +47,14 @@ protected function setUp(): void
4447
$currentCompanyId = $this->user->getCurrentCompanyId();
4548
session(['current_company_id' => $currentCompanyId]);
4649

50+
/*
51+
* Resources gate every page on Spatie permissions (canViewAny etc.),
52+
* so the test user needs the seeded client_admin permission set.
53+
*/
54+
(new PermissionsSeeder())->run();
55+
(new RolesSeeder())->run();
56+
$this->user->assignRole(UserRole::CUSTOMER_ADMIN->value);
57+
4758
$this->withoutExceptionHandling();
4859
}
4960

@@ -58,8 +69,9 @@ protected function tearDown(): void
5869
*/
5970
protected function testLivewire($component, $params = [])
6071
{
61-
return Livewire::actingAs($this->user)
62-
->withSession(['current_company_id' => $this->company->id])
63-
->test($component, $params);
72+
$this->actingAs($this->user)
73+
->withSession(['current_company_id' => $this->company->id]);
74+
75+
return Livewire::test($component, $params);
6476
}
6577
}

Modules/Invoices/Tests/Feature/InvoicesTest.php

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
use Illuminate\Support\Str;
99
use Livewire\Livewire;
1010
use Modules\Clients\Models\Relation;
11+
use Modules\Core\Enums\NumberingType;
1112
use Modules\Core\Models\Numbering;
1213
use Modules\Core\Models\TaxRate;
1314
use Modules\Core\Tests\AbstractCompanyPanelTestCase;
@@ -38,7 +39,7 @@ public function it_lists_invoices(): void
3839
/* Arrange */
3940
$user = $this->user;
4041
$customer = Relation::factory()->for($this->company)->customer()->create();
41-
$documentGroup = Numbering::factory()->for($this->company)->create();
42+
$documentGroup = Numbering::factory()->for($this->company)->state(['type' => NumberingType::INVOICE->value])->create();
4243
$taxRate = TaxRate::factory()->for($this->company)->create();
4344
$productCategory = ProductCategory::factory()->for($this->company)->create();
4445
$productUnit = ProductUnit::factory()->for($this->company)->create();
@@ -87,7 +88,7 @@ public function it_creates_an_invoice_through_a_modal(): void
8788
{
8889
/* Arrange */
8990
$customer = Relation::factory()->for($this->company)->customer()->create();
90-
$documentGroup = Numbering::factory()->for($this->company)->create();
91+
$documentGroup = Numbering::factory()->for($this->company)->state(['type' => NumberingType::INVOICE->value])->create();
9192
$taxRate = TaxRate::factory()->for($this->company)->create();
9293
$productCategory = ProductCategory::factory()->for($this->company)->create();
9394
$productUnit = ProductUnit::factory()->for($this->company)->create();
@@ -141,7 +142,7 @@ public function it_fails_to_create_invoice_through_a_modal_without_required_invo
141142
{
142143
/* Arrange */
143144
$customer = Relation::factory()->for($this->company)->customer()->create();
144-
$documentGroup = Numbering::factory()->for($this->company)->create();
145+
$documentGroup = Numbering::factory()->for($this->company)->state(['type' => NumberingType::INVOICE->value])->create();
145146
$taxRate = TaxRate::factory()->for($this->company)->create();
146147
$productCategory = ProductCategory::factory()->for($this->company)->create();
147148
$productUnit = ProductUnit::factory()->for($this->company)->create();
@@ -186,7 +187,7 @@ public function it_fails_to_create_invoice_through_a_modal_without_required_invo
186187
{
187188
/* Arrange */
188189
$customer = Relation::factory()->for($this->company)->customer()->create();
189-
$documentGroup = Numbering::factory()->for($this->company)->create();
190+
$documentGroup = Numbering::factory()->for($this->company)->state(['type' => NumberingType::INVOICE->value])->create();
190191
$taxRate = TaxRate::factory()->for($this->company)->create();
191192
$productCategory = ProductCategory::factory()->for($this->company)->create();
192193
$productUnit = ProductUnit::factory()->for($this->company)->create();
@@ -229,7 +230,7 @@ public function it_fails_to_create_invoice_through_a_modal_without_required_cust
229230
{
230231
/* Arrange */
231232
$customer = Relation::factory()->for($this->company)->customer()->create();
232-
$documentGroup = Numbering::factory()->for($this->company)->create();
233+
$documentGroup = Numbering::factory()->for($this->company)->state(['type' => NumberingType::INVOICE->value])->create();
233234
$taxRate = TaxRate::factory()->for($this->company)->create();
234235
$productCategory = ProductCategory::factory()->for($this->company)->create();
235236
$productUnit = ProductUnit::factory()->for($this->company)->create();
@@ -274,7 +275,7 @@ public function it_updates_an_invoice_through_a_modal(): void
274275
{
275276
/* Arrange */
276277
$customer = Relation::factory()->for($this->company)->customer()->create();
277-
$documentGroup = Numbering::factory()->for($this->company)->create();
278+
$documentGroup = Numbering::factory()->for($this->company)->state(['type' => NumberingType::INVOICE->value])->create();
278279
$taxRate = TaxRate::factory()->for($this->company)->create();
279280
$productCategory = ProductCategory::factory()->for($this->company)->create();
280281
$productUnit = ProductUnit::factory()->for($this->company)->create();
@@ -325,7 +326,7 @@ public function it_updates_an_invoice_through_a_modal(): void
325326
public function it_creates_an_invoice_with_items(): void
326327
{
327328
$customer = Relation::factory()->for($this->company)->customer()->create();
328-
$documentGroup = Numbering::factory()->for($this->company)->create();
329+
$documentGroup = Numbering::factory()->for($this->company)->state(['type' => NumberingType::INVOICE->value])->create();
329330
$taxRate = TaxRate::factory()->for($this->company)->create();
330331
$productCategory = ProductCategory::factory()->for($this->company)->create();
331332
$productUnit = ProductUnit::factory()->for($this->company)->create();
@@ -381,7 +382,7 @@ public function it_fails_to_create_invoice_without_required_invoice_number(): vo
381382
/* Arrange */
382383
$user = $this->user;
383384
$customer = Relation::factory()->for($this->company)->customer()->create();
384-
$documentGroup = Numbering::factory()->for($this->company)->create();
385+
$documentGroup = Numbering::factory()->for($this->company)->state(['type' => NumberingType::INVOICE->value])->create();
385386
$taxRate = TaxRate::factory()->for($this->company)->create();
386387
$productCategory = ProductCategory::factory()->for($this->company)->create();
387388
$productUnit = ProductUnit::factory()->for($this->company)->create();
@@ -425,7 +426,7 @@ public function it_fails_to_create_invoice_without_required_invoice_status(): vo
425426
/* Arrange */
426427
$user = $this->user;
427428
$customer = Relation::factory()->for($this->company)->customer()->create();
428-
$documentGroup = Numbering::factory()->for($this->company)->create();
429+
$documentGroup = Numbering::factory()->for($this->company)->state(['type' => NumberingType::INVOICE->value])->create();
429430
$taxRate = TaxRate::factory()->for($this->company)->create();
430431
$productCategory = ProductCategory::factory()->for($this->company)->create();
431432
$productUnit = ProductUnit::factory()->for($this->company)->create();
@@ -467,7 +468,7 @@ public function it_fails_to_create_invoice_without_required_customer(): void
467468
/* Arrange */
468469
$user = $this->user;
469470
$customer = Relation::factory()->for($this->company)->customer()->create();
470-
$documentGroup = Numbering::factory()->for($this->company)->create();
471+
$documentGroup = Numbering::factory()->for($this->company)->state(['type' => NumberingType::INVOICE->value])->create();
471472
$taxRate = TaxRate::factory()->for($this->company)->create();
472473
$productCategory = ProductCategory::factory()->for($this->company)->create();
473474
$productUnit = ProductUnit::factory()->for($this->company)->create();
@@ -510,7 +511,7 @@ public function it_updates_an_invoice(): void
510511
{
511512
/* Arrange */
512513
$customer = Relation::factory()->for($this->company)->customer()->create();
513-
$documentGroup = Numbering::factory()->for($this->company)->create();
514+
$documentGroup = Numbering::factory()->for($this->company)->state(['type' => NumberingType::INVOICE->value])->create();
514515
$taxRate = TaxRate::factory()->for($this->company)->create();
515516
$productCategory = ProductCategory::factory()->for($this->company)->create();
516517
$productUnit = ProductUnit::factory()->for($this->company)->create();
@@ -586,7 +587,7 @@ public function it_deletes_an_invoice(): void
586587
/* Arrange */
587588
$user = $this->user;
588589
$customer = Relation::factory()->for($this->company)->customer()->create();
589-
$documentGroup = Numbering::factory()->for($this->company)->create();
590+
$documentGroup = Numbering::factory()->for($this->company)->state(['type' => NumberingType::INVOICE->value])->create();
590591
$taxRate = TaxRate::factory()->for($this->company)->create();
591592
$productCategory = ProductCategory::factory()->for($this->company)->create();
592593
$productUnit = ProductUnit::factory()->for($this->company)->create();

Modules/Quotes/Tests/Feature/QuotesTest.php

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
use Illuminate\Support\Str;
88
use Livewire\Livewire;
99
use Modules\Clients\Models\Relation;
10+
use Modules\Core\Enums\NumberingType;
1011
use Modules\Core\Models\Numbering;
1112
use Modules\Core\Models\TaxRate;
1213
use Modules\Core\Tests\AbstractCompanyPanelTestCase;
@@ -65,7 +66,7 @@ public function it_creates_a_quote_through_a_modal(): void
6566
{
6667
/* Arrange */
6768
$prospect = Relation::factory()->for($this->company)->prospect()->create();
68-
$documentGroup = Numbering::factory()->for($this->company)->create();
69+
$documentGroup = Numbering::factory()->for($this->company)->state(['type' => NumberingType::QUOTE->value])->create();
6970

7071
$taxRate = TaxRate::factory()->for($this->company)->create();
7172
$productCategory = ProductCategory::factory()->for($this->company)->create();
@@ -139,7 +140,7 @@ public function it_creates_a_quote_through_a_modal(): void
139140
public function it_fails_to_create_a_quote_through_a_modal_without_required_prospect(): void
140141
{
141142
/* Arrange */
142-
$documentGroup = Numbering::factory()->for($this->company)->create();
143+
$documentGroup = Numbering::factory()->for($this->company)->state(['type' => NumberingType::QUOTE->value])->create();
143144

144145
$taxRate = TaxRate::factory()->for($this->company)->create();
145146
$productCategory = ProductCategory::factory()->for($this->company)->create();
@@ -295,7 +296,7 @@ public function it_updates_a_quote_through_a_modal(): void
295296
{
296297
/* Arrange */
297298
$prospect = Relation::factory()->for($this->company)->prospect()->create();
298-
$documentGroup = Numbering::factory()->for($this->company)->create();
299+
$documentGroup = Numbering::factory()->for($this->company)->state(['type' => NumberingType::QUOTE->value])->create();
299300

300301
$quote = Quote::factory()
301302
->for($this->company)
@@ -348,7 +349,7 @@ public function it_creates_a_quote(): void
348349
{
349350
/* Arrange */
350351
$prospect = Relation::factory()->for($this->company)->prospect()->create();
351-
$documentGroup = Numbering::factory()->for($this->company)->create();
352+
$documentGroup = Numbering::factory()->for($this->company)->state(['type' => NumberingType::QUOTE->value])->create();
352353

353354
$taxRate = TaxRate::factory()->for($this->company)->create();
354355
$productCategory = ProductCategory::factory()->for($this->company)->create();

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# InvoicePlane v2
22

33
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
4-
[![PHP Version](https://img.shields.io/badge/PHP-8.2%2B-blue.svg)](https://php.net)
4+
[![PHP Version](https://img.shields.io/badge/PHP-8.3%2B-blue.svg)](https://php.net)
55
[![Laravel Version](https://img.shields.io/badge/Laravel-13%2B-red.svg)](https://laravel.com)
66
[![Filament Version](https://img.shields.io/badge/Filament-5.x-orange.svg)](https://filamentphp.com)
77

@@ -44,7 +44,7 @@
4444

4545
## 📦 Requirements
4646

47-
- **PHP** 8.2 or higher
47+
- **PHP** 8.3 or higher
4848
- **Composer** 2.x
4949
- **Node.js** 20+ and Yarn
5050
- **Database** MariaDB 10.11+ (recommended), MySQL 8.0+, or SQLite (dev only)

0 commit comments

Comments
 (0)