diff --git a/tests/Feature/Admin/Master/CategoryTest.php b/tests/Feature/Admin/Master/CategoryTest.php
index f4d7d7f..37011eb 100644
--- a/tests/Feature/Admin/Master/CategoryTest.php
+++ b/tests/Feature/Admin/Master/CategoryTest.php
@@ -1,9 +1,21 @@
get(route('admin.master.categories.index'));
$response->assertRedirect(route('login'));
@@ -17,11 +29,17 @@
$response->assertOk();
});
+/*
+|--------------------------------------------------------------------------
+| INDEX PAGE
+|--------------------------------------------------------------------------
+*/
+
test('category index page displays categories', function () {
$user = User::factory()->create();
$this->actingAs($user);
- $categories = Category::factory()->count(3)->create();
+ Category::factory()->count(3)->create();
$response = $this->get(route('admin.master.categories.index'));
$response->assertOk();
@@ -31,6 +49,57 @@
);
});
+test('index page works with zero categories', function () {
+ $user = User::factory()->create();
+ $this->actingAs($user);
+
+ $response = $this->get(route('admin.master.categories.index'));
+ $response->assertOk();
+ $response->assertInertia(fn (Assert $page) => $page
+ ->component('admin/master/category/index')
+ ->has('categories', 0)
+ );
+});
+
+test('index page does not display soft-deleted categories', function () {
+ $user = User::factory()->create();
+ $this->actingAs($user);
+
+ Category::factory()->create(['name' => 'Active Category']);
+ Category::factory()->create(['name' => 'Deleted Category'])->delete();
+
+ $response = $this->get(route('admin.master.categories.index'));
+ $response->assertOk();
+ $response->assertInertia(fn (Assert $page) => $page
+ ->component('admin/master/category/index')
+ ->has('categories', 1)
+ ->where('categories.0.name', 'Active Category')
+ );
+});
+
+test('index page displays correct count after delete', function () {
+ $user = User::factory()->create();
+ $this->actingAs($user);
+
+ Category::factory()->count(5)->create();
+ $category = Category::first();
+
+ $this->delete(route('admin.master.categories.destroy', $category));
+
+ $response = $this->get(route('admin.master.categories.index'));
+ $response->assertOk();
+ $response->assertInertia(fn (Assert $page) => $page
+ ->component('admin/master/category/index')
+ ->has('categories', 4)
+ );
+});
+
+/*
+|--------------------------------------------------------------------------
+| CREATE / STORE
+|--------------------------------------------------------------------------
+*/
+
test('category can be created', function () {
$user = User::factory()->create();
$this->actingAs($user);
@@ -49,6 +118,20 @@
]);
});
+test('category slug is automatically generated from name', function () {
+ $user = User::factory()->create();
+ $this->actingAs($user);
+
+ $this->post(route('admin.master.categories.store'), [
+ 'name' => 'Kategori Otomatis',
+ ]);
+
+ $this->assertDatabaseHas('categories', [
+ 'name' => 'Kategori Otomatis',
+ 'slug' => 'kategori-otomatis',
+ ]);
+});
+
test('category name is required', function () {
$user = User::factory()->create();
$this->actingAs($user);
@@ -60,6 +143,17 @@
$response->assertSessionHasErrors('name');
});
+test('category name must be string', function () {
+ $user = User::factory()->create();
+ $this->actingAs($user);
+
+ $response = $this->post(route('admin.master.categories.store'), [
+ 'name' => 12345,
+ ]);
+
+ $response->assertSessionHasErrors('name');
+});
+
test('category name must not exceed 100 characters', function () {
$user = User::factory()->create();
$this->actingAs($user);
@@ -71,6 +165,33 @@
$response->assertSessionHasErrors('name');
});
+test('category name exactly 100 characters passes validation', function () {
+ $user = User::factory()->create();
+ $this->actingAs($user);
+
+ $response = $this->post(route('admin.master.categories.store'), [
+ 'name' => str_repeat('a', 100),
+ ]);
+
+ $response->assertSessionHasNoErrors();
+});
+
+test('category name exactly 1 character passes validation', function () {
+ $user = User::factory()->create();
+ $this->actingAs($user);
+
+ $response = $this->post(route('admin.master.categories.store'), [
+ 'name' => 'A',
+ ]);
+
+ $response->assertSessionHasNoErrors();
+
+ $this->assertDatabaseHas('categories', [
+ 'name' => 'A',
+ 'slug' => 'a',
+ ]);
+});
+
test('category name must be unique', function () {
$user = User::factory()->create();
$this->actingAs($user);
@@ -84,20 +205,253 @@
$response->assertSessionHasErrors('name');
});
-test('category slug is automatically generated', function () {
+test('category name uniqueness is case sensitive', function () {
+ $user = User::factory()->create();
+ $this->actingAs($user);
+
+ Category::factory()->create(['name' => 'Makanan']);
+
+ $response = $this->post(route('admin.master.categories.store'), [
+ 'name' => 'Makanan',
+ ]);
+
+ $response->assertSessionHasErrors('name');
+});
+
+test('category name with only whitespace is rejected', function () {
+ $user = User::factory()->create();
+ $this->actingAs($user);
+
+ $response = $this->post(route('admin.master.categories.store'), [
+ 'name' => ' ',
+ ]);
+
+ $response->assertSessionHasErrors('name');
+});
+
+test('category name with special characters is accepted', function () {
+ $user = User::factory()->create();
+ $this->actingAs($user);
+
+ $response = $this->post(route('admin.master.categories.store'), [
+ 'name' => 'Makanan & Minuman!',
+ ]);
+
+ $response->assertSessionHasNoErrors();
+
+ $this->assertDatabaseHas('categories', [
+ 'name' => 'Makanan & Minuman!',
+ 'slug' => 'makanan-minuman',
+ ]);
+});
+
+test('category name with HTML tags is accepted as plain text', function () {
+ $user = User::factory()->create();
+ $this->actingAs($user);
+
+ $response = $this->post(route('admin.master.categories.store'), [
+ 'name' => '',
+ ]);
+
+ $response->assertSessionHasNoErrors();
+
+ $this->assertDatabaseHas('categories', [
+ 'name' => '',
+ ]);
+});
+
+test('category name with numbers is accepted', function () {
+ $user = User::factory()->create();
+ $this->actingAs($user);
+
+ $response = $this->post(route('admin.master.categories.store'), [
+ 'name' => 'Kategori 123',
+ ]);
+
+ $response->assertSessionHasNoErrors();
+
+ $this->assertDatabaseHas('categories', [
+ 'name' => 'Kategori 123',
+ 'slug' => 'kategori-123',
+ ]);
+});
+
+test('category name with only numbers is accepted', function () {
+ $user = User::factory()->create();
+ $this->actingAs($user);
+
+ $response = $this->post(route('admin.master.categories.store'), [
+ 'name' => '12345',
+ ]);
+
+ $response->assertSessionHasNoErrors();
+
+ $this->assertDatabaseHas('categories', [
+ 'name' => '12345',
+ 'slug' => '12345',
+ ]);
+});
+
+test('category name with only punctuation is accepted', function () {
+ $user = User::factory()->create();
+ $this->actingAs($user);
+
+ $response = $this->post(route('admin.master.categories.store'), [
+ 'name' => '---',
+ ]);
+
+ $response->assertSessionHasNoErrors();
+});
+
+test('category name with parentheses and brackets is accepted', function () {
+ $user = User::factory()->create();
+ $this->actingAs($user);
+
+ $response = $this->post(route('admin.master.categories.store'), [
+ 'name' => 'Kategori (A) [VIP]',
+ ]);
+
+ $response->assertSessionHasNoErrors();
+
+ $this->assertDatabaseHas('categories', [
+ 'name' => 'Kategori (A) [VIP]',
+ 'slug' => 'kategori-a-vip',
+ ]);
+});
+
+test('category name with unicode characters is accepted', function () {
+ $user = User::factory()->create();
+ $this->actingAs($user);
+
+ $response = $this->post(route('admin.master.categories.store'), [
+ 'name' => 'Kategori Nono',
+ ]);
+
+ $response->assertSessionHasNoErrors();
+
+ $this->assertDatabaseHas('categories', [
+ 'name' => 'Kategori Nono',
+ ]);
+});
+
+test('category name with mixed case is stored as entered', function () {
+ $user = User::factory()->create();
+ $this->actingAs($user);
+
+ $response = $this->post(route('admin.master.categories.store'), [
+ 'name' => 'makanAN MinUMAN',
+ ]);
+
+ $response->assertSessionHasNoErrors();
+
+ $this->assertDatabaseHas('categories', [
+ 'name' => 'makanAN MinUMAN',
+ 'slug' => 'makanan-minuman',
+ ]);
+});
+
+test('category name with multiple spaces generates correct slug', function () {
+ $user = User::factory()->create();
+ $this->actingAs($user);
+
+ $response = $this->post(route('admin.master.categories.store'), [
+ 'name' => 'Kategori dengan spasi banyak',
+ ]);
+
+ $response->assertSessionHasNoErrors();
+
+ $this->assertDatabaseHas('categories', [
+ 'name' => 'Kategori dengan spasi banyak',
+ 'slug' => 'kategori-dengan-spasi-banyak',
+ ]);
+});
+
+test('category name with leading and trailing spaces is accepted', function () {
+ $user = User::factory()->create();
+ $this->actingAs($user);
+
+ $response = $this->post(route('admin.master.categories.store'), [
+ 'name' => ' Kategori Spasi ',
+ ]);
+
+ $response->assertSessionHasNoErrors();
+});
+
+test('category name with newline characters is accepted', function () {
+ $user = User::factory()->create();
+ $this->actingAs($user);
+
+ $response = $this->post(route('admin.master.categories.store'), [
+ 'name' => "Kategori\nBaru",
+ ]);
+
+ $response->assertSessionHasNoErrors();
+});
+
+test('category can be created with name at exact DB column limit (50 chars)', function () {
+ $user = User::factory()->create();
+ $this->actingAs($user);
+
+ $name = str_repeat('a', 50);
+
+ $response = $this->post(route('admin.master.categories.store'), [
+ 'name' => $name,
+ ]);
+
+ $response->assertSessionHasNoErrors();
+
+ $this->assertDatabaseHas('categories', [
+ 'name' => $name,
+ ]);
+});
+
+test('store flashes success toast via Inertia', function () {
+ $user = User::factory()->create();
+ $this->actingAs($user);
+
+ $response = $this->post(route('admin.master.categories.store'), [
+ 'name' => 'Kategori Toast',
+ ]);
+
+ $response->assertRedirect();
+});
+
+test('store creates new category in database', function () {
$user = User::factory()->create();
$this->actingAs($user);
$this->post(route('admin.master.categories.store'), [
- 'name' => 'Kategori Otomatis',
+ 'name' => 'Database Check',
]);
+ $this->assertDatabaseCount('categories', 1);
$this->assertDatabaseHas('categories', [
- 'name' => 'Kategori Otomatis',
- 'slug' => 'kategori-otomatis',
+ 'name' => 'Database Check',
+ 'slug' => 'database-check',
]);
});
+test('multiple categories can be created sequentially', function () {
+ $user = User::factory()->create();
+ $this->actingAs($user);
+
+ $this->post(route('admin.master.categories.store'), ['name' => 'Kategori 1']);
+ $this->post(route('admin.master.categories.store'), ['name' => 'Kategori 2']);
+ $this->post(route('admin.master.categories.store'), ['name' => 'Kategori 3']);
+
+ $this->assertDatabaseCount('categories', 3);
+
+ $this->assertDatabaseHas('categories', ['name' => 'Kategori 1', 'slug' => 'kategori-1']);
+ $this->assertDatabaseHas('categories', ['name' => 'Kategori 2', 'slug' => 'kategori-2']);
+ $this->assertDatabaseHas('categories', ['name' => 'Kategori 3', 'slug' => 'kategori-3']);
+});
+
+/*
+|--------------------------------------------------------------------------
+| UPDATE / PUT
+|--------------------------------------------------------------------------
+*/
+
test('category can be updated', function () {
$user = User::factory()->create();
$this->actingAs($user);
@@ -144,6 +498,136 @@
$response->assertSessionHasErrors('name');
});
+test('category update name is required', function () {
+ $user = User::factory()->create();
+ $this->actingAs($user);
+
+ $category = Category::factory()->create();
+
+ $response = $this->put(route('admin.master.categories.update', $category), [
+ 'name' => '',
+ ]);
+
+ $response->assertSessionHasErrors('name');
+});
+
+test('category update name must not exceed 100 characters', function () {
+ $user = User::factory()->create();
+ $this->actingAs($user);
+
+ $category = Category::factory()->create();
+
+ $response = $this->put(route('admin.master.categories.update', $category), [
+ 'name' => str_repeat('a', 101),
+ ]);
+
+ $response->assertSessionHasErrors('name');
+});
+
+test('category update with special characters generates correct slug', function () {
+ $user = User::factory()->create();
+ $this->actingAs($user);
+
+ $category = Category::factory()->create(['name' => 'Old Name']);
+
+ $response = $this->put(route('admin.master.categories.update', $category), [
+ 'name' => 'New Name (Updated) & More!',
+ ]);
+
+ $response->assertSessionHasNoErrors();
+
+ $category->refresh();
+ expect($category->slug)->toBe('new-name-updated-more');
+});
+
+test('category update changes the slug to match new name', function () {
+ $user = User::factory()->create();
+ $this->actingAs($user);
+
+ $category = Category::factory()->create(['name' => 'Original', 'slug' => 'original']);
+
+ $this->put(route('admin.master.categories.update', $category), [
+ 'name' => 'Modified',
+ ]);
+
+ $category->refresh();
+ expect($category->slug)->toBe('modified');
+});
+
+test('category can be updated multiple times', function () {
+ $user = User::factory()->create();
+ $this->actingAs($user);
+
+ $category = Category::factory()->create(['name' => 'Version 1']);
+
+ $this->put(route('admin.master.categories.update', $category), ['name' => 'Version 2']);
+ $category->refresh();
+ expect($category->name)->toBe('Version 2');
+
+ $this->put(route('admin.master.categories.update', $category), ['name' => 'Version 3']);
+ $category->refresh();
+ expect($category->name)->toBe('Version 3');
+});
+
+test('update flashes success toast via Inertia', function () {
+ $user = User::factory()->create();
+ $this->actingAs($user);
+
+ $category = Category::factory()->create();
+
+ $response = $this->put(route('admin.master.categories.update', $category), [
+ 'name' => 'Updated Name',
+ ]);
+
+ $response->assertRedirect();
+});
+
+test('category update with only whitespace is rejected', function () {
+ $user = User::factory()->create();
+ $this->actingAs($user);
+
+ $category = Category::factory()->create();
+
+ $response = $this->put(route('admin.master.categories.update', $category), [
+ 'name' => ' ',
+ ]);
+
+ $response->assertSessionHasErrors('name');
+});
+
+test('category can be updated with unicode characters', function () {
+ $user = User::factory()->create();
+ $this->actingAs($user);
+
+ $category = Category::factory()->create(['name' => 'Original']);
+
+ $response = $this->put(route('admin.master.categories.update', $category), [
+ 'name' => 'Kategori Nono',
+ ]);
+
+ $response->assertSessionHasNoErrors();
+
+ $category->refresh();
+ expect($category->name)->toBe('Kategori Nono');
+});
+
+test('updating non-existent category returns 404', function () {
+ $user = User::factory()->create();
+ $this->actingAs($user);
+
+ $response = $this->put(route('admin.master.categories.update', 999999), [
+ 'name' => 'Ghost Category',
+ ]);
+
+ $response->assertStatus(404);
+});
+
+/*
+|--------------------------------------------------------------------------
+| DELETE / DESTROY
+|--------------------------------------------------------------------------
+*/
+
test('category can be deleted', function () {
$user = User::factory()->create();
$this->actingAs($user);
@@ -158,3 +642,1181 @@
$this->assertSoftDeleted('categories', ['id' => $category->id]);
});
+
+test('category is soft-deleted not permanently removed', function () {
+ $user = User::factory()->create();
+ $this->actingAs($user);
+
+ $category = Category::factory()->create();
+
+ $this->delete(route('admin.master.categories.destroy', $category));
+
+ $this->assertDatabaseHas('categories', ['id' => $category->id]);
+ $this->assertSoftDeleted('categories', ['id' => $category->id]);
+});
+
+test('soft-deleted category still exists in database', function () {
+ $user = User::factory()->create();
+ $this->actingAs($user);
+
+ $category = Category::factory()->create();
+
+ $this->delete(route('admin.master.categories.destroy', $category));
+
+ $this->assertDatabaseHas('categories', ['id' => $category->id]);
+});
+
+test('soft-deleted category has deleted_at timestamp', function () {
+ $user = User::factory()->create();
+ $this->actingAs($user);
+
+ $category = Category::factory()->create();
+
+ $this->delete(route('admin.master.categories.destroy', $category));
+
+ $category->refresh();
+ expect($category->deleted_at)->not->toBeNull();
+});
+
+test('deleted category is excluded from getAll query', function () {
+ $user = User::factory()->create();
+ $this->actingAs($user);
+
+ Category::factory()->create(['name' => 'Active']);
+ $deleted = Category::factory()->create(['name' => 'Deleted']);
+ $deleted->delete();
+
+ $response = $this->get(route('admin.master.categories.index'));
+ $response->assertOk();
+ $response->assertInertia(fn (Assert $page) => $page
+ ->component('admin/master/category/index')
+ ->has('categories', 1)
+ ->where('categories.0.name', 'Active')
+ );
+});
+
+test('delete flashes success toast via Inertia', function () {
+ $user = User::factory()->create();
+ $this->actingAs($user);
+
+ $category = Category::factory()->create();
+
+ $response = $this->delete(route('admin.master.categories.destroy', $category));
+
+ $response->assertRedirect();
+});
+
+test('multiple categories can be deleted one by one', function () {
+ $user = User::factory()->create();
+ $this->actingAs($user);
+
+ $categories = Category::factory()->count(3)->create();
+
+ $this->delete(route('admin.master.categories.destroy', $categories[0]));
+ $this->delete(route('admin.master.categories.destroy', $categories[1]));
+
+ $this->assertSoftDeleted('categories', ['id' => $categories[0]->id]);
+ $this->assertSoftDeleted('categories', ['id' => $categories[1]->id]);
+ $this->assertDatabaseHas('categories', ['id' => $categories[2]->id, 'deleted_at' => null]);
+});
+
+test('updating soft-deleted category via route returns 404', function () {
+ $user = User::factory()->create();
+ $this->actingAs($user);
+
+ $category = Category::factory()->create();
+ $category->delete();
+
+ $response = $this->put(route('admin.master.categories.update', $category), [
+ 'name' => 'Should Not Work',
+ ]);
+
+ $response->assertStatus(404);
+});
+
+test('deleting soft-deleted category via route returns 404', function () {
+ $user = User::factory()->create();
+ $this->actingAs($user);
+
+ $category = Category::factory()->create();
+ $category->delete();
+
+ $response = $this->delete(route('admin.master.categories.destroy', $category));
+
+ $response->assertStatus(404);
+});
+
+test('deleting non-existent category returns 404', function () {
+ $user = User::factory()->create();
+ $this->actingAs($user);
+
+ $response = $this->delete(route('admin.master.categories.destroy', 999999));
+
+ $response->assertStatus(404);
+});
+
+/*
+|--------------------------------------------------------------------------
+| CATEGORY - PRODUCT RELATIONSHIP
+|--------------------------------------------------------------------------
+*/
+
+test('category with products can be deleted without deleting products', function () {
+ $user = User::factory()->create();
+ $this->actingAs($user);
+
+ $category = Category::factory()->create();
+ $product = Product::factory()->create();
+ DB::table('product_categories')->insert([
+ 'product_id' => $product->id,
+ 'category_id' => $category->id,
+ ]);
+
+ $this->delete(route('admin.master.categories.destroy', $category));
+
+ $this->assertSoftDeleted('categories', ['id' => $category->id]);
+ $this->assertDatabaseHas('products', ['id' => $product->id, 'deleted_at' => null]);
+});
+
+test('category-product pivot record remains after soft delete', function () {
+ $user = User::factory()->create();
+ $this->actingAs($user);
+
+ $category = Category::factory()->create();
+ $product = Product::factory()->create();
+ DB::table('product_categories')->insert([
+ 'product_id' => $product->id,
+ 'category_id' => $category->id,
+ ]);
+
+ $this->assertDatabaseHas('product_categories', [
+ 'category_id' => $category->id,
+ 'product_id' => $product->id,
+ ]);
+
+ $this->delete(route('admin.master.categories.destroy', $category));
+
+ // Soft delete doesn't trigger cascade delete, so pivot record remains
+ $this->assertDatabaseHas('product_categories', [
+ 'category_id' => $category->id,
+ 'product_id' => $product->id,
+ ]);
+});
+
+test('index page works correctly with categories that have products', function () {
+ $user = User::factory()->create();
+ $this->actingAs($user);
+
+ $category = Category::factory()->create();
+ $products = Product::factory()->count(3)->create();
+ foreach ($products as $product) {
+ DB::table('product_categories')->insert([
+ 'product_id' => $product->id,
+ 'category_id' => $category->id,
+ ]);
+ }
+
+ $response = $this->get(route('admin.master.categories.index'));
+ $response->assertOk();
+ $response->assertInertia(fn (Assert $page) => $page
+ ->component('admin/master/category/index')
+ ->has('categories', 1)
+ );
+});
+
+test('category with many products can be deleted', function () {
+ $user = User::factory()->create();
+ $this->actingAs($user);
+
+ $category = Category::factory()->create();
+ $products = Product::factory()->count(10)->create();
+ foreach ($products as $product) {
+ DB::table('product_categories')->insert([
+ 'product_id' => $product->id,
+ 'category_id' => $category->id,
+ ]);
+ }
+
+ $this->delete(route('admin.master.categories.destroy', $category));
+
+ $this->assertSoftDeleted('categories', ['id' => $category->id]);
+ $this->assertDatabaseCount('products', 10);
+});
+
+test('deleting category does not affect other categories products belong to', function () {
+ $user = User::factory()->create();
+ $this->actingAs($user);
+
+ $category1 = Category::factory()->create();
+ $category2 = Category::factory()->create();
+ $product = Product::factory()->create();
+ DB::table('product_categories')->insert([
+ ['product_id' => $product->id, 'category_id' => $category1->id],
+ ['product_id' => $product->id, 'category_id' => $category2->id],
+ ]);
+
+ $this->delete(route('admin.master.categories.destroy', $category1));
+
+ $this->assertSoftDeleted('categories', ['id' => $category1->id]);
+ $this->assertDatabaseHas('categories', ['id' => $category2->id, 'deleted_at' => null]);
+ $this->assertDatabaseHas('product_categories', [
+ 'product_id' => $product->id,
+ 'category_id' => $category2->id,
+ ]);
+});
+
+/*
+|--------------------------------------------------------------------------
+| SLUG EDGE CASES
+|--------------------------------------------------------------------------
+*/
+
+test('slug is generated correctly from name with ampersand', function () {
+ $user = User::factory()->create();
+ $this->actingAs($user);
+
+ $this->post(route('admin.master.categories.store'), [
+ 'name' => 'Food & Beverage',
+ ]);
+
+ $this->assertDatabaseHas('categories', [
+ 'name' => 'Food & Beverage',
+ 'slug' => Str::slug('Food & Beverage'),
+ ]);
+});
+
+test('slug is generated correctly from name with dots', function () {
+ $user = User::factory()->create();
+ $this->actingAs($user);
+
+ $this->post(route('admin.master.categories.store'), [
+ 'name' => 'Mr. Category',
+ ]);
+
+ $this->assertDatabaseHas('categories', [
+ 'name' => 'Mr. Category',
+ 'slug' => Str::slug('Mr. Category'),
+ ]);
+});
+
+test('slug is generated correctly from name with slashes', function () {
+ $user = User::factory()->create();
+ $this->actingAs($user);
+
+ $this->post(route('admin.master.categories.store'), [
+ 'name' => 'Food/Drink',
+ ]);
+
+ $this->assertDatabaseHas('categories', [
+ 'name' => 'Food/Drink',
+ 'slug' => Str::slug('Food/Drink'),
+ ]);
+});
+
+test('slug is generated correctly from name with commas', function () {
+ $user = User::factory()->create();
+ $this->actingAs($user);
+
+ $this->post(route('admin.master.categories.store'), [
+ 'name' => 'Makanan, Minuman',
+ ]);
+
+ $this->assertDatabaseHas('categories', [
+ 'name' => 'Makanan, Minuman',
+ 'slug' => Str::slug('Makanan, Minuman'),
+ ]);
+});
+
+test('slug is generated correctly from name with single quotes', function () {
+ $user = User::factory()->create();
+ $this->actingAs($user);
+
+ $this->post(route('admin.master.categories.store'), [
+ 'name' => "It's Category",
+ ]);
+
+ $this->assertDatabaseHas('categories', [
+ 'name' => "It's Category",
+ 'slug' => Str::slug("It's Category"),
+ ]);
+});
+
+test('slug is generated correctly from name with double quotes', function () {
+ $user = User::factory()->create();
+ $this->actingAs($user);
+
+ $this->post(route('admin.master.categories.store'), [
+ 'name' => '"Premium" Category',
+ ]);
+
+ $this->assertDatabaseHas('categories', [
+ 'name' => '"Premium" Category',
+ 'slug' => Str::slug('"Premium" Category'),
+ ]);
+});
+
+test('slug is generated correctly from name with exclamation mark', function () {
+ $user = User::factory()->create();
+ $this->actingAs($user);
+
+ $this->post(route('admin.master.categories.store'), [
+ 'name' => 'Hot Sale!',
+ ]);
+
+ $this->assertDatabaseHas('categories', [
+ 'name' => 'Hot Sale!',
+ 'slug' => Str::slug('Hot Sale!'),
+ ]);
+});
+
+test('slug is generated correctly from name with question mark', function () {
+ $user = User::factory()->create();
+ $this->actingAs($user);
+
+ $this->post(route('admin.master.categories.store'), [
+ 'name' => 'Best Choice?',
+ ]);
+
+ $this->assertDatabaseHas('categories', [
+ 'name' => 'Best Choice?',
+ 'slug' => Str::slug('Best Choice?'),
+ ]);
+});
+
+test('slug is generated correctly from all-caps name', function () {
+ $user = User::factory()->create();
+ $this->actingAs($user);
+
+ $this->post(route('admin.master.categories.store'), [
+ 'name' => 'ELECTRONICS',
+ ]);
+
+ $this->assertDatabaseHas('categories', [
+ 'name' => 'ELECTRONICS',
+ 'slug' => Str::slug('ELECTRONICS'),
+ ]);
+});
+
+test('slug is generated correctly from name with numbers and letters mixed', function () {
+ $user = User::factory()->create();
+ $this->actingAs($user);
+
+ $this->post(route('admin.master.categories.store'), [
+ 'name' => 'iPhone 15 Pro',
+ ]);
+
+ $this->assertDatabaseHas('categories', [
+ 'name' => 'iPhone 15 Pro',
+ 'slug' => Str::slug('iPhone 15 Pro'),
+ ]);
+});
+
+test('slug is generated correctly from name with dash', function () {
+ $user = User::factory()->create();
+ $this->actingAs($user);
+
+ $this->post(route('admin.master.categories.store'), [
+ 'name' => 'Food-Grade',
+ ]);
+
+ $this->assertDatabaseHas('categories', [
+ 'name' => 'Food-Grade',
+ 'slug' => Str::slug('Food-Grade'),
+ ]);
+});
+
+test('slug is generated correctly from name with underscore', function () {
+ $user = User::factory()->create();
+ $this->actingAs($user);
+
+ $this->post(route('admin.master.categories.store'), [
+ 'name' => 'Food_Grade',
+ ]);
+
+ $this->assertDatabaseHas('categories', [
+ 'name' => 'Food_Grade',
+ 'slug' => Str::slug('Food_Grade'),
+ ]);
+});
+
+test('slug is generated correctly from name with at symbol', function () {
+ $user = User::factory()->create();
+ $this->actingAs($user);
+
+ $this->post(route('admin.master.categories.store'), [
+ 'name' => 'Category@Home',
+ ]);
+
+ $this->assertDatabaseHas('categories', [
+ 'name' => 'Category@Home',
+ 'slug' => Str::slug('Category@Home'),
+ ]);
+});
+
+test('slug is generated correctly from name with hash symbol', function () {
+ $user = User::factory()->create();
+ $this->actingAs($user);
+
+ $this->post(route('admin.master.categories.store'), [
+ 'name' => 'Category#1',
+ ]);
+
+ $this->assertDatabaseHas('categories', [
+ 'name' => 'Category#1',
+ 'slug' => Str::slug('Category#1'),
+ ]);
+});
+
+test('slug is generated correctly from name with currency symbol', function () {
+ $user = User::factory()->create();
+ $this->actingAs($user);
+
+ $this->post(route('admin.master.categories.store'), [
+ 'name' => 'Rp 50.000',
+ ]);
+
+ $this->assertDatabaseHas('categories', [
+ 'name' => 'Rp 50.000',
+ 'slug' => Str::slug('Rp 50.000'),
+ ]);
+});
+
+test('slug is generated correctly from name with pipe symbol', function () {
+ $user = User::factory()->create();
+ $this->actingAs($user);
+
+ $this->post(route('admin.master.categories.store'), [
+ 'name' => 'A|B',
+ ]);
+
+ $this->assertDatabaseHas('categories', [
+ 'name' => 'A|B',
+ 'slug' => Str::slug('A|B'),
+ ]);
+});
+
+test('slug is generated correctly from name with mixed special chars', function () {
+ $user = User::factory()->create();
+ $this->actingAs($user);
+
+ $this->post(route('admin.master.categories.store'), [
+ 'name' => 'A & B (v2.0)!',
+ ]);
+
+ $this->assertDatabaseHas('categories', [
+ 'name' => 'A & B (v2.0)!',
+ 'slug' => Str::slug('A & B (v2.0)!'),
+ ]);
+});
+
+test('slug does not change when category is updated with same name', function () {
+ $user = User::factory()->create();
+ $this->actingAs($user);
+
+ $category = Category::factory()->create(['name' => 'Same Name', 'slug' => 'same-name']);
+ $originalSlug = $category->slug;
+
+ $this->put(route('admin.master.categories.update', $category), [
+ 'name' => 'Same Name',
+ ]);
+
+ $category->refresh();
+ expect($category->slug)->toBe($originalSlug);
+});
+
+test('slug changes when category name changes', function () {
+ $user = User::factory()->create();
+ $this->actingAs($user);
+
+ $category = Category::factory()->create(['name' => 'Old Name', 'slug' => 'old-name']);
+
+ $this->put(route('admin.master.categories.update', $category), [
+ 'name' => 'New Name',
+ ]);
+
+ $category->refresh();
+ expect($category->slug)->toBe('new-name');
+ expect($category->slug)->not->toBe('old-name');
+});
+
+/*
+|--------------------------------------------------------------------------
+| AUTHORIZATION - GUEST CANNOT PERFORM ACTIONS
+|--------------------------------------------------------------------------
+*/
+
+test('guest cannot create category', function () {
+ $response = $this->post(route('admin.master.categories.store'), [
+ 'name' => 'Unauthorized',
+ ]);
+
+ $response->assertRedirect(route('login'));
+ $this->assertDatabaseMissing('categories', ['name' => 'Unauthorized']);
+});
+
+test('guest cannot update category', function () {
+ $category = Category::factory()->create();
+
+ $response = $this->put(route('admin.master.categories.update', $category), [
+ 'name' => 'Unauthorized Update',
+ ]);
+
+ $response->assertRedirect(route('login'));
+});
+
+test('guest cannot delete category', function () {
+ $category = Category::factory()->create();
+
+ $response = $this->delete(route('admin.master.categories.destroy', $category));
+
+ $response->assertRedirect(route('login'));
+ $this->assertDatabaseHas('categories', ['id' => $category->id, 'deleted_at' => null]);
+});
+
+/*
+|--------------------------------------------------------------------------
+| DATA INTEGRITY
+|--------------------------------------------------------------------------
+*/
+
+test('created category has correct timestamps', function () {
+ $user = User::factory()->create();
+ $this->actingAs($user);
+
+ $this->post(route('admin.master.categories.store'), [
+ 'name' => 'Timestamp Test',
+ ]);
+
+ $category = Category::where('name', 'Timestamp Test')->first();
+ expect($category->created_at)->not->toBeNull();
+ expect($category->updated_at)->not->toBeNull();
+});
+
+test('updated category has updated timestamp changed', function () {
+ $user = User::factory()->create();
+ $this->actingAs($user);
+
+ $category = Category::factory()->create();
+ $originalUpdatedAt = $category->updated_at;
+
+ sleep(1);
+
+ $this->put(route('admin.master.categories.update', $category), [
+ 'name' => 'Timestamp Changed',
+ ]);
+
+ $category->refresh();
+ expect($category->updated_at->gt($originalUpdatedAt))->toBeTrue();
+});
+
+test('category ID is auto-incremented', function () {
+ $user = User::factory()->create();
+ $this->actingAs($user);
+
+ $this->post(route('admin.master.categories.store'), ['name' => 'First']);
+ $first = Category::where('name', 'First')->first();
+
+ $this->post(route('admin.master.categories.store'), ['name' => 'Second']);
+ $second = Category::where('name', 'Second')->first();
+
+ expect($second->id)->toBe($first->id + 1);
+});
+
+test('category factory creates valid category', function () {
+ $category = Category::factory()->create();
+
+ expect($category->name)->not->toBeEmpty();
+ expect($category->slug)->not->toBeEmpty();
+ expect($category->id)->toBeInt();
+});
+
+test('category factory creates unique names', function () {
+ $categories = Category::factory()->count(20)->create();
+
+ $names = $categories->pluck('name')->toArray();
+ expect($names)->toHaveCount(count(array_unique($names)));
+});
+
+/*
+|--------------------------------------------------------------------------
+| SOFT DELETE & RESTORE
+|--------------------------------------------------------------------------
+*/
+
+test('category can be restored after soft delete', function () {
+ $user = User::factory()->create();
+ $this->actingAs($user);
+
+ $category = Category::factory()->create(['name' => 'Restored Category']);
+ $category->delete();
+
+ $this->assertSoftDeleted('categories', ['id' => $category->id]);
+
+ $category->restore();
+
+ $this->assertDatabaseHas('categories', ['id' => $category->id, 'deleted_at' => null]);
+});
+
+test('restored category appears in index again', function () {
+ $user = User::factory()->create();
+ $this->actingAs($user);
+
+ $category = Category::factory()->create(['name' => 'Restored Category']);
+ $category->delete();
+
+ $response = $this->get(route('admin.master.categories.index'));
+ $response->assertInertia(fn (Assert $page) => $page
+ ->component('admin/master/category/index')
+ ->has('categories', 0)
+ );
+
+ $category->restore();
+
+ $response = $this->get(route('admin.master.categories.index'));
+ $response->assertInertia(fn (Assert $page) => $page
+ ->component('admin/master/category/index')
+ ->has('categories', 1)
+ ->where('categories.0.name', 'Restored Category')
+ );
+});
+
+test('restored category can be updated', function () {
+ $user = User::factory()->create();
+ $this->actingAs($user);
+
+ $category = Category::factory()->create(['name' => 'Before Restore']);
+ $category->delete();
+ $category->restore();
+
+ $response = $this->put(route('admin.master.categories.update', $category), [
+ 'name' => 'After Restore',
+ ]);
+
+ $response->assertSessionHasNoErrors();
+
+ $category->refresh();
+ expect($category->name)->toBe('After Restore');
+});
+
+/*
+|--------------------------------------------------------------------------
+| REALISTIC USER SCENARIOS
+|--------------------------------------------------------------------------
+*/
+
+test('user creates category then immediately edits it', function () {
+ $user = User::factory()->create();
+ $this->actingAs($user);
+
+ $this->post(route('admin.master.categories.store'), ['name' => 'Draft Category']);
+ $category = Category::where('name', 'Draft Category')->first();
+
+ $this->put(route('admin.master.categories.update', $category), ['name' => 'Final Category']);
+
+ $category->refresh();
+ expect($category->name)->toBe('Final Category');
+ expect($category->slug)->toBe('final-category');
+});
+
+test('user creates multiple categories and deletes one', function () {
+ $user = User::factory()->create();
+ $this->actingAs($user);
+
+ $this->post(route('admin.master.categories.store'), ['name' => 'Keep']);
+ $this->post(route('admin.master.categories.store'), ['name' => 'Delete Me']);
+ $this->post(route('admin.master.categories.store'), ['name' => 'Also Keep']);
+
+ $toDelete = Category::where('name', 'Delete Me')->first();
+ $this->delete(route('admin.master.categories.destroy', $toDelete));
+
+ $response = $this->get(route('admin.master.categories.index'));
+ $response->assertOk();
+ $response->assertInertia(fn (Assert $page) => $page
+ ->component('admin/master/category/index')
+ ->has('categories', 2)
+ );
+});
+
+test('user renames category to match another existing category name', function () {
+ $user = User::factory()->create();
+ $this->actingAs($user);
+
+ Category::factory()->create(['name' => 'Target Name']);
+ $category = Category::factory()->create(['name' => 'Source Name']);
+
+ $response = $this->put(route('admin.master.categories.update', $category), [
+ 'name' => 'Target Name',
+ ]);
+
+ $response->assertSessionHasErrors('name');
+});
+
+test('user creates category with Indonesian characters', function () {
+ $user = User::factory()->create();
+ $this->actingAs($user);
+
+ $response = $this->post(route('admin.master.categories.store'), [
+ 'name' => 'Makanan Khas Jawa',
+ ]);
+
+ $response->assertSessionHasNoErrors();
+
+ $this->assertDatabaseHas('categories', [
+ 'name' => 'Makanan Khas Jawa',
+ 'slug' => 'makanan-khas-jawa',
+ ]);
+});
+
+test('user tries to create category without submitting any data', function () {
+ $user = User::factory()->create();
+ $this->actingAs($user);
+
+ $response = $this->post(route('admin.master.categories.store'), []);
+
+ $response->assertSessionHasErrors('name');
+});
+
+test('user tries to update category without submitting any data', function () {
+ $user = User::factory()->create();
+ $this->actingAs($user);
+
+ $category = Category::factory()->create();
+
+ $response = $this->put(route('admin.master.categories.update', $category), []);
+
+ $response->assertSessionHasErrors('name');
+});
+
+test('user rapidly submits same category creation twice', function () {
+ $user = User::factory()->create();
+ $this->actingAs($user);
+
+ $this->post(route('admin.master.categories.store'), ['name' => 'Rapid Submit']);
+ $response = $this->post(route('admin.master.categories.store'), ['name' => 'Rapid Submit']);
+
+ $response->assertSessionHasErrors('name');
+ $this->assertDatabaseCount('categories', 1);
+});
+
+test('user creates category with name containing chinese characters', function () {
+ $user = User::factory()->create();
+ $this->actingAs($user);
+
+ $response = $this->post(route('admin.master.categories.store'), ['name' => '电子产品']);
+ $response->assertSessionHasNoErrors();
+});
+
+test('user creates category with name containing japanese characters', function () {
+ $user = User::factory()->create();
+ $this->actingAs($user);
+
+ $response = $this->post(route('admin.master.categories.store'), ['name' => 'カテゴリ']);
+ $response->assertSessionHasNoErrors();
+});
+
+test('user creates category with name containing korean characters', function () {
+ $user = User::factory()->create();
+ $this->actingAs($user);
+
+ $response = $this->post(route('admin.master.categories.store'), ['name' => '카테고리']);
+ $response->assertSessionHasNoErrors();
+});
+
+test('user creates category with name containing arabic characters', function () {
+ $user = User::factory()->create();
+ $this->actingAs($user);
+
+ $response = $this->post(route('admin.master.categories.store'), ['name' => 'الفئة']);
+ $response->assertSessionHasNoErrors();
+});
+
+test('user creates category with name containing cyrillic characters', function () {
+ $user = User::factory()->create();
+ $this->actingAs($user);
+
+ $response = $this->post(route('admin.master.categories.store'), ['name' => 'Электроника']);
+ $response->assertSessionHasNoErrors();
+});
+
+test('user creates category with name containing thai characters', function () {
+ $user = User::factory()->create();
+ $this->actingAs($user);
+
+ $response = $this->post(route('admin.master.categories.store'), ['name' => 'หมวดหมู่']);
+ $response->assertSessionHasNoErrors();
+});
+
+test('user creates category with name containing hindi characters', function () {
+ $user = User::factory()->create();
+ $this->actingAs($user);
+
+ $response = $this->post(route('admin.master.categories.store'), ['name' => 'श्रेणी']);
+ $response->assertSessionHasNoErrors();
+});
+
+test('user creates category with name containing greek characters', function () {
+ $user = User::factory()->create();
+ $this->actingAs($user);
+
+ $response = $this->post(route('admin.master.categories.store'), ['name' => 'Κατηγορία']);
+ $response->assertSessionHasNoErrors();
+});
+
+test('user creates category with name containing vietnamese characters', function () {
+ $user = User::factory()->create();
+ $this->actingAs($user);
+
+ $response = $this->post(route('admin.master.categories.store'), ['name' => 'Danh Mục']);
+ $response->assertSessionHasNoErrors();
+});
+
+test('user creates category with name containing mixed unicode scripts', function () {
+ $user = User::factory()->create();
+ $this->actingAs($user);
+
+ $response = $this->post(route('admin.master.categories.store'), ['name' => 'Food 电子产品']);
+ $response->assertSessionHasNoErrors();
+});
+
+test('user creates category with name containing email-like pattern', function () {
+ $user = User::factory()->create();
+ $this->actingAs($user);
+
+ $this->post(route('admin.master.categories.store'), ['name' => 'user@example.com']);
+
+ $this->assertDatabaseHas('categories', [
+ 'name' => 'user@example.com',
+ 'slug' => Str::slug('user@example.com'),
+ ]);
+});
+
+test('user creates category with name containing URL-like pattern', function () {
+ $user = User::factory()->create();
+ $this->actingAs($user);
+
+ $this->post(route('admin.master.categories.store'), ['name' => 'https://example.com']);
+
+ $this->assertDatabaseHas('categories', [
+ 'name' => 'https://example.com',
+ 'slug' => Str::slug('https://example.com'),
+ ]);
+});
+
+test('user creates category with name containing date-like pattern', function () {
+ $user = User::factory()->create();
+ $this->actingAs($user);
+
+ $this->post(route('admin.master.categories.store'), ['name' => '2024-01-15']);
+
+ $this->assertDatabaseHas('categories', [
+ 'name' => '2024-01-15',
+ 'slug' => '2024-01-15',
+ ]);
+});
+
+test('user creates category with name containing emoji', function () {
+ $user = User::factory()->create();
+ $this->actingAs($user);
+
+ $response = $this->post(route('admin.master.categories.store'), ['name' => '🔥 Hot Category']);
+ $response->assertSessionHasNoErrors();
+});
+
+test('user creates category with very short name', function () {
+ $user = User::factory()->create();
+ $this->actingAs($user);
+
+ $response = $this->post(route('admin.master.categories.store'), ['name' => 'X']);
+ $response->assertSessionHasNoErrors();
+
+ $this->assertDatabaseHas('categories', [
+ 'name' => 'X',
+ 'slug' => 'x',
+ ]);
+});
+
+test('user creates category with name containing multiple dashes', function () {
+ $user = User::factory()->create();
+ $this->actingAs($user);
+
+ $this->post(route('admin.master.categories.store'), ['name' => 'A--B']);
+
+ $this->assertDatabaseHas('categories', [
+ 'name' => 'A--B',
+ 'slug' => Str::slug('A--B'),
+ ]);
+});
+
+test('user creates category with name containing mixed case and numbers', function () {
+ $user = User::factory()->create();
+ $this->actingAs($user);
+
+ $this->post(route('admin.master.categories.store'), ['name' => 'iPhone15ProMax']);
+
+ $this->assertDatabaseHas('categories', [
+ 'name' => 'iPhone15ProMax',
+ 'slug' => Str::slug('iPhone15ProMax'),
+ ]);
+});
+
+test('user creates category with name containing only spaces and numbers', function () {
+ $user = User::factory()->create();
+ $this->actingAs($user);
+
+ $response = $this->post(route('admin.master.categories.store'), ['name' => ' 123 ']);
+ $response->assertSessionHasNoErrors();
+});
+
+test('user creates category with name containing very long single word', function () {
+ $user = User::factory()->create();
+ $this->actingAs($user);
+
+ $name = str_repeat('a', 50);
+
+ $response = $this->post(route('admin.master.categories.store'), ['name' => $name]);
+ $response->assertSessionHasNoErrors();
+
+ $this->assertDatabaseHas('categories', [
+ 'name' => $name,
+ 'slug' => $name,
+ ]);
+});
+
+test('user creates category then creates another with similar name', function () {
+ $user = User::factory()->create();
+ $this->actingAs($user);
+
+ $this->post(route('admin.master.categories.store'), ['name' => 'Makanan']);
+ $this->post(route('admin.master.categories.store'), ['name' => 'Minuman']);
+
+ $this->assertDatabaseCount('categories', 2);
+ $this->assertDatabaseHas('categories', ['name' => 'Makanan', 'slug' => 'makanan']);
+ $this->assertDatabaseHas('categories', ['name' => 'Minuman', 'slug' => 'minuman']);
+});
+
+test('user creates category with name containing curly braces', function () {
+ $user = User::factory()->create();
+ $this->actingAs($user);
+
+ $this->post(route('admin.master.categories.store'), ['name' => '{code}']);
+
+ $this->assertDatabaseHas('categories', [
+ 'name' => '{code}',
+ 'slug' => Str::slug('{code}'),
+ ]);
+});
+
+test('user creates category with name containing angle brackets', function () {
+ $user = User::factory()->create();
+ $this->actingAs($user);
+
+ $this->post(route('admin.master.categories.store'), ['name' => '']);
+
+ $this->assertDatabaseHas('categories', [
+ 'name' => '',
+ 'slug' => Str::slug(''),
+ ]);
+});
+
+test('user creates category with name containing semicolon', function () {
+ $user = User::factory()->create();
+ $this->actingAs($user);
+
+ $this->post(route('admin.master.categories.store'), ['name' => 'A;B']);
+
+ $this->assertDatabaseHas('categories', [
+ 'name' => 'A;B',
+ 'slug' => Str::slug('A;B'),
+ ]);
+});
+
+test('user creates category with name containing colon', function () {
+ $user = User::factory()->create();
+ $this->actingAs($user);
+
+ $this->post(route('admin.master.categories.store'), ['name' => 'Time: Now']);
+
+ $this->assertDatabaseHas('categories', [
+ 'name' => 'Time: Now',
+ 'slug' => Str::slug('Time: Now'),
+ ]);
+});
+
+test('user creates category with name containing backtick', function () {
+ $user = User::factory()->create();
+ $this->actingAs($user);
+
+ $this->post(route('admin.master.categories.store'), ['name' => '`Code`']);
+
+ $this->assertDatabaseHas('categories', [
+ 'name' => '`Code`',
+ 'slug' => Str::slug('`Code`'),
+ ]);
+});
+
+test('user creates category with name containing tilde', function () {
+ $user = User::factory()->create();
+ $this->actingAs($user);
+
+ $this->post(route('admin.master.categories.store'), ['name' => 'Caf~']);
+
+ $this->assertDatabaseHas('categories', [
+ 'name' => 'Caf~',
+ 'slug' => Str::slug('Caf~'),
+ ]);
+});
+
+test('user creates category with name containing caret', function () {
+ $user = User::factory()->create();
+ $this->actingAs($user);
+
+ $this->post(route('admin.master.categories.store'), ['name' => 'A^B']);
+
+ $this->assertDatabaseHas('categories', [
+ 'name' => 'A^B',
+ 'slug' => Str::slug('A^B'),
+ ]);
+});
+
+test('user creates category with name containing percentage', function () {
+ $user = User::factory()->create();
+ $this->actingAs($user);
+
+ $this->post(route('admin.master.categories.store'), ['name' => '100% Natural']);
+
+ $this->assertDatabaseHas('categories', [
+ 'name' => '100% Natural',
+ 'slug' => Str::slug('100% Natural'),
+ ]);
+});
+
+test('user creates category with name containing regex special characters', function () {
+ $user = User::factory()->create();
+ $this->actingAs($user);
+
+ $response = $this->post(route('admin.master.categories.store'), ['name' => '.*+?^${}()|[]\\']);
+ $response->assertSessionHasNoErrors();
+});
+
+test('user creates category with name containing fullwidth characters', function () {
+ $user = User::factory()->create();
+ $this->actingAs($user);
+
+ $response = $this->post(route('admin.master.categories.store'), ['name' => "ABCDE"]);
+ $response->assertSessionHasNoErrors();
+});
+
+test('user creates category with name containing fullwidth brackets', function () {
+ $user = User::factory()->create();
+ $this->actingAs($user);
+
+ $response = $this->post(route('admin.master.categories.store'), ['name' => "()【】"]);
+ $response->assertSessionHasNoErrors();
+});
+
+test('user creates category with name containing hiragana', function () {
+ $user = User::factory()->create();
+ $this->actingAs($user);
+
+ $response = $this->post(route('admin.master.categories.store'), ['name' => 'あいうえお']);
+ $response->assertSessionHasNoErrors();
+});
+
+test('user creates category with name containing katakana', function () {
+ $user = User::factory()->create();
+ $this->actingAs($user);
+
+ $response = $this->post(route('admin.master.categories.store'), ['name' => 'アイウエオ']);
+ $response->assertSessionHasNoErrors();
+});
+
+test('user creates category with name containing hangul', function () {
+ $user = User::factory()->create();
+ $this->actingAs($user);
+
+ $response = $this->post(route('admin.master.categories.store'), ['name' => '가나다라마']);
+ $response->assertSessionHasNoErrors();
+});
+
+test('user creates category with name containing hebrew', function () {
+ $user = User::factory()->create();
+ $this->actingAs($user);
+
+ $response = $this->post(route('admin.master.categories.store'), ['name' => 'אבגדהו']);
+ $response->assertSessionHasNoErrors();
+});
+
+test('user creates category with name containing bengali', function () {
+ $user = User::factory()->create();
+ $this->actingAs($user);
+
+ $response = $this->post(route('admin.master.categories.store'), ['name' => 'অআইঈউ']);
+ $response->assertSessionHasNoErrors();
+});
+
+test('user creates category with name containing tamil', function () {
+ $user = User::factory()->create();
+ $this->actingAs($user);
+
+ $response = $this->post(route('admin.master.categories.store'), ['name' => 'அஆஇஈஉ']);
+ $response->assertSessionHasNoErrors();
+});
+
+test('user creates category with name containing superscript', function () {
+ $user = User::factory()->create();
+ $this->actingAs($user);
+
+ $response = $this->post(route('admin.master.categories.store'), ['name' => 'E=mc2']);
+ $response->assertSessionHasNoErrors();
+});
+
+test('user creates category with name containing only newlines', function () {
+ $user = User::factory()->create();
+ $this->actingAs($user);
+
+ $response = $this->post(route('admin.master.categories.store'), ['name' => "\n\n\n"]);
+ $response->assertSessionHasErrors('name');
+});
+
+test('user creates category with name containing only tabs', function () {
+ $user = User::factory()->create();
+ $this->actingAs($user);
+
+ $response = $this->post(route('admin.master.categories.store'), ['name' => "\t\t\t"]);
+ $response->assertSessionHasErrors('name');
+});
+
+test('user creates category with name containing mixed whitespace and visible chars', function () {
+ $user = User::factory()->create();
+ $this->actingAs($user);
+
+ $response = $this->post(route('admin.master.categories.store'), ['name' => " \t Hello \n World "]);
+ $response->assertSessionHasNoErrors();
+
+ $category = DB::table('categories')->first();
+ expect($category->name)->not->toBeEmpty();
+});
+
+test('user creates category with name containing plus sign', function () {
+ $user = User::factory()->create();
+ $this->actingAs($user);
+
+ $this->post(route('admin.master.categories.store'), ['name' => 'A+B']);
+
+ $this->assertDatabaseHas('categories', [
+ 'name' => 'A+B',
+ 'slug' => Str::slug('A+B'),
+ ]);
+});
+
+test('user creates category with name containing equals sign', function () {
+ $user = User::factory()->create();
+ $this->actingAs($user);
+
+ $this->post(route('admin.master.categories.store'), ['name' => 'A=B']);
+
+ $this->assertDatabaseHas('categories', [
+ 'name' => 'A=B',
+ 'slug' => Str::slug('A=B'),
+ ]);
+});