Skip to content

Commit 63e2c91

Browse files
committed
Add Pest PHP testing framework and update PHPUnit configuration
1 parent e18cc63 commit 63e2c91

File tree

9 files changed

+879
-12
lines changed

9 files changed

+879
-12
lines changed

composer.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@
2828
"larastan/larastan": "^3.8",
2929
"mockery/mockery": "^1.6",
3030
"orchestra/testbench": "10.8",
31+
"pestphp/pest": "^4.1",
32+
"pestphp/pest-plugin-laravel": "^4.0",
3133
"phpstan/extension-installer": "^1.4",
3234
"phpstan/phpstan": "^2.1",
3335
"phpstan/phpstan-deprecation-rules": "^2.0",

phpunit.xml

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,24 +3,17 @@
33
xsi:noNamespaceSchemaLocation="vendor/phpunit/phpunit/phpunit.xsd"
44
bootstrap="vendor/autoload.php"
55
backupGlobals="false"
6-
backupStaticAttributes="false"
76
colors="true"
8-
verbose="true"
9-
convertErrorsToExceptions="true"
10-
convertNoticesToExceptions="true"
11-
convertWarningsToExceptions="true"
127
processIsolation="false"
138
stopOnFailure="true">
149
<testsuites>
1510
<testsuite name="Default">
1611
<directory suffix="Test.php">tests/</directory>
1712
</testsuite>
1813
</testsuites>
19-
<deleteCoverage-->
20-
<coverage processUncoveredFiles="true" ignoreDeprecatedCodeUnits="true">
21-
<include>
22-
<directory suffix=".php">src</directory>
23-
</include>
24-
</coverage>
25-
</deleteCoverage-->
14+
<source>
15+
<include>
16+
<directory suffix=".php">src</directory>
17+
</include>
18+
</source>
2619
</phpunit>

tests/Pest.php

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<?php
2+
3+
use Orchestra\Testbench\TestCase;
4+
use LaravelReady\ModelSupport\ServiceProvider;
5+
6+
uses(TestCase::class)->in(__DIR__);
7+
8+
// Set up the package service provider for testing
9+
uses()->beforeEach(function () {
10+
//
11+
})->in(__DIR__);
12+
13+
function getPackageProviders($app)
14+
{
15+
return [
16+
ServiceProvider::class,
17+
];
18+
}

tests/Traits/HasActiveTest.php

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
<?php
2+
3+
namespace Tests\Traits;
4+
5+
use Illuminate\Database\Eloquent\Model;
6+
use Illuminate\Database\Schema\Blueprint;
7+
use Illuminate\Support\Facades\Config;
8+
use Illuminate\Support\Facades\Schema;
9+
use LaravelReady\ModelSupport\Traits\HasActive;
10+
11+
beforeEach(function () {
12+
// Create a test table
13+
Schema::create('test_active_models', function (Blueprint $table) {
14+
$table->id();
15+
$table->boolean('is_active')->default(true);
16+
$table->string('name');
17+
$table->timestamps();
18+
});
19+
20+
// Create test model class
21+
$this->modelClass = new class extends Model {
22+
use HasActive;
23+
24+
protected $table = 'test_active_models';
25+
protected $fillable = ['name'];
26+
};
27+
28+
// Create test data
29+
$this->modelClass::create(['is_active' => true, 'name' => 'Active Item 1']);
30+
$this->modelClass::create(['is_active' => true, 'name' => 'Active Item 2']);
31+
$this->modelClass::create(['is_active' => false, 'name' => 'Inactive Item 1']);
32+
$this->modelClass::create(['is_active' => false, 'name' => 'Inactive Item 2']);
33+
});
34+
35+
afterEach(function () {
36+
Schema::dropIfExists('test_active_models');
37+
});
38+
39+
test('initializeHasActive adds is_active to fillable', function () {
40+
$model = $this->modelClass::create(['is_active' => true, 'name' => 'Test']);
41+
42+
expect($model->getFillable())->toContain('is_active');
43+
});
44+
45+
test('initializeHasActive adds is_active to casts as boolean', function () {
46+
$model = $this->modelClass::create(['is_active' => true, 'name' => 'Test']);
47+
48+
expect($model->getCasts())->toHaveKey('is_active')
49+
->and($model->getCasts()['is_active'])->toBe('boolean');
50+
});
51+
52+
test('scopeActive returns only active items', function () {
53+
$results = $this->modelClass::active()->get();
54+
55+
expect($results)->toHaveCount(2)
56+
->and($results->every(fn($item) => $item->is_active === true))->toBeTrue();
57+
});
58+
59+
test('scopeInactive returns only inactive items', function () {
60+
$results = $this->modelClass::inactive()->get();
61+
62+
expect($results)->toHaveCount(2)
63+
->and($results->every(fn($item) => $item->is_active === false))->toBeTrue();
64+
});
65+
66+
test('scopeStatus filters by status true', function () {
67+
$results = $this->modelClass::status(true)->get();
68+
69+
expect($results)->toHaveCount(2)
70+
->and($results->every(fn($item) => $item->is_active === true))->toBeTrue();
71+
});
72+
73+
test('scopeStatus filters by status false', function () {
74+
$results = $this->modelClass::status(false)->get();
75+
76+
expect($results)->toHaveCount(2)
77+
->and($results->every(fn($item) => $item->is_active === false))->toBeTrue();
78+
});
79+
80+
test('is_active field is properly cast to boolean', function () {
81+
$model = $this->modelClass::create(['is_active' => 1, 'name' => 'Test']);
82+
83+
expect($model->is_active)->toBeTrue()
84+
->and($model->is_active)->toBeBool();
85+
86+
$model = $this->modelClass::create(['is_active' => 0, 'name' => 'Test 2']);
87+
88+
expect($model->is_active)->toBeFalse()
89+
->and($model->is_active)->toBeBool();
90+
});
91+
92+
test('scopeActive respects custom is_active field from config', function () {
93+
Config::set('has_active.is_active', 'status');
94+
95+
Schema::create('test_custom_active_models', function (Blueprint $table) {
96+
$table->id();
97+
$table->boolean('status')->default(true);
98+
$table->string('name');
99+
$table->timestamps();
100+
});
101+
102+
$customModel = new class extends Model {
103+
use HasActive;
104+
105+
protected $table = 'test_custom_active_models';
106+
protected $fillable = ['name'];
107+
};
108+
109+
$customModel::create(['status' => true, 'name' => 'Active']);
110+
$customModel::create(['status' => false, 'name' => 'Inactive']);
111+
112+
$results = $customModel::active()->get();
113+
114+
expect($results)->toHaveCount(1)
115+
->and($results->first()->status)->toBeTrue();
116+
117+
Schema::dropIfExists('test_custom_active_models');
118+
Config::set('has_active.is_active', 'is_active');
119+
});
120+
121+
test('scopes can be chained with other query methods', function () {
122+
$this->modelClass::create(['is_active' => true, 'name' => 'Active Special']);
123+
124+
$results = $this->modelClass::active()
125+
->where('name', 'like', '%Special%')
126+
->get();
127+
128+
expect($results)->toHaveCount(1)
129+
->and($results->first()->name)->toBe('Active Special');
130+
});

