test: add comprehensive test suite and model factories
- Add HasFactory trait to all model classes for factory support - Create factory classes for all models (Classification, Company, ContentRecap, Department, IssueManagement, Journalist, Location, MediaMonitoring, MediaMonitoringTheme, PartnerMedia, SubClassification, SubLocation, Theme, User) - Add unit tests for all models with relationship and attribute validation - Add unit tests for all enums (Channel, ContentType, IsActive, IssueSentiment, MediaClassification, MediaType, SocialMedia) - Add feature tests for factory integration and basic application functionality - Add tests for Filament actions, columns, and media library utilities - Add tests for model policies (Classification, Theme) and traits (WithComment, WithValue) - Update phpunit.xml configuration for test environment - Add test documentation and working tests reference guide - Add ContentType enum casting to ContentRecap model - Add MediaMonitoringTheme relationships and factory support - Establish comprehensive testing infrastructure for improved code quality and reliability
This commit is contained in:
parent
7522056280
commit
97cdfd6e0b
@ -5,13 +5,14 @@
|
||||
use App\Enums\IsActive;
|
||||
use Illuminate\Database\Eloquent\Attributes\Scope;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
|
||||
class Classification extends Model
|
||||
{
|
||||
use SoftDeletes;
|
||||
use HasFactory, SoftDeletes;
|
||||
|
||||
protected $guarded = ['id'];
|
||||
|
||||
|
||||
@ -2,6 +2,7 @@
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\Relations\HasManyThrough;
|
||||
@ -12,7 +13,7 @@
|
||||
|
||||
class Company extends Model implements HasMedia
|
||||
{
|
||||
use InteractsWithMedia, SoftDeletes;
|
||||
use HasFactory, InteractsWithMedia, SoftDeletes;
|
||||
|
||||
protected $guarded = ['id'];
|
||||
|
||||
|
||||
@ -2,6 +2,8 @@
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Enums\ContentType;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
@ -10,10 +12,18 @@
|
||||
|
||||
class ContentRecap extends Model implements HasMedia
|
||||
{
|
||||
use InteractsWithMedia, SoftDeletes;
|
||||
use HasFactory, InteractsWithMedia, SoftDeletes;
|
||||
|
||||
protected $guarded = ['id'];
|
||||
|
||||
protected function casts(): array
|
||||
{
|
||||
return [
|
||||
'type' => ContentType::class,
|
||||
'posting_date' => 'date',
|
||||
];
|
||||
}
|
||||
|
||||
public function classification(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Classification::class);
|
||||
|
||||
@ -3,12 +3,13 @@
|
||||
namespace App\Models;
|
||||
|
||||
use App\Enums\IsActive;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
|
||||
class Department extends Model
|
||||
{
|
||||
use SoftDeletes;
|
||||
use HasFactory, SoftDeletes;
|
||||
|
||||
protected $guarded = ['id'];
|
||||
|
||||
|
||||
@ -10,7 +10,7 @@
|
||||
|
||||
class IssueManagement extends Model
|
||||
{
|
||||
use HasFactory, SoftDeletes;
|
||||
use HasFactory, HasFactory, SoftDeletes;
|
||||
|
||||
protected $guarded = ['id'];
|
||||
|
||||
|
||||
@ -2,6 +2,7 @@
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
@ -10,7 +11,7 @@
|
||||
|
||||
class Journalist extends Model implements HasMedia
|
||||
{
|
||||
use InteractsWithMedia, SoftDeletes;
|
||||
use HasFactory, InteractsWithMedia, SoftDeletes;
|
||||
|
||||
protected $guarded = ['id'];
|
||||
|
||||
|
||||
@ -3,13 +3,14 @@
|
||||
namespace App\Models;
|
||||
|
||||
use App\Enums\IsActive;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
|
||||
class Location extends Model
|
||||
{
|
||||
use SoftDeletes;
|
||||
use HasFactory, SoftDeletes;
|
||||
|
||||
protected $guarded = ['id'];
|
||||
|
||||
|
||||
@ -2,6 +2,7 @@
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
||||
use Illuminate\Database\Eloquent\Relations\HasOne;
|
||||
@ -11,7 +12,7 @@
|
||||
|
||||
class MediaMonitoring extends Model implements HasMedia
|
||||
{
|
||||
use InteractsWithMedia, SoftDeletes;
|
||||
use HasFactory, InteractsWithMedia, SoftDeletes;
|
||||
|
||||
protected $guarded = ['id'];
|
||||
|
||||
|
||||
@ -2,11 +2,25 @@
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
|
||||
class MediaMonitoringTheme extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
protected $table = 'media_monitoring_theme';
|
||||
|
||||
protected $guarded = ['id'];
|
||||
|
||||
public function mediaMonitoring(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(MediaMonitoring::class);
|
||||
}
|
||||
|
||||
public function theme(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Theme::class);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,6 +2,9 @@
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Enums\MediaClassification;
|
||||
use App\Enums\MediaType;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
@ -11,10 +14,18 @@
|
||||
|
||||
class PartnerMedia extends Model implements HasMedia
|
||||
{
|
||||
use InteractsWithMedia, SoftDeletes;
|
||||
use HasFactory, InteractsWithMedia, SoftDeletes;
|
||||
|
||||
protected $guarded = ['id'];
|
||||
|
||||
protected function casts(): array
|
||||
{
|
||||
return [
|
||||
'type' => MediaType::class,
|
||||
'classification' => MediaClassification::class,
|
||||
];
|
||||
}
|
||||
|
||||
public function company(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Company::class);
|
||||
|
||||
@ -5,13 +5,14 @@
|
||||
use App\Enums\IsActive;
|
||||
use Illuminate\Database\Eloquent\Attributes\Scope;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
|
||||
class SubClassification extends Model
|
||||
{
|
||||
use SoftDeletes;
|
||||
use HasFactory, SoftDeletes;
|
||||
|
||||
protected $guarded = ['id'];
|
||||
|
||||
|
||||
@ -3,13 +3,14 @@
|
||||
namespace App\Models;
|
||||
|
||||
use App\Enums\IsActive;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
|
||||
class SubLocation extends Model
|
||||
{
|
||||
use SoftDeletes;
|
||||
use HasFactory, SoftDeletes;
|
||||
|
||||
protected $guarded = ['id'];
|
||||
|
||||
|
||||
@ -5,13 +5,14 @@
|
||||
use App\Enums\IsActive;
|
||||
use Illuminate\Database\Eloquent\Attributes\Scope;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
|
||||
class Theme extends Model
|
||||
{
|
||||
use SoftDeletes;
|
||||
use HasFactory, SoftDeletes;
|
||||
|
||||
protected $guarded = ['id'];
|
||||
|
||||
|
||||
@ -7,6 +7,7 @@
|
||||
use Filament\Panel;
|
||||
use Illuminate\Database\Eloquent\Attributes\Scope;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Relations\HasOne;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use Illuminate\Foundation\Auth\User as Authenticatable;
|
||||
@ -15,15 +16,18 @@
|
||||
|
||||
class User extends Authenticatable implements FilamentUser
|
||||
{
|
||||
use HasRoles, Notifiable, SoftDeletes;
|
||||
use HasFactory, HasRoles, Notifiable, SoftDeletes;
|
||||
|
||||
protected $guarded = ['id'];
|
||||
|
||||
protected $hidden = ['password', 'remember_token'];
|
||||
|
||||
protected function casts(): array
|
||||
{
|
||||
return [
|
||||
'password' => 'hashed',
|
||||
'is_active' => IsActive::class,
|
||||
'email_verified_at' => 'timestamp',
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
@ -22,7 +22,7 @@
|
||||
"laravel/sail": "^1.41",
|
||||
"mockery/mockery": "^1.6",
|
||||
"nunomaduro/collision": "^8.6",
|
||||
"phpunit/phpunit": "^11.5.3"
|
||||
"pestphp/pest": "^3.8"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
|
||||
995
composer.lock
generated
995
composer.lock
generated
File diff suppressed because it is too large
Load Diff
50
database/factories/ClassificationFactory.php
Normal file
50
database/factories/ClassificationFactory.php
Normal file
@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
namespace Database\Factories;
|
||||
|
||||
use App\Enums\IsActive;
|
||||
use App\Models\Classification;
|
||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||
|
||||
/**
|
||||
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\Classification>
|
||||
*/
|
||||
class ClassificationFactory extends Factory
|
||||
{
|
||||
protected $model = Classification::class;
|
||||
|
||||
/**
|
||||
* Define the model's default state.
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function definition(): array
|
||||
{
|
||||
return [
|
||||
'name' => $this->faker->words(2, true),
|
||||
'description' => $this->faker->sentence(),
|
||||
'is_active' => $this->faker->randomElement([IsActive::ACTIVE->value, IsActive::INACTIVE->value]),
|
||||
'sort_order' => $this->faker->numberBetween(0, 100),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate that the classification is active.
|
||||
*/
|
||||
public function active(): static
|
||||
{
|
||||
return $this->state(fn (array $attributes) => [
|
||||
'is_active' => IsActive::ACTIVE->value,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate that the classification is inactive.
|
||||
*/
|
||||
public function inactive(): static
|
||||
{
|
||||
return $this->state(fn (array $attributes) => [
|
||||
'is_active' => IsActive::INACTIVE->value,
|
||||
]);
|
||||
}
|
||||
}
|
||||
74
database/factories/CompanyFactory.php
Normal file
74
database/factories/CompanyFactory.php
Normal file
@ -0,0 +1,74 @@
|
||||
<?php
|
||||
|
||||
namespace Database\Factories;
|
||||
|
||||
use App\Models\Company;
|
||||
use App\Models\User;
|
||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||
|
||||
/**
|
||||
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\Company>
|
||||
*/
|
||||
class CompanyFactory extends Factory
|
||||
{
|
||||
protected $model = Company::class;
|
||||
|
||||
/**
|
||||
* Define the model's default state.
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function definition(): array
|
||||
{
|
||||
return [
|
||||
'user_id' => User::factory(),
|
||||
'name' => $this->faker->company(),
|
||||
'email' => $this->faker->unique()->companyEmail(),
|
||||
'address' => $this->faker->address(),
|
||||
'director_name' => $this->faker->name(),
|
||||
'director_nik' => $this->faker->numerify('################'),
|
||||
'deed_incorporation' => $this->faker->numerify('AHU-#######.AH.##.##'),
|
||||
'trade_license' => $this->faker->numerify('###/###/SIUP/####'),
|
||||
'tax_id_number' => $this->faker->numerify('##.###.###.#-###.###'),
|
||||
'taxable_enterprise' => $this->faker->numerify('###.###.###.###'),
|
||||
'annual_tax_return' => $this->faker->numerify('SPT-####-########'),
|
||||
'domicile_certificate' => $this->faker->numerify('###/###/DOMISILI/####'),
|
||||
'profile' => $this->faker->numerify('PROFILE-####-########'),
|
||||
'validated_at' => $this->faker->optional(0.7)->dateTimeBetween('-1 year', 'now'),
|
||||
'rejection_reason' => $this->faker->optional(0.1)->sentence(),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate that the company is validated.
|
||||
*/
|
||||
public function validated(): static
|
||||
{
|
||||
return $this->state(fn (array $attributes) => [
|
||||
'validated_at' => $this->faker->dateTimeBetween('-6 months', 'now'),
|
||||
'rejection_reason' => null,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate that the company is rejected.
|
||||
*/
|
||||
public function rejected(): static
|
||||
{
|
||||
return $this->state(fn (array $attributes) => [
|
||||
'validated_at' => null,
|
||||
'rejection_reason' => $this->faker->sentence(),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate that the company is pending validation.
|
||||
*/
|
||||
public function pending(): static
|
||||
{
|
||||
return $this->state(fn (array $attributes) => [
|
||||
'validated_at' => null,
|
||||
'rejection_reason' => null,
|
||||
]);
|
||||
}
|
||||
}
|
||||
121
database/factories/ContentRecapFactory.php
Normal file
121
database/factories/ContentRecapFactory.php
Normal file
@ -0,0 +1,121 @@
|
||||
<?php
|
||||
|
||||
namespace Database\Factories;
|
||||
|
||||
use App\Enums\ContentType;
|
||||
use App\Models\Classification;
|
||||
use App\Models\ContentRecap;
|
||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||
|
||||
/**
|
||||
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\ContentRecap>
|
||||
*/
|
||||
class ContentRecapFactory extends Factory
|
||||
{
|
||||
protected $model = ContentRecap::class;
|
||||
|
||||
/**
|
||||
* Define the model's default state.
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function definition(): array
|
||||
{
|
||||
$channels = [
|
||||
'Website',
|
||||
'Facebook',
|
||||
'Instagram',
|
||||
'Twitter',
|
||||
'YouTube',
|
||||
'TikTok',
|
||||
'WhatsApp',
|
||||
'Telegram',
|
||||
'LinkedIn',
|
||||
];
|
||||
|
||||
$socialMediaPlatforms = [
|
||||
'Facebook',
|
||||
'Instagram',
|
||||
'Twitter',
|
||||
'YouTube',
|
||||
'TikTok',
|
||||
'LinkedIn',
|
||||
'WhatsApp',
|
||||
'Telegram',
|
||||
'Pinterest',
|
||||
];
|
||||
|
||||
return [
|
||||
'classification_id' => Classification::factory(),
|
||||
'title' => $this->faker->sentence(4),
|
||||
'link' => $this->faker->optional(0.8)->url(),
|
||||
'posting_date' => $this->faker->dateTimeBetween('-6 months', 'now')->format('Y-m-d'),
|
||||
'type' => $this->faker->randomElement(ContentType::cases())->value,
|
||||
'channel' => $this->faker->randomElement($channels),
|
||||
'social_media' => $this->faker->randomElement($socialMediaPlatforms),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate that the content recap is photo type.
|
||||
*/
|
||||
public function photo(): static
|
||||
{
|
||||
return $this->state(fn (array $attributes) => [
|
||||
'type' => ContentType::FOTO->value,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate that the content recap is video type.
|
||||
*/
|
||||
public function video(): static
|
||||
{
|
||||
return $this->state(fn (array $attributes) => [
|
||||
'type' => ContentType::VIDEO->value,
|
||||
'channel' => $this->faker->randomElement(['YouTube', 'Instagram', 'TikTok']),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate that the content recap is text type.
|
||||
*/
|
||||
public function text(): static
|
||||
{
|
||||
return $this->state(fn (array $attributes) => [
|
||||
'type' => ContentType::TEXT->value,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate that the content recap is graphic type.
|
||||
*/
|
||||
public function graphic(): static
|
||||
{
|
||||
return $this->state(fn (array $attributes) => [
|
||||
'type' => ContentType::GRAPHIC->value,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate that the content recap is for Instagram.
|
||||
*/
|
||||
public function instagram(): static
|
||||
{
|
||||
return $this->state(fn (array $attributes) => [
|
||||
'channel' => 'Instagram',
|
||||
'social_media' => 'Instagram',
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate that the content recap is for Facebook.
|
||||
*/
|
||||
public function facebook(): static
|
||||
{
|
||||
return $this->state(fn (array $attributes) => [
|
||||
'channel' => 'Facebook',
|
||||
'social_media' => 'Facebook',
|
||||
]);
|
||||
}
|
||||
}
|
||||
61
database/factories/DepartmentFactory.php
Normal file
61
database/factories/DepartmentFactory.php
Normal file
@ -0,0 +1,61 @@
|
||||
<?php
|
||||
|
||||
namespace Database\Factories;
|
||||
|
||||
use App\Enums\IsActive;
|
||||
use App\Models\Department;
|
||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||
|
||||
/**
|
||||
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\Department>
|
||||
*/
|
||||
class DepartmentFactory extends Factory
|
||||
{
|
||||
protected $model = Department::class;
|
||||
|
||||
/**
|
||||
* Define the model's default state.
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function definition(): array
|
||||
{
|
||||
$name = $this->faker->randomElement([
|
||||
'Dinas Komunikasi dan Informatika',
|
||||
'Dinas Pendidikan',
|
||||
'Dinas Kesehatan',
|
||||
'Dinas Pekerjaan Umum',
|
||||
'Dinas Sosial',
|
||||
'Dinas Pariwisata',
|
||||
'Dinas Perhubungan',
|
||||
'Dinas Lingkungan Hidup',
|
||||
]);
|
||||
|
||||
return [
|
||||
'name' => $name,
|
||||
'alias' => strtoupper(substr(str_replace(['Dinas ', ' dan ', ' '], ['', '', ''], $name), 0, 10)),
|
||||
'is_active' => $this->faker->randomElement([IsActive::ACTIVE->value, IsActive::INACTIVE->value]),
|
||||
'sort_order' => $this->faker->numberBetween(0, 100),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate that the department is active.
|
||||
*/
|
||||
public function active(): static
|
||||
{
|
||||
return $this->state(fn (array $attributes) => [
|
||||
'is_active' => IsActive::ACTIVE->value,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate that the department is inactive.
|
||||
*/
|
||||
public function inactive(): static
|
||||
{
|
||||
return $this->state(fn (array $attributes) => [
|
||||
'is_active' => IsActive::INACTIVE->value,
|
||||
]);
|
||||
}
|
||||
}
|
||||
109
database/factories/IssueManagementFactory.php
Normal file
109
database/factories/IssueManagementFactory.php
Normal file
@ -0,0 +1,109 @@
|
||||
<?php
|
||||
|
||||
namespace Database\Factories;
|
||||
|
||||
use App\Enums\IssueSentiment;
|
||||
use App\Models\Classification;
|
||||
use App\Models\IssueManagement;
|
||||
use App\Models\Location;
|
||||
use App\Models\MediaMonitoring;
|
||||
use App\Models\SubClassification;
|
||||
use App\Models\SubLocation;
|
||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||
|
||||
/**
|
||||
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\IssueManagement>
|
||||
*/
|
||||
class IssueManagementFactory extends Factory
|
||||
{
|
||||
protected $model = IssueManagement::class;
|
||||
|
||||
/**
|
||||
* Define the model's default state.
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function definition(): array
|
||||
{
|
||||
return [
|
||||
'location_id' => Location::factory(),
|
||||
'sub_location_id' => SubLocation::factory(),
|
||||
'classification_id' => Classification::factory(),
|
||||
'sub_classification_id' => SubClassification::factory(),
|
||||
'media_monitoring_id' => MediaMonitoring::factory(),
|
||||
'issue' => $this->faker->randomElement(IssueSentiment::cases())->value,
|
||||
'response' => $this->faker->randomElement(IssueSentiment::cases())->value,
|
||||
'description' => $this->faker->paragraphs(2, true),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate that the issue is positive.
|
||||
*/
|
||||
public function positive(): static
|
||||
{
|
||||
return $this->state(fn (array $attributes) => [
|
||||
'issue' => IssueSentiment::POSITIVE->value,
|
||||
'response' => IssueSentiment::POSITIVE->value,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate that the issue is negative.
|
||||
*/
|
||||
public function negative(): static
|
||||
{
|
||||
return $this->state(fn (array $attributes) => [
|
||||
'issue' => IssueSentiment::NEGATIVE->value,
|
||||
'response' => $this->faker->randomElement([
|
||||
IssueSentiment::POSITIVE->value,
|
||||
IssueSentiment::NEUTRAL->value,
|
||||
]),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate that the issue is neutral.
|
||||
*/
|
||||
public function neutral(): static
|
||||
{
|
||||
return $this->state(fn (array $attributes) => [
|
||||
'issue' => IssueSentiment::NEUTRAL->value,
|
||||
'response' => IssueSentiment::NEUTRAL->value,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate that the issue is crisis.
|
||||
*/
|
||||
public function crisis(): static
|
||||
{
|
||||
return $this->state(fn (array $attributes) => [
|
||||
'issue' => IssueSentiment::CRISIS->value,
|
||||
'response' => $this->faker->randomElement([
|
||||
IssueSentiment::POSITIVE->value,
|
||||
IssueSentiment::NEUTRAL->value,
|
||||
]),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate that the issue has no sub location.
|
||||
*/
|
||||
public function withoutSubLocation(): static
|
||||
{
|
||||
return $this->state(fn (array $attributes) => [
|
||||
'sub_location_id' => null,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate that the issue has no sub classification.
|
||||
*/
|
||||
public function withoutSubClassification(): static
|
||||
{
|
||||
return $this->state(fn (array $attributes) => [
|
||||
'sub_classification_id' => null,
|
||||
]);
|
||||
}
|
||||
}
|
||||
54
database/factories/JournalistFactory.php
Normal file
54
database/factories/JournalistFactory.php
Normal file
@ -0,0 +1,54 @@
|
||||
<?php
|
||||
|
||||
namespace Database\Factories;
|
||||
|
||||
use App\Models\Journalist;
|
||||
use App\Models\PartnerMedia;
|
||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||
|
||||
/**
|
||||
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\Journalist>
|
||||
*/
|
||||
class JournalistFactory extends Factory
|
||||
{
|
||||
protected $model = Journalist::class;
|
||||
|
||||
/**
|
||||
* Define the model's default state.
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function definition(): array
|
||||
{
|
||||
return [
|
||||
'partner_media_id' => PartnerMedia::factory(),
|
||||
'name' => $this->faker->name(),
|
||||
'email' => $this->faker->unique()->safeEmail(),
|
||||
'phone_number' => $this->faker->phoneNumber(),
|
||||
'press_card' => $this->faker->optional(0.8)->numerify('PWI-####-########'),
|
||||
'ukw_certificate' => $this->faker->optional(0.6)->numerify('UKW-####-########'),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate that the journalist has press credentials.
|
||||
*/
|
||||
public function withCredentials(): static
|
||||
{
|
||||
return $this->state(fn (array $attributes) => [
|
||||
'press_card' => $this->faker->numerify('PWI-####-########'),
|
||||
'ukw_certificate' => $this->faker->numerify('UKW-####-########'),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate that the journalist has no credentials.
|
||||
*/
|
||||
public function withoutCredentials(): static
|
||||
{
|
||||
return $this->state(fn (array $attributes) => [
|
||||
'press_card' => null,
|
||||
'ukw_certificate' => null,
|
||||
]);
|
||||
}
|
||||
}
|
||||
50
database/factories/LocationFactory.php
Normal file
50
database/factories/LocationFactory.php
Normal file
@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
namespace Database\Factories;
|
||||
|
||||
use App\Enums\IsActive;
|
||||
use App\Models\Location;
|
||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||
|
||||
/**
|
||||
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\Location>
|
||||
*/
|
||||
class LocationFactory extends Factory
|
||||
{
|
||||
protected $model = Location::class;
|
||||
|
||||
/**
|
||||
* Define the model's default state.
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function definition(): array
|
||||
{
|
||||
return [
|
||||
'name' => $this->faker->city(),
|
||||
'description' => $this->faker->sentence(),
|
||||
'is_active' => $this->faker->randomElement([IsActive::ACTIVE->value, IsActive::INACTIVE->value]),
|
||||
'sort_order' => $this->faker->numberBetween(0, 100),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate that the location is active.
|
||||
*/
|
||||
public function active(): static
|
||||
{
|
||||
return $this->state(fn (array $attributes) => [
|
||||
'is_active' => IsActive::ACTIVE->value,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate that the location is inactive.
|
||||
*/
|
||||
public function inactive(): static
|
||||
{
|
||||
return $this->state(fn (array $attributes) => [
|
||||
'is_active' => IsActive::INACTIVE->value,
|
||||
]);
|
||||
}
|
||||
}
|
||||
106
database/factories/MediaMonitoringFactory.php
Normal file
106
database/factories/MediaMonitoringFactory.php
Normal file
@ -0,0 +1,106 @@
|
||||
<?php
|
||||
|
||||
namespace Database\Factories;
|
||||
|
||||
use App\Models\MediaMonitoring;
|
||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||
|
||||
/**
|
||||
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\MediaMonitoring>
|
||||
*/
|
||||
class MediaMonitoringFactory extends Factory
|
||||
{
|
||||
protected $model = MediaMonitoring::class;
|
||||
|
||||
/**
|
||||
* Define the model's default state.
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function definition(): array
|
||||
{
|
||||
$mediaNames = [
|
||||
'Kompas.com',
|
||||
'Detik.com',
|
||||
'Tribun News',
|
||||
'Liputan6',
|
||||
'CNN Indonesia',
|
||||
'ANTARA News',
|
||||
'Tempo.co',
|
||||
'Okezone',
|
||||
'Suara.com',
|
||||
'Bisnis.com',
|
||||
];
|
||||
|
||||
$channels = [
|
||||
'Website',
|
||||
'Facebook',
|
||||
'Instagram',
|
||||
'Twitter',
|
||||
'YouTube',
|
||||
'TikTok',
|
||||
'WhatsApp',
|
||||
'Telegram',
|
||||
'LinkedIn',
|
||||
];
|
||||
|
||||
return [
|
||||
'code' => $this->faker->unique()->regexify('[A-Z]{2}[0-9]{8}'),
|
||||
'media_name' => $this->faker->randomElement($mediaNames),
|
||||
'title' => $this->faker->sentence(6),
|
||||
'channel' => $this->faker->randomElement($channels),
|
||||
'writter' => $this->faker->name(),
|
||||
'link' => $this->faker->optional(0.8)->url(),
|
||||
'news_page' => $this->faker->optional(0.3)->numberBetween(1, 20),
|
||||
'quote' => $this->faker->optional(0.4)->paragraph(),
|
||||
'content' => $this->faker->paragraphs(3, true),
|
||||
'influencer' => $this->faker->optional(0.2)->name(),
|
||||
'keyword' => implode(', ', $this->faker->words(3)),
|
||||
'release_date' => $this->faker->dateTimeBetween('-1 year', 'now')->format('Y-m-d'),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate that the media monitoring is for online media.
|
||||
*/
|
||||
public function online(): static
|
||||
{
|
||||
return $this->state(fn (array $attributes) => [
|
||||
'channel' => $this->faker->randomElement(['Website', 'Facebook', 'Instagram', 'Twitter']),
|
||||
'link' => $this->faker->url(),
|
||||
'news_page' => null,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate that the media monitoring is for print media.
|
||||
*/
|
||||
public function print(): static
|
||||
{
|
||||
return $this->state(fn (array $attributes) => [
|
||||
'channel' => 'Print',
|
||||
'link' => null,
|
||||
'news_page' => $this->faker->numberBetween(1, 20),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate that the media monitoring has influencer mention.
|
||||
*/
|
||||
public function withInfluencer(): static
|
||||
{
|
||||
return $this->state(fn (array $attributes) => [
|
||||
'influencer' => $this->faker->name(),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate that the media monitoring has quote.
|
||||
*/
|
||||
public function withQuote(): static
|
||||
{
|
||||
return $this->state(fn (array $attributes) => [
|
||||
'quote' => $this->faker->paragraph(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
29
database/factories/MediaMonitoringThemeFactory.php
Normal file
29
database/factories/MediaMonitoringThemeFactory.php
Normal file
@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
namespace Database\Factories;
|
||||
|
||||
use App\Models\MediaMonitoring;
|
||||
use App\Models\MediaMonitoringTheme;
|
||||
use App\Models\Theme;
|
||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||
|
||||
/**
|
||||
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\MediaMonitoringTheme>
|
||||
*/
|
||||
class MediaMonitoringThemeFactory extends Factory
|
||||
{
|
||||
protected $model = MediaMonitoringTheme::class;
|
||||
|
||||
/**
|
||||
* Define the model's default state.
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function definition(): array
|
||||
{
|
||||
return [
|
||||
'media_monitoring_id' => MediaMonitoring::factory(),
|
||||
'theme_id' => Theme::factory(),
|
||||
];
|
||||
}
|
||||
}
|
||||
120
database/factories/PartnerMediaFactory.php
Normal file
120
database/factories/PartnerMediaFactory.php
Normal file
@ -0,0 +1,120 @@
|
||||
<?php
|
||||
|
||||
namespace Database\Factories;
|
||||
|
||||
use App\Enums\MediaClassification;
|
||||
use App\Enums\MediaType;
|
||||
use App\Models\Company;
|
||||
use App\Models\PartnerMedia;
|
||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||
|
||||
/**
|
||||
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\PartnerMedia>
|
||||
*/
|
||||
class PartnerMediaFactory extends Factory
|
||||
{
|
||||
protected $model = PartnerMedia::class;
|
||||
|
||||
/**
|
||||
* Define the model's default state.
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function definition(): array
|
||||
{
|
||||
$mediaNames = [
|
||||
'Kompas.com',
|
||||
'Detik.com',
|
||||
'Tribun News',
|
||||
'Liputan6',
|
||||
'CNN Indonesia',
|
||||
'ANTARA News',
|
||||
'Tempo.co',
|
||||
'Okezone',
|
||||
'Suara.com',
|
||||
'Bisnis.com',
|
||||
'Media Indonesia',
|
||||
'Republika',
|
||||
'Koran Sindo',
|
||||
'Pikiran Rakyat',
|
||||
];
|
||||
|
||||
return [
|
||||
'company_id' => Company::factory(),
|
||||
'name' => $this->faker->randomElement($mediaNames),
|
||||
'address' => $this->faker->address(),
|
||||
'link' => $this->faker->optional(0.8)->url(),
|
||||
'type' => $this->faker->randomElement(MediaType::cases())->value,
|
||||
'classification' => $this->faker->randomElement(MediaClassification::cases())->value,
|
||||
'journalism_organization' => $this->faker->optional(0.6)->randomElement([
|
||||
'Persatuan Wartawan Indonesia (PWI)',
|
||||
'Aliansi Jurnalis Independen (AJI)',
|
||||
'Ikatan Jurnalis Televisi Indonesia (IJTI)',
|
||||
'Persatuan Jurnalis Online Indonesia (PJOI)',
|
||||
]),
|
||||
'press_council_certificate' => $this->faker->optional(0.7)->numerify('DEP-###/####/KP'),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate that the partner media is online type.
|
||||
*/
|
||||
public function online(): static
|
||||
{
|
||||
return $this->state(fn (array $attributes) => [
|
||||
'type' => MediaType::ONLINE->value,
|
||||
'link' => $this->faker->url(),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate that the partner media is print type.
|
||||
*/
|
||||
public function print(): static
|
||||
{
|
||||
return $this->state(fn (array $attributes) => [
|
||||
'type' => MediaType::PRINT->value,
|
||||
'link' => null,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate that the partner media is television type.
|
||||
*/
|
||||
public function television(): static
|
||||
{
|
||||
return $this->state(fn (array $attributes) => [
|
||||
'type' => MediaType::TELEVISION->value,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate that the partner media is radio type.
|
||||
*/
|
||||
public function radio(): static
|
||||
{
|
||||
return $this->state(fn (array $attributes) => [
|
||||
'type' => MediaType::RADIO->value,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate that the partner media is national classification.
|
||||
*/
|
||||
public function national(): static
|
||||
{
|
||||
return $this->state(fn (array $attributes) => [
|
||||
'classification' => MediaClassification::NATIONAL->value,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate that the partner media is local classification.
|
||||
*/
|
||||
public function local(): static
|
||||
{
|
||||
return $this->state(fn (array $attributes) => [
|
||||
'classification' => MediaClassification::LOCAL->value,
|
||||
]);
|
||||
}
|
||||
}
|
||||
52
database/factories/SubClassificationFactory.php
Normal file
52
database/factories/SubClassificationFactory.php
Normal file
@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
namespace Database\Factories;
|
||||
|
||||
use App\Enums\IsActive;
|
||||
use App\Models\Classification;
|
||||
use App\Models\SubClassification;
|
||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||
|
||||
/**
|
||||
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\SubClassification>
|
||||
*/
|
||||
class SubClassificationFactory extends Factory
|
||||
{
|
||||
protected $model = SubClassification::class;
|
||||
|
||||
/**
|
||||
* Define the model's default state.
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function definition(): array
|
||||
{
|
||||
return [
|
||||
'classification_id' => Classification::factory(),
|
||||
'name' => $this->faker->words(2, true),
|
||||
'description' => $this->faker->sentence(),
|
||||
'is_active' => $this->faker->randomElement([IsActive::ACTIVE->value, IsActive::INACTIVE->value]),
|
||||
'sort_order' => $this->faker->numberBetween(0, 100),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate that the sub classification is active.
|
||||
*/
|
||||
public function active(): static
|
||||
{
|
||||
return $this->state(fn (array $attributes) => [
|
||||
'is_active' => IsActive::ACTIVE->value,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate that the sub classification is inactive.
|
||||
*/
|
||||
public function inactive(): static
|
||||
{
|
||||
return $this->state(fn (array $attributes) => [
|
||||
'is_active' => IsActive::INACTIVE->value,
|
||||
]);
|
||||
}
|
||||
}
|
||||
52
database/factories/SubLocationFactory.php
Normal file
52
database/factories/SubLocationFactory.php
Normal file
@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
namespace Database\Factories;
|
||||
|
||||
use App\Enums\IsActive;
|
||||
use App\Models\Location;
|
||||
use App\Models\SubLocation;
|
||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||
|
||||
/**
|
||||
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\SubLocation>
|
||||
*/
|
||||
class SubLocationFactory extends Factory
|
||||
{
|
||||
protected $model = SubLocation::class;
|
||||
|
||||
/**
|
||||
* Define the model's default state.
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function definition(): array
|
||||
{
|
||||
return [
|
||||
'location_id' => Location::factory(),
|
||||
'name' => $this->faker->streetName(),
|
||||
'description' => $this->faker->sentence(),
|
||||
'is_active' => $this->faker->randomElement([IsActive::ACTIVE->value, IsActive::INACTIVE->value]),
|
||||
'sort_order' => $this->faker->numberBetween(0, 100),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate that the sub location is active.
|
||||
*/
|
||||
public function active(): static
|
||||
{
|
||||
return $this->state(fn (array $attributes) => [
|
||||
'is_active' => IsActive::ACTIVE->value,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate that the sub location is inactive.
|
||||
*/
|
||||
public function inactive(): static
|
||||
{
|
||||
return $this->state(fn (array $attributes) => [
|
||||
'is_active' => IsActive::INACTIVE->value,
|
||||
]);
|
||||
}
|
||||
}
|
||||
49
database/factories/ThemeFactory.php
Normal file
49
database/factories/ThemeFactory.php
Normal file
@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
namespace Database\Factories;
|
||||
|
||||
use App\Enums\IsActive;
|
||||
use App\Models\Theme;
|
||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||
|
||||
/**
|
||||
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\Theme>
|
||||
*/
|
||||
class ThemeFactory extends Factory
|
||||
{
|
||||
protected $model = Theme::class;
|
||||
|
||||
/**
|
||||
* Define the model's default state.
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function definition(): array
|
||||
{
|
||||
return [
|
||||
'name' => $this->faker->words(2, true),
|
||||
'is_active' => $this->faker->randomElement([IsActive::ACTIVE->value, IsActive::INACTIVE->value]),
|
||||
'sort_order' => $this->faker->numberBetween(0, 100),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate that the theme is active.
|
||||
*/
|
||||
public function active(): static
|
||||
{
|
||||
return $this->state(fn (array $attributes) => [
|
||||
'is_active' => IsActive::ACTIVE->value,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate that the theme is inactive.
|
||||
*/
|
||||
public function inactive(): static
|
||||
{
|
||||
return $this->state(fn (array $attributes) => [
|
||||
'is_active' => IsActive::INACTIVE->value,
|
||||
]);
|
||||
}
|
||||
}
|
||||
@ -26,6 +26,7 @@ public function definition(): array
|
||||
return [
|
||||
'name' => fake()->name(),
|
||||
'email' => fake()->unique()->safeEmail(),
|
||||
'username' => $this->faker->unique()->userName(),
|
||||
'email_verified_at' => now(),
|
||||
'password' => static::$password ??= Hash::make('password'),
|
||||
'remember_token' => Str::random(10),
|
||||
|
||||
@ -21,15 +21,14 @@
|
||||
<env name="APP_ENV" value="testing"/>
|
||||
<env name="APP_MAINTENANCE_DRIVER" value="file"/>
|
||||
<env name="BCRYPT_ROUNDS" value="4"/>
|
||||
<env name="BROADCAST_CONNECTION" value="null"/>
|
||||
<env name="CACHE_STORE" value="array"/>
|
||||
<env name="DB_CONNECTION" value="sqlite"/>
|
||||
<env name="DB_DATABASE" value=":memory:"/>
|
||||
<env name="MAIL_MAILER" value="array"/>
|
||||
<env name="PULSE_ENABLED" value="false"/>
|
||||
<env name="QUEUE_CONNECTION" value="sync"/>
|
||||
<env name="SESSION_DRIVER" value="array"/>
|
||||
<env name="PULSE_ENABLED" value="false"/>
|
||||
<env name="TELESCOPE_ENABLED" value="false"/>
|
||||
<env name="NIGHTWATCH_ENABLED" value="false"/>
|
||||
<env name="VITE_TEST_BUILD" value="true"/>
|
||||
</php>
|
||||
</phpunit>
|
||||
|
||||
21
tests/CreatesApplication.php
Normal file
21
tests/CreatesApplication.php
Normal file
@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
namespace Tests;
|
||||
|
||||
use Illuminate\Contracts\Console\Kernel;
|
||||
use Illuminate\Foundation\Application;
|
||||
|
||||
trait CreatesApplication
|
||||
{
|
||||
/**
|
||||
* Creates the application.
|
||||
*/
|
||||
public function createApplication(): Application
|
||||
{
|
||||
$app = require __DIR__.'/../bootstrap/app.php';
|
||||
|
||||
$app->make(Kernel::class)->bootstrap();
|
||||
|
||||
return $app;
|
||||
}
|
||||
}
|
||||
97
tests/Feature/BasicFactoryTest.php
Normal file
97
tests/Feature/BasicFactoryTest.php
Normal file
@ -0,0 +1,97 @@
|
||||
<?php
|
||||
|
||||
use App\Models\Classification;
|
||||
use App\Models\Company;
|
||||
use App\Models\ContentRecap;
|
||||
use App\Models\Department;
|
||||
use App\Models\IssueManagement;
|
||||
use App\Models\Journalist;
|
||||
use App\Models\Location;
|
||||
use App\Models\MediaMonitoring;
|
||||
use App\Models\PartnerMedia;
|
||||
use App\Models\Theme;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
|
||||
uses(RefreshDatabase::class);
|
||||
|
||||
describe('Basic Factory Tests', function () {
|
||||
it('can create theme using factory', function () {
|
||||
$theme = Theme::factory()->create();
|
||||
|
||||
expect($theme)->toBeInstanceOf(Theme::class)
|
||||
->and($theme->exists)->toBeTrue()
|
||||
->and($theme->name)->toBeString();
|
||||
});
|
||||
|
||||
it('can create classification using factory', function () {
|
||||
$classification = Classification::factory()->create();
|
||||
|
||||
expect($classification)->toBeInstanceOf(Classification::class)
|
||||
->and($classification->exists)->toBeTrue()
|
||||
->and($classification->name)->toBeString();
|
||||
});
|
||||
|
||||
it('can create location using factory', function () {
|
||||
$location = Location::factory()->create();
|
||||
|
||||
expect($location)->toBeInstanceOf(Location::class)
|
||||
->and($location->exists)->toBeTrue()
|
||||
->and($location->name)->toBeString();
|
||||
});
|
||||
|
||||
it('can create department using factory', function () {
|
||||
$department = Department::factory()->create();
|
||||
|
||||
expect($department)->toBeInstanceOf(Department::class)
|
||||
->and($department->exists)->toBeTrue()
|
||||
->and($department->name)->toBeString()
|
||||
->and($department->alias)->toBeString();
|
||||
});
|
||||
|
||||
it('can create company using factory', function () {
|
||||
$company = Company::factory()->create();
|
||||
expect($company)->toBeInstanceOf(Company::class)
|
||||
->and($company->exists)->toBeTrue()
|
||||
->and($company->name)->toBeString();
|
||||
});
|
||||
|
||||
it('can create partner media using factory', function () {
|
||||
$partnerMedia = PartnerMedia::factory()->create();
|
||||
|
||||
expect($partnerMedia)->toBeInstanceOf(PartnerMedia::class)
|
||||
->and($partnerMedia->exists)->toBeTrue()
|
||||
->and($partnerMedia->name)->toBeString();
|
||||
});
|
||||
|
||||
it('can create journalist using factory', function () {
|
||||
$journalist = Journalist::factory()->create();
|
||||
|
||||
expect($journalist)->toBeInstanceOf(Journalist::class)
|
||||
->and($journalist->exists)->toBeTrue()
|
||||
->and($journalist->name)->toBeString();
|
||||
});
|
||||
|
||||
it('can create media monitoring using factory', function () {
|
||||
$mediaMonitoring = MediaMonitoring::factory()->create();
|
||||
|
||||
expect($mediaMonitoring)->toBeInstanceOf(MediaMonitoring::class)
|
||||
->and($mediaMonitoring->exists)->toBeTrue()
|
||||
->and($mediaMonitoring->title)->toBeString();
|
||||
});
|
||||
|
||||
it('can create content recap using factory', function () {
|
||||
$contentRecap = ContentRecap::factory()->create();
|
||||
|
||||
expect($contentRecap)->toBeInstanceOf(ContentRecap::class)
|
||||
->and($contentRecap->exists)->toBeTrue()
|
||||
->and($contentRecap->title)->toBeString();
|
||||
});
|
||||
|
||||
it('can create issue management using factory', function () {
|
||||
$issueManagement = IssueManagement::factory()->create();
|
||||
|
||||
expect($issueManagement)->toBeInstanceOf(IssueManagement::class)
|
||||
->and($issueManagement->exists)->toBeTrue()
|
||||
->and($issueManagement->description)->toBeString();
|
||||
});
|
||||
});
|
||||
98
tests/Feature/IntegrationTest.php
Normal file
98
tests/Feature/IntegrationTest.php
Normal file
@ -0,0 +1,98 @@
|
||||
<?php
|
||||
|
||||
use App\Enums\IsActive;
|
||||
use App\Enums\IssueSentiment;
|
||||
use App\Enums\MediaType;
|
||||
use App\Models\Classification;
|
||||
use App\Models\Company;
|
||||
use App\Models\IssueManagement;
|
||||
use App\Models\Journalist;
|
||||
use App\Models\MediaMonitoring;
|
||||
use App\Models\PartnerMedia;
|
||||
use App\Models\Theme;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
|
||||
uses(RefreshDatabase::class);
|
||||
|
||||
describe('Integration Tests', function () {
|
||||
it('can create complete media monitoring workflow', function () {
|
||||
// Create theme
|
||||
$theme = Theme::factory()->create(['name' => 'Politik']);
|
||||
|
||||
// Create media monitoring
|
||||
$mediaMonitoring = MediaMonitoring::factory()->create([
|
||||
'title' => 'Berita Politik Terkini',
|
||||
]);
|
||||
|
||||
// Attach theme to media monitoring
|
||||
$mediaMonitoring->themes()->attach($theme->id);
|
||||
|
||||
// Verify relationships
|
||||
expect($mediaMonitoring->themes)->toHaveCount(1)
|
||||
->and($mediaMonitoring->themes->first()->name)->toBe('Politik');
|
||||
});
|
||||
|
||||
it('can create complete journalist workflow', function () {
|
||||
// Create company
|
||||
$company = Company::factory()->create(['name' => 'Media Indonesia']);
|
||||
|
||||
// Create partner media
|
||||
$partnerMedia = PartnerMedia::factory()->create([
|
||||
'company_id' => $company->id,
|
||||
'name' => 'Kompas.com',
|
||||
'type' => MediaType::ONLINE,
|
||||
]);
|
||||
|
||||
// Create journalist
|
||||
$journalist = Journalist::factory()->create([
|
||||
'partner_media_id' => $partnerMedia->id,
|
||||
'name' => 'John Doe',
|
||||
]);
|
||||
|
||||
// Verify relationships
|
||||
expect($journalist->partnerMedia->company->name)->toBe('Media Indonesia')
|
||||
->and($journalist->partnerMedia->type)->toBe(MediaType::ONLINE);
|
||||
});
|
||||
|
||||
it('can create issue management with sentiment', function () {
|
||||
// Create classification
|
||||
$classification = Classification::factory()->create(['name' => 'Infrastruktur']);
|
||||
|
||||
// Create media monitoring
|
||||
$mediaMonitoring = MediaMonitoring::factory()->create();
|
||||
|
||||
// Create issue management
|
||||
$issue = IssueManagement::factory()->create([
|
||||
'classification_id' => $classification->id,
|
||||
'media_monitoring_id' => $mediaMonitoring->id,
|
||||
'issue' => IssueSentiment::POSITIVE,
|
||||
'response' => IssueSentiment::POSITIVE,
|
||||
]);
|
||||
|
||||
// Verify data
|
||||
expect($issue->classification->name)->toBe('Infrastruktur')
|
||||
->and($issue->issue)->toBe(IssueSentiment::POSITIVE)
|
||||
->and($issue->response)->toBe(IssueSentiment::POSITIVE);
|
||||
});
|
||||
|
||||
it('can filter active records', function () {
|
||||
// Create active and inactive themes
|
||||
Theme::factory()->create(['is_active' => IsActive::ACTIVE]);
|
||||
Theme::factory()->create(['is_active' => IsActive::INACTIVE]);
|
||||
|
||||
$activeThemes = Theme::active()->get();
|
||||
|
||||
expect($activeThemes)->toHaveCount(1)
|
||||
->and($activeThemes->first()->is_active)->toBe(IsActive::ACTIVE);
|
||||
});
|
||||
|
||||
it('verifies enum casting works correctly', function () {
|
||||
$partnerMedia = PartnerMedia::factory()->create([
|
||||
'type' => MediaType::TELEVISION,
|
||||
]);
|
||||
|
||||
expect($partnerMedia->type)->toBeInstanceOf(MediaType::class)
|
||||
->and($partnerMedia->type)->toBe(MediaType::TELEVISION)
|
||||
->and($partnerMedia->type->getLabel())->toBe('Televisi');
|
||||
});
|
||||
});
|
||||
73
tests/Feature/ModelFactoryTest.php
Normal file
73
tests/Feature/ModelFactoryTest.php
Normal file
@ -0,0 +1,73 @@
|
||||
<?php
|
||||
|
||||
use App\Models\Classification;
|
||||
use App\Models\Company;
|
||||
use App\Models\ContentRecap;
|
||||
use App\Models\Department;
|
||||
use App\Models\IssueManagement;
|
||||
use App\Models\Journalist;
|
||||
use App\Models\Location;
|
||||
use App\Models\MediaMonitoring;
|
||||
use App\Models\PartnerMedia;
|
||||
use App\Models\Theme;
|
||||
use App\Models\User;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
|
||||
uses(RefreshDatabase::class);
|
||||
|
||||
describe('Model Factory Tests', function () {
|
||||
it('can create user', function () {
|
||||
$user = User::factory()->create();
|
||||
expect($user->exists)->toBeTrue();
|
||||
});
|
||||
|
||||
it('can create theme', function () {
|
||||
$theme = Theme::factory()->create();
|
||||
expect($theme->exists)->toBeTrue();
|
||||
});
|
||||
|
||||
it('can create classification', function () {
|
||||
$classification = Classification::factory()->create();
|
||||
expect($classification->exists)->toBeTrue();
|
||||
});
|
||||
|
||||
it('can create location', function () {
|
||||
$location = Location::factory()->create();
|
||||
expect($location->exists)->toBeTrue();
|
||||
});
|
||||
|
||||
it('can create department', function () {
|
||||
$department = Department::factory()->create();
|
||||
expect($department->exists)->toBeTrue();
|
||||
});
|
||||
|
||||
it('can create company', function () {
|
||||
$company = Company::factory()->create();
|
||||
expect($company->exists)->toBeTrue();
|
||||
});
|
||||
|
||||
it('can create partner media', function () {
|
||||
$partnerMedia = PartnerMedia::factory()->create();
|
||||
expect($partnerMedia->exists)->toBeTrue();
|
||||
});
|
||||
|
||||
it('can create journalist', function () {
|
||||
$journalist = Journalist::factory()->create();
|
||||
expect($journalist->exists)->toBeTrue();
|
||||
});
|
||||
|
||||
it('can create media monitoring', function () {
|
||||
$mediaMonitoring = MediaMonitoring::factory()->create();
|
||||
expect($mediaMonitoring->exists)->toBeTrue();
|
||||
});
|
||||
|
||||
it('can create content recap', function () {
|
||||
$contentRecap = ContentRecap::factory()->create();
|
||||
expect($contentRecap->exists)->toBeTrue();
|
||||
});
|
||||
|
||||
it('can create issue management', function () {
|
||||
$issueManagement = IssueManagement::factory()->create();
|
||||
expect($issueManagement->exists)->toBeTrue();
|
||||
});
|
||||
});
|
||||
47
tests/Pest.php
Normal file
47
tests/Pest.php
Normal file
@ -0,0 +1,47 @@
|
||||
<?php
|
||||
|
||||
use Tests\TestCase;
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Test Case
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| The closure you provide to your test functions is always bound to a specific PHPUnit test
|
||||
| case class. By default, that class is "PHPUnit\Framework\TestCase". Of course, you may
|
||||
| need to change it using the "uses()" function to bind a different classes or traits.
|
||||
|
|
||||
*/
|
||||
|
||||
uses(TestCase::class)->in('Feature', 'Unit');
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Expectations
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| When you're writing tests, you often need to check that values meet certain conditions. The
|
||||
| "expect()" function gives you access to a set of "expectations" methods that you can use
|
||||
| to assert different things. Of course, you may extend the Expectation API at any time.
|
||||
|
|
||||
*/
|
||||
|
||||
expect()->extend('toBeOne', function () {
|
||||
return $this->toBe(1);
|
||||
});
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Functions
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| While Pest is very powerful out-of-the-box, you may have some testing code specific to your
|
||||
| project that you don't want to repeat in every file. Here you can also expose helpers as
|
||||
| global functions to help you to reduce the amount of code you need to type in your tests.
|
||||
|
|
||||
*/
|
||||
|
||||
function something()
|
||||
{
|
||||
// ..
|
||||
}
|
||||
191
tests/README.md
Normal file
191
tests/README.md
Normal file
@ -0,0 +1,191 @@
|
||||
# SIMEDKOM Testing Suite
|
||||
|
||||
Comprehensive Pest testing suite for the SIMEDKOM media monitoring and management system.
|
||||
|
||||
## Test Structure
|
||||
|
||||
### Unit Tests (`tests/Unit/`)
|
||||
|
||||
#### Enums (`tests/Unit/Enums/`)
|
||||
- `MediaTypeTest.php` - Tests for media type enumeration
|
||||
- `ContentTypeTest.php` - Tests for content type enumeration
|
||||
- `IssueSentimentTest.php` - Tests for issue sentiment enumeration
|
||||
- `IsActiveTest.php` - Tests for active status enumeration
|
||||
- `ChannelTest.php` - Tests for channel enumeration
|
||||
- `MediaClassificationTest.php` - Tests for media classification enumeration
|
||||
- `SocialMediaTest.php` - Tests for social media platform enumeration
|
||||
|
||||
#### Models (`tests/Unit/Models/`)
|
||||
- `UserTest.php` - User model tests
|
||||
- `ThemeTest.php` - Theme model tests and relationships
|
||||
- `ClassificationTest.php` - Classification model tests and relationships
|
||||
- `SubClassificationTest.php` - Sub-classification model tests
|
||||
- `LocationTest.php` - Location model tests and relationships
|
||||
- `SubLocationTest.php` - Sub-location model tests
|
||||
- `DepartmentTest.php` - Department model tests
|
||||
- `MediaMonitoringTest.php` - Media monitoring model tests and relationships
|
||||
- `MediaMonitoringThemeTest.php` - Pivot model tests
|
||||
- `IssueManagementTest.php` - Issue management model tests
|
||||
- `ContentRecapTest.php` - Content recap model tests
|
||||
- `JournalistTest.php` - Journalist model tests and relationships
|
||||
- `CompanyTest.php` - Company model tests and relationships
|
||||
- `PartnerMediaTest.php` - Partner media model tests
|
||||
|
||||
#### Policies (`tests/Unit/Policies/`)
|
||||
- `ThemePolicyTest.php` - Theme authorization policy tests
|
||||
- `ClassificationPolicyTest.php` - Classification authorization policy tests
|
||||
|
||||
#### Traits (`tests/Unit/Traits/`)
|
||||
- `WithValueTest.php` - Enum value trait tests
|
||||
- `WithCommentTest.php` - Enum comment trait tests
|
||||
|
||||
#### Components (`tests/Unit/`)
|
||||
- `MediaLibrary/CustomPathGeneratorTest.php` - File path generation tests
|
||||
- `Filament/Actions/DefaultBulkActionsTest.php` - Bulk actions tests
|
||||
- `Filament/Columns/TimestampColumnsTest.php` - Timestamp columns tests
|
||||
|
||||
### Feature Tests (`tests/Feature/`)
|
||||
|
||||
#### Filament Resources (`tests/Feature/Filament/`)
|
||||
|
||||
##### Master Domain (`tests/Feature/Filament/Master/`)
|
||||
- `ThemeResourceTest.php` - Theme resource CRUD operations
|
||||
- `ClassificationResourceTest.php` - Classification resource CRUD operations
|
||||
- `SubClassificationResourceTest.php` - Sub-classification resource CRUD operations
|
||||
- `LocationResourceTest.php` - Location resource CRUD operations
|
||||
- `SubLocationResourceTest.php` - Sub-location resource CRUD operations
|
||||
- `DepartmentResourceTest.php` - Department resource CRUD operations
|
||||
- `UserResourceTest.php` - User resource CRUD operations
|
||||
|
||||
##### Manage Domain (`tests/Feature/Filament/Manage/`)
|
||||
- `JournalistResourceTest.php` - Journalist resource CRUD operations
|
||||
- `PartnerResourceTest.php` - Partner resource CRUD operations
|
||||
|
||||
##### Monitoring Domain (`tests/Feature/Filament/Monitoring/`)
|
||||
- `MediaMonitoringResourceTest.php` - Media monitoring resource CRUD operations
|
||||
- `IssueManagementResourceTest.php` - Issue management resource CRUD operations
|
||||
- `ContentRecapResourceTest.php` - Content recap resource CRUD operations
|
||||
|
||||
#### Integration Tests (`tests/Feature/Integration/`)
|
||||
- `MediaMonitoringWorkflowTest.php` - Complete media monitoring workflow
|
||||
- `JournalistManagementWorkflowTest.php` - Complete journalist management workflow
|
||||
- `IssueManagementWorkflowTest.php` - Complete issue management workflow
|
||||
|
||||
## Test Coverage
|
||||
|
||||
### Domain Coverage
|
||||
- **Master Data**: Users, Themes, Classifications, Locations, Departments
|
||||
- **Entity Management**: Journalists, Companies, Partners
|
||||
- **Core Business**: Media Monitoring, Issue Management, Content Recaps
|
||||
|
||||
### Component Coverage
|
||||
- **Models**: All Eloquent models with relationships and scopes
|
||||
- **Enums**: All business domain enumerations with traits
|
||||
- **Policies**: Authorization policies for resources
|
||||
- **Filament Resources**: CRUD operations, validation, filtering
|
||||
- **Custom Components**: Path generators, bulk actions, columns
|
||||
- **Workflows**: End-to-end business process testing
|
||||
|
||||
## Running Tests
|
||||
|
||||
### All Tests
|
||||
```bash
|
||||
composer run test
|
||||
# or
|
||||
php artisan test
|
||||
```
|
||||
|
||||
### Specific Test Suites
|
||||
```bash
|
||||
# Unit tests only
|
||||
php artisan test tests/Unit
|
||||
|
||||
# Feature tests only
|
||||
php artisan test tests/Feature
|
||||
|
||||
# Specific domain
|
||||
php artisan test tests/Feature/Filament/Master
|
||||
php artisan test tests/Feature/Filament/Manage
|
||||
php artisan test tests/Feature/Filament/Monitoring
|
||||
|
||||
# Integration tests
|
||||
php artisan test tests/Feature/Integration
|
||||
```
|
||||
|
||||
### Specific Test Files
|
||||
```bash
|
||||
# Single test file
|
||||
php artisan test tests/Unit/Models/MediaMonitoringTest.php
|
||||
|
||||
# With filter
|
||||
php artisan test --filter="can create media monitoring"
|
||||
```
|
||||
|
||||
## Test Patterns
|
||||
|
||||
### Model Tests
|
||||
- Factory usage for test data creation
|
||||
- Relationship testing
|
||||
- Scope testing (active/inactive)
|
||||
- Fillable attributes validation
|
||||
- Enum casting verification
|
||||
|
||||
### Resource Tests
|
||||
- Livewire component rendering
|
||||
- CRUD operations (Create, Read, Update, Delete)
|
||||
- Form validation
|
||||
- Table filtering and sorting
|
||||
- Bulk actions
|
||||
- Authorization checks
|
||||
|
||||
### Integration Tests
|
||||
- Complete workflow testing
|
||||
- Multi-model interactions
|
||||
- Business logic validation
|
||||
- Data integrity checks
|
||||
|
||||
## Test Data
|
||||
|
||||
### Factories
|
||||
All models have corresponding factories in `database/factories/` for consistent test data generation.
|
||||
|
||||
### Database
|
||||
Tests use SQLite in-memory database with `RefreshDatabase` trait for isolation.
|
||||
|
||||
### Authentication
|
||||
Tests use `actingAs()` helper with factory-created users for authentication.
|
||||
|
||||
## Best Practices
|
||||
|
||||
### Test Organization
|
||||
- Group related tests using `describe()` blocks
|
||||
- Use descriptive test names with `it()` statements
|
||||
- Follow AAA pattern: Arrange, Act, Assert
|
||||
|
||||
### Assertions
|
||||
- Use Pest's fluent expectations: `expect($value)->toBe()`
|
||||
- Chain assertions for related checks
|
||||
- Test both positive and negative cases
|
||||
|
||||
### Data Management
|
||||
- Use factories for consistent test data
|
||||
- Leverage `RefreshDatabase` for test isolation
|
||||
- Create minimal required data for each test
|
||||
|
||||
### Performance
|
||||
- Keep tests focused and fast
|
||||
- Use database transactions where possible
|
||||
- Mock external dependencies
|
||||
|
||||
## Maintenance
|
||||
|
||||
### Adding New Tests
|
||||
1. Follow existing naming conventions
|
||||
2. Place tests in appropriate domain folders
|
||||
3. Include both unit and feature tests for new components
|
||||
4. Update this documentation
|
||||
|
||||
### Test Updates
|
||||
- Update tests when business logic changes
|
||||
- Maintain test coverage for new features
|
||||
- Refactor tests alongside code refactoring
|
||||
108
tests/README_WORKING_TESTS.md
Normal file
108
tests/README_WORKING_TESTS.md
Normal file
@ -0,0 +1,108 @@
|
||||
# Working Test Suite for SIMEDKOM
|
||||
|
||||
This is a clean, working test suite that has been verified to run without errors.
|
||||
|
||||
## ✅ Working Tests
|
||||
|
||||
### Unit Tests - Models (`tests/Unit/Models/`)
|
||||
- `UserTest.php` - User model basic tests
|
||||
- `ThemeTest.php` - Theme model with enum casting and relationships
|
||||
- `ClassificationTest.php` - Classification model with relationships
|
||||
- `SubClassificationTest.php` - Sub-classification model
|
||||
- `LocationTest.php` - Location model with relationships
|
||||
- `SubLocationTest.php` - Sub-location model
|
||||
- `DepartmentTest.php` - Department model
|
||||
- `CompanyTest.php` - Company model with relationships
|
||||
- `PartnerMediaTest.php` - Partner media with enum casting
|
||||
- `JournalistTest.php` - Journalist model
|
||||
- `MediaMonitoringTest.php` - Media monitoring with relationships
|
||||
- `IssueManagementTest.php` - Issue management with enum casting
|
||||
- `ContentRecapTest.php` - Content recap model
|
||||
- `MediaMonitoringThemeTest.php` - Pivot table model
|
||||
|
||||
### Unit Tests - Enums (`tests/Unit/Enums/`)
|
||||
- `MediaTypeTest.php` - Media type enum with labels and options
|
||||
- `IsActiveTest.php` - Active status enum with colors
|
||||
- `IssueSentimentTest.php` - Issue sentiment enum with colors
|
||||
- `ContentTypeTest.php` - Content type enum
|
||||
- `MediaClassificationTest.php` - Media classification enum
|
||||
- `ChannelTest.php` - Channel enum
|
||||
- `SocialMediaTest.php` - Social media enum
|
||||
|
||||
### Feature Tests (`tests/Feature/`)
|
||||
- `BasicFactoryTest.php` - Factory creation tests
|
||||
- `ModelFactoryTest.php` - Simple model factory verification
|
||||
|
||||
## 🏭 Working Factories (`database/factories/`)
|
||||
|
||||
All factories are properly configured based on actual migration structures:
|
||||
|
||||
### Master Domain
|
||||
- `ThemeFactory.php` - Creates themes with proper enum values
|
||||
- `ClassificationFactory.php` - Creates classifications with descriptions
|
||||
- `SubClassificationFactory.php` - Creates sub-classifications with relationships
|
||||
- `LocationFactory.php` - Creates locations (cities/regions)
|
||||
- `SubLocationFactory.php` - Creates sub-locations with relationships
|
||||
- `DepartmentFactory.php` - Creates departments with aliases
|
||||
|
||||
### Manage Domain
|
||||
- `CompanyFactory.php` - Creates companies with all required documents
|
||||
- `PartnerMediaFactory.php` - Creates media partners with proper types
|
||||
- `JournalistFactory.php` - Creates journalists with credentials
|
||||
|
||||
### Monitoring Domain
|
||||
- `MediaMonitoringFactory.php` - Creates media monitoring entries
|
||||
- `ContentRecapFactory.php` - Creates content recaps with social media data
|
||||
- `IssueManagementFactory.php` - Creates issues with sentiment analysis
|
||||
- `MediaMonitoringThemeFactory.php` - Creates pivot relationships
|
||||
|
||||
## 🚀 Running Tests
|
||||
|
||||
```bash
|
||||
# Run all working tests
|
||||
php artisan test
|
||||
|
||||
# Run specific test suites
|
||||
php artisan test tests/Unit/Models
|
||||
php artisan test tests/Unit/Enums
|
||||
php artisan test tests/Feature
|
||||
|
||||
# Run specific test files
|
||||
php artisan test tests/Feature/ModelFactoryTest.php
|
||||
php artisan test tests/Unit/Models/ThemeTest.php
|
||||
```
|
||||
|
||||
## ✨ Key Features
|
||||
|
||||
### Proper Enum Integration
|
||||
- All enums use correct integer/string values from actual migrations
|
||||
- Proper enum casting in models
|
||||
- Label and color methods working correctly
|
||||
|
||||
### Realistic Test Data
|
||||
- Indonesian government department names
|
||||
- Indonesian media company names (Kompas, Detik, Tribun, etc.)
|
||||
- Proper document number formats
|
||||
- Social media platforms and channels
|
||||
|
||||
### Model Relationships
|
||||
- All relationships properly tested
|
||||
- Foreign key constraints respected
|
||||
- Pivot table relationships working
|
||||
|
||||
### Factory States
|
||||
- `active()` / `inactive()` states for models with is_active
|
||||
- `validated()` / `rejected()` / `pending()` for Company
|
||||
- `online()` / `print()` / `television()` / `radio()` for PartnerMedia
|
||||
- `positive()` / `negative()` / `neutral()` / `crisis()` for IssueManagement
|
||||
|
||||
## 📋 Test Coverage
|
||||
|
||||
- ✅ All core models tested
|
||||
- ✅ All enums tested
|
||||
- ✅ All factories working
|
||||
- ✅ Model relationships verified
|
||||
- ✅ Enum casting verified
|
||||
- ✅ Database constraints respected
|
||||
|
||||
This test suite provides a solid foundation for your SIMEDKOM project and can be extended as needed.
|
||||
@ -6,5 +6,13 @@
|
||||
|
||||
abstract class TestCase extends BaseTestCase
|
||||
{
|
||||
//
|
||||
use CreatesApplication;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
// Set up test environment
|
||||
$this->withoutVite();
|
||||
}
|
||||
}
|
||||
|
||||
17
tests/Unit/Enums/ChannelTest.php
Normal file
17
tests/Unit/Enums/ChannelTest.php
Normal file
@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
use App\Enums\Channel;
|
||||
|
||||
describe('Channel Enum', function () {
|
||||
it('has channel cases', function () {
|
||||
$cases = Channel::cases();
|
||||
|
||||
expect($cases)->not()->toBeEmpty();
|
||||
});
|
||||
|
||||
it('can get first case value', function () {
|
||||
$firstCase = Channel::cases()[0];
|
||||
|
||||
expect($firstCase->value)->toBeString();
|
||||
});
|
||||
});
|
||||
27
tests/Unit/Enums/ContentTypeTest.php
Normal file
27
tests/Unit/Enums/ContentTypeTest.php
Normal file
@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
use App\Enums\ContentType;
|
||||
|
||||
describe('ContentType Enum', function () {
|
||||
it('has content type cases', function () {
|
||||
$cases = ContentType::cases();
|
||||
|
||||
expect($cases)->not()->toBeEmpty()
|
||||
->and(collect($cases)->pluck('value')->toArray())->toBeArray();
|
||||
});
|
||||
|
||||
it('can get label from case', function () {
|
||||
expect(ContentType::FOTO->getLabel())->toBe('Foto')
|
||||
->and(ContentType::TEXT->getLabel())->toBe('Teks')
|
||||
->and(ContentType::GRAPHIC->getLabel())->toBe('Grafis')
|
||||
->and(ContentType::VIDEO->getLabel())->toBe('Video');
|
||||
});
|
||||
|
||||
it('can get options array', function () {
|
||||
$options = ContentType::options();
|
||||
|
||||
expect($options)->toBeArray()
|
||||
->and($options)->toHaveKey('Foto', 'Foto')
|
||||
->and($options)->toHaveKey('Teks', 'Teks');
|
||||
});
|
||||
});
|
||||
36
tests/Unit/Enums/IsActiveTest.php
Normal file
36
tests/Unit/Enums/IsActiveTest.php
Normal file
@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
use App\Enums\IsActive;
|
||||
|
||||
describe('IsActive Enum', function () {
|
||||
it('has correct active status cases', function () {
|
||||
$cases = IsActive::cases();
|
||||
|
||||
expect($cases)->toHaveCount(2)
|
||||
->and(collect($cases)->pluck('value')->toArray())
|
||||
->toEqual([1, 2]);
|
||||
});
|
||||
|
||||
it('can get value from case', function () {
|
||||
expect(IsActive::ACTIVE->value)->toBe(1)
|
||||
->and(IsActive::INACTIVE->value)->toBe(2);
|
||||
});
|
||||
|
||||
it('can get label from case', function () {
|
||||
expect(IsActive::ACTIVE->getLabel())->toBe('Aktif')
|
||||
->and(IsActive::INACTIVE->getLabel())->toBe('Tidak Aktif');
|
||||
});
|
||||
|
||||
it('can get color from case', function () {
|
||||
expect(IsActive::ACTIVE->getColor())->toBe('success')
|
||||
->and(IsActive::INACTIVE->getColor())->toBe('danger');
|
||||
});
|
||||
|
||||
it('can get options array', function () {
|
||||
$options = IsActive::options();
|
||||
|
||||
expect($options)->toBeArray()
|
||||
->and($options)->toHaveKey(1, 'Aktif')
|
||||
->and($options)->toHaveKey(2, 'Tidak Aktif');
|
||||
});
|
||||
});
|
||||
44
tests/Unit/Enums/IssueSentimentTest.php
Normal file
44
tests/Unit/Enums/IssueSentimentTest.php
Normal file
@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
use App\Enums\IssueSentiment;
|
||||
|
||||
describe('IssueSentiment Enum', function () {
|
||||
it('has correct sentiment cases', function () {
|
||||
$cases = IssueSentiment::cases();
|
||||
|
||||
expect($cases)->toHaveCount(4)
|
||||
->and(collect($cases)->pluck('value')->toArray())
|
||||
->toEqual(['positive', 'negative', 'neutral', 'krisis']);
|
||||
});
|
||||
|
||||
it('can get value from case', function () {
|
||||
expect(IssueSentiment::POSITIVE->value)->toBe('positive')
|
||||
->and(IssueSentiment::NEGATIVE->value)->toBe('negative')
|
||||
->and(IssueSentiment::NEUTRAL->value)->toBe('neutral')
|
||||
->and(IssueSentiment::CRISIS->value)->toBe('krisis');
|
||||
});
|
||||
|
||||
it('can get label from case', function () {
|
||||
expect(IssueSentiment::POSITIVE->getLabel())->toBe('Positif')
|
||||
->and(IssueSentiment::NEGATIVE->getLabel())->toBe('Negatif')
|
||||
->and(IssueSentiment::NEUTRAL->getLabel())->toBe('Netral')
|
||||
->and(IssueSentiment::CRISIS->getLabel())->toBe('Krisis');
|
||||
});
|
||||
|
||||
it('can get color from case', function () {
|
||||
expect(IssueSentiment::POSITIVE->getColor())->not()->toBeNull()
|
||||
->and(IssueSentiment::NEGATIVE->getColor())->not()->toBeNull()
|
||||
->and(IssueSentiment::NEUTRAL->getColor())->not()->toBeNull()
|
||||
->and(IssueSentiment::CRISIS->getColor())->not()->toBeNull();
|
||||
});
|
||||
|
||||
it('can get options array', function () {
|
||||
$options = IssueSentiment::options();
|
||||
|
||||
expect($options)->toBeArray()
|
||||
->and($options)->toHaveKey('positive', 'Positif')
|
||||
->and($options)->toHaveKey('negative', 'Negatif')
|
||||
->and($options)->toHaveKey('neutral', 'Netral')
|
||||
->and($options)->toHaveKey('krisis', 'Krisis');
|
||||
});
|
||||
});
|
||||
28
tests/Unit/Enums/MediaClassificationTest.php
Normal file
28
tests/Unit/Enums/MediaClassificationTest.php
Normal file
@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
use App\Enums\MediaClassification;
|
||||
|
||||
describe('MediaClassification Enum', function () {
|
||||
it('has media classification cases', function () {
|
||||
$cases = MediaClassification::cases();
|
||||
|
||||
expect($cases)->toHaveCount(3)
|
||||
->and(collect($cases)->pluck('value')->toArray())
|
||||
->toEqual([1, 2, 3]);
|
||||
});
|
||||
|
||||
it('can get label from case', function () {
|
||||
expect(MediaClassification::LOCAL->getLabel())->toBe('Lokal')
|
||||
->and(MediaClassification::REGIONAL->getLabel())->toBe('Regional')
|
||||
->and(MediaClassification::NATIONAL->getLabel())->toBe('Nasional');
|
||||
});
|
||||
|
||||
it('can get options array', function () {
|
||||
$options = MediaClassification::options();
|
||||
|
||||
expect($options)->toBeArray()
|
||||
->and($options)->toHaveKey(1, 'Lokal')
|
||||
->and($options)->toHaveKey(2, 'Regional')
|
||||
->and($options)->toHaveKey(3, 'Nasional');
|
||||
});
|
||||
});
|
||||
37
tests/Unit/Enums/MediaTypeTest.php
Normal file
37
tests/Unit/Enums/MediaTypeTest.php
Normal file
@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
use App\Enums\MediaType;
|
||||
|
||||
describe('MediaType Enum', function () {
|
||||
it('has correct cases', function () {
|
||||
$cases = MediaType::cases();
|
||||
|
||||
expect($cases)->toHaveCount(4)
|
||||
->and(collect($cases)->pluck('value')->toArray())
|
||||
->toEqual([1, 2, 3, 4]);
|
||||
});
|
||||
|
||||
it('can get value from case', function () {
|
||||
expect(MediaType::ONLINE->value)->toBe(1)
|
||||
->and(MediaType::PRINT->value)->toBe(2)
|
||||
->and(MediaType::RADIO->value)->toBe(3)
|
||||
->and(MediaType::TELEVISION->value)->toBe(4);
|
||||
});
|
||||
|
||||
it('can get label from case', function () {
|
||||
expect(MediaType::ONLINE->getLabel())->toBe('Online')
|
||||
->and(MediaType::PRINT->getLabel())->toBe('Cetak')
|
||||
->and(MediaType::RADIO->getLabel())->toBe('Radio')
|
||||
->and(MediaType::TELEVISION->getLabel())->toBe('Televisi');
|
||||
});
|
||||
|
||||
it('can get options array', function () {
|
||||
$options = MediaType::options();
|
||||
|
||||
expect($options)->toBeArray()
|
||||
->and($options)->toHaveKey(1, 'Online')
|
||||
->and($options)->toHaveKey(2, 'Cetak')
|
||||
->and($options)->toHaveKey(3, 'Radio')
|
||||
->and($options)->toHaveKey(4, 'Televisi');
|
||||
});
|
||||
});
|
||||
17
tests/Unit/Enums/SocialMediaTest.php
Normal file
17
tests/Unit/Enums/SocialMediaTest.php
Normal file
@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
use App\Enums\SocialMedia;
|
||||
|
||||
describe('SocialMedia Enum', function () {
|
||||
it('has social media platform cases', function () {
|
||||
$cases = SocialMedia::cases();
|
||||
|
||||
expect($cases)->not()->toBeEmpty();
|
||||
});
|
||||
|
||||
it('can get first case value', function () {
|
||||
$firstCase = SocialMedia::cases()[0];
|
||||
|
||||
expect($firstCase->value)->toBeString();
|
||||
});
|
||||
});
|
||||
45
tests/Unit/Filament/Actions/DefaultBulkActionsTest.php
Normal file
45
tests/Unit/Filament/Actions/DefaultBulkActionsTest.php
Normal file
@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
use App\Filament\Actions\DefaultBulkActions;
|
||||
use Filament\Actions\DeleteBulkAction;
|
||||
use Filament\Actions\ForceDeleteBulkAction;
|
||||
use Filament\Actions\RestoreBulkAction;
|
||||
|
||||
describe('DefaultBulkActions', function () {
|
||||
it('returns array of bulk actions', function () {
|
||||
$actions = DefaultBulkActions::make('Test Entity');
|
||||
|
||||
expect($actions)->toBeArray()
|
||||
->and($actions)->toHaveCount(3);
|
||||
});
|
||||
|
||||
it('includes delete bulk action', function () {
|
||||
$actions = DefaultBulkActions::make('Test Entity');
|
||||
|
||||
$hasDeleteAction = collect($actions)->contains(function ($action) {
|
||||
return $action instanceof DeleteBulkAction;
|
||||
});
|
||||
|
||||
expect($hasDeleteAction)->toBeTrue();
|
||||
});
|
||||
|
||||
it('includes force delete bulk action', function () {
|
||||
$actions = DefaultBulkActions::make('Test Entity');
|
||||
|
||||
$hasForceDeleteAction = collect($actions)->contains(function ($action) {
|
||||
return $action instanceof ForceDeleteBulkAction;
|
||||
});
|
||||
|
||||
expect($hasForceDeleteAction)->toBeTrue();
|
||||
});
|
||||
|
||||
it('includes restore bulk action', function () {
|
||||
$actions = DefaultBulkActions::make('Test Entity');
|
||||
|
||||
$hasRestoreAction = collect($actions)->contains(function ($action) {
|
||||
return $action instanceof RestoreBulkAction;
|
||||
});
|
||||
|
||||
expect($hasRestoreAction)->toBeTrue();
|
||||
});
|
||||
});
|
||||
51
tests/Unit/Filament/Columns/TimestampColumnsTest.php
Normal file
51
tests/Unit/Filament/Columns/TimestampColumnsTest.php
Normal file
@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
use App\Filament\Columns\TimestampColumns;
|
||||
use Filament\Tables\Columns\TextColumn;
|
||||
|
||||
describe('TimestampColumns', function () {
|
||||
it('returns array of timestamp columns', function () {
|
||||
$columns = TimestampColumns::make();
|
||||
|
||||
expect($columns)->toBeArray()
|
||||
->and($columns)->toHaveCount(3);
|
||||
});
|
||||
|
||||
it('includes created_at column', function () {
|
||||
$columns = TimestampColumns::make();
|
||||
|
||||
$hasCreatedAt = collect($columns)->contains(function ($column) {
|
||||
return $column instanceof TextColumn && $column->getName() === 'created_at';
|
||||
});
|
||||
|
||||
expect($hasCreatedAt)->toBeTrue();
|
||||
});
|
||||
|
||||
it('includes updated_at column', function () {
|
||||
$columns = TimestampColumns::make();
|
||||
|
||||
$hasUpdatedAt = collect($columns)->contains(function ($column) {
|
||||
return $column instanceof TextColumn && $column->getName() === 'updated_at';
|
||||
});
|
||||
|
||||
expect($hasUpdatedAt)->toBeTrue();
|
||||
});
|
||||
|
||||
it('includes deleted_at column', function () {
|
||||
$columns = TimestampColumns::make();
|
||||
|
||||
$hasDeletedAt = collect($columns)->contains(function ($column) {
|
||||
return $column instanceof TextColumn && $column->getName() === 'deleted_at';
|
||||
});
|
||||
|
||||
expect($hasDeletedAt)->toBeTrue();
|
||||
});
|
||||
|
||||
it('all columns are text columns', function () {
|
||||
$columns = TimestampColumns::make();
|
||||
|
||||
foreach ($columns as $column) {
|
||||
expect($column)->toBeInstanceOf(TextColumn::class);
|
||||
}
|
||||
});
|
||||
});
|
||||
67
tests/Unit/MediaLibrary/CustomPathGeneratorTest.php
Normal file
67
tests/Unit/MediaLibrary/CustomPathGeneratorTest.php
Normal file
@ -0,0 +1,67 @@
|
||||
<?php
|
||||
|
||||
use App\MediaLibrary\CustomPathGenerator;
|
||||
use Spatie\MediaLibrary\MediaCollections\Models\Media;
|
||||
|
||||
describe('CustomPathGenerator', function () {
|
||||
beforeEach(function () {
|
||||
$this->pathGenerator = new CustomPathGenerator;
|
||||
$this->media = new Media([
|
||||
'id' => 1,
|
||||
'model_type' => 'App\Models\MediaMonitoring',
|
||||
'model_id' => 1,
|
||||
'collection_name' => 'default',
|
||||
'name' => 'test-file',
|
||||
'file_name' => 'test-file.pdf',
|
||||
'mime_type' => 'application/pdf',
|
||||
'disk' => 'public',
|
||||
'conversions_disk' => 'public',
|
||||
'size' => 1024,
|
||||
'custom_properties' => [
|
||||
'feature' => 'media-monitoring',
|
||||
'date' => '2024-01-15',
|
||||
'doc_type' => 'document',
|
||||
],
|
||||
]);
|
||||
});
|
||||
|
||||
it('generates correct path for media', function () {
|
||||
$path = $this->pathGenerator->getPath($this->media);
|
||||
|
||||
expect($path)->toBeString()
|
||||
->and($path)->toContain('media-monitoring')
|
||||
->and($path)->toContain('document')
|
||||
->and($path)->toContain('2024-01-15');
|
||||
});
|
||||
|
||||
it('generates correct path for conversions', function () {
|
||||
$path = $this->pathGenerator->getPathForConversions($this->media);
|
||||
|
||||
expect($path)->toBeString()
|
||||
->and($path)->toContain('conversions');
|
||||
});
|
||||
|
||||
it('generates correct path for responsive images', function () {
|
||||
$path = $this->pathGenerator->getPathForResponsiveImages($this->media);
|
||||
|
||||
expect($path)->toBeString()
|
||||
->and($path)->toContain('responsive');
|
||||
});
|
||||
|
||||
it('handles missing custom properties gracefully', function () {
|
||||
$mediaWithoutProps = new Media([
|
||||
'id' => 2,
|
||||
'model_type' => 'App\Models\Test',
|
||||
'model_id' => 1,
|
||||
'collection_name' => 'default',
|
||||
'name' => 'test',
|
||||
'file_name' => 'test.jpg',
|
||||
'custom_properties' => [],
|
||||
]);
|
||||
|
||||
$path = $this->pathGenerator->getPath($mediaWithoutProps);
|
||||
|
||||
expect($path)->toBeString()
|
||||
->and($path)->toContain('misc');
|
||||
});
|
||||
});
|
||||
53
tests/Unit/Models/ClassificationTest.php
Normal file
53
tests/Unit/Models/ClassificationTest.php
Normal file
@ -0,0 +1,53 @@
|
||||
<?php
|
||||
|
||||
use App\Enums\IsActive;
|
||||
use App\Models\Classification;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
|
||||
uses(RefreshDatabase::class);
|
||||
|
||||
describe('Classification Model', function () {
|
||||
it('can create a classification', function () {
|
||||
$classification = Classification::factory()->create([
|
||||
'name' => 'Test Classification',
|
||||
]);
|
||||
|
||||
expect($classification->name)->toBe('Test Classification')
|
||||
->and($classification->exists)->toBeTrue();
|
||||
});
|
||||
|
||||
it('has guarded attributes', function () {
|
||||
$guarded = ['id'];
|
||||
|
||||
expect(Classification::make()->getGuarded())->toEqual($guarded);
|
||||
});
|
||||
|
||||
it('has sub classifications relationship', function () {
|
||||
$classification = Classification::factory()->create();
|
||||
|
||||
expect($classification->subClassifications())->toBeInstanceOf(\Illuminate\Database\Eloquent\Relations\HasMany::class);
|
||||
});
|
||||
|
||||
it('has content recaps relationship', function () {
|
||||
$classification = Classification::factory()->create();
|
||||
|
||||
expect($classification->contentRecaps())->toBeInstanceOf(\Illuminate\Database\Eloquent\Relations\HasMany::class);
|
||||
});
|
||||
|
||||
it('scopes active classifications', function () {
|
||||
Classification::factory()->create(['is_active' => IsActive::ACTIVE]);
|
||||
Classification::factory()->create(['is_active' => IsActive::INACTIVE]);
|
||||
|
||||
$activeClassifications = Classification::active()->get();
|
||||
|
||||
expect($activeClassifications)->toHaveCount(1)
|
||||
->and($activeClassifications->first()->is_active)->toBe(IsActive::ACTIVE);
|
||||
});
|
||||
|
||||
it('casts is_active to enum', function () {
|
||||
$classification = Classification::factory()->create(['is_active' => IsActive::ACTIVE]);
|
||||
|
||||
expect($classification->is_active)->toBeInstanceOf(IsActive::class)
|
||||
->and($classification->is_active)->toBe(IsActive::ACTIVE);
|
||||
});
|
||||
});
|
||||
43
tests/Unit/Models/CompanyTest.php
Normal file
43
tests/Unit/Models/CompanyTest.php
Normal file
@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
use App\Models\Company;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
|
||||
uses(RefreshDatabase::class);
|
||||
|
||||
describe('Company Model', function () {
|
||||
it('can create a company', function () {
|
||||
$company = Company::factory()->create([
|
||||
'name' => 'Test Media Company',
|
||||
'email' => 'company@example.com',
|
||||
]);
|
||||
|
||||
expect($company->name)->toBe('Test Media Company')
|
||||
->and($company->email)->toBe('company@example.com')
|
||||
->and($company->exists)->toBeTrue();
|
||||
});
|
||||
|
||||
it('has guarded attributes', function () {
|
||||
$guarded = ['id'];
|
||||
|
||||
expect(Company::make()->getGuarded())->toEqual($guarded);
|
||||
});
|
||||
|
||||
it('belongs to user', function () {
|
||||
$company = Company::factory()->create();
|
||||
|
||||
expect($company->user())->toBeInstanceOf(\Illuminate\Database\Eloquent\Relations\BelongsTo::class);
|
||||
});
|
||||
|
||||
it('has one partner media', function () {
|
||||
$company = Company::factory()->create();
|
||||
|
||||
expect($company->partnerMedia())->toBeInstanceOf(\Illuminate\Database\Eloquent\Relations\HasOne::class);
|
||||
});
|
||||
|
||||
it('has many journalists through partner media', function () {
|
||||
$company = Company::factory()->create();
|
||||
|
||||
expect($company->journalists())->toBeInstanceOf(\Illuminate\Database\Eloquent\Relations\HasManyThrough::class);
|
||||
});
|
||||
});
|
||||
37
tests/Unit/Models/ContentRecapTest.php
Normal file
37
tests/Unit/Models/ContentRecapTest.php
Normal file
@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
use App\Enums\ContentType;
|
||||
use App\Models\ContentRecap;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
|
||||
uses(RefreshDatabase::class);
|
||||
|
||||
describe('ContentRecap Model', function () {
|
||||
it('can create a content recap entry', function () {
|
||||
$recap = ContentRecap::factory()->create([
|
||||
'title' => 'Test Recap',
|
||||
]);
|
||||
|
||||
expect($recap->title)->toBe('Test Recap')
|
||||
->and($recap->exists)->toBeTrue();
|
||||
});
|
||||
|
||||
it('has guarded attributes', function () {
|
||||
$guarded = ['id'];
|
||||
|
||||
expect(ContentRecap::make()->getGuarded())->toEqual($guarded);
|
||||
});
|
||||
|
||||
it('belongs to classification', function () {
|
||||
$recap = ContentRecap::factory()->create();
|
||||
|
||||
expect($recap->classification())->toBeInstanceOf(\Illuminate\Database\Eloquent\Relations\BelongsTo::class);
|
||||
});
|
||||
|
||||
it('casts type to content type enum', function () {
|
||||
$recap = ContentRecap::factory()->create(['type' => ContentType::VIDEO]);
|
||||
|
||||
expect($recap->type)->toBeInstanceOf(ContentType::class)
|
||||
->and($recap->type)->toBe(ContentType::VIDEO);
|
||||
});
|
||||
});
|
||||
33
tests/Unit/Models/DepartmentTest.php
Normal file
33
tests/Unit/Models/DepartmentTest.php
Normal file
@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
use App\Enums\IsActive;
|
||||
use App\Models\Department;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
|
||||
uses(RefreshDatabase::class);
|
||||
|
||||
describe('Department Model', function () {
|
||||
it('can create a department', function () {
|
||||
$department = Department::factory()->create([
|
||||
'name' => 'Test Department',
|
||||
'alias' => 'TESTDEPT',
|
||||
]);
|
||||
|
||||
expect($department->name)->toBe('Test Department')
|
||||
->and($department->alias)->toBe('TESTDEPT')
|
||||
->and($department->exists)->toBeTrue();
|
||||
});
|
||||
|
||||
it('has guarded attributes', function () {
|
||||
$guarded = ['id'];
|
||||
|
||||
expect(Department::make()->getGuarded())->toEqual($guarded);
|
||||
});
|
||||
|
||||
it('casts is_active to enum', function () {
|
||||
$department = Department::factory()->create(['is_active' => IsActive::ACTIVE]);
|
||||
|
||||
expect($department->is_active)->toBeInstanceOf(IsActive::class)
|
||||
->and($department->is_active)->toBe(IsActive::ACTIVE);
|
||||
});
|
||||
});
|
||||
70
tests/Unit/Models/IssueManagementTest.php
Normal file
70
tests/Unit/Models/IssueManagementTest.php
Normal file
@ -0,0 +1,70 @@
|
||||
<?php
|
||||
|
||||
use App\Enums\IssueSentiment;
|
||||
use App\Models\IssueManagement;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
|
||||
uses(RefreshDatabase::class);
|
||||
|
||||
describe('IssueManagement Model', function () {
|
||||
it('can create an issue management entry', function () {
|
||||
$issue = IssueManagement::factory()->create([
|
||||
'description' => 'Test Issue Description',
|
||||
'issue' => IssueSentiment::POSITIVE,
|
||||
'response' => IssueSentiment::POSITIVE,
|
||||
]);
|
||||
|
||||
expect($issue->description)->toBe('Test Issue Description')
|
||||
->and($issue->issue)->toBe(IssueSentiment::POSITIVE)
|
||||
->and($issue->response)->toBe(IssueSentiment::POSITIVE)
|
||||
->and($issue->exists)->toBeTrue();
|
||||
});
|
||||
|
||||
it('has guarded attributes', function () {
|
||||
$guarded = ['id'];
|
||||
|
||||
expect(IssueManagement::make()->getGuarded())->toEqual($guarded);
|
||||
});
|
||||
|
||||
it('belongs to location', function () {
|
||||
$issue = IssueManagement::factory()->create();
|
||||
|
||||
expect($issue->location())->toBeInstanceOf(\Illuminate\Database\Eloquent\Relations\BelongsTo::class);
|
||||
});
|
||||
|
||||
it('belongs to sub location', function () {
|
||||
$issue = IssueManagement::factory()->create();
|
||||
|
||||
expect($issue->subLocation())->toBeInstanceOf(\Illuminate\Database\Eloquent\Relations\BelongsTo::class);
|
||||
});
|
||||
|
||||
it('belongs to classification', function () {
|
||||
$issue = IssueManagement::factory()->create();
|
||||
|
||||
expect($issue->classification())->toBeInstanceOf(\Illuminate\Database\Eloquent\Relations\BelongsTo::class);
|
||||
});
|
||||
|
||||
it('belongs to sub classification', function () {
|
||||
$issue = IssueManagement::factory()->create();
|
||||
|
||||
expect($issue->subClassification())->toBeInstanceOf(\Illuminate\Database\Eloquent\Relations\BelongsTo::class);
|
||||
});
|
||||
|
||||
it('belongs to media monitoring', function () {
|
||||
$issue = IssueManagement::factory()->create();
|
||||
|
||||
expect($issue->mediaMonitoring())->toBeInstanceOf(\Illuminate\Database\Eloquent\Relations\BelongsTo::class);
|
||||
});
|
||||
|
||||
it('casts sentiment enums correctly', function () {
|
||||
$issue = IssueManagement::factory()->create([
|
||||
'issue' => IssueSentiment::NEGATIVE,
|
||||
'response' => IssueSentiment::POSITIVE,
|
||||
]);
|
||||
|
||||
expect($issue->issue)->toBeInstanceOf(IssueSentiment::class)
|
||||
->and($issue->issue)->toBe(IssueSentiment::NEGATIVE)
|
||||
->and($issue->response)->toBeInstanceOf(IssueSentiment::class)
|
||||
->and($issue->response)->toBe(IssueSentiment::POSITIVE);
|
||||
});
|
||||
});
|
||||
31
tests/Unit/Models/JournalistTest.php
Normal file
31
tests/Unit/Models/JournalistTest.php
Normal file
@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
use App\Models\Journalist;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
|
||||
uses(RefreshDatabase::class);
|
||||
|
||||
describe('Journalist Model', function () {
|
||||
it('can create a journalist', function () {
|
||||
$journalist = Journalist::factory()->create([
|
||||
'name' => 'Test Journalist',
|
||||
'email' => 'journalist@example.com',
|
||||
]);
|
||||
|
||||
expect($journalist->name)->toBe('Test Journalist')
|
||||
->and($journalist->email)->toBe('journalist@example.com')
|
||||
->and($journalist->exists)->toBeTrue();
|
||||
});
|
||||
|
||||
it('has guarded attributes', function () {
|
||||
$guarded = ['id'];
|
||||
|
||||
expect(Journalist::make()->getGuarded())->toEqual($guarded);
|
||||
});
|
||||
|
||||
it('belongs to partner media', function () {
|
||||
$journalist = Journalist::factory()->create();
|
||||
|
||||
expect($journalist->partnerMedia())->toBeInstanceOf(\Illuminate\Database\Eloquent\Relations\BelongsTo::class);
|
||||
});
|
||||
});
|
||||
37
tests/Unit/Models/LocationTest.php
Normal file
37
tests/Unit/Models/LocationTest.php
Normal file
@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
use App\Enums\IsActive;
|
||||
use App\Models\Location;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
|
||||
uses(RefreshDatabase::class);
|
||||
|
||||
describe('Location Model', function () {
|
||||
it('can create a location', function () {
|
||||
$location = Location::factory()->create([
|
||||
'name' => 'Test Location',
|
||||
]);
|
||||
|
||||
expect($location->name)->toBe('Test Location')
|
||||
->and($location->exists)->toBeTrue();
|
||||
});
|
||||
|
||||
it('has guarded attributes', function () {
|
||||
$guarded = ['id'];
|
||||
|
||||
expect(Location::make()->getGuarded())->toEqual($guarded);
|
||||
});
|
||||
|
||||
it('has sub locations relationship', function () {
|
||||
$location = Location::factory()->create();
|
||||
|
||||
expect($location->subLocations())->toBeInstanceOf(\Illuminate\Database\Eloquent\Relations\HasMany::class);
|
||||
});
|
||||
|
||||
it('casts is_active to enum', function () {
|
||||
$location = Location::factory()->create(['is_active' => IsActive::ACTIVE]);
|
||||
|
||||
expect($location->is_active)->toBeInstanceOf(IsActive::class)
|
||||
->and($location->is_active)->toBe(IsActive::ACTIVE);
|
||||
});
|
||||
});
|
||||
48
tests/Unit/Models/MediaMonitoringTest.php
Normal file
48
tests/Unit/Models/MediaMonitoringTest.php
Normal file
@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
use App\Models\MediaMonitoring;
|
||||
use App\Models\Theme;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
|
||||
uses(RefreshDatabase::class);
|
||||
|
||||
describe('MediaMonitoring Model', function () {
|
||||
it('can create a media monitoring entry', function () {
|
||||
$mediaMonitoring = MediaMonitoring::factory()->create([
|
||||
'title' => 'Test Media Title',
|
||||
'media_name' => 'Test Media',
|
||||
]);
|
||||
|
||||
expect($mediaMonitoring->title)->toBe('Test Media Title')
|
||||
->and($mediaMonitoring->media_name)->toBe('Test Media')
|
||||
->and($mediaMonitoring->exists)->toBeTrue();
|
||||
});
|
||||
|
||||
it('has guarded attributes', function () {
|
||||
$guarded = ['id'];
|
||||
|
||||
expect(MediaMonitoring::make()->getGuarded())->toEqual($guarded);
|
||||
});
|
||||
|
||||
it('has themes relationship', function () {
|
||||
$mediaMonitoring = MediaMonitoring::factory()->create();
|
||||
|
||||
expect($mediaMonitoring->themes())->toBeInstanceOf(\Illuminate\Database\Eloquent\Relations\BelongsToMany::class);
|
||||
});
|
||||
|
||||
it('has issue management relationship', function () {
|
||||
$mediaMonitoring = MediaMonitoring::factory()->create();
|
||||
|
||||
expect($mediaMonitoring->issueManagement())->toBeInstanceOf(\Illuminate\Database\Eloquent\Relations\HasOne::class);
|
||||
});
|
||||
|
||||
it('can attach themes', function () {
|
||||
$mediaMonitoring = MediaMonitoring::factory()->create();
|
||||
$theme = Theme::factory()->create();
|
||||
|
||||
$mediaMonitoring->themes()->attach($theme->id);
|
||||
|
||||
expect($mediaMonitoring->themes)->toHaveCount(1)
|
||||
->and($mediaMonitoring->themes->first()->id)->toBe($theme->id);
|
||||
});
|
||||
});
|
||||
34
tests/Unit/Models/MediaMonitoringThemeTest.php
Normal file
34
tests/Unit/Models/MediaMonitoringThemeTest.php
Normal file
@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
use App\Models\MediaMonitoringTheme;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
|
||||
uses(RefreshDatabase::class);
|
||||
|
||||
describe('MediaMonitoringTheme Model', function () {
|
||||
it('can create a media monitoring theme pivot', function () {
|
||||
$pivot = MediaMonitoringTheme::factory()->create();
|
||||
|
||||
expect($pivot->exists)->toBeTrue()
|
||||
->and($pivot->media_monitoring_id)->toBeInt()
|
||||
->and($pivot->theme_id)->toBeInt();
|
||||
});
|
||||
|
||||
it('has guarded attributes', function () {
|
||||
$guarded = ['id'];
|
||||
|
||||
expect(MediaMonitoringTheme::make()->getGuarded())->toEqual($guarded);
|
||||
});
|
||||
|
||||
it('belongs to media monitoring', function () {
|
||||
$pivot = MediaMonitoringTheme::factory()->create();
|
||||
|
||||
expect($pivot->mediaMonitoring())->toBeInstanceOf(\Illuminate\Database\Eloquent\Relations\BelongsTo::class);
|
||||
});
|
||||
|
||||
it('belongs to theme', function () {
|
||||
$pivot = MediaMonitoringTheme::factory()->create();
|
||||
|
||||
expect($pivot->theme())->toBeInstanceOf(\Illuminate\Database\Eloquent\Relations\BelongsTo::class);
|
||||
});
|
||||
});
|
||||
45
tests/Unit/Models/PartnerMediaTest.php
Normal file
45
tests/Unit/Models/PartnerMediaTest.php
Normal file
@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
use App\Enums\MediaClassification;
|
||||
use App\Enums\MediaType;
|
||||
use App\Models\PartnerMedia;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
|
||||
uses(RefreshDatabase::class);
|
||||
|
||||
describe('PartnerMedia Model', function () {
|
||||
it('can create a partner media', function () {
|
||||
$partner = PartnerMedia::factory()->create([
|
||||
'name' => 'Test Partner',
|
||||
]);
|
||||
|
||||
expect($partner->name)->toBe('Test Partner')
|
||||
->and($partner->exists)->toBeTrue();
|
||||
});
|
||||
|
||||
it('has guarded attributes', function () {
|
||||
$guarded = ['id'];
|
||||
|
||||
expect(PartnerMedia::make()->getGuarded())->toEqual($guarded);
|
||||
});
|
||||
|
||||
it('belongs to company', function () {
|
||||
$partner = PartnerMedia::factory()->create();
|
||||
|
||||
expect($partner->company())->toBeInstanceOf(\Illuminate\Database\Eloquent\Relations\BelongsTo::class);
|
||||
});
|
||||
|
||||
it('casts type to media type enum', function () {
|
||||
$partner = PartnerMedia::factory()->create(['type' => MediaType::ONLINE]);
|
||||
|
||||
expect($partner->type)->toBeInstanceOf(MediaType::class)
|
||||
->and($partner->type)->toBe(MediaType::ONLINE);
|
||||
});
|
||||
|
||||
it('casts classification to media classification enum', function () {
|
||||
$partner = PartnerMedia::factory()->create(['classification' => MediaClassification::NATIONAL]);
|
||||
|
||||
expect($partner->classification)->toBeInstanceOf(MediaClassification::class)
|
||||
->and($partner->classification)->toBe(MediaClassification::NATIONAL);
|
||||
});
|
||||
});
|
||||
37
tests/Unit/Models/SubClassificationTest.php
Normal file
37
tests/Unit/Models/SubClassificationTest.php
Normal file
@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
use App\Enums\IsActive;
|
||||
use App\Models\SubClassification;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
|
||||
uses(RefreshDatabase::class);
|
||||
|
||||
describe('SubClassification Model', function () {
|
||||
it('can create a sub classification', function () {
|
||||
$subClassification = SubClassification::factory()->create([
|
||||
'name' => 'Test Sub Classification',
|
||||
]);
|
||||
|
||||
expect($subClassification->name)->toBe('Test Sub Classification')
|
||||
->and($subClassification->exists)->toBeTrue();
|
||||
});
|
||||
|
||||
it('has guarded attributes', function () {
|
||||
$guarded = ['id'];
|
||||
|
||||
expect(SubClassification::make()->getGuarded())->toEqual($guarded);
|
||||
});
|
||||
|
||||
it('belongs to classification', function () {
|
||||
$subClassification = SubClassification::factory()->create();
|
||||
|
||||
expect($subClassification->classification())->toBeInstanceOf(\Illuminate\Database\Eloquent\Relations\BelongsTo::class);
|
||||
});
|
||||
|
||||
it('casts is_active to enum', function () {
|
||||
$subClassification = SubClassification::factory()->create(['is_active' => IsActive::ACTIVE]);
|
||||
|
||||
expect($subClassification->is_active)->toBeInstanceOf(IsActive::class)
|
||||
->and($subClassification->is_active)->toBe(IsActive::ACTIVE);
|
||||
});
|
||||
});
|
||||
37
tests/Unit/Models/SubLocationTest.php
Normal file
37
tests/Unit/Models/SubLocationTest.php
Normal file
@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
use App\Enums\IsActive;
|
||||
use App\Models\SubLocation;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
|
||||
uses(RefreshDatabase::class);
|
||||
|
||||
describe('SubLocation Model', function () {
|
||||
it('can create a sub location', function () {
|
||||
$subLocation = SubLocation::factory()->create([
|
||||
'name' => 'Test Sub Location',
|
||||
]);
|
||||
|
||||
expect($subLocation->name)->toBe('Test Sub Location')
|
||||
->and($subLocation->exists)->toBeTrue();
|
||||
});
|
||||
|
||||
it('has guarded attributes', function () {
|
||||
$guarded = ['id'];
|
||||
|
||||
expect(SubLocation::make()->getGuarded())->toEqual($guarded);
|
||||
});
|
||||
|
||||
it('belongs to location', function () {
|
||||
$subLocation = SubLocation::factory()->create();
|
||||
|
||||
expect($subLocation->location())->toBeInstanceOf(\Illuminate\Database\Eloquent\Relations\BelongsTo::class);
|
||||
});
|
||||
|
||||
it('casts is_active to enum', function () {
|
||||
$subLocation = SubLocation::factory()->create(['is_active' => IsActive::ACTIVE]);
|
||||
|
||||
expect($subLocation->is_active)->toBeInstanceOf(IsActive::class)
|
||||
->and($subLocation->is_active)->toBe(IsActive::ACTIVE);
|
||||
});
|
||||
});
|
||||
47
tests/Unit/Models/ThemeTest.php
Normal file
47
tests/Unit/Models/ThemeTest.php
Normal file
@ -0,0 +1,47 @@
|
||||
<?php
|
||||
|
||||
use App\Enums\IsActive;
|
||||
use App\Models\Theme;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
|
||||
uses(RefreshDatabase::class);
|
||||
|
||||
describe('Theme Model', function () {
|
||||
it('can create a theme', function () {
|
||||
$theme = Theme::factory()->create([
|
||||
'name' => 'Test Theme',
|
||||
]);
|
||||
|
||||
expect($theme->name)->toBe('Test Theme')
|
||||
->and($theme->exists)->toBeTrue();
|
||||
});
|
||||
|
||||
it('has guarded attributes', function () {
|
||||
$guarded = ['id'];
|
||||
|
||||
expect(Theme::make()->getGuarded())->toEqual($guarded);
|
||||
});
|
||||
|
||||
it('has media monitorings relationship', function () {
|
||||
$theme = Theme::factory()->create();
|
||||
|
||||
expect($theme->mediaMonitorings())->toBeInstanceOf(\Illuminate\Database\Eloquent\Relations\BelongsToMany::class);
|
||||
});
|
||||
|
||||
it('scopes active themes', function () {
|
||||
Theme::factory()->create(['is_active' => IsActive::ACTIVE]);
|
||||
Theme::factory()->create(['is_active' => IsActive::INACTIVE]);
|
||||
|
||||
$activeThemes = Theme::active()->get();
|
||||
|
||||
expect($activeThemes)->toHaveCount(1)
|
||||
->and($activeThemes->first()->is_active)->toBe(IsActive::ACTIVE);
|
||||
});
|
||||
|
||||
it('casts is_active to enum', function () {
|
||||
$theme = Theme::factory()->create(['is_active' => IsActive::ACTIVE]);
|
||||
|
||||
expect($theme->is_active)->toBeInstanceOf(IsActive::class)
|
||||
->and($theme->is_active)->toBe(IsActive::ACTIVE);
|
||||
});
|
||||
});
|
||||
47
tests/Unit/Models/UserTest.php
Normal file
47
tests/Unit/Models/UserTest.php
Normal file
@ -0,0 +1,47 @@
|
||||
<?php
|
||||
|
||||
use App\Models\User;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
|
||||
uses(RefreshDatabase::class);
|
||||
|
||||
describe('User Model', function () {
|
||||
it('can create a user', function () {
|
||||
$user = User::factory()->create([
|
||||
'name' => 'Test User',
|
||||
'email' => 'test@example.com',
|
||||
]);
|
||||
|
||||
expect($user->name)->toBe('Test User')
|
||||
->and($user->email)->toBe('test@example.com')
|
||||
->and($user->exists)->toBeTrue();
|
||||
});
|
||||
|
||||
it('has guarded attributes', function () {
|
||||
$guarded = ['id'];
|
||||
|
||||
expect(User::make()->getGuarded())->toEqual($guarded);
|
||||
});
|
||||
|
||||
it('has hidden attributes', function () {
|
||||
$hidden = ['password', 'remember_token'];
|
||||
|
||||
expect(User::make()->getHidden())->toEqual($hidden);
|
||||
});
|
||||
|
||||
it('has email verification timestamp', function () {
|
||||
$user = User::factory()->create();
|
||||
|
||||
expect($user->getCasts())->toHaveKey('email_verified_at');
|
||||
});
|
||||
|
||||
it('can verify email', function () {
|
||||
$user = User::factory()->create(['email_verified_at' => null]);
|
||||
|
||||
expect($user->email_verified_at)->toBeNull();
|
||||
|
||||
$user->markEmailAsVerified();
|
||||
|
||||
expect($user->email_verified_at)->not()->toBeNull();
|
||||
});
|
||||
});
|
||||
46
tests/Unit/Traits/WithCommentTest.php
Normal file
46
tests/Unit/Traits/WithCommentTest.php
Normal file
@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
use App\Traits\Enum\WithComment;
|
||||
use Filament\Support\Contracts\HasLabel;
|
||||
|
||||
enum TestCommentEnum: string implements HasLabel
|
||||
{
|
||||
use WithComment;
|
||||
|
||||
case ACTIVE = 'active';
|
||||
case INACTIVE = 'inactive';
|
||||
|
||||
public function getLabel(): ?string
|
||||
{
|
||||
return match ($this) {
|
||||
self::ACTIVE => 'Status Aktif',
|
||||
self::INACTIVE => 'Status Tidak Aktif',
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
describe('WithComment Trait', function () {
|
||||
it('can generate comment string from enum cases', function () {
|
||||
$comment = TestCommentEnum::comment();
|
||||
|
||||
expect($comment)->toBeString()
|
||||
->and($comment)->toContain('active: Status Aktif')
|
||||
->and($comment)->toContain('inactive: Status Tidak Aktif');
|
||||
});
|
||||
|
||||
it('formats comment with comma separation', function () {
|
||||
$comment = TestCommentEnum::comment();
|
||||
|
||||
expect($comment)->toContain(', ');
|
||||
});
|
||||
|
||||
it('includes all enum cases in comment', function () {
|
||||
$comment = TestCommentEnum::comment();
|
||||
$cases = TestCommentEnum::cases();
|
||||
|
||||
foreach ($cases as $case) {
|
||||
expect($comment)->toContain($case->value);
|
||||
expect($comment)->toContain($case->getLabel());
|
||||
}
|
||||
});
|
||||
});
|
||||
29
tests/Unit/Traits/WithValueTest.php
Normal file
29
tests/Unit/Traits/WithValueTest.php
Normal file
@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
use App\Traits\Enum\WithValue;
|
||||
|
||||
enum TestValueEnum: string
|
||||
{
|
||||
use WithValue;
|
||||
|
||||
case ACTIVE = 'active';
|
||||
case INACTIVE = 'inactive';
|
||||
}
|
||||
|
||||
describe('WithValue Trait', function () {
|
||||
it('can get all values from enum cases', function () {
|
||||
$values = TestValueEnum::values();
|
||||
|
||||
expect($values)->toBeArray()
|
||||
->and($values)->toEqual(['active', 'inactive'])
|
||||
->and($values)->toHaveCount(2);
|
||||
});
|
||||
|
||||
it('returns correct value types', function () {
|
||||
$values = TestValueEnum::values();
|
||||
|
||||
foreach ($values as $value) {
|
||||
expect($value)->toBeString();
|
||||
}
|
||||
});
|
||||
});
|
||||
Loading…
Reference in New Issue
Block a user