This commit is contained in:
punkfairie 2025-02-24 20:31:25 -08:00
parent de6c64bb14
commit 7ec1c2b7f0
Signed by: punkfairie
GPG key ID: B3C5488E9A1A7CA6
6 changed files with 392 additions and 85 deletions

View file

@ -2,7 +2,6 @@
namespace App\Http\Requests;
use App\Models\Category;
use Illuminate\Foundation\Http\FormRequest;
class CategoryRequest extends FormRequest
@ -17,18 +16,6 @@ public function rules(): array
public function authorize(): bool
{
if ($this->request->has('restore')) {
return $this->user()->can('restore', $this->route('category'));
}
if ($this->routeIs('categories.store')) {
return $this->user()->can('create', Category::class);
}
if ($this->routeIs('categories.update')) {
return $this->user()->can('update', $this->route('category'));
}
return false;
return true;
}
}

View file

@ -14,6 +14,7 @@
"laravel/tinker": "^2.9"
},
"require-dev": {
"defstudio/pest-plugin-laravel-expectations": "^2.4",
"fakerphp/faker": "^1.23",
"laravel/breeze": "^2.3",
"laravel/pail": "^1.1",

99
composer.lock generated
View file

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "a739267a391adcba09c97097cbad9f52",
"content-hash": "077ed4d24289030adae8f37f202a9bbc",
"packages": [
{
"name": "brick/math",
@ -5893,6 +5893,103 @@
],
"time": "2024-12-11T14:50:44+00:00"
},
{
"name": "defstudio/pest-plugin-laravel-expectations",
"version": "v2.4.0",
"source": {
"type": "git",
"url": "https://github.com/defstudio/pest-plugin-laravel-expectations.git",
"reference": "4bfa314db13cba3271e25cb571aa8e8f73f8a2b4"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/defstudio/pest-plugin-laravel-expectations/zipball/4bfa314db13cba3271e25cb571aa8e8f73f8a2b4",
"reference": "4bfa314db13cba3271e25cb571aa8e8f73f8a2b4",
"shasum": ""
},
"require": {
"illuminate/contracts": "^10.0|^11.0.3|^12.0",
"illuminate/database": "^10.0|^11.0.3|^12.0",
"illuminate/http": "^10.0|^11.0.3|^12.0",
"illuminate/support": "^10.0|^11.0.3|^12.0",
"illuminate/testing": "^10.0|^11.0.3|^12.0",
"pestphp/pest": "^2.0|^3.0",
"pestphp/pest-plugin": "^2.0|^3.0",
"pestphp/pest-plugin-laravel": "^2.0|^3.0",
"php": "^8.1.0"
},
"require-dev": {
"ergebnis/phpstan-rules": "^2.1.0",
"laravel/pint": "^1.11.0",
"nesbot/carbon": "^2.62.1",
"orchestra/testbench": "^8.0|^9.0",
"phpstan/phpstan": "^1.10.29",
"phpstan/phpstan-strict-rules": "^1.5.1",
"rector/rector": "^1.0.3",
"symfony/var-dumper": "^6.3.3|^v7.0.4",
"symplify/phpstan-rules": "^12.1.4.72",
"thecodingmachine/phpstan-strict-rules": "^1.0.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.x-dev"
}
},
"autoload": {
"files": [
"src/Autoload.php"
],
"psr-4": {
"DefStudio\\PestLaravelExpectations\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabio Ivona",
"email": "fabio.ivona@defstudio.it",
"homepage": "https://defstudio.it",
"role": "Developer"
},
{
"name": "Daniele Romeo",
"email": "danieleromeo@defstudio.it",
"homepage": "https://defstudio.it",
"role": "Developer"
}
],
"description": "A plugin to add laravel tailored expectations to Pest",
"keywords": [
"expectations",
"framework",
"laravel",
"pest",
"php",
"plugin",
"test",
"testing",
"unit"
],
"support": {
"issues": "https://github.com/defstudio/pest-plugin-laravel-expectations/issues",
"source": "https://github.com/defstudio/pest-plugin-laravel-expectations/tree/v2.4.0"
},
"funding": [
{
"url": "https://github.com/defstudio",
"type": "github"
},
{
"url": "https://github.com/fabio-ivona",
"type": "github"
}
],
"time": "2025-02-22T11:50:10+00:00"
},
{
"name": "doctrine/deprecations",
"version": "1.1.4",

View file

@ -6,65 +6,286 @@
covers(CategoryController::class);
test('guests can view category index', function () {
$response = $this->get(route('categories.index'));
dataset('invalid-values', [null, true, 50]);
dataset('valid-values', ['hello', 'hello there', 'h#llo', 'h3llo']);
$response->assertOk();
describe('categories.index', function () {
test('can be rendered to guests', function () {
$response = $this->get(route('categories.index'));
$response->assertOk();
});
test('can be rendered to logged in users', function () {
$user = User::factory()->create();
$response = $this->actingAs($user)->get(route('categories.index'));
$response->assertOk();
});
test('contains categories', function () {
$response = $this->get(route('categories.index'));
$response->assertViewHas('categories', Category::all());
expect($response['categories'])
->toBeEloquentCollection()
->toContainOnlyInstancesOf(Category::class);
});
test('contains trashedCategories', function () {
$response = $this->get(route('categories.index'));
$response->assertViewHas(
'trashedCategories',
Category::onlyTrashed()->get()
);
expect($response['categories'])
->toBeEloquentCollection()
->toContainOnlyInstancesOf(Category::class);
});
});
test('logged in users can view category index', function () {
$user = User::factory()->create();
$response = $this->actingAs($user)->get(route('categories.index'));
describe('categories.create', function () {
test("can't be rendered to guests", function () {
$response = $this->get(route('categories.create'));
$response->assertOk();
$response->assertForbidden();
});
test('can be rendered to logged in users', function () {
$user = User::factory()->create();
$response = $this->actingAs($user)->get(route('categories.create'));
$response->assertOk();
});
});
test('categories.index request contains categories', function () {
$response = $this->get(route('categories.index'));
describe('categories.store', function () {
test("can't be accessed by guests", function () {
$category = Category::factory()->make();
$response->assertViewHas('categories', Category::all());
expect($response['categories'])
->toBeEloquentCollection()
->toContainOnlyInstancesOf(Category::class);
$response = $this->post(
route('categories.store'),
['name' => $category->name]
);
$response->assertForbidden();
expect($category)->not->toMatchDatabaseRecord();
});
test('can be accessed by logged in users', function () {
$user = User::factory()->create();
$category = Category::factory()->make();
$response = $this->actingAs($user)->post(
route('categories.store'),
['name' => $category->name]
);
$response->assertRedirect(route('categories.index', absolute: false));
expect($category)->toMatchDatabaseRecord();
});
test('stores valid categories', function (string $name) {
$user = User::factory()->create();
$category = Category::factory()->make(['name' => $name]);
$response = $this->actingAs($user)->post(
route('categories.store'),
['name' => $name]
);
$response->assertRedirect(route('categories.index', absolute: false));
expect($category)->toMatchDatabaseRecord();
})->with('valid-values');
test('does not store invalid categories', function (mixed $name) {
$user = User::factory()->create();
$category = Category::factory()->make(['name' => $name]);
$response = $this->actingAs($user)->post(
route('categories.store'),
['name' => $name],
);
$response->assertRedirect();
expect($category)->not->toMatchDatabaseRecord();
})->with('invalid-values');
});
test('categories.index request contains trashedCategories', function () {
$response = $this->get(route('categories.index'));
describe('categories.show', function () {
test('can be rendered to guests', function () {
$category = Category::factory()->create();
$response
->assertViewHas('trashedCategories', Category::onlyTrashed()->get());
expect($response['categories'])
->toBeEloquentCollection()
->toContainOnlyInstancesOf(Category::class);
$response = $this->get(route('categories.show', $category));
$response->assertOk();
});
test('can be rendered to logged in users', function () {
$user = User::factory()->create();
$category = Category::factory()->create();
$response = $this
->actingAs($user)
->get(route('categories.show', $category));
$response->assertOk();
});
test('contains the category', function () {
$category = Category::factory()->create();
$response = $this->get(route('categories.show', $category));
$response->assertViewHas('category', $category);
});
test('can show trashed categories', function () {
$category = Category::factory()->trashed()->create();
$response = $this->get(route('categories.show', $category));
$response->assertViewHas('category', $category);
expect($category)->toBeSoftDeleted();
});
});
test('guests can view categories', function () {
$category = Category::factory()->create();
$response = $this->get(route('categories.show', $category));
describe('categories.edit', function () {
test("can't be rendered to guests", function () {
$category = Category::factory()->create();
$response->assertOk();
$response = $this->get(route('categories.edit', $category));
$response->assertForbidden();
});
test('can be rendered to logged in users', function () {
$user = User::factory()->create();
$category = Category::factory()->create();
$response = $this
->actingAs($user)
->get(route('categories.edit', $category));
$response->assertOk();
});
test('contains the category', function () {
$user = User::factory()->create();
$category = Category::factory()->create();
$response = $this
->actingAs($user)
->get(route('categories.edit', $category));
$response->assertViewHas('category', $category);
});
});
test('logged in users can view categories', function () {
$user = User::factory()->create();
$category = Category::factory()->create();
$response = $this
->actingAs($user)
->get(route('categories.show', $category));
describe('categories.update', function () {
test("can't be accessed by guests", function () {
$category = Category::factory()->create();
$updated = Category::factory()->make();
$response->assertOk();
$response = $this->patch(route('categories.update', $category), [
'name' => $updated->name
]);
$response->assertForbidden();
expect($category)->toBeInDatabaseExactly();
});
test('can be accessed by logged in users', function () {
$user = User::factory()->create();
$category = Category::factory()->create();
$updated = Category::factory()->make();
$response = $this
->actingAs($user)
->patch(route('categories.update', $category), [
'name' => $updated->name
]);
$category->refresh();
$response->assertRedirect(route('categories.index', absolute: false));
expect($category)->toMatchObject($updated);
});
test('updates categories with valid values', function (string $name) {
$user = User::factory()->create();
$category = Category::factory()->create();
$response = $this
->actingAs($user)
->patch(route('categories.update', $category), [
'name' => $name
]);
$category->refresh();
$response->assertRedirect(route('categories.index', absolute: false));
expect($category->name)->toEqual($name);
})->with('valid-values');
test(
'does not update categories with invalid values',
function (mixed $name) {
$user = User::factory()->create();
$category = Category::factory()->create();
$response = $this
->actingAs($user)
->patch(route('categories.update', $category), [
'name' => $name
]);
$updated = Category::find($category->id);
$response->assertRedirect();
expect($updated->name)->toEqual($category->name);
}
)->with('invalid-values');
test('restores trashed categories', function () {
$user = User::factory()->create();
$category = Category::factory()->trashed()->create();
$response = $this
->actingAs($user)
->patch(route('categories.update', $category), [
'restore' => 1
]);
$category->refresh();
$response->assertRedirect();
expect($category)->not->toBeSoftDeleted();
});
});
test('categories.show contains the category', function () {
$category = Category::factory()->create();
$response = $this->get(route('categories.show', $category));
describe('categories.destroy', function () {
test("can't be accessed by guests", function () {
$category = Category::factory()->create();
$response->assertViewHas('category', $category);
});
test('categories.show can show trashed categories', function () {
$category = Category::factory()->trashed()->create();
$response = $this->get(route('categories.show', $category));
$response->assertViewHas('category', $category);
$response = $this->delete(route('categories.destroy', $category));
$response->assertForbidden();
expect($category)->not->toBeSoftDeleted();
});
test('can be accessed by logged in users', function () {
$user = User::factory()->create();
$category = Category::factory()->create();
$response = $this
->actingAs($user)
->delete(route('categories.destroy', $category));
$response->assertRedirect(route('categories.index', absolute: false));
expect($category)->toBeSoftDeleted();
});
});

View file

@ -11,10 +11,10 @@
|
*/
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Database\Eloquent\Model;
use Pest\Expectation;
use function PHPUnit\Framework\assertTrue;
use function Pest\Laravel\assertDatabaseHas;
use function Pest\Laravel\assertModelExists;
pest()
->extend(Tests\TestCase::class)
@ -37,38 +37,39 @@
// https://github.com/defstudio/pest-plugin-laravel-expectations
expect()->extend(
'toBeEloquentCollection',
/**
* Assert that the value is an instance of \Illuminate\Database\Eloquent\Collection.
*/
function (): Expectation {
return $this->toBeInstanceOf(Collection::class);
}
);
'toMatchDatabaseRecord',
function (?string $table = null, ?string $connection = null): Expectation {
$this->toBeInstanceOf(Model::class);
$table = $table ?? $this->value->getTable();
$value = $this->value->attributesToArray();
expect()->extend(
'toBeSameModelAs',
/**
* Assert that the given model has the same ID and belong to the same table of another model.
*/
function (Model $model): Expectation {
/** @var Model $value */
$value = $this->value;
assertTrue($value->is($model),
'Failed asserting that two models have the same ID and belong to the same table');
assertDatabaseHas($table, $value, $connection);
return $this;
}
);
expect()->intercept(
'toBe',
Model::class,
function (Model $anotherModel) {
return expect($this->value)->toBeSameModelAs($anotherModel);
});
expect()->extend(
'toBeInDatabaseExactly',
function (?string $table = null, ?string $connection = null): Expectation {
assertModelExists($this->value);
return $this->toMatchDatabaseRecord();
}
);
expect()->pipe(
'toMatchObject',
function (Closure $next, mixed $expected) {
if ($expected instanceof Model) {
return expect($this->value)
->toMatchObject($expected->attributesToArray());
}
return $next;
}
);
/*
|--------------------------------------------------------------------------

View file

@ -12,5 +12,5 @@ abstract class TestCase extends BaseTestCase
* @var bool
* @noinspection PhpMissingFieldTypeInspection
*/
protected $seed = true;
// protected $seed = true;
}