tests/Traits/HasLanguageTest.php

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
<?php
2+
3+
namespace Tests\Traits;
4+
5+
use Illuminate\Database\Eloquent\Model;
6+
use Illuminate\Database\Schema\Blueprint;
7+
use Illuminate\Support\Facades\Config;
8+
use Illuminate\Support\Facades\Schema;
9+
use LaravelReady\ModelSupport\Traits\HasLanguage;
10+
11+
beforeEach(function () {
12+
// Create a test table
13+
Schema::create('test_models', function (Blueprint $table) {
14+
$table->id();
15+
$table->string('lang');
16+
$table->string('name');
17+
$table->timestamps();
18+
});
19+
20+
// Create test model class
21+
$this->modelClass = new class extends Model {
22+
use HasLanguage;
23+
24+
protected $table = 'test_models';
25+
protected $guarded = [];
26+
};
27+
28+
// Create test data
29+
$this->modelClass::create(['lang' => 'en', 'name' => 'English Item']);
30+
$this->modelClass::create(['lang' => 'tr', 'name' => 'Turkish Item']);
31+
$this->modelClass::create(['lang' => 'de', 'name' => 'German Item']);
32+
$this->modelClass::create(['lang' => 'fr', 'name' => 'French Item']);
33+
});
34+
35+
afterEach(function () {
36+
Schema::dropIfExists('test_models');
37+
});
38+
39+
test('scopeLang filters by language', function () {
40+
$results = $this->modelClass::lang('en')->get();
41+
42+
expect($results)->toHaveCount(1)
43+
->and($results->first()->lang)->toBe('en')
44+
->and($results->first()->name)->toBe('English Item');
45+
});
46+
47+
test('scopeLangNot filters out specific language', function () {
48+
$results = $this->modelClass::langNot('en')->get();
49+
50+
expect($results)->toHaveCount(3)
51+
->and($results->pluck('lang')->toArray())->not->toContain('en');
52+
});
53+
54+
test('scopeLangIn filters by multiple languages', function () {
55+
$results = $this->modelClass::langIn(['en', 'tr'])->get();
56+
57+
expect($results)->toHaveCount(2)
58+
->and($results->pluck('lang')->toArray())->toBe(['en', 'tr']);
59+
});
60+
61+
test('scopeLangNotIn filters out multiple languages', function () {
62+
$results = $this->modelClass::langNotIn(['en', 'tr'])->get();
63+
64+
expect($results)->toHaveCount(2)
65+
->and($results->pluck('lang')->toArray())->toBe(['de', 'fr']);
66+
});
67+
68+
test('scopeLang respects custom language field from config', function () {
69+
Config::set('has_language.language_field', 'custom_lang');
70+
71+
Schema::create('test_custom_models', function (Blueprint $table) {
72+
$table->id();
73+
$table->string('custom_lang');
74+
$table->string('name');
75+
$table->timestamps();
76+
});
77+
78+
$customModel = new class extends Model {
79+
use HasLanguage;
80+
81+
protected $table = 'test_custom_models';
82+
protected $guarded = [];
83+
};
84+
85+
$customModel::create(['custom_lang' => 'en', 'name' => 'English']);
86+
$customModel::create(['custom_lang' => 'tr', 'name' => 'Turkish']);
87+
88+
$results = $customModel::lang('en')->get();
89+
90+
expect($results)->toHaveCount(1)
91+
->and($results->first()->custom_lang)->toBe('en');
92+
93+
Schema::dropIfExists('test_custom_models');
94+
Config::set('has_language.language_field', 'lang');
95+
});
96+
97+
test('all scopes can be chained with other query methods', function () {
98+
$this->modelClass::create(['lang' => 'en', 'name' => 'Another English Item']);
99+
100+
$results = $this->modelClass::lang('en')
101+
->where('name', 'English Item')
102+
->get();
103+
104+
expect($results)->toHaveCount(1)
105+
->and($results->first()->name)->toBe('English Item');
106+
});

0 commit comments

Comments
 (0)