diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml new file mode 100644 index 0000000..da7b93b --- /dev/null +++ b/.github/workflows/tests.yml @@ -0,0 +1,87 @@ +name: Tests + +on: + push: + branches: [master] + pull_request: + branches: [master] + +jobs: + test: + runs-on: ubuntu-latest + + strategy: + fail-fast: false + matrix: + php: [8.1, 8.2, 8.3, 8.4] + laravel: [10.*, 11.*, 12.*] + exclude: + - php: 8.1 + laravel: 11.* + - php: 8.1 + laravel: 12.* + + name: PHP ${{ matrix.php }} - Laravel ${{ matrix.laravel }} + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php }} + extensions: dom, curl, libxml, mbstring, zip, json + coverage: none + + - name: Install dependencies + run: | + composer require "laravel/framework:${{ matrix.laravel }}" --no-interaction --no-update + composer update --prefer-dist --no-interaction + + - name: Run tests + run: composer test + + static-analysis: + runs-on: ubuntu-latest + + name: Static Analysis + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: 8.3 + extensions: dom, curl, libxml, mbstring, zip, json + coverage: none + + - name: Install dependencies + run: composer update --prefer-dist --no-interaction + + - name: Run PHPStan + run: composer analyse + + code-style: + runs-on: ubuntu-latest + + name: Code Style + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: 8.3 + extensions: dom, curl, libxml, mbstring, zip, json + coverage: none + + - name: Install dependencies + run: composer update --prefer-dist --no-interaction + + - name: Check code style + run: ./vendor/bin/php-cs-fixer fix --dry-run --diff --allow-risky=yes diff --git a/.gitignore b/.gitignore index 6c6cff4..a7c1796 100644 --- a/.gitignore +++ b/.gitignore @@ -6,7 +6,7 @@ build coverage .env .env.backup -.php_cs.cache +.php-cs-fixer.cache .phpunit.result.cache .phpunit.cache .phpstorm.meta.php diff --git a/.php-cs-fixer.dist.php b/.php-cs-fixer.dist.php new file mode 100644 index 0000000..3012836 --- /dev/null +++ b/.php-cs-fixer.dist.php @@ -0,0 +1,39 @@ +in([ + __DIR__ . '/src', + __DIR__ . '/tests', + ]) + ->name('*.php') + ->notName('*.blade.php') + ->exclude([ + '__snapshots__', + '.pest', + ]) + ->ignoreDotFiles(true) + ->ignoreVCS(true); + +return (new PhpCsFixer\Config()) + ->setRules([ + '@PSR12' => true, + 'array_syntax' => ['syntax' => 'short'], + 'ordered_imports' => ['sort_algorithm' => 'alpha'], + 'no_unused_imports' => true, + 'not_operator_with_successor_space' => true, + 'trailing_comma_in_multiline' => true, + 'phpdoc_scalar' => true, + 'unary_operator_spaces' => true, + 'binary_operator_spaces' => true, + 'blank_line_before_statement' => [ + 'statements' => ['break', 'continue', 'declare', 'return', 'throw', 'try'], + ], + 'phpdoc_single_line_var_spacing' => true, + 'phpdoc_var_without_name' => true, + 'method_argument_space' => [ + 'on_multiline' => 'ensure_fully_multiline', + 'keep_multiple_spaces_after_comma' => true, + ], + 'single_trait_insert_per_statement' => true, + ]) + ->setFinder($finder); diff --git a/README.md b/README.md index bc2d753..83b500f 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,871 @@ # Laraneat Modules -This package is reworked version of [Laravel modules](https://github.com/nWidart/laravel-modules/). +A Laravel package that provides a powerful modular architecture system for organizing large-scale applications into self-contained, reusable modules. -- The code is completely rewritten in PHP 8 -- Removed dead unused code -- Significantly improved performance by refactoring the caching system -- Dropped Lumen support -- Rewritten code generators, added many new component generators related to [Laraneat framework](https://github.com/laraneat/laraneat) +## Table of Contents -Developed as part of the [Laraneat framework](https://github.com/laraneat/laraneat) +- [Overview](#overview) +- [Performance Comparison](#performance-comparison-with-nwidartlaravel-modules) +- [Architecture Diagram](#architecture-diagram) +- [Installation](#installation) +- [Quick Start](#quick-start) +- [Module Structure](#module-structure) +- [Core Concepts](#core-concepts) +- [Configuration](#configuration) +- [Artisan Commands](#artisan-commands) +- [Component Types](#component-types) +- [Module Presets](#module-presets) +- [Best Practices](#best-practices) -Currently under development. +## Overview + +Laraneat Modules helps you build maintainable, scalable Laravel applications. Inspired by the [Porto SAP (Software Architectural Pattern)](https://github.com/Mahmoudz/Porto), it encourages organizing code by business domains (modules) instead of technical layers. + +### Why Modular Architecture? + +| Traditional Laravel | Modular Approach | +|---------------------|------------------| +| All controllers in `app/Http/Controllers` | Each module has its own controllers | +| All models in `app/Models` | Each module has its own models | +| Coupled, hard to maintain | Decoupled, easy to maintain | +| Difficult to reuse | Easy to extract and reuse | +| Complex routing | Module-scoped routing | + +## Performance Comparison with nWidart/laravel-modules + +This package is designed with performance in mind. **With caching enabled, it adds virtually zero overhead** — the cached manifest is a simple PHP array that loads in microseconds, and all core services use lazy loading. + +Here's how it compares to the popular [nWidart/laravel-modules](https://github.com/nWidart/laravel-modules): + +### Key Differences + +| Feature | Laraneat Modules | nWidart/laravel-modules | +|---------|------------------|-------------------------| +| **Module manifest** | `composer.json` only | `module.json` + `composer.json` | +| **Cache type** | Persistent file cache | In-memory only (per request) | +| **Service providers** | DeferrableProvider (lazy) | Eager loading | +| **Enable/disable modules** | Not supported | Supported via JSON file | +| **Architecture pattern** | Domain-driven (Porto-inspired) | Flexible structure | + +### Performance Impact + +#### Production (with cache enabled) + +| Metric | Laraneat | nWidart | +|--------|----------|---------| +| File operations (first request) | 1 (cached manifest) | N (module.json × modules) | +| File operations (subsequent) | 1 | N | +| Providers loaded | On-demand | All modules | + +#### Development (without cache) + +Both packages scan the filesystem on each request. However, Laraneat uses `DeferrableProvider`, so the `ModulesRepository` is only instantiated when actually needed. + +### Why Laraneat is Faster + +1. **Persistent manifest cache** — Module metadata is cached to `bootstrap/cache/laraneat-modules.php`, eliminating filesystem scans in production. + +2. **DeferrableProvider** — Core services (`ModulesRepository`, `Composer`, console commands) implement Laravel's `DeferrableProvider` interface, loading only when requested. + +3. **Single manifest file** — Uses existing `composer.json` instead of requiring an additional `module.json` per module. + +4. **No status file I/O** — No `modules_statuses.json` reads on every request (unlike nWidart's enabled/disabled feature). + +### Recommendations + +```php +// config/modules.php - Enable cache in production +'cache' => [ + 'enabled' => env('APP_ENV') === 'production', +], +``` + +After deployment: +```bash +php artisan module:cache +``` + +For development with many modules, consider enabling cache manually to avoid repeated filesystem scans. + +## Architecture Diagram + +``` +┌─────────────────────────────────────────────────────────────────────────────┐ +│ LARAVEL APPLICATION │ +├─────────────────────────────────────────────────────────────────────────────┤ +│ │ +│ ┌─────────────────────────────────────────────────────────────────────┐ │ +│ │ ModulesRepository │ │ +│ │ - Discovers modules by scanning configured paths │ │ +│ │ - Manages module manifest (cached in production) │ │ +│ │ - Provides find, filter, delete operations │ │ +│ └──────────────────────────────┬──────────────────────────────────────┘ │ +│ │ │ +│ ┌────────────┼────────────┐ │ +│ │ │ │ │ +│ ▼ ▼ ▼ │ +│ ┌─────────────────────┐ ┌─────────────────────┐ ┌─────────────────────┐ │ +│ │ MODULE: Users │ │ MODULE: Articles │ │ MODULE: Orders │ │ +│ │ │ │ │ │ │ │ +│ │ ┌───────────────┐ │ │ ┌───────────────┐ │ │ ┌───────────────┐ │ │ +│ │ │ composer.json│ │ │ │ composer.json│ │ │ │ composer.json│ │ │ +│ │ │ - providers │ │ │ │ - providers │ │ │ │ - providers │ │ │ +│ │ │ - aliases │ │ │ │ - aliases │ │ │ │ - aliases │ │ │ +│ │ │ - namespace │ │ │ │ - namespace │ │ │ │ - namespace │ │ │ +│ │ └───────────────┘ │ │ └───────────────┘ │ │ └───────────────┘ │ │ +│ │ │ │ │ │ │ │ +│ │ src/ │ │ src/ │ │ src/ │ │ +│ │ ├── Actions/ │ │ ├── Actions/ │ │ ├── Actions/ │ │ +│ │ ├── Models/ │ │ ├── Models/ │ │ ├── Models/ │ │ +│ │ ├── Providers/ │ │ ├── Providers/ │ │ ├── Providers/ │ │ +│ │ └── UI/ │ │ └── UI/ │ │ └── UI/ │ │ +│ │ ├── API/ │ │ ├── API/ │ │ ├── API/ │ │ +│ │ ├── WEB/ │ │ ├── WEB/ │ │ ├── WEB/ │ │ +│ │ └── CLI/ │ │ └── CLI/ │ │ └── CLI/ │ │ +│ │ │ │ │ │ │ │ +│ │ database/ │ │ database/ │ │ database/ │ │ +│ │ └── migrations/ │ │ └── migrations/ │ │ └── migrations/ │ │ +│ │ │ │ │ │ │ │ +│ │ tests/ │ │ tests/ │ │ tests/ │ │ +│ └─────────────────────┘ └─────────────────────┘ └─────────────────────┘ │ +│ │ +└─────────────────────────────────────────────────────────────────────────────┘ +``` + +### Module Internal Architecture + +``` +┌────────────────────────────────────────────────────────────────────────┐ +│ MODULE │ +├────────────────────────────────────────────────────────────────────────┤ +│ │ +│ ┌──────────────────────────── UI LAYER ────────────────────────────┐ │ +│ │ │ │ +│ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ │ +│ │ │ API │ │ WEB │ │ CLI │ │ │ +│ │ │ Controllers │ │ Controllers │ │ Commands │ │ │ +│ │ │ Requests │ │ Requests │ │ │ │ │ +│ │ │ Resources │ │ Views │ │ │ │ │ +│ │ │ Routes │ │ Routes │ │ │ │ │ +│ │ └──────┬──────┘ └──────┬──────┘ └──────┬──────┘ │ │ +│ │ │ │ │ │ │ +│ └─────────┼──────────────────┼──────────────────┼──────────────────┘ │ +│ │ │ │ │ +│ └──────────────────┼──────────────────┘ │ +│ ▼ │ +│ ┌────────────────────── DOMAIN LAYER ──────────────────────────────┐ │ +│ │ │ │ +│ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ │ +│ │ │ Actions │ │ DTOs │ │ Events │ │ │ +│ │ │ (Business │ │ (Data │ │ (Domain │ │ │ +│ │ │ Logic) │ │ Transfer) │ │ Events) │ │ │ +│ │ └──────┬──────┘ └─────────────┘ └─────────────┘ │ │ +│ │ │ │ │ +│ │ ▼ │ │ +│ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ │ +│ │ │ Models │ │ Observers │ │ Rules │ │ │ +│ │ │ (Entities) │ │ │ │(Validation) │ │ │ +│ │ └─────────────┘ └─────────────┘ └─────────────┘ │ │ +│ │ │ │ +│ └───────────────────────────────────────────────────────────────────┘ │ +│ │ +│ ┌─────────────────── INFRASTRUCTURE LAYER ─────────────────────────┐ │ +│ │ │ │ +│ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ │ +│ │ │ Providers │ │ Middleware │ │ Policies │ │ │ +│ │ └─────────────┘ └─────────────┘ └─────────────┘ │ │ +│ │ │ │ +│ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ │ +│ │ │ Mails │ │Notifications│ │ Jobs │ │ │ +│ │ └─────────────┘ └─────────────┘ └─────────────┘ │ │ +│ │ │ │ +│ └───────────────────────────────────────────────────────────────────┘ │ +│ │ +│ ┌──────────────────── DATABASE LAYER ──────────────────────────────┐ │ +│ │ │ │ +│ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ │ +│ │ │ Migrations │ │ Seeders │ │ Factories │ │ │ +│ │ └─────────────┘ └─────────────┘ └─────────────┘ │ │ +│ │ │ │ +│ └───────────────────────────────────────────────────────────────────┘ │ +│ │ +└────────────────────────────────────────────────────────────────────────┘ +``` + +### Request Flow Through Module + +Actions serve as controllers using the `lorisleiva/laravel-actions` package. Each Action has two entry points: +- `handle()` - Core business logic (can be called from anywhere) +- `asController()` - HTTP entry point (receives Request, returns Response) + +``` +┌──────────┐ ┌───────────┐ ┌────────────┐ +│ HTTP │────▶│ Routes │────▶│ Middleware │ +│ Request │ │ │ │ │ +└──────────┘ └───────────┘ └─────┬──────┘ + │ + ┌───────────────────────┘ + ▼ +┌─────────────────────────────────────────────────────────────────┐ +│ ACTION │ +│ ┌─────────────────────────────────────────────────────────┐ │ +│ │ asController(Request $request) ◀── HTTP Entry │ │ +│ │ │ │ │ +│ │ ├── $request->toDTO() ─────────▶ DTO │ │ +│ │ │ │ │ +│ │ └── $this->handle($dto) │ │ +│ └────────────────────────┬────────────────────────────────┘ │ +│ ▼ │ +│ ┌─────────────────────────────────────────────────────────┐ │ +│ │ handle(DTO $dto) ◀── Business Logic │ │ +│ │ │ │ │ +│ │ └── Model::create($dto->all()) ──▶ Database │ │ +│ └────────────────────────┬────────────────────────────────┘ │ +│ │ │ +└───────────────────────────┼──────────────────────────────────────┘ + ▼ + ┌────────────────┐ ┌─────────────┐ + │ Resource │────▶│ HTTP │ + │ (Format) │ │ Response │ + └────────────────┘ └─────────────┘ +``` + +**Example Action:** + +```php +class CreatePostAction +{ + use AsAction; + + // Business logic - can be called from anywhere + public function handle(CreatePostDTO $dto): Post + { + return Post::create($dto->all()); + } + + // HTTP entry point - acts as controller + public function asController(CreatePostRequest $request): JsonResponse + { + $post = $this->handle($request->toDTO()); + + return (new PostResource($post))->created(); + } +} +``` + +## Installation + +```bash +composer require laraneat/modules +``` + +Publish the configuration file: + +```bash +php artisan vendor:publish --provider="Laraneat\Modules\Providers\ModulesServiceProvider" +``` + +## Quick Start + +### 1. Create Your First Module + +```bash +php artisan module:make Blog +``` + +This creates a new module at `modules/blog/` with basic structure. + +### 2. Create Module with Full API + +```bash +php artisan module:make Blog --preset=api --entity=Post +``` + +This creates a complete REST API module with: +- Controllers for CRUD operations +- Request validation classes +- API resources for JSON responses +- Database migrations and seeders +- Complete test suite + +### 3. Generate Components + +```bash +# Create a model +php artisan module:make:model Post app/blog + +# Create a controller +php artisan module:make:controller PostController app/blog --ui=api + +# Create a migration +php artisan module:make:migration create_posts_table app/blog + +# Create an action +php artisan module:make:action CreatePostAction app/blog +``` + +### 4. Run Module Migrations + +```bash +php artisan module:migrate +``` + +## Module Structure + +A complete module follows this structure: + +``` +modules/blog/ +├── composer.json # Module package definition +├── src/ +│ ├── Actions/ # Business logic actions +│ │ ├── CreatePostAction.php +│ │ ├── UpdatePostAction.php +│ │ └── DeletePostAction.php +│ │ +│ ├── Models/ # Eloquent models +│ │ └── Post.php +│ │ +│ ├── DTO/ # Data Transfer Objects +│ │ ├── CreatePostDTO.php +│ │ └── UpdatePostDTO.php +│ │ +│ ├── Events/ # Domain events +│ │ └── PostCreated.php +│ │ +│ ├── Listeners/ # Event listeners +│ │ └── SendPostNotification.php +│ │ +│ ├── Jobs/ # Queued jobs +│ │ └── ProcessPost.php +│ │ +│ ├── Policies/ # Authorization policies +│ │ └── PostPolicy.php +│ │ +│ ├── Providers/ # Service providers +│ │ ├── BlogServiceProvider.php +│ │ └── RouteServiceProvider.php +│ │ +│ └── UI/ +│ ├── API/ +│ │ ├── Controllers/ +│ │ │ └── PostController.php +│ │ ├── Requests/ +│ │ │ ├── CreatePostRequest.php +│ │ │ └── UpdatePostRequest.php +│ │ ├── Resources/ +│ │ │ └── PostResource.php +│ │ ├── QueryWizards/ +│ │ │ └── PostsQueryWizard.php +│ │ └── routes/ +│ │ └── v1.php +│ │ +│ ├── WEB/ +│ │ ├── Controllers/ +│ │ ├── Requests/ +│ │ └── routes/ +│ │ +│ └── CLI/ +│ └── Commands/ +│ +├── database/ +│ ├── migrations/ +│ │ └── 2024_01_01_create_posts_table.php +│ ├── seeders/ +│ │ └── PostSeeder.php +│ └── factories/ +│ └── PostFactory.php +│ +├── resources/ +│ └── views/ +│ +├── lang/ +│ +├── config/ +│ +└── tests/ + ├── Unit/ + ├── Feature/ + └── API/ +``` + +## Core Concepts + +### Module + +A **Module** represents a self-contained business domain. It's identified by its Composer package name (e.g., `app/blog`). + +```php +use Laraneat\Modules\ModulesRepository; + +$repository = app(ModulesRepository::class); + +// Find a module +$module = $repository->find('app/blog'); + +// Get module properties +$module->getName(); // "blog" +$module->getStudlyName(); // "Blog" +$module->getNamespace(); // "Modules\Blog" +$module->getPath(); // "/path/to/modules/blog" +$module->getProviders(); // ["Modules\Blog\Providers\BlogServiceProvider"] +``` + +### ModulesRepository + +The **ModulesRepository** discovers and manages all modules in your application. + +```php +use Laraneat\Modules\ModulesRepository; + +$repository = app(ModulesRepository::class); + +// Get all modules +$modules = $repository->getModules(); + +// Check if module exists +$repository->has('app/blog'); + +// Find module by name +$repository->filterByName('Blog'); + +// Delete a module +$repository->delete('app/blog'); +``` + +### Actions + +**Actions** are the core of the architecture. Using `lorisleiva/laravel-actions`, they serve dual purposes: +- **Business Logic** via `handle()` method - can be called from anywhere (other actions, jobs, commands) +- **HTTP Controller** via `asController()` method - handles HTTP requests directly + +```php +// src/Actions/CreatePostAction.php +namespace Modules\Blog\Actions; + +use Lorisleiva\Actions\Concerns\AsAction; +use Modules\Blog\DTO\CreatePostDTO; +use Modules\Blog\Models\Post; +use Modules\Blog\UI\API\Requests\CreatePostRequest; +use Modules\Blog\UI\API\Resources\PostResource; +use Illuminate\Http\JsonResponse; + +class CreatePostAction +{ + use AsAction; + + // Core business logic - reusable from anywhere + public function handle(CreatePostDTO $dto): Post + { + return Post::create($dto->all()); + } + + // HTTP entry point - acts as controller + public function asController(CreatePostRequest $request): JsonResponse + { + $post = $this->handle($request->toDTO()); + + return (new PostResource($post))->created(); + } +} +``` + +**Routes point directly to Actions:** + +```php +// routes/v1.php +Route::post('/posts', CreatePostAction::class); +Route::get('/posts', ListPostsAction::class); +Route::get('/posts/{post}', ViewPostAction::class); +Route::put('/posts/{post}', UpdatePostAction::class); +Route::delete('/posts/{post}', DeletePostAction::class); +``` + +### DTOs (Data Transfer Objects) + +**DTOs** are simple objects that carry data between layers. + +```php +// src/DTO/CreatePostDTO.php +namespace Modules\Blog\DTO; + +class CreatePostDTO +{ + public function __construct( + public readonly string $title, + public readonly string $content, + public readonly int $authorId, + ) {} + + public static function fromRequest(CreatePostRequest $request): self + { + return new self( + title: $request->validated('title'), + content: $request->validated('content'), + authorId: $request->user()->id, + ); + } +} +``` + +### Module Service Provider + +Each module has a service provider that extends `ModuleServiceProvider`: + +```php +// src/Providers/BlogServiceProvider.php +namespace Modules\Blog\Providers; + +use Laraneat\Modules\Support\ModuleServiceProvider; + +class BlogServiceProvider extends ModuleServiceProvider +{ + public function boot(): void + { + // Load module commands + $this->loadCommandsFrom([ + 'Modules\\Blog\\UI\\CLI\\Commands' => __DIR__ . '/../UI/CLI/Commands', + ]); + + // Load migrations + $this->loadMigrationsFrom(__DIR__ . '/../../database/migrations'); + + // Load views + $this->loadViewsFrom(__DIR__ . '/../../resources/views', 'blog'); + } +} +``` + +## Configuration + +After publishing, edit `config/modules.php`: + +```php +return [ + // Where modules are stored + 'path' => base_path('modules'), + + // Base namespace for all modules + 'namespace' => 'Modules', + + // Custom stubs location (optional) + 'custom_stubs' => base_path('stubs/modules'), + + // Composer settings for generated modules + 'composer' => [ + 'vendor' => 'app', + 'author' => [ + 'name' => 'Your Name', + 'email' => 'your@email.com', + ], + ], + + // Component path/namespace mappings + 'components' => [ + 'action' => [ + 'path' => 'src/Actions', + 'namespace' => 'Actions', + ], + 'model' => [ + 'path' => 'src/Models', + 'namespace' => 'Models', + ], + // ... more components + ], + + // Enable manifest caching (recommended for production) + 'cache' => [ + 'enabled' => env('APP_ENV') === 'production', + ], +]; +``` + +## Artisan Commands + +### Module Management + +| Command | Description | +|---------|-------------| +| `module:list` | Display all registered modules | +| `module:sync` | Refresh manifest and sync with Composer | +| `module:delete {package}` | Delete a module completely | +| `module:cache` | Build module manifest cache | +| `module:cache:clear` | Clear module manifest cache | + +### Module Creation + +```bash +# Create module with interactive preset selection +php artisan module:make Blog + +# Create with specific preset +php artisan module:make Blog --preset=api --entity=Post +``` + +### Component Generators + +| Command | Description | +|---------|-------------| +| `module:make:action` | Create an Action class | +| `module:make:controller` | Create a Controller (--ui=api\|web) | +| `module:make:model` | Create an Eloquent Model | +| `module:make:migration` | Create a database migration | +| `module:make:request` | Create a Form Request | +| `module:make:resource` | Create an API Resource | +| `module:make:dto` | Create a DTO class | +| `module:make:event` | Create an Event class | +| `module:make:listener` | Create an Event Listener | +| `module:make:job` | Create a Job class | +| `module:make:policy` | Create a Policy class | +| `module:make:provider` | Create a Service Provider | +| `module:make:middleware` | Create Middleware | +| `module:make:command` | Create a Console Command | +| `module:make:factory` | Create a Model Factory | +| `module:make:seeder` | Create a Database Seeder | +| `module:make:test` | Create a Test class | +| `module:make:observer` | Create a Model Observer | +| `module:make:notification` | Create a Notification | +| `module:make:mail` | Create a Mailable | +| `module:make:rule` | Create a Validation Rule | +| `module:make:query-wizard` | Create a QueryWizard | +| `module:make:route` | Create a Route file | +| `module:make:exception` | Create an Exception class | + +### Migration Commands + +| Command | Description | +|---------|-------------| +| `module:migrate` | Run module migrations | +| `module:migrate:rollback` | Rollback module migrations | +| `module:migrate:reset` | Reset all module migrations | +| `module:migrate:refresh` | Refresh module migrations | +| `module:migrate:status` | Show migration status | + +## Component Types + +The package supports 30+ component types organized by architectural layers: + +### UI Layer + +**API Components:** +- `ApiController` - REST API controllers +- `ApiRequest` - API form requests +- `ApiResource` - API JSON resources +- `ApiRoute` - API route files +- `ApiQueryWizard` - Query builder wrappers +- `ApiTest` - API integration tests + +**WEB Components:** +- `WebController` - Web controllers +- `WebRequest` - Web form requests +- `WebRoute` - Web route files +- `WebTest` - Web integration tests + +**CLI Components:** +- `CliCommand` - Artisan commands +- `CliTest` - Command tests + +### Domain Layer + +- `Action` - Business logic actions +- `Model` - Eloquent models +- `Dto` - Data Transfer Objects +- `Event` - Domain events +- `Listener` - Event listeners +- `Job` - Queued jobs +- `Rule` - Validation rules +- `Observer` - Model observers + +### Infrastructure Layer + +- `Provider` - Service providers +- `Middleware` - HTTP middleware +- `Policy` - Authorization policies +- `Mail` - Mailable classes +- `Notification` - Notifications + +### Database Layer + +- `Migration` - Database migrations +- `Seeder` - Database seeders +- `Factory` - Model factories + +## Module Presets + +### Plain Preset (Default) + +Basic module with minimal structure: +- Service providers only +- Empty directory structure + +```bash +php artisan module:make Blog --preset=plain +``` + +### Base Preset + +Includes database layer components: +- Model with migrations +- Factory for testing +- Seeder with permissions +- Authorization policy + +```bash +php artisan module:make Blog --preset=base --entity=Post +``` + +### API Preset + +Complete REST API module: +- All base preset components +- CRUD controllers +- Form requests (create, update, delete, list, view) +- API resources +- QueryWizard for filtering/sorting +- DTOs for data transfer +- Complete route file +- Full test coverage + +```bash +php artisan module:make Blog --preset=api --entity=Post +``` + +## Best Practices + +### 1. Keep Modules Independent + +Modules should be loosely coupled. If module A depends on module B, consider: +- Using events for communication +- Creating shared interfaces +- Moving shared code to a separate package + +### 2. Separate HTTP Logic from Business Logic + +Keep `asController()` thin - it should only handle HTTP concerns. Put business logic in `handle()`: + +```php +class CreatePostAction +{ + use AsAction; + + // Business logic - reusable, testable + public function handle(CreatePostDTO $dto): Post + { + $post = Post::create($dto->all()); + event(new PostCreated($post)); + + return $post; + } + + // HTTP concerns only - request/response handling + public function asController(CreatePostRequest $request): JsonResponse + { + $post = $this->handle($request->toDTO()); + + return (new PostResource($post))->created(); + } +} +``` + +### 3. Reuse Actions Across Contexts + +Actions can be called from multiple places: + +```php +// From another Action +class ImportPostsAction +{ + public function __construct(private CreatePostAction $createPost) {} + + public function handle(array $posts): void + { + foreach ($posts as $postData) { + $this->createPost->handle(new CreatePostDTO(...$postData)); + } + } +} + +// From a Job +class ProcessImportJob implements ShouldQueue +{ + public function handle(CreatePostAction $action): void + { + $action->handle($this->dto); + } +} + +// From a Command +class SeedPostsCommand extends Command +{ + public function handle(CreatePostAction $action): void + { + $action->handle(new CreatePostDTO(...)); + } +} +``` + +### 4. Use DTOs for Data Transfer + +DTOs provide type safety and clear contracts: + +```php +class CreatePostDTO +{ + public function __construct( + public readonly string $title, + public readonly string $content, + public readonly int $authorId, + ) {} + + public function all(): array + { + return [ + 'title' => $this->title, + 'content' => $this->content, + 'author_id' => $this->authorId, + ]; + } +} +``` + +### 5. Organize Routes by Version + +For APIs, version your routes: + +``` +routes/ +├── v1.php # Version 1 routes +└── v2.php # Version 2 routes +``` + +### 6. Write Tests + +Each module should have comprehensive tests: + +```bash +# Run all module tests +./vendor/bin/pest modules/blog/tests + +# Run specific test +./vendor/bin/pest --filter "can create post" +``` + +### 7. Use Caching in Production + +Enable manifest caching for better performance: + +```php +// config/modules.php +'cache' => [ + 'enabled' => env('APP_ENV') === 'production', +], +``` + +Run after deployment: +```bash +php artisan module:cache +``` + +## License + +MIT License. See [LICENSE](LICENSE) for details. diff --git a/composer.json b/composer.json index 76bcb02..2337c17 100644 --- a/composer.json +++ b/composer.json @@ -1,10 +1,10 @@ { "name": "laraneat/modules", "description": "Laraneat modules.", - "homepage": "https://github.com/laraneat/core/", + "homepage": "https://github.com/laraneat/modules/", "support": { - "issues": "https://github.com/laraneat/core/issues", - "source": "https://github.com/laraneat/core" + "issues": "https://github.com/laraneat/modules/issues", + "source": "https://github.com/laraneat/modules" }, "authors": [ { @@ -24,41 +24,46 @@ ], "license": "MIT", "require": { - "php": "^8.0", + "php": "^8.1", "ext-json": "*", - "laravel/framework": "^10.0" + "composer/composer": "^2.1", + "laravel/framework": "^10.0 || ^11.0 || ^12.0" }, "require-dev": { "friendsofphp/php-cs-fixer": "^3.48", - "mockery/mockery": "~1.0", - "orchestra/testbench": "^8.2", - "phpstan/phpstan": "^1.10", - "phpunit/phpunit": "^10.5", + "larastan/larastan": "^2.0 || ^3.0", + "mockery/mockery": "^1.0", + "orchestra/testbench": "^8.0 || ^9.0 || ^10.0", + "pestphp/pest": "^2.31 || ^3.0", + "pestphp/pest-plugin-laravel": "^2.0 || ^3.0", + "phpstan/phpstan": "^1.10.58 || ^2.0", "roave/security-advisories": "dev-latest", - "spatie/phpunit-snapshot-assertions": "^5.1" + "spatie/pest-plugin-snapshots": "^2.1" }, "autoload": { "psr-4": { "Laraneat\\Modules\\": "src" - }, - "files": [ - "src/helpers.php" - ] + } }, "autoload-dev": { "psr-4": { "Laraneat\\Modules\\Tests\\": "tests", - "App\\": "tests/laravel/app", - "App\\Modules\\Article\\": "tests/fixtures/stubs/valid/Article" + "App\\": "tests/fixtures/laravel/app", + "Modules\\": "tests/fixtures/laravel/modules" } }, "suggest": { - "wikimedia/composer-merge-plugin": "Allows the ability to create and merge composer.json files for your individual modules for module-specific dependency management." + "lorisleiva/laravel-actions": "Laravel components that take care of one specific task", + "jackardios/laravel-query-wizard": "Easily build Eloquent queries from API requests", + "spatie/laravel-data": "Powerful data objects for Laravel" }, "extra": { "laravel": { "providers": [ - "Laraneat\\Modules\\ModulesServiceProvider" + "Laraneat\\Modules\\Providers\\ComposerServiceProvider", + "Laraneat\\Modules\\Providers\\ConsoleServiceProvider", + "Laraneat\\Modules\\Providers\\ModulesRepositoryServiceProvider", + "Laraneat\\Modules\\Providers\\ModulesServiceProvider" ], "aliases": { "Modules": "Laraneat\\Modules\\Facades\\Modules" @@ -66,17 +71,16 @@ } }, "scripts": { - "update-snapshots": "./vendor/bin/phpunit --no-coverage -d --update-snapshots", - "test": "vendor/bin/phpunit", - "test-coverage": "vendor/bin/phpunit --debug --coverage-html coverage", - "pcf": "vendor/bin/php-cs-fixer fix --verbose", + "analyse" : "./vendor/bin/phpstan analyse --memory-limit=256M", + "test" : "./vendor/bin/pest --no-coverage", + "test-coverage" : "./vendor/bin/pest --coverage-html coverage", + "update-snapshots": "./vendor/bin/pest --no-coverage --update-snapshots", + "format": "./vendor/bin/php-cs-fixer fix --allow-risky=yes", "post-autoload-dump": [ "@php ./vendor/bin/testbench package:discover --ansi" ] }, - "config": { - "optimize-autoloader": true, - "preferred-install": "dist", + "config" : { "sort-packages": true, "allow-plugins": { "pestphp/pest-plugin": true diff --git a/config/config.php b/config/config.php index 653752f..7848b1c 100755 --- a/config/config.php +++ b/config/config.php @@ -1,301 +1,229 @@ [ - /* - |-------------------------------------------------------------------------- - | The path to assets - |-------------------------------------------------------------------------- - | - | This path is used to store public assets of modules - | - */ - 'assets' => public_path('modules'), - ], - - 'generator' => [ - /* - |-------------------------------------------------------------------------- - | Modules path - |-------------------------------------------------------------------------- - | - | This path used for save the generated module. This path also will be added - | automatically to list of scanned folders. - | - */ - 'path' => base_path('app/Modules'), - - /* - |-------------------------------------------------------------------------- - | Default Module Namespace - |-------------------------------------------------------------------------- - | - | Default module namespace. - | - */ - 'namespace' => 'App\\Modules', - - /* - |-------------------------------------------------------------------------- - | Custom generator stubs path - |-------------------------------------------------------------------------- - | - | Place your custom stubs in this folder - | - */ - 'custom_stubs' => base_path('app/Ship/Generators/custom-stubs'), - - /* - |-------------------------------------------------------------------------- - | User model class - |-------------------------------------------------------------------------- - | - | Customize the User model of the application - | - */ - 'user_model' => null, - - /* - |-------------------------------------------------------------------------- - | "Create permission" classes - |-------------------------------------------------------------------------- - | - | Customize "create permission" classes - | - */ - 'create_permission' => [ - 'action' => null, - 'dto' => null - ], + /* + |-------------------------------------------------------------------------- + | Modules path + |-------------------------------------------------------------------------- + | + | This path used for scan and save the generated module. + | + */ + 'path' => base_path('modules'), - /* - |-------------------------------------------------------------------------- - | Component paths - |-------------------------------------------------------------------------- - | - | Customize the paths where the folders will be generated. - | Set the generate key to `false` to not generate that folder when creating - | a module - | - */ - 'components' => [ - 'action' => [ - 'path' => 'Actions', - 'generate' => true - ], - 'api-controller' => [ - 'path' => 'UI/API/Controllers', - 'generate' => false - ], - 'api-query-wizard' => [ - 'path' => 'UI/API/QueryWizards', - 'generate' => true - ], - 'api-request' => [ - 'path' => 'UI/API/Requests', - 'generate' => true - ], - 'api-resource' => [ - 'path' => 'UI/API/Resources', - 'generate' => true - ], - 'api-route' => [ - 'path' => 'UI/API/Routes', - 'generate' => true - ], - 'api-test' => [ - 'path' => 'UI/API/Tests', - 'generate' => true - ], - 'cli-command' => [ - 'path' => 'UI/CLI/Commands', - 'generate' => false - ], - 'cli-test' => [ - 'path' => 'UI/CLI/Tests', - 'generate' => false - ], - 'dto' => [ - 'path' => 'DTO', - 'generate' => true - ], - 'event' => [ - 'path' => 'Events', - 'generate' => false - ], - 'exception' => [ - 'path' => 'Exceptions', - 'generate' => false - ], - 'factory' => [ - 'path' => 'Data/Factories', - 'generate' => true - ], - 'feature-test' => [ - 'path' => 'Tests/Feature', - 'generate' => false - ], - 'job' => [ - 'path' => 'Jobs', - 'generate' => false - ], - 'lang' => [ - 'path' => 'Resources/lang', - 'generate' => false - ], - 'listener' => [ - 'path' => 'Listeners', - 'generate' => false - ], - 'mail' => [ - 'path' => 'Mails', - 'generate' => false - ], - 'middleware' => [ - 'path' => 'Middleware', - 'generate' => false - ], - 'migration' => [ - 'path' => 'Data/Migrations', - 'generate' => true - ], - 'model' => [ - 'path' => 'Models', - 'generate' => true - ], - 'notification' => [ - 'path' => 'Notifications', - 'generate' => false - ], - 'observer' => [ - 'path' => 'Observers', - 'generate' => false - ], - 'policy' => [ - 'path' => 'Policies', - 'generate' => true - ], - 'provider' => [ - 'path' => 'Providers', - 'generate' => true - ], - 'rule' => [ - 'path' => 'Rules', - 'generate' => false - ], - 'seeder' => [ - 'path' => 'Data/Seeders', - 'generate' => true - ], - 'web-controller' => [ - 'path' => 'UI/WEB/Controllers', - 'generate' => false - ], - 'web-request' => [ - 'path' => 'UI/WEB/Requests', - 'generate' => false, - ], - 'web-route' => [ - 'path' => 'UI/WEB/Routes', - 'generate' => false - ], - 'web-test' => [ - 'path' => 'UI/WEB/Tests', - 'generate' => false - ], - 'view' => [ - 'path' => 'Resources/views', - 'generate' => false - ], - 'unit-test' => [ - 'path' => 'Tests/Unit', - 'generate' => false - ], - ], - ], + /* + |-------------------------------------------------------------------------- + | Module namespace prefix + |-------------------------------------------------------------------------- + | + | Prefix for the namespace of the generated modules + | + */ + 'namespace' => 'Modules', /* |-------------------------------------------------------------------------- - | Scan Path + | Custom generator stubs path |-------------------------------------------------------------------------- | - | Here you define which folder will be scanned. By default will scan vendor - | directory. This is useful if you host the package in packagist website. + | Place your custom stubs in this folder | */ - 'scan' => [ - 'enabled' => false, - 'paths' => [ - base_path('vendor/*/*'), - ], - ], + 'custom_stubs' => base_path('stubs/modules'), /* |-------------------------------------------------------------------------- | Composer File Template |-------------------------------------------------------------------------- | - | Here is the config for composer.json file, generated by this package + | Configuration composer.json of generated modules | */ 'composer' => [ - 'vendor' => 'example', + 'vendor' => 'app', 'author' => [ - 'name' => 'Example name', - 'email' => 'example@example.com', + 'name' => 'Example', + 'email' => 'example@example.com' ], - 'composer-output' => false, ], /* |-------------------------------------------------------------------------- - | Caching + | User model class |-------------------------------------------------------------------------- | - | Here is the config for setting up caching feature for scanned modules. + | Customize the User model of the application | */ - 'cache' => [ - 'enabled' => env('APP_ENV', 'production') === 'production', - 'key' => 'laraneat.modules', - 'lifetime' => null, // store cache indefinitely - ], + 'user_model' => null, /* |-------------------------------------------------------------------------- - | Choose what laraneat/modules will register as custom namespaces. - | Setting one to false will require you to register that part - | in your own Service Provider class. + | "Create permission" classes |-------------------------------------------------------------------------- + | + | Customize "create permission" classes + | */ - 'register' => [ - /** - * load files on boot or register method - * - * @example boot|register - */ - 'files' => 'register', + 'create_permission' => [ + 'action' => null, + 'dto' => null ], /* |-------------------------------------------------------------------------- - | Activators + | Component paths |-------------------------------------------------------------------------- | - | You can define new types of activators here, file, database etc. The only - | required parameter is 'class'. - | The file activator will store the activation status in storage/installed_modules + | Customize the paths where the folders will be generated. + | */ - 'activators' => [ - 'file' => [ - 'class' => FileActivator::class, - 'statuses-file' => base_path('modules_statuses.json'), - 'cache-key' => 'laraneat.activator.installed', - 'cache-lifetime' => null, // store cache indefinitely + 'components' => [ + ModuleComponentType::Action->value => [ + 'path' => 'src/Actions', + 'namespace' => 'Actions' + ], + ModuleComponentType::ApiController->value => [ + 'path' => 'src/UI/API/Controllers', + 'namespace' => 'UI\\API\\Controllers' + ], + ModuleComponentType::ApiQueryWizard->value => [ + 'path' => 'src/UI/API/QueryWizards', + 'namespace' => 'UI\\API\\QueryWizards' + ], + ModuleComponentType::ApiRequest->value => [ + 'path' => 'src/UI/API/Requests', + 'namespace' => 'UI\\API\\Requests' + ], + ModuleComponentType::ApiResource->value => [ + 'path' => 'src/UI/API/Resources', + 'namespace' => 'UI\\API\\Resources' + ], + ModuleComponentType::ApiRoute->value => [ + 'path' => 'src/UI/API/routes', + 'namespace' => 'UI\\API\\Routes' + ], + ModuleComponentType::ApiTest->value => [ + 'path' => 'tests/UI/API', + 'namespace' => 'Tests\\UI\\API' + ], + ModuleComponentType::CliCommand->value => [ + 'path' => 'src/UI/CLI/Commands', + 'namespace' => 'UI\\CLI\\Commands' + ], + ModuleComponentType::CliTest->value => [ + 'path' => 'tests/UI/CLI', + 'namespace' => 'Tests\\UI\\CLI' + ], + ModuleComponentType::Config->value => [ + 'path' => 'config' + ], + ModuleComponentType::Dto->value => [ + 'path' => 'src/DTO', + 'namespace' => 'DTO' + ], + ModuleComponentType::Event->value => [ + 'path' => 'src/Events', + 'namespace' => 'Events' + ], + ModuleComponentType::Exception->value => [ + 'path' => 'src/Exceptions', + 'namespace' => 'Exceptions' + ], + ModuleComponentType::Factory->value => [ + 'path' => 'database/factories', + 'namespace' => 'Database\\Factories' + ], + ModuleComponentType::FeatureTest->value => [ + 'path' => 'tests/Feature', + 'namespace' => 'Tests\\Feature' + ], + ModuleComponentType::Job->value => [ + 'path' => 'src/Jobs', + 'namespace' => 'Jobs' + ], + ModuleComponentType::Lang->value => [ + 'path' => 'lang' + ], + ModuleComponentType::Listener->value => [ + 'path' => 'src/Listeners', + 'namespace' => 'Listeners' + ], + ModuleComponentType::Mail->value => [ + 'path' => 'src/Mails', + 'namespace' => 'Mails' + ], + ModuleComponentType::Middleware->value => [ + 'path' => 'src/Middleware', + 'namespace' => 'Middleware' + ], + ModuleComponentType::Migration->value => [ + 'path' => 'database/migrations' + ], + ModuleComponentType::Model->value => [ + 'path' => 'src/Models', + 'namespace' => 'Models' + ], + ModuleComponentType::Notification->value => [ + 'path' => 'src/Notifications', + 'namespace' => 'Notifications' + ], + ModuleComponentType::Observer->value => [ + 'path' => 'src/Observers', + 'namespace' => 'Observers' + ], + ModuleComponentType::Policy->value => [ + 'path' => 'src/Policies', + 'namespace' => 'Policies' + ], + ModuleComponentType::Provider->value => [ + 'path' => 'src/Providers', + 'namespace' => 'Providers' + ], + ModuleComponentType::Rule->value => [ + 'path' => 'src/Rules', + 'namespace' => 'Rules' + ], + ModuleComponentType::Seeder->value => [ + 'path' => 'database/seeders', + 'namespace' => 'Database\\Seeders' + ], + ModuleComponentType::UnitTest->value => [ + 'path' => 'tests/Unit', + 'namespace' => 'Tests\\Unit' + ], + ModuleComponentType::View->value => [ + 'path' => 'resources/views' + ], + ModuleComponentType::WebController->value => [ + 'path' => 'src/UI/WEB/Controllers', + 'namespace' => 'UI\\WEB\\Controllers' + ], + ModuleComponentType::WebRequest->value => [ + 'path' => 'src/UI/WEB/Requests', + 'namespace' => 'UI\\WEB\\Requests' + ], + ModuleComponentType::WebRoute->value => [ + 'path' => 'src/UI/WEB/routes', + 'namespace' => 'UI\\WEB\\Routes' + ], + ModuleComponentType::WebTest->value => [ + 'path' => 'tests/UI/WEB', + 'namespace' => 'Tests\\UI\\WEB' ], ], - 'activator' => 'file', + /* + |-------------------------------------------------------------------------- + | Caching + |-------------------------------------------------------------------------- + | + | Here is the config for setting up caching feature for scanned modules. + | + */ + 'cache' => [ + 'enabled' => env('APP_ENV', 'production') === 'production', + ], ]; diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon new file mode 100644 index 0000000..4a797fd --- /dev/null +++ b/phpstan-baseline.neon @@ -0,0 +1,3 @@ +parameters: + ignoreErrors: + - "#Unsafe usage of new static#" diff --git a/phpstan.neon.dist b/phpstan.neon.dist new file mode 100644 index 0000000..db492d2 --- /dev/null +++ b/phpstan.neon.dist @@ -0,0 +1,22 @@ +includes: + - phpstan-baseline.neon + - vendor/larastan/larastan/extension.neon + +parameters: + level: 5 + paths: + - src + tmpDir: build/phpstan + reportUnmatchedIgnoredErrors: true + checkOctaneCompatibility: true + checkModelProperties: true + ignoreErrors: + # These traits are used in stubs for generated code + - identifier: trait.unused + path: src/Support/Concerns/CanLoadRoutesFromDirectory.php + - identifier: trait.unused + path: src/Support/Concerns/CanRunModuleSeeders.php + - identifier: trait.unused + path: src/Support/Concerns/InteractsWithTestUser.php + - identifier: trait.unused + path: src/Support/Concerns/WithJsonResponseHelpers.php diff --git a/phpunit.xml b/phpunit.xml new file mode 100644 index 0000000..417198b --- /dev/null +++ b/phpunit.xml @@ -0,0 +1,17 @@ + + + + + ./tests + + + + + ./src + + + diff --git a/phpunit.xml.dist b/phpunit.xml.dist deleted file mode 100644 index 9912507..0000000 --- a/phpunit.xml.dist +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - - - - - - tests - - - - - - - - src/ - - - diff --git a/src/Activators/FileActivator.php b/src/Activators/FileActivator.php deleted file mode 100755 index 0837e8b..0000000 --- a/src/Activators/FileActivator.php +++ /dev/null @@ -1,196 +0,0 @@ -cache = $app['cache']; - $this->files = $app['files']; - $this->config = $app['config']; - $this->statusesFile = $this->config('statuses-file'); - $this->cacheKey = $this->config('cache-key'); - $this->cacheLifetime = $this->config('cache-lifetime'); - $this->modulesStatuses = $this->getModulesStatuses(); - } - - /** - * Get the path of the file where statuses are stored - */ - public function getStatusesFilePath(): string - { - return $this->statusesFile; - } - - /** - * @inheritDoc - * @see ActivatorInterface::reset() - */ - public function reset(): void - { - if ($this->files->exists($this->statusesFile)) { - $this->files->delete($this->statusesFile); - } - $this->modulesStatuses = []; - $this->flushCache(); - } - - /** - * @inheritDoc - * @see ActivatorInterface::enable() - */ - public function enable(Module $module): void - { - $this->setActiveByName($module->getName(), true); - } - - /** - * @inheritDoc - * @see ActivatorInterface::disable() - */ - public function disable(Module $module): void - { - $this->setActiveByName($module->getName(), false); - } - - /** - * @inheritDoc - * @see ActivatorInterface::hasStatus() - */ - public function hasStatus(Module $module, bool $status): bool - { - if (!isset($this->modulesStatuses[$module->getName()])) { - return $status === false; - } - - return $this->modulesStatuses[$module->getName()] === $status; - } - - /** - * @inheritDoc - * @see ActivatorInterface::setActive() - */ - public function setActive(Module $module, bool $active): void - { - $this->setActiveByName($module->getName(), $active); - } - - /** - * @inheritDoc - * @see ActivatorInterface::setActiveByName() - */ - public function setActiveByName(string $moduleName, bool $active): void - { - $this->modulesStatuses[$moduleName] = $active; - $this->writeJson(); - $this->flushCache(); - } - - /** - * @inheritDoc - * @see ActivatorInterface::delete() - */ - public function delete(Module $module): void - { - if (!isset($this->modulesStatuses[$module->getName()])) { - return; - } - unset($this->modulesStatuses[$module->getName()]); - $this->writeJson(); - $this->flushCache(); - } - - /** - * Writes the activation statuses in a file, as json - */ - protected function writeJson(): void - { - $this->files->put($this->statusesFile, json_encode($this->modulesStatuses, JSON_PRETTY_PRINT)); - } - - /** - * Reads the json file that contains the activation statuses. - */ - protected function readJson(): array - { - if (!$this->files->exists($this->statusesFile)) { - return []; - } - - return json_decode($this->files->get($this->statusesFile), true); - } - - /** - * Get modules statuses, either from the cache or from - * the json statuses file if the cache is disabled. - */ - protected function getModulesStatuses(): array - { - if (!$this->config->get('modules.cache.enabled')) { - return $this->readJson(); - } - - return $this->cache->remember($this->cacheKey, $this->cacheLifetime, function () { - return $this->readJson(); - }); - } - - /** - * Reads a config parameter under the 'activators.file' key - */ - protected function config(string $key, $default = null) - { - return $this->config->get('modules.activators.file.' . $key, $default); - } - - /** - * Flush the modules activation statuses cache - */ - public function flushCache(): void - { - $this->cache->forget($this->cacheKey); - } -} diff --git a/src/Commands/BaseCommand.php b/src/Commands/BaseCommand.php new file mode 100644 index 0000000..5bb23b9 --- /dev/null +++ b/src/Commands/BaseCommand.php @@ -0,0 +1,187 @@ +|Module + * + * @throws ModuleNotFound + * @throws ModuleHasNonUniquePackageName + * @throws ModuleHasNoNamespace + */ + protected function getModuleArgumentOrFail(): array|Module + { + $allPackageNames = array_keys($this->modulesRepository->getModules()); + $moduleArgument = $this->input->getArgument('module'); + $multipleModuleMode = is_array($moduleArgument); + + if ($multipleModuleMode) { + $moduleArgument = array_values(array_unique($moduleArgument)); + + if (empty($moduleArgument)) { + $moduleArgument = multiselect( + label: 'Select one or more module', + options: [ + 'all' => 'All modules', + ...array_combine($allPackageNames, $allPackageNames), + ], + required: 'You must select at least one module', + ); + } + + if (in_array('all', $moduleArgument, true)) { + $moduleArgument = $allPackageNames; + } + } else { + if (empty($moduleArgument)) { + $moduleArgument = select( + label: 'Select one module', + options: $allPackageNames, + required: 'You must select a module', + ); + } + } + + $this->input->setArgument('module', value: $moduleArgument); + + if (! $multipleModuleMode) { + return $this->findModuleByNameOrPackageNameOrFail($moduleArgument); + } + + return collect($moduleArgument) + ->map(fn (string $moduleNameOrPackageName) + => $this->findModuleByNameOrPackageNameOrFail($moduleNameOrPackageName)) + ->unique(fn (Module $module) => $module->getPackageName()) + ->values() + ->all(); + } + + /** + * @throws ModuleNotFound + * @throws ModuleHasNonUniquePackageName + * @throws ModuleHasNoNamespace + */ + protected function findModuleByNameOrPackageNameOrFail($moduleNameOrPackageName): Module + { + if ($foundModule = $this->modulesRepository->find($moduleNameOrPackageName)) { + return $foundModule; + } + + $foundModules = collect($this->modulesRepository->filterByName($moduleNameOrPackageName)); + $numberOfFoundModules = $foundModules->count(); + + if ($numberOfFoundModules === 0) { + throw ModuleNotFound::makeForNameOrPackageName($moduleNameOrPackageName); + } + + if ($numberOfFoundModules === 1) { + return $foundModules->first(); + } + + $selectedPackageName = $this->choice( + "$numberOfFoundModules modules with name '{$moduleNameOrPackageName}' found, please select one module from those found", + $foundModules->keys()->all(), + ); + + + return $this->modulesRepository->findOrFail($selectedPackageName); + } + + /** + * Checks if the option is set (via CLI), otherwise asks the user for a value + * + * @throws InvalidOptionException + */ + protected function getOptionOrAsk( + string $optionName, + string $question, + ?string $default = null, + bool $required = true + ): ?string { + $value = $this->option($optionName); + + if ($value === '' || $value === null) { + $value = trim($this->ask($question, $default)); + } + + if ($required && $value === '') { + throw new InvalidOptionException( + sprintf("The '%s' option is required", $optionName) + ); + } + + return $value; + } + + /** + * Checks if the option is set (via CLI), otherwise proposes choices to the user + * + * @throws InvalidOptionException + */ + protected function getOptionOrChoice( + string $optionName, + string $question, + array $choices, + ?string $default = null + ): ?string { + $value = $this->option($optionName); + + if ($value === '' || $value === null) { + $value = $this->choice($question, $choices, $default); + } elseif (! in_array($value, $choices, true)) { + throw new InvalidOptionException( + sprintf( + "Wrong '%s' option value provided. Value should be one of '%s'.", + $optionName, + implode("' or '", $choices) + ) + ); + } + + return $value; + } + + /** + * Checks if the option is set (via CLI) + * + * @throws InvalidOptionException + */ + protected function getOptionOneOf( + string $optionName, + array $choices, + ?string $default = null + ): ?string { + $value = $this->option($optionName) ?: $default; + + if (! in_array($value, $choices, true)) { + throw new InvalidOptionException( + sprintf( + "Wrong '%s' option value provided. Value should be one of '%s'.", + $optionName, + implode("' or '", $choices) + ) + ); + } + + return $value; + } +} diff --git a/src/Commands/BaseMigrationCommand.php b/src/Commands/BaseMigrationCommand.php new file mode 100644 index 0000000..6d5fba3 --- /dev/null +++ b/src/Commands/BaseMigrationCommand.php @@ -0,0 +1,71 @@ +requiresConfirmation && ! $this->confirmToProceed()) { + return self::FAILURE; + } + + try { + $modulesToHandle = $this->getModuleArgumentOrFail(); + } catch (ModuleNotFound|ModuleHasNonUniquePackageName|ModuleHasNoNamespace $exception) { + $this->error($exception->getMessage()); + + return self::FAILURE; + } + + foreach ($modulesToHandle as $module) { + $this->line('Running for module: ' . $module->getPackageName() . ''); + $this->executeForModule($module); + } + + return self::SUCCESS; + } + + /** + * Execute the migration operation for a single module. + */ + abstract protected function executeForModule(Module $module): void; + + /** + * Get migration paths for a module. + * + * @return array + */ + protected function getMigrationPaths(Module $module): array + { + /** @var Migrator|null $migrator */ + $migrator = $this->laravel['migrator'] ?? null; + + if ($migrator === null) { + return []; + } + + return collect($migrator->paths()) + ->filter(fn (string $path) => Str::startsWith($path, $module->getPath())) + ->values() + ->toArray(); + } +} diff --git a/src/Commands/CacheClearCommand.php b/src/Commands/CacheClearCommand.php index ff06446..641ecff 100644 --- a/src/Commands/CacheClearCommand.php +++ b/src/Commands/CacheClearCommand.php @@ -2,17 +2,14 @@ namespace Laraneat\Modules\Commands; -use Illuminate\Console\Command; -use Laraneat\Modules\Facades\Modules; - -class CacheClearCommand extends Command +class CacheClearCommand extends BaseCommand { /** - * The console command name. + * The name and signature of the console command. * * @var string */ - protected $name = 'module:clear'; + protected $signature = 'module:clear'; /** * The console command description. @@ -26,8 +23,8 @@ class CacheClearCommand extends Command */ public function handle(): int { - Modules::flushCache(); - $this->info("Modules cache cleared!"); + $this->modulesRepository->pruneModulesManifest(); + $this->components->info("Modules manifest cache cleared!"); return self::SUCCESS; } diff --git a/src/Commands/CacheCommand.php b/src/Commands/CacheCommand.php index cd74be2..f065224 100644 --- a/src/Commands/CacheCommand.php +++ b/src/Commands/CacheCommand.php @@ -2,34 +2,29 @@ namespace Laraneat\Modules\Commands; -use Illuminate\Console\Command; -use Laraneat\Modules\Facades\Modules; - -class CacheCommand extends Command +class CacheCommand extends BaseCommand { /** - * The console command name. + * The name and signature of the console command. * * @var string */ - protected $name = 'module:cache'; + protected $signature = 'module:cache'; /** * The console command description. * * @var string */ - protected $description = 'Create a modules cache.'; + protected $description = 'Caches modules.'; /** * Execute the console command. */ public function handle(): int { - $this->call('module:clear'); - - Modules::getCached(); - $this->info("Modules cached successfully!"); + $this->modulesRepository->buildModulesManifest(); + $this->components->info("Modules manifest cached!"); return self::SUCCESS; } diff --git a/src/Commands/DisableCommand.php b/src/Commands/DisableCommand.php deleted file mode 100755 index d1a96b3..0000000 --- a/src/Commands/DisableCommand.php +++ /dev/null @@ -1,81 +0,0 @@ -argument('module') === null) { - $this->disableAll(); - } - - $module = Modules::findOrFail($this->argument('module')); - - if ($module->isEnabled()) { - $module->disable(); - - $this->info("Module [{$module}] disabled successful."); - } else { - $this->comment("Module [{$module}] has already disabled."); - } - - return self::SUCCESS; - } - - /** - * disableAll - * - * @return void - */ - public function disableAll(): void - { - $modules = Modules::all(); - - foreach ($modules as $module) { - if ($module->isEnabled()) { - $module->disable(); - - $this->info("Module [{$module}] disabled successful."); - } else { - $this->comment("Module [{$module}] has already disabled."); - } - } - } - - /** - * Get the console command arguments. - * - * @return array - */ - protected function getArguments(): array - { - return [ - ['module', InputArgument::OPTIONAL, 'Module name.'], - ]; - } -} diff --git a/src/Commands/DumpCommand.php b/src/Commands/DumpCommand.php deleted file mode 100755 index 79880b3..0000000 --- a/src/Commands/DumpCommand.php +++ /dev/null @@ -1,65 +0,0 @@ -info('Generating optimized autoload modules.'); - - if ($module = $this->argument('module')) { - $this->dump($module); - } else { - foreach (Modules::all() as $module) { - $this->dump($module->getStudlyName()); - } - } - - return self::SUCCESS; - } - - public function dump(string $moduleName): void - { - $module = Modules::findOrFail($moduleName); - - $this->line("Running for module: {$moduleName}"); - - chdir($module->getPath()); - - passthru('composer dump -o -n -q'); - } - - /** - * Get the console command arguments. - * - * @return array - */ - protected function getArguments(): array - { - return [ - ['module', InputArgument::OPTIONAL, 'Module name.'], - ]; - } -} diff --git a/src/Commands/EnableCommand.php b/src/Commands/EnableCommand.php deleted file mode 100755 index 049f763..0000000 --- a/src/Commands/EnableCommand.php +++ /dev/null @@ -1,83 +0,0 @@ -argument('module') === null) { - $this->enableAll(); - - return self::SUCCESS; - } - - $module = Modules::findOrFail($this->argument('module')); - - if ($module->isDisabled()) { - $module->enable(); - - $this->info("Module [{$module}] enabled successful."); - } else { - $this->comment("Module [{$module}] has already enabled."); - } - - return self::SUCCESS; - } - - /** - * enableAll - * - * @return void - */ - public function enableAll() - { - $modules = Modules::all(); - - foreach ($modules as $module) { - if ($module->isDisabled()) { - $module->enable(); - - $this->info("Module [{$module}] enabled successful."); - } else { - $this->comment("Module [{$module}] has already enabled."); - } - } - } - - /** - * Get the console command arguments. - * - * @return array - */ - protected function getArguments(): array - { - return [ - ['module', InputArgument::OPTIONAL, 'Module name.'], - ]; - } -} diff --git a/src/Commands/Generators/ActionMakeCommand.php b/src/Commands/Generators/ActionMakeCommand.php index b0b555e..94eb3b6 100644 --- a/src/Commands/Generators/ActionMakeCommand.php +++ b/src/Commands/Generators/ActionMakeCommand.php @@ -2,158 +2,143 @@ namespace Laraneat\Modules\Commands\Generators; +use Illuminate\Contracts\Console\PromptsForMissingInput; use Illuminate\Support\Str; -use Laraneat\Modules\Module; -use Laraneat\Modules\Support\Stub; -use Laraneat\Modules\Traits\ModuleCommandTrait; -use Symfony\Component\Console\Input\InputOption; +use Laraneat\Modules\Enums\ModuleComponentType; +use Laraneat\Modules\Support\Generator\Stub; /** * @group generator */ -class ActionMakeCommand extends ComponentGeneratorCommand +class ActionMakeCommand extends BaseComponentGeneratorCommand implements PromptsForMissingInput { - use ModuleCommandTrait; - /** * The name and signature of the console command. * * @var string */ - protected $name = 'module:make:action'; + protected $signature = 'module:make:action + {name : The name of the action class} + {module? : The name or package name of the app module} + {--s|stub= : The stub name to load for this generator} + {--dto= : The class name of the DTO to be used in the action} + {--model= : The class name of the model to be used in the action} + {--request= : The class name of the request to be used in the action} + {--resource= : The class name of the resource to be used in the action} + {--wizard= : The class name of the query wizard to be used in the action} + {--force : Overwrite the file if it already exists}'; /** * The console command description. * * @var string */ - protected $description = 'Generate new action for the specified module.'; - - /** - * The stub name to load for this generator. - * - * @var string - */ - protected string $stub = 'plain'; + protected $description = 'Generate new action class for the specified module.'; /** - * Module instance. - * - * @var Module + * The module component type. */ - protected Module $module; + protected ModuleComponentType $componentType = ModuleComponentType::Action; /** - * Component type. - * - * @var string + * Prompt for missing input arguments using the returned questions. */ - protected string $componentType; - - /** - * Prepared 'name' argument. - * - * @var string - */ - protected string $nameArgument; - - /** - * Get the console command options. - * - * @return array - */ - protected function getOptions(): array + protected function promptForMissingArgumentsUsing(): array { return [ - ['stub', 's', InputOption::VALUE_REQUIRED, 'The stub name to load for this generator.'], - ['dto', null, InputOption::VALUE_REQUIRED, 'The class name of the DTO to be used in the action.'], - ['model', null, InputOption::VALUE_REQUIRED, 'The class name of the model to be used in the action.'], - ['request', null, InputOption::VALUE_REQUIRED, 'The class name of the request to be used in the action.'], - ['resource', null, InputOption::VALUE_REQUIRED, 'The class name of the resource to be used in the action.'], - ['wizard', null, InputOption::VALUE_REQUIRED, 'The class name of the wizard to be used in the action.'], + 'name' => 'Enter the action class name', ]; } - protected function prepare() + protected function beforeGenerate(): void + { + $this->ensurePackageIsInstalledOrWarn('lorisleiva/laravel-actions'); + } + + protected function getContents(): string { - $this->module = $this->getModule(); - $this->stub = $this->getOptionOrChoice( + $stub = $this->getOptionOrChoice( 'stub', 'Select the stub you want to use for generator', ['plain', 'create', 'delete', 'list', 'update', 'view'], 'plain' ); - $this->componentType = 'action'; - $this->nameArgument = $this->getTrimmedArgument('name'); - } - protected function getDestinationFilePath(): string - { - return $this->getComponentPath($this->module, $this->nameArgument, $this->componentType); + return Stub::create("action/$stub.stub", $this->getStubReplaces($stub))->render(); } - protected function getTemplateContents(): string + /** + * @param string $stub + * @return array + */ + protected function getStubReplaces(string $stub): array { $stubReplaces = [ - 'namespace' => $this->getComponentNamespace($this->module, $this->nameArgument, $this->componentType), - 'class' => $this->getClass($this->nameArgument) + 'namespace' => $this->getComponentNamespace( + $this->module, + $this->nameArgument, + $this->componentType + ), + 'class' => class_basename($this->nameArgument), ]; - if ($this->stub !== 'plain') { - if ($this->stub === 'create' || $this->stub === 'update') { - $dto = $this->getOptionOrAsk( - 'dto', - 'Enter the class name of the DTO to be used in the request', - '', - true - ); - $stubReplaces['dto'] = $this->getClass($dto); - $stubReplaces['dtoEntity'] = Str::camel($stubReplaces['dto']); - $stubReplaces['dtoNamespace'] = $this->getComponentNamespace($this->module, $dto, 'dto'); - } - - if ($this->stub !== 'delete') { - $resource = $this->getOptionOrAsk( - 'resource', - 'Enter the class name of the resource to be used in the action', - '', - true - ); - $stubReplaces['resource'] = $this->getClass($resource); - $stubReplaces['resourceNamespace'] = $this->getComponentNamespace($this->module, $resource, 'api-resource'); - - if (in_array($this->stub, ['list', 'view'], true)) { - $wizard = $this->getOptionOrAsk( - 'wizard', - 'Enter the class name of the wizard to be used in the action', - '', - true - ); - $stubReplaces['queryWizard'] = $this->getClass($wizard); - $stubReplaces['queryWizardNamespace'] = $this->getComponentNamespace($this->module, $wizard, 'api-query-wizard'); - } - } + if ($stub === 'plain') { + return $stubReplaces; + } - $model = $this->getOptionOrAsk( - 'model', - 'Enter the class name of the model to be used in the action', - '', - true + if ($stub === 'create' || $stub === 'update') { + $dtoClass = $this->getFullClassFromOptionOrAsk( + optionName: 'dto', + question: 'Enter the class name of the DTO to be used in the action', + componentType: ModuleComponentType::Dto, + module: $this->module ); - $stubReplaces['model'] = $this->getClass($model); - $stubReplaces['modelEntity'] = Str::camel($stubReplaces['model']); - $stubReplaces['modelNamespace'] = $this->getComponentNamespace($this->module, $model, 'model'); - - $request = $this->getOptionOrAsk( - 'request', - 'Enter the class name of the request to be used in the action', - '', - true + $stubReplaces['dto'] = class_basename($dtoClass); + $stubReplaces['dtoCamelCase'] = Str::camel($stubReplaces['dto']); + $stubReplaces['dtoNamespace'] = $this->getNamespaceOfClass($dtoClass); + } + + if ($stub !== 'delete') { + $resourceClass = $this->getFullClassFromOptionOrAsk( + optionName: 'resource', + question: 'Enter the class name of the resource to be used in the action', + componentType: ModuleComponentType::ApiResource, + module: $this->module ); - $stubReplaces['request'] = $this->getClass($request); - $stubReplaces['requestNamespace'] = $this->getComponentNamespace($this->module, $request, 'api-request'); + $stubReplaces['resource'] = class_basename($resourceClass); + $stubReplaces['resourceNamespace'] = $this->getNamespaceOfClass($resourceClass); + + if (in_array($stub, ['list', 'view'], true)) { + $wizardClass = $this->getFullClassFromOptionOrAsk( + optionName: 'wizard', + question: 'Enter the class name of the query wizard to be used in the action', + componentType: ModuleComponentType::ApiQueryWizard, + module: $this->module + ); + $stubReplaces['queryWizard'] = class_basename($wizardClass); + $stubReplaces['queryWizardNamespace'] = $this->getNamespaceOfClass($wizardClass); + } } - return Stub::create("action/{$this->stub}.stub", $stubReplaces)->render(); + $modelClass = $this->getFullClassFromOptionOrAsk( + optionName: 'model', + question: 'Enter the class name of the model to be used in the action', + componentType: ModuleComponentType::Model, + module: $this->module + ); + $stubReplaces['model'] = class_basename($modelClass); + $stubReplaces['modelCamelCase'] = Str::camel($stubReplaces['model']); + $stubReplaces['modelNamespace'] = $this->getNamespaceOfClass($modelClass); + + $requestClass = $this->getFullClassFromOptionOrAsk( + optionName: 'request', + question: 'Enter the class name of the request to be used in the action', + componentType: ModuleComponentType::ApiRequest, + module: $this->module + ); + $stubReplaces['request'] = class_basename($requestClass); + $stubReplaces['requestNamespace'] = $this->getNamespaceOfClass($requestClass); + + return $stubReplaces; } } diff --git a/src/Commands/Generators/BaseComponentGeneratorCommand.php b/src/Commands/Generators/BaseComponentGeneratorCommand.php new file mode 100755 index 0000000..3aabbfa --- /dev/null +++ b/src/Commands/Generators/BaseComponentGeneratorCommand.php @@ -0,0 +1,474 @@ +beforeGenerate(); + + try { + $this->nameArgument = $this->argument('name'); + $this->ensureNameIsNotReserved($this->nameArgument); + $this->ensureNameIsValidClassName($this->nameArgument); + $this->module = $this->getModuleArgumentOrFail(); + } catch (NameIsReserved|InvalidClassName|ModuleNotFound|ModuleHasNonUniquePackageName|ModuleHasNoNamespace $exception) { + $this->components->error($exception->getMessage()); + + return self::FAILURE; + } + + try { + $result = $this->generate( + $this->getGeneratedFilePath(), + $this->getContents(), + $this->option('force') + ); + } catch (InvalidTableName $exception) { + $this->components->error($exception->getMessage()); + + return self::FAILURE; + } + + if ($result !== self::SUCCESS) { + return $result; + } + + $this->afterGenerate(); + + return self::SUCCESS; + } + + /** + * Hook called before generation starts. + * Override in subclasses to add custom logic (e.g., package checks). + */ + protected function beforeGenerate(): void + { + // Default: do nothing + } + + /** + * Get the contents for the generated file. + * Must be implemented by subclasses. + */ + abstract protected function getContents(): string; + + /** + * Get the path for the generated file. + * Override in subclasses to customize (e.g., MigrationMakeCommand). + */ + protected function getGeneratedFilePath(): string + { + return $this->getComponentPath($this->module, $this->nameArgument, $this->componentType); + } + + /** + * Hook called after successful generation. + * Override in subclasses to add custom logic (e.g., ProviderMakeCommand). + */ + protected function afterGenerate(): void + { + // Default: do nothing + } + + /** + * @param string $name + * @return void + * + * @throws NameIsReserved + */ + protected function ensureNameIsNotReserved(string $name): void + { + $classBaseName = class_basename($name); + + if ($this->isReservedName($classBaseName)) { + throw NameIsReserved::make($classBaseName); + } + } + + /** + * Validate that the name is a valid PHP class name. + * + * @throws InvalidClassName + */ + protected function ensureNameIsValidClassName(string $name): void + { + $classBaseName = class_basename($name); + + if (! $this->isValidClassName($classBaseName)) { + throw InvalidClassName::make($classBaseName); + } + } + + /** + * Check if the name is a valid PHP class name. + */ + protected function isValidClassName(string $name): bool + { + // PHP class names must start with a letter or underscore, + // followed by any number of letters, numbers, or underscores. + // Also supports multibyte characters as per PHP specification. + return (bool) preg_match('/^[a-zA-Z_\x80-\xff][a-zA-Z0-9_\x80-\xff]*$/', $name); + } + + /** + * Check if the name is a valid database table name. + */ + protected function isValidTableName(string $name): bool + { + // Table names should contain only letters, numbers, and underscores, + // and start with a letter or underscore. + return (bool) preg_match('/^[a-zA-Z_][a-zA-Z0-9_]*$/', $name); + } + + /** + * Generate component file. + * @return int The function returns "0" on success and "1" on failure. + */ + protected function generate(string $path, string $contents, bool $force = false): int + { + if ($force === false && $this->filesystem->exists($path)) { + $this->components->error("File already exists: `$path`. Use --force to overwrite."); + + return self::FAILURE; + } + + $path = str_replace('\\', '/', $path); + $contents = $this->sortImports($contents); + + $directory = dirname($path); + + try { + $this->filesystem->ensureDirectoryExists($directory); + } catch (\Exception $e) { + $this->components->error("Failed to create directory: `$directory`. " . $e->getMessage()); + + return self::FAILURE; + } + + if (! is_writable($directory)) { + $this->components->error("Directory is not writable: `$directory`. Check file permissions."); + + return self::FAILURE; + } + + if ($this->filesystem->put($path, $contents) !== false) { + $this->components->info("Created: `$path`"); + + return self::SUCCESS; + } + + $this->components->error("Failed to write file: `$path`. Check disk space and file permissions."); + + return self::FAILURE; + } + + /** + * Get full class name from option or ask + */ + public function getFullClassFromOptionOrAsk( + string $optionName, + string $question, + ModuleComponentType $componentType, + Module $module + ): string { + return $this->getFullClass( + $this->getOptionOrAsk( + $optionName, + $question, + ), + GeneratorHelper::component($componentType)->getFullNamespace($module) + ); + } + + /** + * Get full class name with namespace + */ + public function getFullClass(string $class, string $defaultNamespace): string + { + if (Str::startsWith($class, '\\')) { + return $class; + } + + return trim($defaultNamespace, '\\') . '\\' . trim($class, '\\'); + } + + /** + * Get the class "namespace" of the given class. + */ + protected function getNamespaceOfClass(string $class): string + { + if (! Str::contains($class, '\\')) { + return ''; + } + + return rtrim(Str::beforeLast($class, '\\'), '\\'); + } + + /** + * Get component namespace, without the class name. + */ + protected function getComponentNamespace(Module $module, string $name, ModuleComponentType $componentType): string + { + $name = str_replace('/', '\\', $name); + $componentNamespace = GeneratorHelper::component($componentType)->getFullNamespace($module); + $subNamespace = $this->getNamespaceOfClass($name); + + $namespace = $componentNamespace . '\\' . $subNamespace; + + return trim($namespace, '\\'); + } + + /** + * Get component path + */ + protected function getComponentPath( + Module $module, + string $name, + ModuleComponentType $componentType, + string $extension = '.php' + ): string { + $componentPath = GeneratorHelper::component($componentType)->getFullPath($module); + $fileName = GeneratorHelper::normalizePath($name); + + return $componentPath . '/' . $fileName . $extension; + } + + protected function getUserModelClass(): string + { + $userModel = GeneratorHelper::getUserModelClass(); + + if (! $userModel) { + $userModel = $this->ask('Enter the class name of the "User model"'); + + if (empty($userModel)) { + throw new LogicException('The "User model" option is required'); + } + } + + return $userModel; + } + + protected function getCreatePermissionActionClass(): string + { + $createPermissionAction = GeneratorHelper::getCreatePermissionActionClass(); + + if (! $createPermissionAction) { + $createPermissionAction = $this->ask('Enter the class name of the "Create permission action"'); + + if (empty($createPermissionAction)) { + throw new LogicException('The "Create permission action" option is required'); + } + } + + return $createPermissionAction; + } + + protected function getCreatePermissionDTOClass(): string + { + $createPermissionAction = GeneratorHelper::getCreatePermissionDTOClass(); + + if (! $createPermissionAction) { + $createPermissionAction = $this->ask('Enter the class name of the "Create permission DTO"'); + + if (empty($createPermissionAction)) { + throw new LogicException('The "Create permission DTO" option is required'); + } + } + + return $createPermissionAction; + } + + /** + * Checks whether the given name is reserved. + */ + protected function isReservedName(string $name): bool + { + $name = strtolower($name); + + return in_array($name, $this->reservedNames, true); + } + + /** + * Alphabetically sorts the imports for the given stub. + */ + protected function sortImports(string $stubContent): string + { + if (preg_match('/(?P(?:use [^;]+;$\n?)+)/m', $stubContent, $match)) { + $imports = explode("\n", trim($match['imports'])); + + sort($imports); + $imports = array_unique($imports); + + return str_replace(trim($match['imports']), implode("\n", $imports), $stubContent); + } + + return $stubContent; + } + + /** + * Get the first view directory path from the application configuration. + */ + protected function viewPath(string $path = ''): string + { + $views = $this->laravel['config']['view.paths'][0] ?? resource_path('views'); + + return $views.($path ? DIRECTORY_SEPARATOR.$path : $path); + } + + /** + * Check if the package is installed + */ + protected function packageIsInstalled(string $packageName): bool + { + $vendorPath = Env::get('COMPOSER_VENDOR_DIR') ?: $this->laravel->basePath('/vendor'); + $installedJsonPath = $vendorPath . '/composer/installed.json'; + + if (! $this->filesystem->exists($installedJsonPath)) { + return false; + } + + /** @var array{packages?: array} $installed */ + $installed = json_decode($this->filesystem->get($installedJsonPath), true); + + foreach ($installed['packages'] ?? [] as $package) { + if ($package['name'] === $packageName) { + return true; + } + } + + return false; + } + + protected function ensurePackageIsInstalledOrWarn(string $packageName): void + { + if ($this->packageIsInstalled($packageName)) { + $this->components->warn("Package '$packageName' is not installed!"); + $this->components->warn("Please install by entering `composer require $packageName` on the command line"); + } + } +} diff --git a/src/Commands/Generators/CommandMakeCommand.php b/src/Commands/Generators/CommandMakeCommand.php index e049b51..d085dbd 100644 --- a/src/Commands/Generators/CommandMakeCommand.php +++ b/src/Commands/Generators/CommandMakeCommand.php @@ -2,89 +2,70 @@ namespace Laraneat\Modules\Commands\Generators; -use Laraneat\Modules\Module; -use Laraneat\Modules\Support\Stub; -use Laraneat\Modules\Traits\ModuleCommandTrait; -use Symfony\Component\Console\Input\InputOption; +use Illuminate\Contracts\Console\PromptsForMissingInput; +use Laraneat\Modules\Enums\ModuleComponentType; +use Laraneat\Modules\Support\Generator\Stub; /** * @group generator */ -class CommandMakeCommand extends ComponentGeneratorCommand +class CommandMakeCommand extends BaseComponentGeneratorCommand implements PromptsForMissingInput { - use ModuleCommandTrait; - /** * The name and signature of the console command. * * @var string */ - protected $name = 'module:make:command'; + protected $signature = 'module:make:command + {name : The name of the command class} + {module? : The name or package name of the app module} + {--s|signature= : The signature of the console command} + {--description= : The console command description} + {--force : Overwrite the file if it already exists}'; /** * The console command description. * * @var string */ - protected $description = 'Generate new Artisan command for the specified module.'; + protected $description = 'Generate new artisan command for the specified module.'; /** - * Module instance. - * - * @var Module + * The module component type. */ - protected Module $module; + protected ModuleComponentType $componentType = ModuleComponentType::CliCommand; /** - * Component type. - * - * @var string + * Prompt for missing input arguments using the returned questions. */ - protected string $componentType = 'cli-command'; - - /** - * Prepared 'name' argument. - * - * @var string - */ - protected string $nameArgument; - - /** - * Get the console command options. - * - * @return array - */ - protected function getOptions(): array + protected function promptForMissingArgumentsUsing(): array { return [ - ['command', 'c', InputOption::VALUE_REQUIRED, 'The terminal command that should be assigned.'], + 'name' => 'Enter the command class name', ]; } - protected function prepare() - { - $this->module = $this->getModule(); - $this->nameArgument = $this->getTrimmedArgument('name'); - } - - protected function getDestinationFilePath(): string + protected function getContents(): string { - return $this->getComponentPath($this->module, $this->nameArgument, $this->componentType); - } - - protected function getTemplateContents(): string - { - $command = $this->getOptionOrAsk( - 'command', - 'Enter the terminal command that should be assigned', - 'command:name', - true + $signature = $this->getOptionOrAsk( + 'signature', + 'Enter the console command signature that should be assigned' + ); + $description = $this->getOptionOrAsk( + 'description', + 'Enter the console command description', + '', + false ); - $stubReplaces = [ - 'namespace' => $this->getComponentNamespace($this->module, $this->nameArgument, $this->componentType), - 'class' => $this->getClass($this->nameArgument), - 'command' => $command + 'namespace' => $this->getComponentNamespace( + $this->module, + $this->nameArgument, + $this->componentType + ), + 'class' => class_basename($this->nameArgument), + 'signature' => $signature, + 'description' => $description, ]; return Stub::create("command.stub", $stubReplaces)->render(); diff --git a/src/Commands/Generators/ComponentGeneratorCommand.php b/src/Commands/Generators/ComponentGeneratorCommand.php deleted file mode 100755 index 979eafb..0000000 --- a/src/Commands/Generators/ComponentGeneratorCommand.php +++ /dev/null @@ -1,338 +0,0 @@ -prepare(); - - // First we need to ensure that the given name is not a reserved word within the PHP - // language and that the class name will actually be valid. If it is not valid we - // can error now and prevent from polluting the filesystem using invalid files. - if ($this->isReservedName($this->getClass($this->getTrimmedArgument('name')))) { - $this->error('The name "'.$this->getClass($this->getTrimmedArgument('name')).'" is reserved by PHP.'); - - return self::FAILURE; - } - - $path = str_replace('\\', '/', $this->getDestinationFilePath()); - $contents = $this->sortImports($this->getTemplateContents()); - - try { - $overwriteFile = $this->hasOption('force') && $this->option('force'); - - File::ensureDirectoryExists(dirname($path)); - (new FileGenerator($path, $contents))->withFileOverwrite($overwriteFile)->generate(); - - $this->info("Created: `$path`"); - } catch (FileAlreadyExistException $e) { - $this->error("File: `$path` already exists."); - - return self::FAILURE; - } - - return self::SUCCESS; - } - - /** - * Get the class "basename" of the given class. - * - * @param string $class - * - * @return string - */ - protected function getClass(string $class): string - { - return class_basename($class); - } - - /** - * Get the class "namespace" of the given class. - * - * @param string $class - * - * @return string - */ - protected function getNamespaceOfClass(string $class): string - { - if (! Str::contains($class, '\\')) { - return ''; - } - - return rtrim(Str::beforeLast($class, '\\'), '\\'); - } - - /** - * Get component namespace, without the class name. - * - * @param Module $module - * @param string $name - * @param string $componentType - * - * @return string - */ - protected function getComponentNamespace(Module $module, string $name, string $componentType): string - { - $name = str_replace('/', '\\', $name); - $componentNamespace = GeneratorHelper::component($componentType)->getFullNamespace($module); - $extraNamespace = $this->getNamespaceOfClass($name); - - $namespace = $componentNamespace . '\\' . $extraNamespace; - - return trim($namespace, '\\'); - } - - /** - * Get component path, without the extension. - * - * @param Module $module - * @param string $name - * @param string $componentType - * @param string $extension - * @return string - */ - protected function getComponentPath( - Module $module, - string $name, - string $componentType, - string $extension = '.php' - ): string { - $componentPath = GeneratorHelper::component($componentType)->getFullPath($module); - $fileName = $this->convertNamespaceToPath($name); - - return $componentPath . '/' . $fileName . $extension; - } - - /** - * @return string - */ - protected function getUserModelClass(): string - { - $userModel = GeneratorHelper::userModel(); - - if (!$userModel) { - $userModel = $this->ask('Enter the class name of the "User model"'); - - if (empty($userModel)) { - throw new LogicException('The "User model" option is required'); - } - } - - return $userModel; - } - - /** - * @return string - */ - protected function getCreatePermissionActionClass(): string - { - $createPermissionAction = GeneratorHelper::createPermissionAction(); - - if (!$createPermissionAction) { - $createPermissionAction = $this->ask('Enter the class name of the "Create permission action"'); - - if (empty($createPermissionAction)) { - throw new LogicException('The "Create permission action" option is required'); - } - } - - return $createPermissionAction; - } - - /** - * @return string - */ - protected function getCreatePermissionDTOClass(): string - { - $createPermissionAction = GeneratorHelper::createPermissionDTO(); - - if (!$createPermissionAction) { - $createPermissionAction = $this->ask('Enter the class name of the "Create permission DTO"'); - - if (empty($createPermissionAction)) { - throw new LogicException('The "Create permission DTO" option is required'); - } - } - - return $createPermissionAction; - } - - /** - * Convert namespace to path - * - * @param string $namespace - * - * @return string - */ - protected function convertNamespaceToPath(string $namespace): string - { - return trim(str_replace('\\', '/', $namespace), '/'); - } - - /** - * Checks whether the given name is reserved. - * - * @param string $name - * @return bool - */ - protected function isReservedName(string $name): bool - { - $name = strtolower($name); - - return in_array($name, $this->reservedNames, true); - } - - /** - * Alphabetically sorts the imports for the given stub. - * - * @param string $stubContent - * - * @return string - */ - protected function sortImports(string $stubContent): string - { - if (preg_match('/(?P(?:use [^;]+;$\n?)+)/m', $stubContent, $match)) { - $imports = explode("\n", trim($match['imports'])); - - sort($imports); - $imports = array_unique($imports); - - return str_replace(trim($match['imports']), implode("\n", $imports), $stubContent); - } - - return $stubContent; - } -} diff --git a/src/Commands/Generators/ComponentsMakeCommand.php b/src/Commands/Generators/ComponentsMakeCommand.php deleted file mode 100644 index 09d599d..0000000 --- a/src/Commands/Generators/ComponentsMakeCommand.php +++ /dev/null @@ -1,88 +0,0 @@ -argument('name'); - $entityName = $this->option('entity'); - $module = Modules::findOrFail($name); - - $code = (new ModuleComponentsGenerator($module)) - ->setEntityName($entityName ?: $name) - ->setType($this->getModuleType()) - ->setConsole($this) - ->generate(); - - if ($code === E_ERROR) { - return self::FAILURE; - } - - return self::SUCCESS; - } - - /** - * Get the console command arguments. - * - * @return array - */ - protected function getArguments(): array - { - return [ - ['name', InputArgument::REQUIRED, 'The name of the module.'], - ]; - } - - protected function getOptions(): array - { - return [ -// ['api', 'a', InputOption::VALUE_NONE, 'Generate an api module (with api components, enabled by default).'], -// ['web', 'w', InputOption::VALUE_NONE, 'Generate a web module (with web components).'], - ['entity', null, InputOption::VALUE_REQUIRED, 'Entity name (used to create module components, the default is the name of the module).'], - ]; - } - - /** - * Get module type. - * - * @return string - */ - private function getModuleType(): string - { -// if ($this->option('web')) { -// return 'web'; -// } - - return 'api'; - } -} diff --git a/src/Commands/Generators/ControllerMakeCommand.php b/src/Commands/Generators/ControllerMakeCommand.php index c137dc9..d5fa285 100644 --- a/src/Commands/Generators/ControllerMakeCommand.php +++ b/src/Commands/Generators/ControllerMakeCommand.php @@ -2,24 +2,25 @@ namespace Laraneat\Modules\Commands\Generators; -use Laraneat\Modules\Module; -use Laraneat\Modules\Support\Stub; -use Laraneat\Modules\Traits\ModuleCommandTrait; -use Symfony\Component\Console\Input\InputOption; +use Illuminate\Contracts\Console\PromptsForMissingInput; +use Laraneat\Modules\Enums\ModuleComponentType; +use Laraneat\Modules\Support\Generator\Stub; /** * @group generator */ -class ControllerMakeCommand extends ComponentGeneratorCommand +class ControllerMakeCommand extends BaseComponentGeneratorCommand implements PromptsForMissingInput { - use ModuleCommandTrait; - /** * The name and signature of the console command. * * @var string */ - protected $name = 'module:make:controller'; + protected $signature = 'module:make:controller + {name : The name of the controller class} + {module? : The name or package name of the app module} + {--ui= : The UI for which the controller will be created} + {--force : Overwrite the file if it already exists}'; /** * The console command description. @@ -29,68 +30,39 @@ class ControllerMakeCommand extends ComponentGeneratorCommand protected $description = 'Generate new controller for the specified module.'; /** - * The UI for which the request will be created. - * - * @var string + * The UI for which the controller will be created. + * ('web' or 'api') */ - protected string $ui = 'api'; + protected string $ui; /** - * Module instance. - * - * @var Module + * Prompt for missing input arguments using the returned questions. */ - protected Module $module; - - /** - * Component type. - * - * @var string - */ - protected string $componentType; - - /** - * Prepared 'name' argument. - * - * @var string - */ - protected string $nameArgument; - - /** - * Get the console command options. - * - * @return array - */ - protected function getOptions(): array + protected function promptForMissingArgumentsUsing(): array { return [ - ['ui', null, InputOption::VALUE_REQUIRED, 'The UI for which the request will be created.'], + 'name' => 'Enter the controller class name', ]; } - protected function prepare() + protected function beforeGenerate(): void { - $this->module = $this->getModule(); $this->ui = $this->getOptionOrChoice( 'ui', - 'Select UI for which the request will be created', - ['api', 'web'], - 'api' + question: 'Enter the UI for which the controller will be created', + choices: ['api', 'web'], + default: 'api' ); - $this->componentType = "{$this->ui}-controller"; - $this->nameArgument = $this->getTrimmedArgument('name'); - } - - protected function getDestinationFilePath(): string - { - return $this->getComponentPath($this->module, $this->nameArgument, $this->componentType); + $this->componentType = $this->ui === 'api' + ? ModuleComponentType::ApiController + : ModuleComponentType::WebController; } - protected function getTemplateContents(): string + protected function getContents(): string { $stubReplaces = [ 'namespace' => $this->getComponentNamespace($this->module, $this->nameArgument, $this->componentType), - 'class' => $this->getClass($this->nameArgument) + 'class' => class_basename($this->nameArgument), ]; return Stub::create("controller/{$this->ui}.stub", $stubReplaces)->render(); diff --git a/src/Commands/Generators/DTOMakeCommand.php b/src/Commands/Generators/DTOMakeCommand.php index dbb8f2b..fc7457e 100644 --- a/src/Commands/Generators/DTOMakeCommand.php +++ b/src/Commands/Generators/DTOMakeCommand.php @@ -2,23 +2,24 @@ namespace Laraneat\Modules\Commands\Generators; -use Laraneat\Modules\Module; -use Laraneat\Modules\Support\Stub; -use Laraneat\Modules\Traits\ModuleCommandTrait; +use Illuminate\Contracts\Console\PromptsForMissingInput; +use Laraneat\Modules\Enums\ModuleComponentType; +use Laraneat\Modules\Support\Generator\Stub; /** * @group generator */ -class DTOMakeCommand extends ComponentGeneratorCommand +class DTOMakeCommand extends BaseComponentGeneratorCommand implements PromptsForMissingInput { - use ModuleCommandTrait; - /** * The name and signature of the console command. * * @var string */ - protected $name = 'module:make:dto'; + protected $signature = 'module:make:dto + {name : The name of the DTO class} + {module? : The name or package name of the app module} + {--force : Overwrite the file if it already exists}'; /** * The console command description. @@ -28,54 +29,36 @@ class DTOMakeCommand extends ComponentGeneratorCommand protected $description = 'Generate new DTO for the specified module.'; /** - * Module instance. - * - * @var Module + * The module component type. */ - protected Module $module; + protected ModuleComponentType $componentType = ModuleComponentType::Dto; /** - * Component type. - * - * @var string - */ - protected string $componentType = 'dto'; - - /** - * Prepared 'name' argument. - * - * @var string + * Prompt for missing input arguments using the returned questions. */ - protected string $nameArgument; - - /** - * Get the console command options. - * - * @return array - */ - protected function getOptions(): array + protected function promptForMissingArgumentsUsing(): array { - return []; - } - - protected function prepare() - { - $this->module = $this->getModule(); - $this->nameArgument = $this->getTrimmedArgument('name'); + return [ + 'name' => 'Enter the DTO class name', + ]; } - protected function getDestinationFilePath(): string + protected function beforeGenerate(): void { - return $this->getComponentPath($this->module, $this->nameArgument, $this->componentType); + $this->ensurePackageIsInstalledOrWarn('spatie/laravel-data'); } - protected function getTemplateContents(): string + protected function getContents(): string { $stubReplaces = [ - 'namespace' => $this->getComponentNamespace($this->module, $this->nameArgument, $this->componentType), - 'class' => $this->getClass($this->nameArgument) + 'namespace' => $this->getComponentNamespace( + $this->module, + $this->nameArgument, + $this->componentType + ), + 'class' => class_basename($this->nameArgument), ]; - return Stub::create("dto/default.stub", $stubReplaces)->render(); + return Stub::create("dto.stub", $stubReplaces)->render(); } } diff --git a/src/Commands/Generators/EventMakeCommand.php b/src/Commands/Generators/EventMakeCommand.php index 85f4eec..612963e 100644 --- a/src/Commands/Generators/EventMakeCommand.php +++ b/src/Commands/Generators/EventMakeCommand.php @@ -2,68 +2,56 @@ namespace Laraneat\Modules\Commands\Generators; -use Laraneat\Modules\Module; -use Laraneat\Modules\Support\Stub; -use Laraneat\Modules\Traits\ModuleCommandTrait; +use Illuminate\Contracts\Console\PromptsForMissingInput; +use Laraneat\Modules\Enums\ModuleComponentType; +use Laraneat\Modules\Support\Generator\Stub; /** * @group generator */ -class EventMakeCommand extends ComponentGeneratorCommand +class EventMakeCommand extends BaseComponentGeneratorCommand implements PromptsForMissingInput { - use ModuleCommandTrait; - /** * The name and signature of the console command. * * @var string */ - protected $name = 'module:make:event'; + protected $signature = 'module:make:event + {name : The name of the event class} + {module? : The name or package name of the app module} + {--force : Overwrite the file if it already exists}'; /** * The console command description. * * @var string */ - protected $description = 'Generate new event for the specified module.'; + protected $description = 'Generate new event class for the specified module.'; /** - * Module instance. - * - * @var Module - */ - protected Module $module; - - /** - * Component type. - * - * @var string + * The module component type. */ - protected string $componentType = 'event'; + protected ModuleComponentType $componentType = ModuleComponentType::Event; /** - * Prepared 'name' argument. - * - * @var string + * Prompt for missing input arguments using the returned questions. */ - protected string $nameArgument; - - protected function prepare() + protected function promptForMissingArgumentsUsing(): array { - $this->module = $this->getModule(); - $this->nameArgument = $this->getTrimmedArgument('name'); - } - - protected function getDestinationFilePath(): string - { - return $this->getComponentPath($this->module, $this->nameArgument, $this->componentType); + return [ + 'name' => 'Enter the event class name', + ]; } - protected function getTemplateContents(): string + protected function getContents(): string { $stubReplaces = [ - 'namespace' => $this->getComponentNamespace($this->module, $this->nameArgument, $this->componentType), - 'class' => $this->getClass($this->nameArgument), + 'namespace' => $this->getComponentNamespace( + $this->module, + $this->nameArgument, + $this->componentType + ), + 'class' => class_basename($this->nameArgument), ]; return Stub::create("event.stub", $stubReplaces)->render(); diff --git a/src/Commands/Generators/ExceptionMakeCommand.php b/src/Commands/Generators/ExceptionMakeCommand.php index 3e57409..d4f77e8 100644 --- a/src/Commands/Generators/ExceptionMakeCommand.php +++ b/src/Commands/Generators/ExceptionMakeCommand.php @@ -2,68 +2,56 @@ namespace Laraneat\Modules\Commands\Generators; -use Laraneat\Modules\Module; -use Laraneat\Modules\Support\Stub; -use Laraneat\Modules\Traits\ModuleCommandTrait; +use Illuminate\Contracts\Console\PromptsForMissingInput; +use Laraneat\Modules\Enums\ModuleComponentType; +use Laraneat\Modules\Support\Generator\Stub; /** * @group generator */ -class ExceptionMakeCommand extends ComponentGeneratorCommand +class ExceptionMakeCommand extends BaseComponentGeneratorCommand implements PromptsForMissingInput { - use ModuleCommandTrait; - /** * The name and signature of the console command. * * @var string */ - protected $name = 'module:make:exception'; + protected $signature = 'module:make:exception + {name : The name of the exception class} + {module? : The name or package name of the app module} + {--force : Overwrite the file if it already exists}'; /** * The console command description. * * @var string */ - protected $description = 'Generate new exception for the specified module.'; + protected $description = 'Generate new exception class for the specified module.'; /** - * Module instance. - * - * @var Module - */ - protected Module $module; - - /** - * Component type. - * - * @var string + * The module component type. */ - protected string $componentType = 'exception'; + protected ModuleComponentType $componentType = ModuleComponentType::Exception; /** - * Prepared 'name' argument. - * - * @var string + * Prompt for missing input arguments using the returned questions. */ - protected string $nameArgument; - - protected function prepare() + protected function promptForMissingArgumentsUsing(): array { - $this->module = $this->getModule(); - $this->nameArgument = $this->getTrimmedArgument('name'); - } - - protected function getDestinationFilePath(): string - { - return $this->getComponentPath($this->module, $this->nameArgument, $this->componentType); + return [ + 'name' => 'Enter the exception class name', + ]; } - protected function getTemplateContents(): string + protected function getContents(): string { $stubReplaces = [ - 'namespace' => $this->getComponentNamespace($this->module, $this->nameArgument, $this->componentType), - 'class' => $this->getClass($this->nameArgument), + 'namespace' => $this->getComponentNamespace( + $this->module, + $this->nameArgument, + $this->componentType + ), + 'class' => class_basename($this->nameArgument), ]; return Stub::create("exception.stub", $stubReplaces)->render(); diff --git a/src/Commands/Generators/FactoryMakeCommand.php b/src/Commands/Generators/FactoryMakeCommand.php index 12948b5..1537e65 100644 --- a/src/Commands/Generators/FactoryMakeCommand.php +++ b/src/Commands/Generators/FactoryMakeCommand.php @@ -2,25 +2,26 @@ namespace Laraneat\Modules\Commands\Generators; +use Illuminate\Contracts\Console\PromptsForMissingInput; use Illuminate\Support\Str; -use Laraneat\Modules\Module; -use Laraneat\Modules\Support\Stub; -use Laraneat\Modules\Traits\ModuleCommandTrait; -use Symfony\Component\Console\Input\InputOption; +use Laraneat\Modules\Enums\ModuleComponentType; +use Laraneat\Modules\Support\Generator\Stub; /** * @group generator */ -class FactoryMakeCommand extends ComponentGeneratorCommand +class FactoryMakeCommand extends BaseComponentGeneratorCommand implements PromptsForMissingInput { - use ModuleCommandTrait; - /** * The name and signature of the console command. * * @var string */ - protected $name = 'module:make:factory'; + protected $signature = 'module:make:factory + {name : The name of the factory class} + {module? : The name or package name of the app module} + {--model= : The class name of the model to be used in the factory} + {--force : Overwrite the file if it already exists}'; /** * The console command description. @@ -30,64 +31,38 @@ class FactoryMakeCommand extends ComponentGeneratorCommand protected $description = 'Generate new factory for the specified module.'; /** - * Module instance. - * - * @var Module - */ - protected Module $module; - - /** - * Component type. - * - * @var string + * The module component type. */ - protected string $componentType = 'factory'; + protected ModuleComponentType $componentType = ModuleComponentType::Factory; /** - * Prepared 'name' argument. - * - * @var string + * Prompt for missing input arguments using the returned questions. */ - protected string $nameArgument; - - /** - * Get the console command options. - * - * @return array - */ - protected function getOptions(): array + protected function promptForMissingArgumentsUsing(): array { return [ - ['model', null, InputOption::VALUE_REQUIRED, 'The class name of the model to be used in the factory.'], + 'name' => 'Enter the factory class name', ]; } - protected function prepare() - { - $this->module = $this->getModule(); - $this->nameArgument = $this->getTrimmedArgument('name'); - } - - protected function getDestinationFilePath(): string - { - return $this->getComponentPath($this->module, $this->nameArgument, $this->componentType); - } - - protected function getTemplateContents(): string + protected function getContents(): string { - $model = $this->getOptionOrAsk( - 'model', - 'Enter the class name of the model to be used in the factory', - '', - true + $modelClass = $this->getFullClassFromOptionOrAsk( + optionName: 'model', + question: 'Enter the class name of the model to be used in the factory', + componentType: ModuleComponentType::Model, + module: $this->module ); - $modelClass = $this->getClass($model); $stubReplaces = [ - 'namespace' => $this->getComponentNamespace($this->module, $this->nameArgument, $this->componentType), - 'class' => $this->getClass($this->nameArgument), - 'model' => $modelClass, - 'modelEntity' => Str::camel($modelClass), - 'modelNamespace' => $this->getComponentNamespace($this->module, $model, 'model') + 'namespace' => $this->getComponentNamespace( + $this->module, + $this->nameArgument, + $this->componentType + ), + 'class' => class_basename($this->nameArgument), + 'model' => class_basename($modelClass), + 'modelCamelCase' => Str::camel(class_basename($modelClass)), + 'modelNamespace' => $this->getNamespaceOfClass($modelClass), ]; return Stub::create("factory.stub", $stubReplaces)->render(); diff --git a/src/Commands/Generators/JobMakeCommand.php b/src/Commands/Generators/JobMakeCommand.php index 3a9fe90..27beb39 100644 --- a/src/Commands/Generators/JobMakeCommand.php +++ b/src/Commands/Generators/JobMakeCommand.php @@ -2,97 +2,65 @@ namespace Laraneat\Modules\Commands\Generators; -use Laraneat\Modules\Module; -use Laraneat\Modules\Support\Stub; -use Laraneat\Modules\Traits\ModuleCommandTrait; -use Symfony\Component\Console\Input\InputOption; +use Illuminate\Contracts\Console\PromptsForMissingInput; +use Laraneat\Modules\Enums\ModuleComponentType; +use Laraneat\Modules\Support\Generator\Stub; /** * @group generator */ -class JobMakeCommand extends ComponentGeneratorCommand +class JobMakeCommand extends BaseComponentGeneratorCommand implements PromptsForMissingInput { - use ModuleCommandTrait; - /** * The name and signature of the console command. * * @var string */ - protected $name = 'module:make:job'; + protected $signature = 'module:make:job + {name : The name of the job class} + {module? : The name or package name of the app module} + {--s|stub= : The stub name to load for this generator} + {--force : Overwrite the file if it already exists}'; /** * The console command description. * * @var string */ - protected $description = 'Generate new job for the specified module.'; - - /** - * The stub name to load for this generator. - * - * @var string - */ - protected string $stub = 'plain'; - - /** - * Module instance. - * - * @var Module - */ - protected Module $module; - - /** - * Component type. - * - * @var string - */ - protected string $componentType; + protected $description = 'Generate new job class for the specified module.'; /** - * Prepared 'name' argument. - * - * @var string + * The module component type. */ - protected string $nameArgument; + protected ModuleComponentType $componentType = ModuleComponentType::Job; /** - * Get the console command options. - * - * @return array + * Prompt for missing input arguments using the returned questions. */ - protected function getOptions(): array + protected function promptForMissingArgumentsUsing(): array { return [ - ['stub', 's', InputOption::VALUE_REQUIRED, 'The stub name to load for this generator.'], + 'name' => 'Enter the job class name', ]; } - protected function prepare() + protected function getContents(): string { - $this->module = $this->getModule(); - $this->stub = $this->getOptionOrChoice( + $stub = $this->getOptionOrChoice( 'stub', 'Select the stub you want to use for generator', ['plain', 'queued'], 'plain' ); - $this->componentType = 'job'; - $this->nameArgument = $this->getTrimmedArgument('name'); - } - - protected function getDestinationFilePath(): string - { - return $this->getComponentPath($this->module, $this->nameArgument, $this->componentType); - } - - protected function getTemplateContents(): string - { $stubReplaces = [ - 'namespace' => $this->getComponentNamespace($this->module, $this->nameArgument, $this->componentType), - 'class' => $this->getClass($this->nameArgument) + 'namespace' => $this->getComponentNamespace( + $this->module, + $this->nameArgument, + $this->componentType + ), + 'class' => class_basename($this->nameArgument), ]; - return Stub::create("job/{$this->stub}.stub", $stubReplaces)->render(); + return Stub::create("job/$stub.stub", $stubReplaces)->render(); } } diff --git a/src/Commands/Generators/ListenerMakeCommand.php b/src/Commands/Generators/ListenerMakeCommand.php index 7301d0d..cd1dfb0 100644 --- a/src/Commands/Generators/ListenerMakeCommand.php +++ b/src/Commands/Generators/ListenerMakeCommand.php @@ -2,107 +2,74 @@ namespace Laraneat\Modules\Commands\Generators; -use Laraneat\Modules\Module; -use Laraneat\Modules\Support\Stub; -use Laraneat\Modules\Traits\ModuleCommandTrait; -use Symfony\Component\Console\Input\InputOption; +use Laraneat\Modules\Enums\ModuleComponentType; +use Laraneat\Modules\Support\Generator\Stub; /** * @group generator */ -class ListenerMakeCommand extends ComponentGeneratorCommand +class ListenerMakeCommand extends BaseComponentGeneratorCommand { - use ModuleCommandTrait; - /** * The name and signature of the console command. * * @var string */ - protected $name = 'module:make:listener'; + protected $signature = 'module:make:listener + {name : The name of the listener class} + {module? : The name or package name of the app module} + {--s|stub= : The stub name to load for this generator} + {--event= : The class name of the event to listen to} + {--force : Overwrite the file if it already exists}'; /** * The console command description. * * @var string */ - protected $description = 'Generate new listener for the specified module.'; - - /** - * The stub name to load for this generator. - * - * @var string - */ - protected string $stub = 'plain'; - - /** - * Module instance. - * - * @var Module - */ - protected Module $module; - - /** - * Component type. - * - * @var string - */ - protected string $componentType; + protected $description = 'Generate new listener class for the specified module.'; /** - * Prepared 'name' argument - * - * @var string + * The module component type. */ - protected string $nameArgument; + protected ModuleComponentType $componentType = ModuleComponentType::Listener; /** - * Get the console command options. - * - * @return array + * Prompt for missing input arguments using the returned questions. */ - protected function getOptions(): array + protected function promptForMissingArgumentsUsing(): array { return [ - ['stub', 's', InputOption::VALUE_REQUIRED, 'The stub name to load for this generator.'], - ['event', null, InputOption::VALUE_REQUIRED, 'The class name of the event to listen to.'], + 'name' => 'Enter the listener class name', ]; } - protected function prepare() + protected function getContents(): string { - $this->module = $this->getModule(); - $this->stub = $this->getOptionOrChoice( + $stub = $this->getOptionOrChoice( 'stub', 'Select the stub you want to use for generator', ['plain', 'queued'], 'plain' ); - $this->componentType = 'listener'; - $this->nameArgument = $this->getTrimmedArgument('name'); - } - - protected function getDestinationFilePath(): string - { - return $this->getComponentPath($this->module, $this->nameArgument, $this->componentType); - } - - protected function getTemplateContents(): string - { $stubReplaces = [ - 'namespace' => $this->getComponentNamespace($this->module, $this->nameArgument, $this->componentType), - 'class' => $this->getClass($this->nameArgument), + 'namespace' => $this->getComponentNamespace( + $this->module, + $this->nameArgument, + $this->componentType + ), + 'class' => class_basename($this->nameArgument), ]; - $event = $this->getOptionOrAsk( - 'event', - 'Enter the class name of the event that will be listened to', - '', - true + $eventClass = $this->getFullClassFromOptionOrAsk( + optionName: 'event', + question: 'Enter the class name of the event to listen to', + componentType: ModuleComponentType::Event, + module: $this->module ); - $stubReplaces['event'] = $this->getClass($event); - $stubReplaces['eventNamespace'] = $this->getComponentNamespace($this->module, $event, 'event'); + $stubReplaces['event'] = class_basename($eventClass); + $stubReplaces['eventNamespace'] = $this->getNamespaceOfClass($eventClass); - return Stub::create("listener/{$this->stub}.stub", $stubReplaces)->render(); + return Stub::create("listener/$stub.stub", $stubReplaces)->render(); } } diff --git a/src/Commands/Generators/MailMakeCommand.php b/src/Commands/Generators/MailMakeCommand.php index 833e514..3dda291 100644 --- a/src/Commands/Generators/MailMakeCommand.php +++ b/src/Commands/Generators/MailMakeCommand.php @@ -2,97 +2,75 @@ namespace Laraneat\Modules\Commands\Generators; -use Laraneat\Modules\Module; -use Laraneat\Modules\Support\Stub; -use Laraneat\Modules\Traits\ModuleCommandTrait; -use Symfony\Component\Console\Input\InputOption; +use Illuminate\Contracts\Console\PromptsForMissingInput; +use Illuminate\Support\Str; +use Laraneat\Modules\Enums\ModuleComponentType; +use Laraneat\Modules\Support\Generator\Stub; /** * @group generator */ -class MailMakeCommand extends ComponentGeneratorCommand +class MailMakeCommand extends BaseComponentGeneratorCommand implements PromptsForMissingInput { - use ModuleCommandTrait; - /** * The name and signature of the console command. * * @var string */ - protected $name = 'module:make:mail'; + protected $signature = 'module:make:mail + {name : The name of the mail class} + {module? : The name or package name of the app module} + {--subject= : The subject of mail} + {--view= : The view for the mail} + {--force : Overwrite the file if it already exists}'; /** * The console command description. * * @var string */ - protected $description = 'Generate new mail for the specified module.'; - - /** - * The stub name to load for this generator. - * - * @var string - */ - protected string $stub = 'plain'; - - /** - * Module instance. - * - * @var Module - */ - protected Module $module; - - /** - * Component type. - * - * @var string - */ - protected string $componentType; + protected $description = 'Generate new mail class for the specified module.'; /** - * Prepared 'name' argument. - * - * @var string + * The module component type. */ - protected string $nameArgument; + protected ModuleComponentType $componentType = ModuleComponentType::Mail; /** - * Get the console command options. - * - * @return array + * Prompt for missing input arguments using the returned questions. */ - protected function getOptions(): array + protected function promptForMissingArgumentsUsing(): array { return [ - ['stub', 's', InputOption::VALUE_REQUIRED, 'The stub name to load for this generator.'], + 'name' => 'Enter the mail class name', ]; } - protected function prepare() + protected function getContents(): string { - $this->module = $this->getModule(); - $this->stub = $this->getOptionOrChoice( - 'stub', - 'Select the stub you want to use for generator', - ['plain', 'queued'], - 'plain' + $classBaseName = class_basename($this->nameArgument); + $subject = $this->getOptionOrAsk( + "subject", + "Enter the subject of mail", + Str::headline($classBaseName) + ); + $view = $this->getOptionOrAsk( + "view", + "Enter the view for the mail", + 'view.name' ); - $this->componentType = 'mail'; - $this->nameArgument = $this->getTrimmedArgument('name'); - } - - protected function getDestinationFilePath(): string - { - return $this->getComponentPath($this->module, $this->nameArgument, $this->componentType); - } - protected function getTemplateContents(): string - { $stubReplaces = [ - 'namespace' => $this->getComponentNamespace($this->module, $this->nameArgument, $this->componentType), - 'class' => $this->getClass($this->nameArgument) + 'namespace' => $this->getComponentNamespace( + $this->module, + $this->nameArgument, + $this->componentType + ), + 'class' => $classBaseName, + 'subject' => $subject, + 'view' => $view, ]; - return Stub::create("mail/{$this->stub}.stub", $stubReplaces)->render(); + return Stub::create("mail.stub", $stubReplaces)->render(); } } diff --git a/src/Commands/Generators/MiddlewareMakeCommand.php b/src/Commands/Generators/MiddlewareMakeCommand.php index 0befdd1..84dc4ae 100644 --- a/src/Commands/Generators/MiddlewareMakeCommand.php +++ b/src/Commands/Generators/MiddlewareMakeCommand.php @@ -2,68 +2,56 @@ namespace Laraneat\Modules\Commands\Generators; -use Laraneat\Modules\Module; -use Laraneat\Modules\Support\Stub; -use Laraneat\Modules\Traits\ModuleCommandTrait; +use Illuminate\Contracts\Console\PromptsForMissingInput; +use Laraneat\Modules\Enums\ModuleComponentType; +use Laraneat\Modules\Support\Generator\Stub; /** * @group generator */ -class MiddlewareMakeCommand extends ComponentGeneratorCommand +class MiddlewareMakeCommand extends BaseComponentGeneratorCommand implements PromptsForMissingInput { - use ModuleCommandTrait; - /** * The name and signature of the console command. * * @var string */ - protected $name = 'module:make:middleware'; + protected $signature = 'module:make:middleware + {name : The name of the middleware class} + {module? : The name or package name of the app module} + {--force : Overwrite the file if it already exists}'; /** * The console command description. * * @var string */ - protected $description = 'Generate new middleware for the specified module.'; + protected $description = 'Generate new middleware class for the specified module.'; /** - * Module instance. - * - * @var Module - */ - protected Module $module; - - /** - * Component type. - * - * @var string + * The module component type. */ - protected string $componentType = 'middleware'; + protected ModuleComponentType $componentType = ModuleComponentType::Middleware; /** - * Prepared 'name' argument. - * - * @var string + * Prompt for missing input arguments using the returned questions. */ - protected string $nameArgument; - - protected function prepare() + protected function promptForMissingArgumentsUsing(): array { - $this->module = $this->getModule(); - $this->nameArgument = $this->getTrimmedArgument('name'); - } - - protected function getDestinationFilePath(): string - { - return $this->getComponentPath($this->module, $this->nameArgument, $this->componentType); + return [ + 'name' => 'Enter the middleware class name', + ]; } - protected function getTemplateContents(): string + protected function getContents(): string { $stubReplaces = [ - 'namespace' => $this->getComponentNamespace($this->module, $this->nameArgument, $this->componentType), - 'class' => $this->getClass($this->nameArgument), + 'namespace' => $this->getComponentNamespace( + $this->module, + $this->nameArgument, + $this->componentType + ), + 'class' => class_basename($this->nameArgument), ]; return Stub::create("middleware.stub", $stubReplaces)->render(); diff --git a/src/Commands/Generators/MigrationMakeCommand.php b/src/Commands/Generators/MigrationMakeCommand.php index 5dad12d..9e17aa5 100644 --- a/src/Commands/Generators/MigrationMakeCommand.php +++ b/src/Commands/Generators/MigrationMakeCommand.php @@ -2,29 +2,33 @@ namespace Laraneat\Modules\Commands\Generators; +use Illuminate\Contracts\Console\PromptsForMissingInput; use Illuminate\Support\Str; -use Laraneat\Modules\Module; -use Laraneat\Modules\Support\Generator\GeneratorHelper; +use Laraneat\Modules\Enums\ModuleComponentType; +use Laraneat\Modules\Exceptions\InvalidTableName; +use Laraneat\Modules\Support\Generator\Stub; use Laraneat\Modules\Support\Migrations\NameParser; use Laraneat\Modules\Support\Migrations\SchemaParser; -use Laraneat\Modules\Support\Stub; -use Laraneat\Modules\Traits\ModuleCommandTrait; use LogicException; -use Symfony\Component\Console\Input\InputOption; /** * @group generator */ -class MigrationMakeCommand extends ComponentGeneratorCommand +class MigrationMakeCommand extends BaseComponentGeneratorCommand implements PromptsForMissingInput { - use ModuleCommandTrait; - /** * The name and signature of the console command. * * @var string */ - protected $name = 'module:make:migration'; + protected $signature = 'module:make:migration + {name : The name of the migration} + {module? : The name or package name of the app module} + {--s|stub= : The stub name to load for this generator} + {--f|fields= : The specified fields table} + {--t1|tableOne= : The name of first table} + {--t2|tableTwo= : The name of second table} + {--force : Overwrite the file if it already exists}'; /** * The console command description. @@ -34,77 +38,45 @@ class MigrationMakeCommand extends ComponentGeneratorCommand protected $description = 'Generate new migration for the specified module.'; /** - * The stub name to load for this generator. - * - * @var string|null - */ - protected ?string $stub = null; - - /** - * Module instance. - * - * @var Module - */ - protected Module $module; - - /** - * Component type. - * - * @var string + * The module component type. */ - protected string $componentType; + protected ModuleComponentType $componentType = ModuleComponentType::Migration; /** - * Get the console command options. - * - * @return array + * Prompt for missing input arguments using the returned questions. */ - protected function getOptions(): array + protected function promptForMissingArgumentsUsing(): array { return [ - ['stub', 's', InputOption::VALUE_REQUIRED, 'The stub name to load for this generator.', ''], - ['fields', 'f', InputOption::VALUE_REQUIRED, 'The specified fields table.', null], - ['tableOne', 't1', InputOption::VALUE_REQUIRED, 'The name of first table.'], - ['tableTwo', 't2', InputOption::VALUE_REQUIRED, 'The name of second table.'], + 'name' => 'Enter the migration name', ]; } - protected function getSchemaParser(): SchemaParser + protected function getGeneratedFilePath(): string { - return new SchemaParser($this->option('fields')); + return $this->getComponentPath($this->module, $this->getFileName(), $this->componentType); } - protected function getFileName(): string + protected function getContents(): string { - return date('Y_m_d_His_') . Str::snake($this->getTrimmedArgument('name')); - } - - protected function prepare() - { - $this->module = $this->getModule(); - $this->stub = $this->getOptionOneOf( + $stub = $this->getOptionOneOf( 'stub', - ['', 'plain', 'add', 'create', 'delete', 'pivot'], + choices: [ + null, + 'add', + 'create', + 'delete', + 'pivot', + 'plain', + ], ); - $this->componentType = 'migration'; - } - - protected function getDestinationFilePath(): string - { - $componentPath = GeneratorHelper::component($this->componentType)->getFullPath($this->module); - - return $componentPath . '/' . $this->getFileName() . '.php'; - } - - protected function getTemplateContents(): string - { $parser = new NameParser($this->argument('name')); - if ($this->stub === 'pivot') { + if ($stub === 'pivot') { return $this->generatePivotMigrationContent($parser); } - if ($this->stub === 'add' || (empty($this->stub) && $parser->isAdd())) { + if ($stub === 'add' || (empty($stub) && $parser->isAdd())) { return Stub::create('/migration/add.stub', [ 'table' => $this->getTableName($parser), 'fieldsUp' => $this->getSchemaParser()->up(), @@ -112,14 +84,14 @@ protected function getTemplateContents(): string ])->render(); } - if ($this->stub === 'create' || (empty($this->stub) && $parser->isCreate())) { + if ($stub === 'create' || (empty($stub) && $parser->isCreate())) { return Stub::create('/migration/create.stub', [ 'table' => $this->getTableName($parser), 'fields' => $this->getSchemaParser()->render(), ])->render(); } - if ($this->stub === 'delete' || (empty($this->stub) && $parser->isDelete())) { + if ($stub === 'delete' || (empty($stub) && $parser->isDelete())) { return Stub::create('/migration/delete.stub', [ 'table' => $this->getTableName($parser), 'fieldsDown' => $this->getSchemaParser()->up(), @@ -130,6 +102,9 @@ protected function getTemplateContents(): string return Stub::create('/migration/plain.stub')->render(); } + /** + * @throws InvalidTableName + */ protected function getTableName(NameParser $parser): string { $tableName = $parser->getTableName(); @@ -142,9 +117,16 @@ protected function getTableName(NameParser $parser): string } } + if (! $this->isValidTableName($tableName)) { + throw InvalidTableName::make($tableName); + } + return $tableName; } + /** + * @throws InvalidTableName + */ protected function generatePivotMigrationContent(NameParser $parser): string { $table = $this->getTableName($parser); @@ -157,26 +139,42 @@ protected function generatePivotMigrationContent(NameParser $parser): string $tableOne = $this->getOptionOrAsk( 'tableOne', - 'Enter the name of first table.', - $tableOne ?? '', - true + 'Enter the name of first table', + $tableOne ?? '' ); $tableTwo = $this->getOptionOrAsk( 'tableTwo', - 'Enter the name of second table.', - $tableTwo ?? '', - true + 'Enter the name of second table', + $tableTwo ?? '' ); + if (! $this->isValidTableName($tableOne)) { + throw InvalidTableName::make($tableOne); + } + + if (! $this->isValidTableName($tableTwo)) { + throw InvalidTableName::make($tableTwo); + } + return Stub::create('/migration/pivot.stub', [ 'table' => $table, 'tableOne' => $tableOne, 'tableTwo' => $tableTwo, 'columnOne' => $this->convertTableNameToPrimaryColumnName($tableOne), - 'columnTwo' => $this->convertTableNameToPrimaryColumnName($tableTwo) + 'columnTwo' => $this->convertTableNameToPrimaryColumnName($tableTwo), ])->render(); } + protected function getSchemaParser(): SchemaParser + { + return new SchemaParser($this->option('fields')); + } + + protected function getFileName(): string + { + return now()->format('Y_m_d_His_') . Str::snake($this->nameArgument); + } + protected function convertTableNameToPrimaryColumnName(string $tableName): string { return Str::singular($tableName) . '_id'; diff --git a/src/Commands/Generators/ModelMakeCommand.php b/src/Commands/Generators/ModelMakeCommand.php index 7d45240..44cd497 100644 --- a/src/Commands/Generators/ModelMakeCommand.php +++ b/src/Commands/Generators/ModelMakeCommand.php @@ -2,24 +2,26 @@ namespace Laraneat\Modules\Commands\Generators; -use Laraneat\Modules\Module; -use Laraneat\Modules\Support\Stub; -use Laraneat\Modules\Traits\ModuleCommandTrait; -use Symfony\Component\Console\Input\InputOption; +use Illuminate\Contracts\Console\PromptsForMissingInput; +use Laraneat\Modules\Enums\ModuleComponentType; +use Laraneat\Modules\Support\Generator\GeneratorHelper; +use Laraneat\Modules\Support\Generator\Stub; /** * @group generator */ -class ModelMakeCommand extends ComponentGeneratorCommand +class ModelMakeCommand extends BaseComponentGeneratorCommand implements PromptsForMissingInput { - use ModuleCommandTrait; - /** * The name and signature of the console command. * * @var string */ - protected $name = 'module:make:model'; + protected $signature = 'module:make:model + {name : The name of the model} + {module? : The name or package name of the app module} + {--factory= : The class name of the model factory} + {--force : Overwrite the file if it already exists}'; /** * The console command description. @@ -29,86 +31,46 @@ class ModelMakeCommand extends ComponentGeneratorCommand protected $description = 'Generate new model for the specified module.'; /** - * The stub name to load for this generator. - * - * @var string - */ - protected string $stub = 'full'; - - /** - * Module instance. - * - * @var Module - */ - protected Module $module; - - /** - * Component type. - * - * @var string + * The module component type. */ - protected string $componentType; + protected ModuleComponentType $componentType = ModuleComponentType::Model; /** - * Prepared 'name' argument. - * - * @var string + * Prompt for missing input arguments using the returned questions. */ - protected string $nameArgument; - - /** - * Get the console command options. - * - * @return array - */ - protected function getOptions(): array + protected function promptForMissingArgumentsUsing(): array { return [ - ['stub', 's', InputOption::VALUE_REQUIRED, 'The stub name to load for this generator.'], - ['factory', null, InputOption::VALUE_REQUIRED, 'The class name of the model factory.'], + 'name' => 'Enter the model class name', ]; } - protected function prepare() - { - $this->module = $this->getModule(); - $this->stub = $this->getOptionOrChoice( - 'stub', - 'Select the stub you want to use for generator', - ['plain', 'full'], - 'full' - ); - $this->componentType = 'model'; - $this->nameArgument = $this->getTrimmedArgument('name'); - } - - protected function getDestinationFilePath(): string - { - return $this->getComponentPath($this->module, $this->nameArgument, $this->componentType); - } - - protected function getTemplateContents(): string + protected function getContents(): string { + $stub = 'plain'; $stubReplaces = [ 'namespace' => $this->getComponentNamespace($this->module, $this->nameArgument, $this->componentType), - 'class' => $this->getClass($this->nameArgument) + 'class' => class_basename($this->nameArgument), ]; - if ($this->stub === 'full') { - $factory = $this->getOptionOrAsk( - 'factory', - 'Enter the class name of the model factory', - '' + $factoryOption = $this->getOptionOrAsk( + 'factory', + question: 'Enter the class name of the factory to be used in the model (optional)', + required: false + ); + + if ($factoryOption) { + $factoryClass = $this->getFullClass( + $factoryOption, + GeneratorHelper::component(ModuleComponentType::Factory) + ->getFullNamespace($this->module) ); - if ($factory) { - $stubReplaces['factory'] = $this->getClass($factory); - $stubReplaces['factoryNamespace'] = $this->getComponentNamespace($this->module, $factory, 'factory'); - } else { - $this->stub = 'plain'; - } + $stub = 'full'; + $stubReplaces['factory'] = class_basename($factoryClass); + $stubReplaces['factoryNamespace'] = $this->getNamespaceOfClass($factoryClass); } - return Stub::create("model/{$this->stub}.stub", $stubReplaces)->render(); + return Stub::create("model/$stub.stub", $stubReplaces)->render(); } } diff --git a/src/Commands/Generators/ModuleMakeCommand.php b/src/Commands/Generators/ModuleMakeCommand.php index 03fd0b8..1c4bef0 100755 --- a/src/Commands/Generators/ModuleMakeCommand.php +++ b/src/Commands/Generators/ModuleMakeCommand.php @@ -2,23 +2,31 @@ namespace Laraneat\Modules\Commands\Generators; -use Illuminate\Console\Command; -use Laraneat\Modules\Contracts\ActivatorInterface; -use Laraneat\Modules\Generators\ModuleGenerator; -use Symfony\Component\Console\Input\InputArgument; -use Symfony\Component\Console\Input\InputOption; +use Composer\Factory; +use Illuminate\Contracts\Console\PromptsForMissingInput; +use Illuminate\Support\Str; +use Laraneat\Modules\Exceptions\ComposerException; +use Laraneat\Modules\Module; +use Laraneat\Modules\Support\Composer; +use Laraneat\Modules\Support\ComposerJsonFile; +use Laraneat\Modules\Support\Generator\GeneratorHelper; +use Laraneat\Modules\Support\Generator\Stub; /** * @group generator */ -class ModuleMakeCommand extends Command +class ModuleMakeCommand extends BaseComponentGeneratorCommand implements PromptsForMissingInput { /** - * The console command name. + * The name and signature of the console command. * * @var string */ - protected $name = 'module:make'; + protected $signature = 'module:make + {name : The name of the module to be created} + {--preset= : The preset of the module to be created ("plain", "base", or "api", "plain" by default)} + {--entity= : Entity name (used to create module components, the default is the name of the module)} + {--force : Overwrite the module if it already exists}'; /** * The console command description. @@ -27,74 +35,365 @@ class ModuleMakeCommand extends Command */ protected $description = 'Create a new module.'; + /** + * The name of the module to be created + */ + protected string $moduleName; + + /** + * Studly name of the module to be created + */ + protected string $moduleStudlyName; + + /** + * The package name of the module to be created + */ + protected string $modulePackageName; + + /** + * The preset of the module to be created + */ + protected string $modulePreset; + + /** + * The entity name (used to create module components, the default is the name of the module) + */ + protected ?string $entityName = null; + + /** + * Prompt for missing input arguments using the returned questions. + */ + protected function promptForMissingArgumentsUsing(): array + { + return [ + 'name' => 'Enter the name of the module to be created', + ]; + } + /** * Execute the console command. - * - * @return int */ public function handle(): int { - $name = $this->argument('name'); - $entityName = $this->option('entity'); - - $code = (new ModuleGenerator($name)) - ->setEntityName($entityName ?: $name) - ->setFilesystem($this->laravel['files']) - ->setRepository($this->laravel['modules']) - ->setConfig($this->laravel['config']) - ->setActivator($this->laravel[ActivatorInterface::class]) - ->setConsole($this) - ->setForce($this->option('force')) - ->setType($this->getModuleType()) - ->setActive(!$this->option('disabled')) - ->generate(); - - if ($code === E_ERROR) { + $nameArgument = trim($this->argument('name'), '/\\'); + $explodedNameArgument = explode('/', $nameArgument, 2); + + [$rawVendor, $rawModuleName] = empty($explodedNameArgument[1]) + ? ['', $explodedNameArgument[0]] + : $explodedNameArgument; + + $this->moduleStudlyName = Str::studly($rawModuleName); + $this->moduleName = Str::kebab($this->moduleStudlyName); + if (! $this->validateModuleStudlyName($this->moduleStudlyName)) { + $this->components->error("The module name passed is not valid!"); + + return self::FAILURE; + } + + $this->modulePackageName = sprintf( + '%s/%s', + Str::kebab($rawVendor ?: config('modules.composer.vendor', 'app')), + $this->moduleName + ); + + if ($this->modulesRepository->has($this->modulePackageName)) { + $this->components->error("Module '$this->modulePackageName' already exist!"); + + return self::FAILURE; + } + + $this->modulePreset = $this->getOptionOrChoice( + optionName: 'preset', + question: 'Select the preset of module to create', + choices: [ + 'plain', + 'base', + 'api', + ], + default: 'plain' + ); + + if ($this->isFailure($this->generateComposerJsonFile())) { + return self::FAILURE; + } + + $this->modulesRepository->pruneModulesManifest(); + + if ($this->isFailure($this->generateComponents($this->modulesRepository->find($this->modulePackageName)))) { return self::FAILURE; } + try { + $this->addModuleToComposer(); + } catch (ComposerException $exception) { + $this->components->error($exception->getMessage()); + $this->components->info("Please run composer update {$this->modulePackageName} manually"); + } + return self::SUCCESS; } - /** - * Get the console command arguments. - * - * @return array - */ - protected function getArguments(): array + protected function addModuleToComposer(): void { - return [ - ['name', InputArgument::REQUIRED, 'The name of the module to be created.'], - ]; + $initialWorkingDir = getcwd(); + $appBasePath = $this->laravel->basePath(); + chdir($appBasePath); + $moduleRelativePath = GeneratorHelper::makeRelativePath($appBasePath, GeneratorHelper::makeModulePath($this->moduleName)); + ComposerJsonFile::create(Factory::getComposerFile()) + ->addModule($this->modulePackageName, $moduleRelativePath) + ->save(); + chdir($initialWorkingDir); + + $composerClass = Composer::class; + $composer = $this->laravel[$composerClass]; + if (! ($composer instanceof Composer)) { + throw ComposerException::make("$composerClass not registered in your app."); + } + if (! $composer->updatePackages([$this->modulePackageName], false, $this->output)) { + throw ComposerException::make("Failed to update package with composer."); + } } - protected function getOptions(): array + protected function generateComposerJsonFile(): int { - return [ -// ['api', 'a', InputOption::VALUE_NONE, 'Generate an api module (with api components, enabled by default).'], -// ['web', 'w', InputOption::VALUE_NONE, 'Generate a web module (with web components).'], - ['plain', 'p', InputOption::VALUE_NONE, 'Generate a plain module (without some components).'], - ['disabled', 'd', InputOption::VALUE_NONE, 'Do not enable the module at creation.'], - ['force', 'f', InputOption::VALUE_NONE, 'Force the operation to run when the module already exists.'], - ['entity', null, InputOption::VALUE_REQUIRED, 'Entity name (used to create module components, the default is the name of the module).'], + $path = GeneratorHelper::makeModulePath($this->moduleName, 'composer.json'); + $contents = Stub::create("composer.json.stub", [ + 'modulePackageName' => $this->modulePackageName, + 'moduleName' => $this->moduleName, + 'moduleNamespace' => str_replace( + '\\', + '\\\\', + GeneratorHelper::makeModuleNamespace($this->moduleName) + ), + 'authorName' => config('modules.composer.author.name', 'Example'), + 'authorEmail' => config('modules.composer.author.email', 'example@example.com'), + ])->render(); + + return $this->generate($path, $contents); + } + + protected function generateComponents(Module $module): int + { + $statuses = [$this->generateProviders($module)]; + + if ($this->modulePreset !== 'plain') { + $statuses[] = $this->generateBaseComponents($module); + } + + if ($this->modulePreset === 'api') { + $statuses[] = $this->generateApiComponents($module); + } + + return $this->isFailure(...$statuses) ? self::FAILURE : self::SUCCESS; + } + + protected function generateBaseComponents(Module $module): int + { + $modulePackageName = $module->getPackageName(); + $entityName = $this->getEntityName(); + $snakeEntityName = Str::snake($entityName); + $snakePluralEntityName = Str::plural($snakeEntityName); + + return $this->isFailure( + $this->call('module:make:factory', [ + 'name' => "{$entityName}Factory", + 'module' => $modulePackageName, + '--model' => $entityName, + ]), + $this->call('module:make:migration', [ + 'name' => "create_{$snakePluralEntityName}_table", + 'module' => $modulePackageName, + '--stub' => 'create', + ]), + $this->call('module:make:seeder', [ + 'name' => "{$entityName}PermissionsSeeder_1", + 'module' => $modulePackageName, + '--stub' => 'permissions', + '--model' => $entityName, + ]), + $this->call('module:make:model', [ + 'name' => $entityName, + 'module' => $modulePackageName, + '--factory' => "{$entityName}Factory", + ]), + $this->call('module:make:policy', [ + 'name' => "{$entityName}Policy", + 'module' => $modulePackageName, + '--model' => $entityName, + ]) + ) ? self::FAILURE : self::SUCCESS; + } + + protected function generateApiComponents(Module $module): int + { + $actionVerbs = ['create', 'update', 'delete', 'list', 'view']; + + $modulePackageName = $module->getPackageName(); + $entityName = $this->getEntityName(); + $pluralEntityName = Str::plural($entityName); + $camelEntityName = Str::camel($entityName); + $kebabPluralEntityName = Str::kebab($pluralEntityName); + $snakePluralEntityName = Str::snake($pluralEntityName); + + $statuses = [ + $this->call('module:make:query-wizard', [ + 'name' => "{$pluralEntityName}QueryWizard", + 'module' => $modulePackageName, + '--stub' => 'eloquent', + ]), + $this->call('module:make:query-wizard', [ + 'name' => "{$entityName}QueryWizard", + 'module' => $modulePackageName, + '--stub' => 'model', + ]), + $this->call('module:make:resource', [ + 'name' => "{$entityName}Resource", + 'module' => $modulePackageName, + '--stub' => 'single', + ]), + $this->call('module:make:dto', [ + 'name' => "Create{$entityName}DTO", + 'module' => $modulePackageName, + ]), + $this->call('module:make:dto', [ + 'name' => "Update{$entityName}DTO", + 'module' => $modulePackageName, + ]), ]; + + foreach ($actionVerbs as $actionVerb) { + $studlyActionVerb = Str::studly($actionVerb); + + $resourceClass = "{$entityName}Resource"; + $dtoClass = "{$studlyActionVerb}{$entityName}DTO"; + $routeName = 'api.' . $snakePluralEntityName . '.' . $actionVerb; + + if ($actionVerb === "list") { + $actionClass = "{$studlyActionVerb}{$pluralEntityName}Action"; + $requestClass = "{$studlyActionVerb}{$pluralEntityName}Request"; + $wizardClass = "{$pluralEntityName}QueryWizard"; + } else { + $actionClass = "{$studlyActionVerb}{$entityName}Action"; + $requestClass = "{$studlyActionVerb}{$entityName}Request"; + $wizardClass = "{$entityName}QueryWizard"; + } + $statuses[] = $this->call('module:make:action', [ + 'name' => $actionClass, + 'module' => $modulePackageName, + '--stub' => $actionVerb, + '--dto' => $dtoClass, + '--model' => $entityName, + '--request' => $requestClass, + '--resource' => $resourceClass, + '--wizard' => $wizardClass, + ]); + $statuses[] = $this->call('module:make:request', [ + 'name' => $requestClass, + 'module' => $modulePackageName, + '--stub' => $actionVerb, + '--ui' => 'api', + '--dto' => $dtoClass, + '--model' => $entityName, + ]); + + $actionMethodsMap = [ + 'create' => 'post', + 'update' => 'patch', + 'delete' => 'delete', + 'list' => 'get', + 'view' => 'get', + ]; + + $url = $kebabPluralEntityName; + if (in_array($actionVerb, ['update', 'delete', 'view'])) { + $url .= '/{' . $camelEntityName . '}'; + } + + $filePath = Str::snake(str_replace('Action', '', $actionClass), '_'); + $filePath = 'v1/' . $filePath; + + $statuses[] = $this->call('module:make:route', [ + 'name' => $filePath, + 'module' => $modulePackageName, + '--ui' => 'api', + '--action' => $actionClass, + '--method' => $actionMethodsMap[$actionVerb], + '--url' => $url, + '--name' => $routeName, + ]); + + $testClass = $actionVerb === 'list' + ? "{$studlyActionVerb}{$pluralEntityName}Test" + : "{$studlyActionVerb}{$entityName}Test"; + + $statuses[] = $this->call('module:make:test', [ + 'name' => $testClass, + 'module' => $modulePackageName, + '--stub' => $actionVerb, + '--type' => 'api', + '--model' => $entityName, + '--route' => $routeName, + ]); + } + + return $this->isFailure(...$statuses) ? self::FAILURE : self::SUCCESS; + } + + protected function generateProviders(Module $module): int + { + return $this->isFailure( + $this->call('module:make:provider', [ + 'name' => "{$module->getStudlyName()}ServiceProvider", + 'module' => $module->getPackageName(), + '--stub' => 'module', + ]), + $this->call('module:make:provider', [ + 'name' => 'RouteServiceProvider', + 'module' => $module->getPackageName(), + '--stub' => 'route', + ]) + ) ? self::FAILURE : self::SUCCESS; } /** - * Get module type. - * - * @return string - */ - private function getModuleType(): string + * @param int ...$statuses + * @return bool + */ + protected function isFailure(...$statuses): bool { - if ($this->option('plain')) { - return 'plain'; + foreach ($statuses as $status) { + if ($status !== self::SUCCESS) { + return true; + } } -// if ($this->option('web')) { -// return 'web'; -// } + return false; + } - return 'api'; + protected function validateModuleStudlyName(string $name): bool + { + return preg_match('/^[a-zA-Z_\x80-\xff][a-zA-Z0-9_\x80-\xff]*$/', $name); + } + + protected function getEntityName(): string + { + if ($this->entityName !== null) { + return $this->entityName; + } + + return $this->entityName = Str::studly($this->getOptionOrAsk( + optionName: 'entity', + question: 'Enter the entity name (used to create module components)', + default: $this->moduleStudlyName + )); + } + + /** + * Not used by ModuleMakeCommand as it fully overrides handle(). + * Required by abstract base class. + */ + protected function getContents(): string + { + return ''; } } diff --git a/src/Commands/Generators/NotificationMakeCommand.php b/src/Commands/Generators/NotificationMakeCommand.php index 9d82693..86deaf9 100644 --- a/src/Commands/Generators/NotificationMakeCommand.php +++ b/src/Commands/Generators/NotificationMakeCommand.php @@ -2,97 +2,65 @@ namespace Laraneat\Modules\Commands\Generators; -use Laraneat\Modules\Module; -use Laraneat\Modules\Support\Stub; -use Laraneat\Modules\Traits\ModuleCommandTrait; -use Symfony\Component\Console\Input\InputOption; +use Illuminate\Contracts\Console\PromptsForMissingInput; +use Laraneat\Modules\Enums\ModuleComponentType; +use Laraneat\Modules\Support\Generator\Stub; /** * @group generator */ -class NotificationMakeCommand extends ComponentGeneratorCommand +class NotificationMakeCommand extends BaseComponentGeneratorCommand implements PromptsForMissingInput { - use ModuleCommandTrait; - /** * The name and signature of the console command. * * @var string */ - protected $name = 'module:make:notification'; + protected $signature = 'module:make:notification + {name : The name of the notification class} + {module? : The name or package name of the app module} + {--s|stub= : The stub name to load for this generator} + {--force : Overwrite the file if it already exists}'; /** * The console command description. * * @var string */ - protected $description = 'Generate new notification for the specified module.'; - - /** - * The stub name to load for this generator. - * - * @var string - */ - protected string $stub = 'queued'; - - /** - * Module instance. - * - * @var Module - */ - protected Module $module; - - /** - * Component type. - * - * @var string - */ - protected string $componentType; + protected $description = 'Generate new notification class for the specified module.'; /** - * Prepared 'name' argument. - * - * @var string + * The module component type. */ - protected string $nameArgument; + protected ModuleComponentType $componentType = ModuleComponentType::Notification; /** - * Get the console command options. - * - * @return array + * Prompt for missing input arguments using the returned questions. */ - protected function getOptions(): array + protected function promptForMissingArgumentsUsing(): array { return [ - ['stub', 's', InputOption::VALUE_REQUIRED, 'The stub name to load for this generator.'], + 'name' => 'Enter the notification class name', ]; } - protected function prepare() + protected function getContents(): string { - $this->module = $this->getModule(); - $this->stub = $this->getOptionOrChoice( + $stub = $this->getOptionOrChoice( 'stub', 'Select the stub you want to use for generator', ['plain', 'queued'], - 'queued' + 'plain' ); - $this->componentType = 'notification'; - $this->nameArgument = $this->getTrimmedArgument('name'); - } - - protected function getDestinationFilePath(): string - { - return $this->getComponentPath($this->module, $this->nameArgument, $this->componentType); - } - - protected function getTemplateContents(): string - { $stubReplaces = [ - 'namespace' => $this->getComponentNamespace($this->module, $this->nameArgument, $this->componentType), - 'class' => $this->getClass($this->nameArgument) + 'namespace' => $this->getComponentNamespace( + $this->module, + $this->nameArgument, + $this->componentType + ), + 'class' => class_basename($this->nameArgument), ]; - return Stub::create("notification/{$this->stub}.stub", $stubReplaces)->render(); + return Stub::create("notification/$stub.stub", $stubReplaces)->render(); } } diff --git a/src/Commands/Generators/ObserverMakeCommand.php b/src/Commands/Generators/ObserverMakeCommand.php index a25ab2b..30963ba 100644 --- a/src/Commands/Generators/ObserverMakeCommand.php +++ b/src/Commands/Generators/ObserverMakeCommand.php @@ -2,25 +2,26 @@ namespace Laraneat\Modules\Commands\Generators; +use Illuminate\Contracts\Console\PromptsForMissingInput; use Illuminate\Support\Str; -use Laraneat\Modules\Module; -use Laraneat\Modules\Support\Stub; -use Laraneat\Modules\Traits\ModuleCommandTrait; -use Symfony\Component\Console\Input\InputOption; +use Laraneat\Modules\Enums\ModuleComponentType; +use Laraneat\Modules\Support\Generator\Stub; /** * @group generator */ -class ObserverMakeCommand extends ComponentGeneratorCommand +class ObserverMakeCommand extends BaseComponentGeneratorCommand implements PromptsForMissingInput { - use ModuleCommandTrait; - /** * The name and signature of the console command. * * @var string */ - protected $name = 'module:make:observer'; + protected $signature = 'module:make:observer + {name : The name of the observer class} + {module? : The name or package name of the app module} + {--model= : The class name of the model to be used in the observer} + {--force : Overwrite the file if it already exists}'; /** * The console command description. @@ -30,64 +31,38 @@ class ObserverMakeCommand extends ComponentGeneratorCommand protected $description = 'Generate new observer for the specified module.'; /** - * Module instance. - * - * @var Module - */ - protected Module $module; - - /** - * Component type. - * - * @var string + * The module component type. */ - protected string $componentType = 'observer'; + protected ModuleComponentType $componentType = ModuleComponentType::Observer; /** - * Prepared 'name' argument. - * - * @var string + * Prompt for missing input arguments using the returned questions. */ - protected string $nameArgument; - - /** - * Get the console command options. - * - * @return array - */ - protected function getOptions(): array + protected function promptForMissingArgumentsUsing(): array { return [ - ['model', null, InputOption::VALUE_REQUIRED, 'The class name of the model to be used in the observer.'], + 'name' => 'Enter the observer class name', ]; } - protected function prepare() - { - $this->module = $this->getModule(); - $this->nameArgument = $this->getTrimmedArgument('name'); - } - - protected function getDestinationFilePath(): string - { - return $this->getComponentPath($this->module, $this->nameArgument, $this->componentType); - } - - protected function getTemplateContents(): string + protected function getContents(): string { - $model = $this->getOptionOrAsk( - 'model', - 'Enter the class name of the model to be used in the observer', - '', - true + $modelClass = $this->getFullClassFromOptionOrAsk( + optionName: 'model', + question: 'Enter the class name of the model to be used in the observer', + componentType: ModuleComponentType::Model, + module: $this->module ); - $modelClass = $this->getClass($model); $stubReplaces = [ - 'namespace' => $this->getComponentNamespace($this->module, $this->nameArgument, $this->componentType), - 'class' => $this->getClass($this->nameArgument), - 'model' => $modelClass, - 'modelEntity' => Str::camel($modelClass), - 'modelNamespace' => $this->getComponentNamespace($this->module, $model, 'model') + 'namespace' => $this->getComponentNamespace( + $this->module, + $this->nameArgument, + $this->componentType + ), + 'class' => class_basename($this->nameArgument), + 'model' => class_basename($modelClass), + 'modelCamelCase' => Str::camel(class_basename($modelClass)), + 'modelNamespace' => $this->getNamespaceOfClass($modelClass), ]; return Stub::create("observer.stub", $stubReplaces)->render(); diff --git a/src/Commands/Generators/PolicyMakeCommand.php b/src/Commands/Generators/PolicyMakeCommand.php index 91650eb..1ef9a1d 100644 --- a/src/Commands/Generators/PolicyMakeCommand.php +++ b/src/Commands/Generators/PolicyMakeCommand.php @@ -2,25 +2,27 @@ namespace Laraneat\Modules\Commands\Generators; +use Illuminate\Contracts\Console\PromptsForMissingInput; use Illuminate\Support\Str; -use Laraneat\Modules\Module; -use Laraneat\Modules\Support\Stub; -use Laraneat\Modules\Traits\ModuleCommandTrait; -use Symfony\Component\Console\Input\InputOption; +use Laraneat\Modules\Enums\ModuleComponentType; +use Laraneat\Modules\Support\Generator\GeneratorHelper; +use Laraneat\Modules\Support\Generator\Stub; /** * @group generator */ -class PolicyMakeCommand extends ComponentGeneratorCommand +class PolicyMakeCommand extends BaseComponentGeneratorCommand implements PromptsForMissingInput { - use ModuleCommandTrait; - /** * The name and signature of the console command. * * @var string */ - protected $name = 'module:make:policy'; + protected $signature = 'module:make:policy + {name : The name of the policy} + {module? : The name or package name of the app module} + {--model= : The class name of the model to be used in the policy} + {--force : Overwrite the file if it already exists}'; /** * The console command description. @@ -30,94 +32,56 @@ class PolicyMakeCommand extends ComponentGeneratorCommand protected $description = 'Generate new policy for the specified module.'; /** - * The stub name to load for this generator. - * - * @var string - */ - protected string $stub = 'full'; - - /** - * Module instance. - * - * @var Module - */ - protected Module $module; - - /** - * Component type. - * - * @var string - */ - protected string $componentType; - - /** - * Prepared 'name' argument. - * - * @var string + * The module component type. */ - protected string $nameArgument; + protected ModuleComponentType $componentType = ModuleComponentType::Policy; /** - * Get the console command options. - * - * @return array + * Prompt for missing input arguments using the returned questions. */ - protected function getOptions(): array + protected function promptForMissingArgumentsUsing(): array { return [ - ['stub', 's', InputOption::VALUE_REQUIRED, 'The stub name to load for this generator.'], - ['model', null, InputOption::VALUE_REQUIRED, 'The class name of the model.'], + 'name' => 'Enter the policy class name', ]; } - protected function prepare() - { - $this->module = $this->getModule(); - $this->stub = $this->getOptionOrChoice( - 'stub', - 'Select the stub you want to use for generator', - ['plain', 'full'], - 'full' - ); - $this->componentType = 'policy'; - $this->nameArgument = $this->getTrimmedArgument('name'); - } - - protected function getDestinationFilePath(): string - { - return $this->getComponentPath($this->module, $this->nameArgument, $this->componentType); - } - - protected function getTemplateContents(): string + protected function getContents(): string { + $stub = 'plain'; $stubReplaces = [ 'namespace' => $this->getComponentNamespace($this->module, $this->nameArgument, $this->componentType), - 'class' => $this->getClass($this->nameArgument) + 'class' => class_basename($this->nameArgument), ]; - if ($this->stub === 'full') { - $model = $this->getOptionOrAsk( - 'model', - 'Enter the class name of the model', - '', - true + $modelOption = $this->getOptionOrAsk( + 'model', + question: 'Enter the class name of the model to be used in the policy (optional)', + required: false + ); + + if ($modelOption) { + $modelClass = $this->getFullClass( + $modelOption, + GeneratorHelper::component(ModuleComponentType::Model) + ->getFullNamespace($this->module) ); - $stubReplaces['model'] = $this->getClass($model); - $stubReplaces['modelEntity'] = Str::camel($stubReplaces['model']); - if ($stubReplaces['modelEntity'] === 'user') { - $stubReplaces['modelEntity'] = 'model'; + $stub = 'full'; + $stubReplaces['model'] = class_basename($modelClass); + $stubReplaces['modelNamespace'] = $this->getNamespaceOfClass($modelClass); + $stubReplaces['modelKebabCase'] = Str::kebab($stubReplaces['model']); + $stubReplaces['modelsKebabCase'] = Str::kebab(Str::plural($stubReplaces['model'])); + $stubReplaces['modelCamelCase'] = Str::camel($stubReplaces['model']); + if ($stubReplaces['modelCamelCase'] === 'user') { + $stubReplaces['modelCamelCase'] = 'model'; } - $stubReplaces['modelPermissionEntity'] = Str::snake($stubReplaces['model'], '-'); - $stubReplaces['modelPermissionEntities'] = Str::snake(Str::plural($stubReplaces['model']), '-'); - $stubReplaces['modelNamespace'] = $this->getComponentNamespace($this->module, $model, 'model'); $fullUserClass = $this->getUserModelClass(); - - $stubReplaces['user'] = $this->getClass($fullUserClass); + $stubReplaces['user'] = class_basename($fullUserClass); $stubReplaces['userNamespace'] = $this->getNamespaceOfClass($fullUserClass); } - return Stub::create("policy/{$this->stub}.stub", $stubReplaces)->render(); + return Stub::create("policy/$stub.stub", $stubReplaces)->render(); } } diff --git a/src/Commands/Generators/ProviderMakeCommand.php b/src/Commands/Generators/ProviderMakeCommand.php index 7ce6044..f2f4fe1 100644 --- a/src/Commands/Generators/ProviderMakeCommand.php +++ b/src/Commands/Generators/ProviderMakeCommand.php @@ -2,26 +2,27 @@ namespace Laraneat\Modules\Commands\Generators; -use Laraneat\Modules\Facades\Modules; -use Laraneat\Modules\Module; +use Illuminate\Contracts\Console\PromptsForMissingInput; +use Laraneat\Modules\Enums\ModuleComponentType; use Laraneat\Modules\Support\Generator\GeneratorHelper; -use Laraneat\Modules\Support\Stub; -use Laraneat\Modules\Traits\ModuleCommandTrait; -use Symfony\Component\Console\Input\InputOption; +use Laraneat\Modules\Support\Generator\Stub; +use Laraneat\Modules\Support\ModuleConfigWriter; /** * @group generator */ -class ProviderMakeCommand extends ComponentGeneratorCommand +class ProviderMakeCommand extends BaseComponentGeneratorCommand implements PromptsForMissingInput { - use ModuleCommandTrait; - /** * The name and signature of the console command. * * @var string */ - protected $name = 'module:make:provider'; + protected $signature = 'module:make:provider + {name : The name of the provider class} + {module? : The name or package name of the app module} + {--s|stub= : The stub name to load for this generator} + {--force : Overwrite the file if it already exists}'; /** * The console command description. @@ -31,96 +32,98 @@ class ProviderMakeCommand extends ComponentGeneratorCommand protected $description = 'Generate new provider for the specified module.'; /** - * The stub name to load for this generator. - */ - protected string $stub = 'plain'; - - /** - * Module instance. + * The module component type. */ - protected Module $module; + protected ModuleComponentType $componentType = ModuleComponentType::Provider; /** - * Component type. + * Selected stub name (cached for afterGenerate). */ - protected string $componentType; + protected string $selectedStub; /** - * Prepared 'name' argument. + * Stub replacements (cached for afterGenerate). */ - protected string $nameArgument; + protected array $stubReplaces; /** - * Get the console command options. + * Prompt for missing input arguments using the returned questions. */ - protected function getOptions(): array + protected function promptForMissingArgumentsUsing(): array { return [ - ['stub', 's', InputOption::VALUE_REQUIRED, 'The stub name to load for this generator.'], + 'name' => 'Enter the provider class name', ]; } - protected function prepare(): void + protected function getContents(): string { - $this->module = $this->getModule(); - $this->stub = $this->getOptionOrChoice( + $this->selectedStub = $this->getOptionOrChoice( 'stub', 'Select the stub you want to use for generator', - ['plain', 'module', 'route', 'event'], + ['plain', 'module', 'event', 'route'], 'plain' ); - $this->componentType = 'provider'; - $this->nameArgument = $this->getTrimmedArgument('name'); - } - - protected function getDestinationFilePath(): string - { - return $this->getComponentPath($this->module, $this->nameArgument, $this->componentType); - } - protected function getTemplateContents(): string - { - $stubReplaces = [ - 'namespace' => $this->getComponentNamespace($this->module, $this->nameArgument, $this->componentType), - 'class' => $this->getClass($this->nameArgument) + $this->stubReplaces = [ + 'namespace' => $this->getComponentNamespace( + $this->module, + $this->nameArgument, + $this->componentType + ), + 'class' => class_basename($this->nameArgument), ]; - if ($this->stub === 'module') { - $stubReplaces = array_merge($stubReplaces, [ - 'moduleName' => $this->module->getStudlyName(), - 'moduleKey' => $this->module->getKey(), - 'commandsPath' => GeneratorHelper::component('cli-command')->getPath(), - 'langPath' => GeneratorHelper::component('lang')->getPath(), - 'configPath' => GeneratorHelper::component('config')->getPath(), - 'viewsPath' => GeneratorHelper::component('view')->getPath(), - 'migrationsPath' => GeneratorHelper::component('migration')->getPath(), + $providerDir = GeneratorHelper::component(ModuleComponentType::Provider)->getFullPath($this->module); + + if ($this->selectedStub === 'module') { + $this->stubReplaces = array_merge($this->stubReplaces, [ + 'modulePackageName' => $this->module->getPackageName(), + 'moduleNameKebabCase' => $this->module->getKebabName(), + 'commandsNamespace' => str_replace('\\', '\\\\', GeneratorHelper::component(ModuleComponentType::CliCommand)->getFullNamespace($this->module)), + 'commandsPath' => GeneratorHelper::makeRelativePath( + $providerDir, + GeneratorHelper::component(ModuleComponentType::CliCommand)->getFullPath($this->module) + ), + 'configPath' => GeneratorHelper::makeRelativePath( + $providerDir, + GeneratorHelper::component(ModuleComponentType::Config)->getFullPath($this->module) + ), + 'langPath' => GeneratorHelper::makeRelativePath( + $providerDir, + GeneratorHelper::component(ModuleComponentType::Lang)->getFullPath($this->module) + ), + 'viewsPath' => GeneratorHelper::makeRelativePath( + $providerDir, + GeneratorHelper::component(ModuleComponentType::View)->getFullPath($this->module) + ), + 'migrationsPath' => GeneratorHelper::makeRelativePath( + $providerDir, + GeneratorHelper::component(ModuleComponentType::Migration)->getFullPath($this->module) + ), ]); - } elseif ($this->stub === 'route') { - $stubReplaces = array_merge($stubReplaces, [ - 'moduleName' => $this->module->getStudlyName(), - 'webControllerNamespace' => str_replace('\\', '\\\\', GeneratorHelper::component('web-controller')->getFullNamespace($this->module)), - 'apiControllerNamespace' => str_replace('\\', '\\\\', GeneratorHelper::component('api-controller')->getFullNamespace($this->module)), - 'webRoutesPath' => GeneratorHelper::component('web-route')->getPath(), - 'apiRoutesPath' => GeneratorHelper::component('api-route')->getPath() + } elseif ($this->selectedStub === 'route') { + $this->stubReplaces = array_merge($this->stubReplaces, [ + 'modulePackageName' => $this->module->getPackageName(), + 'webControllerNamespace' => str_replace('\\', '\\\\', GeneratorHelper::component(ModuleComponentType::WebController)->getFullNamespace($this->module)), + 'apiControllerNamespace' => str_replace('\\', '\\\\', GeneratorHelper::component(ModuleComponentType::ApiController)->getFullNamespace($this->module)), + 'webRoutesPath' => GeneratorHelper::makeRelativePath( + $providerDir, + GeneratorHelper::component(ModuleComponentType::WebRoute)->getFullPath($this->module) + ), + 'apiRoutesPath' => GeneratorHelper::makeRelativePath( + $providerDir, + GeneratorHelper::component(ModuleComponentType::ApiRoute)->getFullPath($this->module) + ), ]); } - $this->addProviderClassToModuleJson($stubReplaces['namespace'] . '\\' . $stubReplaces['class']); - - return Stub::create("provider/{$this->stub}.stub", $stubReplaces)->render(); + return Stub::create("provider/{$this->selectedStub}.stub", $this->stubReplaces)->render(); } - protected function addProviderClassToModuleJson(string $providerClass): void + protected function afterGenerate(): void { - $json = $this->module->json(); - $providers = $json->get('providers'); - if (! is_array($providers)) { - $providers = []; - } - $providers[] = $providerClass; - $json->set('providers', $providers) - ->save(); - - Modules::flushCache(); + $this->laravel->make(ModuleConfigWriter::class) + ->addProvider($this->module, $this->stubReplaces['namespace'] . '\\' . $this->stubReplaces['class']); } } diff --git a/src/Commands/Generators/QueryWizardMakeCommand.php b/src/Commands/Generators/QueryWizardMakeCommand.php index 98dd9d9..79dac50 100644 --- a/src/Commands/Generators/QueryWizardMakeCommand.php +++ b/src/Commands/Generators/QueryWizardMakeCommand.php @@ -2,97 +2,71 @@ namespace Laraneat\Modules\Commands\Generators; -use Laraneat\Modules\Module; -use Laraneat\Modules\Support\Stub; -use Laraneat\Modules\Traits\ModuleCommandTrait; -use Symfony\Component\Console\Input\InputOption; +use Illuminate\Contracts\Console\PromptsForMissingInput; +use Laraneat\Modules\Enums\ModuleComponentType; +use Laraneat\Modules\Support\Generator\Stub; /** * @group generator */ -class QueryWizardMakeCommand extends ComponentGeneratorCommand +class QueryWizardMakeCommand extends BaseComponentGeneratorCommand implements PromptsForMissingInput { - use ModuleCommandTrait; - /** * The name and signature of the console command. * * @var string */ - protected $name = 'module:make:wizard'; + protected $signature = 'module:make:query-wizard + {name : The name of the query-wizard class} + {module? : The name or package name of the app module} + {--s|stub= : The stub name to load for this generator} + {--force : Overwrite the file if it already exists}'; /** * The console command description. * * @var string */ - protected $description = 'Generate new query-wizard for the specified module.'; - - /** - * The stub name to load for this generator. - * - * @var string - */ - protected string $stub = 'eloquent'; - - /** - * Module instance. - * - * @var Module - */ - protected Module $module; - - /** - * Component type. - * - * @var string - */ - protected string $componentType; + protected $description = 'Generate new query-wizard class for the specified module.'; /** - * Prepared 'name' argument. - * - * @var string + * The module component type. */ - protected string $nameArgument; + protected ModuleComponentType $componentType = ModuleComponentType::ApiQueryWizard; /** - * Get the console command options. - * - * @return array + * Prompt for missing input arguments using the returned questions. */ - protected function getOptions(): array + protected function promptForMissingArgumentsUsing(): array { return [ - ['stub', 's', InputOption::VALUE_REQUIRED, 'The stub name to load for this generator.'], + 'name' => 'Enter the query-wizard class name', ]; } - protected function prepare() + protected function beforeGenerate(): void { - $this->module = $this->getModule(); - $this->stub = $this->getOptionOrChoice( + $this->ensurePackageIsInstalledOrWarn('jackardios/laravel-query-wizard'); + } + + protected function getContents(): string + { + $stub = $this->getOptionOrChoice( 'stub', 'Select the stub you want to use for generator', - ['eloquent', 'model', 'scout', 'elastic'], + ['eloquent', 'model'], 'eloquent' ); - $this->componentType = 'api-query-wizard'; - $this->nameArgument = $this->getTrimmedArgument('name'); - } - - protected function getDestinationFilePath(): string - { - return $this->getComponentPath($this->module, $this->nameArgument, $this->componentType); - } - protected function getTemplateContents(): string - { $stubReplaces = [ - 'namespace' => $this->getComponentNamespace($this->module, $this->nameArgument, $this->componentType), - 'class' => $this->getClass($this->nameArgument) + 'namespace' => $this->getComponentNamespace( + $this->module, + $this->nameArgument, + $this->componentType + ), + 'class' => class_basename($this->nameArgument), ]; - return Stub::create("query-wizard/{$this->stub}.stub", $stubReplaces)->render(); + return Stub::create("query-wizard/$stub.stub", $stubReplaces)->render(); } } diff --git a/src/Commands/Generators/RequestMakeCommand.php b/src/Commands/Generators/RequestMakeCommand.php index d35a5eb..88c6f54 100644 --- a/src/Commands/Generators/RequestMakeCommand.php +++ b/src/Commands/Generators/RequestMakeCommand.php @@ -2,25 +2,29 @@ namespace Laraneat\Modules\Commands\Generators; +use Illuminate\Contracts\Console\PromptsForMissingInput; use Illuminate\Support\Str; -use Laraneat\Modules\Module; -use Laraneat\Modules\Support\Stub; -use Laraneat\Modules\Traits\ModuleCommandTrait; -use Symfony\Component\Console\Input\InputOption; +use Laraneat\Modules\Enums\ModuleComponentType; +use Laraneat\Modules\Support\Generator\Stub; /** * @group generator */ -class RequestMakeCommand extends ComponentGeneratorCommand +class RequestMakeCommand extends BaseComponentGeneratorCommand implements PromptsForMissingInput { - use ModuleCommandTrait; - /** * The name and signature of the console command. * * @var string */ - protected $name = 'module:make:request'; + protected $signature = 'module:make:request + {name : The name of the request} + {module? : The name or package name of the app module} + {--s|stub= : The stub name to load for this generator} + {--ui= : The UI for which the request will be created} + {--dto= : The class name of the DTO to be used in the request} + {--model= : The class name of the model to be used in the request} + {--force : Overwrite the file if it already exists}'; /** * The console command description. @@ -31,111 +35,75 @@ class RequestMakeCommand extends ComponentGeneratorCommand /** * The UI for which the request will be created. - * - * @var string - */ - protected string $ui = 'api'; - - /** - * The stub name to load for this generator - * - * @var string - */ - protected string $stub = 'plain'; - - /** - * Module instance. - * - * @var Module - */ - protected Module $module; - - /** - * Component type. - * - * @var string - */ - protected string $componentType; - - /** - * Prepared 'name' argument. - * - * @var string + * ('web' or 'api') */ - protected string $nameArgument; + protected string $ui; /** - * Get the console command options. - * - * @return array + * Prompt for missing input arguments using the returned questions. */ - protected function getOptions(): array + protected function promptForMissingArgumentsUsing(): array { return [ - ['ui', null, InputOption::VALUE_REQUIRED, 'The UI for which the request will be created.'], - ['stub', 's', InputOption::VALUE_REQUIRED, 'The stub name to load for this generator.'], - ['dto', null, InputOption::VALUE_REQUIRED, 'The class name of the DTO to be used in the request.'], - ['model', null, InputOption::VALUE_REQUIRED, 'The class name of the model to be used in the request.'], + 'name' => 'Enter the request class name', ]; } - protected function prepare() + protected function beforeGenerate(): void { - $this->module = $this->getModule(); $this->ui = $this->getOptionOrChoice( 'ui', - 'Select the UI for which the request will be created', - ['api', 'web'], - 'api' + question: 'Enter the UI for which the request will be created', + choices: ['api', 'web'], + default: 'api' ); - $stubChoices = ($this->ui === "web") - ? ['plain', 'create', 'delete', 'update'] - : ['plain', 'create', 'delete', 'list', 'update', 'view']; - $this->stub = $this->getOptionOrChoice( - 'stub', - 'Select the stub you want to use for generator', - $stubChoices, - 'plain' - ); - $this->componentType = "{$this->ui}-request"; - $this->nameArgument = $this->getTrimmedArgument('name'); + $this->componentType = $this->ui === 'api' + ? ModuleComponentType::ApiRequest + : ModuleComponentType::WebRequest; } - protected function getDestinationFilePath(): string - { - return $this->getComponentPath($this->module, $this->nameArgument, $this->componentType); - } - - protected function getTemplateContents(): string + protected function getContents(): string { + $stub = $this->getOptionOrChoice( + optionName: 'stub', + question: 'Select the stub you want to use for generator', + choices: ($this->ui === "web") + ? ['plain', 'create', 'delete', 'update'] + : ['plain', 'create', 'delete', 'list', 'update', 'view'], + default: 'plain' + ); $stubReplaces = [ - 'namespace' => $this->getComponentNamespace($this->module, $this->nameArgument, $this->componentType), - 'class' => $this->getClass($this->nameArgument) + 'namespace' => $this->getComponentNamespace( + $this->module, + $this->nameArgument, + $this->componentType + ), + 'class' => class_basename($this->nameArgument), ]; - if ($this->stub !== 'plain') { - if ($this->stub === 'create' || $this->stub === 'update') { - $dto = $this->getOptionOrAsk( - 'dto', - 'Enter the class name of the DTO to be used in the request', - '', - true + if ($stub !== 'plain') { + if ($stub === 'create' || $stub === 'update') { + $dtoClass = $this->getFullClassFromOptionOrAsk( + optionName: 'dto', + question: 'Enter the class name of the DTO to be used in the request', + componentType: ModuleComponentType::Dto, + module: $this->module ); - $stubReplaces['dto'] = $this->getClass($dto); - $stubReplaces['dtoNamespace'] = $this->getComponentNamespace($this->module, $dto, 'dto'); + $stubReplaces['dto'] = class_basename($dtoClass); + $stubReplaces['dtoNamespace'] = $this->getNamespaceOfClass($dtoClass); } - $model = $this->getOptionOrAsk( - 'model', - 'Enter the class name of the model to be used in the request', - '', - true + $modelClass = $this->getFullClassFromOptionOrAsk( + optionName: 'model', + question: 'Enter the class name of the model to be used in the request', + componentType: ModuleComponentType::Model, + module: $this->module ); - $stubReplaces['model'] = $this->getClass($model); - $stubReplaces['modelEntity'] = Str::camel($stubReplaces['model']); - $stubReplaces['modelNamespace'] = $this->getComponentNamespace($this->module, $model, 'model'); + $stubReplaces['model'] = class_basename($modelClass); + $stubReplaces['modelCamelCase'] = Str::camel($stubReplaces['model']); + $stubReplaces['modelNamespace'] = $this->getNamespaceOfClass($modelClass); } - return Stub::create("request/{$this->stub}.stub", $stubReplaces)->render(); + return Stub::create("request/$stub.stub", $stubReplaces)->render(); } } diff --git a/src/Commands/Generators/ResourceMakeCommand.php b/src/Commands/Generators/ResourceMakeCommand.php index dd0f0a9..7abae6d 100644 --- a/src/Commands/Generators/ResourceMakeCommand.php +++ b/src/Commands/Generators/ResourceMakeCommand.php @@ -2,97 +2,66 @@ namespace Laraneat\Modules\Commands\Generators; -use Laraneat\Modules\Module; -use Laraneat\Modules\Support\Stub; -use Laraneat\Modules\Traits\ModuleCommandTrait; -use Symfony\Component\Console\Input\InputOption; +use Illuminate\Contracts\Console\PromptsForMissingInput; +use Laraneat\Modules\Enums\ModuleComponentType; +use Laraneat\Modules\Support\Generator\Stub; /** * @group generator */ -class ResourceMakeCommand extends ComponentGeneratorCommand +class ResourceMakeCommand extends BaseComponentGeneratorCommand implements PromptsForMissingInput { - use ModuleCommandTrait; - /** * The name and signature of the console command. * * @var string */ - protected $name = 'module:make:resource'; + protected $signature = 'module:make:resource + {name : The name of the resource class} + {module? : The name or package name of the app module} + {--s|stub= : The stub name to load for this generator} + {--force : Overwrite the file if it already exists}'; /** * The console command description. * * @var string */ - protected $description = 'Generate new API resource for the specified module.'; - - /** - * The stub name to load for this generator. - * - * @var string - */ - protected string $stub = 'single'; + protected $description = 'Generate new resource class for the specified module.'; /** - * Module instance. - * - * @var Module + * The module component type. */ - protected Module $module; + protected ModuleComponentType $componentType = ModuleComponentType::ApiResource; /** - * Component type. - * - * @var string + * Prompt for missing input arguments using the returned questions. */ - protected string $componentType; - - /** - * Prepared 'name' argument. - * - * @var string - */ - protected string $nameArgument; - - /** - * Get the console command options. - * - * @return array - */ - protected function getOptions(): array + protected function promptForMissingArgumentsUsing(): array { return [ - ['stub', 's', InputOption::VALUE_REQUIRED, 'The stub name to load for this generator.'], + 'name' => 'Enter the resource class name', ]; } - protected function prepare() + protected function getContents(): string { - $this->module = $this->getModule(); - $this->stub = $this->getOptionOrChoice( + $stub = $this->getOptionOrChoice( 'stub', 'Select the stub you want to use for generator', ['single', 'collection'], - 'single' + 'eloquent' ); - $this->componentType = 'api-resource'; - $this->nameArgument = $this->getTrimmedArgument('name'); - } - protected function getDestinationFilePath(): string - { - return $this->getComponentPath($this->module, $this->nameArgument, $this->componentType); - } - - protected function getTemplateContents(): string - { $stubReplaces = [ - 'namespace' => $this->getComponentNamespace($this->module, $this->nameArgument, $this->componentType), - 'class' => $this->getClass($this->nameArgument) + 'namespace' => $this->getComponentNamespace( + $this->module, + $this->nameArgument, + $this->componentType + ), + 'class' => class_basename($this->nameArgument), ]; - return Stub::create("resource/{$this->stub}.stub", $stubReplaces)->render(); + return Stub::create("resource/$stub.stub", $stubReplaces)->render(); } } diff --git a/src/Commands/Generators/RouteMakeCommand.php b/src/Commands/Generators/RouteMakeCommand.php index 2865345..e34618d 100644 --- a/src/Commands/Generators/RouteMakeCommand.php +++ b/src/Commands/Generators/RouteMakeCommand.php @@ -2,25 +2,30 @@ namespace Laraneat\Modules\Commands\Generators; +use Illuminate\Contracts\Console\PromptsForMissingInput; use Illuminate\Support\Str; -use Laraneat\Modules\Module; -use Laraneat\Modules\Support\Stub; -use Laraneat\Modules\Traits\ModuleCommandTrait; -use Symfony\Component\Console\Input\InputOption; +use Laraneat\Modules\Enums\ModuleComponentType; +use Laraneat\Modules\Support\Generator\Stub; /** * @group generator */ -class RouteMakeCommand extends ComponentGeneratorCommand +class RouteMakeCommand extends BaseComponentGeneratorCommand implements PromptsForMissingInput { - use ModuleCommandTrait; - /** * The name and signature of the console command. * * @var string */ - protected $name = 'module:make:route'; + protected $signature = 'module:make:route + {name : The name of the route} + {module? : The name or package name of the app module} + {--ui= : The UI for which the route will be created} + {--action= : The class name of the action to be used in the route} + {--method= : HTTP request method} + {--url= : Route URL} + {--name= : Route name} + {--force : Overwrite the file if it already exists}'; /** * The console command description. @@ -30,74 +35,39 @@ class RouteMakeCommand extends ComponentGeneratorCommand protected $description = 'Generate new route for the specified module.'; /** - * Module instance. - * - * @var Module - */ - protected Module $module; - - /** - * Component type. - * - * @var string - */ - protected string $componentType; - - /** - * The UI for which the request will be created. - * - * @var string - */ - protected string $ui = 'api'; - - /** - * Prepared 'name' argument. - * - * @var string + * The UI for which the route will be created. + * ('web' or 'api') */ - protected string $nameArgument; + protected string $ui; /** - * Get the console command options. - * - * @return array + * Prompt for missing input arguments using the returned questions. */ - protected function getOptions(): array + protected function promptForMissingArgumentsUsing(): array { return [ - ['ui', null, InputOption::VALUE_REQUIRED, 'The UI for which the route will be created.'], - ['action', null, InputOption::VALUE_REQUIRED, 'The class name of the action to be used in the route.'], - ['method', 'm', InputOption::VALUE_REQUIRED, 'HTTP request method.'], - ['url', 'u', InputOption::VALUE_REQUIRED, 'Route URL.'], - ['name', null, InputOption::VALUE_REQUIRED, 'Route name.'], + 'name' => 'Enter the route class name', ]; } - protected function prepare() + protected function beforeGenerate(): void { - $this->module = $this->getModule(); $this->ui = $this->getOptionOrChoice( 'ui', - 'Select the UI for which the request will be created', - ['api', 'web'], - 'api' + question: 'Enter the UI for which the route will be created', + choices: ['api', 'web'], + default: 'api' ); - $this->componentType = "{$this->ui}-route"; - $this->nameArgument = $this->getTrimmedArgument('name'); + $this->componentType = $this->ui === 'api' + ? ModuleComponentType::ApiRoute + : ModuleComponentType::WebRoute; } - protected function getDestinationFilePath(): string - { - return $this->getComponentPath($this->module, $this->nameArgument, $this->componentType); - } - - protected function getTemplateContents(): string + protected function getContents(): string { $url = $this->getOptionOrAsk( 'url', 'Enter the route URL', - '', - true ); $method = $this->getOptionOrChoice( 'method', @@ -105,25 +75,24 @@ protected function getTemplateContents(): string ['get', 'post', 'put', 'patch', 'delete', 'options'], 'get' ); - $action = $this->getOptionOrAsk( - 'action', - 'Enter the class name of the action to be used in the route', - $this->generateDefaultActionName($url, $method), - true - ); $name = $this->getOptionOrAsk( 'name', 'Enter the route name', $this->generateDefaultRouteName($url, $method), - true + ); + $actionClass = $this->getFullClassFromOptionOrAsk( + optionName: 'action', + question: 'Enter the class name of the action to be used in the route', + componentType: ModuleComponentType::Action, + module: $this->module ); $stubReplaces = [ - 'actionNamespace' => $this->getComponentNamespace($this->module, $action, 'action'), - 'action' => $this->getClass($action), 'method' => $method, 'url' => $url, 'name' => $name, ]; + $stubReplaces['action'] = class_basename($actionClass); + $stubReplaces['actionNamespace'] = $this->getNamespaceOfClass($actionClass); return Stub::create("route.stub", $stubReplaces)->render(); } @@ -151,6 +120,7 @@ protected function generateDefaultRouteName(string $url, string $method): string { $verb = $this->recognizeActionVerbByMethod($url, $method); $resource = Str::snake($this->recognizeResourceByUrl($url)); + return Str::lower($this->ui) . '.' . $resource . '.' . $verb; } diff --git a/src/Commands/Generators/RuleMakeCommand.php b/src/Commands/Generators/RuleMakeCommand.php index de35145..2853588 100644 --- a/src/Commands/Generators/RuleMakeCommand.php +++ b/src/Commands/Generators/RuleMakeCommand.php @@ -2,68 +2,56 @@ namespace Laraneat\Modules\Commands\Generators; -use Laraneat\Modules\Module; -use Laraneat\Modules\Support\Stub; -use Laraneat\Modules\Traits\ModuleCommandTrait; +use Illuminate\Contracts\Console\PromptsForMissingInput; +use Laraneat\Modules\Enums\ModuleComponentType; +use Laraneat\Modules\Support\Generator\Stub; /** * @group generator */ -class RuleMakeCommand extends ComponentGeneratorCommand +class RuleMakeCommand extends BaseComponentGeneratorCommand implements PromptsForMissingInput { - use ModuleCommandTrait; - /** * The name and signature of the console command. * * @var string */ - protected $name = 'module:make:rule'; + protected $signature = 'module:make:rule + {name : The name of the rule class} + {module? : The name or package name of the app module} + {--force : Overwrite the file if it already exists}'; /** * The console command description. * * @var string */ - protected $description = 'Generate new rule for the specified module.'; + protected $description = 'Generate new rule class for the specified module.'; /** - * Module instance. - * - * @var Module - */ - protected Module $module; - - /** - * Component type. - * - * @var string + * The module component type. */ - protected string $componentType = 'rule'; + protected ModuleComponentType $componentType = ModuleComponentType::Rule; /** - * Prepared 'name' argument. - * - * @var string + * Prompt for missing input arguments using the returned questions. */ - protected string $nameArgument; - - protected function prepare() + protected function promptForMissingArgumentsUsing(): array { - $this->module = $this->getModule(); - $this->nameArgument = $this->getTrimmedArgument('name'); - } - - protected function getDestinationFilePath(): string - { - return $this->getComponentPath($this->module, $this->nameArgument, $this->componentType); + return [ + 'name' => 'Enter the rule class name', + ]; } - protected function getTemplateContents(): string + protected function getContents(): string { $stubReplaces = [ - 'namespace' => $this->getComponentNamespace($this->module, $this->nameArgument, $this->componentType), - 'class' => $this->getClass($this->nameArgument), + 'namespace' => $this->getComponentNamespace( + $this->module, + $this->nameArgument, + $this->componentType + ), + 'class' => class_basename($this->nameArgument), ]; return Stub::create("rule.stub", $stubReplaces)->render(); diff --git a/src/Commands/Generators/SeederMakeCommand.php b/src/Commands/Generators/SeederMakeCommand.php index bda2cd0..32a538f 100644 --- a/src/Commands/Generators/SeederMakeCommand.php +++ b/src/Commands/Generators/SeederMakeCommand.php @@ -2,25 +2,27 @@ namespace Laraneat\Modules\Commands\Generators; +use Illuminate\Contracts\Console\PromptsForMissingInput; use Illuminate\Support\Str; -use Laraneat\Modules\Module; -use Laraneat\Modules\Support\Stub; -use Laraneat\Modules\Traits\ModuleCommandTrait; -use Symfony\Component\Console\Input\InputOption; +use Laraneat\Modules\Enums\ModuleComponentType; +use Laraneat\Modules\Support\Generator\Stub; /** * @group generator */ -class SeederMakeCommand extends ComponentGeneratorCommand +class SeederMakeCommand extends BaseComponentGeneratorCommand implements PromptsForMissingInput { - use ModuleCommandTrait; - /** * The name and signature of the console command. * * @var string */ - protected $name = 'module:make:seeder'; + protected $signature = 'module:make:seeder + {name : The name of the seeder} + {module? : The name or package name of the app module} + {--s|stub= : The stub name to load for this generator} + {--model= : The class name of the model to be used in the seeder} + {--force : Overwrite the file if it already exists}'; /** * The console command description. @@ -30,92 +32,53 @@ class SeederMakeCommand extends ComponentGeneratorCommand protected $description = 'Generate new seeder for the specified module.'; /** - * The stub name to load for this generator. - * - * @var string - */ - protected string $stub = 'plain'; - - /** - * Module instance. - * - * @var Module - */ - protected Module $module; - - /** - * Component type. - * - * @var string - */ - protected string $componentType; - - /** - * Prepared 'name' argument. - * - * @var string + * The module component type. */ - protected string $nameArgument; + protected ModuleComponentType $componentType = ModuleComponentType::Seeder; /** - * Get the console command options. - * - * @return array + * Prompt for missing input arguments using the returned questions. */ - protected function getOptions(): array + protected function promptForMissingArgumentsUsing(): array { return [ - ['stub', 's', InputOption::VALUE_REQUIRED, 'The stub name to load for this generator.'], - ['model', null, InputOption::VALUE_REQUIRED, 'The class name of the model to be used in the seeder.'], + 'name' => 'Enter the seeder class name', ]; } - protected function prepare() + protected function getContents(): string { - $this->module = $this->getModule(); - $this->stub = $this->getOptionOrChoice( + $stub = $this->getOptionOrChoice( 'stub', 'Select the stub you want to use for generator', ['plain', 'permissions'], 'plain' ); - $this->componentType = 'seeder'; - $this->nameArgument = $this->getTrimmedArgument('name'); - } - - protected function getDestinationFilePath(): string - { - return $this->getComponentPath($this->module, $this->nameArgument, $this->componentType); - } - - protected function getTemplateContents(): string - { $stubReplaces = [ 'namespace' => $this->getComponentNamespace($this->module, $this->nameArgument, $this->componentType), - 'class' => $this->getClass($this->nameArgument) + 'class' => class_basename($this->nameArgument), ]; - if ($this->stub === 'permissions') { - $createPermissionAction = $this->getCreatePermissionActionClass(); - $stubReplaces['createPermissionAction'] = $this->getClass($createPermissionAction); - $stubReplaces['actionNamespace'] = $this->getNamespaceOfClass($createPermissionAction); + if ($stub === 'permissions') { + $createPermissionActionClass = $this->getCreatePermissionActionClass(); + $stubReplaces['createPermissionAction'] = class_basename($createPermissionActionClass); + $stubReplaces['createPermissionActionNamespace'] = $this->getNamespaceOfClass($createPermissionActionClass); - $createPermissionDTO = $this->getCreatePermissionDTOClass(); - $stubReplaces['createPermissionDTO'] = $this->getClass($createPermissionDTO); - $stubReplaces['dtoNamespace'] = $this->getNamespaceOfClass($createPermissionDTO); + $createPermissionDTOClass = $this->getCreatePermissionDTOClass(); + $stubReplaces['createPermissionDTO'] = class_basename($createPermissionDTOClass); + $stubReplaces['createPermissionDTONamespace'] = $this->getNamespaceOfClass($createPermissionDTOClass); - $model = $this->getOptionOrAsk( - 'model', - 'Enter the class name of the model to be used in the seeder', - '', - true + $modelClass = $this->getFullClassFromOptionOrAsk( + optionName: 'model', + question: 'Enter the class name of the model to be used in the seeder', + componentType: ModuleComponentType::Model, + module: $this->module ); - $modelClass = $this->getClass($model); - $stubReplaces['modelPermissionEntity'] = Str::snake($modelClass, '-'); - $stubReplaces['modelPermissionEntities'] = Str::plural($stubReplaces['modelPermissionEntity']); + $stubReplaces['modelKebabCase'] = Str::kebab(class_basename($modelClass)); + $stubReplaces['modelsKebabCase'] = Str::plural($stubReplaces['modelKebabCase']); } - return Stub::create("seeder/{$this->stub}.stub", $stubReplaces)->render(); + return Stub::create("seeder/$stub.stub", $stubReplaces)->render(); } } diff --git a/src/Commands/Generators/TestMakeCommand.php b/src/Commands/Generators/TestMakeCommand.php index 7617bb9..37395e3 100644 --- a/src/Commands/Generators/TestMakeCommand.php +++ b/src/Commands/Generators/TestMakeCommand.php @@ -2,25 +2,29 @@ namespace Laraneat\Modules\Commands\Generators; +use Illuminate\Contracts\Console\PromptsForMissingInput; use Illuminate\Support\Str; -use Laraneat\Modules\Module; -use Laraneat\Modules\Support\Stub; -use Laraneat\Modules\Traits\ModuleCommandTrait; -use Symfony\Component\Console\Input\InputOption; +use Laraneat\Modules\Enums\ModuleComponentType; +use Laraneat\Modules\Support\Generator\Stub; /** * @group generator */ -class TestMakeCommand extends ComponentGeneratorCommand +class TestMakeCommand extends BaseComponentGeneratorCommand implements PromptsForMissingInput { - use ModuleCommandTrait; - /** * The name and signature of the console command. * * @var string */ - protected $name = 'module:make:test'; + protected $signature = 'module:make:test + {name : The name of the test} + {module? : The name or package name of the app module} + {--s|stub= : The stub name to load for this generator} + {--type : The type of test to be created} + {--model= : The class name of the model to be used in the test} + {--route= : The route name for HTTP tests} + {--force : Overwrite the file if it already exists}'; /** * The console command description. @@ -30,127 +34,88 @@ class TestMakeCommand extends ComponentGeneratorCommand protected $description = 'Generate new test for the specified module.'; /** - * The type of test to be created. - * - * @var string - */ - protected string $type = 'unit'; - - /** - * The stub name to load for this generator - * - * @var string - */ - protected string $stub = 'plain'; - - /** - * Module instance. - * - * @var Module - */ - protected Module $module; - - /** - * Component type. - * - * @var string - */ - protected string $componentType; - - /** - * Prepared 'name' argument. - * - * @var string + * The test type. */ - protected string $nameArgument; + protected string $type; /** - * Get the console command options. - * - * @return array + * Prompt for missing input arguments using the returned questions. */ - protected function getOptions(): array + protected function promptForMissingArgumentsUsing(): array { return [ - ['type', 't', InputOption::VALUE_REQUIRED, 'The type of test to be created.'], - ['stub', 's', InputOption::VALUE_REQUIRED, 'The stub name to load for this generator.'], - ['model', null, InputOption::VALUE_REQUIRED, 'The class name of the model to be used in the test.'], - ['route', null, InputOption::VALUE_REQUIRED, 'The route name for HTTP tests.'], + 'name' => 'Enter the test class name', ]; } - protected function prepare() + protected function beforeGenerate(): void { - $this->module = $this->getModule(); $this->type = $this->getOptionOrChoice( 'type', - 'Select the type of test to be created', - ['unit', 'feature', 'api', 'web', 'cli'], - 'unit' + question: 'Enter the type of test to be created', + choices: ['unit', 'feature', 'api', 'web', 'cli'], + default: 'unit' ); + $this->componentType = match($this->type) { + 'unit' => ModuleComponentType::UnitTest, + 'feature' => ModuleComponentType::FeatureTest, + 'api' => ModuleComponentType::ApiTest, + 'web' => ModuleComponentType::WebTest, + 'cli' => ModuleComponentType::CliTest, + default => ModuleComponentType::UnitTest, + }; + } - $stubsMap = [ + protected function getContents(): string + { + $stubChoices = match($this->type) { + 'unit' => ['plain'], + 'feature' => ['plain'], 'api' => ['plain', 'create', 'delete', 'list', 'update', 'view'], 'web' => ['plain', 'create', 'delete', 'update'], 'cli' => ['plain'], - 'unit' => ['plain'], - 'feature' => ['plain'] - ]; - $stubChoices = $stubsMap[$this->type]; - if (count($stubChoices) === 1) { - $this->stub = $stubChoices[0]; - } else { - $this->stub = $this->getOptionOrChoice( + default => ['plain'], + }; + + $stub = count($stubChoices) === 1 + ? $stubChoices[0] + : $this->getOptionOrChoice( 'stub', 'Select the stub you want to use for generator', $stubChoices, 'plain' ); - } - - $this->componentType = "{$this->type}-test"; - $this->nameArgument = $this->getTrimmedArgument('name'); - } - protected function getDestinationFilePath(): string - { - return $this->getComponentPath($this->module, $this->nameArgument, $this->componentType); - } - - protected function getTemplateContents(): string - { $stubReplaces = [ - 'moduleKey' => $this->module->getKey(), + 'group' => $this->module->getKebabName(), 'namespace' => $this->getComponentNamespace($this->module, $this->nameArgument, $this->componentType), - 'class' => $this->getClass($this->nameArgument) + 'class' => class_basename($this->nameArgument), ]; - if ($this->stub !== 'plain') { - $model = $this->getOptionOrAsk( - 'model', - 'Enter the class name of the model to be used in the test', - '', - true + if ($stub !== 'plain') { + $modelClass = $this->getFullClassFromOptionOrAsk( + optionName: 'model', + question: 'Enter the class name of the model to be used in the test', + componentType: ModuleComponentType::Model, + module: $this->module ); - $stubReplaces['model'] = $this->getClass($model); - $stubReplaces['modelSnake'] = Str::snake($stubReplaces['model']); + $stubReplaces['model'] = class_basename($modelClass); + $stubReplaces['modelNamespace'] = $this->getNamespaceOfClass($modelClass); $stubReplaces['models'] = Str::plural($stubReplaces['model']); - $stubReplaces['modelsSnake'] = Str::snake($stubReplaces['models']); - $stubReplaces['modelEntity'] = Str::camel($stubReplaces['model']); - $stubReplaces['modelNamespace'] = $this->getComponentNamespace($this->module, $model, 'model'); - $stubReplaces['modelPermissionEntity'] = Str::snake($stubReplaces['model'], '-'); - $stubReplaces['modelPermissionEntities'] = Str::plural($stubReplaces['modelPermissionEntity']); + $stubReplaces['modelCamelCase'] = Str::camel($stubReplaces['model']); + $stubReplaces['modelSnakeCase'] = Str::snake($stubReplaces['model']); + $stubReplaces['modelsSnakeCase'] = Str::snake($stubReplaces['models']); + $stubReplaces['modelKebabCase'] = Str::kebab($stubReplaces['model']); + $stubReplaces['modelsKebabCase'] = Str::plural($stubReplaces['modelKebabCase']); } if (in_array($this->type, ['api', 'web'])) { $stubReplaces['routeName'] = $this->getOptionOrAsk( 'route', - 'Enter the route name for HTTP tests', - '', - true + 'Enter the route name for HTTP tests' ); } - return Stub::create("test/{$this->type}/{$this->stub}.stub", $stubReplaces)->render(); + return Stub::create("test/{$this->type}/{$stub}.stub", $stubReplaces)->render(); } } diff --git a/src/Commands/Generators/stubs/action/create.stub b/src/Commands/Generators/stubs/action/create.stub index 8969a80..1340d64 100644 --- a/src/Commands/Generators/stubs/action/create.stub +++ b/src/Commands/Generators/stubs/action/create.stub @@ -2,15 +2,17 @@ namespace {{ namespace }}; -use App\Ship\Abstracts\Actions\Action; -use Illuminate\Http\JsonResponse; use {{ dtoNamespace }}\{{ dto }}; use {{ modelNamespace }}\{{ model }}; use {{ requestNamespace }}\{{ request }}; use {{ resourceNamespace }}\{{ resource }}; +use Illuminate\Http\JsonResponse; +use Lorisleiva\Actions\Concerns\AsAction; -class {{ class }} extends Action +class {{ class }} { + use AsAction; + public function handle({{ dto }} $dto): {{ model }} { return {{ model }}::create($dto->all()); @@ -18,8 +20,8 @@ class {{ class }} extends Action public function asController({{ request }} $request): JsonResponse { - ${{ modelEntity }} = $this->handle($request->toDTO()); + ${{ modelCamelCase }} = $this->handle($request->toDTO()); - return (new {{ resource }}(${{ modelEntity }}))->created(); + return (new {{ resource }}(${{ modelCamelCase }}))->created(); } } diff --git a/src/Commands/Generators/stubs/action/delete.stub b/src/Commands/Generators/stubs/action/delete.stub index b0b3784..b210dcc 100644 --- a/src/Commands/Generators/stubs/action/delete.stub +++ b/src/Commands/Generators/stubs/action/delete.stub @@ -2,21 +2,23 @@ namespace {{ namespace }}; -use App\Ship\Abstracts\Actions\Action; -use Illuminate\Http\JsonResponse; use {{ modelNamespace }}\{{ model }}; use {{ requestNamespace }}\{{ request }}; +use Lorisleiva\Actions\Concerns\AsAction; +use Symfony\Component\HttpFoundation\Response; -class {{ class }} extends Action +class {{ class }} { - public function handle({{ model }} ${{ modelEntity }}): bool + use AsAction; + + public function handle({{ model }} ${{ modelCamelCase }}): bool { - return ${{ modelEntity }}->delete(); + return ${{ modelCamelCase }}->delete(); } - public function asController({{ request }} $request, {{ model }} ${{ modelEntity }}): JsonResponse + public function asController({{ request }} $request, {{ model }} ${{ modelCamelCase }}): Response { - $this->handle(${{ modelEntity }}); + $this->handle(${{ modelCamelCase }}); return $this->noContent(); } diff --git a/src/Commands/Generators/stubs/action/list.stub b/src/Commands/Generators/stubs/action/list.stub index 9f6fa04..011b4b2 100644 --- a/src/Commands/Generators/stubs/action/list.stub +++ b/src/Commands/Generators/stubs/action/list.stub @@ -2,24 +2,26 @@ namespace {{ namespace }}; -use App\Ship\Abstracts\Actions\Action; -use Illuminate\Pagination\AbstractPaginator; -use Illuminate\Http\Resources\Json\ResourceCollection; use {{ modelNamespace }}\{{ model }}; use {{ queryWizardNamespace }}\{{ queryWizard }}; use {{ requestNamespace }}\{{ request }}; use {{ resourceNamespace }}\{{ resource }}; +use Illuminate\Contracts\Pagination\LengthAwarePaginator; +use Illuminate\Http\Resources\Json\AnonymousResourceCollection; +use Lorisleiva\Actions\Concerns\AsAction; -class {{ class }} extends Action +class {{ class }} { - public function handle({{ request }} $request): AbstractPaginator + use AsAction; + + public function handle({{ request }} $request): LengthAwarePaginator { return {{ queryWizard }}::for({{ model }}::query()) ->build() - ->jsonPaginate(); + ->paginate(); } - public function asController({{ request }} $request): ResourceCollection + public function asController({{ request }} $request): AnonymousResourceCollection { return {{ resource }}::collection($this->handle($request)); } diff --git a/src/Commands/Generators/stubs/action/plain.stub b/src/Commands/Generators/stubs/action/plain.stub index b9abacf..32d49ea 100644 --- a/src/Commands/Generators/stubs/action/plain.stub +++ b/src/Commands/Generators/stubs/action/plain.stub @@ -2,10 +2,12 @@ namespace {{ namespace }}; -use App\Ship\Abstracts\Actions\Action; +use Lorisleiva\Actions\Concerns\AsAction; -class {{ class }} extends Action +class {{ class }} { + use AsAction; + public function handle() { // diff --git a/src/Commands/Generators/stubs/action/update.stub b/src/Commands/Generators/stubs/action/update.stub index 38d8446..a97f569 100644 --- a/src/Commands/Generators/stubs/action/update.stub +++ b/src/Commands/Generators/stubs/action/update.stub @@ -2,32 +2,31 @@ namespace {{ namespace }}; -use App\Ship\Abstracts\Actions\Action; -use App\Ship\Exceptions\UpdateResourceFailedException; use {{ dtoNamespace }}\{{ dto }}; use {{ modelNamespace }}\{{ model }}; use {{ requestNamespace }}\{{ request }}; use {{ resourceNamespace }}\{{ resource }}; +use Lorisleiva\Actions\Concerns\AsAction; -class {{ class }} extends Action +class {{ class }} { - public function handle({{ model }} ${{ modelEntity }}, {{ dto }} $dto): {{ model }} + use AsAction; + + public function handle({{ model }} ${{ modelCamelCase }}, {{ dto }} $dto): {{ model }} { $data = $dto->all(); - if (empty($data)) { - throw new UpdateResourceFailedException(); + if ($data) { + ${{ modelCamelCase }}->update($data); } - ${{ modelEntity }}->update($data); - - return ${{ modelEntity }}; + return ${{ modelCamelCase }}; } - public function asController({{ request }} $request, {{ model }} ${{ modelEntity }}): {{ resource }} + public function asController({{ request }} $request, {{ model }} ${{ modelCamelCase }}): {{ resource }} { - ${{ modelEntity }} = $this->handle(${{ modelEntity }}, $request->toDTO()); + ${{ modelCamelCase }} = $this->handle(${{ modelCamelCase }}, $request->toDTO()); - return new {{ resource }}(${{ modelEntity }}); + return new {{ resource }}(${{ modelCamelCase }}); } } diff --git a/src/Commands/Generators/stubs/action/view.stub b/src/Commands/Generators/stubs/action/view.stub index d02f0ad..7b16531 100644 --- a/src/Commands/Generators/stubs/action/view.stub +++ b/src/Commands/Generators/stubs/action/view.stub @@ -2,22 +2,24 @@ namespace {{ namespace }}; -use App\Ship\Abstracts\Actions\Action; -use Illuminate\Database\Eloquent\Model; use {{ modelNamespace }}\{{ model }}; use {{ queryWizardNamespace }}\{{ queryWizard }}; use {{ requestNamespace }}\{{ request }}; use {{ resourceNamespace }}\{{ resource }}; +use Illuminate\Database\Eloquent\Model; +use Lorisleiva\Actions\Concerns\AsAction; -class {{ class }} extends Action +class {{ class }} { - public function handle({{ request }} $request, {{ model }} ${{ modelEntity }}): Model + use AsAction; + + public function handle({{ request }} $request, {{ model }} ${{ modelCamelCase }}): Model { - return {{ queryWizard }}::for(${{ modelEntity }})->build(); + return {{ queryWizard }}::for(${{ modelCamelCase }})->build(); } - public function asController({{ request }} $request, {{ model }} ${{ modelEntity }}): {{ resource }} + public function asController({{ request }} $request, {{ model }} ${{ modelCamelCase }}): {{ resource }} { - return new {{ resource }}($this->handle($request, ${{ modelEntity }})); + return new {{ resource }}($this->handle($request, ${{ modelCamelCase }})); } } diff --git a/src/Commands/Generators/stubs/command.stub b/src/Commands/Generators/stubs/command.stub index cbe9c7c..8adfa16 100755 --- a/src/Commands/Generators/stubs/command.stub +++ b/src/Commands/Generators/stubs/command.stub @@ -2,9 +2,7 @@ namespace {{ namespace }}; -use App\Ship\Abstracts\Console\Command; -use Symfony\Component\Console\Input\InputOption; -use Symfony\Component\Console\Input\InputArgument; +use Illuminate\Console\Command; class {{ class }} extends Command { @@ -13,14 +11,14 @@ class {{ class }} extends Command * * @var string */ - protected $name = '{{ command }}'; + protected $signature = '{{ signature }}'; /** * The console command description. * * @var string */ - protected $description = 'Command description.'; + protected $description = '{{ description }}'; /** * Execute the console command. @@ -29,24 +27,4 @@ class {{ class }} extends Command { return self::SUCCESS; } - - /** - * Get the console command arguments. - */ - protected function getArguments(): array - { - return [ - ['example', InputArgument::REQUIRED, 'An example argument.'], - ]; - } - - /** - * Get the console command options. - */ - protected function getOptions(): array - { - return [ - ['example', null, InputOption::VALUE_OPTIONAL, 'An example option.', null], - ]; - } } diff --git a/src/Commands/Generators/stubs/composer.json.stub b/src/Commands/Generators/stubs/composer.json.stub old mode 100755 new mode 100644 index 948782b..4c24087 --- a/src/Commands/Generators/stubs/composer.json.stub +++ b/src/Commands/Generators/stubs/composer.json.stub @@ -1,6 +1,7 @@ { - "name": "{{ vendor }}/{{ moduleKey }}", - "description": "", + "name": "{{ modulePackageName }}", + "description": "{{ moduleName }} module", + "version": "1.0.0", "authors": [ { "name": "{{ authorName }}", @@ -9,7 +10,20 @@ ], "autoload": { "psr-4": { - "{{ moduleNamespace }}\\": "" + "{{ moduleNamespace }}\\": "src/", + "{{ moduleNamespace }}\\Database\\Factories\\": "database/factories/", + "{{ moduleNamespace }}\\Database\\Seeders\\": "database/seeders/" + } + }, + "autoload-dev": { + "psr-4": { + "{{ moduleNamespace }}\\Tests\\": "tests/" + } + }, + "extra": { + "laravel": { + "providers": [], + "aliases": {} } } } diff --git a/src/Commands/Generators/stubs/controller/api.stub b/src/Commands/Generators/stubs/controller/api.stub index bb986f5..6523cc4 100644 --- a/src/Commands/Generators/stubs/controller/api.stub +++ b/src/Commands/Generators/stubs/controller/api.stub @@ -2,9 +2,13 @@ namespace {{ namespace }}; -use App\Ship\Abstracts\Controllers\ApiController; +use Illuminate\Foundation\Auth\Access\AuthorizesRequests; +use Illuminate\Foundation\Bus\DispatchesJobs; +use Illuminate\Foundation\Validation\ValidatesRequests; +use Illuminate\Routing\Controller; +use Laraneat\Modules\Support\Concerns\WithJsonResponseHelpers; -class {{ class }} extends ApiController +class {{ class }} extends Controller { - // + use AuthorizesRequests, DispatchesJobs, ValidatesRequests, WithJsonResponseHelpers; } diff --git a/src/Commands/Generators/stubs/controller/web.stub b/src/Commands/Generators/stubs/controller/web.stub index 91e2189..09114c6 100644 --- a/src/Commands/Generators/stubs/controller/web.stub +++ b/src/Commands/Generators/stubs/controller/web.stub @@ -2,9 +2,12 @@ namespace {{ namespace }}; -use App\Ship\Abstracts\Controllers\WebController; +use Illuminate\Foundation\Auth\Access\AuthorizesRequests; +use Illuminate\Foundation\Bus\DispatchesJobs; +use Illuminate\Foundation\Validation\ValidatesRequests; +use Illuminate\Routing\Controller; -class {{ class }} extends WebController +class {{ class }} extends Controller { - // + use AuthorizesRequests, DispatchesJobs, ValidatesRequests; } diff --git a/src/Commands/Generators/stubs/dto.stub b/src/Commands/Generators/stubs/dto.stub new file mode 100644 index 0000000..5d127b7 --- /dev/null +++ b/src/Commands/Generators/stubs/dto.stub @@ -0,0 +1,13 @@ +|{{ model }} create($attributes = [], ?{{ model }} $parent = null) + * @method \Illuminate\Support\Collection createMany(iterable $records) * @method {{ model }} createOne($attributes = []) - * @method \Illuminate\Support\Collection|{{ model }} make($attributes = [], ?{{ model }} $parent = null) + * @method \Illuminate\Support\Collection|{{ model }} make($attributes = [], ?{{ model }} $parent = null) * @method {{ model }} makeOne($attributes = []) */ class {{ class }} extends Factory @@ -17,7 +17,7 @@ class {{ class }} extends Factory /** * The name of the factory's corresponding model. * - * @var string + * @var class-string */ protected $model = {{ model }}::class; @@ -31,4 +31,3 @@ class {{ class }} extends Factory ]; } } - diff --git a/src/Commands/Generators/stubs/job/plain.stub b/src/Commands/Generators/stubs/job/plain.stub index 0d579ca..a539371 100644 --- a/src/Commands/Generators/stubs/job/plain.stub +++ b/src/Commands/Generators/stubs/job/plain.stub @@ -2,13 +2,15 @@ namespace {{ namespace }}; -use App\Ship\Abstracts\Jobs\Job; use Illuminate\Foundation\Bus\Dispatchable; -class {{ class }} extends Job +class {{ class }} { use Dispatchable; + /** + * Create a new job instance. + */ public function __construct() { // diff --git a/src/Commands/Generators/stubs/job/queued.stub b/src/Commands/Generators/stubs/job/queued.stub index e62208e..9a7cec5 100644 --- a/src/Commands/Generators/stubs/job/queued.stub +++ b/src/Commands/Generators/stubs/job/queued.stub @@ -2,17 +2,19 @@ namespace {{ namespace }}; -use App\Ship\Abstracts\Jobs\Job; use Illuminate\Bus\Queueable; -use Illuminate\Queue\SerializesModels; -use Illuminate\Queue\InteractsWithQueue; use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Foundation\Bus\Dispatchable; +use Illuminate\Queue\InteractsWithQueue; +use Illuminate\Queue\SerializesModels; -class {{ class }} extends Job implements ShouldQueue +class {{ class }} implements ShouldQueue { use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; + /** + * Create a new job instance. + */ public function __construct() { // diff --git a/src/Commands/Generators/stubs/listener/plain.stub b/src/Commands/Generators/stubs/listener/plain.stub index 562fefb..6d96bd8 100755 --- a/src/Commands/Generators/stubs/listener/plain.stub +++ b/src/Commands/Generators/stubs/listener/plain.stub @@ -2,11 +2,13 @@ namespace {{ namespace }}; -use App\Ship\Abstracts\Listeners\Listener; use {{ eventNamespace }}\{{ event }}; -class {{ class }} extends Listener +class {{ class }} { + /** + * Create the event listener. + */ public function __construct() { // diff --git a/src/Commands/Generators/stubs/listener/queued.stub b/src/Commands/Generators/stubs/listener/queued.stub index 374ed50..65f0bc2 100755 --- a/src/Commands/Generators/stubs/listener/queued.stub +++ b/src/Commands/Generators/stubs/listener/queued.stub @@ -2,15 +2,17 @@ namespace {{ namespace }}; -use App\Ship\Abstracts\Listeners\Listener; use {{ eventNamespace }}\{{ event }}; -use Illuminate\Queue\InteractsWithQueue; use Illuminate\Contracts\Queue\ShouldQueue; +use Illuminate\Queue\InteractsWithQueue; -class {{ class }} extends Listener implements ShouldQueue +class {{ class }} implements ShouldQueue { use InteractsWithQueue; + /** + * Create the event listener. + */ public function __construct() { // diff --git a/src/Commands/Generators/stubs/mail.stub b/src/Commands/Generators/stubs/mail.stub new file mode 100644 index 0000000..8633302 --- /dev/null +++ b/src/Commands/Generators/stubs/mail.stub @@ -0,0 +1,52 @@ + + */ + public function attachments(): array + { + return []; + } +} diff --git a/src/Commands/Generators/stubs/mail/plain.stub b/src/Commands/Generators/stubs/mail/plain.stub deleted file mode 100644 index 898db1a..0000000 --- a/src/Commands/Generators/stubs/mail/plain.stub +++ /dev/null @@ -1,24 +0,0 @@ -view('view.name'); - } -} diff --git a/src/Commands/Generators/stubs/mail/queued.stub b/src/Commands/Generators/stubs/mail/queued.stub deleted file mode 100644 index 47ed21c..0000000 --- a/src/Commands/Generators/stubs/mail/queued.stub +++ /dev/null @@ -1,26 +0,0 @@ -view('view.name'); - } -} diff --git a/src/Commands/Generators/stubs/middleware.stub b/src/Commands/Generators/stubs/middleware.stub index b234b14..323c213 100755 --- a/src/Commands/Generators/stubs/middleware.stub +++ b/src/Commands/Generators/stubs/middleware.stub @@ -2,16 +2,18 @@ namespace {{ namespace }}; -use App\Ship\Abstracts\Middleware\Middleware; use Closure; use Illuminate\Http\Request; +use Symfony\Component\HttpFoundation\Response; -class {{ class }} extends Middleware +class {{ class }} { /** * Handle an incoming request. + * + * @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next */ - public function handle(Request $request, Closure $next) + public function handle(Request $request, Closure $next): Response { return $next($request); } diff --git a/src/Commands/Generators/stubs/model/full.stub b/src/Commands/Generators/stubs/model/full.stub index aa1a955..39d7bf9 100644 --- a/src/Commands/Generators/stubs/model/full.stub +++ b/src/Commands/Generators/stubs/model/full.stub @@ -2,10 +2,10 @@ namespace {{ namespace }}; -use App\Ship\Abstracts\Models\Model; +use {{ factoryNamespace }}\{{ factory }}; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Factories\Factory; -use {{ factoryNamespace }}\{{ factory }}; +use Illuminate\Database\Eloquent\Model; class {{ class }} extends Model { diff --git a/src/Commands/Generators/stubs/model/plain.stub b/src/Commands/Generators/stubs/model/plain.stub index 90b8d19..279313e 100644 --- a/src/Commands/Generators/stubs/model/plain.stub +++ b/src/Commands/Generators/stubs/model/plain.stub @@ -2,7 +2,7 @@ namespace {{ namespace }}; -use App\Ship\Abstracts\Models\Model; +use Illuminate\Database\Eloquent\Model; class {{ class }} extends Model { diff --git a/src/Commands/Generators/stubs/module.json.stub b/src/Commands/Generators/stubs/module.json.stub deleted file mode 100755 index 532fd74..0000000 --- a/src/Commands/Generators/stubs/module.json.stub +++ /dev/null @@ -1,12 +0,0 @@ -{ - "name": "{{ moduleName }}", - "namespace": "{{ moduleNamespace }}", - "alias": "{{ moduleKey }}", - "description": "{{ moduleName }} module", - "keywords": [], - "priority": 0, - "providers": [], - "aliases": {}, - "files": [], - "requires": [] -} diff --git a/src/Commands/Generators/stubs/notification/plain.stub b/src/Commands/Generators/stubs/notification/plain.stub index d7fbd9f..240ef66 100644 --- a/src/Commands/Generators/stubs/notification/plain.stub +++ b/src/Commands/Generators/stubs/notification/plain.stub @@ -2,19 +2,32 @@ namespace {{ namespace }}; -use App\Ship\Abstracts\Notifications\Notification; +use Illuminate\Notifications\Notification; class {{ class }} extends Notification { + /** + * Create a new notification instance. + */ public function __construct() { // } + /** + * Get the notification's delivery channels. + * + * @return array + */ + public function via(object $notifiable): array + { + return ['mail', 'database']; + } + /** * Get the array representation of the notification. */ - public function toArray($notifiable): array + public function toArray(object $notifiable): array { return [ // diff --git a/src/Commands/Generators/stubs/notification/queued.stub b/src/Commands/Generators/stubs/notification/queued.stub index 8c16dac..d4d0905 100644 --- a/src/Commands/Generators/stubs/notification/queued.stub +++ b/src/Commands/Generators/stubs/notification/queued.stub @@ -2,15 +2,18 @@ namespace {{ namespace }}; -use App\Ship\Abstracts\Notifications\Notification; use Illuminate\Bus\Queueable; use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Notifications\Messages\MailMessage; +use Illuminate\Notifications\Notification; class {{ class }} extends Notification implements ShouldQueue { use Queueable; + /** + * Create a new notification instance. + */ public function __construct() { // @@ -18,8 +21,10 @@ class {{ class }} extends Notification implements ShouldQueue /** * Get the notification's delivery channels. + * + * @return array */ - public function via($notifiable): array + public function via(object $notifiable): array { return ['mail']; } @@ -27,18 +32,20 @@ class {{ class }} extends Notification implements ShouldQueue /** * Get the mail representation of the notification. */ - public function toMail($notifiable): MailMessage + public function toMail(object $notifiable): MailMessage { return (new MailMessage) ->line('The introduction to the notification.') - ->action('Notification Action', 'https://laravel.com') + ->action('Notification Action', url('/')) ->line('Thank you for using our application!'); } /** * Get the array representation of the notification. + * + * @return array */ - public function toArray($notifiable): array + public function toArray(object $notifiable): array { return [ // diff --git a/src/Commands/Generators/stubs/observer.stub b/src/Commands/Generators/stubs/observer.stub index 1d29d41..cff4f33 100644 --- a/src/Commands/Generators/stubs/observer.stub +++ b/src/Commands/Generators/stubs/observer.stub @@ -2,15 +2,14 @@ namespace {{ namespace }}; -use App\Ship\Abstracts\Observers\Observer; use {{ modelNamespace }}\{{ model }}; -class {{ class }} extends Observer +class {{ class }} { /** * Handle the {{ model }} "created" event. */ - public function created({{ model }} ${{ modelEntity }}): void + public function created({{ model }} ${{ modelCamelCase }}): void { // } @@ -18,7 +17,7 @@ class {{ class }} extends Observer /** * Handle the {{ model }} "updated" event. */ - public function updated({{ model }} ${{ modelEntity }}): void + public function updated({{ model }} ${{ modelCamelCase }}): void { // } @@ -26,7 +25,7 @@ class {{ class }} extends Observer /** * Handle the {{ model }} "deleted" event. */ - public function deleted({{ model }} ${{ modelEntity }}): void + public function deleted({{ model }} ${{ modelCamelCase }}): void { // } @@ -34,7 +33,7 @@ class {{ class }} extends Observer /** * Handle the {{ model }} "restored" event. */ - public function restored({{ model }} ${{ modelEntity }}): void + public function restored({{ model }} ${{ modelCamelCase }}): void { // } @@ -42,7 +41,7 @@ class {{ class }} extends Observer /** * Handle the {{ model }} "force deleted" event. */ - public function forceDeleted({{ model }} ${{ modelEntity }}): void + public function forceDeleted({{ model }} ${{ modelCamelCase }}): void { // } diff --git a/src/Commands/Generators/stubs/policy/full.stub b/src/Commands/Generators/stubs/policy/full.stub index 0aa6861..7193c8c 100644 --- a/src/Commands/Generators/stubs/policy/full.stub +++ b/src/Commands/Generators/stubs/policy/full.stub @@ -2,29 +2,26 @@ namespace {{ namespace }}; -use App\Ship\Abstracts\Policies\Policy; -use Illuminate\Auth\Access\HandlesAuthorization; use {{ modelNamespace }}\{{ model }}; use {{ userNamespace }}\{{ user }}; -class {{ class }} extends Policy +class {{ class }} { - use HandlesAuthorization; /** * Determine whether the user can view any models. */ public function viewAny({{ user }} $user): bool { - return $user->can('view-{{ modelPermissionEntity }}'); + return $user->can('view-{{ modelKebabCase }}'); } /** * Determine whether the user can view the model. */ - public function view({{ user }} $user, {{ model }} ${{ modelEntity }}): bool + public function view({{ user }} $user, {{ model }} ${{ modelCamelCase }}): bool { - return $user->can('view-{{ modelPermissionEntity }}'); + return $user->can('view-{{ modelKebabCase }}'); } /** @@ -32,38 +29,38 @@ class {{ class }} extends Policy */ public function create({{ user }} $user): bool { - return $user->can('create-{{ modelPermissionEntity }}'); + return $user->can('create-{{ modelKebabCase }}'); } /** * Determine whether the user can update the model. */ - public function update({{ user }} $user, {{ model }} ${{ modelEntity }}): bool + public function update({{ user }} $user, {{ model }} ${{ modelCamelCase }}): bool { - return $user->can('update-{{ modelPermissionEntity }}'); + return $user->can('update-{{ modelKebabCase }}'); } /** * Determine whether the user can delete the model. */ - public function delete({{ user }} $user, {{ model }} ${{ modelEntity }}): bool + public function delete({{ user }} $user, {{ model }} ${{ modelCamelCase }}): bool { - return $user->can('delete-{{ modelPermissionEntity }}'); + return $user->can('delete-{{ modelKebabCase }}'); } /** * Determine whether the user can restore the model. */ - public function restore({{ user }} $user, {{ model }} ${{ modelEntity }}): bool + public function restore({{ user }} $user, {{ model }} ${{ modelCamelCase }}): bool { - return $user->can('delete-{{ modelPermissionEntity }}'); + return $user->can('delete-{{ modelKebabCase }}'); } /** * Determine whether the user can permanently delete the model. */ - public function forceDelete({{ user }} $user, {{ model }} ${{ modelEntity }}): bool + public function forceDelete({{ user }} $user, {{ model }} ${{ modelCamelCase }}): bool { - return $user->can('force-delete-{{ modelPermissionEntity }}'); + return $user->can('force-delete-{{ modelKebabCase }}'); } } diff --git a/src/Commands/Generators/stubs/policy/plain.stub b/src/Commands/Generators/stubs/policy/plain.stub index b4ceb60..e2506b7 100644 --- a/src/Commands/Generators/stubs/policy/plain.stub +++ b/src/Commands/Generators/stubs/policy/plain.stub @@ -2,15 +2,7 @@ namespace {{ namespace }}; -use App\Ship\Abstracts\Policies\Policy; -use Illuminate\Auth\Access\HandlesAuthorization; - -class {{ class }} extends Policy +class {{ class }} { - use HandlesAuthorization; - - public function __construct() - { - // - } + // } diff --git a/src/Commands/Generators/stubs/provider/event.stub b/src/Commands/Generators/stubs/provider/event.stub index 4667946..fdd9935 100644 --- a/src/Commands/Generators/stubs/provider/event.stub +++ b/src/Commands/Generators/stubs/provider/event.stub @@ -2,12 +2,14 @@ namespace {{ namespace }}; -use App\Ship\Abstracts\Providers\EventServiceProvider as ServiceProvider; +use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider; class {{ class }} extends ServiceProvider { /** - * The event listener mappings for the application. + * The event handler mappings for the application. + * + * @var array> */ protected $listen = []; diff --git a/src/Commands/Generators/stubs/provider/module.stub b/src/Commands/Generators/stubs/provider/module.stub index 6f73860..429cd9d 100644 --- a/src/Commands/Generators/stubs/provider/module.stub +++ b/src/Commands/Generators/stubs/provider/module.stub @@ -2,22 +2,16 @@ namespace {{ namespace }}; -use App\Ship\Abstracts\Providers\ServiceProvider; -use Laraneat\Modules\Traits\ModuleProviderHelpersTrait; +use Laraneat\Modules\Support\ModuleServiceProvider; -class {{ class }} extends ServiceProvider +class {{ class }} extends ModuleServiceProvider { - use ModuleProviderHelpersTrait; - - protected string $moduleName = '{{ moduleName }}'; - protected string $moduleKey = '{{ moduleKey }}'; - /** * Register services. */ public function register(): void { - // + // $this->loadConfigurations(); } /** @@ -25,75 +19,84 @@ class {{ class }} extends ServiceProvider */ public function boot(): void { - $this->registerMigrations(); - // $this->registerTranslations(); - // $this->registerCommands(); - // $this->registerViews(); + $this->loadMigrations(); + // $this->loadCommands(); + // $this->loadTranslations(); + // $this->loadViews(); } /** - * Get the services provided by the provider. + * Register configuration files. */ - public function provides(): array + public function loadConfigurations(): void { - return []; + $sourcePath = __DIR__.'/{{ configPath }}/{{ moduleNameKebabCase }}.php'; + $configsPath = $this->app->configPath('{{ moduleNameKebabCase }}.php'); + + $this->mergeConfigFrom($sourcePath, '{{ moduleNameKebabCase }}'); + + $this->publishes([ + $sourcePath => $configsPath + ], '{{ moduleNameKebabCase }}-config'); } /** - * Register translations. + * Register migrations. */ - public function registerTranslations(): void + public function loadMigrations(): void { - $sourcePath = module_path($this->moduleName, '{{ langPath }}'); - $langPath = resource_path('lang/modules/' . $this->moduleKey); + $sourcePath = __DIR__.'/{{ migrationsPath }}'; + $migrationsPath = $this->app->databasePath('migrations'); - $this->loadTranslationsFrom($sourcePath, $this->moduleKey); + $this->loadMigrationsFrom($sourcePath); $this->publishes([ - $sourcePath => $langPath, - ], 'translations'); + $sourcePath => $migrationsPath + ], '{{ moduleNameKebabCase }}-migrations'); } /** - * Register views. + * Register artisan commands. */ - public function registerViews(): void + public function loadCommands(): void { - $sourcePath = module_path($this->moduleName, '{{ viewsPath }}'); - $viewsPath = resource_path('views/modules/' . $this->moduleKey); - - $this->loadViewsFrom( - array_merge($this->getPublishableViewPaths($this->moduleKey), [$sourcePath]), - $this->moduleKey - ); - - $this->publishes([ - $sourcePath => $viewsPath - ], 'views'); + if ($this->app->runningInConsole()) { + $this->loadCommandsFrom([ + '{{ commandsNamespace }}' => __DIR__.'/{{ commandsPath }}', + ]); + } } /** - * Register migrations. + * Register translations. */ - public function registerMigrations(): void + public function loadTranslations(): void { - $sourcePath = module_path($this->moduleName, '{{ migrationsPath }}'); - $migrationsPath = database_path('migrations'); + $sourcePath = __DIR__.'/{{ langPath }}'; + $langPath = $this->app->langPath('modules/{{ moduleNameKebabCase }}'); - $this->loadMigrationsFrom($sourcePath); + $this->loadTranslationsFrom($sourcePath, '{{ moduleNameKebabCase }}'); $this->publishes([ - $sourcePath => $migrationsPath - ], 'migrations'); + $sourcePath => $langPath, + ], '{{ moduleNameKebabCase }}-translations'); } /** - * Register artisan commands. + * Register views. */ - public function registerCommands(): void + public function loadViews(): void { - if ($this->app->runningInConsole()) { - $this->loadCommands(module_path($this->moduleName, '{{ commandsPath }}')); - } + $sourcePath = __DIR__.'/{{ viewsPath }}'; + $viewsPath = $this->app->resourcePath('views/modules/{{ moduleNameKebabCase }}'); + + $this->loadViewsFrom( + array_merge($this->getPublishableViewPaths('{{ moduleNameKebabCase }}'), [$sourcePath]), + '{{ moduleNameKebabCase }}' + ); + + $this->publishes([ + $sourcePath => $viewsPath + ], '{{ moduleNameKebabCase }}-views'); } } diff --git a/src/Commands/Generators/stubs/provider/plain.stub b/src/Commands/Generators/stubs/provider/plain.stub index 4f35cc2..537ce0a 100644 --- a/src/Commands/Generators/stubs/provider/plain.stub +++ b/src/Commands/Generators/stubs/provider/plain.stub @@ -2,12 +2,12 @@ namespace {{ namespace }}; -use App\Ship\Abstracts\Providers\ServiceProvider; +use Illuminate\Support\ServiceProvider; class {{ class }} extends ServiceProvider { /** - * Bootstrap services. + * Register services. */ public function register(): void { diff --git a/src/Commands/Generators/stubs/provider/route.stub b/src/Commands/Generators/stubs/provider/route.stub index bf97cd2..a5e1af7 100644 --- a/src/Commands/Generators/stubs/provider/route.stub +++ b/src/Commands/Generators/stubs/provider/route.stub @@ -2,15 +2,13 @@ namespace {{ namespace }}; -use App\Ship\Abstracts\Providers\RouteServiceProvider as ServiceProvider; +use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider; use Illuminate\Support\Facades\Route; -use Laraneat\Modules\Traits\RouteProviderHelpersTrait; +use Laraneat\Modules\Support\Concerns\CanLoadRoutesFromDirectory; class {{ class }} extends ServiceProvider { - use RouteProviderHelpersTrait; - - protected string $moduleName = '{{ moduleName }}'; + use CanLoadRoutesFromDirectory; /** * Called before routes are registered. @@ -37,9 +35,9 @@ class {{ class }} extends ServiceProvider protected function mapWebRoutes(): void { Route::middleware('web') -// ->namespace('{{ webControllerNamespace }}') + // ->namespace('{{ webControllerNamespace }}') ->group(function () { - $this->loadRoutesFromDirectory(module_path($this->moduleName, '{{ webRoutesPath }}')); + $this->loadRoutesFromDirectory(__DIR__.'/{{ webRoutesPath }}'); }); } @@ -51,9 +49,9 @@ class {{ class }} extends ServiceProvider { Route::prefix('api') ->middleware('api') -// ->namespace('{{ apiControllerNamespace }}') + // ->namespace('{{ apiControllerNamespace }}') ->group(function () { - $this->loadRoutesFromDirectory(module_path($this->moduleName, '{{ apiRoutesPath }}')); + $this->loadRoutesFromDirectory(__DIR__.'/{{ apiRoutesPath }}'); }); } } diff --git a/src/Commands/Generators/stubs/query-wizard/elastic.stub b/src/Commands/Generators/stubs/query-wizard/elastic.stub deleted file mode 100644 index c87f673..0000000 --- a/src/Commands/Generators/stubs/query-wizard/elastic.stub +++ /dev/null @@ -1,78 +0,0 @@ - - */ - protected function allowedAppends(): array - { - return []; - } - - /** - * @return array - */ - protected function defaultAppends(): array - { - return []; - } - - /** - * @return array - */ - protected function allowedFields(): array - { - return [ - // TODO: add fields here - ]; - } - - /** - * @return array - */ - protected function allowedFilters(): array - { - return []; - } - - /** - * @return array - */ - protected function allowedIncludes(): array - { - return []; - } - - /** - * @return array - */ - protected function defaultIncludes(): array - { - return []; - } - - /** - * @return array - */ - protected function allowedSorts(): array - { - return []; - } - - /** - * @return array - */ - protected function defaultSorts(): array - { - return []; - } -} diff --git a/src/Commands/Generators/stubs/query-wizard/eloquent.stub b/src/Commands/Generators/stubs/query-wizard/eloquent.stub index 6e9e3ca..a001698 100644 --- a/src/Commands/Generators/stubs/query-wizard/eloquent.stub +++ b/src/Commands/Generators/stubs/query-wizard/eloquent.stub @@ -2,7 +2,7 @@ namespace {{ namespace }}; -use App\Ship\Abstracts\QueryWizards\EloquentQueryWizard; +use Jackardios\QueryWizard\Eloquent\EloquentQueryWizard; use Jackardios\QueryWizard\Eloquent\EloquentFilter; use Jackardios\QueryWizard\Eloquent\EloquentInclude; use Jackardios\QueryWizard\Eloquent\EloquentSort; @@ -10,7 +10,7 @@ use Jackardios\QueryWizard\Eloquent\EloquentSort; class {{ class }} extends EloquentQueryWizard { /** - * @return array + * @return array */ protected function allowedAppends(): array { @@ -18,7 +18,7 @@ class {{ class }} extends EloquentQueryWizard } /** - * @return array + * @return array */ protected function defaultAppends(): array { @@ -26,7 +26,7 @@ class {{ class }} extends EloquentQueryWizard } /** - * @return array + * @return array */ protected function allowedFields(): array { @@ -36,7 +36,7 @@ class {{ class }} extends EloquentQueryWizard } /** - * @return array + * @return array */ protected function allowedFilters(): array { @@ -44,7 +44,7 @@ class {{ class }} extends EloquentQueryWizard } /** - * @return array + * @return array */ protected function allowedIncludes(): array { @@ -52,7 +52,7 @@ class {{ class }} extends EloquentQueryWizard } /** - * @return array + * @return array */ protected function defaultIncludes(): array { @@ -60,7 +60,7 @@ class {{ class }} extends EloquentQueryWizard } /** - * @return array + * @return array */ protected function allowedSorts(): array { @@ -68,7 +68,7 @@ class {{ class }} extends EloquentQueryWizard } /** - * @return array + * @return array */ protected function defaultSorts(): array { diff --git a/src/Commands/Generators/stubs/query-wizard/model.stub b/src/Commands/Generators/stubs/query-wizard/model.stub index 10b986d..2b878ef 100644 --- a/src/Commands/Generators/stubs/query-wizard/model.stub +++ b/src/Commands/Generators/stubs/query-wizard/model.stub @@ -2,13 +2,13 @@ namespace {{ namespace }}; -use App\Ship\Abstracts\QueryWizards\ModelQueryWizard; +use Jackardios\QueryWizard\Model\ModelQueryWizard; use Jackardios\QueryWizard\Model\ModelInclude; class {{ class }} extends ModelQueryWizard { /** - * @return array + * @return array */ protected function allowedAppends(): array { @@ -16,7 +16,7 @@ class {{ class }} extends ModelQueryWizard } /** - * @return array + * @return array */ protected function defaultAppends(): array { @@ -24,7 +24,7 @@ class {{ class }} extends ModelQueryWizard } /** - * @return array + * @return array */ protected function allowedFields(): array { @@ -34,7 +34,7 @@ class {{ class }} extends ModelQueryWizard } /** - * @return array + * @return array */ protected function allowedIncludes(): array { @@ -42,7 +42,7 @@ class {{ class }} extends ModelQueryWizard } /** - * @return array + * @return array */ protected function defaultIncludes(): array { diff --git a/src/Commands/Generators/stubs/query-wizard/scout.stub b/src/Commands/Generators/stubs/query-wizard/scout.stub deleted file mode 100644 index 947daeb..0000000 --- a/src/Commands/Generators/stubs/query-wizard/scout.stub +++ /dev/null @@ -1,77 +0,0 @@ - - */ - protected function allowedAppends(): array - { - return []; - } - - /** - * @return array - */ - protected function defaultAppends(): array - { - return []; - } - - /** - * @return array - */ - protected function allowedFields(): array - { - return [ - // TODO: add fields here - ]; - } - - /** - * @return array - */ - protected function allowedFilters(): array - { - return []; - } - - /** - * @return array - */ - protected function allowedIncludes(): array - { - return []; - } - - /** - * @return array - */ - protected function defaultIncludes(): array - { - return []; - } - - /** - * @return array - */ - protected function allowedSorts(): array - { - return []; - } - - /** - * @return array - */ - protected function defaultSorts(): array - { - return []; - } -} diff --git a/src/Commands/Generators/stubs/readme.stub b/src/Commands/Generators/stubs/readme.stub deleted file mode 100644 index be4bf30..0000000 --- a/src/Commands/Generators/stubs/readme.stub +++ /dev/null @@ -1 +0,0 @@ -# [Laraneat](https://github.com/laraneat/laraneat) {{ moduleName }} module diff --git a/src/Commands/Generators/stubs/request/create.stub b/src/Commands/Generators/stubs/request/create.stub index 6e3cf3f..1a2bc08 100644 --- a/src/Commands/Generators/stubs/request/create.stub +++ b/src/Commands/Generators/stubs/request/create.stub @@ -2,12 +2,12 @@ namespace {{ namespace }}; -use App\Ship\Abstracts\Requests\Request; use {{ dtoNamespace }}\{{ dto }}; use {{ modelNamespace }}\{{ model }}; +use Illuminate\Foundation\Http\FormRequest; use Illuminate\Support\Facades\Gate; -class {{ class }} extends Request +class {{ class }} extends FormRequest { public function rules(): array { @@ -23,6 +23,6 @@ class {{ class }} extends Request public function toDTO(): {{ dto }} { - return new {{ dto }}($this->validated()); + return {{ dto }}::from($this->validated()); } } diff --git a/src/Commands/Generators/stubs/request/delete.stub b/src/Commands/Generators/stubs/request/delete.stub index 6e43fbb..837e84f 100644 --- a/src/Commands/Generators/stubs/request/delete.stub +++ b/src/Commands/Generators/stubs/request/delete.stub @@ -2,10 +2,10 @@ namespace {{ namespace }}; -use App\Ship\Abstracts\Requests\Request; +use Illuminate\Foundation\Http\FormRequest; use Illuminate\Support\Facades\Gate; -class {{ class }} extends Request +class {{ class }} extends FormRequest { public function rules(): array { @@ -14,7 +14,7 @@ class {{ class }} extends Request public function authorize(): bool { - ${{ modelEntity }} = $this->route('{{ modelEntity }}'); - return ${{ modelEntity }} && Gate::check('delete', ${{ modelEntity }}); + ${{ modelCamelCase }} = $this->route('{{ modelCamelCase }}'); + return ${{ modelCamelCase }} && Gate::check('delete', ${{ modelCamelCase }}); } } diff --git a/src/Commands/Generators/stubs/request/list.stub b/src/Commands/Generators/stubs/request/list.stub index b04b420..8284481 100644 --- a/src/Commands/Generators/stubs/request/list.stub +++ b/src/Commands/Generators/stubs/request/list.stub @@ -2,11 +2,11 @@ namespace {{ namespace }}; -use App\Ship\Abstracts\Requests\Request; use {{ modelNamespace }}\{{ model }}; +use Illuminate\Foundation\Http\FormRequest; use Illuminate\Support\Facades\Gate; -class {{ class }} extends Request +class {{ class }} extends FormRequest { public function rules(): array { diff --git a/src/Commands/Generators/stubs/request/plain.stub b/src/Commands/Generators/stubs/request/plain.stub index dc4d3b0..48ad628 100644 --- a/src/Commands/Generators/stubs/request/plain.stub +++ b/src/Commands/Generators/stubs/request/plain.stub @@ -2,9 +2,9 @@ namespace {{ namespace }}; -use App\Ship\Abstracts\Requests\Request; +use Illuminate\Foundation\Http\FormRequest; -class {{ class }} extends Request +class {{ class }} extends FormRequest { public function rules(): array { diff --git a/src/Commands/Generators/stubs/request/update.stub b/src/Commands/Generators/stubs/request/update.stub index 45e79b3..00da57c 100644 --- a/src/Commands/Generators/stubs/request/update.stub +++ b/src/Commands/Generators/stubs/request/update.stub @@ -2,11 +2,11 @@ namespace {{ namespace }}; -use App\Ship\Abstracts\Requests\Request; use {{ dtoNamespace }}\{{ dto }}; +use Illuminate\Foundation\Http\FormRequest; use Illuminate\Support\Facades\Gate; -class {{ class }} extends Request +class {{ class }} extends FormRequest { public function rules(): array { @@ -17,12 +17,12 @@ class {{ class }} extends Request public function authorize(): bool { - ${{ modelEntity }} = $this->route('{{ modelEntity }}'); - return ${{ modelEntity }} && Gate::check('update', ${{ modelEntity }}); + ${{ modelCamelCase }} = $this->route('{{ modelCamelCase }}'); + return ${{ modelCamelCase }} && Gate::check('update', ${{ modelCamelCase }}); } public function toDTO(): {{ dto }} { - return new {{ dto }}($this->validated()); + return {{ dto }}::from($this->validated()); } } diff --git a/src/Commands/Generators/stubs/request/view.stub b/src/Commands/Generators/stubs/request/view.stub index 6d827cf..e5c4e85 100644 --- a/src/Commands/Generators/stubs/request/view.stub +++ b/src/Commands/Generators/stubs/request/view.stub @@ -2,10 +2,10 @@ namespace {{ namespace }}; -use App\Ship\Abstracts\Requests\Request; +use Illuminate\Foundation\Http\FormRequest; use Illuminate\Support\Facades\Gate; -class {{ class }} extends Request +class {{ class }} extends FormRequest { public function rules(): array { @@ -14,7 +14,7 @@ class {{ class }} extends Request public function authorize(): bool { - ${{ modelEntity }} = $this->route('{{ modelEntity }}'); - return ${{ modelEntity }} && Gate::check('view', ${{ modelEntity }}); + ${{ modelCamelCase }} = $this->route('{{ modelCamelCase }}'); + return ${{ modelCamelCase }} && Gate::check('view', ${{ modelCamelCase }}); } } diff --git a/src/Commands/Generators/stubs/resource/collection.stub b/src/Commands/Generators/stubs/resource/collection.stub index e1284ed..88f416b 100644 --- a/src/Commands/Generators/stubs/resource/collection.stub +++ b/src/Commands/Generators/stubs/resource/collection.stub @@ -2,17 +2,15 @@ namespace {{ namespace }}; -use App\Ship\Abstracts\Resources\Json\ResourceCollection; +use Illuminate\Http\Request; +use Illuminate\Http\Resources\Json\ResourceCollection; class {{ class }} extends ResourceCollection { /** * Transform the resource collection into an array. - * - * @param \Illuminate\Http\Request $request - * @return array|\Illuminate\Contracts\Support\Arrayable|\JsonSerializable */ - public function toArray($request) + public function toArray(Request $request): array { return parent::toArray($request); } diff --git a/src/Commands/Generators/stubs/resource/single.stub b/src/Commands/Generators/stubs/resource/single.stub index 5997330..e55a05e 100644 --- a/src/Commands/Generators/stubs/resource/single.stub +++ b/src/Commands/Generators/stubs/resource/single.stub @@ -2,17 +2,15 @@ namespace {{ namespace }}; -use App\Ship\Abstracts\Resources\Json\JsonResource; +use Illuminate\Http\Request; +use Illuminate\Http\Resources\Json\JsonResource; class {{ class }} extends JsonResource { /** * Transform the resource into an array. - * - * @param \Illuminate\Http\Request $request - * @return array|\Illuminate\Contracts\Support\Arrayable|\JsonSerializable */ - public function toArray($request) + public function toArray(Request $request): array { return parent::toArray($request); } diff --git a/src/Commands/Generators/stubs/rule.stub b/src/Commands/Generators/stubs/rule.stub index 719a753..e54d7ef 100755 --- a/src/Commands/Generators/stubs/rule.stub +++ b/src/Commands/Generators/stubs/rule.stub @@ -2,34 +2,18 @@ namespace {{ namespace }}; -use App\Ship\Abstracts\Rules\Rule; +use Closure; +use Illuminate\Contracts\Validation\ValidationRule; -class {{ class }} extends Rule +class {{ class }} implements ValidationRule { - public function __construct() - { - // - } - /** - * Determine if the validation rule passes. + * Run the validation rule. * - * @param string $attribute - * @param mixed $value - * @return bool + * @param \Closure(string): \Illuminate\Translation\PotentiallyTranslatedString $fail */ - public function passes($attribute, $value) + public function validate(string $attribute, mixed $value, Closure $fail): void { // } - - /** - * Get the validation error message. - * - * @return string|array - */ - public function message() - { - return 'The validation error message.'; - } } diff --git a/src/Commands/Generators/stubs/seeder/permissions.stub b/src/Commands/Generators/stubs/seeder/permissions.stub index 136e4ef..13772a5 100644 --- a/src/Commands/Generators/stubs/seeder/permissions.stub +++ b/src/Commands/Generators/stubs/seeder/permissions.stub @@ -2,9 +2,9 @@ namespace {{ namespace }}; -use App\Ship\Abstracts\Seeders\Seeder; -use {{ actionNamespace }}\{{ createPermissionAction }}; -use {{ dtoNamespace }}\{{ createPermissionDTO }}; +use {{ createPermissionActionNamespace }}\{{ createPermissionAction }}; +use {{ createPermissionDTONamespace }}\{{ createPermissionDTO }}; +use Illuminate\Database\Seeder; class {{ class }} extends Seeder { @@ -13,33 +13,33 @@ class {{ class }} extends Seeder $createPermissionAction = {{ createPermissionAction }}::make(); $createPermissionAction->handle(new {{ createPermissionDTO }}( - name: 'view-{{ modelPermissionEntity }}', - display_name: 'View any "{{ modelPermissionEntities }}"', - group: '{{ modelPermissionEntities }}' + name: 'view-{{ modelKebabCase }}', + display_name: 'View any "{{ modelsKebabCase }}"', + group: '{{ modelsKebabCase }}' )); $createPermissionAction->handle(new {{ createPermissionDTO }}( - name: 'create-{{ modelPermissionEntity }}', - display_name: 'Create "{{ modelPermissionEntities }}"', - group: '{{ modelPermissionEntities }}' + name: 'create-{{ modelKebabCase }}', + display_name: 'Create "{{ modelsKebabCase }}"', + group: '{{ modelsKebabCase }}' )); $createPermissionAction->handle(new {{ createPermissionDTO }}( - name: 'update-{{ modelPermissionEntity }}', - display_name: 'Update any "{{ modelPermissionEntities }}"', - group: '{{ modelPermissionEntities }}' + name: 'update-{{ modelKebabCase }}', + display_name: 'Update any "{{ modelsKebabCase }}"', + group: '{{ modelsKebabCase }}' )); $createPermissionAction->handle(new {{ createPermissionDTO }}( - name: 'delete-{{ modelPermissionEntity }}', - display_name: 'Delete any "{{ modelPermissionEntities }}"', - group: '{{ modelPermissionEntities }}' + name: 'delete-{{ modelKebabCase }}', + display_name: 'Delete any "{{ modelsKebabCase }}"', + group: '{{ modelsKebabCase }}' )); $createPermissionAction->handle(new {{ createPermissionDTO }}( - name: 'force-delete-{{ modelPermissionEntity }}', - display_name: 'Force delete any "{{ modelPermissionEntities }}"', - group: '{{ modelPermissionEntities }}' + name: 'force-delete-{{ modelKebabCase }}', + display_name: 'Force delete any "{{ modelsKebabCase }}"', + group: '{{ modelsKebabCase }}' )); } } diff --git a/src/Commands/Generators/stubs/seeder/plain.stub b/src/Commands/Generators/stubs/seeder/plain.stub index 613b56b..e8c583f 100644 --- a/src/Commands/Generators/stubs/seeder/plain.stub +++ b/src/Commands/Generators/stubs/seeder/plain.stub @@ -2,7 +2,7 @@ namespace {{ namespace }}; -use App\Ship\Abstracts\Seeders\Seeder; +use Illuminate\Database\Seeder; class {{ class }} extends Seeder { diff --git a/src/Commands/Generators/stubs/test/api/create.stub b/src/Commands/Generators/stubs/test/api/create.stub index 9d897b6..eab8d18 100644 --- a/src/Commands/Generators/stubs/test/api/create.stub +++ b/src/Commands/Generators/stubs/test/api/create.stub @@ -2,34 +2,30 @@ namespace {{ namespace }}; -use App\Ship\Abstracts\Tests\TestCase; -use Illuminate\Testing\Fluent\AssertableJson; use {{ modelNamespace }}\{{ model }}; +use Illuminate\Foundation\Testing\RefreshDatabase; +use Illuminate\Testing\Fluent\AssertableJson; +use Tests\TestCase; /** - * @group {{ moduleKey }} + * @group {{ group }} * @group api */ class {{ class }} extends TestCase { + use RefreshDatabase; + /** * Roles and permissions, to be attached on the user by default */ - protected array $access = [ - 'permissions' => 'create-{{ modelPermissionEntity }}', - 'roles' => '', + protected array $testUserAccess = [ + 'permissions' => 'create-{{ modelKebabCase }}', + 'roles' => '', ]; - protected function getTestData(array $mergeData = []): array - { - return array_merge([ - // TODO: add fields here - ], $mergeData); - } - - public function test_create_{{ modelSnake }}(): void + public function test_create_{{ modelSnakeCase }}(): void { - $this->getTestingUser(); + $this->actingAsTestUser(); $data = $this->getTestData(); @@ -40,19 +36,45 @@ class {{ class }} extends TestCase $json->has('id') ->whereAll($data) ->etc() - ) + ) ); - $this->assertExistsModelWhereColumns({{ model }}::class, $data); + $this->assertDatabaseHas({{ model }}::class, $data); } - public function test_create_{{ modelSnake }}_without_access(): void + public function test_create_{{ modelSnakeCase }}_with_invalid_data(): void { - $this->getTestingUserWithoutAccess(); + $this->actingAsTestUser(); + + $this->postJson(route('{{ routeName }}'), []) + ->assertUnprocessable() + ->assertJsonValidationErrors([ + // TODO: add expected validation errors here + ]); + } + + public function test_create_{{ modelSnakeCase }}_unauthenticated(): void + { + $data = $this->getTestData(); + + $this->postJson(route('{{ routeName }}'), $data) + ->assertUnauthorized(); + } + + public function test_create_{{ modelSnakeCase }}_without_access(): void + { + $this->actingAsTestUserWithoutAccess(); $data = $this->getTestData(); $this->postJson(route('{{ routeName }}'), $data) ->assertForbidden(); } + + protected function getTestData(array $mergeData = []): array + { + return array_merge([ + // TODO: add fields here + ], $mergeData); + } } diff --git a/src/Commands/Generators/stubs/test/api/delete.stub b/src/Commands/Generators/stubs/test/api/delete.stub index 6c1b6ae..412c5df 100644 --- a/src/Commands/Generators/stubs/test/api/delete.stub +++ b/src/Commands/Generators/stubs/test/api/delete.stub @@ -2,50 +2,61 @@ namespace {{ namespace }}; -use App\Ship\Abstracts\Tests\TestCase; use {{ modelNamespace }}\{{ model }}; +use Illuminate\Foundation\Testing\RefreshDatabase; +use Tests\TestCase; /** - * @group {{ moduleKey }} + * @group {{ group }} * @group api */ class {{ class }} extends TestCase { + use RefreshDatabase; + /** * Roles and permissions, to be attached on the user by default */ - protected array $access = [ - 'permissions' => 'delete-{{ modelPermissionEntity }}', - 'roles' => '', + protected array $testUserAccess = [ + 'permissions' => 'delete-{{ modelKebabCase }}', + 'roles' => '', ]; - public function test_delete_{{ modelSnake }}(): void + public function test_delete_{{ modelSnakeCase }}(): void { - $this->getTestingUser(); + $this->actingAsTestUser(); - ${{ modelEntity }} = {{ model }}::factory()->create(); + ${{ modelCamelCase }} = {{ model }}::factory()->create(); - $this->deleteJson(route('{{ routeName }}', ['{{ modelEntity }}' => ${{ modelEntity }}->getKey()])) + $this->deleteJson(route('{{ routeName }}', ['{{ modelCamelCase }}' => ${{ modelCamelCase }}->getKey()])) ->assertNoContent(); - $this->assertNull({{ model }}::find(${{ modelEntity }}->getKey())); + $this->assertNull({{ model }}::find(${{ modelCamelCase }}->getKey())); + } + + public function test_delete_{{ modelSnakeCase }}_unauthenticated(): void + { + ${{ modelCamelCase }} = {{ model }}::factory()->create(); + + $this->deleteJson(route('{{ routeName }}', ['{{ modelCamelCase }}' => ${{ modelCamelCase }}->getKey()])) + ->assertUnauthorized(); } - public function test_delete_{{ modelSnake }}_without_access(): void + public function test_delete_{{ modelSnakeCase }}_without_access(): void { - $this->getTestingUserWithoutAccess(); + $this->actingAsTestUserWithoutAccess(); - ${{ modelEntity }} = {{ model }}::factory()->create(); + ${{ modelCamelCase }} = {{ model }}::factory()->create(); - $this->deleteJson(route('{{ routeName }}', ['{{ modelEntity }}' => ${{ modelEntity }}->getKey()])) + $this->deleteJson(route('{{ routeName }}', ['{{ modelCamelCase }}' => ${{ modelCamelCase }}->getKey()])) ->assertForbidden(); } - public function test_delete_not_existing_{{ modelSnake }}(): void + public function test_delete_not_existing_{{ modelSnakeCase }}(): void { - $this->getTestingUser(); + $this->actingAsTestUser(); - $this->deleteJson(route('{{ routeName }}', ['{{ modelEntity }}' => 7777])) + $this->deleteJson(route('{{ routeName }}', ['{{ modelCamelCase }}' => 7777])) ->assertNotFound(); } } diff --git a/src/Commands/Generators/stubs/test/api/list.stub b/src/Commands/Generators/stubs/test/api/list.stub index 4ae82a1..24871ee 100644 --- a/src/Commands/Generators/stubs/test/api/list.stub +++ b/src/Commands/Generators/stubs/test/api/list.stub @@ -2,28 +2,31 @@ namespace {{ namespace }}; -use App\Ship\Abstracts\Tests\TestCase; use {{ modelNamespace }}\{{ model }}; +use Illuminate\Foundation\Testing\RefreshDatabase; +use Tests\TestCase; /** - * @group {{ moduleKey }} + * @group {{ group }} * @group api */ class {{ class }} extends TestCase { + use RefreshDatabase; + /** * Roles and permissions, to be attached on the user by default */ - protected array $access = [ - 'permissions' => 'view-{{ modelPermissionEntity }}', - 'roles' => '', + protected array $testUserAccess = [ + 'permissions' => 'view-{{ modelKebabCase }}', + 'roles' => '', ]; - public function test_list_{{ modelsSnake }}(): void + public function test_list_{{ modelsSnakeCase }}(): void { - $this->getTestingUser(); + $this->actingAsTestUser(); - {{ model }}::factory()->count(2)->create(); + {{ model }}::factory()->count(3)->create(); $this->getJson(route('{{ routeName }}')) ->assertOk() @@ -32,14 +35,22 @@ class {{ class }} extends TestCase 'meta', 'data' ]) - ->assertJsonCount({{ model }}::query()->count(), 'data'); + ->assertJsonCount(3, 'data'); + } + + public function test_list_{{ modelsSnakeCase }}_unauthenticated(): void + { + {{ model }}::factory()->count(3)->create(); + + $this->getJson(route('{{ routeName }}')) + ->assertUnauthorized(); } - public function test_list_{{ modelsSnake }}_without_access(): void + public function test_list_{{ modelsSnakeCase }}_without_access(): void { - $this->getTestingUserWithoutAccess(); + $this->actingAsTestUserWithoutAccess(); - {{ model }}::factory()->count(2)->create(); + {{ model }}::factory()->count(3)->create(); $this->getJson(route('{{ routeName }}')) ->assertForbidden(); diff --git a/src/Commands/Generators/stubs/test/api/plain.stub b/src/Commands/Generators/stubs/test/api/plain.stub index 9cd9b44..f1b1c75 100644 --- a/src/Commands/Generators/stubs/test/api/plain.stub +++ b/src/Commands/Generators/stubs/test/api/plain.stub @@ -2,20 +2,23 @@ namespace {{ namespace }}; -use App\Ship\Abstracts\Tests\TestCase; +use Illuminate\Foundation\Testing\RefreshDatabase; +use Tests\TestCase; /** - * @group {{ moduleKey }} + * @group {{ group }} * @group api */ class {{ class }} extends TestCase { + use RefreshDatabase; + /** * Roles and permissions, to be attached on the user by default */ - protected array $access = [ + protected array $testUserAccess = [ 'permissions' => '', - 'roles' => '', + 'roles' => '', ]; public function test(): void diff --git a/src/Commands/Generators/stubs/test/api/update.stub b/src/Commands/Generators/stubs/test/api/update.stub index d76a5f1..3ea2009 100644 --- a/src/Commands/Generators/stubs/test/api/update.stub +++ b/src/Commands/Generators/stubs/test/api/update.stub @@ -2,43 +2,39 @@ namespace {{ namespace }}; -use App\Ship\Abstracts\Tests\TestCase; -use Illuminate\Testing\Fluent\AssertableJson; use {{ modelNamespace }}\{{ model }}; +use Illuminate\Foundation\Testing\RefreshDatabase; +use Illuminate\Testing\Fluent\AssertableJson; +use Tests\TestCase; /** - * @group {{ moduleKey }} + * @group {{ group }} * @group api */ class {{ class }} extends TestCase { + use RefreshDatabase; + /** * Roles and permissions, to be attached on the user by default */ - protected array $access = [ - 'permissions' => 'update-{{ modelPermissionEntity }}', - 'roles' => '', + protected array $testUserAccess = [ + 'permissions' => 'update-{{ modelKebabCase }}', + 'roles' => '', ]; - protected function getTestData(array $mergeData = []): array - { - return array_merge([ - // TODO: add fields here - ], $mergeData); - } - - public function test_update_{{ modelSnake }}(): void + public function test_update_{{ modelSnakeCase }}(): void { - $this->getTestingUser(); + $this->actingAsTestUser(); - ${{ modelEntity }} = {{ model }}::factory()->create(); + ${{ modelCamelCase }} = {{ model }}::factory()->create(); $data = $this->getTestData(); $expectedData = array_merge($data, [ - 'id' => ${{ modelEntity }}->getKey(), + 'id' => ${{ modelCamelCase }}->getKey(), ]); - $this->patchJson(route('{{ routeName }}', ['{{ modelEntity }}' => ${{ modelEntity }}->getKey()]), $data) + $this->patchJson(route('{{ routeName }}', ['{{ modelCamelCase }}' => ${{ modelCamelCase }}->getKey()]), $data) ->assertOk() ->assertJson(fn (AssertableJson $json) => $json->has('data', fn (AssertableJson $json) => @@ -47,28 +43,58 @@ class {{ class }} extends TestCase ) ); - $this->assertExistsModelWhereColumns({{ model }}::class, $expectedData); + $this->assertDatabaseHas({{ model }}::class, $expectedData); + } + + public function test_update_{{ modelSnakeCase }}_with_invalid_data(): void + { + $this->actingAsTestUser(); + + ${{ modelCamelCase }} = {{ model }}::factory()->create(); + + $this->patchJson(route('{{ routeName }}', ['{{ modelCamelCase }}' => ${{ modelCamelCase }}->getKey()]), []) + ->assertUnprocessable() + ->assertJsonValidationErrors([ + // TODO: add expected validation errors here + ]); + } + + public function test_update_{{ modelSnakeCase }}_unauthenticated(): void + { + ${{ modelCamelCase }} = {{ model }}::factory()->create(); + + $data = $this->getTestData(); + + $this->patchJson(route('{{ routeName }}', ['{{ modelCamelCase }}' => ${{ modelCamelCase }}->getKey()]), $data) + ->assertUnauthorized(); } - public function test_update_{{ modelSnake }}_without_access(): void + public function test_update_{{ modelSnakeCase }}_without_access(): void { - $this->getTestingUserWithoutAccess(); + $this->actingAsTestUserWithoutAccess(); - ${{ modelEntity }} = {{ model }}::factory()->create(); + ${{ modelCamelCase }} = {{ model }}::factory()->create(); $data = $this->getTestData(); - $this->patchJson(route('{{ routeName }}', ['{{ modelEntity }}' => ${{ modelEntity }}->getKey()]), $data) + $this->patchJson(route('{{ routeName }}', ['{{ modelCamelCase }}' => ${{ modelCamelCase }}->getKey()]), $data) ->assertForbidden(); } - public function test_update_non_existing_{{ modelSnake }}(): void + public function test_update_non_existing_{{ modelSnakeCase }}(): void { - $this->getTestingUser(); + $this->actingAsTestUser(); $data = $this->getTestData(); - $this->patchJson(route('{{ routeName }}', ['{{ modelEntity }}' => 7777]), $data) + $this->patchJson(route('{{ routeName }}', ['{{ modelCamelCase }}' => 7777]), $data) ->assertNotFound(); } + + protected function getTestData(array $mergeData = []): array + { + return array_merge([ + // TODO: add fields here + ], $mergeData); + } } diff --git a/src/Commands/Generators/stubs/test/api/view.stub b/src/Commands/Generators/stubs/test/api/view.stub index a8b530c..61e3b16 100644 --- a/src/Commands/Generators/stubs/test/api/view.stub +++ b/src/Commands/Generators/stubs/test/api/view.stub @@ -2,32 +2,35 @@ namespace {{ namespace }}; -use App\Ship\Abstracts\Tests\TestCase; -use Illuminate\Testing\Fluent\AssertableJson; use {{ modelNamespace }}\{{ model }}; +use Illuminate\Foundation\Testing\RefreshDatabase; +use Illuminate\Testing\Fluent\AssertableJson; +use Tests\TestCase; /** - * @group {{ moduleKey }} + * @group {{ group }} * @group api */ class {{ class }} extends TestCase { + use RefreshDatabase; + /** * Roles and permissions, to be attached on the user by default */ - protected array $access = [ - 'permissions' => 'view-{{ modelPermissionEntity }}', - 'roles' => '', + protected array $testUserAccess = [ + 'permissions' => 'view-{{ modelKebabCase }}', + 'roles' => '', ]; - public function test_view_{{ modelSnake }}(): void + public function test_view_{{ modelSnakeCase }}(): void { - $this->getTestingUser(); + $this->actingAsTestUser(); - ${{ modelEntity }} = {{ model }}::factory()->create(); - $expectedData = ${{ modelEntity }}->toArray(); + ${{ modelCamelCase }} = {{ model }}::factory()->create(); + $expectedData = ${{ modelCamelCase }}->toArray(); - $this->getJson(route('{{ routeName }}', ['{{ modelEntity }}' => ${{ modelEntity }}->getKey()])) + $this->getJson(route('{{ routeName }}', ['{{ modelCamelCase }}' => ${{ modelCamelCase }}->getKey()])) ->assertOk() ->assertJson(fn (AssertableJson $json) => $json->has('data', fn (AssertableJson $json) => @@ -37,21 +40,29 @@ class {{ class }} extends TestCase ); } - public function test_view_{{ modelSnake }}_without_access(): void + public function test_view_{{ modelSnakeCase }}_unauthenticated(): void + { + ${{ modelCamelCase }} = {{ model }}::factory()->create(); + + $this->getJson(route('{{ routeName }}', ['{{ modelCamelCase }}' => ${{ modelCamelCase }}->getKey()])) + ->assertUnauthorized(); + } + + public function test_view_{{ modelSnakeCase }}_without_access(): void { - $this->getTestingUserWithoutAccess(); + $this->actingAsTestUserWithoutAccess(); - ${{ modelEntity }} = {{ model }}::factory()->create(); + ${{ modelCamelCase }} = {{ model }}::factory()->create(); - $this->getJson(route('{{ routeName }}', ['{{ modelEntity }}' => ${{ modelEntity }}->getKey()])) + $this->getJson(route('{{ routeName }}', ['{{ modelCamelCase }}' => ${{ modelCamelCase }}->getKey()])) ->assertForbidden(); } - public function test_view_not_existing_{{ modelSnake }}(): void + public function test_view_not_existing_{{ modelSnakeCase }}(): void { - $this->getTestingUser(); + $this->actingAsTestUser(); - $this->getJson(route('{{ routeName }}', ['{{ modelEntity }}' => 7777])) + $this->getJson(route('{{ routeName }}', ['{{ modelCamelCase }}' => 7777])) ->assertNotFound(); } } diff --git a/src/Commands/Generators/stubs/test/cli/plain.stub b/src/Commands/Generators/stubs/test/cli/plain.stub index 38602c3..39003c4 100644 --- a/src/Commands/Generators/stubs/test/cli/plain.stub +++ b/src/Commands/Generators/stubs/test/cli/plain.stub @@ -2,10 +2,10 @@ namespace {{ namespace }}; -use App\Ship\Abstracts\Tests\TestCase; +use Tests\TestCase; /** - * @group {{ moduleKey }} + * @group {{ group }} * @group cli */ class {{ class }} extends TestCase diff --git a/src/Commands/Generators/stubs/test/feature/plain.stub b/src/Commands/Generators/stubs/test/feature/plain.stub index 2bca030..e975e05 100644 --- a/src/Commands/Generators/stubs/test/feature/plain.stub +++ b/src/Commands/Generators/stubs/test/feature/plain.stub @@ -2,10 +2,10 @@ namespace {{ namespace }}; -use App\Ship\Abstracts\Tests\TestCase; +use Tests\TestCase; /** - * @group {{ moduleKey }} + * @group {{ group }} * @group feature */ class {{ class }} extends TestCase diff --git a/src/Commands/Generators/stubs/test/unit/plain.stub b/src/Commands/Generators/stubs/test/unit/plain.stub index baa4038..3257440 100644 --- a/src/Commands/Generators/stubs/test/unit/plain.stub +++ b/src/Commands/Generators/stubs/test/unit/plain.stub @@ -2,10 +2,10 @@ namespace {{ namespace }}; -use App\Ship\Abstracts\Tests\TestCase; +use Tests\TestCase; /** - * @group {{ moduleKey }} + * @group {{ group }} * @group unit */ class {{ class }} extends TestCase diff --git a/src/Commands/Generators/stubs/test/web/create.stub b/src/Commands/Generators/stubs/test/web/create.stub index e59b1fa..c1c366e 100644 --- a/src/Commands/Generators/stubs/test/web/create.stub +++ b/src/Commands/Generators/stubs/test/web/create.stub @@ -2,11 +2,11 @@ namespace {{ namespace }}; -use App\Ship\Abstracts\Tests\TestCase; use {{ modelNamespace }}\{{ model }}; +use Tests\TestCase; /** - * @group {{ moduleKey }} + * @group {{ group }} * @group web */ class {{ class }} extends TestCase @@ -14,9 +14,9 @@ class {{ class }} extends TestCase /** * Roles and permissions, to be attached on the user by default */ - protected array $access = [ - 'permissions' => 'create-{{ modelPermissionEntity }}', - 'roles' => '', + protected array $testUserAccess = [ + 'permissions' => 'create-{{ modelKebabCase }}', + 'roles' => '', ]; protected function getTestData(array $mergeData = []): array @@ -26,21 +26,21 @@ class {{ class }} extends TestCase ], $mergeData); } - public function test_create_{{ modelSnake }}(): void + public function test_create_{{ modelSnakeCase }}(): void { - $this->getTestingUser(); + $this->actingAsTestUser(); $data = $this->getTestData(); $this->post(route('{{ routeName }}'), $data) ->assertCreated(); - $this->assertExistsModelWhereColumns({{ model }}::class, $data); + $this->assertDatabaseHas({{ model }}::class, $data); } - public function test_create_{{ modelSnake }}_without_access(): void + public function test_create_{{ modelSnakeCase }}_without_access(): void { - $this->getTestingUserWithoutAccess(); + $this->actingAsTestUserWithoutAccess(); $data = $this->getTestData(); diff --git a/src/Commands/Generators/stubs/test/web/delete.stub b/src/Commands/Generators/stubs/test/web/delete.stub index ee46de7..9fbf460 100644 --- a/src/Commands/Generators/stubs/test/web/delete.stub +++ b/src/Commands/Generators/stubs/test/web/delete.stub @@ -2,11 +2,11 @@ namespace {{ namespace }}; -use App\Ship\Abstracts\Tests\TestCase; use {{ modelNamespace }}\{{ model }}; +use Tests\TestCase; /** - * @group {{ moduleKey }} + * @group {{ group }} * @group web */ class {{ class }} extends TestCase @@ -14,38 +14,38 @@ class {{ class }} extends TestCase /** * Roles and permissions, to be attached on the user by default */ - protected array $access = [ - 'permissions' => 'delete-{{ modelPermissionEntity }}', - 'roles' => '', + protected array $testUserAccess = [ + 'permissions' => 'delete-{{ modelKebabCase }}', + 'roles' => '', ]; - public function test_delete_{{ modelSnake }}(): void + public function test_delete_{{ modelSnakeCase }}(): void { - $this->getTestingUser(); + $this->actingAsTestUser(); - ${{ modelEntity }} = {{ model }}::factory()->create(); + ${{ modelCamelCase }} = {{ model }}::factory()->create(); - $this->delete(route('{{ routeName }}', ['{{ modelEntity }}' => ${{ modelEntity }}->getKey()])) + $this->delete(route('{{ routeName }}', ['{{ modelCamelCase }}' => ${{ modelCamelCase }}->getKey()])) ->assertNoContent(); - $this->assertNull({{ model }}::find(${{ modelEntity }}->getKey())); + $this->assertNull({{ model }}::find(${{ modelCamelCase }}->getKey())); } - public function test_delete_{{ modelSnake }}_without_access(): void + public function test_delete_{{ modelSnakeCase }}_without_access(): void { - $this->getTestingUserWithoutAccess(); + $this->actingAsTestUserWithoutAccess(); - ${{ modelEntity }} = {{ model }}::factory()->create(); + ${{ modelCamelCase }} = {{ model }}::factory()->create(); - $this->delete(route('{{ routeName }}', ['{{ modelEntity }}' => ${{ modelEntity }}->getKey()])) + $this->delete(route('{{ routeName }}', ['{{ modelCamelCase }}' => ${{ modelCamelCase }}->getKey()])) ->assertForbidden(); } - public function test_delete_not_existing_{{ modelSnake }}(): void + public function test_delete_not_existing_{{ modelSnakeCase }}(): void { - $this->getTestingUser(); + $this->actingAsTestUser(); - $this->delete(route('{{ routeName }}', ['{{ modelEntity }}' => 7777])) + $this->delete(route('{{ routeName }}', ['{{ modelCamelCase }}' => 7777])) ->assertNotFound(); } } diff --git a/src/Commands/Generators/stubs/test/web/plain.stub b/src/Commands/Generators/stubs/test/web/plain.stub index 646711c..ac802d5 100644 --- a/src/Commands/Generators/stubs/test/web/plain.stub +++ b/src/Commands/Generators/stubs/test/web/plain.stub @@ -2,10 +2,10 @@ namespace {{ namespace }}; -use App\Ship\Abstracts\Tests\TestCase; +use Tests\TestCase; /** - * @group {{ moduleKey }} + * @group {{ group }} * @group web */ class {{ class }} extends TestCase diff --git a/src/Commands/Generators/stubs/test/web/update.stub b/src/Commands/Generators/stubs/test/web/update.stub index 64a67f9..82c1cd0 100644 --- a/src/Commands/Generators/stubs/test/web/update.stub +++ b/src/Commands/Generators/stubs/test/web/update.stub @@ -2,11 +2,11 @@ namespace {{ namespace }}; -use App\Ship\Abstracts\Tests\TestCase; use {{ modelNamespace }}\{{ model }}; +use Tests\TestCase; /** - * @group {{ moduleKey }} + * @group {{ group }} * @group web */ class {{ class }} extends TestCase @@ -14,9 +14,9 @@ class {{ class }} extends TestCase /** * Roles and permissions, to be attached on the user by default */ - protected array $access = [ - 'permissions' => 'update-{{ modelPermissionEntity }}', - 'roles' => '', + protected array $testUserAccess = [ + 'permissions' => 'update-{{ modelKebabCase }}', + 'roles' => '', ]; protected function getTestData(array $mergeData = []): array @@ -26,42 +26,42 @@ class {{ class }} extends TestCase ], $mergeData); } - public function test_update_{{ modelSnake }}(): void + public function test_update_{{ modelSnakeCase }}(): void { - $this->getTestingUser(); + $this->actingAsTestUser(); - ${{ modelEntity }} = {{ model }}::factory()->create(); + ${{ modelCamelCase }} = {{ model }}::factory()->create(); $data = $this->getTestData(); $expectedData = array_merge($data, [ - 'id' => ${{ modelEntity }}->getKey(), + 'id' => ${{ modelCamelCase }}->getKey(), ]); - $this->patch(route('{{ routeName }}', ['{{ modelEntity }}' => ${{ modelEntity }}->getKey()]), $data) + $this->patch(route('{{ routeName }}', ['{{ modelCamelCase }}' => ${{ modelCamelCase }}->getKey()]), $data) ->assertOk(); - $this->assertExistsModelWhereColumns({{ model }}::class, $expectedData); + $this->assertDatabaseHas({{ model }}::class, $expectedData); } - public function test_update_{{ modelSnake }}WithoutAccess(): void + public function test_update_{{ modelSnakeCase }}_without_access(): void { - $this->getTestingUserWithoutAccess(); + $this->actingAsTestUserWithoutAccess(); - ${{ modelEntity }} = {{ model }}::factory()->create(); + ${{ modelCamelCase }} = {{ model }}::factory()->create(); $data = $this->getTestData(); - $this->patch(route('{{ routeName }}', ['{{ modelEntity }}' => ${{ modelEntity }}->getKey()]), $data) + $this->patch(route('{{ routeName }}', ['{{ modelCamelCase }}' => ${{ modelCamelCase }}->getKey()]), $data) ->assertForbidden(); } - public function test_update_non_existing_{{ modelSnake }}(): void + public function test_update_non_existing_{{ modelSnakeCase }}(): void { - $this->getTestingUser(); + $this->actingAsTestUser(); $data = $this->getTestData(); - $this->patch(route('{{ routeName }}', ['{{ modelEntity }}' => 7777]), $data) + $this->patch(route('{{ routeName }}', ['{{ modelCamelCase }}' => 7777]), $data) ->assertNotFound(); } } diff --git a/src/Commands/InstallCommand.php b/src/Commands/InstallCommand.php deleted file mode 100755 index fcae1e7..0000000 --- a/src/Commands/InstallCommand.php +++ /dev/null @@ -1,148 +0,0 @@ -argument('name'))) { - return $this->installFromFile(); - } - - $this->install( - $this->argument('name'), - $this->argument('version'), - $this->option('type'), - $this->option('tree') - ); - - return self::SUCCESS; - } - - /** - * Install modules from modules.json file. - */ - protected function installFromFile(): int - { - if (!file_exists($path = base_path('modules.json'))) { - $this->error("File 'modules.json' does not exist in your project root."); - - return self::FAILURE; - } - - $modules = Json::make($path); - - $dependencies = $modules->get('require', []); - - foreach ($dependencies as $module) { - $module = collect($module); - - $this->install( - $module->get('name'), - $module->get('version'), - $module->get('type') - ); - } - - return self::SUCCESS; - } - - /** - * Install the specified module. - * - * @param string $name - * @param string $version - * @param string $type - * @param bool $tree - */ - protected function install($name, $version = 'dev-master', $type = 'composer', $tree = false) - { - $installer = new Installer( - $name, - $version, - $type ?: $this->option('type'), - $tree ?: $this->option('tree') - ); - - $installer->setRepository($this->laravel['modules']); - - $installer->setConsole($this); - - if ($timeout = $this->option('timeout')) { - $installer->setTimeout($timeout); - } - - if ($path = $this->option('path')) { - $installer->setPath($path); - } - - $installer->run(); - - if (!$this->option('no-update')) { - $this->call('module:update', [ - 'module' => $installer->getModuleName(), - ]); - } - } - - /** - * Get the console command arguments. - * - * @return array - */ - protected function getArguments(): array - { - return [ - ['name', InputArgument::OPTIONAL, 'The name of module will be installed.'], - ['version', InputArgument::OPTIONAL, 'The version of module will be installed.'], - ]; - } - - /** - * Get the console command options. - * - * @return array - */ - protected function getOptions(): array - { - return [ - ['timeout', null, InputOption::VALUE_OPTIONAL, 'The process timeout.', null], - ['path', null, InputOption::VALUE_OPTIONAL, 'The installation path.', null], - ['type', null, InputOption::VALUE_OPTIONAL, 'The type of installation.', null], - ['tree', null, InputOption::VALUE_NONE, 'Install the module as a git subtree', null], - ['no-update', null, InputOption::VALUE_NONE, 'Disables the automatic update of the dependencies.', null], - ]; - } -} diff --git a/src/Commands/ListCommand.php b/src/Commands/ListCommand.php old mode 100755 new mode 100644 index 7efa401..67eb3ab --- a/src/Commands/ListCommand.php +++ b/src/Commands/ListCommand.php @@ -2,19 +2,16 @@ namespace Laraneat\Modules\Commands; -use Illuminate\Console\Command; -use Laraneat\Modules\Facades\Modules; use Laraneat\Modules\Module; -use Symfony\Component\Console\Input\InputOption; -class ListCommand extends Command +class ListCommand extends BaseCommand { /** - * The console command name. + * The name and signature of the console command. * * @var string */ - protected $name = 'module:list'; + protected $signature = 'module:list'; /** * The console command description. @@ -28,62 +25,14 @@ class ListCommand extends Command */ public function handle(): int { - $this->table(['Name', 'Status', 'Priority', 'Path'], $this->getRows()); + $this->table( + ['Package Name', 'Namespace', 'Path'], + collect($this->modulesRepository->getModules()) + ->map(fn (Module $module) => [$module->getPackageName(), $module->getNamespace(), $module->getPath()]) + ->values() + ->toArray() + ); return self::SUCCESS; } - - /** - * Get table rows. - * - * @return array - */ - public function getRows(): array - { - $rows = []; - - foreach ($this->getModules() as $module) { - $rows[] = [ - $module->getName(), - $module->isEnabled() ? 'Enabled' : 'Disabled', - $module->get('priority'), - $module->getPath(), - ]; - } - - return $rows; - } - - /** - * @return Module[] - */ - public function getModules(): array - { - switch ($this->option('only')) { - case 'enabled': - return Modules::getByStatus(true); - - case 'disabled': - return Modules::getByStatus(false); - - case 'priority': - return Modules::getOrdered($this->option('direction')); - - default: - return Modules::all(); - } - } - - /** - * Get the console command options. - * - * @return array - */ - protected function getOptions(): array - { - return [ - ['only', 'o', InputOption::VALUE_OPTIONAL, 'Types of modules will be displayed.', null], - ['direction', 'd', InputOption::VALUE_OPTIONAL, 'The direction of ordering.', 'asc'], - ]; - } } diff --git a/src/Commands/MigrateCommand.php b/src/Commands/MigrateCommand.php old mode 100755 new mode 100644 index dd6e488..d6ad43e --- a/src/Commands/MigrateCommand.php +++ b/src/Commands/MigrateCommand.php @@ -2,106 +2,54 @@ namespace Laraneat\Modules\Commands; -use Illuminate\Console\Command; -use Laraneat\Modules\Facades\Modules; -use Laraneat\Modules\Migrations\Migrator; use Laraneat\Modules\Module; -use Symfony\Component\Console\Input\InputArgument; -use Symfony\Component\Console\Input\InputOption; -class MigrateCommand extends Command +class MigrateCommand extends BaseMigrationCommand { /** - * The console command name. + * The name and signature of the console command. * * @var string */ - protected $name = 'module:migrate'; + protected $signature = 'module:migrate + {module?* : Module name(s) or package name(s)} + {--realpath : Indicate any provided migration file paths are pre-resolved absolute paths} + {--database= : The database connection to use} + {--force : Force the operation to run when in production} + {--schema-path= : The path to a schema dump file} + {--pretend : Dump the SQL queries that would be run} + {--seed : Indicates if the seed task should be re-run} + {--seeder= : The class name of the root seeder} + {--step : Force the migrations to be run so they can be rolled back individually}'; /** * The console command description. * * @var string */ - protected $description = 'Migrate the migrations from the specified module or from all modules.'; - - /** - * Execute the console command. - * - * @return int - */ - public function handle(): int - { - $name = $this->argument('module'); - - if ($name) { - $module = Modules::findOrFail($name); - - $this->migrate($module); - - return self::SUCCESS; - } - - foreach (Modules::getOrdered($this->option('direction')) as $module) { - $this->line('Running for module: ' . $module->getName() . ''); - - $this->migrate($module); - } - - return self::SUCCESS; - } + protected $description = 'Migrate the migrations from the specified module(s) or from all modules.'; /** * Run the migration from the specified module. - * - * @param Module $module */ - protected function migrate(Module $module) + protected function executeForModule(Module $module): void { - $path = str_replace(base_path(), '', (new Migrator($module, $this->getLaravel()))->getPath()); - - if ($this->option('subpath')) { - $path .= "/" . $this->option("subpath"); - } - $this->call('migrate', [ - '--path' => $path, + '--path' => $this->getMigrationPaths($module), + '--realpath' => (bool) $this->option('realpath'), '--database' => $this->option('database'), - '--pretend' => $this->option('pretend'), - '--force' => $this->option('force'), + '--schema-path' => $this->option('schema-path'), + '--pretend' => (bool) $this->option('pretend'), + '--step' => (bool) $this->option('step'), + '--force' => true, ]); - if ($this->option('seed')) { - $this->call('module:seed', ['module' => $module->getName(), '--force' => $this->option('force')]); + if ($this->option('seed') && ! $this->option('pretend')) { + $this->call('module:seed', [ + 'module' => $module->getName(), + '--class' => $this->option('seeder'), + '--force' => true, + ]); } } - - /** - * Get the console command arguments. - * - * @return array - */ - protected function getArguments(): array - { - return [ - ['module', InputArgument::OPTIONAL, 'The name of module will be used.'], - ]; - } - - /** - * Get the console command options. - * - * @return array - */ - protected function getOptions(): array - { - return [ - ['direction', 'd', InputOption::VALUE_OPTIONAL, 'The direction of ordering.', 'asc'], - ['database', null, InputOption::VALUE_OPTIONAL, 'The database connection to use.'], - ['pretend', null, InputOption::VALUE_NONE, 'Dump the SQL queries that would be run.'], - ['force', null, InputOption::VALUE_NONE, 'Force the operation to run when in production.'], - ['seed', null, InputOption::VALUE_NONE, 'Indicates if the seed task should be re-run.'], - ['subpath', null, InputOption::VALUE_OPTIONAL, 'Indicate a subpath to run your migrations from'], - ]; - } } diff --git a/src/Commands/MigrateRefreshCommand.php b/src/Commands/MigrateRefreshCommand.php old mode 100755 new mode 100644 index e26f67b..8ce4c2b --- a/src/Commands/MigrateRefreshCommand.php +++ b/src/Commands/MigrateRefreshCommand.php @@ -2,22 +2,27 @@ namespace Laraneat\Modules\Commands; -use Illuminate\Console\Command; -use Laraneat\Modules\Traits\ConsoleHelpersTrait; -use Laraneat\Modules\Traits\ModuleCommandTrait; -use Symfony\Component\Console\Input\InputArgument; -use Symfony\Component\Console\Input\InputOption; +use Illuminate\Console\ConfirmableTrait; -class MigrateRefreshCommand extends Command +class MigrateRefreshCommand extends BaseCommand { - use ConsoleHelpersTrait, ModuleCommandTrait; + use ConfirmableTrait; /** - * The console command name. + * The name and signature of the console command. * * @var string */ - protected $name = 'module:migrate-refresh'; + protected $signature = 'module:migrate:refresh + {module?* : Module name(s) or package name(s)} + {--realpath : Indicate any provided migration file paths are pre-resolved absolute paths} + {--database= : The database connection to use} + {--force : Force the operation to run when in production} + {--schema-path= : The path to a schema dump file} + {--pretend : Dump the SQL queries that would be run} + {--seed : Indicates if the seed task should be re-run} + {--seeder= : The class name of the root seeder} + {--step : Force the migrations to be run so they can be rolled back individually}'; /** * The console command description. @@ -31,52 +36,30 @@ class MigrateRefreshCommand extends Command */ public function handle(): int { - $module = $this->getModule(); + if (! $this->confirmToProceed()) { + return self::FAILURE; + } - $this->call('module:migrate-reset', [ - 'module' => $module->getStudlyName(), + $this->call('module:migrate:reset', [ + 'module' => $this->argument('module'), + '--realpath' => $this->option('realpath'), '--database' => $this->option('database'), - '--force' => $this->option('force'), + '--pretend' => $this->option('pretend'), + '--force' => true, ]); $this->call('module:migrate', [ - 'module' => $module->getStudlyName(), + 'module' => $this->argument('module'), + '--realpath' => $this->option('realpath'), '--database' => $this->option('database'), - '--force' => $this->option('force'), + '--schema-path' => $this->option('schema-path'), + '--pretend' => $this->option('pretend'), + '--seed' => $this->option('seed'), + '--seeder' => $this->option('seeder'), + '--step' => $this->option('step'), + '--force' => true, ]); - if ($this->option('seed')) { - $this->call('module:seed', [ - 'module' => $module->getStudlyName(), - ]); - } - return self::SUCCESS; } - - /** - * Get the console command arguments. - * - * @return array - */ - protected function getArguments(): array - { - return [ - ['module', InputArgument::OPTIONAL, 'The name of module will be used.'], - ]; - } - - /** - * Get the console command options. - * - * @return array - */ - protected function getOptions(): array - { - return [ - ['database', null, InputOption::VALUE_OPTIONAL, 'The database connection to use.'], - ['force', null, InputOption::VALUE_NONE, 'Force the operation to run when in production.'], - ['seed', null, InputOption::VALUE_NONE, 'Indicates if the seed task should be re-run.'], - ]; - } } diff --git a/src/Commands/MigrateResetCommand.php b/src/Commands/MigrateResetCommand.php old mode 100755 new mode 100644 index af5da76..248e9dc --- a/src/Commands/MigrateResetCommand.php +++ b/src/Commands/MigrateResetCommand.php @@ -2,22 +2,21 @@ namespace Laraneat\Modules\Commands; -use Illuminate\Console\Command; -use Laraneat\Modules\Contracts\RepositoryInterface; -use Laraneat\Modules\Facades\Modules; -use Laraneat\Modules\Migrations\Migrator; use Laraneat\Modules\Module; -use Symfony\Component\Console\Input\InputArgument; -use Symfony\Component\Console\Input\InputOption; -class MigrateResetCommand extends Command +class MigrateResetCommand extends BaseMigrationCommand { /** - * The console command name. + * The name and signature of the console command. * * @var string */ - protected $name = 'module:migrate-reset'; + protected $signature = 'module:migrate:reset + {module?* : Module name(s) or package name(s)} + {--realpath : Indicate any provided migration file paths are pre-resolved absolute paths} + {--database= : The database connection to use} + {--force : Force the operation to run when in production} + {--pretend : Dump the SQL queries that would be run}'; /** * The console command description. @@ -27,88 +26,16 @@ class MigrateResetCommand extends Command protected $description = 'Reset the modules migrations.'; /** - * @var RepositoryInterface + * Reset migration from the specified module. */ - protected RepositoryInterface $repository; - - /** - * Execute the console command. - */ - public function handle(): int - { - $name = $this->argument('module'); - - if (!empty($name)) { - $this->reset($name); - - return self::SUCCESS; - } - - foreach (Modules::getOrdered($this->option('direction')) as $module) { - $this->line('Running for module: ' . $module->getName() . ''); - - $this->reset($module); - } - - return self::SUCCESS; - } - - /** - * Rollback migration from the specified module. - * - * @param Module|string $module - */ - public function reset($module): void - { - if (is_string($module)) { - $module = Modules::findOrFail($module); - } - - $migrator = new Migrator($module, $this->getLaravel()); - - $database = $this->option('database'); - - if (!empty($database)) { - $migrator->setDatabase($database); - } - - $migrated = $migrator->reset(); - - if (count($migrated)) { - foreach ($migrated as $migration) { - $this->line("Rollback: {$migration}"); - } - - return; - } - - $this->comment('Nothing to rollback.'); - } - - /** - * Get the console command arguments. - * - * @return array - */ - protected function getArguments(): array - { - return [ - ['module', InputArgument::OPTIONAL, 'The name of module will be used.'], - ]; - } - - /** - * Get the console command options. - * - * @return array - */ - protected function getOptions(): array + protected function executeForModule(Module $module): void { - return [ - ['direction', 'd', InputOption::VALUE_OPTIONAL, 'The direction of ordering.', 'desc'], - ['database', null, InputOption::VALUE_OPTIONAL, 'The database connection to use.'], - ['force', null, InputOption::VALUE_NONE, 'Force the operation to run when in production.'], - ['pretend', null, InputOption::VALUE_NONE, 'Dump the SQL queries that would be run.'], - ]; + $this->call('migrate:reset', [ + '--path' => $this->getMigrationPaths($module), + '--database' => $this->option('database'), + '--realpath' => (bool) $this->option('realpath'), + '--pretend' => (bool) $this->option('pretend'), + '--force' => true, + ]); } } diff --git a/src/Commands/MigrateRollbackCommand.php b/src/Commands/MigrateRollbackCommand.php old mode 100755 new mode 100644 index 6ef86d1..f3f6499 --- a/src/Commands/MigrateRollbackCommand.php +++ b/src/Commands/MigrateRollbackCommand.php @@ -2,21 +2,23 @@ namespace Laraneat\Modules\Commands; -use Illuminate\Console\Command; -use Laraneat\Modules\Facades\Modules; -use Laraneat\Modules\Migrations\Migrator; use Laraneat\Modules\Module; -use Symfony\Component\Console\Input\InputArgument; -use Symfony\Component\Console\Input\InputOption; -class MigrateRollbackCommand extends Command +class MigrateRollbackCommand extends BaseMigrationCommand { /** - * The console command name. + * The name and signature of the console command. * * @var string */ - protected $name = 'module:migrate-rollback'; + protected $signature = 'module:migrate:rollback + {module?* : Module name(s) or package name(s)} + {--realpath : Indicate any provided migration file paths are pre-resolved absolute paths} + {--database= : The database connection to use} + {--force : Force the operation to run when in production} + {--pretend : Dump the SQL queries that would be run} + {--step= : The number of migrations to be reverted} + {--batch= : The batch of migrations (identified by their batch number) to be reverted}'; /** * The console command description. @@ -25,84 +27,19 @@ class MigrateRollbackCommand extends Command */ protected $description = 'Rollback the modules migrations.'; - /** - * Execute the console command. - */ - public function handle(): int - { - $name = $this->argument('module'); - - if (!empty($name)) { - $this->rollback($name); - - return self::SUCCESS; - } - - foreach (Modules::getOrdered($this->option('direction')) as $module) { - $this->line('Running for module: ' . $module->getName() . ''); - - $this->rollback($module); - } - - return self::SUCCESS; - } - /** * Rollback migration from the specified module. - * - * @param Module|string $module - */ - public function rollback($module): void - { - if (is_string($module)) { - $module = Modules::findOrFail($module); - } - - $migrator = new Migrator($module, $this->getLaravel()); - - $database = $this->option('database'); - - if (!empty($database)) { - $migrator->setDatabase($database); - } - - $migrated = $migrator->rollback(); - - if (count($migrated)) { - foreach ($migrated as $migration) { - $this->line("Rollback: {$migration}"); - } - - return; - } - - $this->comment('Nothing to rollback.'); - } - - /** - * Get the console command arguments. - * - * @return array - */ - protected function getArguments(): array - { - return [ - ['module', InputArgument::OPTIONAL, 'The name of module will be used.'], - ]; - } - - /** - * Get the console command options. - * - * @return array */ - protected function getOptions(): array + protected function executeForModule(Module $module): void { - return [ - ['direction', 'd', InputOption::VALUE_OPTIONAL, 'The direction of ordering.', 'desc'], - ['database', null, InputOption::VALUE_OPTIONAL, 'The database connection to use.'], - ['force', null, InputOption::VALUE_NONE, 'Force the operation to run when in production.'], - ['pretend', null, InputOption::VALUE_NONE, 'Dump the SQL queries that would be run.'], - ]; + $this->call('migrate:rollback', [ + '--path' => $this->getMigrationPaths($module), + '--database' => $this->option('database'), + '--step' => $this->option('step') ?: null, + '--batch' => $this->option('batch') ?: null, + '--realpath' => (bool) $this->option('realpath'), + '--pretend' => (bool) $this->option('pretend'), + '--force' => true, + ]); } } diff --git a/src/Commands/MigrateStatusCommand.php b/src/Commands/MigrateStatusCommand.php old mode 100755 new mode 100644 index 2af5c8a..1af1b41 --- a/src/Commands/MigrateStatusCommand.php +++ b/src/Commands/MigrateStatusCommand.php @@ -2,91 +2,43 @@ namespace Laraneat\Modules\Commands; -use Illuminate\Console\Command; -use Laraneat\Modules\Facades\Modules; -use Laraneat\Modules\Migrations\Migrator; use Laraneat\Modules\Module; -use Symfony\Component\Console\Input\InputArgument; -use Symfony\Component\Console\Input\InputOption; -class MigrateStatusCommand extends Command +class MigrateStatusCommand extends BaseMigrationCommand { /** - * The console command name. + * The name and signature of the console command. * * @var string */ - protected $name = 'module:migrate-status'; + protected $signature = 'module:migrate:status + {module?* : Module name(s) or package name(s)} + {--realpath : Indicate any provided migration file paths are pre-resolved absolute paths} + {--database= : The database connection to use} + {--pending : Only list pending migrations}'; /** * The console command description. * * @var string */ - protected $description = 'Status for all module migrations'; + protected $description = 'Show the status of the modules migrations.'; /** - * Execute the console command. - * - * @return int + * Whether to require confirmation in production. */ - public function handle(): int - { - $name = $this->argument('module'); - - if ($name) { - $module = Modules::findOrFail($name); - - $this->migrateStatus($module); - - return self::SUCCESS; - } - - foreach (Modules::getOrdered($this->option('direction')) as $module) { - $this->line('Running for module: ' . $module->getName() . ''); - $this->migrateStatus($module); - } - - return self::SUCCESS; - } + protected bool $requiresConfirmation = false; /** - * Run the migration from the specified module. - * - * @param Module $module + * Show migration status from the specified module. */ - protected function migrateStatus(Module $module): void + protected function executeForModule(Module $module): void { - $path = str_replace(base_path(), '', (new Migrator($module, $this->getLaravel()))->getPath()); - $this->call('migrate:status', [ - '--path' => $path, + '--path' => $this->getMigrationPaths($module), '--database' => $this->option('database'), + '--realpath' => (bool) $this->option('realpath'), + '--pending' => (bool) $this->option('pending'), ]); } - - /** - * Get the console command arguments. - * - * @return array - */ - protected function getArguments(): array - { - return [ - ['module', InputArgument::OPTIONAL, 'The name of module will be used.'], - ]; - } - - /** - * Get the console command options. - * - * @return array - */ - protected function getOptions(): array - { - return [ - ['direction', 'd', InputOption::VALUE_OPTIONAL, 'The direction of ordering.', 'asc'], - ['database', null, InputOption::VALUE_OPTIONAL, 'The database connection to use.'], - ]; - } } diff --git a/src/Commands/ModuleDeleteCommand.php b/src/Commands/ModuleDeleteCommand.php old mode 100755 new mode 100644 index 63a7874..e0ae045 --- a/src/Commands/ModuleDeleteCommand.php +++ b/src/Commands/ModuleDeleteCommand.php @@ -2,28 +2,60 @@ namespace Laraneat\Modules\Commands; -use Illuminate\Console\Command; -use Laraneat\Modules\Facades\Modules; -use Symfony\Component\Console\Input\InputArgument; +use Illuminate\Console\ConfirmableTrait; +use Laraneat\Modules\Exceptions\ComposerException; +use Laraneat\Modules\Exceptions\ModuleHasNoNamespace; +use Laraneat\Modules\Exceptions\ModuleHasNonUniquePackageName; +use Laraneat\Modules\Exceptions\ModuleNotFound; -class ModuleDeleteCommand extends Command +class ModuleDeleteCommand extends BaseCommand { - protected $name = 'module:delete'; - protected $description = 'Remove a module from the application'; + use ConfirmableTrait; + + /** + * The name and signature of the console command. + * + * @var string + */ + protected $signature = 'module:delete + {module?* : Module name(s) or package name(s) to delete} + {--force : Force the operation to run when in production}'; + + /** + * The console command description. + * + * @var string + */ + protected $description = 'Delete the specified module(s)'; public function handle(): int { - Modules::delete($this->argument('module')); + if (! $this->confirmToProceed()) { + return self::FAILURE; + } - $this->info("Module {$this->argument('module')} has been deleted."); + try { + $modulesToDelete = $this->getModuleArgumentOrFail(); + } catch (ModuleNotFound|ModuleHasNonUniquePackageName|ModuleHasNoNamespace $exception) { + $this->error($exception->getMessage()); - return self::SUCCESS; - } + return self::FAILURE; + } - protected function getArguments(): array - { - return [ - ['module', InputArgument::REQUIRED, 'The name of module to delete.'], - ]; + foreach ($modulesToDelete as $moduleToDelete) { + try { + $status = $this->modulesRepository->delete($moduleToDelete->getPackageName(), $this->output); + if ($status) { + $this->components->info("Module [{$moduleToDelete->getPackageName()}] has been deleted."); + } else { + $this->components->error("Failed to remove module [{$moduleToDelete->getPackageName()}]."); + } + } catch (ComposerException $exception) { + $this->components->error($exception->getMessage()); + $this->components->info("Please run composer remove {$moduleToDelete->getPackageName()} manually"); + } + } + + return self::SUCCESS; } } diff --git a/src/Commands/SeedCommand.php b/src/Commands/SeedCommand.php deleted file mode 100755 index e2659c9..0000000 --- a/src/Commands/SeedCommand.php +++ /dev/null @@ -1,232 +0,0 @@ -argument('module')) { - $name = Str::studly($name); - $this->moduleSeed($this->getModuleByName($name)); - } else { - $modules = Modules::getOrdered(); - array_walk($modules, [$this, 'moduleSeed']); - $this->info('All modules seeded.'); - } - } catch (\Error $e) { - $e = new ErrorException($e->getMessage(), $e->getCode(), 1, $e->getFile(), $e->getLine(), $e); - $this->reportException($e); - $this->renderException($this->getOutput(), $e); - - return self::FAILURE; - } catch (\Exception $e) { - $this->reportException($e); - $this->renderException($this->getOutput(), $e); - - return self::FAILURE; - } - - return self::SUCCESS; - } - - /** - * @param string $moduleName - * - * @throws RuntimeException - * - * @return Module - */ - public function getModuleByName(string $moduleName): Module - { - if (!Modules::has($moduleName)) { - throw new RuntimeException("Module [$moduleName] does not exists."); - } - - return Modules::find($moduleName); - } - - /** - * @param Module $module - * - * @return void - */ - public function moduleSeed(Module $module): void - { - $seeders = []; - $name = $module->getName(); - $config = $module->get('migration'); - if (is_array($config) && array_key_exists('seeds', $config)) { - foreach ((array)$config['seeds'] as $class) { - if (class_exists($class)) { - $seeders[] = $class; - } - } - } else { - $class = $this->getSeederName($name); //legacy support - if (class_exists($class)) { - $seeders[] = $class; - } else { - //look at other namespaces - $classes = $this->getSeederNames($name); - foreach ($classes as $class) { - if (class_exists($class)) { - $seeders[] = $class; - } - } - } - } - - if (count($seeders) > 0) { - array_walk($seeders, [$this, 'dbSeed']); - $this->info("Module [$name] seeded."); - } - } - - /** - * Seed the specified module. - * - * @param string $className - */ - protected function dbSeed(string $className): void - { - if ($option = $this->option('class')) { - $params['--class'] = Str::finish(substr($className, 0, strrpos($className, '\\')), '\\') . $option; - } else { - $params = ['--class' => $className]; - } - - if ($option = $this->option('database')) { - $params['--database'] = $option; - } - - if ($option = $this->option('force')) { - $params['--force'] = $option; - } - - $this->call('db:seed', $params); - } - - /** - * Get master database seeder name for the specified module. - * - * @param string $name - * - * @return string - */ - public function getSeederName(string $name): string - { - $name = Str::studly($name); - - $namespace = Modules::config('namespace'); - $config = GeneratorHelper::component('seeder'); - $seederPath = str_replace('/', '\\', $config->getPath()); - - return $namespace . '\\' . $name . '\\' . $seederPath . '\\' . $name . 'DatabaseSeeder'; - } - - /** - * Get master database seeder name for the specified module under a different namespace than Modules. - * - * @param string $name - * - * @return array $foundModules array containing namespace paths - */ - public function getSeederNames(string $name): array - { - $name = Str::studly($name); - - $seederPath = GeneratorHelper::component('seeder'); - $seederPath = str_replace('/', '\\', $seederPath->getPath()); - - $foundModules = []; - foreach (Modules::config('scan.paths') as $path) { - $namespace = array_slice(explode('/', $path), -1)[0]; - $foundModules[] = $namespace . '\\' . $name . '\\' . $seederPath . '\\' . $name . 'DatabaseSeeder'; - } - - return $foundModules; - } - - /** - * Report the exception to the exception handler. - * - * @param OutputInterface $output - * @param \Exception $e - * @return void - */ - protected function renderException(OutputInterface $output, \Exception $e) - { - $this->laravel[ExceptionHandler::class]->renderForConsole($output, $e); - } - - /** - * Report the exception to the exception handler. - * - * @param \Exception $e - * @return void - */ - protected function reportException(\Exception $e) - { - $this->laravel[ExceptionHandler::class]->report($e); - } - - /** - * Get the console command arguments. - * - * @return array - */ - protected function getArguments(): array - { - return [ - ['module', InputArgument::OPTIONAL, 'The name of module will be used.'], - ]; - } - - /** - * Get the console command options. - * - * @return array - */ - protected function getOptions(): array - { - return [ - ['class', null, InputOption::VALUE_OPTIONAL, 'The class name of the root seeder.'], - ['database', null, InputOption::VALUE_OPTIONAL, 'The database connection to seed.'], - ['force', null, InputOption::VALUE_NONE, 'Force the operation to run when in production.'], - ]; - } -} diff --git a/src/Commands/SetupCommand.php b/src/Commands/SetupCommand.php deleted file mode 100755 index af45dbb..0000000 --- a/src/Commands/SetupCommand.php +++ /dev/null @@ -1,66 +0,0 @@ -generateModulesFolder(); - } - - /** - * Generate the modules folder. - */ - public function generateModulesFolder() - { - return $this->generateDirectory( - Modules::config('paths.generators'), - 'Modules directory created successfully', - 'Modules directory already exist' - ); - } - - /** - * Generate the specified directory by given $dir. - * - * @param $dir - * @param $success - * @param $error - * @return int - */ - protected function generateDirectory($dir, $success, $error): int - { - if (!$this->laravel['files']->isDirectory($dir)) { - $this->laravel['files']->makeDirectory($dir, 0755, true, true); - - $this->info($success); - - return self::SUCCESS; - } - - $this->error($error); - - return self::FAILURE; - } -} diff --git a/src/Commands/StubPublishCommand.php b/src/Commands/StubPublishCommand.php new file mode 100644 index 0000000..37d6916 --- /dev/null +++ b/src/Commands/StubPublishCommand.php @@ -0,0 +1,54 @@ +ensureDirectoryExists($customStubsPath = GeneratorHelper::getCustomStubsPath()); + $originalStubsPath = realpath(__DIR__ . '/Generators/stubs'); + + foreach ($filesystem->allFiles($originalStubsPath) as $file) { + $from = $file->getRealPath(); + $relativePath = Str::after($file->getRealPath(), $originalStubsPath); + $to = $customStubsPath . '/' . ltrim($relativePath, '/'); + + $onlyExisting = $this->option('existing'); + $force = $this->option('force'); + $toIsExists = $filesystem->exists($to); + + if (($onlyExisting && $toIsExists) || (! $onlyExisting && (! $toIsExists || $force))) { + $filesystem->ensureDirectoryExists(dirname($to)); + $filesystem->put($to, $filesystem->get($from)); + } + } + + $this->components->info(sprintf('Stubs have been successfully published to the "%s" directory.', $customStubsPath)); + } +} diff --git a/src/Commands/SyncCommand.php b/src/Commands/SyncCommand.php new file mode 100644 index 0000000..a85d7dc --- /dev/null +++ b/src/Commands/SyncCommand.php @@ -0,0 +1,45 @@ +modulesRepository->pruneModulesManifest(); + + try { + $this->modulesRepository->syncWithComposer($this->output); + $this->components->info("Modules completed successfully!"); + } catch (ModuleHasNoNamespace|ModuleHasNonUniquePackageName $e) { + $this->components->error($e->getMessage()); + } catch (ComposerException $e) { + $this->components->error($e->getMessage()); + $modulePackageNames = join(" ", array_keys($this->modulesRepository->getModules())); + $this->components->info("Please run composer update {$modulePackageNames} manually"); + } + + return self::SUCCESS; + } +} diff --git a/src/Commands/UnUseCommand.php b/src/Commands/UnUseCommand.php deleted file mode 100755 index 867e44b..0000000 --- a/src/Commands/UnUseCommand.php +++ /dev/null @@ -1,35 +0,0 @@ -info('Previous module used successfully forgotten.'); - - return self::SUCCESS; - } -} diff --git a/src/Commands/UpdateCommand.php b/src/Commands/UpdateCommand.php deleted file mode 100755 index 15b3c82..0000000 --- a/src/Commands/UpdateCommand.php +++ /dev/null @@ -1,68 +0,0 @@ -argument('module'); - - if ($name) { - $this->updateModule($name); - - return self::SUCCESS; - } - - foreach (Modules::getOrdered() as $module) { - $this->updateModule($module->getName()); - } - - return self::SUCCESS; - } - - protected function updateModule(string $moduleName): void - { - $this->line('Running for module: ' . $moduleName . ''); - - Modules::update($moduleName); - - $this->info("Module [{$moduleName}] updated successfully."); - } - - /** - * Get the console command arguments. - * - * @return array - */ - protected function getArguments(): array - { - return [ - ['module', InputArgument::OPTIONAL, 'The name of module will be updated.'], - ]; - } -} diff --git a/src/Commands/UseCommand.php b/src/Commands/UseCommand.php deleted file mode 100755 index 2aa1e1d..0000000 --- a/src/Commands/UseCommand.php +++ /dev/null @@ -1,57 +0,0 @@ -argument('module')); - - if (!Modules::has($module)) { - $this->error("Module [{$module}] does not exists."); - - return self::FAILURE; - } - - Modules::setUsed($module); - - $this->info("Module [{$module}] used successfully."); - - return self::SUCCESS; - } - - /** - * Get the console command arguments. - * - * @return array - */ - protected function getArguments(): array - { - return [ - ['module', InputArgument::REQUIRED, 'The name of module will be used.'], - ]; - } -} diff --git a/src/Contracts/ActivatorInterface.php b/src/Contracts/ActivatorInterface.php deleted file mode 100755 index 6138ca9..0000000 --- a/src/Contracts/ActivatorInterface.php +++ /dev/null @@ -1,43 +0,0 @@ - - */ - public function all(): array; - - /** - * Get cached modules. - * - * @return array - */ - public function getCached(): array; - - /** - * Scan & get all available modules. - * - * @return array - */ - public function scan(): array; - - /** - * Get modules as modules collection instance. - * - * @return Collection - */ - public function toCollection(): Collection; - - /** - * Get scanned paths. - * - * @return string[] - */ - public function getScanPaths(): array; - - /** - * Get list of enabled modules. - * - * @return array - */ - public function allEnabled(): array; - - /** - * Get list of disabled modules. - * - * @return array - */ - public function allDisabled(): array; - - /** - * Get count from all modules. - */ - public function count(): int; - - /** - * Get all ordered modules. - * - * @param string $direction - * - * @return array - */ - public function getOrdered(string $direction = 'asc'): array; - - /** - * Get modules by the given status. - * - * @param bool $status - * - * @return array - */ - public function getByStatus(bool $status): array; - - /** - * Find a specific module. - */ - public function find(string $moduleName): ?Module; - - /** - * Find all modules that are required by a module. If the module cannot be found, throw an exception. - * - * @param string $moduleName - * - * @return array - * @throws ModuleNotFoundException - */ - public function findRequirements(string $moduleName): array; - - /** - * Find a specific module. If there return that, otherwise throw exception. - * - * @throws ModuleNotFoundException - */ - public function findOrFail(string $moduleName): Module; - - /** - * Get path for a specific module. - */ - public function getModulePath(Module|string $module, ?string $extraPath = null): string; - - /** - * Get namespace for a specific module. - */ - public function getModuleNamespace(Module|string $module, ?string $extraNamespace = null): string; - - public function getFilesystem(): Filesystem; - - /** - * Get a specific config data from a configuration file. - */ - public function config(string $key, $default = null); - - /** - * Get default modules path. - */ - public function getDefaultPath(): string; - - /** - * Find a specific module by its alias. - */ - public function findByAlias(string $alias): ?Module; - - /** - * Boot the modules. - */ - public function boot(): void; - - /** - * Register the modules. - */ - public function register(): void; - - /** - * Get asset path for a specific module. - */ - public function assetPath(string $moduleName): string; - - /** - * Delete a specific module. - * - * @throws ModuleNotFoundException - */ - public function delete(string $moduleName): bool; - - /** - * Determine whether the given module is activated. - * - * @throws ModuleNotFoundException - */ - public function isEnabled(string $moduleName): bool; - - /** - * Determine whether the given module is not activated. - * - * @throws ModuleNotFoundException - */ - public function isDisabled(string $moduleName): bool; -} diff --git a/src/Contracts/RunnableInterface.php b/src/Contracts/RunnableInterface.php deleted file mode 100755 index f7d6927..0000000 --- a/src/Contracts/RunnableInterface.php +++ /dev/null @@ -1,11 +0,0 @@ -app = $app; - $this->defaultPath = $defaultPath ? rtrim($defaultPath, '/') : null; - $this->url = $app['url']; - $this->config = $app['config']; - $this->filesystem = $app['files']; - $this->activator = $app[ActivatorInterface::class]; - $this->cache = $app['cache']; - } - - /** - * Register the modules. - */ - public function register(): void - { - foreach ($this->getOrdered() as $module) { - $module->register(); - } - } - - /** - * @inheritDoc - * @see RepositoryInterface::boot() - */ - public function boot(): void - { - foreach ($this->getOrdered() as $module) { - $module->boot(); - } - } - - /** - * Get modules path. - */ - public function getDefaultPath(): string - { - return $this->defaultPath ?: rtrim($this->config('generator.path', base_path('app/Modules')), '/'); - } - - /** - * Get all additional paths. - * - * @return string[] - */ - public function getPaths(): array - { - return $this->paths; - } - - /** - * Add other module location. - */ - public function addLocation(string $path): static - { - $this->paths[] = $path; - $this->flushCache(); - - return $this; - } - - /** - * Get scanned modules paths. - * - * @return string[] - */ - public function getScanPaths(): array - { - $paths = $this->paths; - - $paths[] = $this->getDefaultPath(); - - if ($this->config('scan.enabled')) { - $paths = array_merge($paths, $this->config('scan.paths')); - } - - return array_map(static function ($path) { - return Str::endsWith($path, '/*') ? $path : Str::finish($path, '/*'); - }, $paths); - } - - /** - * Get a module key by its name - */ - public function getModuleKey(Module|string $module): string - { - return $module instanceof Module - ? $module->getKey() - : Str::snake($module, '-'); - } - - /** - * Get path for a specific module. - * - * @throws ModuleNotFoundException - */ - public function getModulePath(Module|string $module, ?string $extraPath = null): string - { - $modulePath = $module instanceof Module - ? $module->getPath() - : $this->findOrFail($module)->getPath(); - - return $extraPath ? $modulePath . '/' . $extraPath : $modulePath; - } - - /** - * Get namespace for a specific module. - * - * @throws ModuleNotFoundException - */ - public function getModuleNamespace(Module|string $module, ?string $extraNamespace = null): string - { - $moduleNamespace = $module instanceof Module - ? $module->getNamespace() - : $this->findOrFail($module)->getNamespace(); - - return $extraNamespace ? $moduleNamespace . '\\' . $extraNamespace : $moduleNamespace; - } - - /** - * Get all modules. - * - * @return array - */ - public function all(): array - { - if (is_array($this->cachedModules)) { - return $this->cachedModules; - } - - if (!$this->config('cache.enabled')) { - return $this->cachedModules = $this->scan(); - } - - return $this->cachedModules = $this->getCached(); - } - - /** - * Get list of enabled modules. - * - * @return array - */ - public function allEnabled(): array - { - return $this->getByStatus(true); - } - - /** - * Get list of disabled modules. - * - * @return array - */ - public function allDisabled(): array - { - return $this->getByStatus(false); - } - - /** - * Get cached modules. - * - * @return array - */ - public function getCached(): array - { - return $this->formatCached($this->getRawCached()); - } - - /** - * Get raw cache of modules. - */ - protected function getRawCached(): array - { - return $this->cache->remember($this->config('cache.key'), $this->config('cache.lifetime'), function () { - return $this->toCollection()->toArray(); - }); - } - - /** - * Get all ordered modules. - * - * @return array - */ - public function getOrdered(string $direction = 'asc'): array - { - $modules = $this->allEnabled(); - - uasort($modules, static function (Module $a, Module $b) use ($direction) { - if ($a->getPriority() === $b->getPriority()) { - return 0; - } - - if ($direction === 'desc') { - return $a->getPriority() < $b->getPriority() ? 1 : -1; - } - - return $a->getPriority() > $b->getPriority() ? 1 : -1; - }); - - return $modules; - } - - /** - * Get modules by status. - * - * @return array - */ - public function getByStatus(bool $status): array - { - $modules = []; - - foreach ($this->all() as $moduleKey => $module) { - if ($module->isStatus($status)) { - $modules[$moduleKey] = $module; - } - } - - return $modules; - } - - /** - * Get & scan all modules. - * - * @return array - */ - public function scan(): array - { - $paths = $this->getScanPaths(); - - $modules = []; - - foreach ($paths as $path) { - $manifests = $this->getFilesystem()->glob("$path/module.json"); - - is_array($manifests) || $manifests = []; - - foreach ($manifests as $manifest) { - $json = Json::make($manifest, $this->filesystem); - $path = dirname($manifest); - $name = (string) $json->get('name'); - $moduleKey = $this->getModuleKey($name); - $namespace = (string) $json->get('namespace', ''); - - $modules[$moduleKey] = $this->createModule( - $this->app, - $name, - $path, - $namespace, - ['module.json' => $json] - ); - } - } - - return $modules; - } - - /** - * Creates a new Module instance - */ - protected function createModule( - Application $app, - string $name, - string $path, - string $namespace, - array $moduleJson = [] - ): Module { - return new Module($app, $name, $path, $namespace, $moduleJson); - } - - /** - * Get all modules as collection instance. - * - * @return Collection - */ - public function toCollection(): Collection - { - return new Collection($this->scan()); - } - - /** - * Determine whether the given module exist. - */ - public function has(string $moduleName): bool - { - $moduleKey = $this->getModuleKey($moduleName); - return array_key_exists($moduleKey, $this->all()); - } - - /** - * Get count from all modules. - */ - public function count(): int - { - return count($this->all()); - } - - /** - * Find a specific module. - */ - public function find(string $moduleName): ?Module - { - $allModules = $this->all(); - $moduleKey = $this->getModuleKey($moduleName); - - return $allModules[$moduleKey] ?? null; - } - - /** - * Find a specific module by its alias. - */ - public function findByAlias(string $alias): ?Module - { - foreach ($this->all() as $module) { - if ($module->getAlias() === $alias) { - return $module; - } - } - - return null; - } - - /** - * Find a specific module, if there return that, otherwise throw exception. - * - * @throws ModuleNotFoundException - */ - public function findOrFail(string $moduleName): Module - { - $module = $this->find($moduleName); - - if ($module !== null) { - return $module; - } - - throw new ModuleNotFoundException("Module [$moduleName] does not exist!"); - } - - /** - * Find all modules that are required by a module. If the module cannot be found, throw an exception. - * - * @return array - * @throws ModuleNotFoundException - */ - public function findRequirements(string $moduleName): array - { - $requirements = []; - - $module = $this->findOrFail($moduleName); - - foreach ($module->getRequires() as $requirementName) { - $requirements[] = $this->findByAlias($requirementName); - } - - return $requirements; - } - - /** - * Format the cached data as array of modules. - * - * @return array - */ - protected function formatCached(array $cached): array - { - $modules = []; - - /** - * @var string $moduleKey - * @var array{name: string, path: string, namespace: string, module_json: array} $moduleArray - */ - foreach ($cached as $moduleKey => $moduleArray) { - $moduleJson = array_map( - fn ($json) => Json::make($json['path'], $this->filesystem, $json['attributes']), - $moduleArray['module_json'] ?? [] - ); - $modules[$moduleKey] = $this->createModule( - $this->app, - $moduleArray['name'], - $moduleArray['path'], - $moduleArray['namespace'], - $moduleJson - ); - } - - return $modules; - } - - /** - * Get all modules as laravel collection instance. - * - * @param bool $status - * - * @return Collection - */ - public function collections(bool $status = true): Collection - { - return new Collection($this->getByStatus($status)); - } - - /** - * @inheritDoc - * @see RepositoryInterface::assetPath() - */ - public function assetPath(string $moduleName): string - { - return rtrim($this->config('paths.assets'), '/') . '/' . $moduleName; - } - - /** - * @inheritDoc - * @see RepositoryInterface::config() - */ - public function config(string $key, $default = null) - { - return $this->config->get('modules.' . $key, $default); - } - - /** - * Get storage path for module used. - */ - public function getUsedStoragePath(): string - { - $directory = storage_path('app/modules'); - if ($this->getFilesystem()->exists($directory) === false) { - $this->getFilesystem()->makeDirectory($directory, 0777, true); - } - - $path = storage_path('app/modules/modules.used'); - if (!$this->getFilesystem()->exists($path)) { - $this->getFilesystem()->put($path, ''); - } - - return $path; - } - - /** - * Set module used for cli session. - * - * @throws ModuleNotFoundException - */ - public function setUsed(string $moduleName): void - { - $module = $this->findOrFail($moduleName); - - $this->getFilesystem()->put($this->getUsedStoragePath(), $module); - } - - /** - * Forget the module used for cli session. - */ - public function forgetUsed(): void - { - if ($this->getFilesystem()->exists($this->getUsedStoragePath())) { - $this->getFilesystem()->delete($this->getUsedStoragePath()); - } - } - - /** - * Get module used for cli session. - * - * @throws ModuleNotFoundException|\Illuminate\Contracts\Filesystem\FileNotFoundException - */ - public function getUsedNow(): Module - { - return $this->findOrFail($this->getFilesystem()->get($this->getUsedStoragePath())); - } - - /** - * Get laravel filesystem instance. - */ - public function getFilesystem(): Filesystem - { - return $this->filesystem; - } - - /** - * Get modules assets path. - */ - public function getAssetsPath(): string - { - return $this->config('paths.assets'); - } - - /** - * Get asset url from a specific module. - * - * @throws InvalidAssetPath - */ - public function asset(string $asset): string - { - if (Str::contains($asset, ':') === false) { - throw InvalidAssetPath::missingModuleName($asset); - } - [$name, $url] = explode(':', $asset); - - $baseUrl = str_replace(public_path() . DIRECTORY_SEPARATOR, '', $this->getAssetsPath()); - - $url = $this->url->asset($baseUrl . "/$name/" . $url); - - return str_replace(['http://', 'https://'], '//', $url); - } - - /** - * @inheritDoc - * @see RepositoryInterface::isEnabled() - */ - public function isEnabled(string $moduleName): bool - { - return $this->findOrFail($moduleName)->isEnabled(); - } - - /** - * @inheritDoc - * @see RepositoryInterface::isDisabled() - */ - public function isDisabled(string $moduleName): bool - { - return !$this->isEnabled($moduleName); - } - - /** - * Enabling a specific module. - * - * @param string $moduleName - * - * @return void - * @throws ModuleNotFoundException - */ - public function enable(string $moduleName): void - { - $this->findOrFail($moduleName)->enable(); - } - - /** - * Disabling a specific module. - * - * @param string $moduleName - * - * @return void - * @throws ModuleNotFoundException - */ - public function disable(string $moduleName): void - { - $this->findOrFail($moduleName)->disable(); - } - - /** - * @inheritDoc - * @see RepositoryInterface::delete() - */ - public function delete(string $moduleName): bool - { - return $this->findOrFail($moduleName)->delete(); - } - - /** - * Update dependencies for the specified module. - */ - public function update(string $moduleName): void - { - (new Updater($this))->update($moduleName); - } - - /** - * Install the specified module. - */ - public function install(string $name, ?string $version = 'dev-master', ?string $type = 'composer', bool $subtree = false): Process - { - $installer = new Installer($name, $version, $type, $subtree); - - return $installer->run(); - } - - /** - * Flush modules cache - */ - public function flushCache(): void - { - $this->cachedModules = null; - $this->cache->forget(config('modules.cache.key')); - $this->activator->flushCache(); - } -} diff --git a/src/Generators/FileGenerator.php b/src/Generators/FileGenerator.php deleted file mode 100755 index f076bad..0000000 --- a/src/Generators/FileGenerator.php +++ /dev/null @@ -1,95 +0,0 @@ -path = $path; - $this->contents = $contents; - $this->filesystem = $filesystem ?: new Filesystem(); - } - - public function getContents(): string - { - return $this->contents; - } - - public function setContents(string $contents): static - { - $this->contents = $contents; - - return $this; - } - - public function getFilesystem(): Filesystem - { - return $this->filesystem; - } - - public function setFilesystem(Filesystem $filesystem): static - { - $this->filesystem = $filesystem; - - return $this; - } - - public function getPath(): string - { - return $this->path; - } - - public function setPath(string $path): static - { - $this->path = $path; - - return $this; - } - - public function withFileOverwrite(bool $overwrite): FileGenerator - { - $this->overwriteFile = $overwrite; - - return $this; - } - - /** - * Generate the file. - * - * @throws FileAlreadyExistException - */ - public function generate(): bool|int - { - $path = $this->getPath(); - - if ($this->overwriteFile === true || !$this->filesystem->exists($path)) { - return $this->filesystem->put($path, $this->getContents()); - } - - throw new FileAlreadyExistException('File already exists!'); - } -} diff --git a/src/Generators/Generator.php b/src/Generators/Generator.php deleted file mode 100755 index 12fa47f..0000000 --- a/src/Generators/Generator.php +++ /dev/null @@ -1,7 +0,0 @@ -module = $module; - $this->setEntityName($entityName ?: $module->getStudlyName()); - $this->setType($type ?: 'api'); - $this->console = $console; - } - - /** - * Get the entity name. - */ - public function getEntityName(): string - { - return $this->entityName ?: $this->module->getStudlyName(); - } - - /** - * Set entity name. - */ - public function setEntityName(string $entityName): static - { - $this->entityName = Str::studly($entityName); - - return $this; - } - - /** - * Get the module components type. - * - * @return string - */ - public function getType(): string - { - return $this->type; - } - - /** - * Set the module components type. - */ - public function setType(string $type): static - { - $this->type = $type; - - return $this; - } - - /** - * Get the laravel console instance. - * - * @return Console|null - */ - public function getConsole(): ?Console - { - return $this->console; - } - - /** - * Set the laravel console instance. - * - * @param Console $console - * - * @return $this - */ - public function setConsole(Console $console) - { - $this->console = $console; - - return $this; - } - - public function generate(): int - { - $name = $this->module->getStudlyName(); - $type = $this->getType(); - - $this->generateBaseComponentsForModule($this->module); - - if ($this->type === 'api') { - $this->generateApiComponentsForModule($this->module); - } - - $this->console->info("[$type] components for [$name] module created successfully."); - - return $this->console::SUCCESS; - } - - public function generateBaseComponentsForModule(Module $module): void - { - $moduleName = $module->getStudlyName(); - $entityName = $this->getEntityName(); - $snakeEntityName = Str::snake($entityName); - $snakePluralEntityName = Str::plural($snakeEntityName); - - if (GeneratorHelper::component('factory')->generate() === true) { - $this->console->call('module:make:factory', [ - 'name' => "{$entityName}Factory", - 'module' => $moduleName, - '--model' => $entityName - ]); - } - - if (GeneratorHelper::component('migration')->generate() === true) { - $this->console->call('module:make:migration', [ - 'name' => "create_{$snakePluralEntityName}_table", - 'module' => $moduleName, - '--stub' => 'create' - ]); - } - - if (GeneratorHelper::component('seeder')->generate() === true) { - $this->console->call('module:make:seeder', [ - 'name' => "{$entityName}PermissionsSeeder_1", - 'module' => $moduleName, - '--stub' => 'permissions', - '--model' => $entityName - ]); - } - - if (GeneratorHelper::component('dto')->generate() === true) { - $this->console->call('module:make:dto', [ - 'name' => "Create{$entityName}DTO", - 'module' => $moduleName, - ]); - $this->console->call('module:make:dto', [ - 'name' => "Update{$entityName}DTO", - 'module' => $moduleName, - ]); - } - - if (GeneratorHelper::component('model')->generate() === true) { - $this->console->call('module:make:model', [ - 'name' => $entityName, - 'module' => $moduleName, - '--stub' => 'full', - '--factory' => "{$entityName}Factory" - ]); - } - - if (GeneratorHelper::component('policy')->generate() === true) { - $this->console->call('module:make:policy', [ - 'name' => "{$entityName}Policy", - 'module' => $moduleName, - '--stub' => 'full', - '--model' => $entityName - ]); - } - } - - public function generateApiComponentsForModule(Module $module): void - { - $actionVerbs = ['create', 'update', 'delete', 'list', 'view']; - - $moduleName = $module->getStudlyName(); - $entityName = $this->getEntityName(); - $pluralEntityName = Str::plural($entityName); - $camelEntityName = Str::camel($entityName); - $dashedPluralEntityName = Str::snake($pluralEntityName, '-'); - $underlinedPluralEntityName = Str::snake($pluralEntityName, '_'); - - if (GeneratorHelper::component('api-query-wizard')->generate() === true) { - $this->console->call('module:make:wizard', [ - 'name' => "{$pluralEntityName}QueryWizard", - 'module' => $moduleName, - '--stub' => 'eloquent', - ]); - $this->console->call('module:make:wizard', [ - 'name' => "{$entityName}QueryWizard", - 'module' => $moduleName, - '--stub' => 'model', - ]); - } - - if (GeneratorHelper::component('api-resource')->generate() === true) { - $this->console->call('module:make:resource', [ - 'name' => "{$entityName}Resource", - 'module' => $moduleName, - '--stub' => 'single' - ]); - } - - foreach ($actionVerbs as $actionVerb) { - $studlyActionVerb = Str::studly($actionVerb); - - $resourceClass = "{$entityName}Resource"; - $dtoClass = "{$studlyActionVerb}{$entityName}DTO"; - $routeName = 'api.' . $underlinedPluralEntityName . '.' . $actionVerb; - - if ($actionVerb === "list") { - $actionClass = "{$studlyActionVerb}{$pluralEntityName}Action"; - $requestClass = "{$studlyActionVerb}{$pluralEntityName}Request"; - $wizardClass = "{$pluralEntityName}QueryWizard"; - } else { - $actionClass = "{$studlyActionVerb}{$entityName}Action"; - $requestClass = "{$studlyActionVerb}{$entityName}Request"; - $wizardClass = "{$entityName}QueryWizard"; - } - - if (GeneratorHelper::component('action')->generate() === true) { - $this->console->call('module:make:action', [ - 'name' => $actionClass, - 'module' => $moduleName, - '--stub' => $actionVerb, - '--dto' => $dtoClass, - '--model' => $entityName, - '--request' => $requestClass, - '--resource' => $resourceClass, - '--wizard' => $wizardClass - ]); - } - - if (GeneratorHelper::component("api-controller")->generate() === true) { - $this->console->call('module:make:controller', [ - 'name' => 'Controller', - 'module' => $moduleName, - '--ui' => 'api' - ]); - } - - if (GeneratorHelper::component("api-request")->generate() === true) { - $this->console->call('module:make:request', [ - 'name' => $requestClass, - 'module' => $moduleName, - '--ui' => 'api', - '--dto' => $dtoClass, - '--stub' => $actionVerb, - '--model' => $entityName, - ]); - } - - if (GeneratorHelper::component("api-route")->generate() === true) { - $actionMethodsMap = [ - 'create' => 'post', - 'update' => 'patch', - 'delete' => 'delete', - 'list' => 'get', - 'view' => 'get' - ]; - - $url = $dashedPluralEntityName; - if (in_array($actionVerb, ['update', 'delete', 'view'])) { - $url .= '/{' . $camelEntityName . '}'; - } - - $filePath = Str::snake(str_replace('Action', '', $actionClass), '_'); - $filePath = 'v1/' . $filePath; - - $this->console->call('module:make:route', [ - 'name' => $filePath, - 'module' => $moduleName, - '--ui' => 'api', - '--action' => $actionClass, - '--method' => $actionMethodsMap[$actionVerb], - '--url' => $url, - '--name' => $routeName - ]); - } - - if (GeneratorHelper::component("api-test")->generate() === true) { - $testClass = $actionVerb === 'list' - ? "{$studlyActionVerb}{$pluralEntityName}Test" - : "{$studlyActionVerb}{$entityName}Test"; - - $this->console->call('module:make:test', [ - 'name' => $testClass, - 'module' => $moduleName, - '--type' => 'api', - '--stub' => $actionVerb, - '--model' => $entityName, - '--route' => $routeName - ]); - } - } - } -} diff --git a/src/Generators/ModuleGenerator.php b/src/Generators/ModuleGenerator.php deleted file mode 100755 index 8002230..0000000 --- a/src/Generators/ModuleGenerator.php +++ /dev/null @@ -1,431 +0,0 @@ -name = $name; - $this->setEntityName($entityName ?: $name); - $this->repository = $repository; - $this->config = $config; - $this->filesystem = $filesystem; - $this->console = $console; - $this->activator = $activator; - } - - /** - * Set entity name. - */ - public function setEntityName(string $entityName): static - { - $this->entityName = Str::studly($entityName); - - return $this; - } - - /** - * Set type. - */ - public function setType(string $type): static - { - $this->type = $type; - - return $this; - } - - /** - * Set active flag. - */ - public function setActive(bool $active): static - { - $this->isActive = $active; - - return $this; - } - - /** - * Get the name of the module to create. By default, in studly case. - */ - public function getName(): string - { - return Str::studly($this->name); - } - - /** - * Get the entity name. - */ - public function getEntityName(): string - { - return $this->entityName ?: $this->getName(); - } - - /** - * Get the laravel config instance. - */ - public function getConfig(): ?Config - { - return $this->config; - } - - /** - * Set the laravel config instance. - */ - public function setConfig(Config $config): static - { - $this->config = $config; - - return $this; - } - - /** - * Set the modules activator - */ - public function setActivator(ActivatorInterface $activator): static - { - $this->activator = $activator; - - return $this; - } - - /** - * Get the laravel filesystem instance. - */ - public function getFilesystem(): ?Filesystem - { - return $this->filesystem; - } - - /** - * Set the laravel filesystem instance. - */ - public function setFilesystem(Filesystem $filesystem): static - { - $this->filesystem = $filesystem; - - return $this; - } - - /** - * Get the laravel console instance. - */ - public function getConsole(): ?Console - { - return $this->console; - } - - /** - * Set the laravel console instance. - */ - public function setConsole(Console $console): static - { - $this->console = $console; - - return $this; - } - - /** - * Get the repository instance. - */ - public function getRepository(): ?FileRepository - { - return $this->repository; - } - - /** - * Set the repository instance. - */ - public function setRepository(FileRepository $repository): static - { - $this->repository = $repository; - - return $this; - } - - /** - * Get the list of folders to be created. - */ - public function getFolders(): array - { - return $this->repository->config('generator.components'); - } - - /** - * Set force status. - */ - public function setForce($force): static - { - $this->force = $force; - - return $this; - } - - /** - * Generate the module. - */ - public function generate(): int - { - $name = $this->getName(); - - if ($this->repository->has($name)) { - if ($this->force) { - $this->repository->delete($name); - } else { - $this->console->error("Module [{$name}] already exist!"); - - return E_ERROR; - } - } - - $this->generateFolders(); - $this->generateScaffoldFiles(); - - $this->activator->setActiveByName($name, $this->isActive); - $this->repository->flushCache(); - - $this->generateProviders(); - - if ($this->type && $this->type !== 'plain') { - $module = $this->repository->findOrFail($name); - $code = (new ModuleComponentsGenerator($module)) - ->setEntityName($this->entityName) - ->setConsole($this->console) - ->generate(); - - if ($code === $this->console::FAILURE) { - return $this->console::FAILURE; - } - } - - $this->console->info("Module [{$name}] created successfully."); - - return $this->console::SUCCESS; - } - - /** - * Generate the folders. - */ - public function generateFolders(): void - { - foreach ($this->getFolders() as $key => $folder) { - $folder = GeneratorHelper::component($key); - - if ($folder->generate() === false) { - continue; - } - - $path = GeneratorHelper::modulePath($this->getName(), $folder->getPath()); - - if (!$this->filesystem->isDirectory($path)) { - $this->filesystem->makeDirectory($path, 0755, true); - if ($folder->withGitKeep()) { - $this->generateGitKeep($path); - } - } - } - } - - public function generateScaffoldFiles(): void - { - $this->generateComposerJsonFile(); - $this->generateModuleJsonFile(); - } - - /** - * Generate module providers. - */ - public function generateProviders(): void - { - if (!GeneratorHelper::component('provider')->generate()) { - return; - } - - $moduleName = $this->getName(); - - $this->console->call('module:make:provider', [ - 'name' => "{$moduleName}ServiceProvider", - 'module' => $moduleName, - '--stub' => 'module' - ]); - - $this->console->call('module:make:provider', [ - 'name' => "RouteServiceProvider", - 'module' => $moduleName, - '--stub' => 'route' - ]); - } - - /** - * Generate git keep to the specified path. - */ - protected function generateGitKeep(string $path): void - { - $this->filesystem->put($path . '/.gitkeep', ''); - } - - /** - * Generate composer.json - */ - protected function generateComposerJsonFile(): void - { - $path = GeneratorHelper::modulePath($this->getName(), 'composer.json'); - - $stubContent = (new Stub('/composer.json.stub', $this->getAllReplacements()))->render(); - - $this->createFile($path, $stubContent); - } - - /** - * Generate the module.json file - */ - protected function generateModuleJsonFile(): void - { - $path = GeneratorHelper::modulePath($this->getName(), 'module.json'); - - $stubContent = (new Stub('/module.json.stub', $this->getAllReplacements()))->render(); - - $this->createFile($path, $stubContent); - } - - /** - * Create a file at the given path, after creating folders if necessary - */ - protected function createFile(string $path, string $content): void - { - $this->filesystem->ensureDirectoryExists(dirname($path)); - $this->filesystem->put($path, $content); - - $this->console->info("Created: `$path`"); - } - - /** - * Get replacements - */ - protected function getAllReplacements(): array - { - return [ - 'authorEmail' => $this->getAuthorEmailReplacement(), - 'authorName' => $this->getAuthorNameReplacement(), - 'moduleKey' => $this->getModuleKeyReplacement(), - 'moduleName' => $this->getModuleNameReplacement(), - 'moduleNamespace' => $this->getModuleNamespaceReplacement(), - 'vendor' => $this->getVendorReplacement() - ]; - } - - /** - * Get replacement for {{ moduleKey }}. - */ - protected function getModuleKeyReplacement(): string - { - return Modules::getModuleKey($this->getName()); - } - - /** - * Get the module name in studly case (replacement for {{ moduleName }}). - */ - protected function getModuleNameReplacement(): string - { - return $this->getName(); - } - - /** - * Get replacement for {{ vendor }}. - */ - protected function getVendorReplacement(): string - { - return $this->repository->config('composer.vendor', ''); - } - - /** - * Get replacement for {{ authorName }}. - */ - protected function getAuthorNameReplacement(): string - { - return $this->repository->config('composer.author.name', ''); - } - - /** - * Get replacement for {{ authorEmail }}. - */ - protected function getAuthorEmailReplacement(): string - { - return $this->repository->config('composer.author.email', ''); - } - - /** - * Get replacement for {{ moduleNamespace }}. - */ - protected function getModuleNamespaceReplacement(): string - { - return str_replace('\\', '\\\\', GeneratorHelper::moduleNamespace($this->getName())); - } -} diff --git a/src/Json.php b/src/Json.php deleted file mode 100755 index ecee94f..0000000 --- a/src/Json.php +++ /dev/null @@ -1,200 +0,0 @@ -path = $path; - $this->filesystem = $filesystem ?: new Filesystem(); - $this->attributes = Collection::make($cachedAttributes ?? $this->retrieveRawAttributes()); - } - - /** - * Make new instance. - */ - public static function make(string $path, ?Filesystem $filesystem = null, ?array $cachedAttributes = null): static - { - return new static($path, $filesystem, $cachedAttributes); - } - - public function getFilesystem(): Filesystem - { - return $this->filesystem; - } - - public function setFilesystem(Filesystem $filesystem): static - { - $this->filesystem = $filesystem; - - return $this; - } - - public function getPath(): string - { - return $this->path; - } - - public function setPath($path): static - { - $this->path = (string) $path; - - return $this; - } - - public function getAttributes(): Collection - { - return $this->attributes; - } - - /** - * Get file contents as array. - * - * @throws FileNotFoundException|JsonException - */ - public function retrieveRawAttributes(): array - { - return json_decode($this->getContents(), true, 512, JSON_THROW_ON_ERROR); - } - - /** - * Get file content. - * - * @return string - * @throws FileNotFoundException - */ - public function getContents(): string - { - return $this->filesystem->get($this->getPath()); - } - - /** - * Convert the given array data to pretty json. - * - * @throws JsonException - */ - public function toJsonPretty(): string - { - return json_encode($this->attributes, JSON_THROW_ON_ERROR | JSON_PRETTY_PRINT); - } - - /** - * Update json contents from array data. - * - * @throws FileNotFoundException|JsonException - */ - public function update(array $data): int|false - { - $this->attributes = new Collection(array_merge($this->attributes->toArray(), $data)); - - return $this->save(); - } - - /** - * Save the current attributes array to the file storage. - * - * @throws JsonException - */ - public function save(): int|false - { - return $this->filesystem->put($this->getPath(), $this->toJsonPretty()); - } - - /** - * Get json attributes as plain array - */ - public function toArray(): array - { - return [ - 'path' => $this->path, - 'attributes' => $this->attributes->toArray(), - ]; - } - - /** - * Get the specified attribute from json file. - */ - public function get($key, $default = null) - { - return $this->attributes->get($key, $default); - } - - /** - * Set a specific key & value. - */ - public function set($key, $value): static - { - $this->attributes->offsetSet($key, $value); - - return $this; - } - - /** - * Handle magic method __get. - */ - public function __get($key) - { - return $this->get($key); - } - - /** - * Handle magic method __set. - */ - public function __set($key, $value) - { - $this->set($key, $value); - } - - /** - * Handle magic method __isset. - */ - public function __isset(string $name): bool - { - return $this->attributes->offsetExists($name); - } - - /** - * Handle call to __call method. - */ - public function __call(string $method, array $arguments = []) - { - if (method_exists($this, $method)) { - return call_user_func_array([$this, $method], $arguments); - } - - return call_user_func_array([$this->attributes, $method], $arguments); - } - - /** - * Handle call to __toString method. - * - * @return string - * @throws FileNotFoundException - */ - public function __toString() - { - return $this->getContents(); - } -} diff --git a/src/Migrations/Migrator.php b/src/Migrations/Migrator.php deleted file mode 100755 index c0d6be5..0000000 --- a/src/Migrations/Migrator.php +++ /dev/null @@ -1,261 +0,0 @@ -module = $module; - $this->laravel = $application; - } - - /** - * Set the database connection to be used - */ - public function setDatabase(string $database): static - { - if ($database) { - $this->database = $database; - } - - return $this; - } - - /** - * Get module instance. - */ - public function getModule(): Module - { - return $this->module; - } - - /** - * Get migration path. - */ - public function getPath(): string - { - $config = $this->module->get('migration'); - - $migrationPath = GeneratorHelper::component('migration'); - $path = (is_array($config) && array_key_exists('path', $config)) ? $config['path'] : $migrationPath->getPath(); - - return $this->module->getExtraPath($path); - } - - /** - * Get migration files. - */ - public function getMigrations(bool $reverse = false): array - { - $files = $this->laravel['files']->glob($this->getPath() . '/*_*.php'); - - // Once we have the array of files in the directory we will just remove the - // extension and take the basename of the file which is all we need when - // finding the migrations that haven't been run against the databases. - if ($files === false) { - return []; - } - - $files = array_map(static function ($file) { - return str_replace('.php', '', basename($file)); - }, $files); - - // Once we have all of the formatted file names we will sort them and since - // they all start with a timestamp this should give us the migrations in - // the order they were actually created by the application developers. - sort($files); - - if ($reverse) { - return array_reverse($files); - } - - return $files; - } - - /** - * Rollback migration. - */ - public function rollback(): array - { - $migrations = $this->getLast($this->getMigrations(true)); - - $this->requireFiles($migrations->toArray()); - - $migrated = []; - - foreach ($migrations as $migration) { - $data = $this->find($migration); - - if ($data->count()) { - $migrated[] = $migration; - - $this->down($migration); - - $data->delete(); - } - } - - return $migrated; - } - - /** - * Reset migration. - */ - public function reset(): array - { - $migrations = $this->getMigrations(true); - - $this->requireFiles($migrations); - - $migrated = []; - - foreach ($migrations as $migration) { - $data = $this->find($migration); - - if ($data->count()) { - $migrated[] = $migration; - - $this->down($migration); - - $data->delete(); - } - } - - return $migrated; - } - - /** - * Run down schema from the given migration name. - */ - public function down(string $migration): void - { - $this->resolve($migration)->down(); - } - - /** - * Run up schema from the given migration name. - */ - public function up(string $migration): void - { - $this->resolve($migration)->up(); - } - - /** - * Resolve a migration instance from a file. - */ - public function resolve(string $file): object - { - $file = implode('_', array_slice(explode('_', $file), 4)); - - $class = Str::studly($file); - - return new $class(); - } - - /** - * Require in all the migration files in a given path. - */ - public function requireFiles(array $files): void - { - $path = $this->getPath(); - foreach ($files as $file) { - $this->laravel['files']->requireOnce($path . '/' . $file . '.php'); - } - } - - /** - * Get table instance. - */ - public function table(): Builder - { - return $this->laravel['db']->connection($this->database ?: null)->table(config('database.migrations')); - } - - /** - * Find migration data from database by given migration name. - */ - public function find(string $migration): Builder - { - return $this->table()->where('migration', $migration); - } - - /** - * Save new migration to database. - */ - public function log(string $migration): bool - { - return $this->table()->insert([ - 'migration' => $migration, - 'batch' => $this->getNextBatchNumber(), - ]); - } - - /** - * Get the next migration batch number. - */ - public function getNextBatchNumber(): int - { - return $this->getLastBatchNumber() + 1; - } - - /** - * Get the last migration batch number. - */ - public function getLastBatchNumber(?array $migrations = null): int - { - $table = $this->table(); - - if (is_array($migrations)) { - $table = $table->whereIn('migration', $migrations); - } - - return $table->max('batch'); - } - - /** - * Get the last migration batch. - */ - public function getLast(array $migrations): Collection - { - $query = $this->table() - ->where('batch', $this->getLastBatchNumber($migrations)) - ->whereIn('migration', $migrations); - - $result = $query->orderBy('migration', 'desc')->get(); - - return collect($result)->map(function ($item) { - return (array) $item; - })->pluck('migration'); - } - - /** - * Get the ran migrations. - */ - public function getRan(): Collection - { - return $this->table()->pluck('migration'); - } -} diff --git a/src/Module.php b/src/Module.php index b8056b8..108ad82 100755 --- a/src/Module.php +++ b/src/Module.php @@ -2,86 +2,59 @@ namespace Laraneat\Modules; -use Illuminate\Contracts\Foundation\Application; use Illuminate\Contracts\Support\Arrayable; -use Illuminate\Filesystem\Filesystem; -use Illuminate\Foundation\AliasLoader; -use Illuminate\Foundation\ProviderRepository; -use Illuminate\Support\Arr; use Illuminate\Support\Str; use Illuminate\Support\Traits\Macroable; -use Laraneat\Modules\Contracts\ActivatorInterface; -use Laraneat\Modules\Facades\Modules; +use Laraneat\Modules\Support\Generator\GeneratorHelper; +/** + * @implements Arrayable + */ class Module implements Arrayable { use Macroable; - /** - * The laravel application instance. - */ - protected Application $app; - - /** - * The module name. - */ + protected string $packageName; protected string $name; - - /** - * The module path. - */ protected string $path; - - /** - * The module namespace. - */ protected string $namespace; /** - * @var array of cached Json objects, keyed by filename - */ - protected array $moduleJson = []; - - /** - * The laravel filesystem instance. + * @param string $packageName The module package name. + * @param string $name The module name. + * @param string $path The module path. + * @param string $namespace The module namespace. + * @param array $providers Module providers + * @param array $aliases Module aliases */ - private Filesystem $filesystem; - - /** - * The activator instance. - */ - private ActivatorInterface $activator; - public function __construct( - Application $app, + string $packageName, string $name, string $path, string $namespace, - array $moduleJson = [] + protected array $providers = [], + protected array $aliases = [], ) { - $this->app = $app; - $this->name = trim($name); + $this->packageName = trim($packageName); + $this->name = trim($name) ?: Str::afterLast($this->packageName, '/'); $this->path = rtrim($path, '/'); $this->namespace = trim($namespace, '\\'); - $this->moduleJson = $moduleJson; - $this->filesystem = $app['files']; - $this->activator = $app[ActivatorInterface::class]; } /** - * Get name. + * Get package name. */ - public function getName(): string + public function getPackageName(): string { - return $this->name; + return $this->packageName; } /** - * Get key. + * Get name. */ - public function getKey(): string + public function getName(): string { - return Str::snake($this->name, '-'); + return $this->name; } /** @@ -93,43 +66,19 @@ public function getStudlyName(): string } /** - * Get name in snake case. + * Get name in kebab case. */ - public function getSnakeName(): string + public function getKebabName(): string { - return Str::snake($this->name); + return Str::kebab(str_replace('_', '-', $this->name)); } /** - * Get description. - */ - public function getDescription(): string - { - return $this->get('description'); - } - - /** - * Get alias. - */ - public function getAlias(): string - { - return $this->get('alias'); - } - - /** - * Get priority. - */ - public function getPriority(): string - { - return $this->get('priority'); - } - - /** - * Get module requirements. + * Get name in snake case. */ - public function getRequires(): array + public function getSnakeName(): string { - return $this->get('requires'); + return Str::snake(str_replace('-', '_', $this->name)); } /** @@ -149,230 +98,68 @@ public function getNamespace(): string } /** - * Bootstrap the application events. - */ - public function boot(): void - { - if ($this->isLoadFilesOnBoot()) { - $this->registerFiles(); - } - - $this->fireEvent('boot'); - } - - /** - * Get json contents from the cache, setting as needed. - */ - public function json(?string $fileName = 'module.json'): Json - { - if ($fileName === null) { - $fileName = 'module.json'; - } - - return Arr::get($this->moduleJson, $fileName, function () use ($fileName) { - return $this->moduleJson[$fileName] = Json::make($this->getExtraPath($fileName), $this->filesystem); - }); - } - - /** - * Get a specific data from json file by given the key. + * Get module providers. + * + * @return array */ - public function get(string $key, $default = null) + public function getProviders(): array { - return $this->json()->get($key, $default); + return $this->providers; } /** - * Get a specific data from composer.json file by given the key. + * Get module aliases. + * + * @return array */ - public function getComposerAttr(string $key, $default = null) + public function getAliases(): array { - return $this->json('composer.json')->get($key, $default); + return $this->aliases; } /** - * Register the module. + * Get sub path. */ - public function register(): void + public function subPath(string $subPath): string { - $this->registerAliases(); - $this->registerProviders(); - - if ($this->isLoadFilesOnBoot() === false) { - $this->registerFiles(); - } - - $this->fireEvent('register'); + return $this->getPath() . '/' . GeneratorHelper::normalizePath($subPath); } /** - * Register the module event. + * Get sub namespace. */ - protected function fireEvent(string $event): void + public function subNamespace(string $subNamespace): string { - $this->app['events']->dispatch(sprintf('modules.%s.' . $event, $this->getKey()), [$this]); - } - - /** - * Get the path to the cached *_module.php file. - */ - public function getCachedServicesPath(): string - { - // This checks if we are running on a Laravel Vapor managed instance - // and sets the path to a writable one (services path is not on a writable storage in Vapor). - if (!is_null(env('VAPOR_MAINTENANCE_MODE', null))) { - return Str::replaceLast('config.php', $this->getSnakeName() . '_module.php', $this->app->getCachedConfigPath()); - } - - return Str::replaceLast('services.php', $this->getSnakeName() . '_module.php', $this->app->getCachedServicesPath()); - } - - /** - * Register the service providers from this module. - */ - public function registerProviders(): void - { - (new ProviderRepository($this->app, new Filesystem(), $this->getCachedServicesPath())) - ->load($this->get('providers', [])); - } - - /** - * Register the aliases from this module. - */ - public function registerAliases(): void - { - $loader = AliasLoader::getInstance(); - foreach ($this->get('aliases', []) as $aliasName => $aliasClass) { - $loader->alias($aliasName, $aliasClass); - } - } - - /** - * Register the files from this module. - */ - protected function registerFiles(): void - { - foreach ($this->get('files', []) as $fileName) { - include $this->path . '/' . $fileName; - } + return $this->getNamespace() . '\\' . GeneratorHelper::normalizeNamespace($subNamespace); } /** * Handle call __toString. */ - public function __toString() - { - return $this->getStudlyName(); - } - - /** - * Determine whether the given status same with the current module status. - */ - public function isStatus(bool $status): bool - { - return $this->activator->hasStatus($this, $status); - } - - /** - * Determine whether the current module activated. - */ - public function isEnabled(): bool - { - return $this->activator->hasStatus($this, true); - } - - /** - * Determine whether the current module not disabled. - */ - public function isDisabled(): bool - { - return !$this->isEnabled(); - } - - /** - * Set active state for current module. - */ - public function setActive(bool $active): void - { - $this->activator->setActive($this, $active); - } - - /** - * Disable the current module. - */ - public function disable(): void + public function __toString(): string { - $this->fireEvent('disabling'); - - $this->activator->disable($this); - $this->flushCache(); - - $this->fireEvent('disabled'); - } - - /** - * Enable the current module. - */ - public function enable(): void - { - $this->fireEvent('enabling'); - - $this->activator->enable($this); - $this->flushCache(); - - $this->fireEvent('enabled'); - } - - /** - * Delete the current module. - */ - public function delete(): bool - { - $this->fireEvent('deleting'); - - $this->activator->delete($this); - $status = $this->json()->getFilesystem()->deleteDirectory($this->getPath()); - $this->flushCache(); - - $this->fireEvent('deleted'); - - return $status; + return $this->getPackageName(); } /** - * Get extra path. - */ - public function getExtraPath(string $path): string - { - return $this->getPath() . '/' . ltrim($path, '/'); - } - - /** - * Check if the module files can be loaded on boot. - */ - protected function isLoadFilesOnBoot(): bool - { - return config('modules.register.files', 'register') === 'boot'; - } - - /** - * Get the module as a plain array. + * @return array{ + * path: string, + * packageName: string, + * name: string, + * namespace: string, + * providers: array, + * aliases: array + * } */ public function toArray(): array { return [ - 'name' => $this->name, 'path' => $this->path, + 'packageName' => $this->packageName, + 'name' => $this->name, 'namespace' => $this->namespace, - 'module_json' => array_map(static fn (Json $json) => $json->toArray(), $this->moduleJson) + 'providers' => $this->providers, + 'aliases' => $this->aliases, ]; } - - /** - * Flush modules cache. - */ - protected function flushCache(): void - { - Modules::flushCache(); - } } diff --git a/src/ModulesRepository.php b/src/ModulesRepository.php new file mode 100644 index 0000000..62152af --- /dev/null +++ b/src/ModulesRepository.php @@ -0,0 +1,396 @@ + + */ + protected array $scanPaths = []; + + /** + * The loaded modules array. + * + * @var array|null + */ + protected ?array $modules = null; + + /** + * Create a new module manifest instance. + */ + public function __construct( + protected Filesystem $filesystem, + protected Composer $composer, + protected string $modulesPath, + protected string $basePath, + protected ?string $modulesManifestPath = null, + ) { + $this->addScanPath($this->modulesPath); + } + + /** + * Get module scan paths. + * + * @return array + */ + public function getScanPaths(): array + { + return $this->scanPaths; + } + + /** + * Add module scan path. + * + * @param string|array $scanPaths + * + * @return $this + */ + public function addScanPath(string|array $scanPaths): static + { + foreach (Arr::wrap($scanPaths) as $scanPath) { + $normalizedScanPath = $this->normalizeScanPath($scanPath); + + if (! $normalizedScanPath || in_array($normalizedScanPath, $this->scanPaths, true)) { + continue; + } + + $this->scanPaths[] = $normalizedScanPath; + } + + $this->modules = null; + + return $this; + } + + /** + * Build the modules manifest and write it to disk if cache enabled. + * + * @return array }> + * + * @throws ModuleHasNoNamespace + * @throws ModuleHasNonUniquePackageName + */ + public function buildModulesManifest(): array + { + $modulesManifest = []; + + foreach ($this->scanPaths as $path) { + $packagePaths = $this->filesystem->glob("$path/composer.json"); + + foreach ($packagePaths as $packagePath) { + $packagePath = GeneratorHelper::normalizePath($packagePath, true); + $composerJsonFile = ComposerJsonFile::create($packagePath); + $packageName = trim($composerJsonFile->get('name') ?? ""); + + if (! $packageName) { + continue; + } + + if (array_key_exists($packageName, $modulesManifest)) { + throw ModuleHasNonUniquePackageName::make($packageName); + } + + $path = str_replace('\\', '/', dirname($packagePath)); + + $moduleData = [ + 'path' => $path, + 'name' => basename($path), + 'namespace' => array_key_first($composerJsonFile->get('autoload.psr-4') ?? []), + 'providers' => $composerJsonFile->get('extra.laravel.providers') ?? [], + 'aliases' => $composerJsonFile->get('extra.laravel.aliases') ?? [], + ]; + + $modulesManifest[$packageName] = $this->validateModuleData($packageName, $moduleData); + } + } + + if ($this->modulesManifestPath) { + $this->write($modulesManifest, $this->modulesManifestPath); + } + + return $modulesManifest; + } + + /** + * Prune modules manifest + */ + public function pruneModulesManifest(): bool + { + $this->modules = null; + + if ($this->modulesManifestPath === null) { + return true; + } + + return $this->filesystem->delete($this->modulesManifestPath); + } + + /** + * Get discovered modules + * + * @return array + * + * @throws ModuleHasNoNamespace + * @throws ModuleHasNonUniquePackageName + */ + public function getModules(): array + { + if ($this->modules !== null) { + return $this->modules; + } + + try { + if ($this->modulesManifestPath && $this->filesystem->isFile($this->modulesManifestPath)) { + return $this->modules = $this->makeModulesFromManifest( + $this->filesystem->getRequire($this->modulesManifestPath) + ); + } + } catch (FileNotFoundException) { + // Manifest file not found, will rebuild + } + + return $this->modules = $this->makeModulesFromManifest($this->buildModulesManifest()); + } + + /** + * Determine whether the given module exist by its package name. + */ + public function has(string $modulePackageName): bool + { + return array_key_exists($modulePackageName, $this->getModules()); + } + + /** + * Get count from all modules. + */ + public function count(): int + { + return count($this->getModules()); + } + + /** + * Find a specific module by its package name. + */ + public function find(string $modulePackageName): ?Module + { + $modulePackageName = trim($modulePackageName); + + return $this->getModules()[$modulePackageName] ?? null; + } + + /** + * Find a specific module by its package name, if there return that, otherwise throw exception. + * + * @throws ModuleNotFound + */ + public function findOrFail(string $modulePackageName): Module + { + $module = $this->find($modulePackageName); + + if ($module === null) { + throw ModuleNotFound::make($modulePackageName); + } + + return $module; + } + + /** + * Filter modules by name. + * + * @return array + */ + public function filterByName(string $moduleName): array + { + $moduleName = trim($moduleName); + $moduleKebabName = Str::kebab($moduleName); + $moduleStudlyName = Str::studly($moduleName); + $foundModules = []; + + foreach ($this->getModules() as $modulePackageName => $module) { + $modulePackageNameWithoutVendor = Str::kebab(Str::afterLast($modulePackageName, '/')); + if ($module->getStudlyName() === $moduleStudlyName || $modulePackageNameWithoutVendor === $moduleKebabName) { + $foundModules[$modulePackageName] = $module; + } + } + + return $foundModules; + } + + /** + * Find a specific module by its name, if there return that, otherwise throw exception. + * + * @return array + * + * @throws ModuleNotFound + */ + public function filterByNameOrFail(string $moduleName): array + { + $modules = $this->filterByName($moduleName); + + if (! $modules) { + throw ModuleNotFound::makeForName($moduleName); + } + + return $modules; + } + + /** + * Delete a specific module by its package name. + * + * @throws ModuleNotFound + * @throws ComposerException + */ + public function delete(string $modulePackageName, \Closure|OutputInterface $output = null): bool + { + $module = $this->findOrFail($modulePackageName); + + $result = $this->filesystem->deleteDirectory($module->getPath()); + + if (! $this->composer->removePackages([$module->getPackageName()], false, $output)) { + throw ComposerException::make("Failed to remove package with composer."); + } + + $this->pruneModulesManifest(); + + return $result; + } + + /** + * Sync modules with composer. + * + * @throws ModuleHasNoNamespace + * @throws ModuleHasNonUniquePackageName + * @throws ComposerException + */ + public function syncWithComposer(\Closure|OutputInterface $output = null): void + { + $composerJsonPath = $this->basePath . '/' . Factory::getComposerFile(); + $composerJsonFile = ComposerJsonFile::create($composerJsonPath); + + foreach ($this->getModules() as $modulePackageName => $module) { + $moduleRelativePath = GeneratorHelper::makeRelativePath($this->basePath, $module->getPath()); + if ($moduleRelativePath !== null) { + $composerJsonFile->addModule($modulePackageName, $moduleRelativePath); + } + } + + $composerJsonFile->save(); + + $modulePackageNames = array_keys($this->getModules()); + if (! $modulePackageNames) { + return; + } + + if (! $this->composer->updatePackages($modulePackageNames, false, $output)) { + throw ComposerException::make("Failed to update package with composer."); + } + } + + /** + * @return array, + * aliases: array + * }> + */ + public function toArray(): array + { + return array_map(static fn (Module $module) => $module->toArray(), $this->getModules()); + } + + /** + * Write the given manifest array to disk. + * + * @param array $manifest + * + * @throws DirectoryMustBePresentAndWritable + */ + protected function write(array $manifest, string $manifestPath): void + { + if (! is_writable($dirname = dirname($manifestPath))) { + throw DirectoryMustBePresentAndWritable::make($dirname); + } + + $this->filesystem->replace( + $manifestPath, + ' } $moduleData + * + * @return array{ path: string, name: string, namespace: string, providers: class-string[], aliases: array } + * + * @throws ModuleHasNoNamespace + */ + protected function validateModuleData(string $packageName, array $moduleData): array + { + if (empty(trim($moduleData['namespace'] ?? ""))) { + throw ModuleHasNoNamespace::make($packageName); + } + + return $moduleData; + } + + /** + * Make Module instances from manifest + * + * @param array }> $manifest + * + * @return array + */ + protected function makeModulesFromManifest(array $manifest): array + { + return collect($manifest) + ->map(fn ($module, $packageName) => $this->makeModuleFromManifestItem($packageName, $module)) + ->all(); + } + + /** + * @param string $packageName + * @param array{ path: string, name: string, namespace: string, providers: class-string[], aliases: array } $moduleData + */ + protected function makeModuleFromManifestItem(string $packageName, array $moduleData): Module + { + return new Module( + packageName: $packageName, + name: $moduleData['name'], + path: $moduleData['path'], + namespace: $moduleData['namespace'], + providers: $moduleData['providers'], + aliases: $moduleData['aliases'], + ); + } +} diff --git a/src/ModulesServiceProvider.php b/src/ModulesServiceProvider.php deleted file mode 100755 index 0c761a6..0000000 --- a/src/ModulesServiceProvider.php +++ /dev/null @@ -1,57 +0,0 @@ -mergeConfigFrom(__DIR__ . '/../config/config.php', 'modules'); - $this->registerServices(); - $this->app->register(ConsoleServiceProvider::class); - } - - /** - * Bootstrap any application services. - */ - public function boot(): void - { - if (app()->runningInConsole()) { - $this->publishes([ - __DIR__ . '/../config/config.php' => config_path('modules.php'), - ], 'config'); - } - $this->app->register(BootstrapServiceProvider::class); - } - - protected function registerServices(): void - { - $this->app->alias(RepositoryInterface::class, 'modules'); - $this->app->singleton(RepositoryInterface::class, function ($app) { - $path = $app['config']->get('modules.generator.path'); - - return new FileRepository($app, $path); - }); - - $this->app->singleton(ActivatorInterface::class, function ($app) { - $activator = $app['config']->get('modules.activator'); - $class = $app['config']->get('modules.activators.' . $activator)['class']; - - if ($class === null) { - throw InvalidActivatorClass::missingConfig(); - } - - return new $class($app); - }); - } -} diff --git a/src/Process/Installer.php b/src/Process/Installer.php deleted file mode 100755 index 2b13af2..0000000 --- a/src/Process/Installer.php +++ /dev/null @@ -1,309 +0,0 @@ -name = $name; - $this->version = $version; - $this->type = $type; - $this->tree = $tree; - } - - /** - * Set destination path. - * - * @param string $path - * - * @return $this - */ - public function setPath(string $path) - { - $this->path = $path; - - return $this; - } - - /** - * Set the module repository instance. - * - * @param RepositoryInterface $repository - * - * @return $this - */ - public function setRepository(RepositoryInterface $repository) - { - $this->repository = $repository; - - return $this; - } - - /** - * Set console command instance. - * - * @param Command $console - * - * @return $this - */ - public function setConsole(Command $console) - { - $this->console = $console; - - return $this; - } - - /** - * Set process timeout. - * - * @param int $timeout - * - * @return $this - */ - public function setTimeout(int $timeout) - { - $this->timeout = $timeout; - - return $this; - } - - /** - * Run the installation process. - * - * @return Process - */ - public function run(): Process - { - $process = $this->getProcess(); - - $process->setTimeout($this->timeout); - - if ($this->console instanceof Command) { - $process->run(function ($type, $line) { - $this->console->line($line); - }); - } - - return $process; - } - - /** - * Get process instance. - * - * @return Process - */ - public function getProcess(): Process - { - if ($this->type) { - if ($this->tree) { - return $this->installViaSubtree(); - } - - return $this->installViaGit(); - } - - return $this->installViaComposer(); - } - - /** - * Get destination path. - * - * @return string - */ - public function getDestinationPath(): string - { - if ($this->path) { - return $this->path; - } - - return GeneratorHelper::path() . '/' . $this->getModuleName(); - } - - /** - * Get git repo url. - * - * @return string|null - */ - public function getRepoUrl(): ?string - { - switch ($this->type) { - case 'github': - return "git@github.com:{$this->name}.git"; - - case 'github-https': - return "https://github.com/{$this->name}.git"; - - case 'gitlab': - return "git@gitlab.com:{$this->name}.git"; - break; - - case 'bitbucket': - return "git@bitbucket.org:{$this->name}.git"; - - default: - // Check of type 'scheme://host/path' - if (filter_var($this->type, FILTER_VALIDATE_URL)) { - return $this->type; - } - - // Check of type 'user@host' - if (filter_var($this->type, FILTER_VALIDATE_EMAIL)) { - return "{$this->type}:{$this->name}.git"; - } - - return null; - } - } - - /** - * Get branch name. - * - * @return string - */ - public function getBranch(): string - { - return is_null($this->version) ? 'master' : $this->version; - } - - /** - * Get module name. - * - * @return string - */ - public function getModuleName(): string - { - $parts = explode('/', $this->name); - - return Str::studly(end($parts)); - } - - /** - * Get composer package name. - * - * @return string - */ - public function getPackageName(): string - { - if (is_null($this->version)) { - return $this->name . ':dev-master'; - } - - return $this->name . ':' . $this->version; - } - - /** - * Install the module via git. - * - * @return Process - */ - public function installViaGit(): Process - { - return Process::fromShellCommandline(sprintf( - 'cd %s && git clone %s %s && cd %s && git checkout %s', - base_path(), - $this->getRepoUrl(), - $this->getDestinationPath(), - $this->getDestinationPath(), - $this->getBranch() - )); - } - - /** - * Install the module via git subtree. - * - * @return Process - */ - public function installViaSubtree(): Process - { - return Process::fromShellCommandline(sprintf( - 'cd %s && git remote add %s %s && git subtree add --prefix=%s --squash %s %s', - base_path(), - $this->getModuleName(), - $this->getRepoUrl(), - $this->getDestinationPath(), - $this->getModuleName(), - $this->getBranch() - )); - } - - /** - * Install the module via composer. - * - * @return Process - */ - public function installViaComposer(): Process - { - return Process::fromShellCommandline(sprintf( - 'cd %s && composer require %s', - base_path(), - $this->getPackageName() - )); - } -} diff --git a/src/Process/Runner.php b/src/Process/Runner.php deleted file mode 100755 index 6ebb766..0000000 --- a/src/Process/Runner.php +++ /dev/null @@ -1,30 +0,0 @@ -repository = $repository; - } - - /** - * Run the given command. - * - * @param string $command - */ - public function run(string $command): void - { - passthru($command); - } -} diff --git a/src/Process/Updater.php b/src/Process/Updater.php deleted file mode 100755 index c8d37c0..0000000 --- a/src/Process/Updater.php +++ /dev/null @@ -1,88 +0,0 @@ -repository->findOrFail($moduleName); - - chdir(base_path()); - - $this->installRequires($module); - $this->installDevRequires($module); - $this->copyScriptsToMainComposerJson($module); - } - - /** - * Check if composer should output anything. - * - * @return string - */ - private function isComposerSilenced(): string - { - return config('modules.composer.composer-output') === false ? ' --quiet' : ''; - } - - /** - * @param Module $module - */ - private function installRequires(Module $module): void - { - $packages = $module->getComposerAttr('require', []); - - $concatenatedPackages = ''; - foreach ($packages as $name => $version) { - $concatenatedPackages .= "\"{$name}:{$version}\" "; - } - - if (!empty($concatenatedPackages)) { - $this->run("composer require {$concatenatedPackages}{$this->isComposerSilenced()}"); - } - } - - /** - * @param Module $module - */ - private function installDevRequires(Module $module): void - { - $devPackages = $module->getComposerAttr('require-dev', []); - - $concatenatedPackages = ''; - foreach ($devPackages as $name => $version) { - $concatenatedPackages .= "\"{$name}:{$version}\" "; - } - - if (!empty($concatenatedPackages)) { - $this->run("composer require --dev {$concatenatedPackages}{$this->isComposerSilenced()}"); - } - } - - /** - * @param Module $module - */ - private function copyScriptsToMainComposerJson(Module $module): void - { - $scripts = $module->getComposerAttr('scripts', []); - - $composer = json_decode(file_get_contents(base_path('composer.json')), true); - - foreach ($scripts as $key => $script) { - if (array_key_exists($key, $composer['scripts'])) { - $composer['scripts'][$key] = array_unique(array_merge($composer['scripts'][$key], $script)); - continue; - } - $composer['scripts'] = array_merge($composer['scripts'], [$key => $script]); - } - - file_put_contents(base_path('composer.json'), json_encode($composer, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT)); - } -} diff --git a/src/Providers/BootstrapServiceProvider.php b/src/Providers/BootstrapServiceProvider.php deleted file mode 100755 index d0c7b32..0000000 --- a/src/Providers/BootstrapServiceProvider.php +++ /dev/null @@ -1,25 +0,0 @@ -app[RepositoryInterface::class]->register(); - } - - /** - * Bootstrap any application services. - */ - public function boot(): void - { - $this->app[RepositoryInterface::class]->boot(); - } -} diff --git a/src/Providers/ComposerServiceProvider.php b/src/Providers/ComposerServiceProvider.php new file mode 100644 index 0000000..1d8ec10 --- /dev/null +++ b/src/Providers/ComposerServiceProvider.php @@ -0,0 +1,31 @@ +app->singleton(Composer::class, function (Application $app) { + return new Composer($app['files'], $app->basePath()); + }); + } + + /** + * Get the services provided by the provider. + * + * @return array + */ + public function provides(): array + { + return [Composer::class]; + } +} diff --git a/src/Providers/ConsoleServiceProvider.php b/src/Providers/ConsoleServiceProvider.php index 32baf5e..551ce58 100644 --- a/src/Providers/ConsoleServiceProvider.php +++ b/src/Providers/ConsoleServiceProvider.php @@ -10,15 +10,12 @@ class ConsoleServiceProvider extends ServiceProvider implements DeferrableProvid { /** * The available commands - * @var array + * + * @var array */ - protected $commands = [ + protected array $commands = [ Commands\CacheClearCommand::class, Commands\CacheCommand::class, - Commands\DisableCommand::class, - Commands\DumpCommand::class, - Commands\EnableCommand::class, - Commands\InstallCommand::class, Commands\ListCommand::class, Commands\MigrateCommand::class, Commands\MigrateRefreshCommand::class, @@ -26,14 +23,10 @@ class ConsoleServiceProvider extends ServiceProvider implements DeferrableProvid Commands\MigrateRollbackCommand::class, Commands\MigrateStatusCommand::class, Commands\ModuleDeleteCommand::class, - Commands\SeedCommand::class, - Commands\SetupCommand::class, - Commands\UnUseCommand::class, - Commands\UpdateCommand::class, - Commands\UseCommand::class, + Commands\StubPublishCommand::class, + Commands\SyncCommand::class, Commands\Generators\ActionMakeCommand::class, Commands\Generators\CommandMakeCommand::class, - Commands\Generators\ComponentsMakeCommand::class, Commands\Generators\ControllerMakeCommand::class, Commands\Generators\DTOMakeCommand::class, Commands\Generators\EventMakeCommand::class, @@ -50,15 +43,18 @@ class ConsoleServiceProvider extends ServiceProvider implements DeferrableProvid Commands\Generators\ObserverMakeCommand::class, Commands\Generators\PolicyMakeCommand::class, Commands\Generators\ProviderMakeCommand::class, - Commands\Generators\RouteMakeCommand::class, Commands\Generators\QueryWizardMakeCommand::class, Commands\Generators\RequestMakeCommand::class, Commands\Generators\ResourceMakeCommand::class, + Commands\Generators\RouteMakeCommand::class, Commands\Generators\RuleMakeCommand::class, Commands\Generators\SeederMakeCommand::class, Commands\Generators\TestMakeCommand::class, ]; + /** + * Register commands + */ public function register(): void { if ($this->app->runningInConsole()) { @@ -69,7 +65,7 @@ public function register(): void /** * Get the services provided by the provider. * - * @return array + * @return array */ public function provides(): array { diff --git a/src/Providers/ModulesRepositoryServiceProvider.php b/src/Providers/ModulesRepositoryServiceProvider.php new file mode 100644 index 0000000..e8392f8 --- /dev/null +++ b/src/Providers/ModulesRepositoryServiceProvider.php @@ -0,0 +1,50 @@ +app->singleton(ModulesRepository::class, function (Application $app) { + return new ModulesRepository( + filesystem: $app['files'], + composer: $app[Composer::class], + modulesPath: $app['config']->get('modules.path'), + basePath: $app->basePath(), + modulesManifestPath: $app['config']->get('modules.cache.enabled') + ? $app->bootstrapPath('cache/laraneat-modules.php') + : null + ); + }); + + $this->app->singleton(ModuleConfigWriter::class, function (Application $app) { + return new ModuleConfigWriter( + modulesRepository: $app[ModulesRepository::class], + ); + }); + } + + /** + * Get the services provided by the provider. + * + * @return array + */ + public function provides(): array + { + return [ + ModulesRepository::class, + ModuleConfigWriter::class, + ]; + } +} diff --git a/src/Providers/ModulesServiceProvider.php b/src/Providers/ModulesServiceProvider.php new file mode 100755 index 0000000..1e62181 --- /dev/null +++ b/src/Providers/ModulesServiceProvider.php @@ -0,0 +1,28 @@ +mergeConfigFrom(__DIR__ . '/../../config/config.php', 'modules'); + } + + /** + * Bootstrap any application services. + */ + public function boot(): void + { + if (app()->runningInConsole()) { + $this->publishes([ + __DIR__ . '/../../config/config.php' => config_path('modules.php'), + ], 'config'); + } + } +} diff --git a/src/Support/Composer.php b/src/Support/Composer.php new file mode 100644 index 0000000..8819813 --- /dev/null +++ b/src/Support/Composer.php @@ -0,0 +1,43 @@ + $packages + * @param bool $dev + * @param Closure|OutputInterface|null $output + * @param string|null $composerBinary + * @return bool + */ + public function updatePackages( + array $packages, + bool $dev = false, + Closure|OutputInterface $output = null, + ?string $composerBinary = null + ): bool { + $command = collect([ + ...$this->findComposer($composerBinary), + 'update', + ...$packages, + ]) + ->when($dev, function ($command) { + $command->push('--dev'); + })->all(); + + return 0 === $this->getProcess($command, ['COMPOSER_MEMORY_LIMIT' => '-1']) + ->run( + $output instanceof OutputInterface + ? function ($type, $line) use ($output) { + $output->write(' '.$line); + } : $output + ); + } +} diff --git a/src/Support/ComposerJsonFile.php b/src/Support/ComposerJsonFile.php new file mode 100644 index 0000000..f6aa7ba --- /dev/null +++ b/src/Support/ComposerJsonFile.php @@ -0,0 +1,149 @@ +composerJsonHandler = new JsonFile($filePath); + } + + public static function create( + string $filePath + ): static { + return new static($filePath); + } + + /** + * Get an item from a json content using "dot" notation. + * + * @param string $key + * @param mixed $default + * @return mixed + */ + public function get(string $key, mixed $default = null): mixed + { + return Arr::get($this->read(), $key, $default); + } + + /** + * Set JSON content item to a given value using "dot" notation. + * + * @param string $key + * @param mixed $value + * @return $this + */ + public function set(string $key, mixed $value): static + { + $this->read(); + Arr::set($this->jsonContent, $key, $value); + + return $this; + } + + public function addModule(string $modulePackageName, string $moduleRelativePath): static + { + $this->addPathRepository(dirname($moduleRelativePath)); + + $packages = $this->get('require'); + + if (! isset($packages[$modulePackageName])) { + $packages[$modulePackageName] = '*'; + $this->set('require', $this->sortPackages($packages)); + } + + return $this; + } + + public function addPathRepository(string $path, array $options = ['symlink' => true]): static + { + $path = Str::finish(GeneratorHelper::normalizePath($path, true), '/*'); + + $repositories = $this->get('repositories', []); + $repositoryAlreadyExists = collect($repositories) + ->contains(fn ($repository) => $repository['url'] === $path); + + if ($repositoryAlreadyExists) { + return $this; + } + + $repositories[] = [ + 'type' => 'path', + 'url' => $path, + 'options' => $options, + ]; + + $this->set('repositories', $repositories); + + return $this; + } + + /** + * Save the JSON content to a file + * + * @throws \Exception + */ + public function save(int $flags = JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE): void + { + if ($this->jsonContent !== null) { + $this->composerJsonHandler->write($this->jsonContent, $flags); + } + } + + protected function read(): array + { + if ($this->jsonContent !== null) { + return $this->jsonContent; + } + + return $this->jsonContent = $this->composerJsonHandler->read(); + } + + /** + * @param array $packages + * @return array + */ + protected function sortPackages(array $packages): array + { + $prefix = fn ($requirement) + => preg_replace( + [ + '/^php$/', + '/^hhvm-/', + '/^ext-/', + '/^lib-/', + '/^\D/', + '/^(?!php$|hhvm-|ext-|lib-)/', + ], + [ + '0-$0', + '1-$0', + '2-$0', + '3-$0', + '4-$0', + '5-$0', + ], + $requirement + ); + + uksort($packages, function ($a, $b) use ($prefix) { + return strnatcmp($prefix($a), $prefix($b)); + }); + + return $packages; + } +} diff --git a/src/Support/Concerns/CanLoadRoutesFromDirectory.php b/src/Support/Concerns/CanLoadRoutesFromDirectory.php new file mode 100644 index 0000000..4a65ce1 --- /dev/null +++ b/src/Support/Concerns/CanLoadRoutesFromDirectory.php @@ -0,0 +1,53 @@ +loadRoutesFromDirectory( + $nestedDirectory, + $directoryRoutePrefix, + $generateRoutePrefixesByNestedDirectories + ); + } + + /** @var SplFileInfo[] $files */ + $files = Arr::sort(File::files($directory), function (SplFileInfo $file) { + return $file->getFilename(); + }); + + Route::prefix($routePrefix)->group(function () use ($files) { + foreach ($files as $file) { + require $file->getPathname(); + } + }); + } +} diff --git a/src/Traits/SeederLoaderTrait.php b/src/Support/Concerns/CanRunModuleSeeders.php similarity index 91% rename from src/Traits/SeederLoaderTrait.php rename to src/Support/Concerns/CanRunModuleSeeders.php index 6a5fbd3..022f763 100644 --- a/src/Traits/SeederLoaderTrait.php +++ b/src/Support/Concerns/CanRunModuleSeeders.php @@ -1,12 +1,13 @@ getFullPath($module); + $moduleSeedersPath = GeneratorHelper::component(ModuleComponentType::Seeder)->getFullPath($module); $paths = [$moduleSeedersPath]; - if (!empty($subdirectories)) { + if (! empty($subdirectories)) { $paths += array_map( static fn ($subdirectory) => rtrim($moduleSeedersPath, '/') . '/' . ltrim($subdirectory, '/'), @@ -109,15 +110,17 @@ protected function getClassNamespaceFromFile(string $filePathName): ?string if ($tokens[$i] === ';') { $namespace_ok = true; $namespace = trim($namespace); + break; } $namespace .= is_array($tokens[$i]) ? $tokens[$i][1] : $tokens[$i]; } + break; } $i++; } - if (!$namespace_ok) { + if (! $namespace_ok) { return null; } diff --git a/src/Support/Concerns/InteractsWithTestUser.php b/src/Support/Concerns/InteractsWithTestUser.php new file mode 100644 index 0000000..c044c0a --- /dev/null +++ b/src/Support/Concerns/InteractsWithTestUser.php @@ -0,0 +1,186 @@ +|null + */ + protected ?string $testUserClass = null; + + /** + * Roles and permissions, to be attached on the user by default + * + * @var array{permissions?: string|array, roles?:string|array} + */ + protected array $testUserAccess = [ + 'permissions' => '', + 'roles' => '', + ]; + + protected ?string $testUserGuard = null; + + /** + * Create a test user without access and make him logged into the application. + * Same as `besTestUser()` but always overrides the user $access + * (roles and permissions) with null. So the user can be used to test + * if unauthorized user tried to access your protected endpoint. + * + * @param array|null $userDetails + * @param string|null $guard + * + * @return $this + */ + public function beTestUserWithoutAccess(?array $userDetails = null, ?string $guard = null): static + { + return $this->actingAsTestUserWithoutAccess($userDetails, $guard); + } + + /** + * Create a test user and make him logged into the application. + * + * @param array|null $userDetails + * @param array{permissions?: string|array, roles?:string|array}|null $access + * @param string|null $guard + * + * @return $this + */ + public function beTestUser(?array $userDetails = null, ?array $access = null, ?string $guard = null): static + { + return $this->actingAsTestUser($userDetails, $access, $guard); + } + + /** + * Create a test user without access and make him logged into the application. + * Same as `actingAsTestUser()` but always overrides the user $access + * (roles and permissions) with null. So the user can be used to test + * if unauthorized user tried to access your protected endpoint. + * + * @param array|null $userDetails + * @param string|null $guard + * + * @return $this + */ + public function actingAsTestUserWithoutAccess(?array $userDetails = null, ?string $guard = null): static + { + return $this->actingAs( + $this->createTestUserWithoutAccess($userDetails), + $guard ?? $this->testUserGuard + ); + } + + /** + * Create a test user and make him logged into the application. + * + * @param array|null $userDetails + * @param array{permissions?: string|array, roles?:string|array}|null $access + * @param string|null $guard + * + * @return $this + */ + public function actingAsTestUser(?array $userDetails = null, ?array $access = null, ?string $guard = null): static + { + return $this->actingAs( + $this->createTestUser($userDetails, $access), + $guard ?? $this->testUserGuard + ); + } + + /** + * Create test user without access. + * Same as `createTestUser()` but always overrides the user $access + * (roles and permissions) with null. So the user can be used to test + * if unauthorized user tried to access your protected endpoint. + * + * @param array|null $userDetails + * @return U + */ + public function createTestUserWithoutAccess(?array $userDetails = null): UserContract + { + return $this->createTestUser($userDetails, [ + 'permissions' => null, + 'roles' => null, + ]); + } + + /** + * Create test user. + * By default, Users will be given the Roles and Permissions found in the class + * `$access` property. But the $access parameter can be used to override the + * defined roles and permissions in the `$access` property of your class. + * + * @param array|null $userDetails + * @param array{permissions?: string|array, roles?:string|array}|null $access + * @return U + */ + public function createTestUser(?array $userDetails = null, ?array $access = null): UserContract + { + $this->testUserClass = $this->testUserClass ?? config('modules.user_model'); + + if (! $this->testUserClass) { + throw new LogicException("User class was not provided!"); + } + + return $this->setupTestUserAccess( + $this->factoryCreateUser($userDetails), + $access + ); + } + + /** + * @param array|null $userDetails + * @return U + */ + private function factoryCreateUser(?array $userDetails = null): UserContract + { + if (! method_exists($this->testUserClass, 'factory')) { + throw new LogicException("class `$this->testUserClass` does not have method `factory()`"); + } + + return $this->testUserClass::factory()->create($this->prepareUserDetails($userDetails)); + } + + private function prepareUserDetails(?array $userDetails = null): array + { + $defaultUserDetails = [ + 'name' => 'Testing user', + 'email' => 'testing@test.com', + 'password' => 'testing-password', + ]; + $userDetails = $userDetails ? array_merge($defaultUserDetails, $userDetails) : $defaultUserDetails; + + $userDetails['password'] = Hash::make($userDetails['password']); + + return $userDetails; + } + + private function setupTestUserAccess(UserContract $user, ?array $access = null): UserContract + { + $access = $access ?: $this->testUserAccess; + + if ($access['permissions'] ?? false) { + if (! method_exists($user, 'givePermissionTo')) { + throw new LogicException("user instance does not have method `givePermissionTo()`, make sure the user class uses `spatie/laravel-permission`"); + } + $user->givePermissionTo($access['permissions']); + } + + if ($access['roles'] ?? false) { + if (! method_exists($user, 'assignRole')) { + throw new LogicException("user instance does not have method `assignRole()`, make sure the user class uses `spatie/laravel-permission`"); + } + $user->assignRole($access['roles']); + } + + return $user; + } +} diff --git a/src/Traits/ResponseHelpersTrait.php b/src/Support/Concerns/WithJsonResponseHelpers.php similarity index 93% rename from src/Traits/ResponseHelpersTrait.php rename to src/Support/Concerns/WithJsonResponseHelpers.php index 40e47f1..86ab197 100644 --- a/src/Traits/ResponseHelpersTrait.php +++ b/src/Support/Concerns/WithJsonResponseHelpers.php @@ -1,10 +1,10 @@ getScanPaths() + * @method static $this addScanPath(string|string[] $scanPaths) + * @method static array buildModulesManifest() + * @method static bool pruneModulesManifest() + * @method static array getModules() + * @method static array getPackagesToIgnore() + * @method static bool has(string $modulePackageName) + * @method static int count() + * @method static Module|null find(string $modulePackageName) + * @method static Module findOrFail(string $modulePackageName) + * @method static array filterByName(string $moduleName) + * @method static array filterByNameOrFail(string $moduleName) + * @method static bool delete(string $modulePackageName) + * @method static void syncWithComposer(\Closure|OutputInterface $output = null) + * @method static array toArray() + * + * @see \Laraneat\Modules\ModulesRepository + */ +class Modules extends Facade +{ + /** + * Get the registered name of the component. + */ + protected static function getFacadeAccessor(): string + { + return ModulesRepository::class; + } +} diff --git a/src/Support/Generator/GeneratorHelper.php b/src/Support/Generator/GeneratorHelper.php index 8665190..dc60a4c 100755 --- a/src/Support/Generator/GeneratorHelper.php +++ b/src/Support/Generator/GeneratorHelper.php @@ -2,8 +2,9 @@ namespace Laraneat\Modules\Support\Generator; -use Laraneat\Modules\Exceptions\ModuleNotFoundException; -use Laraneat\Modules\Facades\Modules; +use Illuminate\Support\Str; +use Laraneat\Modules\Enums\ModuleComponentType; +use Laraneat\Modules\Exceptions\InvalidConfigValue; use Laraneat\Modules\Module; class GeneratorHelper @@ -11,134 +12,203 @@ class GeneratorHelper /** * Get generator modules path * - * @return string + * @throws InvalidConfigValue */ - public static function path(): string + public static function getBasePath(): string { - return rtrim(config("modules.generator.path"), '/'); + $generatorPath = config('modules.path'); + + if (! $generatorPath) { + throw InvalidConfigValue::makeForNullValue('modules.path'); + } + + return self::normalizePath($generatorPath, true); } /** * Get generator modules namespace * - * @return string + * @throws InvalidConfigValue */ - public static function namespace(): string + public static function getBaseNamespace(): string { - return self::formatNamespace(config("modules.generator.namespace")); + $generatorNamespace = config('modules.namespace'); + + if (! $generatorNamespace) { + throw InvalidConfigValue::makeForNullValue('modules.namespace'); + } + + return self::normalizeNamespace($generatorNamespace); } /** * Get custom stubs path - * - * @return string */ - public static function customStubsPath(): string + public static function getCustomStubsPath(): ?string { - return rtrim(config("modules.generator.custom_stubs"), '/'); + $customStubsPath = config('modules.custom_stubs', base_path('/stubs/modules')); + + return $customStubsPath ? self::normalizePath($customStubsPath, true) : null; } /** - * Get user model + * Get user model class * - * @return string + * @return class-string|null */ - public static function userModel(): string + public static function getUserModelClass(): ?string { - return self::formatNamespace(config("modules.generator.user_model")); + $userModelClass = config('modules.user_model'); + + return $userModelClass && is_string($userModelClass) ? self::normalizeNamespace($userModelClass) : null; } /** - * Get "create permission" action + * Get "create permission" action class * - * @return string + * @return class-string|null */ - public static function createPermissionAction(): string + public static function getCreatePermissionActionClass(): ?string { - return self::formatNamespace(config("modules.generator.create_permission.action")); + $createPermissionActionClass = config('modules.create_permission.action'); + + return $createPermissionActionClass && is_string($createPermissionActionClass) + ? self::normalizeNamespace($createPermissionActionClass) + : null; } /** - * Get "create permission" DTO + * Get "create permission" DTO class * - * @return string + * @return class-string|null */ - public static function createPermissionDTO(): string + public static function getCreatePermissionDTOClass(): ?string { - return self::formatNamespace(config("modules.generator.create_permission.dto")); + $createPermissionDTOClass = config('modules.create_permission.dto'); + + return $createPermissionDTOClass && is_string($createPermissionDTOClass) + ? self::normalizeNamespace($createPermissionDTOClass) + : null; } /** * Get component config * - * @param string $componentType - * - * @return GeneratorPath + * @throws InvalidConfigValue */ - public static function component(string $componentType): GeneratorPath + public static function component(ModuleComponentType $componentType): GeneratorPath { - return new GeneratorPath(config("modules.generator.components.$componentType")); + $configPath = "modules.components.{$componentType->value}"; + $generatorComponent = config($configPath); + + if (! is_array($generatorComponent) || empty($generatorComponent['path'])) { + throw InvalidConfigValue::make($configPath); + }; + + return new GeneratorPath($generatorComponent); } /** * Get module path * - * @param Module|string $module - * @param string|null $extraPath - * @return string + * @throws InvalidConfigValue */ - public static function modulePath(Module|string $module, ?string $extraPath = null): string + public static function makeModulePath(Module|string $moduleOrName, ?string $subPath = null): ?string { - try { - $modulePath = Modules::getModulePath($module); - } catch (ModuleNotFoundException $e) { - $modulesPath = self::path(); - $modulePart = self::formatPath($module); - $modulePath = $modulePart ? $modulesPath . '/' . $modulePart : $modulePart; + if ($moduleOrName instanceof Module) { + return $moduleOrName->subPath($subPath); + } + + $modulePart = self::normalizePath($moduleOrName); + + if (! $modulePart) { + return null; } - return $extraPath ? $modulePath . '/' . self::formatPath($extraPath) : $modulePath; + $modulePath = self::getBasePath() . '/' . $modulePart; + + return $subPath ? $modulePath . '/' . self::normalizePath($subPath) : $modulePath; } /** * Get module namespace * - * @param Module|string $module - * @param string|null $extraNamespace - * @return string + * @throws InvalidConfigValue */ - public static function moduleNamespace(Module|string $module, ?string $extraNamespace = null): string + public static function makeModuleNamespace(Module|string $moduleOrName, ?string $subNamespace = null): ?string { - try { - $moduleNamespace = Modules::getModuleNamespace($module); - } catch (ModuleNotFoundException $e) { - $modulesNamespace = self::namespace(); - $modulePart = self::formatNamespace($module); - $moduleNamespace = $modulePart ? $modulesNamespace . '\\' . $modulePart : $modulePart; + if ($moduleOrName instanceof Module) { + return $moduleOrName->subNamespace($subNamespace); } - return $extraNamespace ? $moduleNamespace . '\\' . self::formatNamespace($extraNamespace) : $moduleNamespace; + $modulePart = self::normalizeNamespace(Str::studly($moduleOrName)); + + if (! $modulePart) { + return null; + } + + $moduleNamespace = self::getBaseNamespace() . '\\' . $modulePart; + + return $subNamespace ? $moduleNamespace . '\\' . self::normalizeNamespace($subNamespace) : $moduleNamespace; } /** - * Format path (normalize slashes) - * - * @param string $path - * @return string + * Make relative path or returns null */ - private static function formatPath(string $path): string + public static function makeRelativePath(string $from, string $to): ?string { - return trim(str_replace('\\', '/', $path), '/'); + $from = static::normalizePath($from, true); + $to = static::normalizePath($to, true); + + if ($from === $to) { + return ''; + } + + $fromSegments = explode('/', $from); + $toSegments = explode('/', $to); + + $diffStartIndex = null; + $fromSegmentsCount = count($fromSegments); + $toSegmentsCount = count($toSegments); + $segmentsCount = max($fromSegmentsCount, $toSegmentsCount); + for ($i = 0; $i < $segmentsCount; $i++) { + if (! isset($fromSegments[$i], $toSegments[$i]) || $fromSegments[$i] !== $toSegments[$i]) { + if ($i === 0 || $i === 1 && ! $fromSegments[0]) { + return null; + } + $diffStartIndex = $i; + + break; + } + } + + $relativePath = Str::repeat('../', $fromSegmentsCount - $diffStartIndex) + . join('/', array_slice($toSegments, $diffStartIndex)); + + return rtrim($relativePath, '/'); } /** - * Format namespace (normalize slashes) - * - * @param string $namespace - * @return string + * Normalize path to use only forward slash and trim slashes */ - private static function formatNamespace(string $namespace): string + public static function normalizePath(string $path, $useRtrim = false): string { - return trim(str_replace('/', '\\', $namespace), '\\'); + $path = str_replace('\\', '/', $path); + + return $useRtrim && Str::startsWith($path, '/') + ? '/' . trim($path, '/') + : trim($path, '/'); + } + + /** + * Normalize namespace to use only backslash and trim slashes + */ + public static function normalizeNamespace(string $namespace, $useRtrim = false): string + { + $namespace = str_replace('/', '\\', $namespace); + + return $useRtrim && Str::startsWith($namespace, '\\') + ? '\\' . trim($namespace, '\\') + : trim($namespace, '\\'); } } diff --git a/src/Support/Generator/GeneratorPath.php b/src/Support/Generator/GeneratorPath.php index 6aefc4c..f522389 100755 --- a/src/Support/Generator/GeneratorPath.php +++ b/src/Support/Generator/GeneratorPath.php @@ -6,29 +6,13 @@ class GeneratorPath { - private string $path; - private string $namespace; - private bool $generate; - private bool $gitkeep; + protected string $path; + protected string $namespace; - /** - * @param array|bool|string $config - */ - public function __construct($config) + public function __construct(array $config) { - if (!is_array($config)) { - $config = [ - 'path' => (string) $config, - 'generate' => (bool) $config - ]; - } - - $this->path = $this->formatPath((string) $config['path']); - $this->namespace = $this->formatNamespace( - $this->convertPathToNamespace((string) ($config['namespace'] ?? $this->path)) - ); - $this->generate = (bool) ($config['generate'] ?? true); - $this->gitkeep = (bool) ($config['gitkeep'] ?? false); + $this->path = GeneratorHelper::normalizePath((string) $config['path']); + $this->namespace = GeneratorHelper::normalizeNamespace($config['namespace'] ?? $this->path); } public function getPath(): string @@ -38,7 +22,7 @@ public function getPath(): string public function getFullPath(Module|string $module): string { - return GeneratorHelper::modulePath($module, $this->path); + return GeneratorHelper::makeModulePath($module, $this->path); } public function getNamespace(): string @@ -48,36 +32,6 @@ public function getNamespace(): string public function getFullNamespace(Module|string $module): string { - return GeneratorHelper::moduleNamespace($module, $this->namespace); - } - - public function generate(): bool - { - return $this->generate; - } - - public function withGitKeep(): bool - { - return $this->gitkeep; - } - - protected function formatPath(string $path): string - { - return trim($path, '/'); - } - - protected function formatNamespace(string $namespace): string - { - return trim($namespace, '\\'); - } - - protected function convertPathToNamespace(string $path): string - { - return str_replace('/', '\\', $path); - } - - protected function convertNamespaceToPath(string $path): string - { - return str_replace('\\', '/', $path); + return GeneratorHelper::makeModuleNamespace($module, $this->namespace); } } diff --git a/src/Support/Stub.php b/src/Support/Generator/Stub.php similarity index 62% rename from src/Support/Stub.php rename to src/Support/Generator/Stub.php index e5c414c..523ca75 100755 --- a/src/Support/Stub.php +++ b/src/Support/Generator/Stub.php @@ -1,28 +1,24 @@ */ protected array $replaces = []; /** * @param string $path - * @param array $replaces + * @param array $replaces */ public function __construct(string $path, array $replaces = []) { @@ -34,11 +30,11 @@ public function __construct(string $path, array $replaces = []) * Create new self instance. * * @param string $path - * @param array $replaces + * @param array $replaces * - * @return static + * @return Stub */ - public static function create(string $path, array $replaces = []) + public static function create(string $path, array $replaces = []): Stub { return new static($path, $replaces); } @@ -50,7 +46,7 @@ public static function create(string $path, array $replaces = []) * * @return $this */ - public function setPath(string $path) + public function setPath(string $path): static { $this->path = $path; @@ -59,72 +55,27 @@ public function setPath(string $path) /** * Get stub path. - * - * @return string */ public function getPath(): string { - $customStubsFolderPath = GeneratorHelper::customStubsPath(); + $customStubsFolderPath = GeneratorHelper::getCustomStubsPath(); $customStubFilePath = $customStubsFolderPath . '/' . ltrim($this->path, '/'); if (file_exists($customStubFilePath)) { return $customStubFilePath; } - return __DIR__ . '/../Commands/Generators/stubs/' . ltrim($this->path, '/'); - } - - /** - * Get stub contents. - * - * @return string - */ - public function getContents(): string - { - $contents = file_get_contents($this->getPath()); - - foreach ($this->replaces as $search => $replace) { - $contents = str_replace( - ['{{' . $search . '}}', '{{ ' . $search . ' }}'], - $replace, - $contents - ); - } - - return $contents; - } - - /** - * Get stub contents. - * - * @return string - */ - public function render(): string - { - return $this->getContents(); - } - - /** - * Save stub to specific path. - * - * @param string $path - * @param string $filename - * - * @return int|false - */ - public function saveTo(string $path, string $filename) - { - return file_put_contents($path . '/' . $filename, $this->getContents()); + return __DIR__ . '/../../Commands/Generators/stubs/' . ltrim($this->path, '/'); } /** * Set replacements array. * - * @param array $replaces + * @param array $replaces * * @return $this */ - public function replace(array $replaces = []) + public function setReplaces(array $replaces = []): static { $this->replaces = $replaces; @@ -134,17 +85,33 @@ public function replace(array $replaces = []) /** * Get replacements. * - * @return array + * @return array */ public function getReplaces(): array { return $this->replaces; } + /** + * Render stub contents. + */ + public function render(): string + { + $contents = file_get_contents($this->getPath()); + + foreach ($this->replaces as $search => $replace) { + $contents = str_replace( + ['{{' . $search . '}}', '{{ ' . $search . ' }}'], + $replace, + $contents + ); + } + + return $contents; + } + /** * Handle magic method __toString. - * - * @return string */ public function __toString() { diff --git a/src/Support/Migrations/NameParser.php b/src/Support/Migrations/NameParser.php index 6886d32..84c656f 100755 --- a/src/Support/Migrations/NameParser.php +++ b/src/Support/Migrations/NameParser.php @@ -6,22 +6,16 @@ class NameParser { /** * The migration name. - * - * @var string */ protected string $name; /** * The array data. - * - * @var array */ protected array $data = []; /** * The available schema actions. - * - * @var array */ protected array $actions = [ 'create' => [ @@ -42,8 +36,6 @@ class NameParser /** * The constructor. - * - * @param string $name */ public function __construct(string $name) { @@ -53,8 +45,6 @@ public function __construct(string $name) /** * Get original migration name. - * - * @return string */ public function getOriginalName(): string { @@ -63,8 +53,6 @@ public function getOriginalName(): string /** * Get schema type or action. - * - * @return string */ public function getAction(): string { @@ -73,8 +61,6 @@ public function getAction(): string /** * Get the table will be used. - * - * @return string|null */ public function getTableName(): ?string { @@ -85,8 +71,6 @@ public function getTableName(): ?string /** * Get matches data from regex. - * - * @return array */ public function getMatches(): array { @@ -97,35 +81,18 @@ public function getMatches(): array /** * Get name pattern. - * - * @return string */ public function getPattern(): string { - switch ($action = $this->getAction()) { - case 'add': - case 'append': - case 'update': - case 'insert': - return "/{$action}_(.*)_to_(.*)_table/"; - break; - - case 'delete': - case 'remove': - case 'alter': - return "/{$action}_(.*)_from_(.*)_table/"; - break; - - default: - return "/{$action}_(.*)_table/"; - break; - } + return match ($action = $this->getAction()) { + 'add', 'append', 'update', 'insert' => "/{$action}_(.*)_to_(.*)_table/", + 'delete', 'remove', 'alter' => "/{$action}_(.*)_from_(.*)_table/", + default => "/{$action}_(.*)_table/", + }; } /** * Fetch the migration name to an array data. - * - * @return array */ protected function fetchData(): array { @@ -134,8 +101,6 @@ protected function fetchData(): array /** * Get the array data. - * - * @return array */ public function getData(): array { @@ -144,20 +109,14 @@ public function getData(): array /** * Determine whether the given type is same with the current schema action or type. - * - * @param $type - * - * @return bool */ - public function is($type): bool + public function is(string $type): bool { return $type === $this->getAction(); } /** * Determine whether the current schema action is a adding action. - * - * @return bool */ public function isAdd(): bool { @@ -166,8 +125,6 @@ public function isAdd(): bool /** * Determine whether the current schema action is a deleting action. - * - * @return bool */ public function isDelete(): bool { @@ -176,8 +133,6 @@ public function isDelete(): bool /** * Determine whether the current schema action is a creating action. - * - * @return bool */ public function isCreate(): bool { diff --git a/src/Support/Migrations/SchemaParser.php b/src/Support/Migrations/SchemaParser.php index c6d9939..514ab59 100755 --- a/src/Support/Migrations/SchemaParser.php +++ b/src/Support/Migrations/SchemaParser.php @@ -10,8 +10,6 @@ class SchemaParser implements Arrayable { /** * The array of custom attributes. - * - * @var array */ protected array $customAttributes = [ 'remember_token' => 'rememberToken()', @@ -20,15 +18,11 @@ class SchemaParser implements Arrayable /** * The migration schema. - * - * @var string|null */ protected ?string $schema; /** * The relationship keys. - * - * @var array */ protected array $relationshipKeys = [ 'belongsTo', @@ -36,8 +30,6 @@ class SchemaParser implements Arrayable /** * Create new instance. - * - * @param string|null $schema */ public function __construct(?string $schema = null) { @@ -46,10 +38,6 @@ public function __construct(?string $schema = null) /** * Parse a string to array of formatted schema. - * - * @param string|null $schema - * - * @return array */ public function parse(?string $schema): array { @@ -70,8 +58,6 @@ public function parse(?string $schema): array /** * Get array of schema. - * - * @return array */ public function getSchemas(): array { @@ -84,8 +70,6 @@ public function getSchemas(): array /** * Convert string migration to array. - * - * @return array */ public function toArray(): array { @@ -94,8 +78,6 @@ public function toArray(): array /** * Render the migration to formatted script. - * - * @return string */ public function render(): string { @@ -110,8 +92,6 @@ public function render(): string /** * Render up migration fields. - * - * @return string */ public function up(): string { @@ -120,15 +100,12 @@ public function up(): string /** * Render down migration fields. - * - * @return string */ public function down(): string { $results = ''; foreach ($this->toArray() as $column => $attributes) { - $attributes = [head($attributes)]; $results .= $this->createField($column, $attributes, 'remove'); } @@ -137,21 +114,22 @@ public function down(): string /** * Create field. - * - * @param string $column - * @param array $attributes - * @param string $type - * - * @return string */ public function createField(string $column, array $attributes, string $type = 'add'): string { $results = "\t\t\t" . '$table'; - foreach ($attributes as $key => $field) { - if (in_array($column, $this->relationshipKeys, true)) { - $results .= $this->addRelationColumn($key, $field, $column); - } else { + if (in_array($column, $this->relationshipKeys, true)) { + if ($type === 'add') { + $results .= $this->addRelationColumn($attributes, $column); + } elseif ($type === 'remove') { + $results .= $this->removeRelationColumn($attributes, $column); + } + } else { + if ($type === 'remove') { + $attributes = [head($attributes)]; + } + foreach ($attributes as $key => $field) { $results .= $this->{"{$type}Column"}($key, $field, $column); } } @@ -161,41 +139,45 @@ public function createField(string $column, array $attributes, string $type = 'a /** * Add relation column. - * - * @param int $key - * @param string $field - * @param string $column - * - * @return string */ - protected function addRelationColumn(int $key, string $field, string $column): string + protected function addRelationColumn(array $attributes, string $column): string { - if ($key === 0) { - $relatedColumn = Str::snake(class_basename($field)) . '_id'; + $result = ''; - return "->integer('{$relatedColumn}')->unsigned();" . PHP_EOL . "\t\t\t" . "\$table->foreign('{$relatedColumn}')"; - } - if ($key === 1) { - return "->references('{$field}')"; - } - if ($key === 2) { - return "->on('{$field}')"; + foreach ($attributes as $key => $field) { + if ($key === 0) { + $relatedColumn = Str::snake(class_basename($field)) . '_id'; + $result .= "->integer('$relatedColumn')->unsigned();" . PHP_EOL . "\t\t\t" . "\$table->foreign('$relatedColumn')"; + } elseif ($key === 1) { + $result .= "->references('$field')"; + } elseif ($key === 2) { + $result .= "->on('$field')"; + } elseif (Str::contains($field, '(')) { + $result .= '->' . $field; + } else { + $result .= '->' . $field . '()'; + } } - if (Str::contains($field, '(')) { - return '->' . $field; + + return $result; + } + + /** + * Remove relation column. + */ + protected function removeRelationColumn(array $attributes, string $column): string + { + if (! ($attributes[0] ?? null)) { + return ""; } - return '->' . $field . '()'; + $relatedColumn = Str::snake(class_basename($attributes[0])) . '_id'; + + return "->dropColumn('$relatedColumn');" . PHP_EOL . "\t\t\t" . "\$table->dropForeign(['$relatedColumn'])"; } /** * Format field to script. - * - * @param int $key - * @param string $field - * @param string $column - * - * @return string */ protected function addColumn(int $key, string $field, string $column): string { @@ -216,12 +198,6 @@ protected function addColumn(int $key, string $field, string $column): string /** * Format field to script. - * - * @param int $key - * @param string $field - * @param string $column - * - * @return string */ protected function removeColumn(int $key, string $field, string $column): string { @@ -234,10 +210,6 @@ protected function removeColumn(int $key, string $field, string $column): string /** * Get column name from schema. - * - * @param string $schema - * - * @return string */ public function getColumn(string $schema): string { @@ -246,11 +218,6 @@ public function getColumn(string $schema): string /** * Get column attributes. - * - * @param string $column - * @param string $schema - * - * @return array */ public function getAttributes(string $column, string $schema): array { @@ -260,11 +227,7 @@ public function getAttributes(string $column, string $schema): array } /** - * Determine whether the given column is exist in customAttributes array. - * - * @param string $column - * - * @return bool + * Determine whether the given column exists in the customAttributes array. */ public function hasCustomAttribute(string $column): bool { @@ -273,10 +236,6 @@ public function hasCustomAttribute(string $column): bool /** * Get custom attributes value. - * - * @param string $column - * - * @return array */ public function getCustomAttribute(string $column): array { diff --git a/src/Support/ModuleConfigWriter.php b/src/Support/ModuleConfigWriter.php new file mode 100644 index 0000000..45c0825 --- /dev/null +++ b/src/Support/ModuleConfigWriter.php @@ -0,0 +1,96 @@ + $providers + * + * @throws FileNotFoundException + */ + public function updateProviders(Module $module, array $providers): void + { + $this->updateConfig($module, 'extra.laravel.providers', $providers); + } + + /** + * Update module aliases in composer.json + * + * @param Module $module + * @param array $aliases + * + * @throws FileNotFoundException + */ + public function updateAliases(Module $module, array $aliases): void + { + $this->updateConfig($module, 'extra.laravel.aliases', $aliases); + } + + /** + * Add a provider to the module + * + * @param Module $module + * @param class-string $providerClass + * + * @throws FileNotFoundException + */ + public function addProvider(Module $module, string $providerClass): void + { + $providers = $module->getProviders(); + + if (! in_array($providerClass, $providers, true)) { + $providers[] = $providerClass; + $this->updateProviders($module, $providers); + } + } + + /** + * Add an alias to the module + * + * @param Module $module + * @param string $alias + * @param class-string $class + * + * @throws FileNotFoundException + */ + public function addAlias(Module $module, string $alias, string $class): void + { + $aliases = $module->getAliases(); + + if (! isset($aliases[$alias]) || $aliases[$alias] !== $class) { + $aliases[$alias] = $class; + $this->updateAliases($module, $aliases); + } + } + + /** + * Update a config value in module's composer.json + * + * @param Module $module + * @param string $key + * @param mixed $value + * + * @throws FileNotFoundException|\Exception + */ + protected function updateConfig(Module $module, string $key, mixed $value): void + { + ComposerJsonFile::create($module->getPath() . '/composer.json') + ->set($key, $value) + ->save(); + + $this->modulesRepository->pruneModulesManifest(); + } +} diff --git a/src/Support/ModuleServiceProvider.php b/src/Support/ModuleServiceProvider.php new file mode 100644 index 0000000..70f3484 --- /dev/null +++ b/src/Support/ModuleServiceProvider.php @@ -0,0 +1,111 @@ + $pathsByNamespace + * + * @throws ReflectionException + */ + protected function loadCommandsFrom( + array $pathsByNamespace, + ): void { + $pathsByNamespace = array_unique(Arr::wrap($pathsByNamespace)); + $pathsByNamespace = array_filter($pathsByNamespace, static function ($path) { + return is_dir($path); + }); + + if (empty($pathsByNamespace)) { + return; + } + + foreach ($pathsByNamespace as $namespace => $path) { + foreach (Finder::create()->in($path)->files() as $file) { + $command = $this->commandClassFromFile($file, $path, $namespace); + + if ( + is_subclass_of($command, Command::class) && + ! (new ReflectionClass($command))->isAbstract() + ) { + Artisan::starting(function ($artisan) use ($command) { + $artisan->resolve($command); + }); + } + } + } + } + + /** + * Extract the command class name from the given file path. + */ + protected function commandClassFromFile( + SplFileInfo $file, + string $basePath, + string $baseNamespace + ): string { + return rtrim($baseNamespace, '\\') . '\\' . str_replace( + ['/', '.php'], + ['\\', ''], + Str::after($file->getRealPath(), realpath($basePath) . DIRECTORY_SEPARATOR) + ); + } + + /** + * Load files from directory + */ + protected function loadFiles(string $directory): void + { + if (File::isDirectory($directory)) { + $files = File::files($directory); + + foreach ($files as $file) { + require_once $file; + } + } + } + + /** + * Load all files from directory + */ + protected function loadAllFiles(string $directory): void + { + if (File::isDirectory($directory)) { + $files = File::allFiles($directory); + + foreach ($files as $file) { + require_once $file; + } + } + } +} diff --git a/src/Traits/ConsoleHelpersTrait.php b/src/Traits/ConsoleHelpersTrait.php deleted file mode 100644 index b326d33..0000000 --- a/src/Traits/ConsoleHelpersTrait.php +++ /dev/null @@ -1,136 +0,0 @@ -argument($key)); - } - - /** - * Get trimmed option - * - * @param string $key - * - * @return string - */ - protected function getTrimmedOption(string $key): string - { - return trim($this->option($key)); - } - - /** - * Checks if the option is set (via CLI), otherwise asks the user for a value - * - * @param string $optionName - * @param string $question - * @param mixed $default - * @param bool $required - * - * @return string - */ - protected function getOptionOrAsk(string $optionName, string $question, $default = null, bool $required = false): string - { - $value = $this->getTrimmedOption($optionName); - - if ($value === '' || $value === null) { - $value = trim($this->ask($question, $default)); - } - - if ($required && empty($value)) { - throw new InvalidOptionException( - sprintf('The "%s" option is required', $optionName) - ); - } - - return $value; - } - - /** - * Checks if the option is set (via CLI), otherwise proposes choices to the user - * - * @param string $optionName - * @param string $question - * @param array $choices - * @param mixed $default - * - * @return string - */ - protected function getOptionOrChoice(string $optionName, string $question, array $choices, $default = null): string - { - $value = $this->getTrimmedOption($optionName); - - if ($value === '' || $value === null) { - $value = trim($this->choice($question, $choices, $default)); - } elseif (!in_array(mb_strtolower($value), $choices, true)) { - throw new InvalidOptionException( - sprintf( - 'Wrong "%s" option value provided. Value should be one of "%s".', - $optionName, - implode('" or "', $choices) - ) - ); - } - - return $value; - } - - /** - * Get an option that is one of the valid values - * - * @param string $optionName - * @param array $validValues - * - * @return string - */ - protected function getOptionOneOf(string $optionName, array $validValues): string - { - $value = $this->getTrimmedOption($optionName); - - if (!in_array(mb_strtolower($value), $validValues, true)) { - throw new InvalidOptionException( - sprintf( - 'Wrong "%s" option value provided. Value should be one of "%s".', - $optionName, - implode('" or "', $validValues) - ) - ); - } - - return $value; - } - - /** - * Checks if the option is set (via CLI), otherwise, asks the user for confirmation - * - * @param string $optionName - * @param string $question - * @param bool $default - * - * @return bool - */ - protected function getOptionOrConfirm(string $optionName, string $question, $default = false): bool - { - $value = $this->option($optionName); - - if ($value === null) { - $value = $this->confirm($question, $default); - } - - return (bool) $value; - } -} diff --git a/src/Traits/ModuleCommandTrait.php b/src/Traits/ModuleCommandTrait.php deleted file mode 100755 index ee2a280..0000000 --- a/src/Traits/ModuleCommandTrait.php +++ /dev/null @@ -1,24 +0,0 @@ -argument('module'); - - return $moduleName ? Modules::findOrFail($moduleName) : Modules::getUsedNow(); - } -} diff --git a/src/Traits/ModuleProviderHelpersTrait.php b/src/Traits/ModuleProviderHelpersTrait.php deleted file mode 100644 index b49c446..0000000 --- a/src/Traits/ModuleProviderHelpersTrait.php +++ /dev/null @@ -1,127 +0,0 @@ -app->getNamespace(); - - foreach (Finder::create()->in($paths)->files() as $file) { - $command = $this->commandClassFromFile($file, $namespace); - - if ( - is_subclass_of($command, Command::class) && - !(new ReflectionClass($command))->isAbstract() - ) { - Artisan::starting(function ($artisan) use ($command) { - $artisan->resolve($command); - }); - } - } - } - - /** - * Extract the command class name from the given file path. - * - * @param \SplFileInfo $file - * @param string $namespace - * @return string - */ - protected function commandClassFromFile(SplFileInfo $file, string $namespace): string - { - return $namespace . str_replace( - ['/', '.php'], - ['\\', ''], - Str::after($file->getRealPath(), realpath(app_path()) . DIRECTORY_SEPARATOR) - ); - } - - /** - * Load files from directory - * - * @param string $directory - * - * @return void - */ - protected function loadFiles(string $directory): void - { - if (File::isDirectory($directory)) { - $files = File::files($directory); - - foreach ($files as $file) { - require_once $file; - } - } - } - - /** - * Load all files from directory - * - * @param string $directory - * - * @return void - */ - protected function loadAllFiles(string $directory): void - { - if (File::isDirectory($directory)) { - $files = File::allFiles($directory); - - foreach ($files as $file) { - require_once $file; - } - } - } -} diff --git a/src/Traits/RouteProviderHelpersTrait.php b/src/Traits/RouteProviderHelpersTrait.php deleted file mode 100644 index 5627bdb..0000000 --- a/src/Traits/RouteProviderHelpersTrait.php +++ /dev/null @@ -1,53 +0,0 @@ -loadRoutesFromDirectory( - $nestedDirectory, - $directoryRoutePrefix, - $generateRoutePrefixesByNestedDirectories - ); - } - - /** @var SplFileInfo[] $files */ - $files = Arr::sort(File::files($directory), function (SplFileInfo $file) { - return $file->getFilename(); - }); - - Route::prefix($routePrefix)->group(function () use ($files) { - foreach ($files as $file) { - require $file->getPathname(); - } - }); - } - } -} diff --git a/src/Traits/SanitizerTrait.php b/src/Traits/SanitizerTrait.php deleted file mode 100644 index 4b0cfa6..0000000 --- a/src/Traits/SanitizerTrait.php +++ /dev/null @@ -1,88 +0,0 @@ -getData(); - - $inputAsArray = []; - $fieldsWithDefaultValue = []; - - // create a multidimensional array based on $fields - // which was submitted as DOT notation (e.g., data.name) - foreach ($fields as $key => $value) { - if (is_string($key)) { - // save fields with default values - $fieldsWithDefaultValue[$key] = $value; - Arr::set($inputAsArray, $key, $value); - } else { - Arr::set($inputAsArray, $value, true); - } - } - - // check, if the keys exist in both arrays - $data = $this->recursiveArrayIntersectKey($data, $inputAsArray); - - // set default values if key doesn't exist - foreach ($fieldsWithDefaultValue as $key => $value) { - $data = Arr::add($data, $key, $value); - } - - return $data; - } - - /** - * @throws LogicException - */ - private function getData(): array - { - // get all request data - if ($this instanceof Request) { - $data = $this->all(); - } else { - throw new LogicException('Unsupported class type for sanitization.'); - } - - return $data; - } - - /** - * Recursively intersects 2 arrays based on their keys. - * - * @param array $a first array (that keeps the values) - * @param array $b second array to be compared with - * - * @return array an array containing all keys that are present in $a and $b. Only values from $a are returned - */ - private function recursiveArrayIntersectKey(array $a, array $b): array - { - $a = array_intersect_key($a, $b); - - foreach ($a as $key => &$value) { - if (is_array($value) && is_array($b[$key])) { - $value = $this->recursiveArrayIntersectKey($value, $b[$key]); - } - } - - return $a; - } -} diff --git a/src/Traits/TestsTraits/TestsAuthHelperTrait.php b/src/Traits/TestsTraits/TestsAuthHelperTrait.php deleted file mode 100644 index cb2bf69..0000000 --- a/src/Traits/TestsTraits/TestsAuthHelperTrait.php +++ /dev/null @@ -1,184 +0,0 @@ -, roles:string|array} - */ - protected array $access = [ - 'permissions' => '', - 'roles' => '', - ]; - - /** - * state name on User factory - */ - private ?string $userAdminState = null; - - /** - * create testing user as Admin. - */ - private ?bool $createUserAsAdmin = null; - - /** - * Same as `getTestingUser()` but always overrides the User Access - * (roles and permissions) with null. So the user can be used to test - * if unauthorized user tried to access your protected endpoint. - * - * @param null $userDetails - * @return UserContract|null - */ - public function getTestingUserWithoutAccess($userDetails = null): ?UserContract - { - return $this->getTestingUser($userDetails, $this->getNullAccess()); - } - - /** - * Try to get the last logged-in User, if not found then create new one. - * Note: if $userDetails are provided it will always create new user, even - * if another one was previously created during the execution of your test. - * - * By default, Users will be given the Roles and Permissions found in the class - * `$access` property. But the $access parameter can be used to override the - * defined roles and permissions in the `$access` property of your class. - * - * @param array|null $userDetails what to be attached on the User object - * @param array|null $access roles and permissions you'd like to provide this user with - * @param bool $createUserAsAdmin should create testing user as admin - * @return UserContract|null - */ - public function getTestingUser(?array $userDetails = null, ?array $access = null, bool $createUserAsAdmin = false): ?UserContract - { - $this->createUserAsAdmin = $createUserAsAdmin; - $this->userClass = $this->userClass ?? config('laraneat.tests.user-class'); - - if (!$this->userClass) { - throw new LogicException("User class was not provided"); - } - - $this->userAdminState = config('laraneat.tests.user-admin-state'); - return is_null($userDetails) ? $this->findOrCreateTestingUser($userDetails, $access) - : $this->createTestingUser($userDetails, $access); - } - - private function findOrCreateTestingUser($userDetails, $access): UserContract - { - return $this->testingUser ?: $this->createTestingUser($userDetails, $access); - } - - private function createTestingUser(?array $userDetails = null, ?array $access = null): ?UserContract - { - // create new user - $user = $this->factoryCreateUser($userDetails); - - // assign user roles and permissions based on the access property - $user = $this->setupTestingUserAccess($user, $access); - - // authentication the user - $this->actingAs($user, config('laraneat.tests.guard', 'api')); - - // set the created user - return $this->testingUser = $user; - } - - private function factoryCreateUser(?array $userDetails = null): UserContract - { - $user = str_replace('::class', '', $this->userClass); - if ($this->createUserAsAdmin) { - $state = $this->userAdminState; - return $user::factory()->$state()->create($this->prepareUserDetails($userDetails)); - } - - return $user::factory()->create($this->prepareUserDetails($userDetails)); - } - - private function prepareUserDetails(?array $userDetails = null): array - { - $defaultUserDetails = [ - 'name' => $this->faker->name, - 'email' => $this->faker->email, - 'password' => 'testing-password', - ]; - - // if no user detail provided, use the default details, to find the password or generate one before encoding it - return $this->prepareUserPassword($userDetails ?: $defaultUserDetails); - } - - private function prepareUserPassword(?array $userDetails): ?array - { - // get password from the user details or generate one - $password = $userDetails['password'] ?? $this->faker->password; - - // hash the password and set it back at the user details - $userDetails['password'] = Hash::make($password); - - return $userDetails; - } - - private function setupTestingUserAccess($user, ?array $access = null) - { - $access = $access ?: $this->getAccess(); - - $user = $this->setupTestingUserPermissions($user, $access); - return $this->setupTestingUserRoles($user, $access); - } - - private function getAccess(): ?array - { - return $this->access ?? null; - } - - private function setupTestingUserPermissions($user, ?array $access) - { - if (isset($access['permissions']) && !empty($access['permissions'])) { - $user->givePermissionTo($access['permissions']); - $user = $user->fresh(); - } - - return $user; - } - - private function setupTestingUserRoles($user, ?array $access) - { - if (isset($access['roles']) && !empty($access['roles']) && !$user->hasRole($access['roles'])) { - $user->assignRole($access['roles']); - $user = $user->fresh(); - } - - return $user; - } - - /** - * @return null[] - */ - private function getNullAccess(): array - { - return [ - 'permissions' => null, - 'roles' => null - ]; - } -} diff --git a/src/Traits/TestsTraits/TestsModelHelperTrait.php b/src/Traits/TestsTraits/TestsModelHelperTrait.php deleted file mode 100644 index 46e78cc..0000000 --- a/src/Traits/TestsTraits/TestsModelHelperTrait.php +++ /dev/null @@ -1,46 +0,0 @@ -assertTrue( - $this->makeQueryWhereColumns($subject, $columns)->exists(), - sprintf('Model where columns (%s) not exists', http_build_query($columns)) - ); - } - - public function getModelsWhereColumns($subject, array $columns): Collection - { - return $this->makeQueryWhereColumns($subject, $columns)->get(); - } - - public function makeQueryWhereColumns($subject, array $columns): Builder|Relation - { - if (is_subclass_of($subject, Model::class)) { - $subject = $subject::query(); - } - - throw_unless( - $subject instanceof Builder || $subject instanceof Relation, - InvalidSubject::make($subject) - ); - - foreach ($columns as $column => $value) { - $subject->where($column, $value); - } - - return $subject; - } -} diff --git a/src/Traits/TestsTraits/TestsUrlHelperTrait.php b/src/Traits/TestsTraits/TestsUrlHelperTrait.php deleted file mode 100644 index 412df5b..0000000 --- a/src/Traits/TestsTraits/TestsUrlHelperTrait.php +++ /dev/null @@ -1,68 +0,0 @@ - $value) { - $url = Str::replace($key, $value, $url); - } - - return $url; - } - - public function trimSlashes($path): string - { - return trim($path, '/'); - } - - public function buildUrl(?string $path = null, array $queryParameters = [], array $replaces = []): string - { - $path = $path ?? $this->url ?? null; - - if (!$path) { - throw InvalidPath::make($path); - } - - if ($this->isAbsoluteUrl($path)) { - return $this->addQueryParametersToUrl($path, $queryParameters); - } - - $appUrl = $this->trimSlashes(config('app.url')); - $url = $appUrl.'/'.$this->trimSlashes($path); - - if ($replaces) { - $url = $this->replaceByKeyValues($url, $replaces); - } - - return $this->addQueryParametersToUrl($url, $queryParameters); - } - - public function isAbsoluteUrl(string $url): bool - { - return Str::startsWith($url, ['http://', 'https://']); - } -} diff --git a/src/helpers.php b/src/helpers.php deleted file mode 100755 index 0bf5a8a..0000000 --- a/src/helpers.php +++ /dev/null @@ -1,20 +0,0 @@ -getExtraPath($path); - } -} diff --git a/tests/Activators/FileActivatorTest.php b/tests/Activators/FileActivatorTest.php deleted file mode 100644 index aca311d..0000000 --- a/tests/Activators/FileActivatorTest.php +++ /dev/null @@ -1,86 +0,0 @@ -module = new TestModule($this->app, 'Article', __DIR__ . '/../fixtures/stubs/valid/Article', 'App\\Module\\Article'); - $this->finder = $this->app['files']; - $this->activator = new FileActivator($this->app); - } - - protected function tearDown(): void - { - $this->activator->reset(); - parent::tearDown(); - } - - /** @test */ - public function it_creates_valid_json_file_after_enabling() - { - $this->activator->enable($this->module); - $this->assertMatchesSnapshot($this->finder->get($this->activator->getStatusesFilePath())); - - $this->activator->setActive($this->module, true); - $this->assertMatchesSnapshot($this->finder->get($this->activator->getStatusesFilePath())); - } - - /** @test */ - public function it_creates_valid_json_file_after_disabling() - { - $this->activator->disable($this->module); - $this->assertMatchesSnapshot($this->finder->get($this->activator->getStatusesFilePath())); - - $this->activator->setActive($this->module, false); - $this->assertMatchesSnapshot($this->finder->get($this->activator->getStatusesFilePath())); - } - - /** @test */ - public function it_can_check_module_enabled_status() - { - $this->activator->enable($this->module); - $this->assertTrue($this->activator->hasStatus($this->module, true)); - - $this->activator->setActive($this->module, true); - $this->assertTrue($this->activator->hasStatus($this->module, true)); - } - - /** @test */ - public function it_can_check_module_disabled_status() - { - $this->activator->disable($this->module); - $this->assertTrue($this->activator->hasStatus($this->module, false)); - - $this->activator->setActive($this->module, false); - $this->assertTrue($this->activator->hasStatus($this->module, false)); - } - - /** @test */ - public function it_can_check_status_of_module_that_hasnt_been_enabled_or_disabled() - { - $this->assertTrue($this->activator->hasStatus($this->module, false)); - } -} - -class TestModule extends \Laraneat\Modules\Module -{ - public function registerProviders(): void - { - parent::registerProviders(); - } -} diff --git a/tests/Activators/__snapshots__/FileActivatorTest__it_creates_valid_json_file_after_disabling__1.txt b/tests/Activators/__snapshots__/FileActivatorTest__it_creates_valid_json_file_after_disabling__1.txt deleted file mode 100644 index c740f0a..0000000 --- a/tests/Activators/__snapshots__/FileActivatorTest__it_creates_valid_json_file_after_disabling__1.txt +++ /dev/null @@ -1,3 +0,0 @@ -{ - "Article": false -} \ No newline at end of file diff --git a/tests/Activators/__snapshots__/FileActivatorTest__it_creates_valid_json_file_after_disabling__2.txt b/tests/Activators/__snapshots__/FileActivatorTest__it_creates_valid_json_file_after_disabling__2.txt deleted file mode 100644 index c740f0a..0000000 --- a/tests/Activators/__snapshots__/FileActivatorTest__it_creates_valid_json_file_after_disabling__2.txt +++ /dev/null @@ -1,3 +0,0 @@ -{ - "Article": false -} \ No newline at end of file diff --git a/tests/Activators/__snapshots__/FileActivatorTest__it_creates_valid_json_file_after_enabling__1.txt b/tests/Activators/__snapshots__/FileActivatorTest__it_creates_valid_json_file_after_enabling__1.txt deleted file mode 100644 index 7cbac35..0000000 --- a/tests/Activators/__snapshots__/FileActivatorTest__it_creates_valid_json_file_after_enabling__1.txt +++ /dev/null @@ -1,3 +0,0 @@ -{ - "Article": true -} \ No newline at end of file diff --git a/tests/Activators/__snapshots__/FileActivatorTest__it_creates_valid_json_file_after_enabling__2.txt b/tests/Activators/__snapshots__/FileActivatorTest__it_creates_valid_json_file_after_enabling__2.txt deleted file mode 100644 index 7cbac35..0000000 --- a/tests/Activators/__snapshots__/FileActivatorTest__it_creates_valid_json_file_after_enabling__2.txt +++ /dev/null @@ -1,3 +0,0 @@ -{ - "Article": true -} \ No newline at end of file diff --git a/tests/BaseTestCase.php b/tests/BaseTestCase.php deleted file mode 100644 index 2fc2161..0000000 --- a/tests/BaseTestCase.php +++ /dev/null @@ -1,193 +0,0 @@ -withoutMockingConsoleOutput(); - } - } - - protected function getPackageProviders($app): array - { - return [ - ModulesServiceProvider::class, - ]; - } - - /** - * @param Application $app - */ - protected function getEnvironmentSetUp($app): void - { - $app['config']->set('database.default', 'sqlite'); - $app['config']->set('database.connections.sqlite', [ - 'driver' => 'sqlite', - 'database' => ':memory:', - 'prefix' => '', - ]); - - $app['config']->set('modules.paths.assets', public_path('modules')); - $app['config']->set('modules.generator', [ - 'path' => base_path('app/Modules'), - 'namespace' => 'App\\Modules', - 'custom_stubs' => base_path('app/Ship/Generators/custom-stubs'), - 'user_model' => 'App\\Modules\\User\\Models\\User', - 'create_permission' => [ - 'action' => "App\\Modules\\Authorization\\Actions\\CreatePermissionAction", - 'dto' => "App\\Modules\\Authorization\\DTO\\CreatePermissionDTO" - ], - 'components' => [ - 'action' => [ - 'path' => 'Actions', - 'generate' => true - ], - 'api-controller' => [ - 'path' => 'UI/API/Controllers', - 'generate' => false - ], - 'api-query-wizard' => [ - 'path' => 'UI/API/QueryWizards', - 'generate' => true - ], - 'api-request' => [ - 'path' => 'UI/API/Requests', - 'generate' => true - ], - 'api-resource' => [ - 'path' => 'UI/API/Resources', - 'generate' => true - ], - 'api-route' => [ - 'path' => 'UI/API/Routes', - 'generate' => true - ], - 'api-test' => [ - 'path' => 'UI/API/Tests', - 'generate' => true - ], - 'cli-command' => [ - 'path' => 'UI/CLI/Commands', - 'generate' => false - ], - 'cli-test' => [ - 'path' => 'UI/CLI/Tests', - 'generate' => false - ], - 'dto' => [ - 'path' => 'DTO', - 'generate' => true - ], - 'event' => [ - 'path' => 'Events', - 'generate' => false - ], - 'exception' => [ - 'path' => 'Exceptions', - 'generate' => false - ], - 'factory' => [ - 'path' => 'Data/Factories', - 'generate' => true - ], - 'feature-test' => [ - 'path' => 'Tests/Feature', - 'generate' => false - ], - 'job' => [ - 'path' => 'Jobs', - 'generate' => false - ], - 'lang' => [ - 'path' => 'Resources/lang', - 'generate' => false - ], - 'listener' => [ - 'path' => 'Listeners', - 'generate' => false - ], - 'mail' => [ - 'path' => 'Mails', - 'generate' => false - ], - 'middleware' => [ - 'path' => 'Middleware', - 'generate' => false - ], - 'migration' => [ - 'path' => 'Data/Migrations', - 'generate' => true - ], - 'model' => [ - 'path' => 'Models', - 'generate' => true - ], - 'notification' => [ - 'path' => 'Notifications', - 'generate' => false - ], - 'observer' => [ - 'path' => 'Observers', - 'generate' => false - ], - 'policy' => [ - 'path' => 'Policies', - 'generate' => true - ], - 'provider' => [ - 'path' => 'Providers', - 'generate' => true - ], - 'rule' => [ - 'path' => 'Rules', - 'generate' => false - ], - 'seeder' => [ - 'path' => 'Data/Seeders', - 'generate' => true - ], - 'web-controller' => [ - 'path' => 'UI/WEB/Controllers', - 'generate' => false - ], - 'web-request' => [ - 'path' => 'UI/WEB/Requests', - 'generate' => false, - ], - 'web-route' => [ - 'path' => 'UI/WEB/Routes', - 'generate' => false - ], - 'web-test' => [ - 'path' => 'UI/WEB/Tests', - 'generate' => false - ], - 'view' => [ - 'path' => 'Resources/views', - 'generate' => false - ], - 'unit-test' => [ - 'path' => 'Tests/Unit', - 'generate' => false - ], - ], - ]); - - $app['config']->set('modules.cache.enabled', true); - $app['config']->set('modules.composer.composer-output', true); - } -} diff --git a/tests/CollectionTest.php b/tests/CollectionTest.php deleted file mode 100644 index 1412383..0000000 --- a/tests/CollectionTest.php +++ /dev/null @@ -1,68 +0,0 @@ - 'module-one', - 'path' => __DIR__ . '/fixtures/stubs/valid/Article', - 'namespace' => 'App\\SomeModules\\Article' - ]; - $moduleTwoAttributes = [ - 'name' => 'module-two', - 'path' =>__DIR__ . '/fixtures/stubs/valid/Requirement', - 'namespace' => 'App\\SomeModules\\Requirement' - ]; - - $modules = [ - 'article' => new Module( - $this->app, - $moduleOneAttributes['name'], - $moduleOneAttributes['path'], - $moduleOneAttributes['namespace'] - ), - 'requirement' => new Module( - $this->app, - $moduleTwoAttributes['name'], - $moduleTwoAttributes['path'], - $moduleTwoAttributes['namespace'] - ), - ]; - $moduleOneJson = $modules['article']->json()->toArray(); - $moduleTwoJson = $modules['requirement']->json()->toArray(); - $collection = new Collection($modules); - $collectionArray = $collection->toArray(); - - $this->assertArrayHasKey('path', $collectionArray['article']); - $this->assertEquals($moduleOneAttributes['path'], $collectionArray['article']['path']); - $this->assertEquals($moduleOneAttributes['name'], $collectionArray['article']['name']); - $this->assertEquals($moduleOneAttributes['namespace'], $collectionArray['article']['namespace']); - $this->assertEquals($moduleOneJson, $collectionArray['article']['module_json']['module.json']); - - $this->assertArrayHasKey('path', $collectionArray['requirement']); - $this->assertEquals($moduleTwoAttributes['path'], $collectionArray['requirement']['path']); - $this->assertEquals($moduleTwoAttributes['name'], $collectionArray['requirement']['name']); - $this->assertEquals($moduleTwoAttributes['namespace'], $collectionArray['requirement']['namespace']); - $this->assertEquals($moduleTwoJson, $collectionArray['requirement']['module_json']['module.json']); - } - - public function testMethodAllReturnsTheCollectionItems(): void - { - $modules = [ - 'article' => new Module($this->app, 'module-one', __DIR__ . '/fixtures/stubs/valid/Article', 'App\\Module\\Article'), - 'requirement' => new Module($this->app, 'module-two', __DIR__ . '/fixtures/stubs/valid/Requirement', 'App\\Module\\Requirement'), - ]; - $collection = new Collection($modules); - $items = $collection->all(); - - $this->assertCount(2, $items); - $this->assertInstanceOf(Module::class, $items['article']); - $this->assertInstanceOf(Module::class, $items['requirement']); - } -} diff --git a/tests/Commands/BaseCommandTest.php b/tests/Commands/BaseCommandTest.php new file mode 100644 index 0000000..63f62ea --- /dev/null +++ b/tests/Commands/BaseCommandTest.php @@ -0,0 +1,513 @@ +setModules([ + __DIR__ . '/../fixtures/stubs/modules/valid/article-category', + __DIR__ . '/../fixtures/stubs/modules/valid/article', + __DIR__ . '/../fixtures/stubs/modules/valid/author', + __DIR__ . '/../fixtures/stubs/modules/valid/empty-module', + __DIR__ . '/../fixtures/stubs/modules/valid/empty', + __DIR__ . '/../fixtures/stubs/modules/valid/navigation', + ], $this->app->basePath('/modules')); +}); + +describe('single "module" argument', function () { + class CommandWithSingleModuleArgument extends BaseCommand + { + /** + * The name and signature of the console command. + * + * @var string + */ + protected $signature = 'single-module-argument-command {module? : Module name}'; + + /** + * Execute the console command. + */ + public function handle(): int + { + try { + $moduleToHandle = $this->getModuleArgumentOrFail(); + $this->line($moduleToHandle->getPackageName()); + } catch (ModuleNotFound $exception) { + $this->error($exception->getMessage()); + + return self::FAILURE; + } + + return self::SUCCESS; + } + } + + beforeEach(function () { + /** @var Illuminate\Foundation\Console\Kernel $console */ + $console = $this->app[ConsoleKernelContract::class]; + $console->registerCommand(new CommandWithSingleModuleArgument($this->app[ModulesRepository::class])); + }); + + it('can accept a package name as a single "module" argument', function () { + $this->artisan('single-module-argument-command laraneat/article-category') + ->expectsOutput('laraneat/article-category') + ->assertSuccessful(); + + $this->artisan('single-module-argument-command laraneat/article') + ->expectsOutput('laraneat/article') + ->assertSuccessful(); + + $this->artisan('single-module-argument-command laraneat/author') + ->expectsOutput('laraneat/author') + ->assertSuccessful(); + + $this->artisan('single-module-argument-command laraneat/empty') + ->expectsOutput('laraneat/empty') + ->assertSuccessful(); + + $this->artisan('single-module-argument-command empty/empty') + ->expectsOutput('empty/empty') + ->assertSuccessful(); + + $this->artisan('single-module-argument-command laraneat/location') + ->expectsOutput('laraneat/location') + ->assertSuccessful(); + }); + + it('can accept a module name as a single "module" argument', function () { + $this->artisan('single-module-argument-command Article') + ->expectsOutput('laraneat/article') + ->assertSuccessful(); + + $this->artisan('single-module-argument-command article') + ->expectsOutput('laraneat/article') + ->assertSuccessful(); + + $this->artisan('single-module-argument-command article-category') + ->expectsOutput('laraneat/article-category') + ->assertSuccessful(); + + $this->artisan('single-module-argument-command ArticleCategory') + ->expectsOutput('laraneat/article-category') + ->assertSuccessful(); + + $this->artisan('single-module-argument-command location') + ->expectsOutput('laraneat/location') + ->assertSuccessful(); + + $this->artisan('single-module-argument-command Location') + ->expectsOutput('laraneat/location') + ->assertSuccessful(); + + $this->artisan('single-module-argument-command navigation') + ->expectsOutput('laraneat/location') + ->assertSuccessful(); + + $this->artisan('single-module-argument-command Navigation') + ->expectsOutput('laraneat/location') + ->assertSuccessful(); + }); + + it('displays an error message when passing an invalid single "module" argument', function () { + $this->artisan('single-module-argument-command laraneat/articlee') + ->expectsOutput("Module with 'laraneat/articlee' name or package name does not exist!") + ->assertFailed(); + + $this->artisan('single-module-argument-command laraneat') + ->expectsOutput("Module with 'laraneat' name or package name does not exist!") + ->assertFailed(); + + $this->artisan('single-module-argument-command laraneat/article/') + ->expectsOutput("Module with 'laraneat/article/' name or package name does not exist!") + ->assertFailed(); + + $this->artisan('single-module-argument-command /article') + ->expectsOutput("Module with '/article' name or package name does not exist!") + ->assertFailed(); + + $this->artisan('single-module-argument-command articlee') + ->expectsOutput("Module with 'articlee' name or package name does not exist!") + ->assertFailed(); + }); + + it('gives a module selection if the "module" argument is not passed', function () { + $this->artisan('single-module-argument-command') + ->expectsChoice( + question: 'Select one module', + answer: 'laraneat/empty', + answers: [ + 'laraneat/article-category', + 'laraneat/article', + 'laraneat/author', + 'laraneat/empty', + 'empty/empty', + 'laraneat/location', + ] + ) + ->expectsOutput('laraneat/empty') + ->assertSuccessful(); + }); + + it('gives a module selection if 2 or more modules with the same names are found', function () { + $expectedChoiceOptions = [ + 'laraneat/empty', + 'empty/empty', + ]; + + $this->artisan('single-module-argument-command Empty') + ->expectsChoice( + question: "2 modules with name 'Empty' found, please select one module from those found", + answer: 'empty/empty', + answers: $expectedChoiceOptions + ) + ->expectsOutput('empty/empty') + ->assertSuccessful(); + + $this->artisan('single-module-argument-command empty') + ->expectsChoice( + question: "2 modules with name 'empty' found, please select one module from those found", + answer: 'laraneat/empty', + answers: $expectedChoiceOptions + ) + ->expectsOutput('laraneat/empty') + ->assertSuccessful(); + }); +}); + +describe('multiple "module" argument', function () { + class CommandWithMultipleModuleArgument extends BaseCommand + { + /** + * The name and signature of the console command. + * + * @var string + */ + protected $signature = 'multiple-module-argument-command {module?* : Module name(s) or package name(s)}'; + + /** + * Execute the console command. + */ + public function handle(): int + { + try { + $modulesToHandle = $this->getModuleArgumentOrFail(); + $this->line( + collect($modulesToHandle) + ->map(fn (\Laraneat\Modules\Module $module) => $module->getPackageName()) + ->join(', ') + ); + } catch (ModuleNotFound $exception) { + $this->error($exception->getMessage()); + + return self::FAILURE; + } + + return self::SUCCESS; + } + } + + beforeEach(function () { + /** @var Illuminate\Foundation\Console\Kernel $console */ + $console = $this->app[ConsoleKernelContract::class]; + $console->registerCommand(new CommandWithMultipleModuleArgument($this->app[ModulesRepository::class])); + }); + + it('can accept a package name as a multiple "module" argument', function () { + $this->artisan('multiple-module-argument-command laraneat/article laraneat/empty empty/empty') + ->expectsOutput('laraneat/article, laraneat/empty, empty/empty') + ->assertSuccessful(); + + $this->artisan('multiple-module-argument-command laraneat/article laraneat/article laraneat/empty empty/empty laraneat/empty') + ->expectsOutput('laraneat/article, laraneat/empty, empty/empty') + ->assertSuccessful(); + + $this->artisan('multiple-module-argument-command laraneat/author') + ->expectsOutput('laraneat/author') + ->assertSuccessful(); + + $this->artisan('multiple-module-argument-command laraneat/author laraneat/author laraneat/author') + ->expectsOutput('laraneat/author') + ->assertSuccessful(); + }); + + it('can accept "all" as a multiple "module" argument', function () { + $this->artisan('multiple-module-argument-command all') + ->expectsOutput('laraneat/article-category, laraneat/article, laraneat/author, laraneat/empty, empty/empty, laraneat/location') + ->assertSuccessful(); + + $this->artisan('multiple-module-argument-command all laraneat/empty laraneat/author') + ->expectsOutput('laraneat/article-category, laraneat/article, laraneat/author, laraneat/empty, empty/empty, laraneat/location') + ->assertSuccessful(); + + $this->artisan('multiple-module-argument-command laraneat/empty all laraneat/author') + ->expectsOutput('laraneat/article-category, laraneat/article, laraneat/author, laraneat/empty, empty/empty, laraneat/location') + ->assertSuccessful(); + + $this->artisan('multiple-module-argument-command laraneat/author laraneat/empty all') + ->expectsOutput('laraneat/article-category, laraneat/article, laraneat/author, laraneat/empty, empty/empty, laraneat/location') + ->assertSuccessful(); + }); + + it('can accept a module name as a multiple "module" argument', function () { + $this->artisan('multiple-module-argument-command Article location Navigation ArticleCategory Location article-category') + ->expectsOutput('laraneat/article, laraneat/location, laraneat/article-category') + ->assertSuccessful(); + + $this->artisan('multiple-module-argument-command ArticleCategory articleCategory article-category article_category') + ->expectsOutput('laraneat/article-category') + ->assertSuccessful(); + }); + + it('can accept a module name and package name as a multiple "module" argument', function () { + $this->artisan('multiple-module-argument-command Article location Navigation empty/empty ArticleCategory Location article-category') + ->expectsOutput('laraneat/article, laraneat/location, empty/empty, laraneat/article-category') + ->assertSuccessful(); + + $this->artisan('multiple-module-argument-command ArticleCategory articleCategory laraneat/article-category article-category article_category') + ->expectsOutput('laraneat/article-category') + ->assertSuccessful(); + }); + + it('displays an error message when passing an invalid multiple "module" argument', function () { + $this->artisan('multiple-module-argument-command laraneat/empty laraneat/articlee laraneat/navigation') + ->expectsOutput("Module with 'laraneat/articlee' name or package name does not exist!") + ->assertFailed(); + + $this->artisan('multiple-module-argument-command laraneat laraneat/navigation') + ->expectsOutput("Module with 'laraneat' name or package name does not exist!") + ->assertFailed(); + + $this->artisan('multiple-module-argument-command Author laraneat/navigation') + ->expectsOutput("Module with 'laraneat/navigation' name or package name does not exist!") + ->assertFailed(); + + $this->artisan('multiple-module-argument-command article /article') + ->expectsOutput("Module with '/article' name or package name does not exist!") + ->assertFailed(); + }); + + it('gives a module selection if the multiple "module" argument is not passed', function () { + // Laravel's expectsChoice expects keys and values merged when using associative arrays + // The options are: ['all' => 'All modules', 'laraneat/article' => 'laraneat/article', ...] + // This results in: ['All modules', 'all', 'empty/empty', 'empty/empty', ...] + // In Laravel 10, Symfony adds empty string at position 0 and 'None' at position 2 for multiselect prompts + $isLaravel10 = version_compare(app()->version(), '11.0', '<'); + + $expectedAnswers = $isLaravel10 + ? [ + '', // empty string added by Symfony in Laravel 10 + 'All modules', + 'None', // 'None' option added by Symfony in Laravel 10 + 'all', + 'empty/empty', + 'empty/empty', + 'laraneat/article', + 'laraneat/article', + 'laraneat/article-category', + 'laraneat/article-category', + 'laraneat/author', + 'laraneat/author', + 'laraneat/empty', + 'laraneat/empty', + 'laraneat/location', + 'laraneat/location', + ] + : [ + 'All modules', + 'all', + 'empty/empty', + 'empty/empty', + 'laraneat/article', + 'laraneat/article', + 'laraneat/article-category', + 'laraneat/article-category', + 'laraneat/author', + 'laraneat/author', + 'laraneat/empty', + 'laraneat/empty', + 'laraneat/location', + 'laraneat/location', + ]; + + $this->artisan('multiple-module-argument-command') + ->expectsChoice( + question: 'Select one or more module', + answer: ['laraneat/article', 'empty/empty', 'laraneat/location'], + answers: $expectedAnswers + ) + ->expectsOutput('laraneat/article, empty/empty, laraneat/location') + ->assertSuccessful(); + }); + + it('gives a module selection if 2 or more modules with the same names are found', function () { + $expectedChoiceOptions = [ + 'laraneat/empty', + 'empty/empty', + ]; + + $this->artisan('multiple-module-argument-command empty laraneat/article Author Empty') + ->expectsChoice( + question: "2 modules with name 'empty' found, please select one module from those found", + answer: 'laraneat/empty', + answers: $expectedChoiceOptions + ) + ->expectsChoice( + question: "2 modules with name 'Empty' found, please select one module from those found", + answer: 'laraneat/empty', + answers: $expectedChoiceOptions + ) + ->expectsOutput('laraneat/empty, laraneat/article, laraneat/author') + ->assertSuccessful(); + + $this->artisan('multiple-module-argument-command empty laraneat/article Author Empty') + ->expectsChoice( + question: "2 modules with name 'empty' found, please select one module from those found", + answer: 'laraneat/empty', + answers: $expectedChoiceOptions + ) + ->expectsChoice( + question: "2 modules with name 'Empty' found, please select one module from those found", + answer: 'empty/empty', + answers: $expectedChoiceOptions + ) + ->expectsOutput('laraneat/empty, laraneat/article, laraneat/author, empty/empty') + ->assertSuccessful(); + }); +}); + +describe('getOptionOrAsk', function () { + class CommandWithOptionAsking extends BaseCommand + { + /** + * The name and signature of the console command. + * + * @var string + */ + protected $signature = 'command-with-option-asking {--foo=}'; + + /** + * Execute the console command. + */ + public function handle(): int + { + try { + $this->line($this->getOptionOrAsk('foo', 'Enter "foo" option')); + } catch (InvalidOptionException $exception) { + $this->line($exception->getMessage()); + + return self::FAILURE; + } + + return self::SUCCESS; + } + } + + beforeEach(function () { + /** @var Illuminate\Foundation\Console\Kernel $console */ + $console = $this->app[ConsoleKernelContract::class]; + $console->registerCommand(new CommandWithOptionAsking($this->app[ModulesRepository::class])); + }); + + it('asks for the option value if it is not specified', function () { + $this->artisan('command-with-option-asking') + ->expectsQuestion( + question: 'Enter "foo" option', + answer: 'some foo value', + ) + ->expectsOutput('some foo value') + ->assertSuccessful(); + + $this->artisan('command-with-option-asking --foo=') + ->expectsQuestion( + question: 'Enter "foo" option', + answer: 'some foo value 2', + ) + ->expectsOutput('some foo value 2') + ->assertSuccessful(); + + $this->artisan('command-with-option-asking') + ->expectsQuestion( + question: 'Enter "foo" option', + answer: '', + ) + ->expectsOutput("The 'foo' option is required") + ->assertFailed(); + }); + + it('does not ask for an option value if it specified', function () { + $this->artisan('command-with-option-asking --foo=some-foo-value') + ->expectsOutput('some-foo-value') + ->assertSuccessful(); + }); +}); + +describe('getOptionOrChoice', function () { + class CommandWithOptionChoice extends BaseCommand + { + public const CHOICES = ['first', 'second', 'third']; + + /** + * The name and signature of the console command. + * + * @var string + */ + protected $signature = 'command-with-option-choice {--foo=}'; + + /** + * Execute the console command. + */ + public function handle(): int + { + try { + $this->line($this->getOptionOrChoice('foo', 'Choice the "foo" option', self::CHOICES)); + } catch (InvalidOptionException $exception) { + $this->line($exception->getMessage()); + + return self::FAILURE; + } + + return self::SUCCESS; + } + } + + beforeEach(function () { + /** @var Illuminate\Foundation\Console\Kernel $console */ + $console = $this->app[ConsoleKernelContract::class]; + $console->registerCommand(new CommandWithOptionChoice($this->app[ModulesRepository::class])); + }); + + it('let you choose an option value if it is not specified', function () { + $this->artisan('command-with-option-choice') + ->expectsChoice( + question: 'Choice the "foo" option', + answer: 'first', + answers: CommandWithOptionChoice::CHOICES + ) + ->expectsOutput('first') + ->assertSuccessful(); + + $this->artisan('command-with-option-choice --foo=') + ->expectsChoice( + question: 'Choice the "foo" option', + answer: 'second', + answers: CommandWithOptionChoice::CHOICES + ) + ->expectsOutput('second') + ->assertSuccessful(); + }); + + it('doesnt let you choose an option value if it specified', function () { + $this->artisan('command-with-option-choice --foo=first') + ->expectsOutput('first') + ->assertSuccessful(); + }); + + it('shows an error if the passed option value is not valid', function () { + $this->artisan('command-with-option-choice --foo=some-invalid-value') + ->expectsOutput("Wrong 'foo' option value provided. Value should be one of 'first' or 'second' or 'third'.") + ->assertFailed(); + }); +}); diff --git a/tests/Commands/CacheClearCommandTest.php b/tests/Commands/CacheClearCommandTest.php new file mode 100644 index 0000000..779de5b --- /dev/null +++ b/tests/Commands/CacheClearCommandTest.php @@ -0,0 +1,13 @@ +mock(ModulesRepository::class, function (MockInterface $mock) { + $mock->shouldReceive('pruneModulesManifest')->once(); + }); + $this->artisan('module:clear') + ->expectsOutputToContain('Modules manifest cache cleared!') + ->assertSuccessful(); +}); diff --git a/tests/Commands/CacheCommandTest.php b/tests/Commands/CacheCommandTest.php new file mode 100644 index 0000000..6206208 --- /dev/null +++ b/tests/Commands/CacheCommandTest.php @@ -0,0 +1,13 @@ +mock(ModulesRepository::class, function (MockInterface $mock) { + $mock->shouldReceive('buildModulesManifest')->once(); + }); + $this->artisan('module:cache') + ->expectsOutputToContain('Modules manifest cached!') + ->assertSuccessful(); +}); diff --git a/tests/Commands/EnableCommandTest.php b/tests/Commands/EnableCommandTest.php deleted file mode 100644 index a7cf59d..0000000 --- a/tests/Commands/EnableCommandTest.php +++ /dev/null @@ -1,57 +0,0 @@ -artisan('module:make', ['name' => 'Article']); - $this->artisan('module:make', ['name' => 'Taxonomy']); - } - - protected function tearDown(): void - { - $this->app[RepositoryInterface::class]->delete('Article'); - $this->app[RepositoryInterface::class]->delete('Taxonomy'); - parent::tearDown(); - } - - /** @test */ - public function it_enables_a_module() - { - /** @var Module $blogModule */ - $blogModule = $this->app[RepositoryInterface::class]->find('Article'); - $blogModule->disable(); - - $code = $this->artisan('module:enable', ['module' => 'Article']); - - $this->assertTrue($blogModule->isEnabled()); - $this->assertSame(0, $code); - } - - /** @test */ - public function it_enables_all_modules() - { - /** @var Module $blogModule */ - $blogModule = $this->app[RepositoryInterface::class]->find('Article'); - $blogModule->disable(); - - /** @var Module $taxonomyModule */ - $taxonomyModule = $this->app[RepositoryInterface::class]->find('Taxonomy'); - $taxonomyModule->disable(); - - $code = $this->artisan('module:enable'); - - $this->assertTrue($blogModule->isEnabled() && $taxonomyModule->isEnabled()); - $this->assertSame(0, $code); - } -} diff --git a/tests/Commands/Generators/ActionMakeCommandTest.php b/tests/Commands/Generators/ActionMakeCommandTest.php index 71cd4b5..33cfc04 100644 --- a/tests/Commands/Generators/ActionMakeCommandTest.php +++ b/tests/Commands/Generators/ActionMakeCommandTest.php @@ -1,203 +1,106 @@ modulePath = base_path('app/Modules/Article'); - $this->finder = $this->app['files']; - $this->artisan('module:make', ['name' => 'Article', '--plain' => true]); - } - - protected function tearDown(): void - { - $this->app[RepositoryInterface::class]->delete('Article'); - parent::tearDown(); - } - - /** @test */ - public function it_generates_action_file() - { - $code = $this->artisan('module:make:action', [ - 'name' => 'MyAwesomeAction', - 'module' => 'Article', - '--stub' => 'plain' - ]); - - $this->assertTrue(is_file($this->modulePath . '/Actions/MyAwesomeAction.php')); - $this->assertSame(0, $code); - } - - /** @test */ - public function it_generated_correct_action_file_with_content() - { - $code = $this->artisan('module:make:action', [ - 'name' => 'MyAwesomeAction', - 'module' => 'Article', - '--stub' => 'plain' - ]); - - $file = $this->finder->get($this->modulePath . '/Actions/MyAwesomeAction.php'); - - $this->assertMatchesSnapshot($file); - $this->assertSame(0, $code); - } - - /** @test */ - public function it_can_change_the_default_path() - { - $this->app['config']->set('modules.generator.components.action.path', 'Foo/Bar\\NewActions'); - - $code = $this->artisan('module:make:action', [ - 'name' => 'Baz\\Bat/MyAwesomeAction', - 'module' => 'Article', - '--stub' => 'plain' - ]); - - $file = $this->finder->get($this->modulePath . '/Foo/Bar/NewActions/Baz/Bat/MyAwesomeAction.php'); - - $this->assertMatchesSnapshot($file); - $this->assertSame(0, $code); - } - - /** @test */ - public function it_can_change_the_default_namespace() - { - $this->app['config']->set('modules.generator.components.action.namespace', 'Foo/Bar\\NewActions/'); - - $code = $this->artisan('module:make:action', [ - 'name' => 'Baz\\Bat/MyAwesomeAction', - 'module' => 'Article', - '--stub' => 'plain' - ]); - - $file = $this->finder->get($this->modulePath . '/Actions/Baz/Bat/MyAwesomeAction.php'); - - $this->assertMatchesSnapshot($file); - $this->assertSame(0, $code); - } - - /** @test */ - public function it_can_generate_create_action_file() - { - $code = $this->artisan('module:make:action', [ - 'name' => 'Baz\\Bat/MyAwesomeCreateAction', - 'module' => 'Article', - '--stub' => 'create', - '--dto' => 'Foo/Bar\\TestDTO', - '--model' => 'Bar/TestModel', - '--request' => 'Bat/TestRequest', - '--resource' => 'Baz\\TestResource', - ]); - - $file = $this->finder->get($this->modulePath . '/Actions/Baz/Bat/MyAwesomeCreateAction.php'); - - $this->assertMatchesSnapshot($file); - $this->assertSame(0, $code); - } - - /** @test */ - public function it_throws_exception_when_classes_not_provided_for_create_action_file() - { - $this->expectException(InvalidOptionException::class); - - $this->artisan('module:make:action', [ - 'name' => 'Baz\\Bat/MyAwesomeCreateAction', - 'module' => 'Article', - '--stub' => 'create', - '-n' => '', - ]); - } - - /** @test */ - public function it_can_generate_delete_action_file() - { - $code = $this->artisan('module:make:action', [ - 'name' => 'Baz\\Bat/MyAwesomeDeleteAction', - 'module' => 'Article', - '--stub' => 'delete', - '--model' => 'Bar/TestModel', - '--request' => 'Bat/TestRequest', - ]); - - $file = $this->finder->get($this->modulePath . '/Actions/Baz/Bat/MyAwesomeDeleteAction.php'); - - $this->assertMatchesSnapshot($file); - $this->assertSame(0, $code); - } - - /** @test */ - public function it_can_generate_list_action_file() - { - $code = $this->artisan('module:make:action', [ - 'name' => 'Baz\\Bat/MyAwesomeListAction', - 'module' => 'Article', - '--stub' => 'list', - '--model' => 'Bar/TestModel', - '--request' => 'Bat/TestRequest', - '--resource' => 'Baz\\TestResource', - '--wizard' => 'Bat\\TestQueryWizard', - ]); - - $file = $this->finder->get($this->modulePath . '/Actions/Baz/Bat/MyAwesomeListAction.php'); - - $this->assertMatchesSnapshot($file); - $this->assertSame(0, $code); - } - - /** @test */ - public function it_can_generate_update_action_file() - { - $code = $this->artisan('module:make:action', [ - 'name' => 'Baz\\Bat/MyAwesomeUpdateAction', - 'module' => 'Article', - '--stub' => 'update', - '--dto' => 'Foo/Bar\\TestDTO', - '--model' => 'Bar/TestModel', - '--request' => 'Bat/TestRequest', - '--resource' => 'Baz\\TestResource', - ]); - - $file = $this->finder->get($this->modulePath . '/Actions/Baz/Bat/MyAwesomeUpdateAction.php'); - - $this->assertMatchesSnapshot($file); - $this->assertSame(0, $code); - } - - /** @test */ - public function it_can_generate_view_action_file() - { - $code = $this->artisan('module:make:action', [ - 'name' => 'Baz\\Bat/MyAwesomeViewAction', - 'module' => 'Article', - '--stub' => 'view', - '--model' => 'Bar/TestModel', - '--request' => 'Bat/TestRequest', - '--resource' => 'Baz\\TestResource', - '--wizard' => 'Bat\\TestQueryWizard', - ]); - - $file = $this->finder->get($this->modulePath . '/Actions/Baz/Bat/MyAwesomeViewAction.php'); - - $this->assertMatchesSnapshot($file); - $this->assertSame(0, $code); - } -} +use function PHPUnit\Framework\assertFileExists; +use function Spatie\Snapshots\assertMatchesFileSnapshot; + +beforeEach(function () { + $this->setModules([ + __DIR__ . '/../../fixtures/stubs/modules/valid/author', + ], $this->app->basePath('/modules')); +}); + +it('generates "plain" action for the module', function () { + $this->artisan('module:make:action', [ + 'name' => 'PlainAuthorAction', + 'module' => 'Author', + '--stub' => 'plain', + ]) + ->assertSuccessful(); + + $filePath = $this->app->basePath('/modules/author/src/Actions/PlainAuthorAction.php'); + assertFileExists($filePath); + assertMatchesFileSnapshot($filePath); +}); + +it('generates "create" action for the module', function () { + $this->artisan('module:make:action', [ + 'name' => 'CreateAuthorAction', + 'module' => 'Author', + '--stub' => 'create', + '--dto' => 'CreateAuthorDTO', + '--model' => 'Author', + '--request' => 'CreateAuthorRequest', + '--resource' => 'AuthorResource', + ]) + ->assertSuccessful(); + + $filePath = $this->app->basePath('/modules/author/src/Actions/CreateAuthorAction.php'); + assertFileExists($filePath); + assertMatchesFileSnapshot($filePath); +}); + +it('generates "update" action for the module', function () { + $this->artisan('module:make:action', [ + 'name' => 'UpdateAuthorAction', + 'module' => 'Author', + '--stub' => 'update', + '--dto' => 'UpdateAuthorDTO', + '--model' => 'Author', + '--request' => 'UpdateAuthorRequest', + '--resource' => 'AuthorResource', + ]) + ->assertSuccessful(); + + $filePath = $this->app->basePath('/modules/author/src/Actions/UpdateAuthorAction.php'); + assertFileExists($filePath); + assertMatchesFileSnapshot($filePath); +}); + +it('generates "delete" action for the module', function () { + $this->artisan('module:make:action', [ + 'name' => 'DeleteAuthorAction', + 'module' => 'Author', + '--stub' => 'delete', + '--model' => 'Author', + '--request' => 'DeleteAuthorRequest', + ]) + ->assertSuccessful(); + + $filePath = $this->app->basePath('/modules/author/src/Actions/DeleteAuthorAction.php'); + assertFileExists($filePath); + assertMatchesFileSnapshot($filePath); +}); + +it('generates "view" action for the module', function () { + $this->artisan('module:make:action', [ + 'name' => 'ViewAuthorAction', + 'module' => 'Author', + '--stub' => 'view', + '--model' => 'Author', + '--request' => 'ViewAuthorRequest', + '--resource' => 'AuthorResource', + '--wizard' => 'AuthorQueryWizard', + ]) + ->assertSuccessful(); + + $filePath = $this->app->basePath('/modules/author/src/Actions/ViewAuthorAction.php'); + assertFileExists($filePath); + assertMatchesFileSnapshot($filePath); +}); + +it('generates "list" action for the module', function () { + $this->artisan('module:make:action', [ + 'name' => 'ListAuthorsAction', + 'module' => 'Author', + '--stub' => 'list', + '--model' => 'Author', + '--request' => 'ListAuthorsRequest', + '--resource' => 'AuthorResource', + '--wizard' => 'AuthorsQueryWizard', + ]) + ->assertSuccessful(); + + $filePath = $this->app->basePath('/modules/author/src/Actions/ListAuthorsAction.php'); + assertFileExists($filePath); + assertMatchesFileSnapshot($filePath); +}); diff --git a/tests/Commands/Generators/BaseComponentGeneratorCommandTest.php b/tests/Commands/Generators/BaseComponentGeneratorCommandTest.php new file mode 100644 index 0000000..a29f586 --- /dev/null +++ b/tests/Commands/Generators/BaseComponentGeneratorCommandTest.php @@ -0,0 +1,160 @@ +setModules([ + __DIR__ . '/../../fixtures/stubs/modules/valid/article-category', + __DIR__ . '/../../fixtures/stubs/modules/valid/article', + __DIR__ . '/../../fixtures/stubs/modules/valid/author', + __DIR__ . '/../../fixtures/stubs/modules/valid/empty-module', + __DIR__ . '/../../fixtures/stubs/modules/valid/empty', + __DIR__ . '/../../fixtures/stubs/modules/valid/navigation', + ], $this->app->basePath('/modules')); +}); + +describe('getFullClassFromOptionOrAsk()', function () { + class GetFullClassFromOptionOrAskTestCommand extends BaseComponentGeneratorCommand + { + protected $signature = 'test:command {module?} {--model=}'; + + public function handle(): int + { + $module = $this->getModuleArgumentOrFail(); + $modelClass = $this->getFullClassFromOptionOrAsk( + optionName: 'model', + question: 'Enter "model" class name', + componentType: ModuleComponentType::Model, + module: $module + ); + $this->line($modelClass); + + return self::SUCCESS; + } + + protected function getContents(): string + { + return ''; + } + } + + beforeEach(function () { + /** @var Illuminate\Foundation\Console\Kernel $console */ + $console = $this->app[ConsoleKernelContract::class]; + $console->registerCommand(new GetFullClassFromOptionOrAskTestCommand( + $this->app[ModulesRepository::class], + $this->app[Filesystem::class], + )); + }); + + + it('returns the full class of component if only the base class name is specified', function () { + $this->artisan('test:command', [ + 'module' => 'navigation', + '--model' => 'SomeGeoLocationModel', + ]) + ->expectsOutput('Modules\\GeoLocation\\Models\\SomeGeoLocationModel'); + + $this->artisan('test:command', [ + 'module' => 'navigation', + '--model' => 'Some\\GeoLocationModel', + ]) + ->expectsOutput('Modules\\GeoLocation\\Models\\Some\\GeoLocationModel'); + + $this->artisan('test:command', [ + 'module' => 'navigation', + ]) + ->expectsQuestion('Enter "model" class name', 'SomeGeoLocationModel') + ->expectsOutput('Modules\\GeoLocation\\Models\\SomeGeoLocationModel'); + + $this->artisan('test:command', [ + 'module' => 'navigation', + ]) + ->expectsQuestion('Enter "model" class name', 'Some\\GeoLocationModel') + ->expectsOutput('Modules\\GeoLocation\\Models\\Some\\GeoLocationModel'); + }); + + it('returns the specified class if it begins with a backslash', function () { + $this->artisan('test:command', [ + 'module' => 'navigation', + ]) + ->expectsQuestion('Enter "model" class name', '\\Modules\\Article\\Models\\Article') + ->expectsOutput('\\Modules\\Article\\Models\\Article'); + + $this->artisan('test:command', [ + 'module' => 'navigation', + '--model' => '\\Modules\\Article\\Models\\Article', + ]) + ->expectsOutput('\\Modules\\Article\\Models\\Article'); + + $this->artisan('test:command', [ + 'module' => 'navigation', + ]) + ->expectsQuestion('Enter "model" class name', '\\Modules\\GeoLocation\\Models\\GeoLocation') + ->expectsOutput('\\Modules\\GeoLocation\\Models\\GeoLocation'); + + $this->artisan('test:command', [ + 'module' => 'navigation', + '--model' => '\\Modules\\GeoLocation\\Models\\GeoLocation', + ]) + ->expectsOutput('\\Modules\\GeoLocation\\Models\\GeoLocation'); + }); +}); + +describe('class name validation', function () { + it('rejects invalid class names starting with a number', function () { + $this->artisan('module:make:action', [ + 'name' => '123Invalid', + 'module' => 'Author', + ]) + ->expectsOutputToContain('not a valid PHP class name') + ->assertFailed(); + }); + + it('rejects invalid class names with special characters', function () { + $this->artisan('module:make:action', [ + 'name' => 'Invalid-Class', + 'module' => 'Author', + ]) + ->expectsOutputToContain('not a valid PHP class name') + ->assertFailed(); + }); + + it('rejects invalid class names with spaces', function () { + $this->artisan('module:make:action', [ + 'name' => 'Invalid Class', + 'module' => 'Author', + ]) + ->expectsOutputToContain('not a valid PHP class name') + ->assertFailed(); + }); + + it('accepts valid class names', function () { + $this->artisan('module:make:action', [ + 'name' => 'ValidAction', + 'module' => 'Author', + '--stub' => 'plain', + ]) + ->assertSuccessful(); + + $this->artisan('module:make:action', [ + 'name' => '_ValidAction', + 'module' => 'Author', + '--stub' => 'plain', + '--force' => true, + ]) + ->assertSuccessful(); + + $this->artisan('module:make:action', [ + 'name' => 'Valid123Action', + 'module' => 'Author', + '--stub' => 'plain', + '--force' => true, + ]) + ->assertSuccessful(); + }); +}); diff --git a/tests/Commands/Generators/CommandMakeCommandTest.php b/tests/Commands/Generators/CommandMakeCommandTest.php index 9aff431..fc827b6 100644 --- a/tests/Commands/Generators/CommandMakeCommandTest.php +++ b/tests/Commands/Generators/CommandMakeCommandTest.php @@ -1,96 +1,24 @@ modulePath = base_path('app/Modules/Article'); - $this->finder = $this->app['files']; - $this->artisan('module:make', ['name' => 'Article', '--plain' => true]); - } - - protected function tearDown(): void - { - $this->app[RepositoryInterface::class]->delete('Article'); - parent::tearDown(); - } - - /** @test */ - public function it_generates_command_file() - { - $code = $this->artisan('module:make:command', [ - 'name' => 'MyAwesomeCommand', - 'module' => 'Article', - '-n' => true, - ]); - - $this->assertTrue(is_file($this->modulePath . '/UI/CLI/Commands/MyAwesomeCommand.php')); - $this->assertSame(0, $code); - } - - /** @test */ - public function it_generated_correct_command_file_with_content() - { - $code = $this->artisan('module:make:command', [ - 'name' => 'Foo/Bar\\MyAwesomeCommand', - 'module' => 'Article', - '--command' => 'my:awesome:command' - ]); - - $file = $this->finder->get($this->modulePath . '/UI/CLI/Commands/Foo/Bar/MyAwesomeCommand.php'); - - $this->assertMatchesSnapshot($file); - $this->assertSame(0, $code); - } - - /** @test */ - public function it_can_change_the_default_path_for_command_file() - { - $this->app['config']->set('modules.generator.components.cli-command.path', 'Foo/Bar\\Commands'); - - $code = $this->artisan('module:make:command', [ - 'name' => 'Baz\\Bat/MyAwesomeCommand', - 'module' => 'Article', - '--command' => 'my:awesome:command' - ]); - - $file = $this->finder->get($this->modulePath . '/Foo/Bar/Commands/Baz/Bat/MyAwesomeCommand.php'); - - $this->assertMatchesSnapshot($file); - $this->assertSame(0, $code); - } - - /** @test */ - public function it_can_change_the_default_namespace_for_command_file() - { - $this->app['config']->set('modules.generator.components.cli-command.namespace', 'Foo/Bar\\Commands/'); - - $code = $this->artisan('module:make:command', [ - 'name' => 'Baz\\Bat/MyAwesomeCommand', - 'module' => 'Article', - '--command' => 'my:awesome:command' - ]); - - $file = $this->finder->get($this->modulePath . '/UI/CLI/Commands/Baz/Bat/MyAwesomeCommand.php'); - - $this->assertMatchesSnapshot($file); - $this->assertSame(0, $code); - } -} +use function PHPUnit\Framework\assertFileExists; +use function Spatie\Snapshots\assertMatchesFileSnapshot; + +beforeEach(function () { + $this->setModules([ + __DIR__ . '/../../fixtures/stubs/modules/valid/author', + ], $this->app->basePath('/modules')); +}); + +it('generates console command for the module', function () { + $this->artisan('module:make:command', [ + 'name' => 'SomeAuthorCommand', + 'module' => 'Author', + '--signature' => 'author:some-command {--foo : foo option}', + '--description' => 'Some author command description', + ]) + ->assertSuccessful(); + + $filePath = $this->app->basePath('/modules/author/src/UI/CLI/Commands/SomeAuthorCommand.php'); + assertFileExists($filePath); + assertMatchesFileSnapshot($filePath); +}); diff --git a/tests/Commands/Generators/ComponentsMakeCommandTest.php b/tests/Commands/Generators/ComponentsMakeCommandTest.php deleted file mode 100644 index bd233c7..0000000 --- a/tests/Commands/Generators/ComponentsMakeCommandTest.php +++ /dev/null @@ -1,191 +0,0 @@ -artisan('module:make', ['name' => 'Article', '--plain' => true]); - $this->modulePath = base_path('app/Modules/Article'); - $this->finder = $this->app['files']; - $this->repository = $this->app[RepositoryInterface::class]; - $this->activator = $this->app[ActivatorInterface::class]; - $this->moduleComponentPaths = [ - 'Actions/CreateArticleAction.php', - 'Actions/UpdateArticleAction.php', - 'Actions/DeleteArticleAction.php', - 'Actions/ViewArticleAction.php', - 'Actions/ListArticlesAction.php', - 'Data/Factories/ArticleFactory.php', - 'Data/Seeders/ArticlePermissionsSeeder_1.php', - 'DTO/CreateArticleDTO.php', - 'Models/Article.php', - 'Policies/ArticlePolicy.php', - 'Providers/ArticleServiceProvider.php', - 'Providers/RouteServiceProvider.php', - 'UI/API/QueryWizards/ArticleQueryWizard.php', - 'UI/API/QueryWizards/ArticlesQueryWizard.php', - 'UI/API/Resources/ArticleResource.php', - 'UI/API/Requests/CreateArticleRequest.php', - 'UI/API/Requests/UpdateArticleRequest.php', - 'UI/API/Requests/DeleteArticleRequest.php', - 'UI/API/Requests/ViewArticleRequest.php', - 'UI/API/Requests/ListArticlesRequest.php', - 'UI/API/Routes/v1/create_article.php', - 'UI/API/Routes/v1/update_article.php', - 'UI/API/Routes/v1/delete_article.php', - 'UI/API/Routes/v1/view_article.php', - 'UI/API/Routes/v1/list_articles.php', - 'UI/API/Tests/CreateArticleTest.php', - 'UI/API/Tests/UpdateArticleTest.php', - 'UI/API/Tests/DeleteArticleTest.php', - 'UI/API/Tests/ViewArticleTest.php', - 'UI/API/Tests/ListArticlesTest.php', - ]; - } - - protected function tearDown(): void - { - $this->finder->deleteDirectory($this->modulePath); - if ($this->finder->isDirectory(base_path('app/Modules/Blog'))) { - $this->finder->deleteDirectory(base_path('app/Modules/Blog')); - } - $this->activator->reset(); - - parent::tearDown(); - } - - /** @test */ - public function it_generates_module_components() - { - $code = $this->artisan('module:make:components', [ - 'name' => 'Article', - ]); - - foreach ($this->moduleComponentPaths as $componentPath) { - $path = $this->modulePath . '/' . $componentPath; - $this->assertFileExists($path); - $this->assertMatchesSnapshot($this->finder->get($path)); - } - - $migrationFiles = $this->finder->allFiles($this->modulePath . '/Data/Migrations'); - $this->assertCount(1, $migrationFiles); - $this->assertMatchesSnapshot($migrationFiles[0]->getContents()); - - $this->assertSame(0, $code); - } - - /** @test */ - public function it_can_set_custom_model() - { - $this->artisan('module:make', ['name' => 'Blog', '--entity' => 'Post']); - $this->artisan('module:make:components', ['name' => 'Blog', '--entity' => 'Comment']); - - $modulePath = base_path('app/Modules/Blog'); - $moduleComponentPaths = [ - 'Actions/CreatePostAction.php', - 'Actions/UpdatePostAction.php', - 'Actions/DeletePostAction.php', - 'Actions/ViewPostAction.php', - 'Actions/ListPostsAction.php', - 'Data/Factories/PostFactory.php', - 'Data/Seeders/PostPermissionsSeeder_1.php', - 'DTO/CreatePostDTO.php', - 'Models/Post.php', - 'Policies/PostPolicy.php', - 'Providers/BlogServiceProvider.php', - 'Providers/RouteServiceProvider.php', - 'UI/API/QueryWizards/PostQueryWizard.php', - 'UI/API/QueryWizards/PostsQueryWizard.php', - 'UI/API/Resources/PostResource.php', - 'UI/API/Requests/CreatePostRequest.php', - 'UI/API/Requests/UpdatePostRequest.php', - 'UI/API/Requests/DeletePostRequest.php', - 'UI/API/Requests/ViewPostRequest.php', - 'UI/API/Requests/ListPostsRequest.php', - 'UI/API/Routes/v1/create_post.php', - 'UI/API/Routes/v1/update_post.php', - 'UI/API/Routes/v1/delete_post.php', - 'UI/API/Routes/v1/view_post.php', - 'UI/API/Routes/v1/list_posts.php', - 'UI/API/Tests/CreatePostTest.php', - 'UI/API/Tests/UpdatePostTest.php', - 'UI/API/Tests/DeletePostTest.php', - 'UI/API/Tests/ViewPostTest.php', - 'UI/API/Tests/ListPostsTest.php', - - 'Actions/CreateCommentAction.php', - 'Actions/UpdateCommentAction.php', - 'Actions/DeleteCommentAction.php', - 'Actions/ViewCommentAction.php', - 'Actions/ListCommentsAction.php', - 'Data/Factories/CommentFactory.php', - 'Data/Seeders/CommentPermissionsSeeder_1.php', - 'DTO/CreateCommentDTO.php', - 'Models/Comment.php', - 'Policies/CommentPolicy.php', - 'Providers/BlogServiceProvider.php', - 'Providers/RouteServiceProvider.php', - 'UI/API/QueryWizards/CommentQueryWizard.php', - 'UI/API/QueryWizards/CommentsQueryWizard.php', - 'UI/API/Resources/CommentResource.php', - 'UI/API/Requests/CreateCommentRequest.php', - 'UI/API/Requests/UpdateCommentRequest.php', - 'UI/API/Requests/DeleteCommentRequest.php', - 'UI/API/Requests/ViewCommentRequest.php', - 'UI/API/Requests/ListCommentsRequest.php', - 'UI/API/Routes/v1/create_comment.php', - 'UI/API/Routes/v1/update_comment.php', - 'UI/API/Routes/v1/delete_comment.php', - 'UI/API/Routes/v1/view_comment.php', - 'UI/API/Routes/v1/list_comments.php', - 'UI/API/Tests/CreateCommentTest.php', - 'UI/API/Tests/UpdateCommentTest.php', - 'UI/API/Tests/DeleteCommentTest.php', - 'UI/API/Tests/ViewCommentTest.php', - 'UI/API/Tests/ListCommentsTest.php', - ]; - - $moduleJsonPath = $modulePath . '/module.json'; - $this->assertFileExists($moduleJsonPath); - $this->assertMatchesSnapshot($this->finder->get($moduleJsonPath)); - - $composerJsonPath = $modulePath . '/composer.json'; - $this->assertFileExists($composerJsonPath); - $this->assertMatchesSnapshot($this->finder->get($composerJsonPath)); - - foreach ($moduleComponentPaths as $componentPath) { - $path = $modulePath . '/' . $componentPath; - $this->assertFileExists($path); - $this->assertMatchesSnapshot($this->finder->get($path)); - } - - $migrationFiles = $this->finder->allFiles($modulePath . '/Data/Migrations'); - $this->assertCount(2, $migrationFiles); - $this->assertMatchesSnapshot($migrationFiles[0]->getContents()); - $this->assertMatchesSnapshot($migrationFiles[1]->getContents()); - } -} diff --git a/tests/Commands/Generators/ControllerMakeCommandTest.php b/tests/Commands/Generators/ControllerMakeCommandTest.php index bd5af61..b5b8296 100644 --- a/tests/Commands/Generators/ControllerMakeCommandTest.php +++ b/tests/Commands/Generators/ControllerMakeCommandTest.php @@ -1,158 +1,36 @@ modulePath = base_path('app/Modules/Article'); - $this->finder = $this->app['files']; - $this->artisan('module:make', ['name' => 'Article', '--plain' => true]); - } - - protected function tearDown(): void - { - $this->app[RepositoryInterface::class]->delete('Article'); - parent::tearDown(); - } - - /** @test */ - public function it_generates_api_controller_file() - { - $code = $this->artisan('module:make:controller', [ - 'name' => 'MyAwesomeAPIController', - 'module' => 'Article', - '--ui' => 'api' - ]); - - $this->assertTrue(is_file($this->modulePath . '/UI/API/Controllers/MyAwesomeAPIController.php')); - $this->assertSame(0, $code); - } - - /** @test */ - public function it_generates_web_controller_file() - { - $code = $this->artisan('module:make:controller', [ - 'name' => 'MyAwesomeWEBController', - 'module' => 'Article', - '--ui' => 'web' - ]); - - $this->assertTrue(is_file($this->modulePath . '/UI/WEB/Controllers/MyAwesomeWEBController.php')); - $this->assertSame(0, $code); - } - - /** @test */ - public function it_generated_correct_api_controller_file_with_content() - { - $code = $this->artisan('module:make:controller', [ - 'name' => 'Foo/Bar\\MyAwesomeAPIController', - 'module' => 'Article', - '--ui' => 'api' - ]); - - $file = $this->finder->get($this->modulePath . '/UI/API/Controllers/Foo/Bar/MyAwesomeAPIController.php'); - - $this->assertMatchesSnapshot($file); - $this->assertSame(0, $code); - } - - /** @test */ - public function it_generated_correct_web_controller_file_with_content() - { - $code = $this->artisan('module:make:controller', [ - 'name' => 'Foo/Bar\\MyAwesomeWEBController', - 'module' => 'Article', - '--ui' => 'web' - ]); - - $file = $this->finder->get($this->modulePath . '/UI/WEB/Controllers/Foo/Bar/MyAwesomeWEBController.php'); - - $this->assertMatchesSnapshot($file); - $this->assertSame(0, $code); - } - - /** @test */ - public function it_can_change_the_default_path_for_api_controller_file() - { - $this->app['config']->set('modules.generator.components.api-controller.path', 'Foo/Bar\\Controllers'); - - $code = $this->artisan('module:make:controller', [ - 'name' => 'Baz\\Bat/MyAwesomeAPIController', - 'module' => 'Article', - '--ui' => 'api' - ]); - - $file = $this->finder->get($this->modulePath . '/Foo/Bar/Controllers/Baz/Bat/MyAwesomeAPIController.php'); - - $this->assertMatchesSnapshot($file); - $this->assertSame(0, $code); - } - - /** @test */ - public function it_can_change_the_default_namespace_for_api_controller_file() - { - $this->app['config']->set('modules.generator.components.api-controller.namespace', 'Foo/Bar\\Controllers/'); - - $code = $this->artisan('module:make:controller', [ - 'name' => 'Baz\\Bat/MyAwesomeAPIController', - 'module' => 'Article', - '--ui' => 'api' - ]); - - $file = $this->finder->get($this->modulePath . '/UI/API/Controllers/Baz/Bat/MyAwesomeAPIController.php'); - - $this->assertMatchesSnapshot($file); - $this->assertSame(0, $code); - } - - /** @test */ - public function it_can_change_the_default_path_for_web_controller_file() - { - $this->app['config']->set('modules.generator.components.web-controller.path', 'Foo/Bar\\Controllers'); - - $code = $this->artisan('module:make:controller', [ - 'name' => 'Baz\\Bat/MyAwesomeWEBController', - 'module' => 'Article', - '--ui' => 'web' - ]); - - $file = $this->finder->get($this->modulePath . '/Foo/Bar/Controllers/Baz/Bat/MyAwesomeWEBController.php'); - - $this->assertMatchesSnapshot($file); - $this->assertSame(0, $code); - } - - /** @test */ - public function it_can_change_the_default_namespace_for_web_controller_file() - { - $this->app['config']->set('modules.generator.components.web-controller.namespace', 'Foo/Bar\\Controllers/'); - - $code = $this->artisan('module:make:controller', [ - 'name' => 'Baz\\Bat/MyAwesomeWEBController', - 'module' => 'Article', - '--ui' => 'web' - ]); - - $file = $this->finder->get($this->modulePath . '/UI/WEB/Controllers/Baz/Bat/MyAwesomeWEBController.php'); - - $this->assertMatchesSnapshot($file); - $this->assertSame(0, $code); - } -} +use function PHPUnit\Framework\assertFileExists; +use function Spatie\Snapshots\assertMatchesFileSnapshot; + +beforeEach(function () { + $this->setModules([ + __DIR__ . '/../../fixtures/stubs/modules/valid/author', + ], $this->app->basePath('/modules')); +}); + +it('generates "api" controller for the module', function () { + $this->artisan('module:make:controller', [ + 'name' => 'ApiAuthorController', + 'module' => 'Author', + '--ui' => 'api', + ]) + ->assertSuccessful(); + + $filePath = $this->app->basePath('/modules/author/src/UI/API/Controllers/ApiAuthorController.php'); + assertFileExists($filePath); + assertMatchesFileSnapshot($filePath); +}); + +it('generates "web" controller for the module', function () { + $this->artisan('module:make:controller', [ + 'name' => 'WebAuthorController', + 'module' => 'Author', + '--ui' => 'web', + ]) + ->assertSuccessful(); + + $filePath = $this->app->basePath('/modules/author/src/UI/WEB/Controllers/WebAuthorController.php'); + assertFileExists($filePath); + assertMatchesFileSnapshot($filePath); +}); diff --git a/tests/Commands/Generators/DTOMakeCommandTest.php b/tests/Commands/Generators/DTOMakeCommandTest.php index 386cc12..fe221d6 100644 --- a/tests/Commands/Generators/DTOMakeCommandTest.php +++ b/tests/Commands/Generators/DTOMakeCommandTest.php @@ -1,92 +1,22 @@ modulePath = base_path('app/Modules/Article'); - $this->finder = $this->app['files']; - $this->artisan('module:make', ['name' => 'Article', '--plain' => true]); - } - - protected function tearDown(): void - { - $this->app[RepositoryInterface::class]->delete('Article'); - parent::tearDown(); - } - - /** @test */ - public function it_generates_dto_file() - { - $code = $this->artisan('module:make:dto', [ - 'name' => 'MyAwesomeDTO', - 'module' => 'Article', - ]); - - $this->assertTrue(is_file($this->modulePath . '/DTO/MyAwesomeDTO.php')); - $this->assertSame(0, $code); - } - - /** @test */ - public function it_generated_correct_dto_file() - { - $code = $this->artisan('module:make:dto', [ - 'name' => 'Foo/Bar\\MyAwesomeDTO', - 'module' => 'Article' - ]); - - $file = $this->finder->get($this->modulePath . '/DTO/Foo/Bar/MyAwesomeDTO.php'); - - $this->assertMatchesSnapshot($file); - $this->assertSame(0, $code); - } - - /** @test */ - public function it_can_change_the_default_path_for_dto_file() - { - $this->app['config']->set('modules.generator.components.dto.path', 'Foo/Bar\\DTO'); - - $code = $this->artisan('module:make:dto', [ - 'name' => 'Baz\\Bat/MyAwesomeDTO', - 'module' => 'Article' - ]); - - $file = $this->finder->get($this->modulePath . '/Foo/Bar/DTO/Baz/Bat/MyAwesomeDTO.php'); - - $this->assertMatchesSnapshot($file); - $this->assertSame(0, $code); - } - - /** @test */ - public function it_can_change_the_default_namespace_for_dto_file() - { - $this->app['config']->set('modules.generator.components.dto.namespace', 'Foo/Bar\\DTO/'); - - $code = $this->artisan('module:make:dto', [ - 'name' => 'Baz\\Bat/MyAwesomeDTO', - 'module' => 'Article' - ]); - - $file = $this->finder->get($this->modulePath . '/DTO/Baz/Bat/MyAwesomeDTO.php'); - - $this->assertMatchesSnapshot($file); - $this->assertSame(0, $code); - } -} +use function PHPUnit\Framework\assertFileExists; +use function Spatie\Snapshots\assertMatchesFileSnapshot; + +beforeEach(function () { + $this->setModules([ + __DIR__ . '/../../fixtures/stubs/modules/valid/author', + ], $this->app->basePath('/modules')); +}); + +it('generates dto for the module', function () { + $this->artisan('module:make:dto', [ + 'name' => 'SomeAuthorDTO', + 'module' => 'Author', + ]) + ->assertSuccessful(); + + $filePath = $this->app->basePath('/modules/author/src/DTO/SomeAuthorDTO.php'); + assertFileExists($filePath); + assertMatchesFileSnapshot($filePath); +}); diff --git a/tests/Commands/Generators/EventMakeCommandTest.php b/tests/Commands/Generators/EventMakeCommandTest.php index 7dcd602..d0482ff 100644 --- a/tests/Commands/Generators/EventMakeCommandTest.php +++ b/tests/Commands/Generators/EventMakeCommandTest.php @@ -1,92 +1,22 @@ modulePath = base_path('app/Modules/Article'); - $this->finder = $this->app['files']; - $this->artisan('module:make', ['name' => 'Article', '--plain' => true]); - } - - protected function tearDown(): void - { - $this->app[RepositoryInterface::class]->delete('Article'); - parent::tearDown(); - } - - /** @test */ - public function it_generates_event_file() - { - $code = $this->artisan('module:make:event', [ - 'name' => 'MyAwesomeEvent', - 'module' => 'Article' - ]); - - $this->assertTrue(is_file($this->modulePath . '/Events/MyAwesomeEvent.php')); - $this->assertSame(0, $code); - } - - /** @test */ - public function it_generated_correct_event_file_with_content() - { - $code = $this->artisan('module:make:event', [ - 'name' => 'Foo/Bar\\MyAwesomeEvent', - 'module' => 'Article' - ]); - - $file = $this->finder->get($this->modulePath . '/Events/Foo/Bar/MyAwesomeEvent.php'); - - $this->assertMatchesSnapshot($file); - $this->assertSame(0, $code); - } - - /** @test */ - public function it_can_change_the_default_path_for_event_file() - { - $this->app['config']->set('modules.generator.components.event.path', 'Foo/Bar\\Events'); - - $code = $this->artisan('module:make:event', [ - 'name' => 'Baz\\Bat/MyAwesomeEvent', - 'module' => 'Article' - ]); - - $file = $this->finder->get($this->modulePath . '/Foo/Bar/Events/Baz/Bat/MyAwesomeEvent.php'); - - $this->assertMatchesSnapshot($file); - $this->assertSame(0, $code); - } - - /** @test */ - public function it_can_change_the_default_namespace_for_event_file() - { - $this->app['config']->set('modules.generator.components.event.namespace', 'Foo/Bar\\Events/'); - - $code = $this->artisan('module:make:event', [ - 'name' => 'Baz\\Bat/MyAwesomeEvent', - 'module' => 'Article' - ]); - - $file = $this->finder->get($this->modulePath . '/Events/Baz/Bat/MyAwesomeEvent.php'); - - $this->assertMatchesSnapshot($file); - $this->assertSame(0, $code); - } -} +use function PHPUnit\Framework\assertFileExists; +use function Spatie\Snapshots\assertMatchesFileSnapshot; + +beforeEach(function () { + $this->setModules([ + __DIR__ . '/../../fixtures/stubs/modules/valid/author', + ], $this->app->basePath('/modules')); +}); + +it('generates event for the module', function () { + $this->artisan('module:make:event', [ + 'name' => 'SomeAuthorEvent', + 'module' => 'Author', + ]) + ->assertSuccessful(); + + $filePath = $this->app->basePath('/modules/author/src/Events/SomeAuthorEvent.php'); + assertFileExists($filePath); + assertMatchesFileSnapshot($filePath); +}); diff --git a/tests/Commands/Generators/ExceptionMakeCommandTest.php b/tests/Commands/Generators/ExceptionMakeCommandTest.php index 7c5be37..b5943a4 100644 --- a/tests/Commands/Generators/ExceptionMakeCommandTest.php +++ b/tests/Commands/Generators/ExceptionMakeCommandTest.php @@ -1,92 +1,22 @@ modulePath = base_path('app/Modules/Article'); - $this->finder = $this->app['files']; - $this->artisan('module:make', ['name' => 'Article', '--plain' => true]); - } - - protected function tearDown(): void - { - $this->app[RepositoryInterface::class]->delete('Article'); - parent::tearDown(); - } - - /** @test */ - public function it_generates_exception_file() - { - $code = $this->artisan('module:make:exception', [ - 'name' => 'MyAwesomeException', - 'module' => 'Article', - ]); - - $this->assertTrue(is_file($this->modulePath . '/Exceptions/MyAwesomeException.php')); - $this->assertSame(0, $code); - } - - /** @test */ - public function it_generated_correct_exception_file_with_content() - { - $code = $this->artisan('module:make:exception', [ - 'name' => 'Foo/Bar\\MyAwesomeException', - 'module' => 'Article', - ]); - - $file = $this->finder->get($this->modulePath . '/Exceptions/Foo/Bar/MyAwesomeException.php'); - - $this->assertMatchesSnapshot($file); - $this->assertSame(0, $code); - } - - /** @test */ - public function it_can_change_the_default_path_for_exception_file() - { - $this->app['config']->set('modules.generator.components.exception.path', 'Foo/Bar\\Exceptions'); - - $code = $this->artisan('module:make:exception', [ - 'name' => 'Baz\\Bat/MyAwesomeException', - 'module' => 'Article', - ]); - - $file = $this->finder->get($this->modulePath . '/Foo/Bar/Exceptions/Baz/Bat/MyAwesomeException.php'); - - $this->assertMatchesSnapshot($file); - $this->assertSame(0, $code); - } - - /** @test */ - public function it_can_change_the_default_namespace_for_exception_file() - { - $this->app['config']->set('modules.generator.components.exception.namespace', 'Foo/Bar\\Exceptions/'); - - $code = $this->artisan('module:make:exception', [ - 'name' => 'Baz\\Bat/MyAwesomeException', - 'module' => 'Article', - ]); - - $file = $this->finder->get($this->modulePath . '/Exceptions/Baz/Bat/MyAwesomeException.php'); - - $this->assertMatchesSnapshot($file); - $this->assertSame(0, $code); - } -} +use function PHPUnit\Framework\assertFileExists; +use function Spatie\Snapshots\assertMatchesFileSnapshot; + +beforeEach(function () { + $this->setModules([ + __DIR__ . '/../../fixtures/stubs/modules/valid/author', + ], $this->app->basePath('/modules')); +}); + +it('generates exception for the module', function () { + $this->artisan('module:make:exception', [ + 'name' => 'SomeAuthorException', + 'module' => 'Author', + ]) + ->assertSuccessful(); + + $filePath = $this->app->basePath('/modules/author/src/Exceptions/SomeAuthorException.php'); + assertFileExists($filePath); + assertMatchesFileSnapshot($filePath); +}); diff --git a/tests/Commands/Generators/FactoryMakeCommandTest.php b/tests/Commands/Generators/FactoryMakeCommandTest.php index e26f47a..8dc4410 100644 --- a/tests/Commands/Generators/FactoryMakeCommandTest.php +++ b/tests/Commands/Generators/FactoryMakeCommandTest.php @@ -1,96 +1,23 @@ modulePath = base_path('app/Modules/Article'); - $this->finder = $this->app['files']; - $this->artisan('module:make', ['name' => 'Article', '--plain' => true]); - } - - protected function tearDown(): void - { - $this->app[RepositoryInterface::class]->delete('Article'); - parent::tearDown(); - } - - /** @test */ - public function it_generates_factory_file() - { - $code = $this->artisan('module:make:factory', [ - 'name' => 'MyAwesomeFactory', - 'module' => 'Article', - '--model' => 'Some\\Nested/Model' - ]); - - $this->assertTrue(is_file($this->modulePath . '/Data/Factories/MyAwesomeFactory.php')); - $this->assertSame(0, $code); - } - - /** @test */ - public function it_generated_correct_factory_file_with_content() - { - $code = $this->artisan('module:make:factory', [ - 'name' => 'Foo/Bar\\MyAwesomeFactory', - 'module' => 'Article', - '--model' => 'Some\\Nested/Model' - ]); - - $file = $this->finder->get($this->modulePath . '/Data/Factories/Foo/Bar/MyAwesomeFactory.php'); - - $this->assertMatchesSnapshot($file); - $this->assertSame(0, $code); - } - - /** @test */ - public function it_can_change_the_default_path_for_factory_file() - { - $this->app['config']->set('modules.generator.components.factory.path', 'Foo/Bar\\Factories'); - - $code = $this->artisan('module:make:factory', [ - 'name' => 'Baz\\Bat/MyAwesomeFactory', - 'module' => 'Article', - '--model' => 'Some\\Nested/Model' - ]); - - $file = $this->finder->get($this->modulePath . '/Foo/Bar/Factories/Baz/Bat/MyAwesomeFactory.php'); - - $this->assertMatchesSnapshot($file); - $this->assertSame(0, $code); - } - - /** @test */ - public function it_can_change_the_default_namespace_for_factory_file() - { - $this->app['config']->set('modules.generator.components.factory.namespace', 'Foo/Bar\\Factories/'); - - $code = $this->artisan('module:make:factory', [ - 'name' => 'Baz\\Bat/MyAwesomeFactory', - 'module' => 'Article', - '--model' => 'Some\\Nested/Model' - ]); - - $file = $this->finder->get($this->modulePath . '/Data/Factories/Baz/Bat/MyAwesomeFactory.php'); - - $this->assertMatchesSnapshot($file); - $this->assertSame(0, $code); - } -} +use function PHPUnit\Framework\assertFileExists; +use function Spatie\Snapshots\assertMatchesFileSnapshot; + +beforeEach(function () { + $this->setModules([ + __DIR__ . '/../../fixtures/stubs/modules/valid/author', + ], $this->app->basePath('/modules')); +}); + +it('generates factory for the module', function () { + $this->artisan('module:make:factory', [ + 'name' => 'SomeAuthorFactory', + 'module' => 'Author', + '--model' => 'Author', + ]) + ->assertSuccessful(); + + $filePath = $this->app->basePath('/modules/author/database/factories/SomeAuthorFactory.php'); + assertFileExists($filePath); + assertMatchesFileSnapshot($filePath); +}); diff --git a/tests/Commands/Generators/JobMakeCommandTest.php b/tests/Commands/Generators/JobMakeCommandTest.php index 94d2a57..d43b528 100644 --- a/tests/Commands/Generators/JobMakeCommandTest.php +++ b/tests/Commands/Generators/JobMakeCommandTest.php @@ -1,124 +1,36 @@ modulePath = base_path('app/Modules/Article'); - $this->finder = $this->app['files']; - $this->artisan('module:make', ['name' => 'Article', '--plain' => true]); - } - - protected function tearDown(): void - { - $this->app[RepositoryInterface::class]->delete('Article'); - parent::tearDown(); - } - - /** @test */ - public function it_generates_plain_job_file() - { - $code = $this->artisan('module:make:job', [ - 'name' => 'MyAwesomePlainJob', - 'module' => 'Article', - '--stub' => 'plain' - ]); - - $this->assertTrue(is_file($this->modulePath . '/Jobs/MyAwesomePlainJob.php')); - $this->assertSame(0, $code); - } - - /** @test */ - public function it_generates_queued_job_file() - { - $code = $this->artisan('module:make:job', [ - 'name' => 'MyAwesomeQueuedJob', - 'module' => 'Article', - '--stub' => 'queued' - ]); - - $this->assertTrue(is_file($this->modulePath . '/Jobs/MyAwesomeQueuedJob.php')); - $this->assertSame(0, $code); - } - - /** @test */ - public function it_generated_correct_plain_job_file_with_content() - { - $code = $this->artisan('module:make:job', [ - 'name' => 'Foo/Bar\\MyAwesomePlainJob', - 'module' => 'Article', - '--stub' => 'plain' - ]); - - $file = $this->finder->get($this->modulePath . '/Jobs/Foo/Bar/MyAwesomePlainJob.php'); - - $this->assertMatchesSnapshot($file); - $this->assertSame(0, $code); - } - - /** @test */ - public function it_generated_correct_queued_job_file_with_content() - { - $code = $this->artisan('module:make:job', [ - 'name' => 'Foo/Bar\\MyAwesomeQueuedJob', - 'module' => 'Article', - '--stub' => 'queued' - ]); - - $file = $this->finder->get($this->modulePath . '/Jobs/Foo/Bar/MyAwesomeQueuedJob.php'); - - $this->assertMatchesSnapshot($file); - $this->assertSame(0, $code); - } - - /** @test */ - public function it_can_change_the_default_path_for_plain_job_file() - { - $this->app['config']->set('modules.generator.components.job.path', 'Foo/Bar\\Jobs'); - - $code = $this->artisan('module:make:job', [ - 'name' => 'Baz\\Bat/MyAwesomePlainJob', - 'module' => 'Article', - '--stub' => 'plain' - ]); - - $file = $this->finder->get($this->modulePath . '/Foo/Bar/Jobs/Baz/Bat/MyAwesomePlainJob.php'); - - $this->assertMatchesSnapshot($file); - $this->assertSame(0, $code); - } - - /** @test */ - public function it_can_change_the_default_namespace_for_plain_job_file() - { - $this->app['config']->set('modules.generator.components.job.namespace', 'Foo/Bar\\Jobs/'); - - $code = $this->artisan('module:make:job', [ - 'name' => 'Baz\\Bat/MyAwesomePlainJob', - 'module' => 'Article', - '--stub' => 'plain' - ]); - - $file = $this->finder->get($this->modulePath . '/Jobs/Baz/Bat/MyAwesomePlainJob.php'); - - $this->assertMatchesSnapshot($file); - $this->assertSame(0, $code); - } -} +use function PHPUnit\Framework\assertFileExists; +use function Spatie\Snapshots\assertMatchesFileSnapshot; + +beforeEach(function () { + $this->setModules([ + __DIR__ . '/../../fixtures/stubs/modules/valid/author', + ], $this->app->basePath('/modules')); +}); + +it('generates "plain" job for the module', function () { + $this->artisan('module:make:job', [ + 'name' => 'PlainAuthorJob', + 'module' => 'Author', + '--stub' => 'plain', + ]) + ->assertSuccessful(); + + $filePath = $this->app->basePath('/modules/author/src/Jobs/PlainAuthorJob.php'); + assertFileExists($filePath); + assertMatchesFileSnapshot($filePath); +}); + +it('generates "queued" job for the module', function () { + $this->artisan('module:make:job', [ + 'name' => 'QueuedAuthorJob', + 'module' => 'Author', + '--stub' => 'queued', + ]) + ->assertSuccessful(); + + $filePath = $this->app->basePath('/modules/author/src/Jobs/QueuedAuthorJob.php'); + assertFileExists($filePath); + assertMatchesFileSnapshot($filePath); +}); diff --git a/tests/Commands/Generators/ListenerMakeCommandTest.php b/tests/Commands/Generators/ListenerMakeCommandTest.php index 5f5fa1e..5b2b467 100644 --- a/tests/Commands/Generators/ListenerMakeCommandTest.php +++ b/tests/Commands/Generators/ListenerMakeCommandTest.php @@ -1,130 +1,38 @@ modulePath = base_path('app/Modules/Article'); - $this->finder = $this->app['files']; - $this->artisan('module:make', ['name' => 'Article', '--plain' => true]); - } - - protected function tearDown(): void - { - $this->app[RepositoryInterface::class]->delete('Article'); - parent::tearDown(); - } - - /** @test */ - public function it_generates_plain_listener_file() - { - $code = $this->artisan('module:make:listener', [ - 'name' => 'MyAwesomePlainListener', - 'module' => 'Article', - '--stub' => 'plain', - '--event' => 'Foo\\Bar/MyAwesomeEvent' - ]); - - $this->assertTrue(is_file($this->modulePath . '/Listeners/MyAwesomePlainListener.php')); - $this->assertSame(0, $code); - } - - /** @test */ - public function it_generates_queued_listener_file() - { - $code = $this->artisan('module:make:listener', [ - 'name' => 'MyAwesomeQueuedListener', - 'module' => 'Article', - '--stub' => 'queued', - '--event' => 'Foo\\Bar/MyAwesomeEvent' - ]); - - $this->assertTrue(is_file($this->modulePath . '/Listeners/MyAwesomeQueuedListener.php')); - $this->assertSame(0, $code); - } - - /** @test */ - public function it_generated_correct_plain_listener_file_with_content() - { - $code = $this->artisan('module:make:listener', [ - 'name' => 'Foo/Bar\\MyAwesomePlainListener', - 'module' => 'Article', - '--stub' => 'plain', - '--event' => 'Foo\\Bar/MyAwesomeEvent' - ]); - - $file = $this->finder->get($this->modulePath . '/Listeners/Foo/Bar/MyAwesomePlainListener.php'); - - $this->assertMatchesSnapshot($file); - $this->assertSame(0, $code); - } - - /** @test */ - public function it_generated_correct_queued_listener_file_with_content() - { - $code = $this->artisan('module:make:listener', [ - 'name' => 'Foo/Bar\\MyAwesomeQueuedListener', - 'module' => 'Article', - '--stub' => 'queued', - '--event' => 'Foo\\Bar/MyAwesomeEvent' - ]); - - $file = $this->finder->get($this->modulePath . '/Listeners/Foo/Bar/MyAwesomeQueuedListener.php'); - - $this->assertMatchesSnapshot($file); - $this->assertSame(0, $code); - } - - /** @test */ - public function it_can_change_the_default_path_for_plain_listener_file() - { - $this->app['config']->set('modules.generator.components.listener.path', 'Foo/Bar\\Listeners'); - - $code = $this->artisan('module:make:listener', [ - 'name' => 'Baz\\Bat/MyAwesomePlainListener', - 'module' => 'Article', - '--stub' => 'plain', - '--event' => 'Foo\\Bar/MyAwesomeEvent' - ]); - - $file = $this->finder->get($this->modulePath . '/Foo/Bar/Listeners/Baz/Bat/MyAwesomePlainListener.php'); - - $this->assertMatchesSnapshot($file); - $this->assertSame(0, $code); - } - - /** @test */ - public function it_can_change_the_default_namespace_for_plain_listener_file() - { - $this->app['config']->set('modules.generator.components.listener.namespace', 'Foo/Bar\\Listeners/'); - - $code = $this->artisan('module:make:listener', [ - 'name' => 'Baz\\Bat/MyAwesomePlainListener', - 'module' => 'Article', - '--stub' => 'plain', - '--event' => 'Foo\\Bar/MyAwesomeEvent' - ]); - - $file = $this->finder->get($this->modulePath . '/Listeners/Baz/Bat/MyAwesomePlainListener.php'); - - $this->assertMatchesSnapshot($file); - $this->assertSame(0, $code); - } -} +use function PHPUnit\Framework\assertFileExists; +use function Spatie\Snapshots\assertMatchesFileSnapshot; + +beforeEach(function () { + $this->setModules([ + __DIR__ . '/../../fixtures/stubs/modules/valid/author', + ], $this->app->basePath('/modules')); +}); + +it('generates "plain" listener for the module', function () { + $this->artisan('module:make:listener', [ + 'name' => 'PlainAuthorListener', + 'module' => 'Author', + '--stub' => 'plain', + '--event' => 'SomeAuthorEvent', + ]) + ->assertSuccessful(); + + $filePath = $this->app->basePath('/modules/author/src/Listeners/PlainAuthorListener.php'); + assertFileExists($filePath); + assertMatchesFileSnapshot($filePath); +}); + +it('generates "queued" listener for the module', function () { + $this->artisan('module:make:listener', [ + 'name' => 'QueuedAuthorListener', + 'module' => 'Author', + '--stub' => 'queued', + '--event' => 'SomeAuthorEvent', + ]) + ->assertSuccessful(); + + $filePath = $this->app->basePath('/modules/author/src/Listeners/QueuedAuthorListener.php'); + assertFileExists($filePath); + assertMatchesFileSnapshot($filePath); +}); diff --git a/tests/Commands/Generators/MailMakeCommandTest.php b/tests/Commands/Generators/MailMakeCommandTest.php index 4c8ee55..1c04001 100644 --- a/tests/Commands/Generators/MailMakeCommandTest.php +++ b/tests/Commands/Generators/MailMakeCommandTest.php @@ -1,124 +1,24 @@ modulePath = base_path('app/Modules/Article'); - $this->finder = $this->app['files']; - $this->artisan('module:make', ['name' => 'Article', '--plain' => true]); - } - - protected function tearDown(): void - { - $this->app[RepositoryInterface::class]->delete('Article'); - parent::tearDown(); - } - - /** @test */ - public function it_generates_plain_mail_file() - { - $code = $this->artisan('module:make:mail', [ - 'name' => 'MyAwesomePlainMail', - 'module' => 'Article', - '--stub' => 'plain' - ]); - - $this->assertTrue(is_file($this->modulePath . '/Mails/MyAwesomePlainMail.php')); - $this->assertSame(0, $code); - } - - /** @test */ - public function it_generates_queued_mail_file() - { - $code = $this->artisan('module:make:mail', [ - 'name' => 'MyAwesomeQueuedMail', - 'module' => 'Article', - '--stub' => 'queued' - ]); - - $this->assertTrue(is_file($this->modulePath . '/Mails/MyAwesomeQueuedMail.php')); - $this->assertSame(0, $code); - } - - /** @test */ - public function it_generated_correct_plain_mail_file_with_content() - { - $code = $this->artisan('module:make:mail', [ - 'name' => 'Foo/Bar\\MyAwesomePlainMail', - 'module' => 'Article', - '--stub' => 'plain' - ]); - - $file = $this->finder->get($this->modulePath . '/Mails/Foo/Bar/MyAwesomePlainMail.php'); - - $this->assertMatchesSnapshot($file); - $this->assertSame(0, $code); - } - - /** @test */ - public function it_generated_correct_queued_mail_file_with_content() - { - $code = $this->artisan('module:make:mail', [ - 'name' => 'Foo/Bar\\MyAwesomeQueuedMail', - 'module' => 'Article', - '--stub' => 'queued' - ]); - - $file = $this->finder->get($this->modulePath . '/Mails/Foo/Bar/MyAwesomeQueuedMail.php'); - - $this->assertMatchesSnapshot($file); - $this->assertSame(0, $code); - } - - /** @test */ - public function it_can_change_the_default_path_for_plain_mail_file() - { - $this->app['config']->set('modules.generator.components.mail.path', 'Foo/Bar\\Mails'); - - $code = $this->artisan('module:make:mail', [ - 'name' => 'Baz\\Bat/MyAwesomePlainMail', - 'module' => 'Article', - '--stub' => 'plain' - ]); - - $file = $this->finder->get($this->modulePath . '/Foo/Bar/Mails/Baz/Bat/MyAwesomePlainMail.php'); - - $this->assertMatchesSnapshot($file); - $this->assertSame(0, $code); - } - - /** @test */ - public function it_can_change_the_default_namespace_for_plain_mail_file() - { - $this->app['config']->set('modules.generator.components.mail.namespace', 'Foo/Bar\\Mails/'); - - $code = $this->artisan('module:make:mail', [ - 'name' => 'Baz\\Bat/MyAwesomePlainMail', - 'module' => 'Article', - '--stub' => 'plain' - ]); - - $file = $this->finder->get($this->modulePath . '/Mails/Baz/Bat/MyAwesomePlainMail.php'); - - $this->assertMatchesSnapshot($file); - $this->assertSame(0, $code); - } -} +use function PHPUnit\Framework\assertFileExists; +use function Spatie\Snapshots\assertMatchesFileSnapshot; + +beforeEach(function () { + $this->setModules([ + __DIR__ . '/../../fixtures/stubs/modules/valid/author', + ], $this->app->basePath('/modules')); +}); + +it('generates mail for the module', function () { + $this->artisan('module:make:mail', [ + 'name' => 'SomeAuthorMail', + 'module' => 'Author', + '--subject' => 'Some testing subject', + '--view' => 'author.mail.some-view', + ]) + ->assertSuccessful(); + + $filePath = $this->app->basePath('/modules/author/src/Mails/SomeAuthorMail.php'); + assertFileExists($filePath); + assertMatchesFileSnapshot($filePath); +}); diff --git a/tests/Commands/Generators/MiddlewareMakeCommandTest.php b/tests/Commands/Generators/MiddlewareMakeCommandTest.php index 8c5b7a2..4ea6e63 100644 --- a/tests/Commands/Generators/MiddlewareMakeCommandTest.php +++ b/tests/Commands/Generators/MiddlewareMakeCommandTest.php @@ -1,92 +1,22 @@ modulePath = base_path('app/Modules/Article'); - $this->finder = $this->app['files']; - $this->artisan('module:make', ['name' => 'Article', '--plain' => true]); - } - - protected function tearDown(): void - { - $this->app[RepositoryInterface::class]->delete('Article'); - parent::tearDown(); - } - - /** @test */ - public function it_generates_middleware_file() - { - $code = $this->artisan('module:make:middleware', [ - 'name' => 'MyAwesomeMiddleware', - 'module' => 'Article', - ]); - - $this->assertTrue(is_file($this->modulePath . '/Middleware/MyAwesomeMiddleware.php')); - $this->assertSame(0, $code); - } - - /** @test */ - public function it_generated_correct_middleware_file_with_content() - { - $code = $this->artisan('module:make:middleware', [ - 'name' => 'Foo/Bar\\MyAwesomeMiddleware', - 'module' => 'Article', - ]); - - $file = $this->finder->get($this->modulePath . '/Middleware/Foo/Bar/MyAwesomeMiddleware.php'); - - $this->assertMatchesSnapshot($file); - $this->assertSame(0, $code); - } - - /** @test */ - public function it_can_change_the_default_path_for_middleware_file() - { - $this->app['config']->set('modules.generator.components.middleware.path', 'Foo/Bar\\Middleware'); - - $code = $this->artisan('module:make:middleware', [ - 'name' => 'Baz\\Bat/MyAwesomeMiddleware', - 'module' => 'Article', - ]); - - $file = $this->finder->get($this->modulePath . '/Foo/Bar/Middleware/Baz/Bat/MyAwesomeMiddleware.php'); - - $this->assertMatchesSnapshot($file); - $this->assertSame(0, $code); - } - - /** @test */ - public function it_can_change_the_default_namespace_for_middleware_file() - { - $this->app['config']->set('modules.generator.components.middleware.namespace', 'Foo/Bar\\Middleware/'); - - $code = $this->artisan('module:make:middleware', [ - 'name' => 'Baz\\Bat/MyAwesomeMiddleware', - 'module' => 'Article', - ]); - - $file = $this->finder->get($this->modulePath . '/Middleware/Baz/Bat/MyAwesomeMiddleware.php'); - - $this->assertMatchesSnapshot($file); - $this->assertSame(0, $code); - } -} +use function PHPUnit\Framework\assertFileExists; +use function Spatie\Snapshots\assertMatchesFileSnapshot; + +beforeEach(function () { + $this->setModules([ + __DIR__ . '/../../fixtures/stubs/modules/valid/author', + ], $this->app->basePath('/modules')); +}); + +it('generates middleware for the module', function () { + $this->artisan('module:make:middleware', [ + 'name' => 'SomeAuthorMiddleware', + 'module' => 'Author', + ]) + ->assertSuccessful(); + + $filePath = $this->app->basePath('/modules/author/src/Middleware/SomeAuthorMiddleware.php'); + assertFileExists($filePath); + assertMatchesFileSnapshot($filePath); +}); diff --git a/tests/Commands/Generators/MigrationMakeCommandTest.php b/tests/Commands/Generators/MigrationMakeCommandTest.php index b83133a..431e612 100644 --- a/tests/Commands/Generators/MigrationMakeCommandTest.php +++ b/tests/Commands/Generators/MigrationMakeCommandTest.php @@ -1,169 +1,231 @@ modulePath = base_path('app/Modules/Article'); - $this->finder = $this->app['files']; - $this->artisan('module:make', ['name' => 'Article', '--plain' => true]); - } - - protected function tearDown(): void - { - $this->app[RepositoryInterface::class]->delete('Article'); - parent::tearDown(); - } - - /** @test */ - public function it_generates_migration_file() - { - $code = $this->artisan('module:make:migration', [ - 'name' => 'create_posts_table', - 'module' => 'Article' - ]); - - $files = $this->finder->allFiles($this->modulePath . '/Data/Migrations'); - - $this->assertCount(1, $files); - $this->assertSame(0, $code); - } - - /** @test */ - public function it_can_change_the_default_path() - { - $this->app['config']->set('modules.generator.components.migration.path', 'Foo/Bar\\Migrations'); - - $code = $this->artisan('module:make:migration', [ - 'name' => 'create_posts_table', - 'module' => 'Article' - ]); - - $files = $this->finder->allFiles($this->modulePath . '/Foo/Bar/Migrations'); - - $this->assertCount(1, $files); - $this->assertSame(0, $code); - } - - /** @test */ - public function it_generates_correct_create_migration_file_content() - { - $code = $this->artisan('module:make:migration', [ - 'name' => 'create_posts_table', +use Illuminate\Support\Carbon; + +use function PHPUnit\Framework\assertFileExists; +use function Spatie\Snapshots\assertMatchesFileSnapshot; + +beforeEach(function () { + $this->setModules([ + realpath(__DIR__ . '/../../fixtures/stubs/modules/valid/article'), + ], $this->app->basePath('/modules')); + + $date = Carbon::parse('2024-01-01'); + $this->travelTo($date); + $this->dateStamp = $date->format('Y_m_d_His_'); +}); + +it('generates "plain" migration for the module', function () { + $this->artisan('module:make:migration', [ + 'name' => 'some_plain_migration', + 'module' => 'Article', + '--stub' => 'plain', + ]) + ->assertSuccessful(); + + $filePath = $this->app->basePath("/modules/article/database/migrations/{$this->dateStamp}some_plain_migration.php"); + assertFileExists($filePath); + assertMatchesFileSnapshot($filePath); +}); + +it('generates "plain" migration for the module if the stub is not recognized by name', function () { + $this->artisan('module:make:migration', [ + 'name' => 'some_plain_migration', + 'module' => 'Article', + ]) + ->assertSuccessful(); + + $filePath = $this->app->basePath("/modules/article/database/migrations/{$this->dateStamp}some_plain_migration.php"); + assertFileExists($filePath); + assertMatchesFileSnapshot($filePath); +}); + +it('generates "create" migration for the module', function () { + $this->artisan('module:make:migration', [ + 'name' => 'new_articles_table', + 'module' => 'Article', + '--stub' => 'create', + '--fields' => 'title:string,excerpt:text,content:text,belongsTo:user:id:users', + ]) + ->assertSuccessful(); + + $filePath = $this->app->basePath("/modules/article/database/migrations/{$this->dateStamp}new_articles_table.php"); + assertFileExists($filePath); + assertMatchesFileSnapshot($filePath); +}); + +it('generates "create" migration for the module without fields', function () { + $this->artisan('module:make:migration', [ + 'name' => 'new_articles_table', + 'module' => 'Article', + '--stub' => 'create', + ]) + ->assertSuccessful(); + + $filePath = $this->app->basePath("/modules/article/database/migrations/{$this->dateStamp}new_articles_table.php"); + assertFileExists($filePath); + assertMatchesFileSnapshot($filePath); +}); + +it('automatically detects and generates "create" migration for the module', function () { + $this->artisan('module:make:migration', [ + 'name' => 'create_articles_table', + 'module' => 'Article', + '--fields' => 'title:string,excerpt:text,content:text,belongsTo:user:id:users', + ]) + ->assertSuccessful(); + + $filePath = $this->app->basePath("/modules/article/database/migrations/{$this->dateStamp}create_articles_table.php"); + assertFileExists($filePath); + assertMatchesFileSnapshot($filePath); +}); + +it('generates "add" migration for the module', function () { + $this->artisan('module:make:migration', [ + 'name' => 'modify_articles_table', + 'module' => 'Article', + '--stub' => 'add', + '--fields' => 'title:string,excerpt:text,belongsTo:user:id:users', + ]) + ->assertSuccessful(); + + $filePath = $this->app->basePath("/modules/article/database/migrations/{$this->dateStamp}modify_articles_table.php"); + assertFileExists($filePath); + assertMatchesFileSnapshot($filePath); +}); + +it('generates "add" migration for the module without fields', function () { + $this->artisan('module:make:migration', [ + 'name' => 'modify_articles_table', + 'module' => 'Article', + '--stub' => 'add', + ]) + ->assertSuccessful(); + + $filePath = $this->app->basePath("/modules/article/database/migrations/{$this->dateStamp}modify_articles_table.php"); + assertFileExists($filePath); + assertMatchesFileSnapshot($filePath); +}); + +it('automatically detects and generates "add" migration for the module', function () { + $this->artisan('module:make:migration', [ + 'name' => 'add_title_to_articles_table', + 'module' => 'Article', + '--fields' => 'title:string', + ]) + ->assertSuccessful(); + + $filePath = $this->app->basePath("/modules/article/database/migrations/{$this->dateStamp}add_title_to_articles_table.php"); + assertFileExists($filePath); + assertMatchesFileSnapshot($filePath); +}); + +it('generates "delete" migration for the module', function () { + $this->artisan('module:make:migration', [ + 'name' => 'modify_articles_table', + 'module' => 'Article', + '--stub' => 'delete', + '--fields' => 'title:string,excerpt:text,belongsTo:user:id:users', + ]) + ->assertSuccessful(); + + $filePath = $this->app->basePath("/modules/article/database/migrations/{$this->dateStamp}modify_articles_table.php"); + assertFileExists($filePath); + assertMatchesFileSnapshot($filePath); +}); + +it('generates "delete" migration for the module without fields', function () { + $this->artisan('module:make:migration', [ + 'name' => 'modify_articles_table', + 'module' => 'Article', + '--stub' => 'delete', + ]) + ->assertSuccessful(); + + $filePath = $this->app->basePath("/modules/article/database/migrations/{$this->dateStamp}modify_articles_table.php"); + assertFileExists($filePath); + assertMatchesFileSnapshot($filePath); +}); + +it('automatically detects and generates "delete" migration for the module', function () { + $this->artisan('module:make:migration', [ + 'name' => 'delete_title_from_articles_table', + 'module' => 'Article', + '--fields' => 'title:string', + ]) + ->assertSuccessful(); + + $filePath = $this->app->basePath("/modules/article/database/migrations/{$this->dateStamp}delete_title_from_articles_table.php"); + assertFileExists($filePath); + assertMatchesFileSnapshot($filePath); +}); + +it('generates "pivot" migration for the module', function () { + $this->artisan('module:make:migration', [ + 'name' => 'create_article_author_table', + 'module' => 'Article', + '--stub' => 'pivot', + ]) + ->expectsQuestion('Enter the name of first table', 'articles') + ->expectsQuestion('Enter the name of second table', 'authors') + ->assertSuccessful(); + + $filePath = $this->app->basePath("/modules/article/database/migrations/{$this->dateStamp}create_article_author_table.php"); + assertFileExists($filePath); + assertMatchesFileSnapshot($filePath); +}); + +describe('table name validation', function () { + it('rejects invalid table names with special characters', function () { + // Use a migration name that doesn't auto-detect the table name, + // so we can enter an invalid one via the prompt + $this->artisan('module:make:migration', [ + 'name' => 'modify_something', 'module' => 'Article', - '--fields' => 'title:string,excerpt:text,content:text,belongsTo:user:id:users' - ]); - - $files = $this->finder->allFiles($this->modulePath . '/Data/Migrations'); - $fileName = $files[0]->getRelativePathname(); - $file = $this->finder->get($this->modulePath . '/Data/Migrations/' . $fileName); - - $this->assertMatchesSnapshot($file); - $this->assertSame(0, $code); - } - - /** @test */ - public function it_generates_correct_add_migration_file_content() - { - $code = $this->artisan('module:make:migration', [ - 'name' => 'add_title_to_posts_table', + '--stub' => 'create', + ]) + ->expectsQuestion('Enter the table name', 'invalid-table') + ->expectsOutputToContain('not valid') + ->assertFailed(); + }); + + it('rejects invalid table names starting with a number', function () { + $this->artisan('module:make:migration', [ + 'name' => 'modify_something', 'module' => 'Article', - '--fields' => 'title:string' - ]); - - $files = $this->finder->allFiles($this->modulePath . '/Data/Migrations'); - $fileName = $files[0]->getRelativePathname(); - $file = $this->finder->get($this->modulePath . '/Data/Migrations/' . $fileName); - - $this->assertMatchesSnapshot($file); - $this->assertSame(0, $code); - } - - /** @test */ - public function it_generates_correct_delete_migration_file_content() - { - $code = $this->artisan('module:make:migration', [ - 'name' => 'remove_title_from_posts_table', - 'module' => 'Article', - '--fields' => 'title:string' - ]); - - $files = $this->finder->allFiles($this->modulePath . '/Data/Migrations'); - $fileName = $files[0]->getRelativePathname(); - $file = $this->finder->get($this->modulePath . '/Data/Migrations/' . $fileName); - - $this->assertMatchesSnapshot($file); - $this->assertSame(0, $code); - } - - /** @test */ - public function it_generates_correct_pivot_migration_file_content() - { - $code = $this->artisan('module:make:migration', [ - 'name' => 'create_user_role_table', + '--stub' => 'create', + ]) + ->expectsQuestion('Enter the table name', '123table') + ->expectsOutputToContain('not valid') + ->assertFailed(); + }); + + it('rejects invalid pivot table names', function () { + $this->artisan('module:make:migration', [ + 'name' => 'create_article_author_table', 'module' => 'Article', '--stub' => 'pivot', - '-n' => true - ]); - - $files = $this->finder->allFiles($this->modulePath . '/Data/Migrations'); - $fileName = $files[0]->getRelativePathname(); - $file = $this->finder->get($this->modulePath . '/Data/Migrations/' . $fileName); - - $this->assertMatchesSnapshot($file); - $this->assertSame(0, $code); - } - - /** @test */ -// public function it_generates_correct_pivot_migration_file_content_with_options() -// { -// $code = $this->artisan('module:make:migration', [ -// 'name' => 'create_user_role_table', -// 'module' => 'Article', -// '--stub' => 'pivot', -// '--tableOne' => 'users', -// '--tableTwo' => 'roles', -// ]); -// -// $files = $this->finder->allFiles($this->modulePath . '/Data/Migrations'); -// $fileName = $files[0]->getRelativePathname(); -// $file = $this->finder->get($this->modulePath . '/Data/Migrations/' . $fileName); -// -// $this->assertMatchesSnapshot($file); -// $this->assertSame(0, $code); -// } - - /** @test */ - public function it_generates_correct_plain_migration_file_content() - { - $code = $this->artisan('module:make:migration', [ - 'name' => 'create_posts_table', + ]) + ->expectsQuestion('Enter the name of first table', 'invalid-table') + ->expectsQuestion('Enter the name of second table', 'authors') + ->expectsOutputToContain('not valid') + ->assertFailed(); + }); + + it('accepts valid table names', function () { + $this->artisan('module:make:migration', [ + 'name' => 'create_valid_table_table', 'module' => 'Article', - '--stub' => 'plain' - ]); - - $files = $this->finder->allFiles($this->modulePath . '/Data/Migrations'); - $fileName = $files[0]->getRelativePathname(); - $file = $this->finder->get($this->modulePath . '/Data/Migrations/' . $fileName); + '--stub' => 'create', + ]) + ->assertSuccessful(); - $this->assertMatchesSnapshot($file); - $this->assertSame(0, $code); - } -} + $this->artisan('module:make:migration', [ + 'name' => 'create__underscore_table_table', + 'module' => 'Article', + '--stub' => 'create', + '--force' => true, + ]) + ->assertSuccessful(); + }); +}); diff --git a/tests/Commands/Generators/ModelMakeCommandTest.php b/tests/Commands/Generators/ModelMakeCommandTest.php index a7e374e..88d76f9 100644 --- a/tests/Commands/Generators/ModelMakeCommandTest.php +++ b/tests/Commands/Generators/ModelMakeCommandTest.php @@ -1,112 +1,36 @@ modulePath = base_path('app/Modules/Article'); - $this->finder = $this->app['files']; - $this->artisan('module:make', ['name' => 'Article', '--plain' => true]); - } - - protected function tearDown(): void - { - $this->app[RepositoryInterface::class]->delete('Article'); - parent::tearDown(); - } - - /** @test */ - public function it_generates_plain_model_file() - { - $code = $this->artisan('module:make:model', [ - 'name' => 'MyAwesomePlainModel', - 'module' => 'Article', - '--stub' => 'plain' - ]); - - $this->assertTrue(is_file($this->modulePath . '/Models/MyAwesomePlainModel.php')); - $this->assertSame(0, $code); - } - - /** @test */ - public function it_generated_correct_plain_model_file_with_content() - { - $code = $this->artisan('module:make:model', [ - 'name' => 'Foo/Bar\\MyAwesomePlainModel', - 'module' => 'Article', - '--stub' => 'plain' - ]); - - $file = $this->finder->get($this->modulePath . '/Models/Foo/Bar/MyAwesomePlainModel.php'); - - $this->assertMatchesSnapshot($file); - $this->assertSame(0, $code); - } - - /** @test */ - public function it_generated_correct_full_model_file_with_content() - { - $code = $this->artisan('module:make:model', [ - 'name' => 'Foo/Bar\\MyAwesomeFullModel', - 'module' => 'Article', - '--stub' => 'full', - '--factory' => 'Bar\\Baz/Bat\\MyAwesomeFactory' - ]); - - $file = $this->finder->get($this->modulePath . '/Models/Foo/Bar/MyAwesomeFullModel.php'); - - $this->assertMatchesSnapshot($file); - $this->assertSame(0, $code); - } - - /** @test */ - public function it_can_change_the_default_path_for_plain_model_file() - { - $this->app['config']->set('modules.generator.components.model.path', 'Foo/Bar\\Models'); - - $code = $this->artisan('module:make:model', [ - 'name' => 'Baz\\Bat/MyAwesomePlainModel', - 'module' => 'Article', - '--stub' => 'plain' - ]); - - $file = $this->finder->get($this->modulePath . '/Foo/Bar/Models/Baz/Bat/MyAwesomePlainModel.php'); - - $this->assertMatchesSnapshot($file); - $this->assertSame(0, $code); - } - - /** @test */ - public function it_can_change_the_default_namespace_for_plain_model_file() - { - $this->app['config']->set('modules.generator.components.model.namespace', 'Foo/Bar\\Models/'); - - $code = $this->artisan('module:make:model', [ - 'name' => 'Baz\\Bat/MyAwesomePlainModel', - 'module' => 'Article', - '--stub' => 'plain' - ]); - - $file = $this->finder->get($this->modulePath . '/Models/Baz/Bat/MyAwesomePlainModel.php'); - - $this->assertMatchesSnapshot($file); - $this->assertSame(0, $code); - } -} +use function PHPUnit\Framework\assertFileExists; +use function Spatie\Snapshots\assertMatchesFileSnapshot; + +beforeEach(function () { + $this->setModules([ + __DIR__ . '/../../fixtures/stubs/modules/valid/author', + ], $this->app->basePath('/modules')); +}); + +it('generates plain model for the module', function () { + $this->artisan('module:make:model', [ + 'name' => 'AuthorEmail', + 'module' => 'Author', + ]) + ->expectsQuestion('Enter the class name of the factory to be used in the model (optional)', '') + ->assertSuccessful(); + + $filePath = $this->app->basePath('/modules/author/src/Models/AuthorEmail.php'); + assertFileExists($filePath); + assertMatchesFileSnapshot($filePath); +}); + +it('generates model with factory for the module', function () { + $this->artisan('module:make:model', [ + 'name' => 'AuthorEmail', + 'module' => 'Author', + '--factory' => 'AuthorEmailFactory', + ]) + ->assertSuccessful(); + + $filePath = $this->app->basePath('/modules/author/src/Models/AuthorEmail.php'); + assertFileExists($filePath); + assertMatchesFileSnapshot($filePath); +}); diff --git a/tests/Commands/Generators/ModuleMakeCommandTest.php b/tests/Commands/Generators/ModuleMakeCommandTest.php index 9644398..8e66461 100644 --- a/tests/Commands/Generators/ModuleMakeCommandTest.php +++ b/tests/Commands/Generators/ModuleMakeCommandTest.php @@ -1,309 +1,119 @@ modulePath = base_path('app/Modules/Article'); - $this->finder = $this->app['files']; - $this->repository = $this->app[RepositoryInterface::class]; - $this->activator = $this->app[ActivatorInterface::class]; - $this->moduleComponentPaths = [ - 'Actions/CreateArticleAction.php', - 'Actions/UpdateArticleAction.php', - 'Actions/DeleteArticleAction.php', - 'Actions/ViewArticleAction.php', - 'Actions/ListArticlesAction.php', - 'Data/Factories/ArticleFactory.php', - 'Data/Seeders/ArticlePermissionsSeeder_1.php', - 'DTO/CreateArticleDTO.php', - 'Models/Article.php', - 'Policies/ArticlePolicy.php', - 'Providers/ArticleServiceProvider.php', - 'Providers/RouteServiceProvider.php', - 'UI/API/QueryWizards/ArticleQueryWizard.php', - 'UI/API/QueryWizards/ArticlesQueryWizard.php', - 'UI/API/Resources/ArticleResource.php', - 'UI/API/Requests/CreateArticleRequest.php', - 'UI/API/Requests/UpdateArticleRequest.php', - 'UI/API/Requests/DeleteArticleRequest.php', - 'UI/API/Requests/ViewArticleRequest.php', - 'UI/API/Requests/ListArticlesRequest.php', - 'UI/API/Routes/v1/create_article.php', - 'UI/API/Routes/v1/update_article.php', - 'UI/API/Routes/v1/delete_article.php', - 'UI/API/Routes/v1/view_article.php', - 'UI/API/Routes/v1/list_articles.php', - 'UI/API/Tests/CreateArticleTest.php', - 'UI/API/Tests/UpdateArticleTest.php', - 'UI/API/Tests/DeleteArticleTest.php', - 'UI/API/Tests/ViewArticleTest.php', - 'UI/API/Tests/ListArticlesTest.php', - ]; - } - - protected function tearDown(): void - { - $this->finder->deleteDirectory($this->modulePath); - if ($this->finder->isDirectory(base_path('app/Modules/ModuleName'))) { - $this->finder->deleteDirectory(base_path('app/Modules/ModuleName')); - } - if ($this->finder->isDirectory(base_path('app/Modules/Blog'))) { - $this->finder->deleteDirectory(base_path('app/Modules/Blog')); - } - $this->activator->reset(); - - parent::tearDown(); - } - - /** @test */ - public function it_generates_module() - { - $code = $this->artisan('module:make', ['name' => 'Article']); - - $this->assertDirectoryExists($this->modulePath); - $this->assertSame(0, $code); - } - - /** @test */ - public function it_generates_module_folders() - { - $code = $this->artisan('module:make', ['name' => 'Article']); - - foreach (config('modules.generator.components') as $directory) { - if ($directory['generate'] === true) { - $this->assertDirectoryExists($this->modulePath . '/' . $directory['path']); - } - } - $this->assertSame(0, $code); - } - - /** @test */ - public function it_generates_module_scaffold_files() - { - $code = $this->artisan('module:make', ['name' => 'Article']); - - $moduleJsonPath = $this->modulePath . '/module.json'; - $this->assertFileExists($moduleJsonPath); - $this->assertMatchesSnapshot($this->finder->get($moduleJsonPath)); - - $composerJsonPath = $this->modulePath . '/composer.json'; - $this->assertFileExists($composerJsonPath); - $this->assertMatchesSnapshot($this->finder->get($composerJsonPath)); - - $this->assertSame(0, $code); - } - - /** @test */ - public function it_generates_module_components() - { - $code = $this->artisan('module:make', ['name' => 'Article']); - - foreach ($this->moduleComponentPaths as $componentPath) { - $path = $this->modulePath . '/' . $componentPath; - $this->assertFileExists($path); - $this->assertMatchesSnapshot($this->finder->get($path)); - } - - $migrationFiles = $this->finder->allFiles($this->modulePath . '/Data/Migrations'); - $this->assertCount(1, $migrationFiles); - $this->assertMatchesSnapshot($migrationFiles[0]->getContents()); - - $this->assertSame(0, $code); - } - - /** @test */ - public function it_generates_module_folder_using_studly_case() - { - $code = $this->artisan('module:make', ['name' => 'ModuleName']); - - $this->assertTrue($this->finder->exists(base_path('app/Modules/ModuleName'))); - $this->assertSame(0, $code); - } - - /** @test */ - public function it_generates_module_namespace_using_studly_case() - { - $code = $this->artisan('module:make', ['name' => 'ModuleName']); - - $file = $this->finder->get(base_path('app/Modules/ModuleName') . '/Providers/ModuleNameServiceProvider.php'); - - $this->assertMatchesSnapshot($file); - $this->assertSame(0, $code); - } - - /** @test */ - public function it_generates_a_plain_module_with_no_components() - { - $code = $this->artisan('module:make', ['name' => 'ModuleName', '--plain' => true]); - - foreach ($this->moduleComponentPaths as $componentPath) { - $path = $this->modulePath . '/' . $componentPath; - $this->assertFileDoesNotExist($path); - } - $this->assertDirectoryDoesNotExist($this->modulePath . '/Data/Migrations'); - - $this->assertSame(0, $code); - } - - /** @test */ - public function it_outputs_error_when_module_exists() - { - $this->artisan('module:make', ['name' => 'Article']); - $code = $this->artisan('module:make', ['name' => 'Article']); - - $expected = 'Module [Article] already exist! -'; - $this->assertEquals($expected, Artisan::output()); - $this->assertSame(E_ERROR, $code); - } - - /** @test */ - public function it_still_generates_module_if_it_exists_using_force_flag() - { - $this->artisan('module:make', ['name' => 'Article']); - $code = $this->artisan('module:make', ['name' => 'Article', '--force' => true]); - - $output = Artisan::output(); - - $notExpected = 'Module [Article] already exist! -'; - $this->assertNotEquals($notExpected, $output); - $this->assertTrue(Str::contains($output, 'Module [Article] created successfully.')); - $this->assertSame(0, $code); - } - - /** @test */ - public function it_can_ignore_component_folders_to_generate() - { - $this->app['config']->set('modules.generator.components.seeder', ['path' => 'Data/Seeders', 'generate' => false]); - $this->app['config']->set('modules.generator.components.provider', ['path' => 'Providers', 'generate' => false]); - $this->app['config']->set('modules.generator.components.api-controller', ['path' => 'UI/API/Controllers', 'generate' => false]); - - $code = $this->artisan('module:make', ['name' => 'Article']); - - $this->assertDirectoryDoesNotExist($this->modulePath . '/Data/Seeders'); - $this->assertDirectoryDoesNotExist($this->modulePath . '/Providers'); - $this->assertDirectoryDoesNotExist($this->modulePath . '/UI/API/Controllers'); - $this->assertSame(0, $code); - } - - /** @test */ - public function it_generates_enabled_module() - { - $code = $this->artisan('module:make', ['name' => 'Article']); - - $this->assertTrue($this->repository->isEnabled('Article')); - $this->assertSame(0, $code); - } - - /** @test */ - public function it_generates_disabled_module_with_disabled_flag() - { - $code = $this->artisan('module:make', ['name' => 'Article', '--disabled' => true]); - - $this->assertTrue($this->repository->isDisabled('Article')); - $this->assertSame(0, $code); - } - - /** @test */ - public function it_generes_module_with_new_provider_location() - { - $this->app['config']->set('modules.generator.components.provider', ['path' => 'Base/Providers', 'generate' => true]); - - $code = $this->artisan('module:make', ['name' => 'Article']); - - $this->assertDirectoryExists($this->modulePath . '/Base/Providers'); - - $file = $this->finder->get($this->modulePath . '/module.json'); - $this->assertMatchesSnapshot($file); - - $file = $this->finder->get($this->modulePath . '/composer.json'); - $this->assertMatchesSnapshot($file); - - $this->assertSame(0, $code); - } - - /** @test */ - public function it_can_set_custom_model() - { - $code = $this->artisan('module:make', ['name' => 'Blog', '--entity' => 'PostComment']); - - $modulePath = base_path('app/Modules/Blog'); - $moduleComponentPaths = [ - 'Actions/CreatePostCommentAction.php', - 'Actions/UpdatePostCommentAction.php', - 'Actions/DeletePostCommentAction.php', - 'Actions/ViewPostCommentAction.php', - 'Actions/ListPostCommentsAction.php', - 'Data/Factories/PostCommentFactory.php', - 'Data/Seeders/PostCommentPermissionsSeeder_1.php', - 'DTO/CreatePostCommentDTO.php', - 'Models/PostComment.php', - 'Policies/PostCommentPolicy.php', - 'Providers/BlogServiceProvider.php', - 'Providers/RouteServiceProvider.php', - 'UI/API/QueryWizards/PostCommentQueryWizard.php', - 'UI/API/QueryWizards/PostCommentsQueryWizard.php', - 'UI/API/Resources/PostCommentResource.php', - 'UI/API/Requests/CreatePostCommentRequest.php', - 'UI/API/Requests/UpdatePostCommentRequest.php', - 'UI/API/Requests/DeletePostCommentRequest.php', - 'UI/API/Requests/ViewPostCommentRequest.php', - 'UI/API/Requests/ListPostCommentsRequest.php', - 'UI/API/Routes/v1/create_post_comment.php', - 'UI/API/Routes/v1/update_post_comment.php', - 'UI/API/Routes/v1/delete_post_comment.php', - 'UI/API/Routes/v1/view_post_comment.php', - 'UI/API/Routes/v1/list_post_comments.php', - 'UI/API/Tests/CreatePostCommentTest.php', - 'UI/API/Tests/UpdatePostCommentTest.php', - 'UI/API/Tests/DeletePostCommentTest.php', - 'UI/API/Tests/ViewPostCommentTest.php', - 'UI/API/Tests/ListPostCommentsTest.php', - ]; - - $moduleJsonPath = $modulePath . '/module.json'; - $this->assertFileExists($moduleJsonPath); - $this->assertMatchesSnapshot($this->finder->get($moduleJsonPath)); - - $composerJsonPath = $modulePath . '/composer.json'; - $this->assertFileExists($composerJsonPath); - $this->assertMatchesSnapshot($this->finder->get($composerJsonPath)); - - foreach ($moduleComponentPaths as $componentPath) { - $path = $modulePath . '/' . $componentPath; - $this->assertFileExists($path); - $this->assertMatchesSnapshot($this->finder->get($path)); - } - - $migrationFiles = $this->finder->allFiles($modulePath . '/Data/Migrations'); - $this->assertCount(1, $migrationFiles); - $this->assertMatchesSnapshot($migrationFiles[0]->getContents()); - - $this->assertSame(0, $code); - } -} +use Illuminate\Support\Carbon; +use Laraneat\Modules\Support\Composer; + +use function Spatie\Snapshots\assertMatchesFileSnapshot; + +beforeEach(function () { + $this->backupComposerJson(); + $this->setModules([ + __DIR__ . '/../../fixtures/stubs/modules/valid/article-category', + __DIR__ . '/../../fixtures/stubs/modules/valid/article', + __DIR__ . '/../../fixtures/stubs/modules/valid/author', + __DIR__ . '/../../fixtures/stubs/modules/valid/empty-module', + __DIR__ . '/../../fixtures/stubs/modules/valid/empty', + __DIR__ . '/../../fixtures/stubs/modules/valid/navigation', + ]); + + $this->travelTo(Carbon::parse('2024-01-01')); +}); + +it('generates a "plain" module', function () { + $this->instance(Composer::class, $this->mockComposer(['updatePackages' => true])); + $this->artisan('module:make', [ + 'name' => 'demo/article-comment', + '--preset' => 'plain', + '--entity' => 'ArticleComment', + ]) + ->assertSuccessful(); + + assertMatchesFileSnapshot($this->app->basePath('/composer.json')); + assertsMatchesDirectorySnapshot($this->app->basePath('/modules/article-comment')); +}); + +it('generates a "base" module', function () { + $this->instance(Composer::class, $this->mockComposer(['updatePackages' => true])); + $this->artisan('module:make', [ + 'name' => 'demo/article-comment', + '--preset' => 'base', + '--entity' => 'ArticleComment', + ]) + ->expectsQuestion( + 'Enter the class name of the "Create permission action"', + 'Modules\\Authorization\\Actions\\CreatePermissionAction' + ) + ->expectsQuestion( + 'Enter the class name of the "Create permission DTO"', + 'Modules\\Authorization\\DTO\\CreatePermissionDTO' + ) + ->expectsQuestion( + 'Enter the class name of the "User model"', + 'Modules\\User\\Models\\User' + ) + ->assertSuccessful(); + + assertMatchesFileSnapshot($this->app->basePath('/composer.json')); + assertsMatchesDirectorySnapshot($this->app->basePath('/modules/article-comment')); +}); + +it('generates a "api" module', function () { + $this->instance(Composer::class, $this->mockComposer(['updatePackages' => true])); + $this->artisan('module:make', [ + 'name' => 'demo/article-comment', + '--preset' => 'api', + '--entity' => 'ArticleComment', + ]) + ->expectsQuestion( + 'Enter the class name of the "Create permission action"', + 'Modules\\Authorization\\Actions\\CreatePermissionAction' + ) + ->expectsQuestion( + 'Enter the class name of the "Create permission DTO"', + 'Modules\\Authorization\\DTO\\CreatePermissionDTO' + ) + ->expectsQuestion( + 'Enter the class name of the "User model"', + 'Modules\\User\\Models\\User' + ) + ->assertSuccessful(); + + assertMatchesFileSnapshot($this->app->basePath('/composer.json')); + assertsMatchesDirectorySnapshot($this->app->basePath('/modules/article-comment')); +}); + +it('displays an error message if the passed module name is not valid', function () { + $this->artisan('module:make', [ + 'name' => '1foo', + '--preset' => 'plain', + '--entity' => 'Foo', + ]) + ->expectsOutputToContain("The module name passed is not valid!") + ->assertFailed(); + + assertMatchesFileSnapshot($this->app->basePath('/composer.json')); +}); + +it('displays an error message when a module with the same package name already exists', function () { + $this->artisan('module:make', [ + 'name' => 'laraneat/article', + '--preset' => 'plain', + '--entity' => 'Article', + ]) + ->expectsOutputToContain("Module 'laraneat/article' already exist!") + ->assertFailed(); + + assertMatchesFileSnapshot($this->app->basePath('/composer.json')); +}); + +it('displays an error message when a module with the same folder name already exists', function () { + $this->artisan('module:make', [ + 'name' => 'demo/article', + '--preset' => 'plain', + '--entity' => 'Article', + ]) + ->expectsOutputToContain("File already exists") + ->assertFailed(); + + assertMatchesFileSnapshot($this->app->basePath('/composer.json')); +}); diff --git a/tests/Commands/Generators/NotificationMakeCommandTest.php b/tests/Commands/Generators/NotificationMakeCommandTest.php index ea62571..b659a9c 100644 --- a/tests/Commands/Generators/NotificationMakeCommandTest.php +++ b/tests/Commands/Generators/NotificationMakeCommandTest.php @@ -1,124 +1,36 @@ modulePath = base_path('app/Modules/Article'); - $this->finder = $this->app['files']; - $this->artisan('module:make', ['name' => 'Article', '--plain' => true]); - } - - protected function tearDown(): void - { - $this->app[RepositoryInterface::class]->delete('Article'); - parent::tearDown(); - } - - /** @test */ - public function it_generates_plain_notification_file() - { - $code = $this->artisan('module:make:notification', [ - 'name' => 'MyAwesomePlainNotification', - 'module' => 'Article', - '--stub' => 'plain' - ]); - - $this->assertTrue(is_file($this->modulePath . '/Notifications/MyAwesomePlainNotification.php')); - $this->assertSame(0, $code); - } - - /** @test */ - public function it_generates_queued_notification_file() - { - $code = $this->artisan('module:make:notification', [ - 'name' => 'MyAwesomeQueuedNotification', - 'module' => 'Article', - '--stub' => 'queued' - ]); - - $this->assertTrue(is_file($this->modulePath . '/Notifications/MyAwesomeQueuedNotification.php')); - $this->assertSame(0, $code); - } - - /** @test */ - public function it_generated_correct_plain_notification_file_with_content() - { - $code = $this->artisan('module:make:notification', [ - 'name' => 'Foo/Bar\\MyAwesomePlainNotification', - 'module' => 'Article', - '--stub' => 'plain' - ]); - - $file = $this->finder->get($this->modulePath . '/Notifications/Foo/Bar/MyAwesomePlainNotification.php'); - - $this->assertMatchesSnapshot($file); - $this->assertSame(0, $code); - } - - /** @test */ - public function it_generated_correct_queued_notification_file_with_content() - { - $code = $this->artisan('module:make:notification', [ - 'name' => 'Foo/Bar\\MyAwesomeQueuedNotification', - 'module' => 'Article', - '--stub' => 'queued' - ]); - - $file = $this->finder->get($this->modulePath . '/Notifications/Foo/Bar/MyAwesomeQueuedNotification.php'); - - $this->assertMatchesSnapshot($file); - $this->assertSame(0, $code); - } - - /** @test */ - public function it_can_change_the_default_path_for_plain_notification_file() - { - $this->app['config']->set('modules.generator.components.notification.path', 'Foo/Bar\\Notifications'); - - $code = $this->artisan('module:make:notification', [ - 'name' => 'Baz\\Bat/MyAwesomePlainNotification', - 'module' => 'Article', - '--stub' => 'plain' - ]); - - $file = $this->finder->get($this->modulePath . '/Foo/Bar/Notifications/Baz/Bat/MyAwesomePlainNotification.php'); - - $this->assertMatchesSnapshot($file); - $this->assertSame(0, $code); - } - - /** @test */ - public function it_can_change_the_default_namespace_for_plain_notification_file() - { - $this->app['config']->set('modules.generator.components.notification.namespace', 'Foo/Bar\\Notifications/'); - - $code = $this->artisan('module:make:notification', [ - 'name' => 'Baz\\Bat/MyAwesomePlainNotification', - 'module' => 'Article', - '--stub' => 'plain' - ]); - - $file = $this->finder->get($this->modulePath . '/Notifications/Baz/Bat/MyAwesomePlainNotification.php'); - - $this->assertMatchesSnapshot($file); - $this->assertSame(0, $code); - } -} +use function PHPUnit\Framework\assertFileExists; +use function Spatie\Snapshots\assertMatchesFileSnapshot; + +beforeEach(function () { + $this->setModules([ + __DIR__ . '/../../fixtures/stubs/modules/valid/author', + ], $this->app->basePath('/modules')); +}); + +it('generates "plain" notification for the module', function () { + $this->artisan('module:make:notification', [ + 'name' => 'PlainAuthorNotification', + 'module' => 'Author', + '--stub' => 'plain', + ]) + ->assertSuccessful(); + + $filePath = $this->app->basePath('/modules/author/src/Notifications/PlainAuthorNotification.php'); + assertFileExists($filePath); + assertMatchesFileSnapshot($filePath); +}); + +it('generates "queued" notification for the module', function () { + $this->artisan('module:make:notification', [ + 'name' => 'QueuedAuthorNotification', + 'module' => 'Author', + '--stub' => 'queued', + ]) + ->assertSuccessful(); + + $filePath = $this->app->basePath('/modules/author/src/Notifications/QueuedAuthorNotification.php'); + assertFileExists($filePath); + assertMatchesFileSnapshot($filePath); +}); diff --git a/tests/Commands/Generators/ObserverMakeCommandTest.php b/tests/Commands/Generators/ObserverMakeCommandTest.php index 3dac581..77c64d8 100644 --- a/tests/Commands/Generators/ObserverMakeCommandTest.php +++ b/tests/Commands/Generators/ObserverMakeCommandTest.php @@ -1,96 +1,23 @@ modulePath = base_path('app/Modules/Article'); - $this->finder = $this->app['files']; - $this->artisan('module:make', ['name' => 'Article', '--plain' => true]); - } - - protected function tearDown(): void - { - $this->app[RepositoryInterface::class]->delete('Article'); - parent::tearDown(); - } - - /** @test */ - public function it_generates_observer_file() - { - $code = $this->artisan('module:make:observer', [ - 'name' => 'MyAwesomeObserver', - 'module' => 'Article', - '--model' => 'Some\\Nested/Model' - ]); - - $this->assertTrue(is_file($this->modulePath . '/Observers/MyAwesomeObserver.php')); - $this->assertSame(0, $code); - } - - /** @test */ - public function it_generated_correct_observer_file_with_content() - { - $code = $this->artisan('module:make:observer', [ - 'name' => 'Foo/Bar\\MyAwesomeObserver', - 'module' => 'Article', - '--model' => 'Some\\Nested/Model' - ]); - - $file = $this->finder->get($this->modulePath . '/Observers/Foo/Bar/MyAwesomeObserver.php'); - - $this->assertMatchesSnapshot($file); - $this->assertSame(0, $code); - } - - /** @test */ - public function it_can_change_the_default_path_for_observer_file() - { - $this->app['config']->set('modules.generator.components.observer.path', 'Foo/Bar\\Observers'); - - $code = $this->artisan('module:make:observer', [ - 'name' => 'Baz\\Bat/MyAwesomeObserver', - 'module' => 'Article', - '--model' => 'Some\\Nested/Model' - ]); - - $file = $this->finder->get($this->modulePath . '/Foo/Bar/Observers/Baz/Bat/MyAwesomeObserver.php'); - - $this->assertMatchesSnapshot($file); - $this->assertSame(0, $code); - } - - /** @test */ - public function it_can_change_the_default_namespace_for_observer_file() - { - $this->app['config']->set('modules.generator.components.observer.namespace', 'Foo/Bar\\Observers/'); - - $code = $this->artisan('module:make:observer', [ - 'name' => 'Baz\\Bat/MyAwesomeObserver', - 'module' => 'Article', - '--model' => 'Some\\Nested/Model' - ]); - - $file = $this->finder->get($this->modulePath . '/Observers/Baz/Bat/MyAwesomeObserver.php'); - - $this->assertMatchesSnapshot($file); - $this->assertSame(0, $code); - } -} +use function PHPUnit\Framework\assertFileExists; +use function Spatie\Snapshots\assertMatchesFileSnapshot; + +beforeEach(function () { + $this->setModules([ + __DIR__ . '/../../fixtures/stubs/modules/valid/author', + ], $this->app->basePath('/modules')); +}); + +it('generates observer for the module', function () { + $this->artisan('module:make:observer', [ + 'name' => 'SomeAuthorObserver', + 'module' => 'Author', + '--model' => 'Author', + ]) + ->assertSuccessful(); + + $filePath = $this->app->basePath('/modules/author/src/Observers/SomeAuthorObserver.php'); + assertFileExists($filePath); + assertMatchesFileSnapshot($filePath); +}); diff --git a/tests/Commands/Generators/PolicyMakeCommandTest.php b/tests/Commands/Generators/PolicyMakeCommandTest.php index 4b24a50..bf926ae 100644 --- a/tests/Commands/Generators/PolicyMakeCommandTest.php +++ b/tests/Commands/Generators/PolicyMakeCommandTest.php @@ -1,126 +1,40 @@ modulePath = base_path('app/Modules/Article'); - $this->finder = $this->app['files']; - $this->artisan('module:make', ['name' => 'Article', '--plain' => true]); - } - - protected function tearDown(): void - { - $this->app[RepositoryInterface::class]->delete('Article'); - parent::tearDown(); - } - - /** @test */ - public function it_generates_plain_policy_file() - { - $code = $this->artisan('module:make:policy', [ - 'name' => 'MyAwesomePlainPolicy', - 'module' => 'Article', - '--stub' => 'plain' - ]); - - $this->assertTrue(is_file($this->modulePath . '/Policies/MyAwesomePlainPolicy.php')); - $this->assertSame(0, $code); - } - - /** @test */ - public function it_generates_full_policy_file() - { - $code = $this->artisan('module:make:policy', [ - 'name' => 'MyAwesomeFullPolicy', - 'module' => 'Article', - '--stub' => 'full', - '--model' => 'Bar\\Bat/Baz\\MyAwesomeModel' - ]); - - $this->assertTrue(is_file($this->modulePath . '/Policies/MyAwesomeFullPolicy.php')); - $this->assertSame(0, $code); - } - - /** @test */ - public function it_generated_correct_plain_policy_file_with_content() - { - $code = $this->artisan('module:make:policy', [ - 'name' => 'Foo/Bar\\MyAwesomePlainPolicy', - 'module' => 'Article', - '--stub' => 'plain' - ]); - - $file = $this->finder->get($this->modulePath . '/Policies/Foo/Bar/MyAwesomePlainPolicy.php'); - - $this->assertMatchesSnapshot($file); - $this->assertSame(0, $code); - } - - /** @test */ - public function it_generated_correct_full_policy_file_with_content() - { - $code = $this->artisan('module:make:policy', [ - 'name' => 'Foo/Bar\\MyAwesomeFullPolicy', - 'module' => 'Article', - '--stub' => 'full', - '--model' => 'Bar\\Bat/Baz\\MyAwesomeModel' - ]); - - $file = $this->finder->get($this->modulePath . '/Policies/Foo/Bar/MyAwesomeFullPolicy.php'); - - $this->assertMatchesSnapshot($file); - $this->assertSame(0, $code); - } - - /** @test */ - public function it_can_change_the_default_path_for_plain_policy_file() - { - $this->app['config']->set('modules.generator.components.policy.path', 'Foo/Bar\\Policies'); - - $code = $this->artisan('module:make:policy', [ - 'name' => 'Baz\\Bat/MyAwesomePlainPolicy', - 'module' => 'Article', - '--stub' => 'plain' - ]); - - $file = $this->finder->get($this->modulePath . '/Foo/Bar/Policies/Baz/Bat/MyAwesomePlainPolicy.php'); - - $this->assertMatchesSnapshot($file); - $this->assertSame(0, $code); - } - - /** @test */ - public function it_can_change_the_default_namespace_for_plain_policy_file() - { - $this->app['config']->set('modules.generator.components.policy.namespace', 'Foo/Bar\\Policies/'); - - $code = $this->artisan('module:make:policy', [ - 'name' => 'Baz\\Bat/MyAwesomePlainPolicy', - 'module' => 'Article', - '--stub' => 'plain' - ]); - - $file = $this->finder->get($this->modulePath . '/Policies/Baz/Bat/MyAwesomePlainPolicy.php'); - - $this->assertMatchesSnapshot($file); - $this->assertSame(0, $code); - } -} +use function PHPUnit\Framework\assertFileExists; +use function Spatie\Snapshots\assertMatchesFileSnapshot; + +beforeEach(function () { + $this->setModules([ + __DIR__ . '/../../fixtures/stubs/modules/valid/author', + ], $this->app->basePath('/modules')); +}); + +it('generates plain policy for the module', function () { + $this->artisan('module:make:policy', [ + 'name' => 'AuthorPolicy', + 'module' => 'Author', + ]) + ->expectsQuestion('Enter the class name of the model to be used in the policy (optional)', '') + ->assertSuccessful(); + + $filePath = $this->app->basePath('/modules/author/src/Policies/AuthorPolicy.php'); + assertFileExists($filePath); + assertMatchesFileSnapshot($filePath); +}); + +it('generates full policy for the module', function () { + $this->artisan('module:make:policy', [ + 'name' => 'AuthorPolicy', + 'module' => 'Author', + '--model' => 'Author', + ]) + ->expectsQuestion( + 'Enter the class name of the "User model"', + 'Modules\\User\\Models\\User' + ) + ->assertSuccessful(); + + $filePath = $this->app->basePath('/modules/author/src/Policies/AuthorPolicy.php'); + assertFileExists($filePath); + assertMatchesFileSnapshot($filePath); +}); diff --git a/tests/Commands/Generators/ProviderMakeCommandTest.php b/tests/Commands/Generators/ProviderMakeCommandTest.php index f616078..99a8c74 100644 --- a/tests/Commands/Generators/ProviderMakeCommandTest.php +++ b/tests/Commands/Generators/ProviderMakeCommandTest.php @@ -1,141 +1,77 @@ modulePath = base_path('app/Modules/Article'); - $this->finder = $this->app['files']; - $this->artisan('module:make', ['name' => 'Article', '--plain' => true]); - } - - protected function tearDown(): void - { - $this->app[RepositoryInterface::class]->delete('Article'); - parent::tearDown(); - } - - /** @test */ - public function it_generates_provider_file(): void - { - $code = $this->artisan('module:make:provider', [ - 'name' => 'MyAwesomeProvider', - 'module' => 'Article', - '--stub' => 'plain' - ]); - - $this->assertTrue(is_file($this->modulePath . '/Providers/MyAwesomeProvider.php')); - $this->assertSame(0, $code); - } - - /** @test */ - public function it_generated_correct_provider_file_with_content(): void - { - $code = $this->artisan('module:make:provider', [ - 'name' => 'MyAwesomeProvider', - 'module' => 'Article', - '--stub' => 'plain' - ]); - - $file = $this->finder->get($this->modulePath . '/Providers/MyAwesomeProvider.php'); - - $this->assertMatchesSnapshot($file); - $this->assertSame(0, $code); - } - - /** @test */ - public function it_can_change_the_default_path(): void - { - $this->app['config']->set('modules.generator.components.provider.path', 'Foo/Bar\\NewProviders'); - - $code = $this->artisan('module:make:provider', [ - 'name' => 'Baz\\Bat/MyAwesomeProvider', - 'module' => 'Article', - '--stub' => 'plain' - ]); - - $file = $this->finder->get($this->modulePath . '/Foo/Bar/NewProviders/Baz/Bat/MyAwesomeProvider.php'); - - $this->assertMatchesSnapshot($file); - $this->assertSame(0, $code); - } - - /** @test */ - public function it_can_change_the_default_namespace(): void - { - $this->app['config']->set('modules.generator.components.provider.namespace', 'Foo/Bar\\NewProviders/'); - - $code = $this->artisan('module:make:provider', [ - 'name' => 'Baz\\Bat/MyAwesomeProvider', - 'module' => 'Article', - '--stub' => 'plain' - ]); - - $file = $this->finder->get($this->modulePath . '/Providers/Baz/Bat/MyAwesomeProvider.php'); - - $this->assertMatchesSnapshot($file); - $this->assertSame(0, $code); - } - - /** @test */ - public function it_can_generate_module_provider_file(): void - { - $code = $this->artisan('module:make:provider', [ - 'name' => 'Baz\\Bat/MyAwesomeModuleProvider', - 'module' => 'Article', - '--stub' => 'module', - ]); - - $file = $this->finder->get($this->modulePath . '/Providers/Baz/Bat/MyAwesomeModuleProvider.php'); - - $this->assertMatchesSnapshot($file); - $this->assertSame(0, $code); - } - - /** @test */ - public function it_can_generate_route_provider_file(): void - { - $code = $this->artisan('module:make:provider', [ - 'name' => 'Baz\\Bat/MyAwesomeRouteProvider', - 'module' => 'Article', - '--stub' => 'route', - ]); - - $file = $this->finder->get($this->modulePath . '/Providers/Baz/Bat/MyAwesomeRouteProvider.php'); - - $this->assertMatchesSnapshot($file); - $this->assertSame(0, $code); - } - - /** @test */ - public function it_can_generate_event_provider_file(): void - { - $code = $this->artisan('module:make:provider', [ - 'name' => 'Baz\\Bat/MyAwesomeEventProvider', - 'module' => 'Article', - '--stub' => 'event', - ]); - - $file = $this->finder->get($this->modulePath . '/Providers/Baz/Bat/MyAwesomeEventProvider.php'); - - $this->assertMatchesSnapshot($file); - $this->assertSame(0, $code); - } -} +use function PHPUnit\Framework\assertFileExists; +use function Spatie\Snapshots\assertMatchesFileSnapshot; + +beforeEach(function () { + $this->setModules([ + __DIR__ . '/../../fixtures/stubs/modules/valid/author', + ], $this->app->basePath('/modules')); +}); + +it('generates "plain" provider for the module', function () { + $this->artisan('module:make:provider', [ + 'name' => 'TestServiceProvider', + 'module' => 'Author', + '--stub' => 'plain', + ]) + ->assertSuccessful(); + + $filePath = $this->app->basePath('/modules/author/src/Providers/TestServiceProvider.php'); + assertFileExists($filePath); + assertMatchesFileSnapshot($filePath); +}); + +it('generates "module" provider for the module', function () { + $this->artisan('module:make:provider', [ + 'name' => 'CustomModuleServiceProvider', + 'module' => 'Author', + '--stub' => 'module', + ]) + ->assertSuccessful(); + + $filePath = $this->app->basePath('/modules/author/src/Providers/CustomModuleServiceProvider.php'); + assertFileExists($filePath); + assertMatchesFileSnapshot($filePath); +}); + +it('generates "event" provider for the module', function () { + $this->artisan('module:make:provider', [ + 'name' => 'EventServiceProvider', + 'module' => 'Author', + '--stub' => 'event', + ]) + ->assertSuccessful(); + + $filePath = $this->app->basePath('/modules/author/src/Providers/EventServiceProvider.php'); + assertFileExists($filePath); + assertMatchesFileSnapshot($filePath); +}); + +it('generates "route" provider for the module', function () { + $this->artisan('module:make:provider', [ + 'name' => 'CustomRouteServiceProvider', + 'module' => 'Author', + '--stub' => 'route', + ]) + ->assertSuccessful(); + + $filePath = $this->app->basePath('/modules/author/src/Providers/CustomRouteServiceProvider.php'); + assertFileExists($filePath); + assertMatchesFileSnapshot($filePath); +}); + +it('registers provider in module composer.json', function () { + $this->artisan('module:make:provider', [ + 'name' => 'CustomServiceProvider', + 'module' => 'Author', + '--stub' => 'plain', + ]) + ->assertSuccessful(); + + $composerJsonPath = $this->app->basePath('/modules/author/composer.json'); + $composerJson = json_decode(file_get_contents($composerJsonPath), true); + + expect($composerJson['extra']['laravel']['providers']) + ->toContain('Modules\\Author\\Providers\\CustomServiceProvider'); +}); diff --git a/tests/Commands/Generators/QueryWizardMakeCommandTest.php b/tests/Commands/Generators/QueryWizardMakeCommandTest.php index a7e0b4b..f2852f0 100644 --- a/tests/Commands/Generators/QueryWizardMakeCommandTest.php +++ b/tests/Commands/Generators/QueryWizardMakeCommandTest.php @@ -1,141 +1,36 @@ modulePath = base_path('app/Modules/Article'); - $this->finder = $this->app['files']; - $this->artisan('module:make', ['name' => 'Article', '--plain' => true]); - } - - protected function tearDown(): void - { - $this->app[RepositoryInterface::class]->delete('Article'); - parent::tearDown(); - } - - /** @test */ - public function it_generates_query_wizard_file() - { - $code = $this->artisan('module:make:wizard', [ - 'name' => 'MyAwesomeQueryWizard', - 'module' => 'Article', - '-n' => true - ]); - - $this->assertTrue(is_file($this->modulePath . '/UI/API/QueryWizards/MyAwesomeQueryWizard.php')); - $this->assertSame(0, $code); - } - - /** @test */ - public function it_generated_correct_eloquent_query_wizard_file_with_content() - { - $code = $this->artisan('module:make:wizard', [ - 'name' => 'Foo/Bar\\MyAwesomeEloquentQueryWizard', - 'module' => 'Article', - '--stub' => 'eloquent' - ]); - - $file = $this->finder->get($this->modulePath . '/UI/API/QueryWizards/Foo/Bar/MyAwesomeEloquentQueryWizard.php'); - - $this->assertMatchesSnapshot($file); - $this->assertSame(0, $code); - } - - /** @test */ - public function it_generated_correct_scout_query_wizard_file_with_content() - { - $code = $this->artisan('module:make:wizard', [ - 'name' => 'Foo/Bar\\MyAwesomeScoutQueryWizard', - 'module' => 'Article', - '--stub' => 'scout' - ]); - - $file = $this->finder->get($this->modulePath . '/UI/API/QueryWizards/Foo/Bar/MyAwesomeScoutQueryWizard.php'); - - $this->assertMatchesSnapshot($file); - $this->assertSame(0, $code); - } - - /** @test */ - public function it_generated_correct_model_query_wizard_file_with_content() - { - $code = $this->artisan('module:make:wizard', [ - 'name' => 'Foo/Bar\\MyAwesomeModelQueryWizard', - 'module' => 'Article', - '--stub' => 'model' - ]); - - $file = $this->finder->get($this->modulePath . '/UI/API/QueryWizards/Foo/Bar/MyAwesomeModelQueryWizard.php'); - - $this->assertMatchesSnapshot($file); - $this->assertSame(0, $code); - } - - /** @test */ - public function it_generated_correct_elastic_query_wizard_file_with_content() - { - $code = $this->artisan('module:make:wizard', [ - 'name' => 'Foo/Bar\\MyAwesomeElasticQueryWizard', - 'module' => 'Article', - '--stub' => 'elastic' - ]); - - $file = $this->finder->get($this->modulePath . '/UI/API/QueryWizards/Foo/Bar/MyAwesomeElasticQueryWizard.php'); - - $this->assertMatchesSnapshot($file); - $this->assertSame(0, $code); - } - - /** @test */ - public function it_can_change_the_default_path_for_query_wizard_file() - { - $this->app['config']->set('modules.generator.components.api-query-wizard.path', 'Foo/Bar\\QueryWizards'); - - $code = $this->artisan('module:make:wizard', [ - 'name' => 'Baz\\Bat/MyAwesomeQueryWizard', - 'module' => 'Article', - '-n' => true - ]); - - $file = $this->finder->get($this->modulePath . '/Foo/Bar/QueryWizards/Baz/Bat/MyAwesomeQueryWizard.php'); - - $this->assertMatchesSnapshot($file); - $this->assertSame(0, $code); - } - - /** @test */ - public function it_can_change_the_default_namespace_for_query_wizard_file() - { - $this->app['config']->set('modules.generator.components.api-query-wizard.namespace', 'Foo/Bar\\QueryWizards/'); - - $code = $this->artisan('module:make:wizard', [ - 'name' => 'Baz\\Bat/MyAwesomeQueryWizard', - 'module' => 'Article', - '-n' => true - ]); - - $file = $this->finder->get($this->modulePath . '/UI/API/QueryWizards/Baz/Bat/MyAwesomeQueryWizard.php'); - - $this->assertMatchesSnapshot($file); - $this->assertSame(0, $code); - } -} +use function PHPUnit\Framework\assertFileExists; +use function Spatie\Snapshots\assertMatchesFileSnapshot; + +beforeEach(function () { + $this->setModules([ + __DIR__ . '/../../fixtures/stubs/modules/valid/author', + ], $this->app->basePath('/modules')); +}); + +it('generates "eloquent" query-wizard for the module', function () { + $this->artisan('module:make:query-wizard', [ + 'name' => 'AuthorsQueryWizard', + 'module' => 'Author', + '--stub' => 'eloquent', + ]) + ->assertSuccessful(); + + $filePath = $this->app->basePath('/modules/author/src/UI/API/QueryWizards/AuthorsQueryWizard.php'); + assertFileExists($filePath); + assertMatchesFileSnapshot($filePath); +}); + +it('generates "model" query-wizard for the module', function () { + $this->artisan('module:make:query-wizard', [ + 'name' => 'AuthorQueryWizard', + 'module' => 'Author', + '--stub' => 'model', + ]) + ->assertSuccessful(); + + $filePath = $this->app->basePath('/modules/author/src/UI/API/QueryWizards/AuthorQueryWizard.php'); + assertFileExists($filePath); + assertMatchesFileSnapshot($filePath); +}); diff --git a/tests/Commands/Generators/RequestMakeCommandTest.php b/tests/Commands/Generators/RequestMakeCommandTest.php index 218d82e..aed4fdf 100644 --- a/tests/Commands/Generators/RequestMakeCommandTest.php +++ b/tests/Commands/Generators/RequestMakeCommandTest.php @@ -1,363 +1,162 @@ modulePath = base_path('app/Modules/Article'); - $this->finder = $this->app['files']; - $this->artisan('module:make', ['name' => 'Article', '--plain' => true]); - } - - protected function tearDown(): void - { - $this->app[RepositoryInterface::class]->delete('Article'); - parent::tearDown(); - } - - /** @test */ - public function it_generates_api_request_file() - { - $code = $this->artisan('module:make:request', [ - 'name' => 'MyAwesomeApiRequest', - 'module' => 'Article', - '--ui' => 'api', - '--stub' => 'plain' - ]); - - $this->assertTrue(is_file($this->modulePath . '/UI/API/Requests/MyAwesomeApiRequest.php')); - $this->assertSame(0, $code); - } - - /** @test */ - public function it_generated_correct_api_request_file_with_content() - { - $code = $this->artisan('module:make:request', [ - 'name' => 'MyAwesomeApiRequest', - 'module' => 'Article', - '--ui' => 'api', - '--stub' => 'plain' - ]); - - $file = $this->finder->get($this->modulePath . '/UI/API/Requests/MyAwesomeApiRequest.php'); - - $this->assertMatchesSnapshot($file); - $this->assertSame(0, $code); - } - - /** @test */ - public function it_can_change_the_default_path_for_api_request() - { - $this->app['config']->set('modules.generator.components.api-request.path', 'Foo/Bar\\NewRequests'); - - $code = $this->artisan('module:make:request', [ - 'name' => 'Baz\\Bat/MyAwesomeApiRequest', - 'module' => 'Article', - '--ui' => 'api', - '--stub' => 'plain' - ]); - - $file = $this->finder->get($this->modulePath . '/Foo/Bar/NewRequests/Baz/Bat/MyAwesomeApiRequest.php'); - - $this->assertMatchesSnapshot($file); - $this->assertSame(0, $code); - } - - /** @test */ - public function it_can_change_the_default_namespace_for_api_request() - { - $this->app['config']->set('modules.generator.components.api-request.namespace', 'Foo/Bar\\NewRequests/'); - - $code = $this->artisan('module:make:request', [ - 'name' => 'Baz\\Bat/MyAwesomeApiRequest', - 'module' => 'Article', - '--ui' => 'api', - '--stub' => 'plain' - ]); - - $file = $this->finder->get($this->modulePath . '/UI/API/Requests/Baz/Bat/MyAwesomeApiRequest.php'); - - $this->assertMatchesSnapshot($file); - $this->assertSame(0, $code); - } - - /** @test */ - public function it_can_generate_api_create_request_file() - { - $code = $this->artisan('module:make:request', [ - 'name' => 'Baz\\Bat/MyAwesomeApiCreateRequest', - 'module' => 'Article', - '--ui' => 'api', - '--stub' => 'create', - '--model' => 'Some/Nested\\Model', - '--dto' => 'Foo/Bar\\TestDTO', - ]); - - $file = $this->finder->get($this->modulePath . '/UI/API/Requests/Baz/Bat/MyAwesomeApiCreateRequest.php'); - - $this->assertMatchesSnapshot($file); - $this->assertSame(0, $code); - } - - /** @test */ - public function it_throws_exception_when_classes_not_provided_for_api_create_request_file() - { - $this->expectException(InvalidOptionException::class); - - $this->artisan('module:make:request', [ - 'name' => 'Baz\\Bat/MyAwesomeApiCreateRequest', - 'module' => 'Article', - '--ui' => 'api', - '--stub' => 'create', - '-n' => '', - ]); - } - - /** @test */ - public function it_can_generate_api_delete_request_file() - { - $code = $this->artisan('module:make:request', [ - 'name' => 'Baz\\Bat/MyAwesomeApiDeleteRequest', - 'module' => 'Article', - '--ui' => 'api', - '--stub' => 'delete', - '--model' => 'Some/Nested\\Model', - ]); - - $file = $this->finder->get($this->modulePath . '/UI/API/Requests/Baz/Bat/MyAwesomeApiDeleteRequest.php'); - - $this->assertMatchesSnapshot($file); - $this->assertSame(0, $code); - } - - /** @test */ - public function it_can_generate_api_list_request_file() - { - $code = $this->artisan('module:make:request', [ - 'name' => 'Baz\\Bat/MyAwesomeApiListRequest', - 'module' => 'Article', - '--ui' => 'api', - '--stub' => 'list', - '--model' => 'Some/Nested\\Model', - ]); - - $file = $this->finder->get($this->modulePath . '/UI/API/Requests/Baz/Bat/MyAwesomeApiListRequest.php'); - - $this->assertMatchesSnapshot($file); - $this->assertSame(0, $code); - } - - /** @test */ - public function it_can_generate_api_update_request_file() - { - $code = $this->artisan('module:make:request', [ - 'name' => 'Baz\\Bat/MyAwesomeApiUpdateRequest', - 'module' => 'Article', - '--ui' => 'api', - '--stub' => 'update', - '--model' => 'Some/Nested\\Model', - '--dto' => 'Foo/Bar\\TestDTO', - ]); - - $file = $this->finder->get($this->modulePath . '/UI/API/Requests/Baz/Bat/MyAwesomeApiUpdateRequest.php'); - - $this->assertMatchesSnapshot($file); - $this->assertSame(0, $code); - } - - /** @test */ - public function it_throws_exception_when_classes_not_provided_for_api_update_request_file() - { - $this->expectException(InvalidOptionException::class); - - $this->artisan('module:make:request', [ - 'name' => 'Baz\\Bat/MyAwesomeApiUpdateRequest', - 'module' => 'Article', - '--ui' => 'api', - '--stub' => 'update', - '-n' => '', - ]); - } - - /** @test */ - public function it_can_generate_api_view_request_file() - { - $code = $this->artisan('module:make:request', [ - 'name' => 'Baz\\Bat/MyAwesomeApiViewRequest', - 'module' => 'Article', - '--ui' => 'api', - '--stub' => 'view', - '--model' => 'Some/Nested\\Model', - ]); - - $file = $this->finder->get($this->modulePath . '/UI/API/Requests/Baz/Bat/MyAwesomeApiViewRequest.php'); - - $this->assertMatchesSnapshot($file); - $this->assertSame(0, $code); - } - - /** @test */ - public function it_generates_web_request_file() - { - $code = $this->artisan('module:make:request', [ - 'name' => 'MyAwesomeWebRequest', - 'module' => 'Article', - '--ui' => 'web', - '--stub' => 'plain' - ]); - - $this->assertTrue(is_file($this->modulePath . '/UI/WEB/Requests/MyAwesomeWebRequest.php')); - $this->assertSame(0, $code); - } - - /** @test */ - public function it_generated_correct_web_request_file_with_content() - { - $code = $this->artisan('module:make:request', [ - 'name' => 'MyAwesomeWebRequest', - 'module' => 'Article', - '--ui' => 'web', - '--stub' => 'plain' - ]); - - $file = $this->finder->get($this->modulePath . '/UI/WEB/Requests/MyAwesomeWebRequest.php'); - - $this->assertMatchesSnapshot($file); - $this->assertSame(0, $code); - } - - /** @test */ - public function it_can_change_the_default_path_for_web_request() - { - $this->app['config']->set('modules.generator.components.web-request.path', 'Foo/Bar\\NewRequests'); - - $code = $this->artisan('module:make:request', [ - 'name' => 'Baz\\Bat/MyAwesomeWebRequest', - 'module' => 'Article', - '--ui' => 'web', - '--stub' => 'plain' - ]); - - $file = $this->finder->get($this->modulePath . '/Foo/Bar/NewRequests/Baz/Bat/MyAwesomeWebRequest.php'); - - $this->assertMatchesSnapshot($file); - $this->assertSame(0, $code); - } - - /** @test */ - public function it_can_change_the_default_namespace_for_web_request() - { - $this->app['config']->set('modules.generator.components.web-request.namespace', 'Foo/Bar\\NewRequests/'); - - $code = $this->artisan('module:make:request', [ - 'name' => 'Baz\\Bat/MyAwesomeWebRequest', - 'module' => 'Article', - '--ui' => 'web', - '--stub' => 'plain' - ]); - - $file = $this->finder->get($this->modulePath . '/UI/WEB/Requests/Baz/Bat/MyAwesomeWebRequest.php'); - - $this->assertMatchesSnapshot($file); - $this->assertSame(0, $code); - } - - /** @test */ - public function it_can_generate_web_create_request_file() - { - $code = $this->artisan('module:make:request', [ - 'name' => 'Baz\\Bat/MyAwesomeWebCreateRequest', - 'module' => 'Article', - '--ui' => 'web', - '--stub' => 'create', - '--model' => 'Some/Nested\\Model', - '--dto' => 'Foo/Bar\\TestDTO', - ]); - - $file = $this->finder->get($this->modulePath . '/UI/WEB/Requests/Baz/Bat/MyAwesomeWebCreateRequest.php'); - - $this->assertMatchesSnapshot($file); - $this->assertSame(0, $code); - } - - /** @test */ - public function it_throws_exception_when_classes_not_provided_for_web_create_request_file() - { - $this->expectException(InvalidOptionException::class); - - $this->artisan('module:make:request', [ - 'name' => 'Baz\\Bat/MyAwesomeWebCreateRequest', - 'module' => 'Article', - '--ui' => 'web', - '--stub' => 'create', - '-n' => '', - ]); - } - - /** @test */ - public function it_can_generate_web_delete_request_file() - { - $code = $this->artisan('module:make:request', [ - 'name' => 'Baz\\Bat/MyAwesomeWebDeleteRequest', - 'module' => 'Article', - '--ui' => 'web', - '--stub' => 'delete', - '--model' => 'Some/Nested\\Model', - ]); - - $file = $this->finder->get($this->modulePath . '/UI/WEB/Requests/Baz/Bat/MyAwesomeWebDeleteRequest.php'); - - $this->assertMatchesSnapshot($file); - $this->assertSame(0, $code); - } - - /** @test */ - public function it_can_generate_web_update_request_file() - { - $code = $this->artisan('module:make:request', [ - 'name' => 'Baz\\Bat/MyAwesomeWebUpdateRequest', - 'module' => 'Article', - '--ui' => 'web', - '--stub' => 'update', - '--model' => 'Some/Nested\\Model', - '--dto' => 'Foo/Bar\\TestDTO', - ]); - - $file = $this->finder->get($this->modulePath . '/UI/WEB/Requests/Baz/Bat/MyAwesomeWebUpdateRequest.php'); - - $this->assertMatchesSnapshot($file); - $this->assertSame(0, $code); - } - - /** @test */ - public function it_throws_exception_when_classes_not_provided_for_web_update_request_file() - { - $this->expectException(InvalidOptionException::class); - - $this->artisan('module:make:request', [ - 'name' => 'Baz\\Bat/MyAwesomeWebUpdateRequest', - 'module' => 'Article', - '--ui' => 'web', - '--stub' => 'update', - '-n' => '', - ]); - } -} +use function PHPUnit\Framework\assertFileExists; +use function Spatie\Snapshots\assertMatchesFileSnapshot; + +beforeEach(function () { + $this->setModules([ + __DIR__ . '/../../fixtures/stubs/modules/valid/author', + ], $this->app->basePath('/modules')); +}); + +it('generates "plain" web request for the module', function () { + $this->artisan('module:make:request', [ + 'name' => 'PlainAuthorRequest', + 'module' => 'Author', + '--stub' => 'plain', + '--ui' => 'web', + ]) + ->assertSuccessful(); + + $filePath = $this->app->basePath('/modules/author/src/UI/WEB/Requests/PlainAuthorRequest.php'); + assertFileExists($filePath); + assertMatchesFileSnapshot($filePath); +}); + +it('generates "create" web request for the module', function () { + $this->artisan('module:make:request', [ + 'name' => 'CreateAuthorRequest', + 'module' => 'Author', + '--stub' => 'create', + '--ui' => 'web', + '--dto' => 'CreateAuthorDTO', + '--model' => 'Author', + ]) + ->assertSuccessful(); + + $filePath = $this->app->basePath('/modules/author/src/UI/WEB/Requests/CreateAuthorRequest.php'); + assertFileExists($filePath); + assertMatchesFileSnapshot($filePath); +}); + +it('generates "update" web request for the module', function () { + $this->artisan('module:make:request', [ + 'name' => 'UpdateAuthorRequest', + 'module' => 'Author', + '--stub' => 'update', + '--ui' => 'web', + '--dto' => 'UpdateAuthorDTO', + '--model' => 'Author', + ]) + ->assertSuccessful(); + + $filePath = $this->app->basePath('/modules/author/src/UI/WEB/Requests/UpdateAuthorRequest.php'); + assertFileExists($filePath); + assertMatchesFileSnapshot($filePath); +}); + +it('generates "delete" web request for the module', function () { + $this->artisan('module:make:request', [ + 'name' => 'DeleteAuthorRequest', + 'module' => 'Author', + '--stub' => 'delete', + '--ui' => 'web', + '--model' => 'Author', + ]) + ->assertSuccessful(); + + $filePath = $this->app->basePath('/modules/author/src/UI/WEB/Requests/DeleteAuthorRequest.php'); + assertFileExists($filePath); + assertMatchesFileSnapshot($filePath); +}); + +it('generates "plain" api request for the module', function () { + $this->artisan('module:make:request', [ + 'name' => 'PlainAuthorRequest', + 'module' => 'Author', + '--stub' => 'plain', + '--ui' => 'api', + ]) + ->assertSuccessful(); + + $filePath = $this->app->basePath('/modules/author/src/UI/API/Requests/PlainAuthorRequest.php'); + assertFileExists($filePath); + assertMatchesFileSnapshot($filePath); +}); + +it('generates "create" api request for the module', function () { + $this->artisan('module:make:request', [ + 'name' => 'CreateAuthorRequest', + 'module' => 'Author', + '--stub' => 'create', + '--ui' => 'api', + '--dto' => 'CreateAuthorDTO', + '--model' => 'Author', + ]) + ->assertSuccessful(); + + $filePath = $this->app->basePath('/modules/author/src/UI/API/Requests/CreateAuthorRequest.php'); + assertFileExists($filePath); + assertMatchesFileSnapshot($filePath); +}); + +it('generates "update" api request for the module', function () { + $this->artisan('module:make:request', [ + 'name' => 'UpdateAuthorRequest', + 'module' => 'Author', + '--stub' => 'update', + '--ui' => 'api', + '--dto' => 'UpdateAuthorDTO', + '--model' => 'Author', + ]) + ->assertSuccessful(); + + $filePath = $this->app->basePath('/modules/author/src/UI/API/Requests/UpdateAuthorRequest.php'); + assertFileExists($filePath); + assertMatchesFileSnapshot($filePath); +}); + +it('generates "delete" api request for the module', function () { + $this->artisan('module:make:request', [ + 'name' => 'DeleteAuthorRequest', + 'module' => 'Author', + '--stub' => 'delete', + '--ui' => 'api', + '--model' => 'Author', + ]) + ->assertSuccessful(); + + $filePath = $this->app->basePath('/modules/author/src/UI/API/Requests/DeleteAuthorRequest.php'); + assertFileExists($filePath); + assertMatchesFileSnapshot($filePath); +}); + +it('generates "view" api request for the module', function () { + $this->artisan('module:make:request', [ + 'name' => 'ViewAuthorRequest', + 'module' => 'Author', + '--stub' => 'view', + '--ui' => 'api', + '--model' => 'Author', + ]) + ->assertSuccessful(); + + $filePath = $this->app->basePath('/modules/author/src/UI/API/Requests/ViewAuthorRequest.php'); + assertFileExists($filePath); + assertMatchesFileSnapshot($filePath); +}); + +it('generates "list" api request for the module', function () { + $this->artisan('module:make:request', [ + 'name' => 'ListAuthorsRequest', + 'module' => 'Author', + '--stub' => 'list', + '--ui' => 'api', + '--model' => 'Author', + ]) + ->assertSuccessful(); + + $filePath = $this->app->basePath('/modules/author/src/UI/API/Requests/ListAuthorsRequest.php'); + assertFileExists($filePath); + assertMatchesFileSnapshot($filePath); +}); diff --git a/tests/Commands/Generators/ResourceMakeCommandTest.php b/tests/Commands/Generators/ResourceMakeCommandTest.php index 425a654..8894423 100644 --- a/tests/Commands/Generators/ResourceMakeCommandTest.php +++ b/tests/Commands/Generators/ResourceMakeCommandTest.php @@ -1,111 +1,36 @@ modulePath = base_path('app/Modules/Article'); - $this->finder = $this->app['files']; - $this->artisan('module:make', ['name' => 'Article', '--plain' => true]); - } - - protected function tearDown(): void - { - $this->app[RepositoryInterface::class]->delete('Article'); - parent::tearDown(); - } - - /** @test */ - public function it_generates_resource_file() - { - $code = $this->artisan('module:make:resource', [ - 'name' => 'MyAwesomeResource', - 'module' => 'Article', - '-n' => true - ]); - - $this->assertTrue(is_file($this->modulePath . '/UI/API/Resources/MyAwesomeResource.php')); - $this->assertSame(0, $code); - } - - /** @test */ - public function it_generated_correct_single_resource_file_with_content() - { - $code = $this->artisan('module:make:resource', [ - 'name' => 'Foo/Bar\\MyAwesomeSingleResource', - 'module' => 'Article', - '--stub' => 'single' - ]); - - $file = $this->finder->get($this->modulePath . '/UI/API/Resources/Foo/Bar/MyAwesomeSingleResource.php'); - - $this->assertMatchesSnapshot($file); - $this->assertSame(0, $code); - } - - /** @test */ - public function it_generated_correct_collection_resource_file_with_content() - { - $code = $this->artisan('module:make:resource', [ - 'name' => 'Foo/Bar\\MyAwesomeCollectionResource', - 'module' => 'Article', - '--stub' => 'collection' - ]); - - $file = $this->finder->get($this->modulePath . '/UI/API/Resources/Foo/Bar/MyAwesomeCollectionResource.php'); - - $this->assertMatchesSnapshot($file); - $this->assertSame(0, $code); - } - - /** @test */ - public function it_can_change_the_default_path_for_resource_file() - { - $this->app['config']->set('modules.generator.components.api-resource.path', 'Foo/Bar\\Resources'); - - $code = $this->artisan('module:make:resource', [ - 'name' => 'Baz\\Bat/MyAwesomeResource', - 'module' => 'Article', - '-n' => true - ]); - - $file = $this->finder->get($this->modulePath . '/Foo/Bar/Resources/Baz/Bat/MyAwesomeResource.php'); - - $this->assertMatchesSnapshot($file); - $this->assertSame(0, $code); - } - - /** @test */ - public function it_can_change_the_default_namespace_for_resource_file() - { - $this->app['config']->set('modules.generator.components.api-resource.namespace', 'Foo/Bar\\Resources/'); - - $code = $this->artisan('module:make:resource', [ - 'name' => 'Baz\\Bat/MyAwesomeResource', - 'module' => 'Article', - '-n' => true - ]); - - $file = $this->finder->get($this->modulePath . '/UI/API/Resources/Baz/Bat/MyAwesomeResource.php'); - - $this->assertMatchesSnapshot($file); - $this->assertSame(0, $code); - } -} +use function PHPUnit\Framework\assertFileExists; +use function Spatie\Snapshots\assertMatchesFileSnapshot; + +beforeEach(function () { + $this->setModules([ + __DIR__ . '/../../fixtures/stubs/modules/valid/author', + ], $this->app->basePath('/modules')); +}); + +it('generates "single" resource for the module', function () { + $this->artisan('module:make:resource', [ + 'name' => 'AuthorResource', + 'module' => 'Author', + '--stub' => 'single', + ]) + ->assertSuccessful(); + + $filePath = $this->app->basePath('/modules/author/src/UI/API/Resources/AuthorResource.php'); + assertFileExists($filePath); + assertMatchesFileSnapshot($filePath); +}); + +it('generates "collection" resource for the module', function () { + $this->artisan('module:make:resource', [ + 'name' => 'AuthorResourceCollection', + 'module' => 'Author', + '--stub' => 'collection', + ]) + ->assertSuccessful(); + + $filePath = $this->app->basePath('/modules/author/src/UI/API/Resources/AuthorResourceCollection.php'); + assertFileExists($filePath); + assertMatchesFileSnapshot($filePath); +}); diff --git a/tests/Commands/Generators/RouteMakeCommandTest.php b/tests/Commands/Generators/RouteMakeCommandTest.php index b6bd14d..13d8c46 100644 --- a/tests/Commands/Generators/RouteMakeCommandTest.php +++ b/tests/Commands/Generators/RouteMakeCommandTest.php @@ -1,338 +1,214 @@ modulePath = base_path('app/Modules/Article'); - $this->finder = $this->app['files']; - $this->artisan('module:make', ['name' => 'Article', '--plain' => true]); - } - - protected function tearDown(): void - { - $this->app[RepositoryInterface::class]->delete('Article'); - parent::tearDown(); - } - - /** @test */ - public function it_generates_route_file() - { - $code = $this->artisan('module:make:route', [ - 'name' => '/nested/some_route', - 'module' => 'Article', - '--url' => 'some/route', - '-n' => true - ]); - - $this->assertTrue(is_file($this->modulePath . '/UI/API/Routes/nested/some_route.php')); - $this->assertSame(0, $code); - } - - /** @test */ - public function it_generated_correct_route_file_with_content() - { - $code = $this->artisan('module:make:route', [ - 'name' => '/nested/some_route', - 'module' => 'Article', - '--url' => 'some/route', - '-n' => true - ]); - - $file = $this->finder->get($this->modulePath . '/UI/API/Routes/nested/some_route.php'); - - $this->assertMatchesSnapshot($file); - $this->assertSame(0, $code); - } - - /** @test */ - public function it_can_change_the_default_path_for_route_file() - { - $this->app['config']->set('modules.generator.components.api-route.path', 'Foo/Bar\\Routes'); - - $code = $this->artisan('module:make:route', [ - 'name' => '/nested/some_route', - 'module' => 'Article', - '--url' => 'some/route', - '-n' => true - ]); - - $file = $this->finder->get($this->modulePath . '/Foo/Bar/Routes/nested/some_route.php'); - - $this->assertMatchesSnapshot($file); - $this->assertSame(0, $code); - } - - /** @test */ - public function it_generated_recognized_view_route() - { - $code = $this->artisan('module:make:route', [ - 'name' => '/v1/view_posts', - 'module' => 'Article', - '--url' => 'posts/{post}', - '-n' => true - ]); - - $file = $this->finder->get($this->modulePath . '/UI/API/Routes/v1/view_posts.php'); - - $this->assertMatchesSnapshot($file); - $this->assertSame(0, $code); - } - - /** @test */ - public function it_generated_api_route_with_get_method() - { - $code = $this->artisan('module:make:route', [ - 'name' => '/nested/some_get_route', - 'module' => 'Article', - '--ui' => 'api', - '--url' => 'some/get/route', - '--method' => 'get', - '--name' => 'api.nested.some_get_route', - '--action' => 'SomeGetAction', - '-n' => true - ]); - - $file = $this->finder->get($this->modulePath . '/UI/API/Routes/nested/some_get_route.php'); - - $this->assertMatchesSnapshot($file); - $this->assertSame(0, $code); - } - - /** @test */ - public function it_generated_api_route_with_post_method() - { - $code = $this->artisan('module:make:route', [ - 'name' => '/nested/some_post_route', - 'module' => 'Article', - '--ui' => 'api', - '--url' => 'some/post/route', - '--method' => 'post', - '--name' => 'api.nested.some_post_route', - '--action' => 'SomePostAction', - '-n' => true - ]); - - $file = $this->finder->get($this->modulePath . '/UI/API/Routes/nested/some_post_route.php'); - - $this->assertMatchesSnapshot($file); - $this->assertSame(0, $code); - } - - /** @test */ - public function it_generated_api_route_with_put_method() - { - $code = $this->artisan('module:make:route', [ - 'name' => '/nested/some_put_route', - 'module' => 'Article', - '--ui' => 'api', - '--url' => 'some/put/route', - '--method' => 'put', - '--name' => 'api.nested.some_put_route', - '--action' => 'SomePutAction', - '-n' => true - ]); - - $file = $this->finder->get($this->modulePath . '/UI/API/Routes/nested/some_put_route.php'); - - $this->assertMatchesSnapshot($file); - $this->assertSame(0, $code); - } - - /** @test */ - public function it_generated_api_route_with_patch_method() - { - $code = $this->artisan('module:make:route', [ - 'name' => '/nested/some_patch_route', - 'module' => 'Article', - '--ui' => 'api', - '--url' => 'some/patch/route', - '--method' => 'patch', - '--name' => 'api.nested.some_patch_route', - '--action' => 'SomePatchAction', - '-n' => true - ]); - - $file = $this->finder->get($this->modulePath . '/UI/API/Routes/nested/some_patch_route.php'); - - $this->assertMatchesSnapshot($file); - $this->assertSame(0, $code); - } - - /** @test */ - public function it_generated_api_route_with_delete_method() - { - $code = $this->artisan('module:make:route', [ - 'name' => '/nested/some_delete_route', - 'module' => 'Article', - '--ui' => 'api', - '--url' => 'some/delete/route', - '--method' => 'delete', - '--name' => 'api.nested.some_delete_route', - '--action' => 'SomeDeleteAction', - '-n' => true - ]); - - $file = $this->finder->get($this->modulePath . '/UI/API/Routes/nested/some_delete_route.php'); - - $this->assertMatchesSnapshot($file); - $this->assertSame(0, $code); - } - - /** @test */ - public function it_generated_api_route_with_options_method() - { - $code = $this->artisan('module:make:route', [ - 'name' => '/nested/some_options_route', - 'module' => 'Article', - '--ui' => 'api', - '--url' => 'some/options/route', - '--method' => 'options', - '--name' => 'api.nested.some_options_route', - '--action' => 'SomeOptionsAction', - '-n' => true - ]); - - $file = $this->finder->get($this->modulePath . '/UI/API/Routes/nested/some_options_route.php'); - - $this->assertMatchesSnapshot($file); - $this->assertSame(0, $code); - } - - /** @test */ - public function it_generated_web_route_with_get_method() - { - $code = $this->artisan('module:make:route', [ - 'name' => '/nested/some_get_route', - 'module' => 'Article', - '--ui' => 'web', - '--url' => 'some/get/route', - '--method' => 'get', - '--name' => 'web.nested.some_get_route', - '--action' => 'SomeGetAction', - '-n' => true - ]); - - $file = $this->finder->get($this->modulePath . '/UI/WEB/Routes/nested/some_get_route.php'); - - $this->assertMatchesSnapshot($file); - $this->assertSame(0, $code); - } - - /** @test */ - public function it_generated_web_route_with_post_method() - { - $code = $this->artisan('module:make:route', [ - 'name' => '/nested/some_post_route', - 'module' => 'Article', - '--ui' => 'web', - '--url' => 'some/post/route', - '--method' => 'post', - '--name' => 'web.nested.some_post_route', - '--action' => 'SomePostAction', - '-n' => true - ]); - - $file = $this->finder->get($this->modulePath . '/UI/WEB/Routes/nested/some_post_route.php'); - - $this->assertMatchesSnapshot($file); - $this->assertSame(0, $code); - } - - /** @test */ - public function it_generated_web_route_with_put_method() - { - $code = $this->artisan('module:make:route', [ - 'name' => '/nested/some_put_route', - 'module' => 'Article', - '--ui' => 'web', - '--url' => 'some/put/route', - '--method' => 'put', - '--name' => 'web.nested.some_put_route', - '--action' => 'SomePutAction', - '-n' => true - ]); - - $file = $this->finder->get($this->modulePath . '/UI/WEB/Routes/nested/some_put_route.php'); - - $this->assertMatchesSnapshot($file); - $this->assertSame(0, $code); - } - - /** @test */ - public function it_generated_web_route_with_patch_method() - { - $code = $this->artisan('module:make:route', [ - 'name' => '/nested/some_patch_route', - 'module' => 'Article', - '--ui' => 'web', - '--url' => 'some/patch/route', - '--method' => 'patch', - '--name' => 'web.nested.some_patch_route', - '--action' => 'SomePatchAction', - '-n' => true - ]); - - $file = $this->finder->get($this->modulePath . '/UI/WEB/Routes/nested/some_patch_route.php'); - - $this->assertMatchesSnapshot($file); - $this->assertSame(0, $code); - } - - /** @test */ - public function it_generated_web_route_with_delete_method() - { - $code = $this->artisan('module:make:route', [ - 'name' => '/nested/some_delete_route', - 'module' => 'Article', - '--ui' => 'web', - '--url' => 'some/delete/route', - '--method' => 'delete', - '--name' => 'web.nested.some_delete_route', - '--action' => 'SomeDeleteAction', - '-n' => true - ]); - - $file = $this->finder->get($this->modulePath . '/UI/WEB/Routes/nested/some_delete_route.php'); - - $this->assertMatchesSnapshot($file); - $this->assertSame(0, $code); - } - - /** @test */ - public function it_generated_web_route_with_options_method() - { - $code = $this->artisan('module:make:route', [ - 'name' => '/nested/some_options_route', - 'module' => 'Article', - '--ui' => 'web', - '--url' => 'some/options/route', - '--method' => 'options', - '--name' => 'web.nested.some_options_route', - '--action' => 'SomeOptionsAction', - '-n' => true - ]); - - $file = $this->finder->get($this->modulePath . '/UI/WEB/Routes/nested/some_options_route.php'); - - $this->assertMatchesSnapshot($file); - $this->assertSame(0, $code); - } -} +use function PHPUnit\Framework\assertFileExists; +use function Spatie\Snapshots\assertMatchesFileSnapshot; + +beforeEach(function () { + $this->setModules([ + __DIR__ . '/../../fixtures/stubs/modules/valid/author', + ], $this->app->basePath('/modules')); +}); + +it('generates "get" web route for the module', function () { + $this->artisan('module:make:route', [ + 'name' => 'list_authors', + 'module' => 'Author', + '--ui' => 'web', + '--action' => 'ListAuthorsAction', + '--method' => 'get', + '--url' => 'authors', + '--name' => 'web.authors.list', + ]) + ->assertSuccessful(); + + $filePath = $this->app->basePath('/modules/author/src/UI/WEB/routes/list_authors.php'); + assertFileExists($filePath); + assertMatchesFileSnapshot($filePath); +}); + +it('generates "post" web route for the module', function () { + $this->artisan('module:make:route', [ + 'name' => 'create_author', + 'module' => 'Author', + '--ui' => 'web', + '--action' => 'CreateAuthorAction', + '--method' => 'post', + '--url' => 'authors', + '--name' => 'web.authors.create', + ]) + ->assertSuccessful(); + + $filePath = $this->app->basePath('/modules/author/src/UI/WEB/routes/create_author.php'); + assertFileExists($filePath); + assertMatchesFileSnapshot($filePath); +}); + +it('generates "put" web route for the module', function () { + $this->artisan('module:make:route', [ + 'name' => 'update_author', + 'module' => 'Author', + '--ui' => 'web', + '--action' => 'UpdateAuthorAction', + '--method' => 'put', + '--url' => 'authors/{author}', + '--name' => 'web.authors.update', + ]) + ->assertSuccessful(); + + $filePath = $this->app->basePath('/modules/author/src/UI/WEB/routes/update_author.php'); + assertFileExists($filePath); + assertMatchesFileSnapshot($filePath); +}); + +it('generates "patch" web route for the module', function () { + $this->artisan('module:make:route', [ + 'name' => 'update_author', + 'module' => 'Author', + '--ui' => 'web', + '--action' => 'UpdateAuthorAction', + '--method' => 'patch', + '--url' => 'authors/{author}', + '--name' => 'web.authors.update', + ]) + ->assertSuccessful(); + + $filePath = $this->app->basePath('/modules/author/src/UI/WEB/routes/update_author.php'); + assertFileExists($filePath); + assertMatchesFileSnapshot($filePath); +}); + +it('generates "delete" web route for the module', function () { + $this->artisan('module:make:route', [ + 'name' => 'delete_author', + 'module' => 'Author', + '--ui' => 'web', + '--action' => 'DeleteAuthorAction', + '--method' => 'delete', + '--url' => 'authors/{author}', + '--name' => 'web.authors.delete', + ]) + ->assertSuccessful(); + + $filePath = $this->app->basePath('/modules/author/src/UI/WEB/routes/delete_author.php'); + assertFileExists($filePath); + assertMatchesFileSnapshot($filePath); +}); + +it('generates "options" web route for the module', function () { + $this->artisan('module:make:route', [ + 'name' => 'options_authors', + 'module' => 'Author', + '--ui' => 'web', + '--action' => 'ListAuthorsAction', + '--method' => 'options', + '--url' => 'authors', + '--name' => 'web.authors.options', + ]) + ->assertSuccessful(); + + $filePath = $this->app->basePath('/modules/author/src/UI/WEB/routes/options_authors.php'); + assertFileExists($filePath); + assertMatchesFileSnapshot($filePath); +}); + +it('generates "get" api route for the module', function () { + $this->artisan('module:make:route', [ + 'name' => 'list_authors', + 'module' => 'Author', + '--ui' => 'api', + '--action' => 'ListAuthorsAction', + '--method' => 'get', + '--url' => 'authors', + '--name' => 'web.authors.list', + ]) + ->assertSuccessful(); + + $filePath = $this->app->basePath('/modules/author/src/UI/API/routes/list_authors.php'); + assertFileExists($filePath); + assertMatchesFileSnapshot($filePath); +}); + +it('generates "post" api route for the module', function () { + $this->artisan('module:make:route', [ + 'name' => 'create_author', + 'module' => 'Author', + '--ui' => 'api', + '--action' => 'CreateAuthorAction', + '--method' => 'post', + '--url' => 'authors', + '--name' => 'api.authors.create', + ]) + ->assertSuccessful(); + + $filePath = $this->app->basePath('/modules/author/src/UI/API/routes/create_author.php'); + assertFileExists($filePath); + assertMatchesFileSnapshot($filePath); +}); + +it('generates "put" api route for the module', function () { + $this->artisan('module:make:route', [ + 'name' => 'update_author', + 'module' => 'Author', + '--ui' => 'api', + '--action' => 'UpdateAuthorAction', + '--method' => 'put', + '--url' => 'authors/{author}', + '--name' => 'api.authors.update', + ]) + ->assertSuccessful(); + + $filePath = $this->app->basePath('/modules/author/src/UI/API/routes/update_author.php'); + assertFileExists($filePath); + assertMatchesFileSnapshot($filePath); +}); + +it('generates "patch" api route for the module', function () { + $this->artisan('module:make:route', [ + 'name' => 'update_author', + 'module' => 'Author', + '--ui' => 'api', + '--action' => 'UpdateAuthorAction', + '--method' => 'patch', + '--url' => 'authors/{author}', + '--name' => 'api.authors.update', + ]) + ->assertSuccessful(); + + $filePath = $this->app->basePath('/modules/author/src/UI/API/routes/update_author.php'); + assertFileExists($filePath); + assertMatchesFileSnapshot($filePath); +}); + +it('generates "delete" api route for the module', function () { + $this->artisan('module:make:route', [ + 'name' => 'delete_author', + 'module' => 'Author', + '--ui' => 'api', + '--action' => 'DeleteAuthorAction', + '--method' => 'delete', + '--url' => 'authors/{author}', + '--name' => 'api.authors.delete', + ]) + ->assertSuccessful(); + + $filePath = $this->app->basePath('/modules/author/src/UI/API/routes/delete_author.php'); + assertFileExists($filePath); + assertMatchesFileSnapshot($filePath); +}); + +it('generates "options" api route for the module', function () { + $this->artisan('module:make:route', [ + 'name' => 'options_authors', + 'module' => 'Author', + '--ui' => 'api', + '--action' => 'ListAuthorsAction', + '--method' => 'options', + '--url' => 'authors', + '--name' => 'api.authors.options', + ]) + ->assertSuccessful(); + + $filePath = $this->app->basePath('/modules/author/src/UI/API/routes/options_authors.php'); + assertFileExists($filePath); + assertMatchesFileSnapshot($filePath); +}); diff --git a/tests/Commands/Generators/RuleMakeCommandTest.php b/tests/Commands/Generators/RuleMakeCommandTest.php index 9c3e690..7ff2d59 100644 --- a/tests/Commands/Generators/RuleMakeCommandTest.php +++ b/tests/Commands/Generators/RuleMakeCommandTest.php @@ -1,92 +1,22 @@ modulePath = base_path('app/Modules/Article'); - $this->finder = $this->app['files']; - $this->artisan('module:make', ['name' => 'Article', '--plain' => true]); - } - - protected function tearDown(): void - { - $this->app[RepositoryInterface::class]->delete('Article'); - parent::tearDown(); - } - - /** @test */ - public function it_generates_rule_file() - { - $code = $this->artisan('module:make:rule', [ - 'name' => 'MyAwesomeRule', - 'module' => 'Article', - ]); - - $this->assertTrue(is_file($this->modulePath . '/Rules/MyAwesomeRule.php')); - $this->assertSame(0, $code); - } - - /** @test */ - public function it_generated_correct_rule_file_with_content() - { - $code = $this->artisan('module:make:rule', [ - 'name' => 'Foo/Bar\\MyAwesomeRule', - 'module' => 'Article', - ]); - - $file = $this->finder->get($this->modulePath . '/Rules/Foo/Bar/MyAwesomeRule.php'); - - $this->assertMatchesSnapshot($file); - $this->assertSame(0, $code); - } - - /** @test */ - public function it_can_change_the_default_path_for_rule_file() - { - $this->app['config']->set('modules.generator.components.rule.path', 'Foo/Bar\\Rules'); - - $code = $this->artisan('module:make:rule', [ - 'name' => 'Baz\\Bat/MyAwesomeRule', - 'module' => 'Article', - ]); - - $file = $this->finder->get($this->modulePath . '/Foo/Bar/Rules/Baz/Bat/MyAwesomeRule.php'); - - $this->assertMatchesSnapshot($file); - $this->assertSame(0, $code); - } - - /** @test */ - public function it_can_change_the_default_namespace_for_rule_file() - { - $this->app['config']->set('modules.generator.components.rule.namespace', 'Foo/Bar\\Rules/'); - - $code = $this->artisan('module:make:rule', [ - 'name' => 'Baz\\Bat/MyAwesomeRule', - 'module' => 'Article', - ]); - - $file = $this->finder->get($this->modulePath . '/Rules/Baz/Bat/MyAwesomeRule.php'); - - $this->assertMatchesSnapshot($file); - $this->assertSame(0, $code); - } -} +use function PHPUnit\Framework\assertFileExists; +use function Spatie\Snapshots\assertMatchesFileSnapshot; + +beforeEach(function () { + $this->setModules([ + __DIR__ . '/../../fixtures/stubs/modules/valid/author', + ], $this->app->basePath('/modules')); +}); + +it('generates rule for the module', function () { + $this->artisan('module:make:rule', [ + 'name' => 'SomeAuthorRule', + 'module' => 'Author', + ]) + ->assertSuccessful(); + + $filePath = $this->app->basePath('/modules/author/src/Rules/SomeAuthorRule.php'); + assertFileExists($filePath); + assertMatchesFileSnapshot($filePath); +}); diff --git a/tests/Commands/Generators/SeederMakeCommandTest.php b/tests/Commands/Generators/SeederMakeCommandTest.php index a66cec7..4873e25 100644 --- a/tests/Commands/Generators/SeederMakeCommandTest.php +++ b/tests/Commands/Generators/SeederMakeCommandTest.php @@ -1,126 +1,45 @@ modulePath = base_path('app/Modules/Article'); - $this->finder = $this->app['files']; - $this->artisan('module:make', ['name' => 'Article', '--plain' => true]); - } - - protected function tearDown(): void - { - $this->app[RepositoryInterface::class]->delete('Article'); - parent::tearDown(); - } - - /** @test */ - public function it_generates_plain_seeder_file() - { - $code = $this->artisan('module:make:seeder', [ - 'name' => 'MyAwesomePlainSeeder', - 'module' => 'Article', - '--stub' => 'plain' - ]); - - $this->assertTrue(is_file($this->modulePath . '/Data/Seeders/MyAwesomePlainSeeder.php')); - $this->assertSame(0, $code); - } - - /** @test */ - public function it_generates_permissions_seeder_file() - { - $code = $this->artisan('module:make:seeder', [ - 'name' => 'MyAwesomePermissionsSeeder', - 'module' => 'Article', - '--stub' => 'permissions', - '--model' => 'Bar\\Bat/Baz\\MyAwesomeModel' - ]); - - $this->assertTrue(is_file($this->modulePath . '/Data/Seeders/MyAwesomePermissionsSeeder.php')); - $this->assertSame(0, $code); - } - - /** @test */ - public function it_generated_correct_plain_seeder_file_with_content() - { - $code = $this->artisan('module:make:seeder', [ - 'name' => 'Foo/Bar\\MyAwesomePlainSeeder', - 'module' => 'Article', - '--stub' => 'plain' - ]); - - $file = $this->finder->get($this->modulePath . '/Data/Seeders/Foo/Bar/MyAwesomePlainSeeder.php'); - - $this->assertMatchesSnapshot($file); - $this->assertSame(0, $code); - } - - /** @test */ - public function it_generated_correct_permissions_seeder_file_with_content() - { - $code = $this->artisan('module:make:seeder', [ - 'name' => 'Foo/Bar\\MyAwesomePermissionsSeeder', - 'module' => 'Article', - '--stub' => 'permissions', - '--model' => 'Bar\\Bat/Baz\\MyAwesomeModel' - ]); - - $file = $this->finder->get($this->modulePath . '/Data/Seeders/Foo/Bar/MyAwesomePermissionsSeeder.php'); - - $this->assertMatchesSnapshot($file); - $this->assertSame(0, $code); - } - - /** @test */ - public function it_can_change_the_default_path_for_plain_seeder_file() - { - $this->app['config']->set('modules.generator.components.seeder.path', 'Foo/Bar\\Seeders'); - - $code = $this->artisan('module:make:seeder', [ - 'name' => 'Baz\\Bat/MyAwesomePlainSeeder', - 'module' => 'Article', - '--stub' => 'plain' - ]); - - $file = $this->finder->get($this->modulePath . '/Foo/Bar/Seeders/Baz/Bat/MyAwesomePlainSeeder.php'); - - $this->assertMatchesSnapshot($file); - $this->assertSame(0, $code); - } - - /** @test */ - public function it_can_change_the_default_namespace_for_plain_seeder_file() - { - $this->app['config']->set('modules.generator.components.seeder.namespace', 'Foo/Bar\\Seeders/'); - - $code = $this->artisan('module:make:seeder', [ - 'name' => 'Baz\\Bat/MyAwesomePlainSeeder', - 'module' => 'Article', - '--stub' => 'plain' - ]); - - $file = $this->finder->get($this->modulePath . '/Data/Seeders/Baz/Bat/MyAwesomePlainSeeder.php'); - - $this->assertMatchesSnapshot($file); - $this->assertSame(0, $code); - } -} +use function PHPUnit\Framework\assertFileExists; +use function Spatie\Snapshots\assertMatchesFileSnapshot; + +beforeEach(function () { + $this->setModules([ + __DIR__ . '/../../fixtures/stubs/modules/valid/author', + ], $this->app->basePath('/modules')); +}); + +it('generates "plain" seeder for the module', function () { + $this->artisan('module:make:seeder', [ + 'name' => 'AuthorsSeeder', + 'module' => 'Author', + '--stub' => 'plain', + ]) + ->assertSuccessful(); + + $filePath = $this->app->basePath('/modules/author/database/seeders/AuthorsSeeder.php'); + assertFileExists($filePath); + assertMatchesFileSnapshot($filePath); +}); + +it('generates "permissions" seeder for the module', function () { + $this->artisan('module:make:seeder', [ + 'name' => 'AuthorPermissionsSeeder', + 'module' => 'Author', + '--stub' => 'permissions', + '--model' => 'Author', + ]) + ->expectsQuestion( + 'Enter the class name of the "Create permission action"', + 'Modules\\Authorization\\Actions\\CreatePermissionAction' + ) + ->expectsQuestion( + 'Enter the class name of the "Create permission DTO"', + 'Modules\\Authorization\\DTO\\CreatePermissionDTO' + ) + ->assertSuccessful(); + + $filePath = $this->app->basePath('/modules/author/database/seeders/AuthorPermissionsSeeder.php'); + assertFileExists($filePath); + assertMatchesFileSnapshot($filePath); +}); diff --git a/tests/Commands/Generators/TestMakeCommandTest.php b/tests/Commands/Generators/TestMakeCommandTest.php index 91af8be..e56875e 100644 --- a/tests/Commands/Generators/TestMakeCommandTest.php +++ b/tests/Commands/Generators/TestMakeCommandTest.php @@ -1,557 +1,209 @@ modulePath = base_path('app/Modules/Article'); - $this->finder = $this->app['files']; - $this->artisan('module:make', ['name' => 'Article', '--plain' => true]); - } - - protected function tearDown(): void - { - $this->app[RepositoryInterface::class]->delete('Article'); - parent::tearDown(); - } - - // Type: Unit - - /** @test */ - public function it_generate_unit_test_file() - { - $code = $this->artisan('module:make:test', [ - 'name' => 'MyAwesomeUnitTest', - 'module' => 'Article', - '--type' => 'unit', - '--stub' => 'plain' - ]); - - $this->assertTrue(is_file($this->modulePath . '/Tests/Unit/MyAwesomeUnitTest.php')); - $this->assertSame(0, $code); - } - - /** @test */ - public function it_generated_correct_unit_test_file_with_content() - { - $code = $this->artisan('module:make:test', [ - 'name' => 'MyAwesomeUnitTest', - 'module' => 'Article', - '--type' => 'unit', - '--stub' => 'plain' - ]); - - $file = $this->finder->get($this->modulePath . '/Tests/Unit/MyAwesomeUnitTest.php'); - - $this->assertMatchesSnapshot($file); - $this->assertSame(0, $code); - } - - /** @test */ - public function it_can_change_the_default_path_for_unit_test() - { - $this->app['config']->set('modules.generator.components.unit-test.path', 'Foo/Bar\\NewTests'); - - $code = $this->artisan('module:make:test', [ - 'name' => 'Baz\\Bat/MyAwesomeUnitTest', - 'module' => 'Article', - '--type' => 'unit', - '--stub' => 'plain' - ]); - - $file = $this->finder->get($this->modulePath . '/Foo/Bar/NewTests/Baz/Bat/MyAwesomeUnitTest.php'); - - $this->assertMatchesSnapshot($file); - $this->assertSame(0, $code); - } - - /** @test */ - public function it_can_change_the_default_namespace_for_unit_test() - { - $this->app['config']->set('modules.generator.components.unit-test.namespace', 'Foo/Bar\\NewTests/'); - - $code = $this->artisan('module:make:test', [ - 'name' => 'Baz\\Bat/MyAwesomeUnitTest', - 'module' => 'Article', - '--type' => 'unit', - '--stub' => 'plain' - ]); - - $file = $this->finder->get($this->modulePath . '/Tests/Unit/Baz/Bat/MyAwesomeUnitTest.php'); - - $this->assertMatchesSnapshot($file); - $this->assertSame(0, $code); - } - - // Type: Feature - - /** @test */ - public function it_generate_feature_test_file() - { - $code = $this->artisan('module:make:test', [ - 'name' => 'MyAwesomeFeatureTest', - 'module' => 'Article', - '--type' => 'feature', - '--stub' => 'plain' - ]); - - $this->assertTrue(is_file($this->modulePath . '/Tests/Feature/MyAwesomeFeatureTest.php')); - $this->assertSame(0, $code); - } - - /** @test */ - public function it_generated_correct_feature_test_file_with_content() - { - $code = $this->artisan('module:make:test', [ - 'name' => 'MyAwesomeFeatureTest', - 'module' => 'Article', - '--type' => 'feature', - '--stub' => 'plain' - ]); - - $file = $this->finder->get($this->modulePath . '/Tests/Feature/MyAwesomeFeatureTest.php'); - - $this->assertMatchesSnapshot($file); - $this->assertSame(0, $code); - } - - /** @test */ - public function it_can_change_the_default_path_for_feature_test() - { - $this->app['config']->set('modules.generator.components.feature-test.path', 'Foo/Bar\\NewTests'); - - $code = $this->artisan('module:make:test', [ - 'name' => 'Baz\\Bat/MyAwesomeFeatureTest', - 'module' => 'Article', - '--type' => 'feature', - '--stub' => 'plain' - ]); - - $file = $this->finder->get($this->modulePath . '/Foo/Bar/NewTests/Baz/Bat/MyAwesomeFeatureTest.php'); - - $this->assertMatchesSnapshot($file); - $this->assertSame(0, $code); - } - - /** @test */ - public function it_can_change_the_default_namespace_for_feature_test() - { - $this->app['config']->set('modules.generator.components.feature-test.namespace', 'Foo/Bar\\NewTests/'); - - $code = $this->artisan('module:make:test', [ - 'name' => 'Baz\\Bat/MyAwesomeFeatureTest', - 'module' => 'Article', - '--type' => 'feature', - '--stub' => 'plain' - ]); - - $file = $this->finder->get($this->modulePath . '/Tests/Feature/Baz/Bat/MyAwesomeFeatureTest.php'); - - $this->assertMatchesSnapshot($file); - $this->assertSame(0, $code); - } - - // Type: CLI - - /** @test */ - public function it_generate_cli_test_file() - { - $code = $this->artisan('module:make:test', [ - 'name' => 'MyAwesomeCliTest', - 'module' => 'Article', - '--type' => 'cli', - '--stub' => 'plain' - ]); - - $this->assertTrue(is_file($this->modulePath . '/UI/CLI/Tests/MyAwesomeCliTest.php')); - $this->assertSame(0, $code); - } - - /** @test */ - public function it_generated_correct_cli_test_file_with_content() - { - $code = $this->artisan('module:make:test', [ - 'name' => 'MyAwesomeCliTest', - 'module' => 'Article', - '--type' => 'cli', - '--stub' => 'plain' - ]); - - $file = $this->finder->get($this->modulePath . '/UI/CLI/Tests/MyAwesomeCliTest.php'); - - $this->assertMatchesSnapshot($file); - $this->assertSame(0, $code); - } - - /** @test */ - public function it_can_change_the_default_path_for_cli_test() - { - $this->app['config']->set('modules.generator.components.cli-test.path', 'Foo/Bar\\NewTests'); - - $code = $this->artisan('module:make:test', [ - 'name' => 'Baz\\Bat/MyAwesomeCliTest', - 'module' => 'Article', - '--type' => 'cli', - '--stub' => 'plain' - ]); - - $file = $this->finder->get($this->modulePath . '/Foo/Bar/NewTests/Baz/Bat/MyAwesomeCliTest.php'); - - $this->assertMatchesSnapshot($file); - $this->assertSame(0, $code); - } - - /** @test */ - public function it_can_change_the_default_namespace_for_cli_test() - { - $this->app['config']->set('modules.generator.components.cli-test.namespace', 'Foo/Bar\\NewTests/'); - - $code = $this->artisan('module:make:test', [ - 'name' => 'Baz\\Bat/MyAwesomeCliTest', - 'module' => 'Article', - '--type' => 'cli', - '--stub' => 'plain' - ]); - - $file = $this->finder->get($this->modulePath . '/UI/CLI/Tests/Baz/Bat/MyAwesomeCliTest.php'); - - $this->assertMatchesSnapshot($file); - $this->assertSame(0, $code); - } - - // Type: API - - /** @test */ - public function it_generate_api_test_file() - { - $code = $this->artisan('module:make:test', [ - 'name' => 'MyAwesomeApiTest', - 'module' => 'Article', - '--type' => 'api', - '--stub' => 'plain', - '--route' => 'some.api.url' - ]); - - $this->assertTrue(is_file($this->modulePath . '/UI/API/Tests/MyAwesomeApiTest.php')); - $this->assertSame(0, $code); - } - - /** @test */ - public function it_generated_correct_api_test_file_with_content() - { - $code = $this->artisan('module:make:test', [ - 'name' => 'MyAwesomeApiTest', - 'module' => 'Article', - '--type' => 'api', - '--stub' => 'plain', - '--route' => 'some.api.url' - ]); - - $file = $this->finder->get($this->modulePath . '/UI/API/Tests/MyAwesomeApiTest.php'); - - $this->assertMatchesSnapshot($file); - $this->assertSame(0, $code); - } - - /** @test */ - public function it_can_change_the_default_path_for_api_test() - { - $this->app['config']->set('modules.generator.components.api-test.path', 'Foo/Bar\\NewTests'); - - $code = $this->artisan('module:make:test', [ - 'name' => 'Baz\\Bat/MyAwesomeApiTest', - 'module' => 'Article', - '--type' => 'api', - '--stub' => 'plain', - '--route' => 'some.api.url' - ]); - - $file = $this->finder->get($this->modulePath . '/Foo/Bar/NewTests/Baz/Bat/MyAwesomeApiTest.php'); - - $this->assertMatchesSnapshot($file); - $this->assertSame(0, $code); - } - - /** @test */ - public function it_can_change_the_default_namespace_for_api_test() - { - $this->app['config']->set('modules.generator.components.api-test.namespace', 'Foo/Bar\\NewTests/'); - - $code = $this->artisan('module:make:test', [ - 'name' => 'Baz\\Bat/MyAwesomeApiTest', - 'module' => 'Article', - '--type' => 'api', - '--stub' => 'plain', - '--route' => 'some.api.url' - ]); - - $file = $this->finder->get($this->modulePath . '/UI/API/Tests/Baz/Bat/MyAwesomeApiTest.php'); - - $this->assertMatchesSnapshot($file); - $this->assertSame(0, $code); - } - - /** @test */ - public function it_can_generate_api_create_test_file() - { - $code = $this->artisan('module:make:test', [ - 'name' => 'Baz\\Bat/MyAwesomeApiCreateTest', - 'module' => 'Article', - '--type' => 'api', - '--stub' => 'create', - '--route' => 'some.api.url', - '--model' => 'Some/Nested\\SomeTestModel', - ]); - - $file = $this->finder->get($this->modulePath . '/UI/API/Tests/Baz/Bat/MyAwesomeApiCreateTest.php'); - - $this->assertMatchesSnapshot($file); - $this->assertSame(0, $code); - } - - /** @test */ - public function it_throws_exception_when_classes_not_provided_for_api_create_test_file() - { - $this->expectException(InvalidOptionException::class); - - $this->artisan('module:make:test', [ - 'name' => 'Baz\\Bat/MyAwesomeApiCreateTest', - 'module' => 'Article', - '--type' => 'api', - '--stub' => 'create', - '--route' => 'some.api.url', - '-n' => true, - ]); - } - - /** @test */ - public function it_can_generate_api_delete_test_file() - { - $code = $this->artisan('module:make:test', [ - 'name' => 'Baz\\Bat/MyAwesomeApiDeleteTest', - 'module' => 'Article', - '--type' => 'api', - '--stub' => 'delete', - '--route' => 'some.api.url', - '--model' => 'Some/Nested\\SomeTestModel', - ]); - - $file = $this->finder->get($this->modulePath . '/UI/API/Tests/Baz/Bat/MyAwesomeApiDeleteTest.php'); - - $this->assertMatchesSnapshot($file); - $this->assertSame(0, $code); - } - - /** @test */ - public function it_can_generate_api_list_test_file() - { - $code = $this->artisan('module:make:test', [ - 'name' => 'Baz\\Bat/MyAwesomeApiListTest', - 'module' => 'Article', - '--type' => 'api', - '--stub' => 'list', - '--route' => 'some.api.url', - '--model' => 'Some/Nested\\SomeTestModel', - ]); - - $file = $this->finder->get($this->modulePath . '/UI/API/Tests/Baz/Bat/MyAwesomeApiListTest.php'); - - $this->assertMatchesSnapshot($file); - $this->assertSame(0, $code); - } - - /** @test */ - public function it_can_generate_api_update_test_file() - { - $code = $this->artisan('module:make:test', [ - 'name' => 'Baz\\Bat/MyAwesomeApiUpdateTest', - 'module' => 'Article', - '--type' => 'api', - '--stub' => 'update', - '--route' => 'some.api.url', - '--model' => 'Some/Nested\\SomeTestModel', - ]); - - $file = $this->finder->get($this->modulePath . '/UI/API/Tests/Baz/Bat/MyAwesomeApiUpdateTest.php'); - - $this->assertMatchesSnapshot($file); - $this->assertSame(0, $code); - } - - /** @test */ - public function it_can_generate_api_view_test_file() - { - $code = $this->artisan('module:make:test', [ - 'name' => 'Baz\\Bat/MyAwesomeApiViewTest', - 'module' => 'Article', - '--type' => 'api', - '--stub' => 'view', - '--route' => 'some.api.url', - '--model' => 'Some/Nested\\SomeTestModel', - ]); - - $file = $this->finder->get($this->modulePath . '/UI/API/Tests/Baz/Bat/MyAwesomeApiViewTest.php'); - - $this->assertMatchesSnapshot($file); - $this->assertSame(0, $code); - } - - // Type: WEB - - /** @test */ - public function it_generate_web_test_file() - { - $code = $this->artisan('module:make:test', [ - 'name' => 'MyAwesomeWebTest', - 'module' => 'Article', - '--type' => 'web', - '--stub' => 'plain', - '--route' => 'some.web.url', - ]); - - $this->assertTrue(is_file($this->modulePath . '/UI/WEB/Tests/MyAwesomeWebTest.php')); - $this->assertSame(0, $code); - } - - /** @test */ - public function it_generated_correct_web_test_file_with_content() - { - $code = $this->artisan('module:make:test', [ - 'name' => 'MyAwesomeWebTest', - 'module' => 'Article', - '--type' => 'web', - '--stub' => 'plain', - '--route' => 'some.web.url', - ]); - - $file = $this->finder->get($this->modulePath . '/UI/WEB/Tests/MyAwesomeWebTest.php'); - - $this->assertMatchesSnapshot($file); - $this->assertSame(0, $code); - } - - /** @test */ - public function it_can_change_the_default_path_for_web_test() - { - $this->app['config']->set('modules.generator.components.web-test.path', 'Foo/Bar\\NewTests'); - - $code = $this->artisan('module:make:test', [ - 'name' => 'Baz\\Bat/MyAwesomeWebTest', - 'module' => 'Article', - '--type' => 'web', - '--stub' => 'plain', - '--route' => 'some.web.url', - ]); - - $file = $this->finder->get($this->modulePath . '/Foo/Bar/NewTests/Baz/Bat/MyAwesomeWebTest.php'); - - $this->assertMatchesSnapshot($file); - $this->assertSame(0, $code); - } - - /** @test */ - public function it_can_change_the_default_namespace_for_web_test() - { - $this->app['config']->set('modules.generator.components.web-test.namespace', 'Foo/Bar\\NewTests/'); - - $code = $this->artisan('module:make:test', [ - 'name' => 'Baz\\Bat/MyAwesomeWebTest', - 'module' => 'Article', - '--type' => 'web', - '--stub' => 'plain', - '--route' => 'some.web.url', - ]); - - $file = $this->finder->get($this->modulePath . '/UI/WEB/Tests/Baz/Bat/MyAwesomeWebTest.php'); - - $this->assertMatchesSnapshot($file); - $this->assertSame(0, $code); - } - - /** @test */ - public function it_can_generate_web_create_test_file() - { - $code = $this->artisan('module:make:test', [ - 'name' => 'Baz\\Bat/MyAwesomeWebCreateTest', - 'module' => 'Article', - '--type' => 'web', - '--stub' => 'create', - '--route' => 'some.web.url', - '--model' => 'Some/Nested\\SomeTestModel', - ]); - - $file = $this->finder->get($this->modulePath . '/UI/WEB/Tests/Baz/Bat/MyAwesomeWebCreateTest.php'); - - $this->assertMatchesSnapshot($file); - $this->assertSame(0, $code); - } - - /** @test */ - public function it_throws_exception_when_classes_not_provided_for_web_create_test_file() - { - $this->expectException(InvalidOptionException::class); - - $this->artisan('module:make:test', [ - 'name' => 'Baz\\Bat/MyAwesomeWebCreateTest', - 'module' => 'Article', - '--type' => 'web', - '--stub' => 'create', - '--route' => 'some.web.url', - '-n' => true, - ]); - } - - /** @test */ - public function it_can_generate_web_delete_test_file() - { - $code = $this->artisan('module:make:test', [ - 'name' => 'Baz\\Bat/MyAwesomeWebDeleteTest', - 'module' => 'Article', - '--type' => 'web', - '--stub' => 'delete', - '--route' => 'some.web.url', - '--model' => 'Some/Nested\\SomeTestModel', - ]); - - $file = $this->finder->get($this->modulePath . '/UI/WEB/Tests/Baz/Bat/MyAwesomeWebDeleteTest.php'); - - $this->assertMatchesSnapshot($file); - $this->assertSame(0, $code); - } - - /** @test */ - public function it_can_generate_web_update_test_file() - { - $code = $this->artisan('module:make:test', [ - 'name' => 'Baz\\Bat/MyAwesomeWebUpdateTest', - 'module' => 'Article', - '--type' => 'web', - '--stub' => 'update', - '--route' => 'some.web.url', - '--model' => 'Some/Nested\\SomeTestModel', - ]); - - $file = $this->finder->get($this->modulePath . '/UI/WEB/Tests/Baz/Bat/MyAwesomeWebUpdateTest.php'); - - $this->assertMatchesSnapshot($file); - $this->assertSame(0, $code); - } -} +use function PHPUnit\Framework\assertFileExists; +use function Spatie\Snapshots\assertMatchesFileSnapshot; + +beforeEach(function () { + $this->setModules([ + __DIR__ . '/../../fixtures/stubs/modules/valid/author', + ], $this->app->basePath('/modules')); +}); + +it('generates "unit" test for the module', function () { + $this->artisan('module:make:test', [ + 'name' => 'ExampleTest', + 'module' => 'Author', + '--type' => 'unit', + ]) + ->assertSuccessful(); + + $filePath = $this->app->basePath('/modules/author/tests/Unit/ExampleTest.php'); + assertFileExists($filePath); + assertMatchesFileSnapshot($filePath); +}); + +it('generates plain "feature" test for the module', function () { + $this->artisan('module:make:test', [ + 'name' => 'ExampleTest', + 'module' => 'Author', + '--type' => 'feature', + ]) + ->assertSuccessful(); + + $filePath = $this->app->basePath('/modules/author/tests/Feature/ExampleTest.php'); + assertFileExists($filePath); + assertMatchesFileSnapshot($filePath); +}); + +it('generates plain "cli" test for the module', function () { + $this->artisan('module:make:test', [ + 'name' => 'ExampleTest', + 'module' => 'Author', + '--type' => 'cli', + ]) + ->assertSuccessful(); + + $filePath = $this->app->basePath('/modules/author/tests/UI/CLI/ExampleTest.php'); + assertFileExists($filePath); + assertMatchesFileSnapshot($filePath); +}); + +it('generates plain "web" test for the module', function () { + $this->artisan('module:make:test', [ + 'name' => 'ExampleTest', + 'module' => 'Author', + '--type' => 'web', + '--stub' => 'plain', + '--model' => 'Author', + '--route' => 'web.authors.example', + ]) + ->assertSuccessful(); + + $filePath = $this->app->basePath('/modules/author/tests/UI/WEB/ExampleTest.php'); + assertFileExists($filePath); + assertMatchesFileSnapshot($filePath); +}); + +it('generates create "web" test for the module', function () { + $this->artisan('module:make:test', [ + 'name' => 'CreateAuthorTest', + 'module' => 'Author', + '--type' => 'web', + '--stub' => 'create', + '--model' => 'Author', + '--route' => 'web.authors.create', + ]) + ->assertSuccessful(); + + $filePath = $this->app->basePath('/modules/author/tests/UI/WEB/CreateAuthorTest.php'); + assertFileExists($filePath); + assertMatchesFileSnapshot($filePath); +}); + +it('generates update "web" test for the module', function () { + $this->artisan('module:make:test', [ + 'name' => 'UpdateAuthorTest', + 'module' => 'Author', + '--type' => 'web', + '--stub' => 'update', + '--model' => 'Author', + '--route' => 'web.authors.update', + ]) + ->assertSuccessful(); + + $filePath = $this->app->basePath('/modules/author/tests/UI/WEB/UpdateAuthorTest.php'); + assertFileExists($filePath); + assertMatchesFileSnapshot($filePath); +}); + +it('generates delete "web" test for the module', function () { + $this->artisan('module:make:test', [ + 'name' => 'DeleteAuthorTest', + 'module' => 'Author', + '--type' => 'web', + '--stub' => 'delete', + '--model' => 'Author', + '--route' => 'web.authors.delete', + ]) + ->assertSuccessful(); + + $filePath = $this->app->basePath('/modules/author/tests/UI/WEB/DeleteAuthorTest.php'); + assertFileExists($filePath); + assertMatchesFileSnapshot($filePath); +}); + +it('generates plain "api" test for the module', function () { + $this->artisan('module:make:test', [ + 'name' => 'ExampleTest', + 'module' => 'Author', + '--type' => 'api', + '--stub' => 'plain', + '--model' => 'Author', + '--route' => 'api.authors.example', + ]) + ->assertSuccessful(); + + $filePath = $this->app->basePath('/modules/author/tests/UI/API/ExampleTest.php'); + assertFileExists($filePath); + assertMatchesFileSnapshot($filePath); +}); + +it('generates create "api" test for the module', function () { + $this->artisan('module:make:test', [ + 'name' => 'CreateAuthorTest', + 'module' => 'Author', + '--type' => 'api', + '--stub' => 'create', + '--model' => 'Author', + '--route' => 'api.authors.create', + ]) + ->assertSuccessful(); + + $filePath = $this->app->basePath('/modules/author/tests/UI/API/CreateAuthorTest.php'); + assertFileExists($filePath); + assertMatchesFileSnapshot($filePath); +}); + +it('generates update "api" test for the module', function () { + $this->artisan('module:make:test', [ + 'name' => 'UpdateAuthorTest', + 'module' => 'Author', + '--type' => 'api', + '--stub' => 'update', + '--model' => 'Author', + '--route' => 'api.authors.update', + ]) + ->assertSuccessful(); + + $filePath = $this->app->basePath('/modules/author/tests/UI/API/UpdateAuthorTest.php'); + assertFileExists($filePath); + assertMatchesFileSnapshot($filePath); +}); + +it('generates delete "api" test for the module', function () { + $this->artisan('module:make:test', [ + 'name' => 'DeleteAuthorTest', + 'module' => 'Author', + '--type' => 'api', + '--stub' => 'delete', + '--model' => 'Author', + '--route' => 'api.authors.delete', + ]) + ->assertSuccessful(); + + $filePath = $this->app->basePath('/modules/author/tests/UI/API/DeleteAuthorTest.php'); + assertFileExists($filePath); + assertMatchesFileSnapshot($filePath); +}); + +it('generates list "api" test for the module', function () { + $this->artisan('module:make:test', [ + 'name' => 'ListAuthorsTest', + 'module' => 'Author', + '--type' => 'api', + '--stub' => 'list', + '--model' => 'Author', + '--route' => 'api.authors.list', + ]) + ->assertSuccessful(); + + $filePath = $this->app->basePath('/modules/author/tests/UI/API/ListAuthorsTest.php'); + assertFileExists($filePath); + assertMatchesFileSnapshot($filePath); +}); + +it('generates view "api" test for the module', function () { + $this->artisan('module:make:test', [ + 'name' => 'ViewAuthorTest', + 'module' => 'Author', + '--type' => 'api', + '--stub' => 'view', + '--model' => 'Author', + '--route' => 'api.authors.view', + ]) + ->assertSuccessful(); + + $filePath = $this->app->basePath('/modules/author/tests/UI/API/ViewAuthorTest.php'); + assertFileExists($filePath); + assertMatchesFileSnapshot($filePath); +}); diff --git a/tests/Commands/Generators/__snapshots__/ActionMakeCommandTest__it_can_change_the_default_namespace__1.txt b/tests/Commands/Generators/__snapshots__/ActionMakeCommandTest__it_can_change_the_default_namespace__1.txt deleted file mode 100644 index 63810d9..0000000 --- a/tests/Commands/Generators/__snapshots__/ActionMakeCommandTest__it_can_change_the_default_namespace__1.txt +++ /dev/null @@ -1,13 +0,0 @@ -all()); - } - - public function asController(TestRequest $request): JsonResponse - { - $testModel = $this->handle($request->toDTO()); - - return (new TestResource($testModel))->created(); - } -} diff --git a/tests/Commands/Generators/__snapshots__/ActionMakeCommandTest__it_can_generate_delete_action_file__1.txt b/tests/Commands/Generators/__snapshots__/ActionMakeCommandTest__it_can_generate_delete_action_file__1.txt deleted file mode 100644 index 15d9e9c..0000000 --- a/tests/Commands/Generators/__snapshots__/ActionMakeCommandTest__it_can_generate_delete_action_file__1.txt +++ /dev/null @@ -1,23 +0,0 @@ -delete(); - } - - public function asController(TestRequest $request, TestModel $testModel): JsonResponse - { - $this->handle($testModel); - - return $this->noContent(); - } -} diff --git a/tests/Commands/Generators/__snapshots__/ActionMakeCommandTest__it_can_generate_list_action_file__1.txt b/tests/Commands/Generators/__snapshots__/ActionMakeCommandTest__it_can_generate_list_action_file__1.txt deleted file mode 100644 index 5046d85..0000000 --- a/tests/Commands/Generators/__snapshots__/ActionMakeCommandTest__it_can_generate_list_action_file__1.txt +++ /dev/null @@ -1,26 +0,0 @@ -build() - ->jsonPaginate(); - } - - public function asController(TestRequest $request): ResourceCollection - { - return TestResource::collection($this->handle($request)); - } -} diff --git a/tests/Commands/Generators/__snapshots__/ActionMakeCommandTest__it_can_generate_update_action_file__1.txt b/tests/Commands/Generators/__snapshots__/ActionMakeCommandTest__it_can_generate_update_action_file__1.txt deleted file mode 100644 index 8210ded..0000000 --- a/tests/Commands/Generators/__snapshots__/ActionMakeCommandTest__it_can_generate_update_action_file__1.txt +++ /dev/null @@ -1,33 +0,0 @@ -all(); - - if (empty($data)) { - throw new UpdateResourceFailedException(); - } - - $testModel->update($data); - - return $testModel; - } - - public function asController(TestRequest $request, TestModel $testModel): TestResource - { - $testModel = $this->handle($testModel, $request->toDTO()); - - return new TestResource($testModel); - } -} diff --git a/tests/Commands/Generators/__snapshots__/ActionMakeCommandTest__it_can_generate_view_action_file__1.txt b/tests/Commands/Generators/__snapshots__/ActionMakeCommandTest__it_can_generate_view_action_file__1.txt deleted file mode 100644 index ea5bc46..0000000 --- a/tests/Commands/Generators/__snapshots__/ActionMakeCommandTest__it_can_generate_view_action_file__1.txt +++ /dev/null @@ -1,23 +0,0 @@ -build(); - } - - public function asController(TestRequest $request, TestModel $testModel): TestResource - { - return new TestResource($this->handle($request, $testModel)); - } -} diff --git a/tests/Commands/Generators/__snapshots__/ActionMakeCommandTest__it_generated_correct_action_file_with_content__1.txt b/tests/Commands/Generators/__snapshots__/ActionMakeCommandTest__it_generated_correct_action_file_with_content__1.txt deleted file mode 100644 index f3d22eb..0000000 --- a/tests/Commands/Generators/__snapshots__/ActionMakeCommandTest__it_generated_correct_action_file_with_content__1.txt +++ /dev/null @@ -1,13 +0,0 @@ -can('view-post'); - } - - /** - * Determine whether the user can view the model. - */ - public function view(User $user, Post $post): bool - { - return $user->can('view-post'); - } - - /** - * Determine whether the user can create models. - */ - public function create(User $user): bool - { - return $user->can('create-post'); - } - - /** - * Determine whether the user can update the model. - */ - public function update(User $user, Post $post): bool - { - return $user->can('update-post'); - } - - /** - * Determine whether the user can delete the model. - */ - public function delete(User $user, Post $post): bool - { - return $user->can('delete-post'); - } - - /** - * Determine whether the user can restore the model. - */ - public function restore(User $user, Post $post): bool - { - return $user->can('delete-post'); - } - - /** - * Determine whether the user can permanently delete the model. - */ - public function forceDelete(User $user, Post $post): bool - { - return $user->can('force-delete-post'); - } -} diff --git a/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_can_set_custom_model__13.txt b/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_can_set_custom_model__13.txt deleted file mode 100644 index dca9938..0000000 --- a/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_can_set_custom_model__13.txt +++ /dev/null @@ -1,99 +0,0 @@ -registerMigrations(); - // $this->registerTranslations(); - // $this->registerCommands(); - // $this->registerViews(); - } - - /** - * Get the services provided by the provider. - */ - public function provides(): array - { - return []; - } - - /** - * Register translations. - */ - public function registerTranslations(): void - { - $sourcePath = module_path($this->moduleName, 'Resources/lang'); - $langPath = resource_path('lang/modules/' . $this->moduleKey); - - $this->loadTranslationsFrom($sourcePath, $this->moduleKey); - - $this->publishes([ - $sourcePath => $langPath, - ], 'translations'); - } - - /** - * Register views. - */ - public function registerViews(): void - { - $sourcePath = module_path($this->moduleName, 'Resources/views'); - $viewsPath = resource_path('views/modules/' . $this->moduleKey); - - $this->loadViewsFrom( - array_merge($this->getPublishableViewPaths($this->moduleKey), [$sourcePath]), - $this->moduleKey - ); - - $this->publishes([ - $sourcePath => $viewsPath - ], 'views'); - } - - /** - * Register migrations. - */ - public function registerMigrations(): void - { - $sourcePath = module_path($this->moduleName, 'Data/Migrations'); - $migrationsPath = database_path('migrations'); - - $this->loadMigrationsFrom($sourcePath); - - $this->publishes([ - $sourcePath => $migrationsPath - ], 'migrations'); - } - - /** - * Register artisan commands. - */ - public function registerCommands(): void - { - if ($this->app->runningInConsole()) { - $this->loadCommands(module_path($this->moduleName, 'UI/CLI/Commands')); - } - } -} diff --git a/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_can_set_custom_model__15.txt b/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_can_set_custom_model__15.txt deleted file mode 100644 index 8ccf7a1..0000000 --- a/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_can_set_custom_model__15.txt +++ /dev/null @@ -1,51 +0,0 @@ - - */ - protected function allowedAppends(): array - { - return []; - } - - /** - * @return array - */ - protected function defaultAppends(): array - { - return []; - } - - /** - * @return array - */ - protected function allowedFields(): array - { - return [ - // TODO: add fields here - ]; - } - - /** - * @return array - */ - protected function allowedIncludes(): array - { - return []; - } - - /** - * @return array - */ - protected function defaultIncludes(): array - { - return []; - } -} diff --git a/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_can_set_custom_model__16.txt b/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_can_set_custom_model__16.txt deleted file mode 100644 index 8b3ca78..0000000 --- a/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_can_set_custom_model__16.txt +++ /dev/null @@ -1,77 +0,0 @@ - - */ - protected function allowedAppends(): array - { - return []; - } - - /** - * @return array - */ - protected function defaultAppends(): array - { - return []; - } - - /** - * @return array - */ - protected function allowedFields(): array - { - return [ - // TODO: add fields here - ]; - } - - /** - * @return array - */ - protected function allowedFilters(): array - { - return []; - } - - /** - * @return array - */ - protected function allowedIncludes(): array - { - return []; - } - - /** - * @return array - */ - protected function defaultIncludes(): array - { - return []; - } - - /** - * @return array - */ - protected function allowedSorts(): array - { - return []; - } - - /** - * @return array - */ - protected function defaultSorts(): array - { - return []; - } -} diff --git a/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_can_set_custom_model__17.txt b/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_can_set_custom_model__17.txt deleted file mode 100644 index 0b53eb9..0000000 --- a/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_can_set_custom_model__17.txt +++ /dev/null @@ -1,19 +0,0 @@ -validated()); - } -} diff --git a/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_can_set_custom_model__19.txt b/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_can_set_custom_model__19.txt deleted file mode 100644 index beac85c..0000000 --- a/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_can_set_custom_model__19.txt +++ /dev/null @@ -1,28 +0,0 @@ -route('post'); - return $post && Gate::check('update', $post); - } - - public function toDTO(): UpdatePostDTO - { - return new UpdatePostDTO($this->validated()); - } -} diff --git a/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_can_set_custom_model__2.txt b/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_can_set_custom_model__2.txt deleted file mode 100644 index e2e845d..0000000 --- a/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_can_set_custom_model__2.txt +++ /dev/null @@ -1,15 +0,0 @@ -{ - "name": "example/blog", - "description": "", - "authors": [ - { - "name": "Example name", - "email": "example@example.com" - } - ], - "autoload": { - "psr-4": { - "App\\Modules\\Blog\\": "" - } - } -} diff --git a/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_can_set_custom_model__20.txt b/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_can_set_custom_model__20.txt deleted file mode 100644 index 647bf30..0000000 --- a/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_can_set_custom_model__20.txt +++ /dev/null @@ -1,20 +0,0 @@ -route('post'); - return $post && Gate::check('delete', $post); - } -} diff --git a/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_can_set_custom_model__21.txt b/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_can_set_custom_model__21.txt deleted file mode 100644 index daed175..0000000 --- a/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_can_set_custom_model__21.txt +++ /dev/null @@ -1,20 +0,0 @@ -route('post'); - return $post && Gate::check('view', $post); - } -} diff --git a/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_can_set_custom_model__22.txt b/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_can_set_custom_model__22.txt deleted file mode 100644 index 4180656..0000000 --- a/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_can_set_custom_model__22.txt +++ /dev/null @@ -1,20 +0,0 @@ -name('api.posts.create'); diff --git a/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_can_set_custom_model__24.txt b/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_can_set_custom_model__24.txt deleted file mode 100644 index 5698164..0000000 --- a/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_can_set_custom_model__24.txt +++ /dev/null @@ -1,7 +0,0 @@ -name('api.posts.update'); diff --git a/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_can_set_custom_model__25.txt b/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_can_set_custom_model__25.txt deleted file mode 100644 index 40418a7..0000000 --- a/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_can_set_custom_model__25.txt +++ /dev/null @@ -1,7 +0,0 @@ -name('api.posts.delete'); diff --git a/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_can_set_custom_model__26.txt b/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_can_set_custom_model__26.txt deleted file mode 100644 index f31859f..0000000 --- a/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_can_set_custom_model__26.txt +++ /dev/null @@ -1,7 +0,0 @@ -name('api.posts.view'); diff --git a/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_can_set_custom_model__27.txt b/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_can_set_custom_model__27.txt deleted file mode 100644 index 5f765e2..0000000 --- a/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_can_set_custom_model__27.txt +++ /dev/null @@ -1,7 +0,0 @@ -name('api.posts.list'); diff --git a/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_can_set_custom_model__28.txt b/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_can_set_custom_model__28.txt deleted file mode 100644 index 7c27c0b..0000000 --- a/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_can_set_custom_model__28.txt +++ /dev/null @@ -1,58 +0,0 @@ - 'create-post', - 'roles' => '', - ]; - - protected function getTestData(array $mergeData = []): array - { - return array_merge([ - // TODO: add fields here - ], $mergeData); - } - - public function test_create_post(): void - { - $this->getTestingUser(); - - $data = $this->getTestData(); - - $this->postJson(route('api.posts.create'), $data) - ->assertCreated() - ->assertJson(fn (AssertableJson $json) => - $json->has('data', fn (AssertableJson $json) => - $json->has('id') - ->whereAll($data) - ->etc() - ) - ); - - $this->assertExistsModelWhereColumns(Post::class, $data); - } - - public function test_create_post_without_access(): void - { - $this->getTestingUserWithoutAccess(); - - $data = $this->getTestData(); - - $this->postJson(route('api.posts.create'), $data) - ->assertForbidden(); - } -} diff --git a/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_can_set_custom_model__29.txt b/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_can_set_custom_model__29.txt deleted file mode 100644 index cc3c50b..0000000 --- a/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_can_set_custom_model__29.txt +++ /dev/null @@ -1,74 +0,0 @@ - 'update-post', - 'roles' => '', - ]; - - protected function getTestData(array $mergeData = []): array - { - return array_merge([ - // TODO: add fields here - ], $mergeData); - } - - public function test_update_post(): void - { - $this->getTestingUser(); - - $post = Post::factory()->create(); - - $data = $this->getTestData(); - $expectedData = array_merge($data, [ - 'id' => $post->getKey(), - ]); - - $this->patchJson(route('api.posts.update', ['post' => $post->getKey()]), $data) - ->assertOk() - ->assertJson(fn (AssertableJson $json) => - $json->has('data', fn (AssertableJson $json) => - $json->whereAll($expectedData) - ->etc() - ) - ); - - $this->assertExistsModelWhereColumns(Post::class, $expectedData); - } - - public function test_update_post_without_access(): void - { - $this->getTestingUserWithoutAccess(); - - $post = Post::factory()->create(); - - $data = $this->getTestData(); - - $this->patchJson(route('api.posts.update', ['post' => $post->getKey()]), $data) - ->assertForbidden(); - } - - public function test_update_non_existing_post(): void - { - $this->getTestingUser(); - - $data = $this->getTestData(); - - $this->patchJson(route('api.posts.update', ['post' => 7777]), $data) - ->assertNotFound(); - } -} diff --git a/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_can_set_custom_model__3.txt b/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_can_set_custom_model__3.txt deleted file mode 100644 index 514b69d..0000000 --- a/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_can_set_custom_model__3.txt +++ /dev/null @@ -1,25 +0,0 @@ -all()); - } - - public function asController(CreatePostRequest $request): JsonResponse - { - $post = $this->handle($request->toDTO()); - - return (new PostResource($post))->created(); - } -} diff --git a/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_can_set_custom_model__30.txt b/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_can_set_custom_model__30.txt deleted file mode 100644 index 8f7923b..0000000 --- a/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_can_set_custom_model__30.txt +++ /dev/null @@ -1,51 +0,0 @@ - 'delete-post', - 'roles' => '', - ]; - - public function test_delete_post(): void - { - $this->getTestingUser(); - - $post = Post::factory()->create(); - - $this->deleteJson(route('api.posts.delete', ['post' => $post->getKey()])) - ->assertNoContent(); - - $this->assertNull(Post::find($post->getKey())); - } - - public function test_delete_post_without_access(): void - { - $this->getTestingUserWithoutAccess(); - - $post = Post::factory()->create(); - - $this->deleteJson(route('api.posts.delete', ['post' => $post->getKey()])) - ->assertForbidden(); - } - - public function test_delete_not_existing_post(): void - { - $this->getTestingUser(); - - $this->deleteJson(route('api.posts.delete', ['post' => 7777])) - ->assertNotFound(); - } -} diff --git a/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_can_set_custom_model__31.txt b/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_can_set_custom_model__31.txt deleted file mode 100644 index e748d4c..0000000 --- a/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_can_set_custom_model__31.txt +++ /dev/null @@ -1,57 +0,0 @@ - 'view-post', - 'roles' => '', - ]; - - public function test_view_post(): void - { - $this->getTestingUser(); - - $post = Post::factory()->create(); - $expectedData = $post->toArray(); - - $this->getJson(route('api.posts.view', ['post' => $post->getKey()])) - ->assertOk() - ->assertJson(fn (AssertableJson $json) => - $json->has('data', fn (AssertableJson $json) => - $json->whereAll($expectedData) - ->etc() - ) - ); - } - - public function test_view_post_without_access(): void - { - $this->getTestingUserWithoutAccess(); - - $post = Post::factory()->create(); - - $this->getJson(route('api.posts.view', ['post' => $post->getKey()])) - ->assertForbidden(); - } - - public function test_view_not_existing_post(): void - { - $this->getTestingUser(); - - $this->getJson(route('api.posts.view', ['post' => 7777])) - ->assertNotFound(); - } -} diff --git a/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_can_set_custom_model__32.txt b/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_can_set_custom_model__32.txt deleted file mode 100644 index 00c9255..0000000 --- a/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_can_set_custom_model__32.txt +++ /dev/null @@ -1,47 +0,0 @@ - 'view-post', - 'roles' => '', - ]; - - public function test_list_posts(): void - { - $this->getTestingUser(); - - Post::factory()->count(2)->create(); - - $this->getJson(route('api.posts.list')) - ->assertOk() - ->assertJsonStructure([ - 'links', - 'meta', - 'data' - ]) - ->assertJsonCount(Post::query()->count(), 'data'); - } - - public function test_list_posts_without_access(): void - { - $this->getTestingUserWithoutAccess(); - - Post::factory()->count(2)->create(); - - $this->getJson(route('api.posts.list')) - ->assertForbidden(); - } -} diff --git a/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_can_set_custom_model__33.txt b/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_can_set_custom_model__33.txt deleted file mode 100644 index 4c6d30a..0000000 --- a/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_can_set_custom_model__33.txt +++ /dev/null @@ -1,25 +0,0 @@ -all()); - } - - public function asController(CreateCommentRequest $request): JsonResponse - { - $comment = $this->handle($request->toDTO()); - - return (new CommentResource($comment))->created(); - } -} diff --git a/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_can_set_custom_model__34.txt b/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_can_set_custom_model__34.txt deleted file mode 100644 index 5fe31fe..0000000 --- a/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_can_set_custom_model__34.txt +++ /dev/null @@ -1,33 +0,0 @@ -all(); - - if (empty($data)) { - throw new UpdateResourceFailedException(); - } - - $comment->update($data); - - return $comment; - } - - public function asController(UpdateCommentRequest $request, Comment $comment): CommentResource - { - $comment = $this->handle($comment, $request->toDTO()); - - return new CommentResource($comment); - } -} diff --git a/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_can_set_custom_model__35.txt b/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_can_set_custom_model__35.txt deleted file mode 100644 index 6facba3..0000000 --- a/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_can_set_custom_model__35.txt +++ /dev/null @@ -1,23 +0,0 @@ -delete(); - } - - public function asController(DeleteCommentRequest $request, Comment $comment): JsonResponse - { - $this->handle($comment); - - return $this->noContent(); - } -} diff --git a/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_can_set_custom_model__36.txt b/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_can_set_custom_model__36.txt deleted file mode 100644 index 878e02d..0000000 --- a/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_can_set_custom_model__36.txt +++ /dev/null @@ -1,23 +0,0 @@ -build(); - } - - public function asController(ViewCommentRequest $request, Comment $comment): CommentResource - { - return new CommentResource($this->handle($request, $comment)); - } -} diff --git a/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_can_set_custom_model__37.txt b/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_can_set_custom_model__37.txt deleted file mode 100644 index f4ec852..0000000 --- a/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_can_set_custom_model__37.txt +++ /dev/null @@ -1,26 +0,0 @@ -build() - ->jsonPaginate(); - } - - public function asController(ListCommentsRequest $request): ResourceCollection - { - return CommentResource::collection($this->handle($request)); - } -} diff --git a/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_can_set_custom_model__38.txt b/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_can_set_custom_model__38.txt deleted file mode 100644 index ffdce7c..0000000 --- a/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_can_set_custom_model__38.txt +++ /dev/null @@ -1,34 +0,0 @@ -handle(new CreatePermissionDTO( - name: 'view-comment', - display_name: 'View any "comments"', - group: 'comments' - )); - - $createPermissionAction->handle(new CreatePermissionDTO( - name: 'create-comment', - display_name: 'Create "comments"', - group: 'comments' - )); - - $createPermissionAction->handle(new CreatePermissionDTO( - name: 'update-comment', - display_name: 'Update any "comments"', - group: 'comments' - )); - - $createPermissionAction->handle(new CreatePermissionDTO( - name: 'delete-comment', - display_name: 'Delete any "comments"', - group: 'comments' - )); - - $createPermissionAction->handle(new CreatePermissionDTO( - name: 'force-delete-comment', - display_name: 'Force delete any "comments"', - group: 'comments' - )); - } -} diff --git a/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_can_set_custom_model__4.txt b/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_can_set_custom_model__4.txt deleted file mode 100644 index 91b133e..0000000 --- a/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_can_set_custom_model__4.txt +++ /dev/null @@ -1,33 +0,0 @@ -all(); - - if (empty($data)) { - throw new UpdateResourceFailedException(); - } - - $post->update($data); - - return $post; - } - - public function asController(UpdatePostRequest $request, Post $post): PostResource - { - $post = $this->handle($post, $request->toDTO()); - - return new PostResource($post); - } -} diff --git a/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_can_set_custom_model__40.txt b/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_can_set_custom_model__40.txt deleted file mode 100644 index 4ffab35..0000000 --- a/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_can_set_custom_model__40.txt +++ /dev/null @@ -1,10 +0,0 @@ -can('view-comment'); - } - - /** - * Determine whether the user can view the model. - */ - public function view(User $user, Comment $comment): bool - { - return $user->can('view-comment'); - } - - /** - * Determine whether the user can create models. - */ - public function create(User $user): bool - { - return $user->can('create-comment'); - } - - /** - * Determine whether the user can update the model. - */ - public function update(User $user, Comment $comment): bool - { - return $user->can('update-comment'); - } - - /** - * Determine whether the user can delete the model. - */ - public function delete(User $user, Comment $comment): bool - { - return $user->can('delete-comment'); - } - - /** - * Determine whether the user can restore the model. - */ - public function restore(User $user, Comment $comment): bool - { - return $user->can('delete-comment'); - } - - /** - * Determine whether the user can permanently delete the model. - */ - public function forceDelete(User $user, Comment $comment): bool - { - return $user->can('force-delete-comment'); - } -} diff --git a/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_can_set_custom_model__43.txt b/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_can_set_custom_model__43.txt deleted file mode 100644 index dca9938..0000000 --- a/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_can_set_custom_model__43.txt +++ /dev/null @@ -1,99 +0,0 @@ -registerMigrations(); - // $this->registerTranslations(); - // $this->registerCommands(); - // $this->registerViews(); - } - - /** - * Get the services provided by the provider. - */ - public function provides(): array - { - return []; - } - - /** - * Register translations. - */ - public function registerTranslations(): void - { - $sourcePath = module_path($this->moduleName, 'Resources/lang'); - $langPath = resource_path('lang/modules/' . $this->moduleKey); - - $this->loadTranslationsFrom($sourcePath, $this->moduleKey); - - $this->publishes([ - $sourcePath => $langPath, - ], 'translations'); - } - - /** - * Register views. - */ - public function registerViews(): void - { - $sourcePath = module_path($this->moduleName, 'Resources/views'); - $viewsPath = resource_path('views/modules/' . $this->moduleKey); - - $this->loadViewsFrom( - array_merge($this->getPublishableViewPaths($this->moduleKey), [$sourcePath]), - $this->moduleKey - ); - - $this->publishes([ - $sourcePath => $viewsPath - ], 'views'); - } - - /** - * Register migrations. - */ - public function registerMigrations(): void - { - $sourcePath = module_path($this->moduleName, 'Data/Migrations'); - $migrationsPath = database_path('migrations'); - - $this->loadMigrationsFrom($sourcePath); - - $this->publishes([ - $sourcePath => $migrationsPath - ], 'migrations'); - } - - /** - * Register artisan commands. - */ - public function registerCommands(): void - { - if ($this->app->runningInConsole()) { - $this->loadCommands(module_path($this->moduleName, 'UI/CLI/Commands')); - } - } -} diff --git a/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_can_set_custom_model__46.txt b/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_can_set_custom_model__46.txt deleted file mode 100644 index ccd452c..0000000 --- a/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_can_set_custom_model__46.txt +++ /dev/null @@ -1,77 +0,0 @@ - - */ - protected function allowedAppends(): array - { - return []; - } - - /** - * @return array - */ - protected function defaultAppends(): array - { - return []; - } - - /** - * @return array - */ - protected function allowedFields(): array - { - return [ - // TODO: add fields here - ]; - } - - /** - * @return array - */ - protected function allowedFilters(): array - { - return []; - } - - /** - * @return array - */ - protected function allowedIncludes(): array - { - return []; - } - - /** - * @return array - */ - protected function defaultIncludes(): array - { - return []; - } - - /** - * @return array - */ - protected function allowedSorts(): array - { - return []; - } - - /** - * @return array - */ - protected function defaultSorts(): array - { - return []; - } -} diff --git a/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_can_set_custom_model__47.txt b/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_can_set_custom_model__47.txt deleted file mode 100644 index 86ac883..0000000 --- a/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_can_set_custom_model__47.txt +++ /dev/null @@ -1,19 +0,0 @@ -validated()); - } -} diff --git a/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_can_set_custom_model__49.txt b/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_can_set_custom_model__49.txt deleted file mode 100644 index bfc4a94..0000000 --- a/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_can_set_custom_model__49.txt +++ /dev/null @@ -1,28 +0,0 @@ -route('comment'); - return $comment && Gate::check('update', $comment); - } - - public function toDTO(): UpdateCommentDTO - { - return new UpdateCommentDTO($this->validated()); - } -} diff --git a/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_can_set_custom_model__5.txt b/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_can_set_custom_model__5.txt deleted file mode 100644 index 4cd3469..0000000 --- a/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_can_set_custom_model__5.txt +++ /dev/null @@ -1,23 +0,0 @@ -delete(); - } - - public function asController(DeletePostRequest $request, Post $post): JsonResponse - { - $this->handle($post); - - return $this->noContent(); - } -} diff --git a/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_can_set_custom_model__50.txt b/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_can_set_custom_model__50.txt deleted file mode 100644 index f6bc3e6..0000000 --- a/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_can_set_custom_model__50.txt +++ /dev/null @@ -1,20 +0,0 @@ -route('comment'); - return $comment && Gate::check('delete', $comment); - } -} diff --git a/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_can_set_custom_model__51.txt b/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_can_set_custom_model__51.txt deleted file mode 100644 index 9a4ebd8..0000000 --- a/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_can_set_custom_model__51.txt +++ /dev/null @@ -1,20 +0,0 @@ -route('comment'); - return $comment && Gate::check('view', $comment); - } -} diff --git a/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_can_set_custom_model__52.txt b/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_can_set_custom_model__52.txt deleted file mode 100644 index 973fe91..0000000 --- a/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_can_set_custom_model__52.txt +++ /dev/null @@ -1,20 +0,0 @@ -name('api.comments.create'); diff --git a/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_can_set_custom_model__54.txt b/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_can_set_custom_model__54.txt deleted file mode 100644 index 745efd1..0000000 --- a/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_can_set_custom_model__54.txt +++ /dev/null @@ -1,7 +0,0 @@ -name('api.comments.update'); diff --git a/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_can_set_custom_model__55.txt b/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_can_set_custom_model__55.txt deleted file mode 100644 index 84831ce..0000000 --- a/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_can_set_custom_model__55.txt +++ /dev/null @@ -1,7 +0,0 @@ -name('api.comments.delete'); diff --git a/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_can_set_custom_model__56.txt b/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_can_set_custom_model__56.txt deleted file mode 100644 index b330ee7..0000000 --- a/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_can_set_custom_model__56.txt +++ /dev/null @@ -1,7 +0,0 @@ -name('api.comments.view'); diff --git a/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_can_set_custom_model__57.txt b/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_can_set_custom_model__57.txt deleted file mode 100644 index d666711..0000000 --- a/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_can_set_custom_model__57.txt +++ /dev/null @@ -1,7 +0,0 @@ -name('api.comments.list'); diff --git a/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_can_set_custom_model__58.txt b/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_can_set_custom_model__58.txt deleted file mode 100644 index 2c51daf..0000000 --- a/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_can_set_custom_model__58.txt +++ /dev/null @@ -1,58 +0,0 @@ - 'create-comment', - 'roles' => '', - ]; - - protected function getTestData(array $mergeData = []): array - { - return array_merge([ - // TODO: add fields here - ], $mergeData); - } - - public function test_create_comment(): void - { - $this->getTestingUser(); - - $data = $this->getTestData(); - - $this->postJson(route('api.comments.create'), $data) - ->assertCreated() - ->assertJson(fn (AssertableJson $json) => - $json->has('data', fn (AssertableJson $json) => - $json->has('id') - ->whereAll($data) - ->etc() - ) - ); - - $this->assertExistsModelWhereColumns(Comment::class, $data); - } - - public function test_create_comment_without_access(): void - { - $this->getTestingUserWithoutAccess(); - - $data = $this->getTestData(); - - $this->postJson(route('api.comments.create'), $data) - ->assertForbidden(); - } -} diff --git a/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_can_set_custom_model__59.txt b/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_can_set_custom_model__59.txt deleted file mode 100644 index 1de9a34..0000000 --- a/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_can_set_custom_model__59.txt +++ /dev/null @@ -1,74 +0,0 @@ - 'update-comment', - 'roles' => '', - ]; - - protected function getTestData(array $mergeData = []): array - { - return array_merge([ - // TODO: add fields here - ], $mergeData); - } - - public function test_update_comment(): void - { - $this->getTestingUser(); - - $comment = Comment::factory()->create(); - - $data = $this->getTestData(); - $expectedData = array_merge($data, [ - 'id' => $comment->getKey(), - ]); - - $this->patchJson(route('api.comments.update', ['comment' => $comment->getKey()]), $data) - ->assertOk() - ->assertJson(fn (AssertableJson $json) => - $json->has('data', fn (AssertableJson $json) => - $json->whereAll($expectedData) - ->etc() - ) - ); - - $this->assertExistsModelWhereColumns(Comment::class, $expectedData); - } - - public function test_update_comment_without_access(): void - { - $this->getTestingUserWithoutAccess(); - - $comment = Comment::factory()->create(); - - $data = $this->getTestData(); - - $this->patchJson(route('api.comments.update', ['comment' => $comment->getKey()]), $data) - ->assertForbidden(); - } - - public function test_update_non_existing_comment(): void - { - $this->getTestingUser(); - - $data = $this->getTestData(); - - $this->patchJson(route('api.comments.update', ['comment' => 7777]), $data) - ->assertNotFound(); - } -} diff --git a/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_can_set_custom_model__6.txt b/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_can_set_custom_model__6.txt deleted file mode 100644 index 5187ca6..0000000 --- a/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_can_set_custom_model__6.txt +++ /dev/null @@ -1,23 +0,0 @@ -build(); - } - - public function asController(ViewPostRequest $request, Post $post): PostResource - { - return new PostResource($this->handle($request, $post)); - } -} diff --git a/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_can_set_custom_model__60.txt b/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_can_set_custom_model__60.txt deleted file mode 100644 index 583638f..0000000 --- a/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_can_set_custom_model__60.txt +++ /dev/null @@ -1,51 +0,0 @@ - 'delete-comment', - 'roles' => '', - ]; - - public function test_delete_comment(): void - { - $this->getTestingUser(); - - $comment = Comment::factory()->create(); - - $this->deleteJson(route('api.comments.delete', ['comment' => $comment->getKey()])) - ->assertNoContent(); - - $this->assertNull(Comment::find($comment->getKey())); - } - - public function test_delete_comment_without_access(): void - { - $this->getTestingUserWithoutAccess(); - - $comment = Comment::factory()->create(); - - $this->deleteJson(route('api.comments.delete', ['comment' => $comment->getKey()])) - ->assertForbidden(); - } - - public function test_delete_not_existing_comment(): void - { - $this->getTestingUser(); - - $this->deleteJson(route('api.comments.delete', ['comment' => 7777])) - ->assertNotFound(); - } -} diff --git a/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_can_set_custom_model__61.txt b/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_can_set_custom_model__61.txt deleted file mode 100644 index bf55c65..0000000 --- a/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_can_set_custom_model__61.txt +++ /dev/null @@ -1,57 +0,0 @@ - 'view-comment', - 'roles' => '', - ]; - - public function test_view_comment(): void - { - $this->getTestingUser(); - - $comment = Comment::factory()->create(); - $expectedData = $comment->toArray(); - - $this->getJson(route('api.comments.view', ['comment' => $comment->getKey()])) - ->assertOk() - ->assertJson(fn (AssertableJson $json) => - $json->has('data', fn (AssertableJson $json) => - $json->whereAll($expectedData) - ->etc() - ) - ); - } - - public function test_view_comment_without_access(): void - { - $this->getTestingUserWithoutAccess(); - - $comment = Comment::factory()->create(); - - $this->getJson(route('api.comments.view', ['comment' => $comment->getKey()])) - ->assertForbidden(); - } - - public function test_view_not_existing_comment(): void - { - $this->getTestingUser(); - - $this->getJson(route('api.comments.view', ['comment' => 7777])) - ->assertNotFound(); - } -} diff --git a/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_can_set_custom_model__62.txt b/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_can_set_custom_model__62.txt deleted file mode 100644 index 776fd21..0000000 --- a/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_can_set_custom_model__62.txt +++ /dev/null @@ -1,47 +0,0 @@ - 'view-comment', - 'roles' => '', - ]; - - public function test_list_comments(): void - { - $this->getTestingUser(); - - Comment::factory()->count(2)->create(); - - $this->getJson(route('api.comments.list')) - ->assertOk() - ->assertJsonStructure([ - 'links', - 'meta', - 'data' - ]) - ->assertJsonCount(Comment::query()->count(), 'data'); - } - - public function test_list_comments_without_access(): void - { - $this->getTestingUserWithoutAccess(); - - Comment::factory()->count(2)->create(); - - $this->getJson(route('api.comments.list')) - ->assertForbidden(); - } -} diff --git a/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_can_set_custom_model__7.txt b/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_can_set_custom_model__7.txt deleted file mode 100644 index 464b945..0000000 --- a/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_can_set_custom_model__7.txt +++ /dev/null @@ -1,26 +0,0 @@ -build() - ->jsonPaginate(); - } - - public function asController(ListPostsRequest $request): ResourceCollection - { - return PostResource::collection($this->handle($request)); - } -} diff --git a/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_can_set_custom_model__8.txt b/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_can_set_custom_model__8.txt deleted file mode 100644 index 4b8a96e..0000000 --- a/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_can_set_custom_model__8.txt +++ /dev/null @@ -1,34 +0,0 @@ -handle(new CreatePermissionDTO( - name: 'view-post', - display_name: 'View any "posts"', - group: 'posts' - )); - - $createPermissionAction->handle(new CreatePermissionDTO( - name: 'create-post', - display_name: 'Create "posts"', - group: 'posts' - )); - - $createPermissionAction->handle(new CreatePermissionDTO( - name: 'update-post', - display_name: 'Update any "posts"', - group: 'posts' - )); - - $createPermissionAction->handle(new CreatePermissionDTO( - name: 'delete-post', - display_name: 'Delete any "posts"', - group: 'posts' - )); - - $createPermissionAction->handle(new CreatePermissionDTO( - name: 'force-delete-post', - display_name: 'Force delete any "posts"', - group: 'posts' - )); - } -} diff --git a/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_generates_module_components__1.txt b/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_generates_module_components__1.txt deleted file mode 100644 index f605374..0000000 --- a/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_generates_module_components__1.txt +++ /dev/null @@ -1,25 +0,0 @@ -all()); - } - - public function asController(CreateArticleRequest $request): JsonResponse - { - $article = $this->handle($request->toDTO()); - - return (new ArticleResource($article))->created(); - } -} diff --git a/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_generates_module_components__10.txt b/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_generates_module_components__10.txt deleted file mode 100644 index cd3c04a..0000000 --- a/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_generates_module_components__10.txt +++ /dev/null @@ -1,69 +0,0 @@ -can('view-article'); - } - - /** - * Determine whether the user can view the model. - */ - public function view(User $user, Article $article): bool - { - return $user->can('view-article'); - } - - /** - * Determine whether the user can create models. - */ - public function create(User $user): bool - { - return $user->can('create-article'); - } - - /** - * Determine whether the user can update the model. - */ - public function update(User $user, Article $article): bool - { - return $user->can('update-article'); - } - - /** - * Determine whether the user can delete the model. - */ - public function delete(User $user, Article $article): bool - { - return $user->can('delete-article'); - } - - /** - * Determine whether the user can restore the model. - */ - public function restore(User $user, Article $article): bool - { - return $user->can('delete-article'); - } - - /** - * Determine whether the user can permanently delete the model. - */ - public function forceDelete(User $user, Article $article): bool - { - return $user->can('force-delete-article'); - } -} diff --git a/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_generates_module_components__11.txt b/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_generates_module_components__11.txt deleted file mode 100644 index 9cb9660..0000000 --- a/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_generates_module_components__11.txt +++ /dev/null @@ -1,99 +0,0 @@ -registerMigrations(); - // $this->registerTranslations(); - // $this->registerCommands(); - // $this->registerViews(); - } - - /** - * Get the services provided by the provider. - */ - public function provides(): array - { - return []; - } - - /** - * Register translations. - */ - public function registerTranslations(): void - { - $sourcePath = module_path($this->moduleName, 'Resources/lang'); - $langPath = resource_path('lang/modules/' . $this->moduleKey); - - $this->loadTranslationsFrom($sourcePath, $this->moduleKey); - - $this->publishes([ - $sourcePath => $langPath, - ], 'translations'); - } - - /** - * Register views. - */ - public function registerViews(): void - { - $sourcePath = module_path($this->moduleName, 'Resources/views'); - $viewsPath = resource_path('views/modules/' . $this->moduleKey); - - $this->loadViewsFrom( - array_merge($this->getPublishableViewPaths($this->moduleKey), [$sourcePath]), - $this->moduleKey - ); - - $this->publishes([ - $sourcePath => $viewsPath - ], 'views'); - } - - /** - * Register migrations. - */ - public function registerMigrations(): void - { - $sourcePath = module_path($this->moduleName, 'Data/Migrations'); - $migrationsPath = database_path('migrations'); - - $this->loadMigrationsFrom($sourcePath); - - $this->publishes([ - $sourcePath => $migrationsPath - ], 'migrations'); - } - - /** - * Register artisan commands. - */ - public function registerCommands(): void - { - if ($this->app->runningInConsole()) { - $this->loadCommands(module_path($this->moduleName, 'UI/CLI/Commands')); - } - } -} diff --git a/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_generates_module_components__15.txt b/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_generates_module_components__15.txt deleted file mode 100644 index 0dcdf06..0000000 --- a/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_generates_module_components__15.txt +++ /dev/null @@ -1,19 +0,0 @@ -validated()); - } -} diff --git a/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_generates_module_components__17.txt b/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_generates_module_components__17.txt deleted file mode 100644 index c4cc41c..0000000 --- a/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_generates_module_components__17.txt +++ /dev/null @@ -1,28 +0,0 @@ -route('article'); - return $article && Gate::check('update', $article); - } - - public function toDTO(): UpdateArticleDTO - { - return new UpdateArticleDTO($this->validated()); - } -} diff --git a/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_generates_module_components__18.txt b/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_generates_module_components__18.txt deleted file mode 100644 index 8d99840..0000000 --- a/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_generates_module_components__18.txt +++ /dev/null @@ -1,20 +0,0 @@ -route('article'); - return $article && Gate::check('delete', $article); - } -} diff --git a/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_generates_module_components__19.txt b/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_generates_module_components__19.txt deleted file mode 100644 index 358aa05..0000000 --- a/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_generates_module_components__19.txt +++ /dev/null @@ -1,20 +0,0 @@ -route('article'); - return $article && Gate::check('view', $article); - } -} diff --git a/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_generates_module_components__2.txt b/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_generates_module_components__2.txt deleted file mode 100644 index 6dd1d52..0000000 --- a/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_generates_module_components__2.txt +++ /dev/null @@ -1,33 +0,0 @@ -all(); - - if (empty($data)) { - throw new UpdateResourceFailedException(); - } - - $article->update($data); - - return $article; - } - - public function asController(UpdateArticleRequest $request, Article $article): ArticleResource - { - $article = $this->handle($article, $request->toDTO()); - - return new ArticleResource($article); - } -} diff --git a/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_generates_module_components__20.txt b/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_generates_module_components__20.txt deleted file mode 100644 index d663831..0000000 --- a/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_generates_module_components__20.txt +++ /dev/null @@ -1,20 +0,0 @@ -name('api.articles.create'); diff --git a/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_generates_module_components__22.txt b/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_generates_module_components__22.txt deleted file mode 100644 index 7e76fc3..0000000 --- a/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_generates_module_components__22.txt +++ /dev/null @@ -1,7 +0,0 @@ -name('api.articles.update'); diff --git a/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_generates_module_components__23.txt b/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_generates_module_components__23.txt deleted file mode 100644 index cceba8f..0000000 --- a/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_generates_module_components__23.txt +++ /dev/null @@ -1,7 +0,0 @@ -name('api.articles.delete'); diff --git a/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_generates_module_components__24.txt b/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_generates_module_components__24.txt deleted file mode 100644 index 98fedcf..0000000 --- a/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_generates_module_components__24.txt +++ /dev/null @@ -1,7 +0,0 @@ -name('api.articles.view'); diff --git a/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_generates_module_components__25.txt b/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_generates_module_components__25.txt deleted file mode 100644 index 093f946..0000000 --- a/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_generates_module_components__25.txt +++ /dev/null @@ -1,7 +0,0 @@ -name('api.articles.list'); diff --git a/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_generates_module_components__26.txt b/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_generates_module_components__26.txt deleted file mode 100644 index 02c4e58..0000000 --- a/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_generates_module_components__26.txt +++ /dev/null @@ -1,58 +0,0 @@ - 'create-article', - 'roles' => '', - ]; - - protected function getTestData(array $mergeData = []): array - { - return array_merge([ - // TODO: add fields here - ], $mergeData); - } - - public function test_create_article(): void - { - $this->getTestingUser(); - - $data = $this->getTestData(); - - $this->postJson(route('api.articles.create'), $data) - ->assertCreated() - ->assertJson(fn (AssertableJson $json) => - $json->has('data', fn (AssertableJson $json) => - $json->has('id') - ->whereAll($data) - ->etc() - ) - ); - - $this->assertExistsModelWhereColumns(Article::class, $data); - } - - public function test_create_article_without_access(): void - { - $this->getTestingUserWithoutAccess(); - - $data = $this->getTestData(); - - $this->postJson(route('api.articles.create'), $data) - ->assertForbidden(); - } -} diff --git a/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_generates_module_components__27.txt b/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_generates_module_components__27.txt deleted file mode 100644 index 2ae1db3..0000000 --- a/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_generates_module_components__27.txt +++ /dev/null @@ -1,74 +0,0 @@ - 'update-article', - 'roles' => '', - ]; - - protected function getTestData(array $mergeData = []): array - { - return array_merge([ - // TODO: add fields here - ], $mergeData); - } - - public function test_update_article(): void - { - $this->getTestingUser(); - - $article = Article::factory()->create(); - - $data = $this->getTestData(); - $expectedData = array_merge($data, [ - 'id' => $article->getKey(), - ]); - - $this->patchJson(route('api.articles.update', ['article' => $article->getKey()]), $data) - ->assertOk() - ->assertJson(fn (AssertableJson $json) => - $json->has('data', fn (AssertableJson $json) => - $json->whereAll($expectedData) - ->etc() - ) - ); - - $this->assertExistsModelWhereColumns(Article::class, $expectedData); - } - - public function test_update_article_without_access(): void - { - $this->getTestingUserWithoutAccess(); - - $article = Article::factory()->create(); - - $data = $this->getTestData(); - - $this->patchJson(route('api.articles.update', ['article' => $article->getKey()]), $data) - ->assertForbidden(); - } - - public function test_update_non_existing_article(): void - { - $this->getTestingUser(); - - $data = $this->getTestData(); - - $this->patchJson(route('api.articles.update', ['article' => 7777]), $data) - ->assertNotFound(); - } -} diff --git a/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_generates_module_components__28.txt b/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_generates_module_components__28.txt deleted file mode 100644 index 76e7943..0000000 --- a/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_generates_module_components__28.txt +++ /dev/null @@ -1,51 +0,0 @@ - 'delete-article', - 'roles' => '', - ]; - - public function test_delete_article(): void - { - $this->getTestingUser(); - - $article = Article::factory()->create(); - - $this->deleteJson(route('api.articles.delete', ['article' => $article->getKey()])) - ->assertNoContent(); - - $this->assertNull(Article::find($article->getKey())); - } - - public function test_delete_article_without_access(): void - { - $this->getTestingUserWithoutAccess(); - - $article = Article::factory()->create(); - - $this->deleteJson(route('api.articles.delete', ['article' => $article->getKey()])) - ->assertForbidden(); - } - - public function test_delete_not_existing_article(): void - { - $this->getTestingUser(); - - $this->deleteJson(route('api.articles.delete', ['article' => 7777])) - ->assertNotFound(); - } -} diff --git a/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_generates_module_components__29.txt b/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_generates_module_components__29.txt deleted file mode 100644 index 5450de3..0000000 --- a/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_generates_module_components__29.txt +++ /dev/null @@ -1,57 +0,0 @@ - 'view-article', - 'roles' => '', - ]; - - public function test_view_article(): void - { - $this->getTestingUser(); - - $article = Article::factory()->create(); - $expectedData = $article->toArray(); - - $this->getJson(route('api.articles.view', ['article' => $article->getKey()])) - ->assertOk() - ->assertJson(fn (AssertableJson $json) => - $json->has('data', fn (AssertableJson $json) => - $json->whereAll($expectedData) - ->etc() - ) - ); - } - - public function test_view_article_without_access(): void - { - $this->getTestingUserWithoutAccess(); - - $article = Article::factory()->create(); - - $this->getJson(route('api.articles.view', ['article' => $article->getKey()])) - ->assertForbidden(); - } - - public function test_view_not_existing_article(): void - { - $this->getTestingUser(); - - $this->getJson(route('api.articles.view', ['article' => 7777])) - ->assertNotFound(); - } -} diff --git a/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_generates_module_components__3.txt b/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_generates_module_components__3.txt deleted file mode 100644 index ddf1544..0000000 --- a/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_generates_module_components__3.txt +++ /dev/null @@ -1,23 +0,0 @@ -delete(); - } - - public function asController(DeleteArticleRequest $request, Article $article): JsonResponse - { - $this->handle($article); - - return $this->noContent(); - } -} diff --git a/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_generates_module_components__30.txt b/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_generates_module_components__30.txt deleted file mode 100644 index adfb1ec..0000000 --- a/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_generates_module_components__30.txt +++ /dev/null @@ -1,47 +0,0 @@ - 'view-article', - 'roles' => '', - ]; - - public function test_list_articles(): void - { - $this->getTestingUser(); - - Article::factory()->count(2)->create(); - - $this->getJson(route('api.articles.list')) - ->assertOk() - ->assertJsonStructure([ - 'links', - 'meta', - 'data' - ]) - ->assertJsonCount(Article::query()->count(), 'data'); - } - - public function test_list_articles_without_access(): void - { - $this->getTestingUserWithoutAccess(); - - Article::factory()->count(2)->create(); - - $this->getJson(route('api.articles.list')) - ->assertForbidden(); - } -} diff --git a/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_generates_module_components__4.txt b/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_generates_module_components__4.txt deleted file mode 100644 index 9d2ab5c..0000000 --- a/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_generates_module_components__4.txt +++ /dev/null @@ -1,23 +0,0 @@ -build(); - } - - public function asController(ViewArticleRequest $request, Article $article): ArticleResource - { - return new ArticleResource($this->handle($request, $article)); - } -} diff --git a/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_generates_module_components__5.txt b/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_generates_module_components__5.txt deleted file mode 100644 index 734c60d..0000000 --- a/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_generates_module_components__5.txt +++ /dev/null @@ -1,26 +0,0 @@ -build() - ->jsonPaginate(); - } - - public function asController(ListArticlesRequest $request): ResourceCollection - { - return ArticleResource::collection($this->handle($request)); - } -} diff --git a/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_generates_module_components__6.txt b/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_generates_module_components__6.txt deleted file mode 100644 index 5f63c9d..0000000 --- a/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_generates_module_components__6.txt +++ /dev/null @@ -1,34 +0,0 @@ -handle(new CreatePermissionDTO( - name: 'view-article', - display_name: 'View any "articles"', - group: 'articles' - )); - - $createPermissionAction->handle(new CreatePermissionDTO( - name: 'create-article', - display_name: 'Create "articles"', - group: 'articles' - )); - - $createPermissionAction->handle(new CreatePermissionDTO( - name: 'update-article', - display_name: 'Update any "articles"', - group: 'articles' - )); - - $createPermissionAction->handle(new CreatePermissionDTO( - name: 'delete-article', - display_name: 'Delete any "articles"', - group: 'articles' - )); - - $createPermissionAction->handle(new CreatePermissionDTO( - name: 'force-delete-article', - display_name: 'Force delete any "articles"', - group: 'articles' - )); - } -} diff --git a/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_generates_module_components__8.txt b/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_generates_module_components__8.txt deleted file mode 100644 index 9065ecf..0000000 --- a/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_generates_module_components__8.txt +++ /dev/null @@ -1,10 +0,0 @@ -view('view.name'); - } -} diff --git a/tests/Commands/Generators/__snapshots__/MailMakeCommandTest__it_can_change_the_default_path_for_plain_mail_file__1.txt b/tests/Commands/Generators/__snapshots__/MailMakeCommandTest__it_can_change_the_default_path_for_plain_mail_file__1.txt deleted file mode 100644 index 25ba89c..0000000 --- a/tests/Commands/Generators/__snapshots__/MailMakeCommandTest__it_can_change_the_default_path_for_plain_mail_file__1.txt +++ /dev/null @@ -1,24 +0,0 @@ -view('view.name'); - } -} diff --git a/tests/Commands/Generators/__snapshots__/MailMakeCommandTest__it_generated_correct_plain_mail_file_with_content__1.txt b/tests/Commands/Generators/__snapshots__/MailMakeCommandTest__it_generated_correct_plain_mail_file_with_content__1.txt deleted file mode 100644 index 666a1d3..0000000 --- a/tests/Commands/Generators/__snapshots__/MailMakeCommandTest__it_generated_correct_plain_mail_file_with_content__1.txt +++ /dev/null @@ -1,24 +0,0 @@ -view('view.name'); - } -} diff --git a/tests/Commands/Generators/__snapshots__/MailMakeCommandTest__it_generated_correct_queued_mail_file_with_content__1.txt b/tests/Commands/Generators/__snapshots__/MailMakeCommandTest__it_generated_correct_queued_mail_file_with_content__1.txt deleted file mode 100644 index 5f01c07..0000000 --- a/tests/Commands/Generators/__snapshots__/MailMakeCommandTest__it_generated_correct_queued_mail_file_with_content__1.txt +++ /dev/null @@ -1,26 +0,0 @@ -view('view.name'); - } -} diff --git a/tests/Commands/Generators/__snapshots__/MiddlewareMakeCommandTest__it_can_change_the_default_namespace_for_middleware_file__1.txt b/tests/Commands/Generators/__snapshots__/MiddlewareMakeCommandTest__it_can_change_the_default_namespace_for_middleware_file__1.txt deleted file mode 100644 index 8983d22..0000000 --- a/tests/Commands/Generators/__snapshots__/MiddlewareMakeCommandTest__it_can_change_the_default_namespace_for_middleware_file__1.txt +++ /dev/null @@ -1,18 +0,0 @@ -unsignedBigInteger('user_id')->index(); - $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade'); - $table->unsignedBigInteger('role_id')->index(); - $table->foreign('role_id')->references('id')->on('roles')->onDelete('cascade'); - $table->primary(['user_id', 'role_id']); - }); - } - - /** - * Reverse the migrations. - */ - public function down(): void - { - Schema::dropIfExists('user_role'); - } -}; diff --git a/tests/Commands/Generators/__snapshots__/ModelMakeCommandTest__it_can_change_the_default_namespace_for_plain_model_file__1.txt b/tests/Commands/Generators/__snapshots__/ModelMakeCommandTest__it_can_change_the_default_namespace_for_plain_model_file__1.txt deleted file mode 100644 index 6511aef..0000000 --- a/tests/Commands/Generators/__snapshots__/ModelMakeCommandTest__it_can_change_the_default_namespace_for_plain_model_file__1.txt +++ /dev/null @@ -1,10 +0,0 @@ -can('view-post-comment'); - } - - /** - * Determine whether the user can view the model. - */ - public function view(User $user, PostComment $postComment): bool - { - return $user->can('view-post-comment'); - } - - /** - * Determine whether the user can create models. - */ - public function create(User $user): bool - { - return $user->can('create-post-comment'); - } - - /** - * Determine whether the user can update the model. - */ - public function update(User $user, PostComment $postComment): bool - { - return $user->can('update-post-comment'); - } - - /** - * Determine whether the user can delete the model. - */ - public function delete(User $user, PostComment $postComment): bool - { - return $user->can('delete-post-comment'); - } - - /** - * Determine whether the user can restore the model. - */ - public function restore(User $user, PostComment $postComment): bool - { - return $user->can('delete-post-comment'); - } - - /** - * Determine whether the user can permanently delete the model. - */ - public function forceDelete(User $user, PostComment $postComment): bool - { - return $user->can('force-delete-post-comment'); - } -} diff --git a/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_can_set_custom_model__13.txt b/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_can_set_custom_model__13.txt deleted file mode 100644 index dca9938..0000000 --- a/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_can_set_custom_model__13.txt +++ /dev/null @@ -1,99 +0,0 @@ -registerMigrations(); - // $this->registerTranslations(); - // $this->registerCommands(); - // $this->registerViews(); - } - - /** - * Get the services provided by the provider. - */ - public function provides(): array - { - return []; - } - - /** - * Register translations. - */ - public function registerTranslations(): void - { - $sourcePath = module_path($this->moduleName, 'Resources/lang'); - $langPath = resource_path('lang/modules/' . $this->moduleKey); - - $this->loadTranslationsFrom($sourcePath, $this->moduleKey); - - $this->publishes([ - $sourcePath => $langPath, - ], 'translations'); - } - - /** - * Register views. - */ - public function registerViews(): void - { - $sourcePath = module_path($this->moduleName, 'Resources/views'); - $viewsPath = resource_path('views/modules/' . $this->moduleKey); - - $this->loadViewsFrom( - array_merge($this->getPublishableViewPaths($this->moduleKey), [$sourcePath]), - $this->moduleKey - ); - - $this->publishes([ - $sourcePath => $viewsPath - ], 'views'); - } - - /** - * Register migrations. - */ - public function registerMigrations(): void - { - $sourcePath = module_path($this->moduleName, 'Data/Migrations'); - $migrationsPath = database_path('migrations'); - - $this->loadMigrationsFrom($sourcePath); - - $this->publishes([ - $sourcePath => $migrationsPath - ], 'migrations'); - } - - /** - * Register artisan commands. - */ - public function registerCommands(): void - { - if ($this->app->runningInConsole()) { - $this->loadCommands(module_path($this->moduleName, 'UI/CLI/Commands')); - } - } -} diff --git a/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_can_set_custom_model__15.txt b/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_can_set_custom_model__15.txt deleted file mode 100644 index 9052338..0000000 --- a/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_can_set_custom_model__15.txt +++ /dev/null @@ -1,51 +0,0 @@ - - */ - protected function allowedAppends(): array - { - return []; - } - - /** - * @return array - */ - protected function defaultAppends(): array - { - return []; - } - - /** - * @return array - */ - protected function allowedFields(): array - { - return [ - // TODO: add fields here - ]; - } - - /** - * @return array - */ - protected function allowedIncludes(): array - { - return []; - } - - /** - * @return array - */ - protected function defaultIncludes(): array - { - return []; - } -} diff --git a/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_can_set_custom_model__17.txt b/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_can_set_custom_model__17.txt deleted file mode 100644 index 52d0931..0000000 --- a/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_can_set_custom_model__17.txt +++ /dev/null @@ -1,19 +0,0 @@ -validated()); - } -} diff --git a/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_can_set_custom_model__19.txt b/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_can_set_custom_model__19.txt deleted file mode 100644 index 34b2104..0000000 --- a/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_can_set_custom_model__19.txt +++ /dev/null @@ -1,28 +0,0 @@ -route('postComment'); - return $postComment && Gate::check('update', $postComment); - } - - public function toDTO(): UpdatePostCommentDTO - { - return new UpdatePostCommentDTO($this->validated()); - } -} diff --git a/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_can_set_custom_model__2.txt b/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_can_set_custom_model__2.txt deleted file mode 100644 index e2e845d..0000000 --- a/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_can_set_custom_model__2.txt +++ /dev/null @@ -1,15 +0,0 @@ -{ - "name": "example/blog", - "description": "", - "authors": [ - { - "name": "Example name", - "email": "example@example.com" - } - ], - "autoload": { - "psr-4": { - "App\\Modules\\Blog\\": "" - } - } -} diff --git a/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_can_set_custom_model__20.txt b/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_can_set_custom_model__20.txt deleted file mode 100644 index d55c8eb..0000000 --- a/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_can_set_custom_model__20.txt +++ /dev/null @@ -1,20 +0,0 @@ -route('postComment'); - return $postComment && Gate::check('delete', $postComment); - } -} diff --git a/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_can_set_custom_model__21.txt b/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_can_set_custom_model__21.txt deleted file mode 100644 index 574b56e..0000000 --- a/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_can_set_custom_model__21.txt +++ /dev/null @@ -1,20 +0,0 @@ -route('postComment'); - return $postComment && Gate::check('view', $postComment); - } -} diff --git a/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_can_set_custom_model__22.txt b/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_can_set_custom_model__22.txt deleted file mode 100644 index a338831..0000000 --- a/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_can_set_custom_model__22.txt +++ /dev/null @@ -1,20 +0,0 @@ -name('api.post_comments.create'); diff --git a/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_can_set_custom_model__24.txt b/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_can_set_custom_model__24.txt deleted file mode 100644 index 5c8081a..0000000 --- a/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_can_set_custom_model__24.txt +++ /dev/null @@ -1,7 +0,0 @@ -name('api.post_comments.update'); diff --git a/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_can_set_custom_model__25.txt b/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_can_set_custom_model__25.txt deleted file mode 100644 index 601e578..0000000 --- a/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_can_set_custom_model__25.txt +++ /dev/null @@ -1,7 +0,0 @@ -name('api.post_comments.delete'); diff --git a/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_can_set_custom_model__26.txt b/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_can_set_custom_model__26.txt deleted file mode 100644 index 4bd5709..0000000 --- a/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_can_set_custom_model__26.txt +++ /dev/null @@ -1,7 +0,0 @@ -name('api.post_comments.view'); diff --git a/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_can_set_custom_model__27.txt b/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_can_set_custom_model__27.txt deleted file mode 100644 index 8711b3a..0000000 --- a/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_can_set_custom_model__27.txt +++ /dev/null @@ -1,7 +0,0 @@ -name('api.post_comments.list'); diff --git a/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_can_set_custom_model__28.txt b/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_can_set_custom_model__28.txt deleted file mode 100644 index 250f1a1..0000000 --- a/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_can_set_custom_model__28.txt +++ /dev/null @@ -1,58 +0,0 @@ - 'create-post-comment', - 'roles' => '', - ]; - - protected function getTestData(array $mergeData = []): array - { - return array_merge([ - // TODO: add fields here - ], $mergeData); - } - - public function test_create_post_comment(): void - { - $this->getTestingUser(); - - $data = $this->getTestData(); - - $this->postJson(route('api.post_comments.create'), $data) - ->assertCreated() - ->assertJson(fn (AssertableJson $json) => - $json->has('data', fn (AssertableJson $json) => - $json->has('id') - ->whereAll($data) - ->etc() - ) - ); - - $this->assertExistsModelWhereColumns(PostComment::class, $data); - } - - public function test_create_post_comment_without_access(): void - { - $this->getTestingUserWithoutAccess(); - - $data = $this->getTestData(); - - $this->postJson(route('api.post_comments.create'), $data) - ->assertForbidden(); - } -} diff --git a/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_can_set_custom_model__29.txt b/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_can_set_custom_model__29.txt deleted file mode 100644 index 196bb05..0000000 --- a/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_can_set_custom_model__29.txt +++ /dev/null @@ -1,74 +0,0 @@ - 'update-post-comment', - 'roles' => '', - ]; - - protected function getTestData(array $mergeData = []): array - { - return array_merge([ - // TODO: add fields here - ], $mergeData); - } - - public function test_update_post_comment(): void - { - $this->getTestingUser(); - - $postComment = PostComment::factory()->create(); - - $data = $this->getTestData(); - $expectedData = array_merge($data, [ - 'id' => $postComment->getKey(), - ]); - - $this->patchJson(route('api.post_comments.update', ['postComment' => $postComment->getKey()]), $data) - ->assertOk() - ->assertJson(fn (AssertableJson $json) => - $json->has('data', fn (AssertableJson $json) => - $json->whereAll($expectedData) - ->etc() - ) - ); - - $this->assertExistsModelWhereColumns(PostComment::class, $expectedData); - } - - public function test_update_post_comment_without_access(): void - { - $this->getTestingUserWithoutAccess(); - - $postComment = PostComment::factory()->create(); - - $data = $this->getTestData(); - - $this->patchJson(route('api.post_comments.update', ['postComment' => $postComment->getKey()]), $data) - ->assertForbidden(); - } - - public function test_update_non_existing_post_comment(): void - { - $this->getTestingUser(); - - $data = $this->getTestData(); - - $this->patchJson(route('api.post_comments.update', ['postComment' => 7777]), $data) - ->assertNotFound(); - } -} diff --git a/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_can_set_custom_model__3.txt b/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_can_set_custom_model__3.txt deleted file mode 100644 index 91c877f..0000000 --- a/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_can_set_custom_model__3.txt +++ /dev/null @@ -1,25 +0,0 @@ -all()); - } - - public function asController(CreatePostCommentRequest $request): JsonResponse - { - $postComment = $this->handle($request->toDTO()); - - return (new PostCommentResource($postComment))->created(); - } -} diff --git a/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_can_set_custom_model__30.txt b/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_can_set_custom_model__30.txt deleted file mode 100644 index 7d73214..0000000 --- a/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_can_set_custom_model__30.txt +++ /dev/null @@ -1,51 +0,0 @@ - 'delete-post-comment', - 'roles' => '', - ]; - - public function test_delete_post_comment(): void - { - $this->getTestingUser(); - - $postComment = PostComment::factory()->create(); - - $this->deleteJson(route('api.post_comments.delete', ['postComment' => $postComment->getKey()])) - ->assertNoContent(); - - $this->assertNull(PostComment::find($postComment->getKey())); - } - - public function test_delete_post_comment_without_access(): void - { - $this->getTestingUserWithoutAccess(); - - $postComment = PostComment::factory()->create(); - - $this->deleteJson(route('api.post_comments.delete', ['postComment' => $postComment->getKey()])) - ->assertForbidden(); - } - - public function test_delete_not_existing_post_comment(): void - { - $this->getTestingUser(); - - $this->deleteJson(route('api.post_comments.delete', ['postComment' => 7777])) - ->assertNotFound(); - } -} diff --git a/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_can_set_custom_model__31.txt b/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_can_set_custom_model__31.txt deleted file mode 100644 index 123294e..0000000 --- a/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_can_set_custom_model__31.txt +++ /dev/null @@ -1,57 +0,0 @@ - 'view-post-comment', - 'roles' => '', - ]; - - public function test_view_post_comment(): void - { - $this->getTestingUser(); - - $postComment = PostComment::factory()->create(); - $expectedData = $postComment->toArray(); - - $this->getJson(route('api.post_comments.view', ['postComment' => $postComment->getKey()])) - ->assertOk() - ->assertJson(fn (AssertableJson $json) => - $json->has('data', fn (AssertableJson $json) => - $json->whereAll($expectedData) - ->etc() - ) - ); - } - - public function test_view_post_comment_without_access(): void - { - $this->getTestingUserWithoutAccess(); - - $postComment = PostComment::factory()->create(); - - $this->getJson(route('api.post_comments.view', ['postComment' => $postComment->getKey()])) - ->assertForbidden(); - } - - public function test_view_not_existing_post_comment(): void - { - $this->getTestingUser(); - - $this->getJson(route('api.post_comments.view', ['postComment' => 7777])) - ->assertNotFound(); - } -} diff --git a/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_can_set_custom_model__32.txt b/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_can_set_custom_model__32.txt deleted file mode 100644 index 1a4ed7d..0000000 --- a/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_can_set_custom_model__32.txt +++ /dev/null @@ -1,47 +0,0 @@ - 'view-post-comment', - 'roles' => '', - ]; - - public function test_list_post_comments(): void - { - $this->getTestingUser(); - - PostComment::factory()->count(2)->create(); - - $this->getJson(route('api.post_comments.list')) - ->assertOk() - ->assertJsonStructure([ - 'links', - 'meta', - 'data' - ]) - ->assertJsonCount(PostComment::query()->count(), 'data'); - } - - public function test_list_post_comments_without_access(): void - { - $this->getTestingUserWithoutAccess(); - - PostComment::factory()->count(2)->create(); - - $this->getJson(route('api.post_comments.list')) - ->assertForbidden(); - } -} diff --git a/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_can_set_custom_model__33.txt b/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_can_set_custom_model__33.txt deleted file mode 100644 index 48bf96f..0000000 --- a/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_can_set_custom_model__33.txt +++ /dev/null @@ -1,28 +0,0 @@ -id(); - - $table->timestamps(); - }); - } - - /** - * Reverse the migrations. - */ - public function down(): void - { - Schema::dropIfExists('post_comments'); - } -}; diff --git a/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_can_set_custom_model__34.txt b/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_can_set_custom_model__34.txt deleted file mode 100644 index 48bf96f..0000000 --- a/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_can_set_custom_model__34.txt +++ /dev/null @@ -1,28 +0,0 @@ -id(); - - $table->timestamps(); - }); - } - - /** - * Reverse the migrations. - */ - public function down(): void - { - Schema::dropIfExists('post_comments'); - } -}; diff --git a/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_can_set_custom_model__4.txt b/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_can_set_custom_model__4.txt deleted file mode 100644 index 4cafb66..0000000 --- a/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_can_set_custom_model__4.txt +++ /dev/null @@ -1,33 +0,0 @@ -all(); - - if (empty($data)) { - throw new UpdateResourceFailedException(); - } - - $postComment->update($data); - - return $postComment; - } - - public function asController(UpdatePostCommentRequest $request, PostComment $postComment): PostCommentResource - { - $postComment = $this->handle($postComment, $request->toDTO()); - - return new PostCommentResource($postComment); - } -} diff --git a/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_can_set_custom_model__5.txt b/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_can_set_custom_model__5.txt deleted file mode 100644 index ba3ac3c..0000000 --- a/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_can_set_custom_model__5.txt +++ /dev/null @@ -1,23 +0,0 @@ -delete(); - } - - public function asController(DeletePostCommentRequest $request, PostComment $postComment): JsonResponse - { - $this->handle($postComment); - - return $this->noContent(); - } -} diff --git a/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_can_set_custom_model__6.txt b/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_can_set_custom_model__6.txt deleted file mode 100644 index c34f6c8..0000000 --- a/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_can_set_custom_model__6.txt +++ /dev/null @@ -1,23 +0,0 @@ -build(); - } - - public function asController(ViewPostCommentRequest $request, PostComment $postComment): PostCommentResource - { - return new PostCommentResource($this->handle($request, $postComment)); - } -} diff --git a/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_can_set_custom_model__7.txt b/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_can_set_custom_model__7.txt deleted file mode 100644 index 9e3d50e..0000000 --- a/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_can_set_custom_model__7.txt +++ /dev/null @@ -1,26 +0,0 @@ -build() - ->jsonPaginate(); - } - - public function asController(ListPostCommentsRequest $request): ResourceCollection - { - return PostCommentResource::collection($this->handle($request)); - } -} diff --git a/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_can_set_custom_model__8.txt b/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_can_set_custom_model__8.txt deleted file mode 100644 index a2f9127..0000000 --- a/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_can_set_custom_model__8.txt +++ /dev/null @@ -1,34 +0,0 @@ -handle(new CreatePermissionDTO( - name: 'view-post-comment', - display_name: 'View any "post-comments"', - group: 'post-comments' - )); - - $createPermissionAction->handle(new CreatePermissionDTO( - name: 'create-post-comment', - display_name: 'Create "post-comments"', - group: 'post-comments' - )); - - $createPermissionAction->handle(new CreatePermissionDTO( - name: 'update-post-comment', - display_name: 'Update any "post-comments"', - group: 'post-comments' - )); - - $createPermissionAction->handle(new CreatePermissionDTO( - name: 'delete-post-comment', - display_name: 'Delete any "post-comments"', - group: 'post-comments' - )); - - $createPermissionAction->handle(new CreatePermissionDTO( - name: 'force-delete-post-comment', - display_name: 'Force delete any "post-comments"', - group: 'post-comments' - )); - } -} diff --git a/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_generates_module_components__1.txt b/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_generates_module_components__1.txt deleted file mode 100644 index f605374..0000000 --- a/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_generates_module_components__1.txt +++ /dev/null @@ -1,25 +0,0 @@ -all()); - } - - public function asController(CreateArticleRequest $request): JsonResponse - { - $article = $this->handle($request->toDTO()); - - return (new ArticleResource($article))->created(); - } -} diff --git a/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_generates_module_components__10.txt b/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_generates_module_components__10.txt deleted file mode 100644 index cd3c04a..0000000 --- a/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_generates_module_components__10.txt +++ /dev/null @@ -1,69 +0,0 @@ -can('view-article'); - } - - /** - * Determine whether the user can view the model. - */ - public function view(User $user, Article $article): bool - { - return $user->can('view-article'); - } - - /** - * Determine whether the user can create models. - */ - public function create(User $user): bool - { - return $user->can('create-article'); - } - - /** - * Determine whether the user can update the model. - */ - public function update(User $user, Article $article): bool - { - return $user->can('update-article'); - } - - /** - * Determine whether the user can delete the model. - */ - public function delete(User $user, Article $article): bool - { - return $user->can('delete-article'); - } - - /** - * Determine whether the user can restore the model. - */ - public function restore(User $user, Article $article): bool - { - return $user->can('delete-article'); - } - - /** - * Determine whether the user can permanently delete the model. - */ - public function forceDelete(User $user, Article $article): bool - { - return $user->can('force-delete-article'); - } -} diff --git a/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_generates_module_components__11.txt b/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_generates_module_components__11.txt deleted file mode 100644 index 9cb9660..0000000 --- a/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_generates_module_components__11.txt +++ /dev/null @@ -1,99 +0,0 @@ -registerMigrations(); - // $this->registerTranslations(); - // $this->registerCommands(); - // $this->registerViews(); - } - - /** - * Get the services provided by the provider. - */ - public function provides(): array - { - return []; - } - - /** - * Register translations. - */ - public function registerTranslations(): void - { - $sourcePath = module_path($this->moduleName, 'Resources/lang'); - $langPath = resource_path('lang/modules/' . $this->moduleKey); - - $this->loadTranslationsFrom($sourcePath, $this->moduleKey); - - $this->publishes([ - $sourcePath => $langPath, - ], 'translations'); - } - - /** - * Register views. - */ - public function registerViews(): void - { - $sourcePath = module_path($this->moduleName, 'Resources/views'); - $viewsPath = resource_path('views/modules/' . $this->moduleKey); - - $this->loadViewsFrom( - array_merge($this->getPublishableViewPaths($this->moduleKey), [$sourcePath]), - $this->moduleKey - ); - - $this->publishes([ - $sourcePath => $viewsPath - ], 'views'); - } - - /** - * Register migrations. - */ - public function registerMigrations(): void - { - $sourcePath = module_path($this->moduleName, 'Data/Migrations'); - $migrationsPath = database_path('migrations'); - - $this->loadMigrationsFrom($sourcePath); - - $this->publishes([ - $sourcePath => $migrationsPath - ], 'migrations'); - } - - /** - * Register artisan commands. - */ - public function registerCommands(): void - { - if ($this->app->runningInConsole()) { - $this->loadCommands(module_path($this->moduleName, 'UI/CLI/Commands')); - } - } -} diff --git a/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_generates_module_components__13.txt b/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_generates_module_components__13.txt deleted file mode 100644 index a2fccb5..0000000 --- a/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_generates_module_components__13.txt +++ /dev/null @@ -1,51 +0,0 @@ - - */ - protected function allowedAppends(): array - { - return []; - } - - /** - * @return array - */ - protected function defaultAppends(): array - { - return []; - } - - /** - * @return array - */ - protected function allowedFields(): array - { - return [ - // TODO: add fields here - ]; - } - - /** - * @return array - */ - protected function allowedIncludes(): array - { - return []; - } - - /** - * @return array - */ - protected function defaultIncludes(): array - { - return []; - } -} diff --git a/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_generates_module_components__14.txt b/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_generates_module_components__14.txt deleted file mode 100644 index b3b0b6d..0000000 --- a/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_generates_module_components__14.txt +++ /dev/null @@ -1,77 +0,0 @@ - - */ - protected function allowedAppends(): array - { - return []; - } - - /** - * @return array - */ - protected function defaultAppends(): array - { - return []; - } - - /** - * @return array - */ - protected function allowedFields(): array - { - return [ - // TODO: add fields here - ]; - } - - /** - * @return array - */ - protected function allowedFilters(): array - { - return []; - } - - /** - * @return array - */ - protected function allowedIncludes(): array - { - return []; - } - - /** - * @return array - */ - protected function defaultIncludes(): array - { - return []; - } - - /** - * @return array - */ - protected function allowedSorts(): array - { - return []; - } - - /** - * @return array - */ - protected function defaultSorts(): array - { - return []; - } -} diff --git a/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_generates_module_components__15.txt b/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_generates_module_components__15.txt deleted file mode 100644 index 0dcdf06..0000000 --- a/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_generates_module_components__15.txt +++ /dev/null @@ -1,19 +0,0 @@ -validated()); - } -} diff --git a/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_generates_module_components__17.txt b/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_generates_module_components__17.txt deleted file mode 100644 index c4cc41c..0000000 --- a/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_generates_module_components__17.txt +++ /dev/null @@ -1,28 +0,0 @@ -route('article'); - return $article && Gate::check('update', $article); - } - - public function toDTO(): UpdateArticleDTO - { - return new UpdateArticleDTO($this->validated()); - } -} diff --git a/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_generates_module_components__18.txt b/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_generates_module_components__18.txt deleted file mode 100644 index 8d99840..0000000 --- a/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_generates_module_components__18.txt +++ /dev/null @@ -1,20 +0,0 @@ -route('article'); - return $article && Gate::check('delete', $article); - } -} diff --git a/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_generates_module_components__19.txt b/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_generates_module_components__19.txt deleted file mode 100644 index 358aa05..0000000 --- a/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_generates_module_components__19.txt +++ /dev/null @@ -1,20 +0,0 @@ -route('article'); - return $article && Gate::check('view', $article); - } -} diff --git a/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_generates_module_components__2.txt b/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_generates_module_components__2.txt deleted file mode 100644 index 6dd1d52..0000000 --- a/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_generates_module_components__2.txt +++ /dev/null @@ -1,33 +0,0 @@ -all(); - - if (empty($data)) { - throw new UpdateResourceFailedException(); - } - - $article->update($data); - - return $article; - } - - public function asController(UpdateArticleRequest $request, Article $article): ArticleResource - { - $article = $this->handle($article, $request->toDTO()); - - return new ArticleResource($article); - } -} diff --git a/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_generates_module_components__20.txt b/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_generates_module_components__20.txt deleted file mode 100644 index d663831..0000000 --- a/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_generates_module_components__20.txt +++ /dev/null @@ -1,20 +0,0 @@ -name('api.articles.create'); diff --git a/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_generates_module_components__22.txt b/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_generates_module_components__22.txt deleted file mode 100644 index 7e76fc3..0000000 --- a/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_generates_module_components__22.txt +++ /dev/null @@ -1,7 +0,0 @@ -name('api.articles.update'); diff --git a/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_generates_module_components__23.txt b/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_generates_module_components__23.txt deleted file mode 100644 index cceba8f..0000000 --- a/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_generates_module_components__23.txt +++ /dev/null @@ -1,7 +0,0 @@ -name('api.articles.delete'); diff --git a/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_generates_module_components__24.txt b/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_generates_module_components__24.txt deleted file mode 100644 index 98fedcf..0000000 --- a/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_generates_module_components__24.txt +++ /dev/null @@ -1,7 +0,0 @@ -name('api.articles.view'); diff --git a/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_generates_module_components__25.txt b/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_generates_module_components__25.txt deleted file mode 100644 index 093f946..0000000 --- a/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_generates_module_components__25.txt +++ /dev/null @@ -1,7 +0,0 @@ -name('api.articles.list'); diff --git a/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_generates_module_components__26.txt b/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_generates_module_components__26.txt deleted file mode 100644 index 02c4e58..0000000 --- a/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_generates_module_components__26.txt +++ /dev/null @@ -1,58 +0,0 @@ - 'create-article', - 'roles' => '', - ]; - - protected function getTestData(array $mergeData = []): array - { - return array_merge([ - // TODO: add fields here - ], $mergeData); - } - - public function test_create_article(): void - { - $this->getTestingUser(); - - $data = $this->getTestData(); - - $this->postJson(route('api.articles.create'), $data) - ->assertCreated() - ->assertJson(fn (AssertableJson $json) => - $json->has('data', fn (AssertableJson $json) => - $json->has('id') - ->whereAll($data) - ->etc() - ) - ); - - $this->assertExistsModelWhereColumns(Article::class, $data); - } - - public function test_create_article_without_access(): void - { - $this->getTestingUserWithoutAccess(); - - $data = $this->getTestData(); - - $this->postJson(route('api.articles.create'), $data) - ->assertForbidden(); - } -} diff --git a/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_generates_module_components__27.txt b/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_generates_module_components__27.txt deleted file mode 100644 index 2ae1db3..0000000 --- a/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_generates_module_components__27.txt +++ /dev/null @@ -1,74 +0,0 @@ - 'update-article', - 'roles' => '', - ]; - - protected function getTestData(array $mergeData = []): array - { - return array_merge([ - // TODO: add fields here - ], $mergeData); - } - - public function test_update_article(): void - { - $this->getTestingUser(); - - $article = Article::factory()->create(); - - $data = $this->getTestData(); - $expectedData = array_merge($data, [ - 'id' => $article->getKey(), - ]); - - $this->patchJson(route('api.articles.update', ['article' => $article->getKey()]), $data) - ->assertOk() - ->assertJson(fn (AssertableJson $json) => - $json->has('data', fn (AssertableJson $json) => - $json->whereAll($expectedData) - ->etc() - ) - ); - - $this->assertExistsModelWhereColumns(Article::class, $expectedData); - } - - public function test_update_article_without_access(): void - { - $this->getTestingUserWithoutAccess(); - - $article = Article::factory()->create(); - - $data = $this->getTestData(); - - $this->patchJson(route('api.articles.update', ['article' => $article->getKey()]), $data) - ->assertForbidden(); - } - - public function test_update_non_existing_article(): void - { - $this->getTestingUser(); - - $data = $this->getTestData(); - - $this->patchJson(route('api.articles.update', ['article' => 7777]), $data) - ->assertNotFound(); - } -} diff --git a/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_generates_module_components__28.txt b/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_generates_module_components__28.txt deleted file mode 100644 index 76e7943..0000000 --- a/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_generates_module_components__28.txt +++ /dev/null @@ -1,51 +0,0 @@ - 'delete-article', - 'roles' => '', - ]; - - public function test_delete_article(): void - { - $this->getTestingUser(); - - $article = Article::factory()->create(); - - $this->deleteJson(route('api.articles.delete', ['article' => $article->getKey()])) - ->assertNoContent(); - - $this->assertNull(Article::find($article->getKey())); - } - - public function test_delete_article_without_access(): void - { - $this->getTestingUserWithoutAccess(); - - $article = Article::factory()->create(); - - $this->deleteJson(route('api.articles.delete', ['article' => $article->getKey()])) - ->assertForbidden(); - } - - public function test_delete_not_existing_article(): void - { - $this->getTestingUser(); - - $this->deleteJson(route('api.articles.delete', ['article' => 7777])) - ->assertNotFound(); - } -} diff --git a/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_generates_module_components__29.txt b/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_generates_module_components__29.txt deleted file mode 100644 index 5450de3..0000000 --- a/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_generates_module_components__29.txt +++ /dev/null @@ -1,57 +0,0 @@ - 'view-article', - 'roles' => '', - ]; - - public function test_view_article(): void - { - $this->getTestingUser(); - - $article = Article::factory()->create(); - $expectedData = $article->toArray(); - - $this->getJson(route('api.articles.view', ['article' => $article->getKey()])) - ->assertOk() - ->assertJson(fn (AssertableJson $json) => - $json->has('data', fn (AssertableJson $json) => - $json->whereAll($expectedData) - ->etc() - ) - ); - } - - public function test_view_article_without_access(): void - { - $this->getTestingUserWithoutAccess(); - - $article = Article::factory()->create(); - - $this->getJson(route('api.articles.view', ['article' => $article->getKey()])) - ->assertForbidden(); - } - - public function test_view_not_existing_article(): void - { - $this->getTestingUser(); - - $this->getJson(route('api.articles.view', ['article' => 7777])) - ->assertNotFound(); - } -} diff --git a/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_generates_module_components__3.txt b/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_generates_module_components__3.txt deleted file mode 100644 index ddf1544..0000000 --- a/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_generates_module_components__3.txt +++ /dev/null @@ -1,23 +0,0 @@ -delete(); - } - - public function asController(DeleteArticleRequest $request, Article $article): JsonResponse - { - $this->handle($article); - - return $this->noContent(); - } -} diff --git a/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_generates_module_components__30.txt b/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_generates_module_components__30.txt deleted file mode 100644 index adfb1ec..0000000 --- a/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_generates_module_components__30.txt +++ /dev/null @@ -1,47 +0,0 @@ - 'view-article', - 'roles' => '', - ]; - - public function test_list_articles(): void - { - $this->getTestingUser(); - - Article::factory()->count(2)->create(); - - $this->getJson(route('api.articles.list')) - ->assertOk() - ->assertJsonStructure([ - 'links', - 'meta', - 'data' - ]) - ->assertJsonCount(Article::query()->count(), 'data'); - } - - public function test_list_articles_without_access(): void - { - $this->getTestingUserWithoutAccess(); - - Article::factory()->count(2)->create(); - - $this->getJson(route('api.articles.list')) - ->assertForbidden(); - } -} diff --git a/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_generates_module_components__4.txt b/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_generates_module_components__4.txt deleted file mode 100644 index 9d2ab5c..0000000 --- a/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_generates_module_components__4.txt +++ /dev/null @@ -1,23 +0,0 @@ -build(); - } - - public function asController(ViewArticleRequest $request, Article $article): ArticleResource - { - return new ArticleResource($this->handle($request, $article)); - } -} diff --git a/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_generates_module_components__5.txt b/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_generates_module_components__5.txt deleted file mode 100644 index 734c60d..0000000 --- a/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_generates_module_components__5.txt +++ /dev/null @@ -1,26 +0,0 @@ -build() - ->jsonPaginate(); - } - - public function asController(ListArticlesRequest $request): ResourceCollection - { - return ArticleResource::collection($this->handle($request)); - } -} diff --git a/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_generates_module_components__6.txt b/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_generates_module_components__6.txt deleted file mode 100644 index 5f63c9d..0000000 --- a/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_generates_module_components__6.txt +++ /dev/null @@ -1,34 +0,0 @@ -handle(new CreatePermissionDTO( - name: 'view-article', - display_name: 'View any "articles"', - group: 'articles' - )); - - $createPermissionAction->handle(new CreatePermissionDTO( - name: 'create-article', - display_name: 'Create "articles"', - group: 'articles' - )); - - $createPermissionAction->handle(new CreatePermissionDTO( - name: 'update-article', - display_name: 'Update any "articles"', - group: 'articles' - )); - - $createPermissionAction->handle(new CreatePermissionDTO( - name: 'delete-article', - display_name: 'Delete any "articles"', - group: 'articles' - )); - - $createPermissionAction->handle(new CreatePermissionDTO( - name: 'force-delete-article', - display_name: 'Force delete any "articles"', - group: 'articles' - )); - } -} diff --git a/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_generates_module_components__8.txt b/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_generates_module_components__8.txt deleted file mode 100644 index 9065ecf..0000000 --- a/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_generates_module_components__8.txt +++ /dev/null @@ -1,10 +0,0 @@ -registerMigrations(); - // $this->registerTranslations(); - // $this->registerCommands(); - // $this->registerViews(); - } - - /** - * Get the services provided by the provider. - */ - public function provides(): array - { - return []; - } - - /** - * Register translations. - */ - public function registerTranslations(): void - { - $sourcePath = module_path($this->moduleName, 'Resources/lang'); - $langPath = resource_path('lang/modules/' . $this->moduleKey); - - $this->loadTranslationsFrom($sourcePath, $this->moduleKey); - - $this->publishes([ - $sourcePath => $langPath, - ], 'translations'); - } - - /** - * Register views. - */ - public function registerViews(): void - { - $sourcePath = module_path($this->moduleName, 'Resources/views'); - $viewsPath = resource_path('views/modules/' . $this->moduleKey); - - $this->loadViewsFrom( - array_merge($this->getPublishableViewPaths($this->moduleKey), [$sourcePath]), - $this->moduleKey - ); - - $this->publishes([ - $sourcePath => $viewsPath - ], 'views'); - } - - /** - * Register migrations. - */ - public function registerMigrations(): void - { - $sourcePath = module_path($this->moduleName, 'Data/Migrations'); - $migrationsPath = database_path('migrations'); - - $this->loadMigrationsFrom($sourcePath); - - $this->publishes([ - $sourcePath => $migrationsPath - ], 'migrations'); - } - - /** - * Register artisan commands. - */ - public function registerCommands(): void - { - if ($this->app->runningInConsole()) { - $this->loadCommands(module_path($this->moduleName, 'UI/CLI/Commands')); - } - } -} diff --git a/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_generates_module_scaffold_files__1.txt b/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_generates_module_scaffold_files__1.txt deleted file mode 100644 index 3441218..0000000 --- a/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_generates_module_scaffold_files__1.txt +++ /dev/null @@ -1,15 +0,0 @@ -{ - "name": "Article", - "namespace": "App\\Modules\\Article", - "alias": "article", - "description": "Article module", - "keywords": [], - "priority": 0, - "providers": [ - "App\\Modules\\Article\\Providers\\ArticleServiceProvider", - "App\\Modules\\Article\\Providers\\RouteServiceProvider" - ], - "aliases": [], - "files": [], - "requires": [] -} \ No newline at end of file diff --git a/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_generates_module_scaffold_files__2.txt b/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_generates_module_scaffold_files__2.txt deleted file mode 100644 index 02911b7..0000000 --- a/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_generates_module_scaffold_files__2.txt +++ /dev/null @@ -1,15 +0,0 @@ -{ - "name": "example/article", - "description": "", - "authors": [ - { - "name": "Example name", - "email": "example@example.com" - } - ], - "autoload": { - "psr-4": { - "App\\Modules\\Article\\": "" - } - } -} diff --git a/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_generates_module_scaffold_files__3.txt b/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_generates_module_scaffold_files__3.txt deleted file mode 100644 index fd8e4c1..0000000 --- a/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_generates_module_scaffold_files__3.txt +++ /dev/null @@ -1,13 +0,0 @@ - 'Article', - -]; diff --git a/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_generes_module_with_new_provider_location__1.txt b/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_generes_module_with_new_provider_location__1.txt deleted file mode 100644 index 52be439..0000000 --- a/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_generes_module_with_new_provider_location__1.txt +++ /dev/null @@ -1,15 +0,0 @@ -{ - "name": "Article", - "namespace": "App\\Modules\\Article", - "alias": "article", - "description": "Article module", - "keywords": [], - "priority": 0, - "providers": [ - "App\\Modules\\Article\\Base\\Providers\\ArticleServiceProvider", - "App\\Modules\\Article\\Base\\Providers\\RouteServiceProvider" - ], - "aliases": [], - "files": [], - "requires": [] -} \ No newline at end of file diff --git a/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_generes_module_with_new_provider_location__2.txt b/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_generes_module_with_new_provider_location__2.txt deleted file mode 100644 index 02911b7..0000000 --- a/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_generes_module_with_new_provider_location__2.txt +++ /dev/null @@ -1,15 +0,0 @@ -{ - "name": "example/article", - "description": "", - "authors": [ - { - "name": "Example name", - "email": "example@example.com" - } - ], - "autoload": { - "psr-4": { - "App\\Modules\\Article\\": "" - } - } -} diff --git a/tests/Commands/Generators/__snapshots__/NotificationMakeCommandTest__it_can_change_the_default_namespace_for_plain_notification_file__1.txt b/tests/Commands/Generators/__snapshots__/NotificationMakeCommandTest__it_can_change_the_default_namespace_for_plain_notification_file__1.txt deleted file mode 100644 index 94859b1..0000000 --- a/tests/Commands/Generators/__snapshots__/NotificationMakeCommandTest__it_can_change_the_default_namespace_for_plain_notification_file__1.txt +++ /dev/null @@ -1,23 +0,0 @@ -can('view-my-awesome-model'); - } - - /** - * Determine whether the user can view the model. - */ - public function view(User $user, MyAwesomeModel $myAwesomeModel): bool - { - return $user->can('view-my-awesome-model'); - } - - /** - * Determine whether the user can create models. - */ - public function create(User $user): bool - { - return $user->can('create-my-awesome-model'); - } - - /** - * Determine whether the user can update the model. - */ - public function update(User $user, MyAwesomeModel $myAwesomeModel): bool - { - return $user->can('update-my-awesome-model'); - } - - /** - * Determine whether the user can delete the model. - */ - public function delete(User $user, MyAwesomeModel $myAwesomeModel): bool - { - return $user->can('delete-my-awesome-model'); - } - - /** - * Determine whether the user can restore the model. - */ - public function restore(User $user, MyAwesomeModel $myAwesomeModel): bool - { - return $user->can('delete-my-awesome-model'); - } - - /** - * Determine whether the user can permanently delete the model. - */ - public function forceDelete(User $user, MyAwesomeModel $myAwesomeModel): bool - { - return $user->can('force-delete-my-awesome-model'); - } -} diff --git a/tests/Commands/Generators/__snapshots__/PolicyMakeCommandTest__it_generated_correct_plain_policy_file_with_content__1.txt b/tests/Commands/Generators/__snapshots__/PolicyMakeCommandTest__it_generated_correct_plain_policy_file_with_content__1.txt deleted file mode 100644 index f9db87f..0000000 --- a/tests/Commands/Generators/__snapshots__/PolicyMakeCommandTest__it_generated_correct_plain_policy_file_with_content__1.txt +++ /dev/null @@ -1,16 +0,0 @@ -registerMigrations(); - // $this->registerTranslations(); - // $this->registerCommands(); - // $this->registerViews(); - } - - /** - * Get the services provided by the provider. - */ - public function provides(): array - { - return []; - } - - /** - * Register translations. - */ - public function registerTranslations(): void - { - $sourcePath = module_path($this->moduleName, 'Resources/lang'); - $langPath = resource_path('lang/modules/' . $this->moduleKey); - - $this->loadTranslationsFrom($sourcePath, $this->moduleKey); - - $this->publishes([ - $sourcePath => $langPath, - ], 'translations'); - } - - /** - * Register views. - */ - public function registerViews(): void - { - $sourcePath = module_path($this->moduleName, 'Resources/views'); - $viewsPath = resource_path('views/modules/' . $this->moduleKey); - - $this->loadViewsFrom( - array_merge($this->getPublishableViewPaths($this->moduleKey), [$sourcePath]), - $this->moduleKey - ); - - $this->publishes([ - $sourcePath => $viewsPath - ], 'views'); - } - - /** - * Register migrations. - */ - public function registerMigrations(): void - { - $sourcePath = module_path($this->moduleName, 'Data/Migrations'); - $migrationsPath = database_path('migrations'); - - $this->loadMigrationsFrom($sourcePath); - - $this->publishes([ - $sourcePath => $migrationsPath - ], 'migrations'); - } - - /** - * Register artisan commands. - */ - public function registerCommands(): void - { - if ($this->app->runningInConsole()) { - $this->loadCommands(module_path($this->moduleName, 'UI/CLI/Commands')); - } - } -} diff --git a/tests/Commands/Generators/__snapshots__/ProviderMakeCommandTest__it_can_generate_route_provider_file__1.txt b/tests/Commands/Generators/__snapshots__/ProviderMakeCommandTest__it_can_generate_route_provider_file__1.txt deleted file mode 100644 index d05bc76..0000000 --- a/tests/Commands/Generators/__snapshots__/ProviderMakeCommandTest__it_can_generate_route_provider_file__1.txt +++ /dev/null @@ -1,59 +0,0 @@ -mapApiRoutes(); - $this->mapWebRoutes(); - } - - /** - * Define the "web" routes for the application. - * These routes all receive session state, CSRF protection, etc. - */ - protected function mapWebRoutes(): void - { - Route::middleware('web') -// ->namespace('App\\Modules\\Article\\UI\\WEB\\Controllers') - ->group(function () { - $this->loadRoutesFromDirectory(module_path($this->moduleName, 'UI/WEB/Routes')); - }); - } - - /** - * Define the "api" routes for the application. - * These routes are typically stateless. - */ - protected function mapApiRoutes(): void - { - Route::prefix('api') - ->middleware('api') -// ->namespace('App\\Modules\\Article\\UI\\API\\Controllers') - ->group(function () { - $this->loadRoutesFromDirectory(module_path($this->moduleName, 'UI/API/Routes')); - }); - } -} diff --git a/tests/Commands/Generators/__snapshots__/QueryWizardMakeCommandTest__it_can_change_the_default_namespace_for_query_wizard_file__1.txt b/tests/Commands/Generators/__snapshots__/QueryWizardMakeCommandTest__it_can_change_the_default_namespace_for_query_wizard_file__1.txt deleted file mode 100644 index 5e3e6d7..0000000 --- a/tests/Commands/Generators/__snapshots__/QueryWizardMakeCommandTest__it_can_change_the_default_namespace_for_query_wizard_file__1.txt +++ /dev/null @@ -1,77 +0,0 @@ - - */ - protected function allowedAppends(): array - { - return []; - } - - /** - * @return array - */ - protected function defaultAppends(): array - { - return []; - } - - /** - * @return array - */ - protected function allowedFields(): array - { - return [ - // TODO: add fields here - ]; - } - - /** - * @return array - */ - protected function allowedFilters(): array - { - return []; - } - - /** - * @return array - */ - protected function allowedIncludes(): array - { - return []; - } - - /** - * @return array - */ - protected function defaultIncludes(): array - { - return []; - } - - /** - * @return array - */ - protected function allowedSorts(): array - { - return []; - } - - /** - * @return array - */ - protected function defaultSorts(): array - { - return []; - } -} diff --git a/tests/Commands/Generators/__snapshots__/QueryWizardMakeCommandTest__it_can_change_the_default_path_for_query_wizard_file__1.txt b/tests/Commands/Generators/__snapshots__/QueryWizardMakeCommandTest__it_can_change_the_default_path_for_query_wizard_file__1.txt deleted file mode 100644 index 5e3e6d7..0000000 --- a/tests/Commands/Generators/__snapshots__/QueryWizardMakeCommandTest__it_can_change_the_default_path_for_query_wizard_file__1.txt +++ /dev/null @@ -1,77 +0,0 @@ - - */ - protected function allowedAppends(): array - { - return []; - } - - /** - * @return array - */ - protected function defaultAppends(): array - { - return []; - } - - /** - * @return array - */ - protected function allowedFields(): array - { - return [ - // TODO: add fields here - ]; - } - - /** - * @return array - */ - protected function allowedFilters(): array - { - return []; - } - - /** - * @return array - */ - protected function allowedIncludes(): array - { - return []; - } - - /** - * @return array - */ - protected function defaultIncludes(): array - { - return []; - } - - /** - * @return array - */ - protected function allowedSorts(): array - { - return []; - } - - /** - * @return array - */ - protected function defaultSorts(): array - { - return []; - } -} diff --git a/tests/Commands/Generators/__snapshots__/QueryWizardMakeCommandTest__it_generated_correct_elastic_query_wizard_file_with_content__1.txt b/tests/Commands/Generators/__snapshots__/QueryWizardMakeCommandTest__it_generated_correct_elastic_query_wizard_file_with_content__1.txt deleted file mode 100644 index 9b4e72c..0000000 --- a/tests/Commands/Generators/__snapshots__/QueryWizardMakeCommandTest__it_generated_correct_elastic_query_wizard_file_with_content__1.txt +++ /dev/null @@ -1,78 +0,0 @@ - - */ - protected function allowedAppends(): array - { - return []; - } - - /** - * @return array - */ - protected function defaultAppends(): array - { - return []; - } - - /** - * @return array - */ - protected function allowedFields(): array - { - return [ - // TODO: add fields here - ]; - } - - /** - * @return array - */ - protected function allowedFilters(): array - { - return []; - } - - /** - * @return array - */ - protected function allowedIncludes(): array - { - return []; - } - - /** - * @return array - */ - protected function defaultIncludes(): array - { - return []; - } - - /** - * @return array - */ - protected function allowedSorts(): array - { - return []; - } - - /** - * @return array - */ - protected function defaultSorts(): array - { - return []; - } -} diff --git a/tests/Commands/Generators/__snapshots__/QueryWizardMakeCommandTest__it_generated_correct_eloquent_query_wizard_file_with_content__1.txt b/tests/Commands/Generators/__snapshots__/QueryWizardMakeCommandTest__it_generated_correct_eloquent_query_wizard_file_with_content__1.txt deleted file mode 100644 index f9f2802..0000000 --- a/tests/Commands/Generators/__snapshots__/QueryWizardMakeCommandTest__it_generated_correct_eloquent_query_wizard_file_with_content__1.txt +++ /dev/null @@ -1,77 +0,0 @@ - - */ - protected function allowedAppends(): array - { - return []; - } - - /** - * @return array - */ - protected function defaultAppends(): array - { - return []; - } - - /** - * @return array - */ - protected function allowedFields(): array - { - return [ - // TODO: add fields here - ]; - } - - /** - * @return array - */ - protected function allowedFilters(): array - { - return []; - } - - /** - * @return array - */ - protected function allowedIncludes(): array - { - return []; - } - - /** - * @return array - */ - protected function defaultIncludes(): array - { - return []; - } - - /** - * @return array - */ - protected function allowedSorts(): array - { - return []; - } - - /** - * @return array - */ - protected function defaultSorts(): array - { - return []; - } -} diff --git a/tests/Commands/Generators/__snapshots__/QueryWizardMakeCommandTest__it_generated_correct_model_query_wizard_file_with_content__1.txt b/tests/Commands/Generators/__snapshots__/QueryWizardMakeCommandTest__it_generated_correct_model_query_wizard_file_with_content__1.txt deleted file mode 100644 index b87717b..0000000 --- a/tests/Commands/Generators/__snapshots__/QueryWizardMakeCommandTest__it_generated_correct_model_query_wizard_file_with_content__1.txt +++ /dev/null @@ -1,51 +0,0 @@ - - */ - protected function allowedAppends(): array - { - return []; - } - - /** - * @return array - */ - protected function defaultAppends(): array - { - return []; - } - - /** - * @return array - */ - protected function allowedFields(): array - { - return [ - // TODO: add fields here - ]; - } - - /** - * @return array - */ - protected function allowedIncludes(): array - { - return []; - } - - /** - * @return array - */ - protected function defaultIncludes(): array - { - return []; - } -} diff --git a/tests/Commands/Generators/__snapshots__/QueryWizardMakeCommandTest__it_generated_correct_scout_query_wizard_file_with_content__1.txt b/tests/Commands/Generators/__snapshots__/QueryWizardMakeCommandTest__it_generated_correct_scout_query_wizard_file_with_content__1.txt deleted file mode 100644 index 5fd6d63..0000000 --- a/tests/Commands/Generators/__snapshots__/QueryWizardMakeCommandTest__it_generated_correct_scout_query_wizard_file_with_content__1.txt +++ /dev/null @@ -1,77 +0,0 @@ - - */ - protected function allowedAppends(): array - { - return []; - } - - /** - * @return array - */ - protected function defaultAppends(): array - { - return []; - } - - /** - * @return array - */ - protected function allowedFields(): array - { - return [ - // TODO: add fields here - ]; - } - - /** - * @return array - */ - protected function allowedFilters(): array - { - return []; - } - - /** - * @return array - */ - protected function allowedIncludes(): array - { - return []; - } - - /** - * @return array - */ - protected function defaultIncludes(): array - { - return []; - } - - /** - * @return array - */ - protected function allowedSorts(): array - { - return []; - } - - /** - * @return array - */ - protected function defaultSorts(): array - { - return []; - } -} diff --git a/tests/Commands/Generators/__snapshots__/RequestMakeCommandTest__it_can_change_the_default_namespace_for_api_request__1.txt b/tests/Commands/Generators/__snapshots__/RequestMakeCommandTest__it_can_change_the_default_namespace_for_api_request__1.txt deleted file mode 100644 index 3acff3b..0000000 --- a/tests/Commands/Generators/__snapshots__/RequestMakeCommandTest__it_can_change_the_default_namespace_for_api_request__1.txt +++ /dev/null @@ -1,18 +0,0 @@ -validated()); - } -} diff --git a/tests/Commands/Generators/__snapshots__/RequestMakeCommandTest__it_can_generate_api_delete_request_file__1.txt b/tests/Commands/Generators/__snapshots__/RequestMakeCommandTest__it_can_generate_api_delete_request_file__1.txt deleted file mode 100644 index 8ce9a82..0000000 --- a/tests/Commands/Generators/__snapshots__/RequestMakeCommandTest__it_can_generate_api_delete_request_file__1.txt +++ /dev/null @@ -1,20 +0,0 @@ -route('model'); - return $model && Gate::check('delete', $model); - } -} diff --git a/tests/Commands/Generators/__snapshots__/RequestMakeCommandTest__it_can_generate_api_list_request_file__1.txt b/tests/Commands/Generators/__snapshots__/RequestMakeCommandTest__it_can_generate_api_list_request_file__1.txt deleted file mode 100644 index 199d9c4..0000000 --- a/tests/Commands/Generators/__snapshots__/RequestMakeCommandTest__it_can_generate_api_list_request_file__1.txt +++ /dev/null @@ -1,20 +0,0 @@ -route('model'); - return $model && Gate::check('update', $model); - } - - public function toDTO(): TestDTO - { - return new TestDTO($this->validated()); - } -} diff --git a/tests/Commands/Generators/__snapshots__/RequestMakeCommandTest__it_can_generate_api_view_request_file__1.txt b/tests/Commands/Generators/__snapshots__/RequestMakeCommandTest__it_can_generate_api_view_request_file__1.txt deleted file mode 100644 index 4340ffa..0000000 --- a/tests/Commands/Generators/__snapshots__/RequestMakeCommandTest__it_can_generate_api_view_request_file__1.txt +++ /dev/null @@ -1,20 +0,0 @@ -route('model'); - return $model && Gate::check('view', $model); - } -} diff --git a/tests/Commands/Generators/__snapshots__/RequestMakeCommandTest__it_can_generate_web_create_request_file__1.txt b/tests/Commands/Generators/__snapshots__/RequestMakeCommandTest__it_can_generate_web_create_request_file__1.txt deleted file mode 100644 index 40d511b..0000000 --- a/tests/Commands/Generators/__snapshots__/RequestMakeCommandTest__it_can_generate_web_create_request_file__1.txt +++ /dev/null @@ -1,28 +0,0 @@ -validated()); - } -} diff --git a/tests/Commands/Generators/__snapshots__/RequestMakeCommandTest__it_can_generate_web_delete_request_file__1.txt b/tests/Commands/Generators/__snapshots__/RequestMakeCommandTest__it_can_generate_web_delete_request_file__1.txt deleted file mode 100644 index 56810b7..0000000 --- a/tests/Commands/Generators/__snapshots__/RequestMakeCommandTest__it_can_generate_web_delete_request_file__1.txt +++ /dev/null @@ -1,20 +0,0 @@ -route('model'); - return $model && Gate::check('delete', $model); - } -} diff --git a/tests/Commands/Generators/__snapshots__/RequestMakeCommandTest__it_can_generate_web_update_request_file__1.txt b/tests/Commands/Generators/__snapshots__/RequestMakeCommandTest__it_can_generate_web_update_request_file__1.txt deleted file mode 100644 index 2eff23f..0000000 --- a/tests/Commands/Generators/__snapshots__/RequestMakeCommandTest__it_can_generate_web_update_request_file__1.txt +++ /dev/null @@ -1,28 +0,0 @@ -route('model'); - return $model && Gate::check('update', $model); - } - - public function toDTO(): TestDTO - { - return new TestDTO($this->validated()); - } -} diff --git a/tests/Commands/Generators/__snapshots__/ResourceMakeCommandTest__it_can_change_the_default_namespace_for_resource_file__1.txt b/tests/Commands/Generators/__snapshots__/ResourceMakeCommandTest__it_can_change_the_default_namespace_for_resource_file__1.txt deleted file mode 100644 index 2cbda71..0000000 --- a/tests/Commands/Generators/__snapshots__/ResourceMakeCommandTest__it_can_change_the_default_namespace_for_resource_file__1.txt +++ /dev/null @@ -1,19 +0,0 @@ -name('api.route.list'); diff --git a/tests/Commands/Generators/__snapshots__/RouteMakeCommandTest__it_generated_api_route_with_delete_method__1.txt b/tests/Commands/Generators/__snapshots__/RouteMakeCommandTest__it_generated_api_route_with_delete_method__1.txt deleted file mode 100644 index 3cd046c..0000000 --- a/tests/Commands/Generators/__snapshots__/RouteMakeCommandTest__it_generated_api_route_with_delete_method__1.txt +++ /dev/null @@ -1,7 +0,0 @@ -name('api.nested.some_delete_route'); diff --git a/tests/Commands/Generators/__snapshots__/RouteMakeCommandTest__it_generated_api_route_with_get_method__1.txt b/tests/Commands/Generators/__snapshots__/RouteMakeCommandTest__it_generated_api_route_with_get_method__1.txt deleted file mode 100644 index e5e9c98..0000000 --- a/tests/Commands/Generators/__snapshots__/RouteMakeCommandTest__it_generated_api_route_with_get_method__1.txt +++ /dev/null @@ -1,7 +0,0 @@ -name('api.nested.some_get_route'); diff --git a/tests/Commands/Generators/__snapshots__/RouteMakeCommandTest__it_generated_api_route_with_options_method__1.txt b/tests/Commands/Generators/__snapshots__/RouteMakeCommandTest__it_generated_api_route_with_options_method__1.txt deleted file mode 100644 index 73edd4c..0000000 --- a/tests/Commands/Generators/__snapshots__/RouteMakeCommandTest__it_generated_api_route_with_options_method__1.txt +++ /dev/null @@ -1,7 +0,0 @@ -name('api.nested.some_options_route'); diff --git a/tests/Commands/Generators/__snapshots__/RouteMakeCommandTest__it_generated_api_route_with_patch_method__1.txt b/tests/Commands/Generators/__snapshots__/RouteMakeCommandTest__it_generated_api_route_with_patch_method__1.txt deleted file mode 100644 index ad5b41c..0000000 --- a/tests/Commands/Generators/__snapshots__/RouteMakeCommandTest__it_generated_api_route_with_patch_method__1.txt +++ /dev/null @@ -1,7 +0,0 @@ -name('api.nested.some_patch_route'); diff --git a/tests/Commands/Generators/__snapshots__/RouteMakeCommandTest__it_generated_api_route_with_post_method__1.txt b/tests/Commands/Generators/__snapshots__/RouteMakeCommandTest__it_generated_api_route_with_post_method__1.txt deleted file mode 100644 index ee0c87d..0000000 --- a/tests/Commands/Generators/__snapshots__/RouteMakeCommandTest__it_generated_api_route_with_post_method__1.txt +++ /dev/null @@ -1,7 +0,0 @@ -name('api.nested.some_post_route'); diff --git a/tests/Commands/Generators/__snapshots__/RouteMakeCommandTest__it_generated_api_route_with_put_method__1.txt b/tests/Commands/Generators/__snapshots__/RouteMakeCommandTest__it_generated_api_route_with_put_method__1.txt deleted file mode 100644 index aedd372..0000000 --- a/tests/Commands/Generators/__snapshots__/RouteMakeCommandTest__it_generated_api_route_with_put_method__1.txt +++ /dev/null @@ -1,7 +0,0 @@ -name('api.nested.some_put_route'); diff --git a/tests/Commands/Generators/__snapshots__/RouteMakeCommandTest__it_generated_correct_route_file_with_content__1.txt b/tests/Commands/Generators/__snapshots__/RouteMakeCommandTest__it_generated_correct_route_file_with_content__1.txt deleted file mode 100644 index 0f966fd..0000000 --- a/tests/Commands/Generators/__snapshots__/RouteMakeCommandTest__it_generated_correct_route_file_with_content__1.txt +++ /dev/null @@ -1,7 +0,0 @@ -name('api.route.list'); diff --git a/tests/Commands/Generators/__snapshots__/RouteMakeCommandTest__it_generated_recognized_view_route__1.txt b/tests/Commands/Generators/__snapshots__/RouteMakeCommandTest__it_generated_recognized_view_route__1.txt deleted file mode 100644 index 556d6bc..0000000 --- a/tests/Commands/Generators/__snapshots__/RouteMakeCommandTest__it_generated_recognized_view_route__1.txt +++ /dev/null @@ -1,7 +0,0 @@ -name('api.posts.view'); diff --git a/tests/Commands/Generators/__snapshots__/RouteMakeCommandTest__it_generated_route_with_options_method__1.txt b/tests/Commands/Generators/__snapshots__/RouteMakeCommandTest__it_generated_route_with_options_method__1.txt deleted file mode 100644 index 73edd4c..0000000 --- a/tests/Commands/Generators/__snapshots__/RouteMakeCommandTest__it_generated_route_with_options_method__1.txt +++ /dev/null @@ -1,7 +0,0 @@ -name('api.nested.some_options_route'); diff --git a/tests/Commands/Generators/__snapshots__/RouteMakeCommandTest__it_generated_web_route_with_delete_method__1.txt b/tests/Commands/Generators/__snapshots__/RouteMakeCommandTest__it_generated_web_route_with_delete_method__1.txt deleted file mode 100644 index 4b7870c..0000000 --- a/tests/Commands/Generators/__snapshots__/RouteMakeCommandTest__it_generated_web_route_with_delete_method__1.txt +++ /dev/null @@ -1,7 +0,0 @@ -name('web.nested.some_delete_route'); diff --git a/tests/Commands/Generators/__snapshots__/RouteMakeCommandTest__it_generated_web_route_with_get_method__1.txt b/tests/Commands/Generators/__snapshots__/RouteMakeCommandTest__it_generated_web_route_with_get_method__1.txt deleted file mode 100644 index 6f99f2a..0000000 --- a/tests/Commands/Generators/__snapshots__/RouteMakeCommandTest__it_generated_web_route_with_get_method__1.txt +++ /dev/null @@ -1,7 +0,0 @@ -name('web.nested.some_get_route'); diff --git a/tests/Commands/Generators/__snapshots__/RouteMakeCommandTest__it_generated_web_route_with_options_method__1.txt b/tests/Commands/Generators/__snapshots__/RouteMakeCommandTest__it_generated_web_route_with_options_method__1.txt deleted file mode 100644 index 6fe1882..0000000 --- a/tests/Commands/Generators/__snapshots__/RouteMakeCommandTest__it_generated_web_route_with_options_method__1.txt +++ /dev/null @@ -1,7 +0,0 @@ -name('web.nested.some_options_route'); diff --git a/tests/Commands/Generators/__snapshots__/RouteMakeCommandTest__it_generated_web_route_with_patch_method__1.txt b/tests/Commands/Generators/__snapshots__/RouteMakeCommandTest__it_generated_web_route_with_patch_method__1.txt deleted file mode 100644 index 51601a0..0000000 --- a/tests/Commands/Generators/__snapshots__/RouteMakeCommandTest__it_generated_web_route_with_patch_method__1.txt +++ /dev/null @@ -1,7 +0,0 @@ -name('web.nested.some_patch_route'); diff --git a/tests/Commands/Generators/__snapshots__/RouteMakeCommandTest__it_generated_web_route_with_post_method__1.txt b/tests/Commands/Generators/__snapshots__/RouteMakeCommandTest__it_generated_web_route_with_post_method__1.txt deleted file mode 100644 index 1a0caa5..0000000 --- a/tests/Commands/Generators/__snapshots__/RouteMakeCommandTest__it_generated_web_route_with_post_method__1.txt +++ /dev/null @@ -1,7 +0,0 @@ -name('web.nested.some_post_route'); diff --git a/tests/Commands/Generators/__snapshots__/RouteMakeCommandTest__it_generated_web_route_with_put_method__1.txt b/tests/Commands/Generators/__snapshots__/RouteMakeCommandTest__it_generated_web_route_with_put_method__1.txt deleted file mode 100644 index c8df1cc..0000000 --- a/tests/Commands/Generators/__snapshots__/RouteMakeCommandTest__it_generated_web_route_with_put_method__1.txt +++ /dev/null @@ -1,7 +0,0 @@ -name('web.nested.some_put_route'); diff --git a/tests/Commands/Generators/__snapshots__/RuleMakeCommandTest__it_can_change_the_default_namespace_for_rule_file__1.txt b/tests/Commands/Generators/__snapshots__/RuleMakeCommandTest__it_can_change_the_default_namespace_for_rule_file__1.txt deleted file mode 100644 index 164e3fb..0000000 --- a/tests/Commands/Generators/__snapshots__/RuleMakeCommandTest__it_can_change_the_default_namespace_for_rule_file__1.txt +++ /dev/null @@ -1,35 +0,0 @@ -handle(new CreatePermissionDTO( - name: 'view-my-awesome-model', - display_name: 'View any "my-awesome-models"', - group: 'my-awesome-models' - )); - - $createPermissionAction->handle(new CreatePermissionDTO( - name: 'create-my-awesome-model', - display_name: 'Create "my-awesome-models"', - group: 'my-awesome-models' - )); - - $createPermissionAction->handle(new CreatePermissionDTO( - name: 'update-my-awesome-model', - display_name: 'Update any "my-awesome-models"', - group: 'my-awesome-models' - )); - - $createPermissionAction->handle(new CreatePermissionDTO( - name: 'delete-my-awesome-model', - display_name: 'Delete any "my-awesome-models"', - group: 'my-awesome-models' - )); - - $createPermissionAction->handle(new CreatePermissionDTO( - name: 'force-delete-my-awesome-model', - display_name: 'Force delete any "my-awesome-models"', - group: 'my-awesome-models' - )); - } -} diff --git a/tests/Commands/Generators/__snapshots__/SeederMakeCommandTest__it_generated_correct_plain_seeder_file_with_content__1.txt b/tests/Commands/Generators/__snapshots__/SeederMakeCommandTest__it_generated_correct_plain_seeder_file_with_content__1.txt deleted file mode 100644 index 1856f3c..0000000 --- a/tests/Commands/Generators/__snapshots__/SeederMakeCommandTest__it_generated_correct_plain_seeder_file_with_content__1.txt +++ /dev/null @@ -1,13 +0,0 @@ - '', - 'roles' => '', - ]; - - public function test(): void - { - // - } -} diff --git a/tests/Commands/Generators/__snapshots__/TestMakeCommandTest__it_can_change_the_default_namespace_for_cli_test__1.txt b/tests/Commands/Generators/__snapshots__/TestMakeCommandTest__it_can_change_the_default_namespace_for_cli_test__1.txt deleted file mode 100644 index 53d5a30..0000000 --- a/tests/Commands/Generators/__snapshots__/TestMakeCommandTest__it_can_change_the_default_namespace_for_cli_test__1.txt +++ /dev/null @@ -1,17 +0,0 @@ - '', - 'roles' => '', - ]; - - public function test(): void - { - // - } -} diff --git a/tests/Commands/Generators/__snapshots__/TestMakeCommandTest__it_can_change_the_default_path_for_cli_test__1.txt b/tests/Commands/Generators/__snapshots__/TestMakeCommandTest__it_can_change_the_default_path_for_cli_test__1.txt deleted file mode 100644 index 53d5a30..0000000 --- a/tests/Commands/Generators/__snapshots__/TestMakeCommandTest__it_can_change_the_default_path_for_cli_test__1.txt +++ /dev/null @@ -1,17 +0,0 @@ - 'create-some-test-model', - 'roles' => '', - ]; - - protected function getTestData(array $mergeData = []): array - { - return array_merge([ - // TODO: add fields here - ], $mergeData); - } - - public function test_create_some_test_model(): void - { - $this->getTestingUser(); - - $data = $this->getTestData(); - - $this->postJson(route('some.api.url'), $data) - ->assertCreated() - ->assertJson(fn (AssertableJson $json) => - $json->has('data', fn (AssertableJson $json) => - $json->has('id') - ->whereAll($data) - ->etc() - ) - ); - - $this->assertExistsModelWhereColumns(SomeTestModel::class, $data); - } - - public function test_create_some_test_model_without_access(): void - { - $this->getTestingUserWithoutAccess(); - - $data = $this->getTestData(); - - $this->postJson(route('some.api.url'), $data) - ->assertForbidden(); - } -} diff --git a/tests/Commands/Generators/__snapshots__/TestMakeCommandTest__it_can_generate_api_delete_test_file__1.txt b/tests/Commands/Generators/__snapshots__/TestMakeCommandTest__it_can_generate_api_delete_test_file__1.txt deleted file mode 100644 index c9e259f..0000000 --- a/tests/Commands/Generators/__snapshots__/TestMakeCommandTest__it_can_generate_api_delete_test_file__1.txt +++ /dev/null @@ -1,51 +0,0 @@ - 'delete-some-test-model', - 'roles' => '', - ]; - - public function test_delete_some_test_model(): void - { - $this->getTestingUser(); - - $someTestModel = SomeTestModel::factory()->create(); - - $this->deleteJson(route('some.api.url', ['someTestModel' => $someTestModel->getKey()])) - ->assertNoContent(); - - $this->assertNull(SomeTestModel::find($someTestModel->getKey())); - } - - public function test_delete_some_test_model_without_access(): void - { - $this->getTestingUserWithoutAccess(); - - $someTestModel = SomeTestModel::factory()->create(); - - $this->deleteJson(route('some.api.url', ['someTestModel' => $someTestModel->getKey()])) - ->assertForbidden(); - } - - public function test_delete_not_existing_some_test_model(): void - { - $this->getTestingUser(); - - $this->deleteJson(route('some.api.url', ['someTestModel' => 7777])) - ->assertNotFound(); - } -} diff --git a/tests/Commands/Generators/__snapshots__/TestMakeCommandTest__it_can_generate_api_list_test_file__1.txt b/tests/Commands/Generators/__snapshots__/TestMakeCommandTest__it_can_generate_api_list_test_file__1.txt deleted file mode 100644 index b41bd37..0000000 --- a/tests/Commands/Generators/__snapshots__/TestMakeCommandTest__it_can_generate_api_list_test_file__1.txt +++ /dev/null @@ -1,47 +0,0 @@ - 'view-some-test-model', - 'roles' => '', - ]; - - public function test_list_some_test_models(): void - { - $this->getTestingUser(); - - SomeTestModel::factory()->count(2)->create(); - - $this->getJson(route('some.api.url')) - ->assertOk() - ->assertJsonStructure([ - 'links', - 'meta', - 'data' - ]) - ->assertJsonCount(SomeTestModel::query()->count(), 'data'); - } - - public function test_list_some_test_models_without_access(): void - { - $this->getTestingUserWithoutAccess(); - - SomeTestModel::factory()->count(2)->create(); - - $this->getJson(route('some.api.url')) - ->assertForbidden(); - } -} diff --git a/tests/Commands/Generators/__snapshots__/TestMakeCommandTest__it_can_generate_api_update_test_file__1.txt b/tests/Commands/Generators/__snapshots__/TestMakeCommandTest__it_can_generate_api_update_test_file__1.txt deleted file mode 100644 index cf6c9bd..0000000 --- a/tests/Commands/Generators/__snapshots__/TestMakeCommandTest__it_can_generate_api_update_test_file__1.txt +++ /dev/null @@ -1,74 +0,0 @@ - 'update-some-test-model', - 'roles' => '', - ]; - - protected function getTestData(array $mergeData = []): array - { - return array_merge([ - // TODO: add fields here - ], $mergeData); - } - - public function test_update_some_test_model(): void - { - $this->getTestingUser(); - - $someTestModel = SomeTestModel::factory()->create(); - - $data = $this->getTestData(); - $expectedData = array_merge($data, [ - 'id' => $someTestModel->getKey(), - ]); - - $this->patchJson(route('some.api.url', ['someTestModel' => $someTestModel->getKey()]), $data) - ->assertOk() - ->assertJson(fn (AssertableJson $json) => - $json->has('data', fn (AssertableJson $json) => - $json->whereAll($expectedData) - ->etc() - ) - ); - - $this->assertExistsModelWhereColumns(SomeTestModel::class, $expectedData); - } - - public function test_update_some_test_model_without_access(): void - { - $this->getTestingUserWithoutAccess(); - - $someTestModel = SomeTestModel::factory()->create(); - - $data = $this->getTestData(); - - $this->patchJson(route('some.api.url', ['someTestModel' => $someTestModel->getKey()]), $data) - ->assertForbidden(); - } - - public function test_update_non_existing_some_test_model(): void - { - $this->getTestingUser(); - - $data = $this->getTestData(); - - $this->patchJson(route('some.api.url', ['someTestModel' => 7777]), $data) - ->assertNotFound(); - } -} diff --git a/tests/Commands/Generators/__snapshots__/TestMakeCommandTest__it_can_generate_api_view_test_file__1.txt b/tests/Commands/Generators/__snapshots__/TestMakeCommandTest__it_can_generate_api_view_test_file__1.txt deleted file mode 100644 index a6fc00d..0000000 --- a/tests/Commands/Generators/__snapshots__/TestMakeCommandTest__it_can_generate_api_view_test_file__1.txt +++ /dev/null @@ -1,57 +0,0 @@ - 'view-some-test-model', - 'roles' => '', - ]; - - public function test_view_some_test_model(): void - { - $this->getTestingUser(); - - $someTestModel = SomeTestModel::factory()->create(); - $expectedData = $someTestModel->toArray(); - - $this->getJson(route('some.api.url', ['someTestModel' => $someTestModel->getKey()])) - ->assertOk() - ->assertJson(fn (AssertableJson $json) => - $json->has('data', fn (AssertableJson $json) => - $json->whereAll($expectedData) - ->etc() - ) - ); - } - - public function test_view_some_test_model_without_access(): void - { - $this->getTestingUserWithoutAccess(); - - $someTestModel = SomeTestModel::factory()->create(); - - $this->getJson(route('some.api.url', ['someTestModel' => $someTestModel->getKey()])) - ->assertForbidden(); - } - - public function test_view_not_existing_some_test_model(): void - { - $this->getTestingUser(); - - $this->getJson(route('some.api.url', ['someTestModel' => 7777])) - ->assertNotFound(); - } -} diff --git a/tests/Commands/Generators/__snapshots__/TestMakeCommandTest__it_can_generate_web_create_test_file__1.txt b/tests/Commands/Generators/__snapshots__/TestMakeCommandTest__it_can_generate_web_create_test_file__1.txt deleted file mode 100644 index 1c808cf..0000000 --- a/tests/Commands/Generators/__snapshots__/TestMakeCommandTest__it_can_generate_web_create_test_file__1.txt +++ /dev/null @@ -1,50 +0,0 @@ - 'create-some-test-model', - 'roles' => '', - ]; - - protected function getTestData(array $mergeData = []): array - { - return array_merge([ - // TODO: add fields here - ], $mergeData); - } - - public function test_create_some_test_model(): void - { - $this->getTestingUser(); - - $data = $this->getTestData(); - - $this->post(route('some.web.url'), $data) - ->assertCreated(); - - $this->assertExistsModelWhereColumns(SomeTestModel::class, $data); - } - - public function test_create_some_test_model_without_access(): void - { - $this->getTestingUserWithoutAccess(); - - $data = $this->getTestData(); - - $this->post(route('some.web.url'), $data) - ->assertForbidden(); - } -} diff --git a/tests/Commands/Generators/__snapshots__/TestMakeCommandTest__it_can_generate_web_delete_test_file__1.txt b/tests/Commands/Generators/__snapshots__/TestMakeCommandTest__it_can_generate_web_delete_test_file__1.txt deleted file mode 100644 index db175a9..0000000 --- a/tests/Commands/Generators/__snapshots__/TestMakeCommandTest__it_can_generate_web_delete_test_file__1.txt +++ /dev/null @@ -1,51 +0,0 @@ - 'delete-some-test-model', - 'roles' => '', - ]; - - public function test_delete_some_test_model(): void - { - $this->getTestingUser(); - - $someTestModel = SomeTestModel::factory()->create(); - - $this->delete(route('some.web.url', ['someTestModel' => $someTestModel->getKey()])) - ->assertNoContent(); - - $this->assertNull(SomeTestModel::find($someTestModel->getKey())); - } - - public function test_delete_some_test_model_without_access(): void - { - $this->getTestingUserWithoutAccess(); - - $someTestModel = SomeTestModel::factory()->create(); - - $this->delete(route('some.web.url', ['someTestModel' => $someTestModel->getKey()])) - ->assertForbidden(); - } - - public function test_delete_not_existing_some_test_model(): void - { - $this->getTestingUser(); - - $this->delete(route('some.web.url', ['someTestModel' => 7777])) - ->assertNotFound(); - } -} diff --git a/tests/Commands/Generators/__snapshots__/TestMakeCommandTest__it_can_generate_web_update_test_file__1.txt b/tests/Commands/Generators/__snapshots__/TestMakeCommandTest__it_can_generate_web_update_test_file__1.txt deleted file mode 100644 index ebf43fa..0000000 --- a/tests/Commands/Generators/__snapshots__/TestMakeCommandTest__it_can_generate_web_update_test_file__1.txt +++ /dev/null @@ -1,67 +0,0 @@ - 'update-some-test-model', - 'roles' => '', - ]; - - protected function getTestData(array $mergeData = []): array - { - return array_merge([ - // TODO: add fields here - ], $mergeData); - } - - public function test_update_some_test_model(): void - { - $this->getTestingUser(); - - $someTestModel = SomeTestModel::factory()->create(); - - $data = $this->getTestData(); - $expectedData = array_merge($data, [ - 'id' => $someTestModel->getKey(), - ]); - - $this->patch(route('some.web.url', ['someTestModel' => $someTestModel->getKey()]), $data) - ->assertOk(); - - $this->assertExistsModelWhereColumns(SomeTestModel::class, $expectedData); - } - - public function test_update_some_test_modelWithoutAccess(): void - { - $this->getTestingUserWithoutAccess(); - - $someTestModel = SomeTestModel::factory()->create(); - - $data = $this->getTestData(); - - $this->patch(route('some.web.url', ['someTestModel' => $someTestModel->getKey()]), $data) - ->assertForbidden(); - } - - public function test_update_non_existing_some_test_model(): void - { - $this->getTestingUser(); - - $data = $this->getTestData(); - - $this->patch(route('some.web.url', ['someTestModel' => 7777]), $data) - ->assertNotFound(); - } -} diff --git a/tests/Commands/Generators/__snapshots__/TestMakeCommandTest__it_generated_correct_api_test_file_with_content__1.txt b/tests/Commands/Generators/__snapshots__/TestMakeCommandTest__it_generated_correct_api_test_file_with_content__1.txt deleted file mode 100644 index 42e323e..0000000 --- a/tests/Commands/Generators/__snapshots__/TestMakeCommandTest__it_generated_correct_api_test_file_with_content__1.txt +++ /dev/null @@ -1,25 +0,0 @@ - '', - 'roles' => '', - ]; - - public function test(): void - { - // - } -} diff --git a/tests/Commands/Generators/__snapshots__/TestMakeCommandTest__it_generated_correct_cli_test_file_with_content__1.txt b/tests/Commands/Generators/__snapshots__/TestMakeCommandTest__it_generated_correct_cli_test_file_with_content__1.txt deleted file mode 100644 index a4ae95e..0000000 --- a/tests/Commands/Generators/__snapshots__/TestMakeCommandTest__it_generated_correct_cli_test_file_with_content__1.txt +++ /dev/null @@ -1,17 +0,0 @@ -setModules([ + __DIR__ . '/../fixtures/stubs/modules/valid/article-category', + __DIR__ . '/../fixtures/stubs/modules/valid/article', + __DIR__ . '/../fixtures/stubs/modules/valid/author', + __DIR__ . '/../fixtures/stubs/modules/valid/empty-module', + __DIR__ . '/../fixtures/stubs/modules/valid/empty', + __DIR__ . '/../fixtures/stubs/modules/valid/navigation', + ], $this->app->basePath('/modules')); +}); -use Laraneat\Modules\Contracts\RepositoryInterface; -use Laraneat\Modules\Tests\BaseTestCase; +it('outputs a table of modules', function () { + $this->artisan('module:list') + ->expectsTable(['Package Name', 'Namespace', 'Path'], [ + ['laraneat/article-category', 'Modules\\ArticleCategory', $this->app->basePath('/modules/article-category')], + ['laraneat/article', 'Modules\\Article', $this->app->basePath('/modules/article')], + ['laraneat/author', 'Modules\\Author', $this->app->basePath('/modules/author')], + ['laraneat/empty', 'Modules\\Empty', $this->app->basePath('/modules/empty-module')], + ['empty/empty', 'Empty\\Empty', $this->app->basePath('/modules/empty')], + ['laraneat/location', 'Modules\\GeoLocation', $this->app->basePath('/modules/navigation')], -/** - * @group command - */ -class ListCommandTest extends BaseTestCase -{ - protected function setUp(): void - { - parent::setUp(); - $this->artisan('module:make', ['name' => 'Article']); - } - - protected function tearDown(): void - { - $this->app[RepositoryInterface::class]->delete('Article'); - parent::tearDown(); - } - - /** @test */ - public function it_can_list_modules() - { - $code = $this->artisan('module:list'); - - // We just want to make sure nothing throws an exception inside the list command - $this->assertTrue(true); - $this->assertSame(0, $code); - } -} + ]) + ->assertSuccessful(); +}); diff --git a/tests/Commands/MigrateCommandTest.php b/tests/Commands/MigrateCommandTest.php new file mode 100644 index 0000000..a477d21 --- /dev/null +++ b/tests/Commands/MigrateCommandTest.php @@ -0,0 +1,37 @@ +setModules([ + __DIR__ . '/../fixtures/stubs/modules/valid/author', + ], $this->app->basePath('/modules')); +}); + +it('runs migrations for the specified module', function () { + $this->artisan('module:migrate', [ + 'module' => 'Author', + '--pretend' => true, + ]) + ->assertSuccessful(); +}); + +it('requires confirmation in production', function () { + $this->app['env'] = 'production'; + + $this->artisan('module:migrate', [ + 'module' => 'Author', + '--pretend' => true, + ]) + ->expectsConfirmation('Are you sure you want to run this command?', 'no') + ->assertFailed(); +}); + +it('can be forced in production', function () { + $this->app['env'] = 'production'; + + $this->artisan('module:migrate', [ + 'module' => 'Author', + '--pretend' => true, + '--force' => true, + ]) + ->assertSuccessful(); +}); diff --git a/tests/Commands/MigrateRefreshCommandTest.php b/tests/Commands/MigrateRefreshCommandTest.php new file mode 100644 index 0000000..cfb0555 --- /dev/null +++ b/tests/Commands/MigrateRefreshCommandTest.php @@ -0,0 +1,37 @@ +setModules([ + __DIR__ . '/../fixtures/stubs/modules/valid/author', + ], $this->app->basePath('/modules')); +}); + +it('refreshes migrations for the specified module', function () { + $this->artisan('module:migrate:refresh', [ + 'module' => 'Author', + '--pretend' => true, + ]) + ->assertSuccessful(); +}); + +it('requires confirmation in production', function () { + $this->app['env'] = 'production'; + + $this->artisan('module:migrate:refresh', [ + 'module' => 'Author', + '--pretend' => true, + ]) + ->expectsConfirmation('Are you sure you want to run this command?', 'no') + ->assertFailed(); +}); + +it('can be forced in production', function () { + $this->app['env'] = 'production'; + + $this->artisan('module:migrate:refresh', [ + 'module' => 'Author', + '--pretend' => true, + '--force' => true, + ]) + ->assertSuccessful(); +}); diff --git a/tests/Commands/MigrateResetCommandTest.php b/tests/Commands/MigrateResetCommandTest.php new file mode 100644 index 0000000..9e8812f --- /dev/null +++ b/tests/Commands/MigrateResetCommandTest.php @@ -0,0 +1,37 @@ +setModules([ + __DIR__ . '/../fixtures/stubs/modules/valid/author', + ], $this->app->basePath('/modules')); +}); + +it('resets migrations for the specified module', function () { + $this->artisan('module:migrate:reset', [ + 'module' => 'Author', + '--pretend' => true, + ]) + ->assertSuccessful(); +}); + +it('requires confirmation in production', function () { + $this->app['env'] = 'production'; + + $this->artisan('module:migrate:reset', [ + 'module' => 'Author', + '--pretend' => true, + ]) + ->expectsConfirmation('Are you sure you want to run this command?', 'no') + ->assertFailed(); +}); + +it('can be forced in production', function () { + $this->app['env'] = 'production'; + + $this->artisan('module:migrate:reset', [ + 'module' => 'Author', + '--pretend' => true, + '--force' => true, + ]) + ->assertSuccessful(); +}); diff --git a/tests/Commands/MigrateRollbackCommandTest.php b/tests/Commands/MigrateRollbackCommandTest.php new file mode 100644 index 0000000..9c07dba --- /dev/null +++ b/tests/Commands/MigrateRollbackCommandTest.php @@ -0,0 +1,37 @@ +setModules([ + __DIR__ . '/../fixtures/stubs/modules/valid/author', + ], $this->app->basePath('/modules')); +}); + +it('rolls back migrations for the specified module', function () { + $this->artisan('module:migrate:rollback', [ + 'module' => 'Author', + '--pretend' => true, + ]) + ->assertSuccessful(); +}); + +it('requires confirmation in production', function () { + $this->app['env'] = 'production'; + + $this->artisan('module:migrate:rollback', [ + 'module' => 'Author', + '--pretend' => true, + ]) + ->expectsConfirmation('Are you sure you want to run this command?', 'no') + ->assertFailed(); +}); + +it('can be forced in production', function () { + $this->app['env'] = 'production'; + + $this->artisan('module:migrate:rollback', [ + 'module' => 'Author', + '--pretend' => true, + '--force' => true, + ]) + ->assertSuccessful(); +}); diff --git a/tests/Commands/MigrateStatusCommandTest.php b/tests/Commands/MigrateStatusCommandTest.php new file mode 100644 index 0000000..457911e --- /dev/null +++ b/tests/Commands/MigrateStatusCommandTest.php @@ -0,0 +1,24 @@ +setModules([ + __DIR__ . '/../fixtures/stubs/modules/valid/author', + ], $this->app->basePath('/modules')); +}); + +it('shows migration status for the specified module', function () { + $this->artisan('module:migrate:status', [ + 'module' => 'Author', + ]) + ->assertSuccessful(); +}); + +it('does not require confirmation in production', function () { + $this->app['env'] = 'production'; + + // migrate:status should not require confirmation as it's read-only + $this->artisan('module:migrate:status', [ + 'module' => 'Author', + ]) + ->assertSuccessful(); +}); diff --git a/tests/Commands/ModuleDeleteCommandTest.php b/tests/Commands/ModuleDeleteCommandTest.php index 7e9b851..ac4e6c9 100644 --- a/tests/Commands/ModuleDeleteCommandTest.php +++ b/tests/Commands/ModuleDeleteCommandTest.php @@ -1,48 +1,44 @@ finder = $this->app['files']; - $this->activator = new FileActivator($this->app); - } - - /** @test */ - public function it_can_delete_a_module_from_disk(): void - { - $this->artisan('module:make', ['name' => 'WrongModule']); - $this->assertDirectoryExists(base_path('app/Modules/WrongModule')); - - $code = $this->artisan('module:delete', ['module' => 'WrongModule']); - $this->assertDirectoryDoesNotExist(base_path('app/Modules/WrongModule')); - $this->assertSame(0, $code); - } - - /** @test */ - public function it_deletes_modules_from_status_file(): void - { - $this->artisan('module:make', ['name' => 'WrongModule']); - $this->assertMatchesSnapshot($this->finder->get($this->activator->getStatusesFilePath())); - - $code = $this->artisan('module:delete', ['module' => 'WrongModule']); - $this->assertMatchesSnapshot($this->finder->get($this->activator->getStatusesFilePath())); - $this->assertSame(0, $code); - } -} +use Laraneat\Modules\ModulesRepository; +use Laraneat\Modules\Support\Composer; + +beforeEach(function () { + // Set mock BEFORE anything else so Module instances get the mock + $this->instance(Composer::class, $this->mockComposer(['removePackages' => true])); + + // Rebind ModulesRepository with the mocked Composer + $this->app->singleton(ModulesRepository::class, function ($app) { + return new ModulesRepository( + filesystem: $app['files'], + composer: $app[Composer::class], + modulesPath: $app['config']->get('modules.path'), + basePath: $app->basePath(), + modulesManifestPath: $app['config']->get('modules.cache.enabled') + ? $app->bootstrapPath('cache/laraneat-modules.php') + : null + ); + }); + + $this->setModules([ + __DIR__ . '/../fixtures/stubs/modules/valid/article-category', + __DIR__ . '/../fixtures/stubs/modules/valid/article', + __DIR__ . '/../fixtures/stubs/modules/valid/author', + __DIR__ . '/../fixtures/stubs/modules/valid/empty-module', + __DIR__ . '/../fixtures/stubs/modules/valid/empty', + __DIR__ . '/../fixtures/stubs/modules/valid/navigation', + ], $this->app->basePath('/modules')); + + /** @var ModulesRepository $modulesRepository */ + $modulesRepository = $this->app[ModulesRepository::class]; + $this->modulesRepository = $modulesRepository; +}); + +it('deletes a module', function () { + expect($this->modulesRepository->has('laraneat/article'))->toBe(true); + + $this->artisan('module:delete article') + ->assertSuccessful(); + + expect($this->modulesRepository->has('laraneat/article'))->toBe(false); +}); diff --git a/tests/Commands/StubPublishCommandTest.php b/tests/Commands/StubPublishCommandTest.php new file mode 100644 index 0000000..482dd3f --- /dev/null +++ b/tests/Commands/StubPublishCommandTest.php @@ -0,0 +1,18 @@ +customStubsPath = $this->app['config']->get('modules.custom_stubs'); +}); + +afterEach(function () { + $this->filesystem->deleteDirectory($this->customStubsPath); +}); + +it('publish all laraneat/modules stubs', function () { + $this->artisan('module:stub:publish') + ->assertSuccessful(); + + assertMatchesSnapshot(getRelativeFilePathsInDirectory($this->customStubsPath)); +}); diff --git a/tests/Commands/SyncCommandTest.php b/tests/Commands/SyncCommandTest.php new file mode 100644 index 0000000..1726444 --- /dev/null +++ b/tests/Commands/SyncCommandTest.php @@ -0,0 +1,62 @@ +mock(ModulesRepository::class, function (MockInterface $mock) { + $mock->shouldReceive('pruneModulesManifest')->once(); + $mock->shouldReceive('syncWithComposer')->once(); + }); + + $this->artisan('module:sync') + ->expectsOutputToContain('Modules completed successfully!') + ->assertSuccessful(); +}); + +it('handles ModuleHasNoNamespace exception', function () { + $this->mock(ModulesRepository::class, function (MockInterface $mock) { + $mock->shouldReceive('pruneModulesManifest')->once(); + $mock->shouldReceive('syncWithComposer')->once()->andThrow( + ModuleHasNoNamespace::make('test/module') + ); + }); + + $this->artisan('module:sync') + ->expectsOutputToContain('No namespace specified for module') + ->assertSuccessful(); +}); + +it('handles ModuleHasNonUniquePackageName exception', function () { + $this->mock(ModulesRepository::class, function (MockInterface $mock) { + $mock->shouldReceive('pruneModulesManifest')->once(); + $mock->shouldReceive('syncWithComposer')->once()->andThrow( + ModuleHasNonUniquePackageName::make('test/module', ['/path1', '/path2']) + ); + }); + + $this->artisan('module:sync') + ->expectsOutputToContain('test/module') + ->assertSuccessful(); +}); + +it('handles ComposerException and shows manual update hint', function () { + $this->mock(ModulesRepository::class, function (MockInterface $mock) { + $mock->shouldReceive('pruneModulesManifest')->once(); + $mock->shouldReceive('syncWithComposer')->once()->andThrow( + ComposerException::make('Failed to update package with composer.') + ); + $mock->shouldReceive('getModules')->once()->andReturn([ + 'test/module-a' => (object) [], + 'test/module-b' => (object) [], + ]); + }); + + $this->artisan('module:sync') + ->expectsOutputToContain('Failed to update package with composer') + ->expectsOutputToContain('composer update test/module-a test/module-b') + ->assertSuccessful(); +}); diff --git a/tests/Commands/__snapshots__/ModuleDeleteCommandTest__it_deletes_modules_from_status_file__1.txt b/tests/Commands/__snapshots__/ModuleDeleteCommandTest__it_deletes_modules_from_status_file__1.txt deleted file mode 100644 index e817d83..0000000 --- a/tests/Commands/__snapshots__/ModuleDeleteCommandTest__it_deletes_modules_from_status_file__1.txt +++ /dev/null @@ -1,3 +0,0 @@ -{ - "WrongModule": true -} \ No newline at end of file diff --git a/tests/Commands/__snapshots__/ModuleDeleteCommandTest__it_deletes_modules_from_status_file__2.txt b/tests/Commands/__snapshots__/ModuleDeleteCommandTest__it_deletes_modules_from_status_file__2.txt deleted file mode 100644 index 0637a08..0000000 --- a/tests/Commands/__snapshots__/ModuleDeleteCommandTest__it_deletes_modules_from_status_file__2.txt +++ /dev/null @@ -1 +0,0 @@ -[] \ No newline at end of file diff --git a/tests/FileRepositoryTest.php b/tests/FileRepositoryTest.php deleted file mode 100644 index ff0b227..0000000 --- a/tests/FileRepositoryTest.php +++ /dev/null @@ -1,227 +0,0 @@ -repository = new FileRepository($this->app); - $this->activator = $this->app[ActivatorInterface::class]; - } - - protected function tearDown(): void - { - $this->activator->reset(); - parent::tearDown(); - } - - /** @test */ - public function it_adds_location_to_paths() - { - $this->repository->addLocation('some/path'); - - $paths = $this->repository->getPaths(); - $this->assertCount(1, $paths); - $this->assertEquals('some/path', $paths[0]); - } - - /** @test */ - public function it_returns_all_enabled_modules() - { - $this->repository->addLocation(__DIR__ . '/fixtures/stubs/valid'); - - $this->assertCount(0, $this->repository->getByStatus(true)); - $this->assertCount(0, $this->repository->allEnabled()); - } - - /** @test */ - public function it_returns_all_disabled_modules() - { - $this->repository->addLocation(__DIR__ . '/fixtures/stubs/valid'); - - $this->assertCount(2, $this->repository->getByStatus(false)); - $this->assertCount(2, $this->repository->allDisabled()); - } - - /** @test */ - public function it_counts_all_modules() - { - $this->repository->addLocation(__DIR__ . '/fixtures/stubs/valid'); - - $this->assertEquals(2, $this->repository->count()); - } - - /** @test */ - public function it_finds_a_module() - { - $this->repository->addLocation(__DIR__ . '/fixtures/stubs/valid'); - - $this->assertInstanceOf(Module::class, $this->repository->find('article')); - } - - /** @test */ - public function it_finds_a_module_by_alias() - { - $this->repository->addLocation(__DIR__ . '/fixtures/stubs/valid'); - - $this->assertInstanceOf(Module::class, $this->repository->findByAlias('article')); - $this->assertInstanceOf(Module::class, $this->repository->findByAlias('required_module')); - } - - /** @test */ - public function it_find_or_fail_throws_exception_if_module_not_found() - { - $this->expectException(ModuleNotFoundException::class); - - $this->repository->findOrFail('something'); - } - - /** @test */ - public function it_finds_the_module_asset_path() - { - $this->repository->addLocation(__DIR__ . '/fixtures/stubs/valid/Article'); - $assetPath = $this->repository->assetPath('article'); - - $this->assertEquals(public_path('modules/article'), $assetPath); - } - - /** @test */ - public function it_gets_the_used_storage_path() - { - $path = $this->repository->getUsedStoragePath(); - - $this->assertEquals(storage_path('app/modules/modules.used'), $path); - } - - /** @test */ - public function it_sets_used_module() - { - $this->repository->addLocation(__DIR__ . '/fixtures/stubs/valid'); - - $this->repository->setUsed('Article'); - - $this->assertEquals('Article', $this->repository->getUsedNow()); - } - - /** @test */ - public function it_gets_the_assets_path() - { - $this->assertEquals(public_path('modules'), $this->repository->getAssetsPath()); - } - - /** @test */ - public function it_gets_a_specific_module_asset() - { - $path = $this->repository->asset('article:test.js'); - - $this->assertEquals('//localhost/modules/article/test.js', $path); - } - - /** @test */ - public function it_throws_exception_if_module_is_omitted() - { - $this->expectException(InvalidAssetPath::class); - $this->expectExceptionMessage('Module name was not specified in asset [test.js].'); - - $this->repository->asset('test.js'); - } - - /** @test */ - public function it_can_detect_if_module_is_active() - { - $this->repository->addLocation(__DIR__ . '/fixtures/stubs/valid'); - - $this->repository->enable('Article'); - - $this->assertTrue($this->repository->isEnabled('Article')); - } - - /** @test */ - public function it_can_detect_if_module_is_inactive() - { - $this->repository->addLocation(__DIR__ . '/fixtures/stubs/valid'); - - $this->repository->isDisabled('Article'); - - $this->assertTrue($this->repository->isDisabled('Article')); - } - - /** @test */ - public function it_can_disabled_a_module() - { - $this->repository->addLocation(__DIR__ . '/fixtures/stubs/valid'); - - $this->repository->disable('Article'); - - $this->assertTrue($this->repository->isDisabled('Article')); - } - - /** @test */ - public function it_can_enable_a_module() - { - $this->repository->addLocation(__DIR__ . '/fixtures/stubs/valid'); - - $this->repository->enable('Article'); - - $this->assertTrue($this->repository->isEnabled('Article')); - } - - /** @test */ - public function it_can_delete_a_module() - { - $this->artisan('module:make', ['name' => 'Article']); - - $this->repository->delete('Article'); - - $this->assertDirectoryDoesNotExist(base_path('app/Modules/Article')); - } - - /** @test */ - public function it_can_find_all_requirements_of_a_module() - { - $this->repository->addLocation(__DIR__ . '/fixtures/stubs/valid'); - - $requirements = $this->repository->findRequirements('Article'); - - $this->assertCount(1, $requirements); - $this->assertInstanceOf(Module::class, $requirements[0]); - } - - /** @test */ - public function it_can_register_macros() - { - Module::macro('registeredMacro', function () {}); - - $this->assertTrue(Module::hasMacro('registeredMacro')); - } - - /** @test */ - public function it_does_not_have_unregistered_macros() - { - $this->assertFalse(Module::hasMacro('unregisteredMacro')); - } - - /** @test */ - public function it_calls_macros_on_modules() - { - Module::macro('getReverseName', function () { - return strrev($this->getName()); - }); - - $this->repository->addLocation(__DIR__ . '/fixtures/stubs/valid'); - $module = $this->repository->find('article'); - - $this->assertEquals('elcitrA', $module->getReverseName()); - } -} diff --git a/tests/GeneratorHelperReaderTest.php b/tests/GeneratorHelperReaderTest.php deleted file mode 100644 index 59d8aa3..0000000 --- a/tests/GeneratorHelperReaderTest.php +++ /dev/null @@ -1,93 +0,0 @@ -finder = $this->app['files']; - $this->modulePath = base_path('app/Modules/Article'); - } - - private function makeModule(): void - { - $this->artisan('module:make', ['name' => 'Article']); - } - - private function removeModule(): void - { - $this->finder->deleteDirectory($this->modulePath); - } - - /** @test */ - public function it_can_read_component_configuration() - { - $seedConfig = GeneratorHelper::component('seeder'); - - $this->assertEquals('Data/Seeders', $seedConfig->getPath()); - $this->assertEquals('Data\\Seeders', $seedConfig->getNamespace()); - $this->assertTrue($seedConfig->generate()); - $this->assertFalse($seedConfig->withGitKeep()); - } - - /** @test */ - public function it_can_guess_namespace_from_path() - { - $this->app['config']->set('modules.generator.components.provider', [ - 'path' => 'Base/Providers', - 'generate' => true, - 'gitkeep' => true - ]); - - $config = GeneratorHelper::component('provider'); - - $this->assertEquals('Base/Providers', $config->getPath()); - $this->assertEquals('Base\\Providers', $config->getNamespace()); - $this->assertTrue($config->generate()); - $this->assertTrue($config->withGitKeep()); - } - - /** @test */ - public function it_can_read_component_full_path_by_existing_module() - { - $this->makeModule(); - $resourceFullPath = GeneratorHelper::component('api-resource')->getFullPath('Article'); - - $this->assertEquals(base_path('app/Modules/Article/UI/API/Resources'), $resourceFullPath); - $this->removeModule(); - } - - /** @test */ - public function it_can_read_component_full_namespace_by_existing_module() - { - $this->makeModule(); - $resourceFullNamespace = GeneratorHelper::component('api-resource')->getFullNamespace('Article'); - - $this->assertEquals('App\\Modules\\Article\\UI\\API\\Resources', $resourceFullNamespace); - $this->removeModule(); - } - - /** @test */ - public function it_can_read_component_full_path_by_not_existing_module() - { - $resourceFullPath = GeneratorHelper::component('api-resource')->getFullPath('SomeModule'); - - $this->assertEquals(base_path('app/Modules/SomeModule/UI/API/Resources'), $resourceFullPath); - } - - /** @test */ - public function it_can_read_component_full_namespace_by_not_existing_module() - { - $resourceFullNamespace = GeneratorHelper::component('api-resource')->getFullNamespace('SomeModule'); - - $this->assertEquals('App\\Modules\\SomeModule\\UI\\API\\Resources', $resourceFullNamespace); - } -} diff --git a/tests/HelpersTest.php b/tests/HelpersTest.php deleted file mode 100644 index 9551de0..0000000 --- a/tests/HelpersTest.php +++ /dev/null @@ -1,38 +0,0 @@ -modulePath = base_path('app/Modules/Article'); - $this->finder = $this->app['files']; - $this->artisan('module:make', ['name' => 'Article']); - } - - protected function tearDown(): void - { - $this->finder->deleteDirectory($this->modulePath); - parent::tearDown(); - } - - /** @test */ - public function it_finds_the_module_path() - { - $this->assertTrue(Str::contains(module_path('Article'), 'app/Modules/Article')); - } - - /** @test */ - public function it_can_bind_a_relative_path_to_module_path() - { - $this->assertTrue(Str::contains(module_path('Article', 'config/config.php'), 'app/Modules/Article/config/config.php')); - } -} diff --git a/tests/JsonTest.php b/tests/JsonTest.php deleted file mode 100644 index 2bffbec..0000000 --- a/tests/JsonTest.php +++ /dev/null @@ -1,133 +0,0 @@ -json = new Json($path, $this->app['files']); - } - - /** @test */ - public function it_gets_the_file_path() - { - $path = __DIR__ . '/fixtures/stubs/valid/module.json'; - - $this->assertEquals($path, $this->json->getPath()); - } - - /** @test */ - public function it_throws_an_exception_with_invalid_json() - { - $path = __DIR__ . '/fixtures/stubs/InvalidJsonModule/module.json'; - - $this->expectException(JsonException::class); - - new Json($path, $this->app['files']); - } - - /** @test */ - public function it_gets_attributes_from_json_file() - { - $this->assertEquals('Order', $this->json->get('name')); - $this->assertEquals('order', $this->json->get('alias')); - $this->assertEquals('My demo module', $this->json->get('description')); - $this->assertEquals('0.1', $this->json->get('version')); - $this->assertEquals(['my', 'stub', 'module'], $this->json->get('keywords')); - $this->assertEquals(1, $this->json->get('active')); - $this->assertEquals(1, $this->json->get('order')); - } - - /** @test */ - public function it_reads_attributes_from_magic_get_method() - { - $this->assertEquals('Order', $this->json->name); - $this->assertEquals('order', $this->json->alias); - $this->assertEquals('My demo module', $this->json->description); - $this->assertEquals('0.1', $this->json->version); - $this->assertEquals(['my', 'stub', 'module'], $this->json->keywords); - $this->assertEquals(1, $this->json->active); - $this->assertEquals(1, $this->json->order); - } - - /** @test */ - public function it_sets_a_path() - { - $path = __DIR__ . '/fixtures/stubs/valid/module.json'; - $this->assertEquals($path, $this->json->getPath()); - - $this->json->setPath('some/path.json'); - $this->assertEquals('some/path.json', $this->json->getPath()); - } - - /** @test */ - public function it_decodes_json() - { - $expected = '{ - "name": "Order", - "alias": "order", - "namespace": "App\\\\Modules", - "description": "My demo module", - "version": "0.1", - "keywords": [ - "my", - "stub", - "module" - ], - "active": 1, - "order": 1, - "providers": [ - "App\\\\Modules\\\\Order\\\\Providers\\\\OrderServiceProvider", - "App\\\\Modules\\\\Order\\\\Providers\\\\EventServiceProvider", - "App\\\\Modules\\\\Order\\\\Providers\\\\RouteServiceProvider" - ], - "aliases": [], - "files": [] -}'; - $this->assertEquals($expected, $this->json->toJsonPretty()); - } - - /** @test */ - public function it_sets_a_key_value() - { - $this->json->set('key', 'value'); - - $this->assertEquals('value', $this->json->get('key')); - } - - /** @test */ - public function it_can_be_casted_to_string() - { - $expected = '{ - "name": "Order", - "alias": "order", - "namespace": "App\\\\Modules", - "description": "My demo module", - "version": "0.1", - "keywords": [ - "my", - "stub", - "module" - ], - "active": 1, - "order": 1, - "providers": [ - "App\\\\Modules\\\\Order\\\\Providers\\\\OrderServiceProvider", - "App\\\\Modules\\\\Order\\\\Providers\\\\EventServiceProvider", - "App\\\\Modules\\\\Order\\\\Providers\\\\RouteServiceProvider" - ], - "aliases": [], - "files": [] -} -'; - $this->assertEquals($expected, (string)$this->json); - } -} diff --git a/tests/ModuleTest.php b/tests/ModuleTest.php index 9db67bd..1b49a92 100644 --- a/tests/ModuleTest.php +++ b/tests/ModuleTest.php @@ -1,246 +1,272 @@ module = new TestingModule( - $this->app, - 'Article Name', - __DIR__ . '/fixtures/stubs/valid/Article', - 'App\\Modules\\Article' - ); - $this->activator = $this->app[ActivatorInterface::class]; - } - - protected function tearDown(): void - { - $this->activator->reset(); - parent::tearDown(); - } - - public static function setUpBeforeClass(): void - { - parent::setUpBeforeClass(); - symlink(__DIR__ . '/fixtures/stubs/valid', __DIR__ . '/fixtures/stubs/valid_symlink'); - } - - public static function tearDownAfterClass(): void - { - parent::tearDownAfterClass(); - unlink(__DIR__ . '/fixtures/stubs/valid_symlink'); - } - - /** @test */ - public function it_gets_module_name() - { - $this->assertEquals('Article Name', $this->module->getName()); - } - - /** @test */ - public function it_gets_module_key() - { - $this->assertEquals('article-name', $this->module->getKey()); - } - - /** @test */ - public function it_gets_studly_name() - { - $this->assertEquals('ArticleName', $this->module->getStudlyName()); - } - - /** @test */ - public function it_gets_snake_name() - { - $this->assertEquals('article_name', $this->module->getSnakeName()); - } - - /** @test */ - public function it_gets_module_description() - { - $this->assertEquals('article module', $this->module->getDescription()); - } - - /** @test */ - public function it_gets_module_alias() - { - $this->assertEquals('article', $this->module->getAlias()); - } - - /** @test */ - public function it_gets_module_path() - { - $this->assertEquals(__DIR__ . '/fixtures/stubs/valid/Article', $this->module->getPath()); - } - - /** @test */ - public function it_gets_module_path_with_symlink() - { - // symlink created in setUpBeforeClass - - $this->module = new TestingModule( - $this->app, - 'Article Name', - __DIR__ . '/fixtures/stubs/valid_symlink/Article', - 'App\\Module\\Article' - ); - - $this->assertEquals(__DIR__ . '/fixtures/stubs/valid_symlink/Article', $this->module->getPath()); - - // symlink deleted in tearDownAfterClass - } - - /** @test */ - public function it_gets_required_modules() - { - $this->assertEquals(['required_module'], $this->module->getRequires()); - } - - /** @test */ - public function it_reads_module_json_files() - { - $jsonModule = $this->module->json(); - $composerJson = $this->module->json('composer.json'); - - $this->assertEquals('0.1', $jsonModule->get('version')); - $this->assertEquals('laraneat/article', $composerJson->get('name')); - } - - /** @test */ - public function it_reads_key_from_module_json_file_via_helper_method() - { - $this->assertEquals('Article', $this->module->get('name')); - $this->assertEquals('0.1', $this->module->get('version')); - $this->assertEquals('my default', $this->module->get('some-thing-non-there', 'my default')); - $this->assertEquals(['required_module'], $this->module->get('requires')); - } - - /** @test */ - public function it_reads_key_from_composer_json_file_via_helper_method() - { - $this->assertEquals('laraneat/article', $this->module->getComposerAttr('name')); - } - - /** @test */ - public function it_casts_module_to_string() - { - $this->assertEquals('ArticleName', (string) $this->module); - } - - /** @test */ - public function it_module_status_check() - { - $this->assertFalse($this->module->isStatus(true)); - $this->assertTrue($this->module->isStatus(false)); - } - - /** @test */ - public function it_checks_module_enabled_status() - { - $this->assertFalse($this->module->isEnabled()); - $this->assertTrue($this->module->isDisabled()); - } - - /** @test */ - public function it_sets_active_status(): void - { - $this->module->setActive(true); - $this->assertTrue($this->module->isEnabled()); - $this->module->setActive(false); - $this->assertFalse($this->module->isEnabled()); - } - - /** @test */ - public function it_fires_events_when_module_is_enabled() - { - Event::fake(); - - $this->module->enable(); - - Event::assertDispatched(sprintf('modules.%s.enabling', $this->module->getKey())); - Event::assertDispatched(sprintf('modules.%s.enabled', $this->module->getKey())); - } - - /** @test */ - public function it_fires_events_when_module_is_disabled() - { - Event::fake(); - - $this->module->disable(); - - Event::assertDispatched(sprintf('modules.%s.disabling', $this->module->getKey())); - Event::assertDispatched(sprintf('modules.%s.disabled', $this->module->getKey())); - } - - /** @test */ - public function it_has_a_good_providers_manifest_path() - { - $this->assertEquals( - $this->app->bootstrapPath("cache/{$this->module->getSnakeName()}_module.php"), - $this->module->getCachedServicesPath() - ); - } - - /** @test */ - public function it_makes_a_manifest_file_when_providers_are_loaded() - { - $cachedServicesPath = $this->module->getCachedServicesPath(); - - @unlink($cachedServicesPath); - $this->assertFileDoesNotExist($cachedServicesPath); - - $this->module->registerProviders(); - - $this->assertFileExists($cachedServicesPath); - $manifest = require $cachedServicesPath; - - $this->assertEquals([ - 'providers' => [ - ArticleServiceProvider::class, - DeferredServiceProvider::class, - ], - 'eager' => [ArticleServiceProvider::class], - 'deferred' => ['deferred' => DeferredServiceProvider::class], - 'when' => - [DeferredServiceProvider::class => []], - ], $manifest); - } - - /** @test */ - public function it_can_load_a_deferred_provider() - { - @unlink($this->module->getCachedServicesPath()); - - $this->module->registerProviders(); - - try { - app('foo'); - $this->fail("app('foo') should throw an exception."); - } catch (\Exception $e) { - $this->assertEquals('Target class [foo] does not exist.', $e->getMessage()); - } - - app('deferred'); - - $this->assertEquals('bar', app('foo')); - } -} - -class TestingModule extends \Laraneat\Modules\Module -{ - public function registerProviders(): void - { - parent::registerProviders(); - } -} +use Laraneat\Modules\Support\ModuleConfigWriter; + +use function PHPUnit\Framework\assertFileExists; +use function Spatie\Snapshots\assertMatchesFileSnapshot; + +it('can return the package name', function () { + expect($this->createModule(['packageName' => 'some-vendor/testing-module'])->getPackageName())->toBe('some-vendor/testing-module'); + expect($this->createModule(['packageName' => 'testing-module'])->getPackageName())->toBe('testing-module'); + expect($this->createModule(['packageName' => ' some-vendor/module '])->getPackageName())->toBe('some-vendor/module'); +}); + +it('can return the name', function () { + expect($this->createModule(['name' => 'TestingModule'])->getName())->toBe('TestingModule'); + expect($this->createModule(['name' => ' SomeModule '])->getName())->toBe('SomeModule'); + expect($this->createModule(['name' => ' some-module '])->getName())->toBe('some-module'); + + expect($this->createModule(['packageName' => 'some-vendor/testing-module'])->getName())->toBe('testing-module'); + expect($this->createModule(['packageName' => ' some-vendor/module '])->getName())->toBe('module'); + expect($this->createModule(['packageName' => 'testing-module'])->getName())->toBe('testing-module'); +}); + +it('can return the studly name', function () { + expect($this->createModule(['name' => 'TestingModule'])->getStudlyName())->toBe('TestingModule'); + expect($this->createModule(['name' => ' SomeModule '])->getStudlyName())->toBe('SomeModule'); + expect($this->createModule(['name' => ' some-module '])->getStudlyName())->toBe('SomeModule'); + + expect($this->createModule(['packageName' => 'some-vendor/testing-module'])->getStudlyName())->toBe('TestingModule'); + expect($this->createModule(['packageName' => ' some-vendor/module '])->getStudlyName())->toBe('Module'); + expect($this->createModule(['packageName' => 'testing-module'])->getStudlyName())->toBe('TestingModule'); +}); + +it('can return the kebab name', function () { + expect($this->createModule(['name' => 'TestingModule'])->getKebabName())->toBe('testing-module'); + expect($this->createModule(['name' => ' SomeModule '])->getKebabName())->toBe('some-module'); + expect($this->createModule(['name' => ' some-module '])->getKebabName())->toBe('some-module'); + + expect($this->createModule(['packageName' => 'some-vendor/testing-module'])->getKebabName())->toBe('testing-module'); + expect($this->createModule(['packageName' => ' some-vendor/module '])->getKebabName())->toBe('module'); + expect($this->createModule(['packageName' => 'testing-module'])->getKebabName())->toBe('testing-module'); +}); + +it('can return the snake name', function () { + expect($this->createModule(['name' => 'TestingModule'])->getSnakeName())->toBe('testing_module'); + expect($this->createModule(['name' => ' SomeModule '])->getSnakeName())->toBe('some_module'); + expect($this->createModule(['name' => ' some-module '])->getSnakeName())->toBe('some_module'); + + expect($this->createModule(['packageName' => 'some-vendor/testing-module'])->getSnakeName())->toBe('testing_module'); + expect($this->createModule(['packageName' => ' some-vendor/module '])->getSnakeName())->toBe('module'); + expect($this->createModule(['packageName' => 'testing-module'])->getSnakeName())->toBe('testing_module'); +}); + +it('can return the path', function () { + expect($this->createModule([ + 'path' => $this->app->basePath('/modules/SomeTestingModule'), + ])->getPath())->toBe($this->app->basePath('/modules/SomeTestingModule')); +}); + +it('can return the namespace', function () { + expect($this->createModule(['namespace' => 'Some\\TestingModule\\'])->getNamespace()) + ->toBe('Some\\TestingModule'); + expect($this->createModule(['namespace' => 'Some\\TestingModule\\\\\\'])->getNamespace()) + ->toBe('Some\\TestingModule'); + expect($this->createModule(['namespace' => '\\\\Some\\TestingModule'])->getNamespace()) + ->toBe('Some\\TestingModule'); + expect($this->createModule(['namespace' => '\\\\Some\\TestingModule\\\\\\'])->getNamespace()) + ->toBe('Some\\TestingModule'); +}); + +it('can return providers', function () { + expect($this->createModule([ + 'providers' => [ + 'SomeVendor\\TestingModule\\Providers\\TestingModuleServiceProvider', + 'SomeVendor\\TestingModule\\Providers\\RouteServiceProvider', + ], + ])->getProviders())->toBe([ + 'SomeVendor\\TestingModule\\Providers\\TestingModuleServiceProvider', + 'SomeVendor\\TestingModule\\Providers\\RouteServiceProvider', + ]); +}); + +it('can return aliases', function () { + expect($this->createModule([ + 'aliases' => [ + 'testing-module' => 'SomeVendor\\TestingModule\\Facades\\TestingModule', + 'some' => 'SomeVendor\\TestingModule\\Facades\\Some', + ], + ])->getAliases())->toBe([ + 'testing-module' => 'SomeVendor\\TestingModule\\Facades\\TestingModule', + 'some' => 'SomeVendor\\TestingModule\\Facades\\Some', + ]); +}); + +it('can make sub path', function () { + expect($this->createModule(['path' => $this->app->basePath('/modules/SomeTestingModule')]) + ->subPath('resources/views/index.blade.php')) + ->toBe($this->app->basePath('/modules/SomeTestingModule/resources/views/index.blade.php')); + + expect($this->createModule(['path' => $this->app->basePath('/modules/SomeTestingModule')]) + ->subPath('///resources/views/index.blade.php')) + ->toBe($this->app->basePath('/modules/SomeTestingModule/resources/views/index.blade.php')); +}); + +it('can make sub namespace', function () { + expect($this->createModule(['namespace' => 'SomeVendor\\SomeTestingModule']) + ->subNamespace('Models\\SomeModel')) + ->toBe('SomeVendor\\SomeTestingModule\\Models\\SomeModel'); + + expect($this->createModule(['namespace' => 'SomeVendor\\SomeTestingModule']) + ->subNamespace('\\\\Models\\SomeModel\\\\')) + ->toBe('SomeVendor\\SomeTestingModule\\Models\\SomeModel'); +}); + +it('can return module as array', function () { + expect($this->createModule([ + 'path' => $this->app->basePath('modules/TestingModule'), + 'packageName' => 'some-vendor/testing-module', + 'name' => 'TestingModule', + 'namespace' => '\\\\\\SomeVendor\\TestingModule\\\\', + 'providers' => [ + 'SomeVendor\\TestingModule\\Providers\\TestingModuleServiceProvider', + 'SomeVendor\\TestingModule\\Providers\\RouteServiceProvider', + ], + 'aliases' => [ + 'testing-module' => 'SomeVendor\\TestingModule\\Facades\\TestingModule', + 'some' => 'SomeVendor\\TestingModule\\Facades\\Some', + ], + ])->toArray())->toBe([ + 'path' => $this->app->basePath('modules/TestingModule'), + 'packageName' => 'some-vendor/testing-module', + 'name' => 'TestingModule', + 'namespace' => 'SomeVendor\\TestingModule', + 'providers' => [ + 'SomeVendor\\TestingModule\\Providers\\TestingModuleServiceProvider', + 'SomeVendor\\TestingModule\\Providers\\RouteServiceProvider', + ], + 'aliases' => [ + 'testing-module' => 'SomeVendor\\TestingModule\\Facades\\TestingModule', + 'some' => 'SomeVendor\\TestingModule\\Facades\\Some', + ], + ]); +}); + +it('can update providers via ModuleConfigWriter', function () { + $this->setModules([ + __DIR__ . '/fixtures/stubs/modules/valid/author', + ]); + + $module = $this->createModule([ + 'path' => $this->app->basePath('/modules/author'), + 'packageName' => 'laraneat/author', + 'name' => 'author', + 'namespace' => 'Modules\\Author', + 'providers' => [ + 'Modules\\Author\\Providers\\AuthorServiceProvider', + 'Modules\\Author\\Providers\\RouteServiceProvider', + ], + 'aliases' => [ + 'AuthorFacade' => 'Modules\\Author\\Facades\\SomeFacade', + ], + ]); + + $composerJsonPath = $module->subPath('composer.json'); + assertFileExists($composerJsonPath); + assertMatchesFileSnapshot($composerJsonPath); + + /** @var ModuleConfigWriter $configWriter */ + $configWriter = $this->app->make(ModuleConfigWriter::class); + $configWriter->updateProviders($module, [ + 'Modules\\Author\\Providers\\FooServiceProvider', + 'Modules\\Foo\\Providers\\BarServiceProvider', + ]); + assertFileExists($composerJsonPath); + assertMatchesFileSnapshot($composerJsonPath); +}); + +it('can update aliases via ModuleConfigWriter', function () { + $this->setModules([ + __DIR__ . '/fixtures/stubs/modules/valid/author', + ]); + + $module = $this->createModule([ + 'path' => $this->app->basePath('/modules/author'), + 'packageName' => 'laraneat/author', + 'name' => 'author', + 'namespace' => 'Modules\\Author', + 'providers' => [ + 'Modules\\Author\\Providers\\AuthorServiceProvider', + 'Modules\\Author\\Providers\\RouteServiceProvider', + ], + 'aliases' => [ + 'AuthorFacade' => 'Modules\\Author\\Facades\\SomeFacade', + ], + ]); + + $composerJsonPath = $module->subPath('composer.json'); + assertFileExists($composerJsonPath); + assertMatchesFileSnapshot($composerJsonPath); + + /** @var ModuleConfigWriter $configWriter */ + $configWriter = $this->app->make(ModuleConfigWriter::class); + $configWriter->updateAliases($module, [ + 'foo' => 'Modules\\Author\\Services\\Foo', + 'bar' => 'Modules\\Bar\\Services\\Bar', + ]); + assertFileExists($composerJsonPath); + assertMatchesFileSnapshot($composerJsonPath); +}); + +it('can add providers via ModuleConfigWriter', function () { + $this->setModules([ + __DIR__ . '/fixtures/stubs/modules/valid/author', + ]); + + $module = $this->createModule([ + 'path' => $this->app->basePath('/modules/author'), + 'packageName' => 'laraneat/author', + 'name' => 'author', + 'namespace' => 'Modules\\Author', + 'providers' => [ + 'Modules\\Author\\Providers\\AuthorServiceProvider', + 'Modules\\Author\\Providers\\RouteServiceProvider', + ], + 'aliases' => [ + 'AuthorFacade' => 'Modules\\Author\\Facades\\SomeFacade', + ], + ]); + + $composerJsonPath = $module->subPath('composer.json'); + assertFileExists($composerJsonPath); + assertMatchesFileSnapshot($composerJsonPath); + + /** @var ModuleConfigWriter $configWriter */ + $configWriter = $this->app->make(ModuleConfigWriter::class); + $configWriter->addProvider($module, 'Modules\\Author\\Providers\\FooServiceProvider'); + $configWriter->addProvider($module, 'Modules\\Author\\Providers\\RouteServiceProvider'); // duplicate, should not be added + $configWriter->addProvider($module, 'Modules\\Foo\\Providers\\BarServiceProvider'); + assertFileExists($composerJsonPath); + assertMatchesFileSnapshot($composerJsonPath); +}); + +it('can add aliases via ModuleConfigWriter', function () { + $this->setModules([ + __DIR__ . '/fixtures/stubs/modules/valid/author', + ]); + + $module = $this->createModule([ + 'path' => $this->app->basePath('/modules/author'), + 'packageName' => 'laraneat/author', + 'name' => 'author', + 'namespace' => 'Modules\\Author', + 'providers' => [ + 'Modules\\Author\\Providers\\AuthorServiceProvider', + 'Modules\\Author\\Providers\\RouteServiceProvider', + ], + 'aliases' => [ + 'AuthorFacade' => 'Modules\\Author\\Facades\\SomeFacade', + ], + ]); + + $composerJsonPath = $module->subPath('composer.json'); + assertFileExists($composerJsonPath); + assertMatchesFileSnapshot($composerJsonPath); + + /** @var ModuleConfigWriter $configWriter */ + $configWriter = $this->app->make(ModuleConfigWriter::class); + $configWriter->addAlias($module, 'foo', 'Modules\\Author\\Services\\Foo'); + $configWriter->addAlias($module, 'bar', 'Modules\\Bar\\Services\\Bar'); + assertFileExists($composerJsonPath); + assertMatchesFileSnapshot($composerJsonPath); +}); diff --git a/tests/ModulesFacadeTest.php b/tests/ModulesFacadeTest.php deleted file mode 100644 index e9eda09..0000000 --- a/tests/ModulesFacadeTest.php +++ /dev/null @@ -1,36 +0,0 @@ -assertIsArray($modules); - } - - /** @test */ - public function it_creates_macros_via_facade() - { - $modules = Modules::macro('testMacro', function () { - return true; - }); - - $this->assertTrue(Modules::hasMacro('testMacro')); - } - - /** @test */ - public function it_calls_macros_via_facade() - { - $modules = Modules::macro('testMacro', function () { - return 'a value'; - }); - - $this->assertEquals('a value', Modules::testMacro()); - } -} diff --git a/tests/ModulesRepositoryTest.php b/tests/ModulesRepositoryTest.php new file mode 100644 index 0000000..85ea62b --- /dev/null +++ b/tests/ModulesRepositoryTest.php @@ -0,0 +1,476 @@ +modulesManifestPath = $this->app->bootstrapPath('cache/testing-laraneat-modules.php'); + $this->repository = new ModulesRepository( + filesystem: $this->app['files'], + composer: $this->app[Composer::class], + modulesPath: $this->app['config']->get('modules.path'), + basePath: $this->app->basePath(), + modulesManifestPath: $this->modulesManifestPath, + ); +}); + +afterEach(function () { + $this->filesystem->delete($this->modulesManifestPath); +}); + +describe('scan paths', function () { + it('sets the initial scan paths from the configuration', function () { + expect($this->repository->getScanPaths())->toBe([ + $this->app->basePath('/modules/*'), + ]); + }); + + it('can add one scan path', function () { + $this->repository->addScanPath($this->app->basePath('/foo_bar')); + expect($this->repository->getScanPaths())->toBe([ + $this->app->basePath('/modules/*'), + $this->app->basePath('/foo_bar/*'), + ]); + }); + + it('can add array of scan paths', function () { + $this->repository->addScanPath([ + $this->app->basePath('/foo/bar/test folder'), + $this->app->basePath('/foo/test/'), + ]); + + expect($this->repository->getScanPaths())->toBe([ + $this->app->basePath('/modules/*'), + $this->app->basePath('/foo/bar/test folder/*'), + $this->app->basePath('/foo/test/*'), + ]); + }); + + it('normalizes the added paths', function () { + $this->repository->addScanPath([ + $this->app->basePath('/foo/bar/some1////'), + $this->app->basePath('/foo/bar/some2'), + $this->app->basePath('/foo/bar/some3/*'), + $this->app->basePath('/foo/bar/some4/*/'), + ]); + + expect($this->repository->getScanPaths())->toBe([ + $this->app->basePath('/modules/*'), + $this->app->basePath('/foo/bar/some1/*'), + $this->app->basePath('/foo/bar/some2/*'), + $this->app->basePath('/foo/bar/some3/*'), + $this->app->basePath('/foo/bar/some4/*'), + ]); + }); + + it('rejects adding a path that is already in the scan paths', function () { + $this->repository->addScanPath([ + $this->app->basePath('/modules'), + $this->app->basePath('/modules/'), + $this->app->basePath('/modules////'), + $this->app->basePath('/modules/*'), + $this->app->basePath('/modules/Nested'), + $this->app->basePath('/modules/Nested/'), + $this->app->basePath('/modules/Nested////'), + $this->app->basePath('/modules/Nested/*'), + $this->app->basePath('/foo/test/'), + $this->app->basePath('/foo/test////'), + ]); + + expect($this->repository->getScanPaths())->toBe([ + $this->app->basePath('/modules/*'), + $this->app->basePath('/modules/Nested/*'), + $this->app->basePath('/foo/test/*'), + ]); + }); +}); + +describe('modules manifest', function () { + it('can build modules manifest', function () { + $this->setModules([ + __DIR__ . '/fixtures/stubs/modules/valid/article', + __DIR__ . '/fixtures/stubs/modules/valid/article-category', + __DIR__ . '/fixtures/stubs/modules/valid/author', + __DIR__ . '/fixtures/stubs/modules/valid/empty-module', + __DIR__ . '/fixtures/stubs/modules/valid/navigation', + ]); + + $expectedManifest = [ + 'laraneat/article-category' => [ + 'path' => $this->app->basePath('/modules/article-category'), + 'name' => 'article-category', + 'namespace' => 'Modules\\ArticleCategory\\', + 'providers' => [ + 'Modules\\ArticleCategory\\Providers\\ArticleCategoryServiceProvider', + 'Modules\\ArticleCategory\\Providers\\RouteServiceProvider', + ], + 'aliases' => [], + ], + 'laraneat/article' => [ + 'path' => $this->app->basePath('/modules/article'), + 'name' => 'article', + 'namespace' => 'Modules\\Article\\', + 'providers' => [ + 'Modules\\Article\\Providers\\ArticleServiceProvider', + 'Modules\\Article\\Providers\\RouteServiceProvider', + ], + 'aliases' => [], + ], + 'laraneat/author' => [ + 'path' => $this->app->basePath('/modules/author'), + 'name' => 'author', + 'namespace' => 'Modules\\Author\\', + 'providers' => [ + 'Modules\\Author\\Providers\\AuthorServiceProvider', + 'Modules\\Author\\Providers\\RouteServiceProvider', + ], + 'aliases' => [ + 'AuthorFacade' => 'Modules\\Author\\Facades\\SomeFacade', + ], + ], + 'laraneat/empty' => [ + 'path' => $this->app->basePath('/modules/empty-module'), + 'name' => 'empty-module', + 'namespace' => 'Modules\\Empty\\', + 'providers' => [], + 'aliases' => [], + ], + 'laraneat/location' => [ + 'path' => $this->app->basePath('/modules/navigation'), + 'name' => 'navigation', + 'namespace' => 'Modules\\GeoLocation\\', + 'providers' => [ + 'Modules\\GeoLocation\\Providers\\GeoLocationServiceProvider', + 'Modules\\GeoLocation\\Providers\\RouteServiceProvider', + ], + 'aliases' => [], + ], + ]; + + expect($this->repository->buildModulesManifest()) + ->toBe($expectedManifest) + ->and($this->modulesManifestPath)->toBeFile(); + }); + + it('throws an exception when modules have the same package names', function () { + $this->setModules([ + __DIR__ . '/fixtures/stubs/modules/valid/article', + __DIR__ . '/fixtures/stubs/modules/valid/article-copy', + ]); + + $this->repository->buildModulesManifest(); + })->throws(ModuleHasNonUniquePackageName::class); + + it('can prune modules manifest', function () { + $this->setModules([ + __DIR__ . '/fixtures/stubs/modules/valid/article', + __DIR__ . '/fixtures/stubs/modules/valid/article-category', + __DIR__ . '/fixtures/stubs/modules/valid/author', + __DIR__ . '/fixtures/stubs/modules/valid/empty-module', + __DIR__ . '/fixtures/stubs/modules/valid/navigation', + ]); + + $this->repository->buildModulesManifest(); + expect($this->modulesManifestPath)->toBeFile(); + $this->repository->pruneModulesManifest(); + expect($this->modulesManifestPath)->not->toBeFile(); + }); +}); + +it('can return modules as array', function () { + $this->setModules([ + __DIR__ . '/fixtures/stubs/modules/valid/article', + __DIR__ . '/fixtures/stubs/modules/valid/article-category', + __DIR__ . '/fixtures/stubs/modules/valid/author', + __DIR__ . '/fixtures/stubs/modules/valid/empty-module', + __DIR__ . '/fixtures/stubs/modules/valid/navigation', + ]); + + expect($this->repository->toArray())->toBe([ + 'laraneat/article-category' => [ + 'path' => $this->app->basePath('/modules/article-category'), + 'packageName' => 'laraneat/article-category', + 'name' => 'article-category', + 'namespace' => 'Modules\\ArticleCategory', + 'providers' => [ + 'Modules\\ArticleCategory\\Providers\\ArticleCategoryServiceProvider', + 'Modules\\ArticleCategory\\Providers\\RouteServiceProvider', + ], + 'aliases' => [], + ], + 'laraneat/article' => [ + 'path' => $this->app->basePath('/modules/article'), + 'packageName' => 'laraneat/article', + 'name' => 'article', + 'namespace' => 'Modules\\Article', + 'providers' => [ + 'Modules\\Article\\Providers\\ArticleServiceProvider', + 'Modules\\Article\\Providers\\RouteServiceProvider', + ], + 'aliases' => [], + ], + 'laraneat/author' => [ + 'path' => $this->app->basePath('/modules/author'), + 'packageName' => 'laraneat/author', + 'name' => 'author', + 'namespace' => 'Modules\\Author', + 'providers' => [ + 'Modules\\Author\\Providers\\AuthorServiceProvider', + 'Modules\\Author\\Providers\\RouteServiceProvider', + ], + 'aliases' => [ + 'AuthorFacade' => 'Modules\\Author\\Facades\\SomeFacade', + ], + ], + 'laraneat/empty' => [ + 'path' => $this->app->basePath('/modules/empty-module'), + 'packageName' => 'laraneat/empty', + 'name' => 'empty-module', + 'namespace' => 'Modules\\Empty', + 'providers' => [], + 'aliases' => [], + ], + 'laraneat/location' => [ + 'path' => $this->app->basePath('/modules/navigation'), + 'packageName' => 'laraneat/location', + 'name' => 'navigation', + 'namespace' => 'Modules\\GeoLocation', + 'providers' => [ + 'Modules\\GeoLocation\\Providers\\GeoLocationServiceProvider', + 'Modules\\GeoLocation\\Providers\\RouteServiceProvider', + ], + 'aliases' => [], + ], + ]); +}); + +it('can check the module for existence', function () { + $this->setModules([ + __DIR__ . '/fixtures/stubs/modules/valid/article', + __DIR__ . '/fixtures/stubs/modules/valid/article-category', + __DIR__ . '/fixtures/stubs/modules/valid/author', + __DIR__ . '/fixtures/stubs/modules/valid/empty-module', + __DIR__ . '/fixtures/stubs/modules/valid/navigation', + ]); + + expect($this->repository->has('laraneat/foo'))->toBe(false) + ->and($this->repository->has('laraneat/article'))->toBe(true) + ->and($this->repository->has('laraneat/author'))->toBe(true) + ->and($this->repository->has('laraneat/location'))->toBe(true) + ->and($this->repository->has('laraneat/navigation'))->toBe(false) + ->and($this->repository->has('laraneat/book'))->toBe(false) + ->and($this->repository->has('laraneat/author/some'))->toBe(false) + ->and($this->repository->has('laraneat'))->toBe(false); +}); + +it('can count the number of modules', function () { + $this->setModules([ + __DIR__ . '/fixtures/stubs/modules/valid/article', + __DIR__ . '/fixtures/stubs/modules/valid/article-category', + __DIR__ . '/fixtures/stubs/modules/valid/author', + __DIR__ . '/fixtures/stubs/modules/valid/empty-module', + __DIR__ . '/fixtures/stubs/modules/valid/navigation', + ]); + + expect($this->repository->count())->toBe(5); +}); + +it('can find a module', function () { + $this->setModules([ + __DIR__ . '/fixtures/stubs/modules/valid/article', + __DIR__ . '/fixtures/stubs/modules/valid/article-category', + __DIR__ . '/fixtures/stubs/modules/valid/author', + __DIR__ . '/fixtures/stubs/modules/valid/empty-module', + __DIR__ . '/fixtures/stubs/modules/valid/navigation', + ]); + + expect($this->repository->find('laraneat/article')?->toArray())->toBe([ + 'path' => $this->app->basePath('/modules/article'), + 'packageName' => 'laraneat/article', + 'name' => 'article', + 'namespace' => 'Modules\\Article', + 'providers' => [ + 'Modules\\Article\\Providers\\ArticleServiceProvider', + 'Modules\\Article\\Providers\\RouteServiceProvider', + ], + 'aliases' => [], + ]) + ->and($this->repository->find('laraneat')?->toArray())->toBe(null) + ->and($this->repository->find('laraneat/article/bar')?->toArray())->toBe(null) + ->and($this->repository->find('laraneat/location')?->toArray())->toBe([ + 'path' => $this->app->basePath('/modules/navigation'), + 'packageName' => 'laraneat/location', + 'name' => 'navigation', + 'namespace' => 'Modules\\GeoLocation', + 'providers' => [ + 'Modules\\GeoLocation\\Providers\\GeoLocationServiceProvider', + 'Modules\\GeoLocation\\Providers\\RouteServiceProvider', + ], + 'aliases' => [], + ]) + ->and($this->repository->find('laraneat/navigation')?->toArray())->toBe(null) + ->and($this->repository->find('laraneat/book')?->toArray())->toBe(null); +}); + +it('throws an exception when the module is not found', function () { + $this->setModules([ + __DIR__ . '/fixtures/stubs/modules/valid/article', + __DIR__ . '/fixtures/stubs/modules/valid/article-category', + __DIR__ . '/fixtures/stubs/modules/valid/author', + __DIR__ . '/fixtures/stubs/modules/valid/empty-module', + __DIR__ . '/fixtures/stubs/modules/valid/navigation', + ]); + + $this->repository->findOrFail('laraneat/book'); +})->throws(ModuleNotFound::class); + +it('throws an exception when trying to remove a module that does not exist', function () { + $this->setModules([ + __DIR__ . '/fixtures/stubs/modules/valid/article', + __DIR__ . '/fixtures/stubs/modules/valid/article-category', + __DIR__ . '/fixtures/stubs/modules/valid/author', + __DIR__ . '/fixtures/stubs/modules/valid/empty-module', + __DIR__ . '/fixtures/stubs/modules/valid/navigation', + ]); + + expect($this->repository->delete('laraneat/book')); +})->throws(ModuleNotFound::class); + +it('can filter modules by name', function () { + $this->setModules([ + __DIR__ . '/fixtures/stubs/modules/valid/article', + __DIR__ . '/fixtures/stubs/modules/valid/article-category', + __DIR__ . '/fixtures/stubs/modules/valid/author', + __DIR__ . '/fixtures/stubs/modules/valid/empty-module', + __DIR__ . '/fixtures/stubs/modules/valid/navigation', + ]); + + $articleModuleMatch = [ + 'laraneat/article' => [ + 'path' => $this->app->basePath('/modules/article'), + 'packageName' => 'laraneat/article', + 'name' => 'article', + 'namespace' => 'Modules\\Article', + 'providers' => [ + 'Modules\\Article\\Providers\\ArticleServiceProvider', + 'Modules\\Article\\Providers\\RouteServiceProvider', + ], + 'aliases' => [], + ], + ]; + + $locationModuleMatch = [ + 'laraneat/location' => [ + 'path' => $this->app->basePath('/modules/navigation'), + 'packageName' => 'laraneat/location', + 'name' => 'navigation', + 'namespace' => 'Modules\\GeoLocation', + 'providers' => [ + 'Modules\\GeoLocation\\Providers\\GeoLocationServiceProvider', + 'Modules\\GeoLocation\\Providers\\RouteServiceProvider', + ], + 'aliases' => [], + ], + ]; + + expect(collect($this->repository->filterByName('article'))->toArray())->toBe($articleModuleMatch) + ->and(collect($this->repository->filterByName('Article'))->toArray())->toBe($articleModuleMatch) + ->and(collect($this->repository->filterByName('ARTICLE'))->toArray())->toBe([]) + ->and(collect($this->repository->filterByName('aarticle'))->toArray())->toBe([]) + ->and(collect($this->repository->filterByName('location'))->toArray())->toBe($locationModuleMatch) + ->and(collect($this->repository->filterByName('navigation'))->toArray())->toBe($locationModuleMatch) + ->and(collect($this->repository->filterByName('Navigation'))->toArray())->toBe($locationModuleMatch) + ->and(collect($this->repository->filterByName('GeoLocation'))->toArray())->toBe([]) + ->and(collect($this->repository->filterByName('foo'))->toArray())->toBe([]) + ->and(collect($this->repository->filterByName('laraneat'))->toArray())->toBe([]); + +}); + +it('throws an exception when modules with the requested name are not found', function () { + $this->setModules([ + __DIR__ . '/fixtures/stubs/modules/valid/article', + __DIR__ . '/fixtures/stubs/modules/valid/article-category', + __DIR__ . '/fixtures/stubs/modules/valid/author', + __DIR__ . '/fixtures/stubs/modules/valid/empty-module', + __DIR__ . '/fixtures/stubs/modules/valid/navigation', + ]); + + $articleModuleMatch = [ + 'laraneat/article' => [ + 'path' => $this->app->basePath('/modules/article'), + 'packageName' => 'laraneat/article', + 'name' => 'article', + 'namespace' => 'Modules\\Article', + 'providers' => [ + 'Modules\\Article\\Providers\\ArticleServiceProvider', + 'Modules\\Article\\Providers\\RouteServiceProvider', + ], + 'aliases' => [], + ], + ]; + + expect(collect($this->repository->filterByNameOrFail('article'))->toArray())->toBe($articleModuleMatch); + expect(fn () => $this->repository->filterByNameOrFail('book'))->toThrow(ModuleNotFound::class); +}); + +it('can delete a module', function () { + // Set mock BEFORE setModules, because Module instances get Composer from container + $this->instance(Composer::class, $this->mockComposer(['removePackages' => true])); + + // Recreate repository with mocked Composer + $this->repository = new ModulesRepository( + filesystem: $this->app['files'], + composer: $this->app[Composer::class], + modulesPath: $this->app['config']->get('modules.path'), + basePath: $this->app->basePath(), + modulesManifestPath: $this->modulesManifestPath, + ); + + $this->setModules([ + __DIR__ . '/fixtures/stubs/modules/valid/article', + __DIR__ . '/fixtures/stubs/modules/valid/article-category', + __DIR__ . '/fixtures/stubs/modules/valid/author', + __DIR__ . '/fixtures/stubs/modules/valid/empty-module', + __DIR__ . '/fixtures/stubs/modules/valid/navigation', + ]); + + expect($this->repository->has('laraneat/article'))->toBe(true) + ->and($this->repository->delete('laraneat/article'))->toBe(true) + ->and($this->repository->has('laraneat/article'))->toBe(false); +}); + +it('can sync modules with composer', function () { + // Set mock BEFORE setModules, because repository uses Composer from container + $this->instance(Composer::class, $this->mockComposer(['updatePackages' => true])); + + // Recreate repository with mocked Composer + $this->repository = new ModulesRepository( + filesystem: $this->app['files'], + composer: $this->app[Composer::class], + modulesPath: $this->app['config']->get('modules.path'), + basePath: $this->app->basePath(), + modulesManifestPath: $this->modulesManifestPath, + ); + + $this->setModules([ + __DIR__ . '/fixtures/stubs/modules/valid/article', + __DIR__ . '/fixtures/stubs/modules/valid/article-category', + __DIR__ . '/fixtures/stubs/modules/valid/author', + __DIR__ . '/fixtures/stubs/modules/valid/empty-module', + __DIR__ . '/fixtures/stubs/modules/valid/navigation', + ]); + + $this->backupComposerJson(); + $composerJsonPath = $this->app->basePath('/composer.json'); + assertFileExists($composerJsonPath); + assertMatchesFileSnapshot($composerJsonPath); + $this->repository->syncWithComposer(); + assertFileExists($composerJsonPath); + assertMatchesFileSnapshot($composerJsonPath); + $this->resetComposerJson(); +}); diff --git a/tests/ModulesServiceProviderTest.php b/tests/ModulesServiceProviderTest.php deleted file mode 100644 index cd0f675..0000000 --- a/tests/ModulesServiceProviderTest.php +++ /dev/null @@ -1,22 +0,0 @@ -assertInstanceOf(RepositoryInterface::class, app(RepositoryInterface::class)); - $this->assertInstanceOf(RepositoryInterface::class, app('modules')); - } - - /** @test */ - public function it_binds_activator_to_activator_class() - { - $this->assertInstanceOf(ActivatorInterface::class, app(ActivatorInterface::class)); - } -} diff --git a/tests/NameParserTest.php b/tests/NameParserTest.php deleted file mode 100644 index 09dc0ec..0000000 --- a/tests/NameParserTest.php +++ /dev/null @@ -1,100 +0,0 @@ -getOriginalName()); - } - - /** @test */ - public function it_gets_the_table_name() - { - $parser = new NameParser('create_users_table'); - - self::assertEquals('users', $parser->getTableName()); - } - - /** @test */ - public function it_gets_the_action_name() - { - self::assertEquals('create', (new NameParser('create_users_table'))->getAction()); - self::assertEquals('update', (new NameParser('update_users_table'))->getAction()); - self::assertEquals('delete', (new NameParser('delete_users_table'))->getAction()); - self::assertEquals('remove', (new NameParser('remove_users_table'))->getAction()); - } - - /** @test */ - public function it_gets_first_part_of_name_if_no_action_was_guessed() - { - self::assertEquals('something', (new NameParser('something_random'))->getAction()); - } - - /** @test */ - public function it_gets_the_correct_matched_results() - { - $matches = (new NameParser('create_users_table'))->getMatches(); - - $expected = [ - 'create_users_table', - 'users', - ]; - - self::assertEquals($expected, $matches); - } - - /** @test */ - public function it_gets_the_exploded_parts_of_migration_name() - { - $parser = new NameParser('create_users_table'); - - $expected = [ - 'create', - 'users', - 'table', - ]; - - self::assertEquals($expected, $parser->getData()); - } - - /** @test */ - public function it_can_check_if_current_migration_type_matches_given_type() - { - $parser = new NameParser('create_users_table'); - - self::assertTrue($parser->is('create')); - } - - /** @test */ - public function it_can_check_if_current_migration_is_about_adding() - { - self::assertTrue((new NameParser('add_users_table'))->isAdd()); - } - - /** @test */ - public function it_can_check_if_current_migration_is_about_deleting() - { - self::assertTrue((new NameParser('delete_users_table'))->isDelete()); - } - - /** @test */ - public function it_can_check_if_current_migration_is_about_creating() - { - self::assertTrue((new NameParser('create_users_table'))->isCreate()); - } - - /** @test */ - public function it_makes_a_regex_pattern() - { - self::assertEquals('/create_(.*)_table/', (new NameParser('create_users_table'))->getPattern()); - self::assertEquals('/add_(.*)_to_(.*)_table/', (new NameParser('add_column_to_users_table'))->getPattern()); - self::assertEquals('/delete_(.*)_from_(.*)_table/', (new NameParser('delete_users_table'))->getPattern()); - } -} diff --git a/tests/Pest.php b/tests/Pest.php new file mode 100644 index 0000000..8b26e9c --- /dev/null +++ b/tests/Pest.php @@ -0,0 +1,55 @@ + + */ +function getRelativeFilePathsInDirectory(string $directory): array +{ + $filesystem = new Illuminate\Filesystem\Filesystem(); + + return array_map(fn (SplFileInfo $fileInfo) => $fileInfo->getRelativePathname(), $filesystem->allFiles($directory)); +} + +/** + * @param array $paths + * @param string $basePath + * @return array + */ +function makeAbsolutePaths(array $paths, string $basePath): array +{ + $basePath = rtrim($basePath, '\\/'); + + return array_map(fn (string $path) => realpath($basePath . '/' . $path), $paths); +} + +function assertsMatchesDirectorySnapshot(string $directory): void +{ + expect($directory)->toBeDirectory(); + $filePaths = getRelativeFilePathsInDirectory($directory); + assertMatchesSnapshot($filePaths); + foreach (makeAbsolutePaths($filePaths, $directory) as $filePath) { + assertMatchesFileSnapshot($filePath); + } +} + +uses(Laraneat\Modules\Tests\TestCase::class) + ->afterEach(function () { + /** @var \Laraneat\Modules\Tests\TestCase $this */ + $this->pruneModulesPaths(); + $this->resetComposerJson(); + }) + ->in('.'); diff --git a/tests/SchemaParserTest.php b/tests/SchemaParserTest.php deleted file mode 100644 index 61891fa..0000000 --- a/tests/SchemaParserTest.php +++ /dev/null @@ -1,47 +0,0 @@ -string('username'); -\t\t\t\$table->integer('password');\n -TEXT; - - self::assertEquals($expected, $parser->render()); - } - - /** @test */ - public function it_generates_migration_methods_for_up_method() - { - $parser = new SchemaParser('username:string, password:integer'); - - $expected = <<string('username'); -\t\t\t\$table->integer('password');\n -TEXT; - - self::assertEquals($expected, $parser->up()); - } - - /** @test */ - public function it_generates_migration_methods_for_down_method() - { - $parser = new SchemaParser('username:string, password:integer'); - - $expected = <<dropColumn('username'); -\t\t\t\$table->dropColumn('password');\n -TEXT; - - self::assertEquals($expected, $parser->down()); - } -} diff --git a/tests/StubTest.php b/tests/StubTest.php deleted file mode 100644 index e880c65..0000000 --- a/tests/StubTest.php +++ /dev/null @@ -1,84 +0,0 @@ -finder = $this->app['files']; - } - - protected function tearDown(): void - { - parent::tearDown(); - $this->finder->delete([ - base_path('my-command.php'), - base_path('stub-override-exists.php'), - base_path('stub-override-not-exists.php'), - ]); - } - - /** @test */ - public function it_initialises_a_stub_instance() - { - $stub = new Stub('/model.stub', [ - 'name' => 'Name', - 'factory' => 'Some\\ModelFactory' - ]); - - $this->assertTrue(Str::contains($stub->getPath(), 'Commands/Generators/stubs/model.stub')); - $this->assertEquals([ - 'name' => 'Name', - 'factory' => 'Some\\ModelFactory' - ], $stub->getReplaces()); - } - - /** @test */ - public function it_sets_new_replaces_array() - { - $stub = new Stub('/model.stub', [ - 'name' => 'Name', - ]); - - $stub->replace([ - 'factory' => 'Some\\New\\ModelFactory' - ]); - $this->assertEquals([ - 'factory' => 'Some\\New\\ModelFactory' - ], $stub->getReplaces()); - } - - /** @test */ - public function it_stores_stub_to_specific_path() - { - $stub = new Stub('/command.stub', [ - 'command' => 'my:command', - 'namespace' => 'Article\\Commands', - 'class' => 'MyCommand', - ]); - - $stub->saveTo(base_path(), 'my-command.php'); - - $this->assertTrue($this->finder->exists(base_path('my-command.php'))); - } - - /** @test */ - public function it_sets_new_path() - { - $stub = new Stub('/model.stub', [ - 'name' => 'Name', - ]); - - $stub->setPath('/new-path/'); - - $this->assertTrue(Str::contains($stub->getPath(), 'Commands/Generators/stubs/new-path/')); - } -} diff --git a/tests/Support/Generator/GeneratorHelperTest.php b/tests/Support/Generator/GeneratorHelperTest.php new file mode 100644 index 0000000..e9bda89 --- /dev/null +++ b/tests/Support/Generator/GeneratorHelperTest.php @@ -0,0 +1,134 @@ +toBe('src/some/Actions'); + expect(GeneratorHelper::normalizePath('/src/some/Actions'))->toBe('src/some/Actions'); + expect(GeneratorHelper::normalizePath('/src/some\\Actions/'))->toBe('src/some/Actions'); + expect(GeneratorHelper::normalizePath('/src/some/Actions///'))->toBe('src/some/Actions'); + expect(GeneratorHelper::normalizePath('////src/some/Actions\\'))->toBe('src/some/Actions'); + expect(GeneratorHelper::normalizePath('////src\\some/Actions////'))->toBe('src/some/Actions'); + expect(GeneratorHelper::normalizePath('\\src\\some\\Actions'))->toBe('src/some/Actions'); + expect(GeneratorHelper::normalizePath('\\\\src\\some\\Actions\\\\'))->toBe('src/some/Actions'); + }); + + it('correctly normalizes path using rtrim', function () { + expect(GeneratorHelper::normalizePath('src/some/Actions', true))->toBe('src/some/Actions'); + expect(GeneratorHelper::normalizePath('/src/some/Actions', true))->toBe('/src/some/Actions'); + expect(GeneratorHelper::normalizePath('/src/some\\Actions/', true))->toBe('/src/some/Actions'); + expect(GeneratorHelper::normalizePath('/src/some/Actions///', true))->toBe('/src/some/Actions'); + expect(GeneratorHelper::normalizePath('////src/some/Actions\\', true))->toBe('/src/some/Actions'); + expect(GeneratorHelper::normalizePath('////src\\some/Actions////', true))->toBe('/src/some/Actions'); + expect(GeneratorHelper::normalizePath('\\src\\some\\Actions', true))->toBe('/src/some/Actions'); + expect(GeneratorHelper::normalizePath('\\\\src\\some\\Actions\\\\', true))->toBe('/src/some/Actions'); + }); +}); + +describe('normalizeNamespace()', function () { + it('correctly normalizes namespace', function () { + expect(GeneratorHelper::normalizeNamespace('src/some/Actions'))->toBe('src\\some\\Actions'); + expect(GeneratorHelper::normalizeNamespace('/src/some/Actions'))->toBe('src\\some\\Actions'); + expect(GeneratorHelper::normalizeNamespace('/src/some\\Actions/'))->toBe('src\\some\\Actions'); + expect(GeneratorHelper::normalizeNamespace('/src/some/Actions///'))->toBe('src\\some\\Actions'); + expect(GeneratorHelper::normalizeNamespace('////src/some/Actions\\'))->toBe('src\\some\\Actions'); + expect(GeneratorHelper::normalizeNamespace('////src\\some/Actions////'))->toBe('src\\some\\Actions'); + expect(GeneratorHelper::normalizeNamespace('\\src\\some\\Actions'))->toBe('src\\some\\Actions'); + expect(GeneratorHelper::normalizeNamespace('\\\\src\\some\\Actions\\\\'))->toBe('src\\some\\Actions'); + }); + + it('correctly normalizes namespace using rtrim', function () { + expect(GeneratorHelper::normalizeNamespace('src/some/Actions', true))->toBe('src\\some\\Actions'); + expect(GeneratorHelper::normalizeNamespace('/src/some/Actions', true))->toBe('\\src\\some\\Actions'); + expect(GeneratorHelper::normalizeNamespace('/src/some\\Actions/', true))->toBe('\\src\\some\\Actions'); + expect(GeneratorHelper::normalizeNamespace('/src/some/Actions///', true))->toBe('\\src\\some\\Actions'); + expect(GeneratorHelper::normalizeNamespace('////src/some/Actions\\', true))->toBe('\\src\\some\\Actions'); + expect(GeneratorHelper::normalizeNamespace('////src\\some/Actions////', true))->toBe('\\src\\some\\Actions'); + expect(GeneratorHelper::normalizeNamespace('\\src\\some\\Actions', true))->toBe('\\src\\some\\Actions'); + expect(GeneratorHelper::normalizeNamespace('\\\\src\\some\\Actions\\\\', true))->toBe('\\src\\some\\Actions'); + }); +}); + +describe('makeRelativePath()', function () { + it('correctly make relative', function () { + expect(GeneratorHelper::makeRelativePath('', ''))->toBe(''); + expect(GeneratorHelper::makeRelativePath('/', ''))->toBe(null); + expect(GeneratorHelper::makeRelativePath('', '/'))->toBe(null); + expect(GeneratorHelper::makeRelativePath('src/some/foo/bar', 'src/some/foo/bar'))->toBe(''); + expect(GeneratorHelper::makeRelativePath('/src/some/foo/bar', 'src/some/foo/bar'))->toBe(null); + expect(GeneratorHelper::makeRelativePath('src/some/foo/bar', '/src/some/foo/bar'))->toBe(null); + expect(GeneratorHelper::makeRelativePath('src/some/foo/bar', 'src/some'))->toBe('../..'); + expect(GeneratorHelper::makeRelativePath('src/some', 'src/some/foo/bar'))->toBe('foo/bar'); + expect(GeneratorHelper::makeRelativePath('/src/some', '/src/some/foo/bar'))->toBe('foo/bar'); + expect(GeneratorHelper::makeRelativePath('src/some/bar', 'src/some/foo/bar'))->toBe('../foo/bar'); + expect(GeneratorHelper::makeRelativePath('src/some/foo/bar', 'src/some/bar'))->toBe('../../bar'); + expect(GeneratorHelper::makeRelativePath('src/some/bar/baz', 'src/some/foo/bar/baz'))->toBe('../../foo/bar/baz'); + expect(GeneratorHelper::makeRelativePath('src/some/foo/bar/baz', 'src/some/bar/baz'))->toBe('../../../bar/baz'); + expect(GeneratorHelper::makeRelativePath('/src/some/foo/bar/baz', '/src/some/bar/baz'))->toBe('../../../bar/baz'); + expect(GeneratorHelper::makeRelativePath('/src/some/foo/bar/baz', '////src/some/bar/baz'))->toBe('../../../bar/baz'); + expect(GeneratorHelper::makeRelativePath('/////src/some/foo/bar/baz', '/src/some/bar/baz'))->toBe('../../../bar/baz'); + expect(GeneratorHelper::makeRelativePath('foo/bar/baz', 'src/some/bar/baz'))->toBe(null); + }); +}); + +describe('getBasePath()', function () { + it('returns base path', function () { + $this->app['config']->set('modules.path', $this->app->basePath('/foo/bar')); + + expect(GeneratorHelper::getBasePath())->toBe($this->app->basePath('/foo/bar')); + + $this->app['config']->set('modules.path', $this->app->basePath('/foo/bar/') . '///'); + + expect(GeneratorHelper::getBasePath())->toBe($this->app->basePath('/foo/bar')); + }); + + it('throws an error when the modules path is not defined', function () { + $this->app['config']->set('modules.path', ''); + + expect(fn () => GeneratorHelper::getBasePath())->toThrow(InvalidConfigValue::class); + }); +}); + +describe('getBaseNamespace()', function () { + it('returns base namespace', function () { + $this->app['config']->set('modules.namespace', '\\Foo\\Bar\\'); + + expect(GeneratorHelper::getBaseNamespace())->toBe('Foo\\Bar'); + }); + + it('throws an error when the modules namespace is not defined', function () { + $this->app['config']->set('modules.namespace', ''); + + expect(fn () => GeneratorHelper::getBaseNamespace())->toThrow(InvalidConfigValue::class); + }); +}); + +describe('component(ModuleComponentType $componentType)', function () { + it('returns component config by type', function () { + $this->app['config']->set('modules.components.' . ModuleComponentType::Action->value, [ + 'path' => '///Some/Actions///', + ]); + + expect(GeneratorHelper::component(ModuleComponentType::Action)->getPath())->toBe('Some/Actions'); + expect(GeneratorHelper::component(ModuleComponentType::Action)->getNamespace())->toBe('Some\\Actions'); + + $this->app['config']->set('modules.components.' . ModuleComponentType::Action->value, [ + 'path' => '///src/Some/Actions///', + 'namespace' => '\\\\\\Some\\Actions\\\\\\', + ]); + + expect(GeneratorHelper::component(ModuleComponentType::Action)->getPath())->toBe('src/Some/Actions'); + expect(GeneratorHelper::component(ModuleComponentType::Action)->getNamespace())->toBe('Some\\Actions'); + }); + + it('throws an error when the component has invalid config', function () { + $this->app['config']->set('modules.components.' . ModuleComponentType::Action->value, 'foo'); + expect(fn () => GeneratorHelper::component(ModuleComponentType::Action))->toThrow(InvalidConfigValue::class); + + $this->app['config']->set('modules.components.' . ModuleComponentType::Action->value, ['namespace' => 'Some\\Actions']); + expect(fn () => GeneratorHelper::component(ModuleComponentType::Action))->toThrow(InvalidConfigValue::class); + }); +}); diff --git a/tests/TestCase.php b/tests/TestCase.php new file mode 100644 index 0000000..814ae6e --- /dev/null +++ b/tests/TestCase.php @@ -0,0 +1,208 @@ +resolveDefaultApplication(); + } + + protected function setUp(): void + { + parent::setUp(); + + $this->filesystem = $this->app['files']; + } + + protected function tearDown(): void + { + parent::tearDown(); + + Mockery::close(); + } + + protected function getPackageProviders($app): array + { + return [ + ComposerServiceProvider::class, + ConsoleServiceProvider::class, + ModulesRepositoryServiceProvider::class, + ModulesServiceProvider::class, + ]; + } + + protected function getPackageAliases($app): array + { + return [ + 'Modules' => Modules::class, + ]; + } + + /** + * @param Application $app + */ + protected function getEnvironmentSetUp($app): void + { + $app['config']->set('database.default', 'sqlite'); + $app['config']->set('database.connections.sqlite', [ + 'driver' => 'sqlite', + 'database' => ':memory:', + 'prefix' => '', + ]); + } + + /** + * @param string[] $paths + */ + public function setModules(array $paths, ?string $modulesPath = null): void + { + $modulesPath = rtrim($modulesPath ?? GeneratorHelper::getBasePath(), '/'); + $this->addModulesPath($modulesPath); + $this->filesystem->ensureDirectoryExists($modulesPath); + + foreach ($paths as $modulePath) { + $modulePath = rtrim($modulePath, '/'); + $this->filesystem->copyDirectory($modulePath, $modulesPath . '/' . Str::afterLast($modulePath, '/')); + } + + $this->app[ModulesRepository::class]->pruneModulesManifest(); + } + + public function pruneModulesPaths(): void + { + if ($this->modulesPaths) { + foreach ($this->modulesPaths as $path) { + if ($this->filesystem->isDirectory($path)) { + $this->filesystem->deleteDirectory($path); + } + } + $this->modulesPaths = []; + $this->app[ModulesRepository::class]->pruneModulesManifest(); + } + } + + public function backupComposerJson(): void + { + if ($this->composerJsonBackupPath !== null) { + return; + } + + $this->composerJsonBackupPath = $this->app->basePath('/composer.backup.json'); + if ($this->filesystem->isFile($this->composerJsonBackupPath)) { + $this->resetComposerJson(); + $this->composerJsonBackupPath = $this->app->basePath('/composer.backup.json'); + } + $this->filesystem->copy($this->app->basePath('/composer.json'), $this->composerJsonBackupPath); + } + + public function resetComposerJson(): void + { + if ($this->composerJsonBackupPath === null) { + return; + } + + if ($this->filesystem->isFile($this->composerJsonBackupPath)) { + $this->filesystem->delete($this->app->basePath('/composer.json')); + $this->filesystem->copy($this->composerJsonBackupPath, $this->app->basePath('/composer.json')); + $this->filesystem->delete($this->composerJsonBackupPath); + } + $this->composerJsonBackupPath = null; + } + + /** + * @param array $attributes + */ + public function createModule(array $attributes = []): Module + { + return new Module( + packageName: $attributes['packageName'] ?? 'some-vendor/testing-module', + name: $attributes['name'] ?? '', + path: $attributes['path'] ?? $this->app->basePath('modules/TestingModule'), + namespace: $attributes['namespace'] ?? 'SomeVendor\\TestingModule\\', + providers: $attributes['providers'] ?? [ + 'SomeVendor\\TestingModule\\Providers\\TestingModuleServiceProvider', + 'SomeVendor\\TestingModule\\Providers\\RouteServiceProvider', + ], + aliases: $attributes['aliases'] ?? [ + 'testing-module' => 'SomeVendor\\TestingModule\\Facades\\TestingModule', + ], + ); + } + + /** + * Create a mock of Composer class for testing. + * + * @param array $methodExpectations Array of method names to their expected return values + * + * @return MockInterface&Composer + */ + public function mockComposer(array $methodExpectations = []): MockInterface + { + $composer = Mockery::mock(Composer::class); + + foreach ($methodExpectations as $method => $returnValue) { + $composer->shouldReceive($method)->andReturn($returnValue); + } + + return $composer; + } + + private function addModulesPath(string $path): void + { + if (in_array($path, $this->modulesPaths, true)) { + return; + } + + if ($this->filesystem->isDirectory($path)) { + $this->filesystem->deleteDirectories($path); + } + + $this->modulesPaths[] = $path; + } +} diff --git a/tests/__snapshots__/ModuleMakeCommandTest__it_generates_a__api__module__2.yml b/tests/__snapshots__/ModuleMakeCommandTest__it_generates_a__api__module__2.yml new file mode 100644 index 0000000..386aaae --- /dev/null +++ b/tests/__snapshots__/ModuleMakeCommandTest__it_generates_a__api__module__2.yml @@ -0,0 +1,33 @@ +- composer.json +- database/factories/ArticleCommentFactory.php +- database/migrations/2024_01_01_000000_create_article_comments_table.php +- database/seeders/ArticleCommentPermissionsSeeder_1.php +- src/Actions/CreateArticleCommentAction.php +- src/Actions/DeleteArticleCommentAction.php +- src/Actions/ListArticleCommentsAction.php +- src/Actions/UpdateArticleCommentAction.php +- src/Actions/ViewArticleCommentAction.php +- src/DTO/CreateArticleCommentDTO.php +- src/DTO/UpdateArticleCommentDTO.php +- src/Models/ArticleComment.php +- src/Policies/ArticleCommentPolicy.php +- src/Providers/ArticleCommentServiceProvider.php +- src/Providers/RouteServiceProvider.php +- src/UI/API/QueryWizards/ArticleCommentQueryWizard.php +- src/UI/API/QueryWizards/ArticleCommentsQueryWizard.php +- src/UI/API/Requests/CreateArticleCommentRequest.php +- src/UI/API/Requests/DeleteArticleCommentRequest.php +- src/UI/API/Requests/ListArticleCommentsRequest.php +- src/UI/API/Requests/UpdateArticleCommentRequest.php +- src/UI/API/Requests/ViewArticleCommentRequest.php +- src/UI/API/Resources/ArticleCommentResource.php +- src/UI/API/routes/v1/create_article_comment.php +- src/UI/API/routes/v1/delete_article_comment.php +- src/UI/API/routes/v1/list_article_comments.php +- src/UI/API/routes/v1/update_article_comment.php +- src/UI/API/routes/v1/view_article_comment.php +- tests/UI/API/CreateArticleCommentTest.php +- tests/UI/API/DeleteArticleCommentTest.php +- tests/UI/API/ListArticleCommentsTest.php +- tests/UI/API/UpdateArticleCommentTest.php +- tests/UI/API/ViewArticleCommentTest.php diff --git a/tests/__snapshots__/ModuleMakeCommandTest__it_generates_a__base__module__2.yml b/tests/__snapshots__/ModuleMakeCommandTest__it_generates_a__base__module__2.yml new file mode 100644 index 0000000..d03888b --- /dev/null +++ b/tests/__snapshots__/ModuleMakeCommandTest__it_generates_a__base__module__2.yml @@ -0,0 +1,8 @@ +- composer.json +- database/factories/ArticleCommentFactory.php +- database/migrations/2024_01_01_000000_create_article_comments_table.php +- database/seeders/ArticleCommentPermissionsSeeder_1.php +- src/Models/ArticleComment.php +- src/Policies/ArticleCommentPolicy.php +- src/Providers/ArticleCommentServiceProvider.php +- src/Providers/RouteServiceProvider.php diff --git a/tests/__snapshots__/ModuleMakeCommandTest__it_generates_a__plain__module__2.yml b/tests/__snapshots__/ModuleMakeCommandTest__it_generates_a__plain__module__2.yml new file mode 100644 index 0000000..173b667 --- /dev/null +++ b/tests/__snapshots__/ModuleMakeCommandTest__it_generates_a__plain__module__2.yml @@ -0,0 +1,3 @@ +- composer.json +- src/Providers/ArticleCommentServiceProvider.php +- src/Providers/RouteServiceProvider.php diff --git a/tests/__snapshots__/StubPublishCommandTest__it_publish_all_laraneat_modules_stubs__1.yml b/tests/__snapshots__/StubPublishCommandTest__it_publish_all_laraneat_modules_stubs__1.yml new file mode 100644 index 0000000..2d90c51 --- /dev/null +++ b/tests/__snapshots__/StubPublishCommandTest__it_publish_all_laraneat_modules_stubs__1.yml @@ -0,0 +1,63 @@ +- action/create.stub +- action/delete.stub +- action/list.stub +- action/plain.stub +- action/update.stub +- action/view.stub +- command.stub +- composer.json.stub +- controller/api.stub +- controller/web.stub +- dto.stub +- event.stub +- exception.stub +- factory.stub +- job/plain.stub +- job/queued.stub +- listener/plain.stub +- listener/queued.stub +- mail.stub +- middleware.stub +- migration/add.stub +- migration/create.stub +- migration/delete.stub +- migration/pivot.stub +- migration/plain.stub +- model/full.stub +- model/plain.stub +- notification/plain.stub +- notification/queued.stub +- observer.stub +- policy/full.stub +- policy/plain.stub +- provider/event.stub +- provider/module.stub +- provider/plain.stub +- provider/route.stub +- query-wizard/eloquent.stub +- query-wizard/model.stub +- request/create.stub +- request/delete.stub +- request/list.stub +- request/plain.stub +- request/update.stub +- request/view.stub +- resource/collection.stub +- resource/single.stub +- route.stub +- rule.stub +- seeder/permissions.stub +- seeder/plain.stub +- test/api/create.stub +- test/api/delete.stub +- test/api/list.stub +- test/api/plain.stub +- test/api/update.stub +- test/api/view.stub +- test/cli/plain.stub +- test/feature/plain.stub +- test/unit/plain.stub +- test/web/create.stub +- test/web/delete.stub +- test/web/plain.stub +- test/web/update.stub diff --git a/tests/__snapshots__/files/ActionMakeCommandTest__it_generates__create__action_for_the_module__1.php b/tests/__snapshots__/files/ActionMakeCommandTest__it_generates__create__action_for_the_module__1.php new file mode 100644 index 0000000..ed01161 --- /dev/null +++ b/tests/__snapshots__/files/ActionMakeCommandTest__it_generates__create__action_for_the_module__1.php @@ -0,0 +1,27 @@ +all()); + } + + public function asController(CreateAuthorRequest $request): JsonResponse + { + $author = $this->handle($request->toDTO()); + + return (new AuthorResource($author))->created(); + } +} diff --git a/tests/__snapshots__/files/ActionMakeCommandTest__it_generates__delete__action_for_the_module__1.php b/tests/__snapshots__/files/ActionMakeCommandTest__it_generates__delete__action_for_the_module__1.php new file mode 100644 index 0000000..0f8b701 --- /dev/null +++ b/tests/__snapshots__/files/ActionMakeCommandTest__it_generates__delete__action_for_the_module__1.php @@ -0,0 +1,25 @@ +delete(); + } + + public function asController(DeleteAuthorRequest $request, Author $author): Response + { + $this->handle($author); + + return $this->noContent(); + } +} diff --git a/tests/__snapshots__/files/ActionMakeCommandTest__it_generates__list__action_for_the_module__1.php b/tests/__snapshots__/files/ActionMakeCommandTest__it_generates__list__action_for_the_module__1.php new file mode 100644 index 0000000..1a51d18 --- /dev/null +++ b/tests/__snapshots__/files/ActionMakeCommandTest__it_generates__list__action_for_the_module__1.php @@ -0,0 +1,28 @@ +build() + ->paginate(); + } + + public function asController(ListAuthorsRequest $request): AnonymousResourceCollection + { + return AuthorResource::collection($this->handle($request)); + } +} diff --git a/tests/__snapshots__/files/ActionMakeCommandTest__it_generates__plain__action_for_the_module__1.php b/tests/__snapshots__/files/ActionMakeCommandTest__it_generates__plain__action_for_the_module__1.php new file mode 100644 index 0000000..148c7ab --- /dev/null +++ b/tests/__snapshots__/files/ActionMakeCommandTest__it_generates__plain__action_for_the_module__1.php @@ -0,0 +1,15 @@ +all(); + + if ($data) { + $author->update($data); + } + + return $author; + } + + public function asController(UpdateAuthorRequest $request, Author $author): AuthorResource + { + $author = $this->handle($author, $request->toDTO()); + + return new AuthorResource($author); + } +} diff --git a/tests/__snapshots__/files/ActionMakeCommandTest__it_generates__view__action_for_the_module__1.php b/tests/__snapshots__/files/ActionMakeCommandTest__it_generates__view__action_for_the_module__1.php new file mode 100644 index 0000000..3443c4b --- /dev/null +++ b/tests/__snapshots__/files/ActionMakeCommandTest__it_generates__view__action_for_the_module__1.php @@ -0,0 +1,25 @@ +build(); + } + + public function asController(ViewAuthorRequest $request, Author $author): AuthorResource + { + return new AuthorResource($this->handle($request, $author)); + } +} diff --git a/tests/__snapshots__/files/CommandMakeCommandTest__it_generates_console_command_for_the_module__1.php b/tests/__snapshots__/files/CommandMakeCommandTest__it_generates_console_command_for_the_module__1.php new file mode 100644 index 0000000..0f87a39 --- /dev/null +++ b/tests/__snapshots__/files/CommandMakeCommandTest__it_generates_console_command_for_the_module__1.php @@ -0,0 +1,30 @@ +|Author create($attributes = [], ?Author $parent = null) + * @method \Illuminate\Support\Collection createMany(iterable $records) + * @method Author createOne($attributes = []) + * @method \Illuminate\Support\Collection|Author make($attributes = [], ?Author $parent = null) + * @method Author makeOne($attributes = []) + */ +class SomeAuthorFactory extends Factory +{ + /** + * The name of the factory's corresponding model. + * + * @var class-string + */ + protected $model = Author::class; + + /** + * Define the model's default state. + */ + public function definition(): array + { + return [ + // TODO: add fields here + ]; + } +} diff --git a/tests/Commands/Generators/__snapshots__/JobMakeCommandTest__it_generated_correct_plain_job_file_with_content__1.txt b/tests/__snapshots__/files/JobMakeCommandTest__it_generates__plain__job_for_the_module__1.php similarity index 67% rename from tests/Commands/Generators/__snapshots__/JobMakeCommandTest__it_generated_correct_plain_job_file_with_content__1.txt rename to tests/__snapshots__/files/JobMakeCommandTest__it_generates__plain__job_for_the_module__1.php index 552312c..eb911c8 100644 --- a/tests/Commands/Generators/__snapshots__/JobMakeCommandTest__it_generated_correct_plain_job_file_with_content__1.txt +++ b/tests/__snapshots__/files/JobMakeCommandTest__it_generates__plain__job_for_the_module__1.php @@ -1,14 +1,16 @@ + */ + public function attachments(): array + { + return []; + } +} diff --git a/tests/__snapshots__/files/MiddlewareMakeCommandTest__it_generates_middleware_for_the_module__1.php b/tests/__snapshots__/files/MiddlewareMakeCommandTest__it_generates_middleware_for_the_module__1.php new file mode 100644 index 0000000..d2f58c3 --- /dev/null +++ b/tests/__snapshots__/files/MiddlewareMakeCommandTest__it_generates_middleware_for_the_module__1.php @@ -0,0 +1,20 @@ +dropColumn('title'); + Schema::table('articles', static function (Blueprint $table) { + $table->string('title'); }); } @@ -22,8 +22,8 @@ public function up(): void */ public function down(): void { - Schema::table('posts', static function (Blueprint $table) { - $table->string('title'); + Schema::table('articles', static function (Blueprint $table) { + $table->dropColumn('title'); }); } diff --git a/tests/Commands/Generators/__snapshots__/MigrationMakeCommandTest__it_generates_correct_create_migration_file_content__1.txt b/tests/__snapshots__/files/MigrationMakeCommandTest__it_automatically_detects_and_generates__create__migration_for_the_module__1.php similarity index 84% rename from tests/Commands/Generators/__snapshots__/MigrationMakeCommandTest__it_generates_correct_create_migration_file_content__1.txt rename to tests/__snapshots__/files/MigrationMakeCommandTest__it_automatically_detects_and_generates__create__migration_for_the_module__1.php index 09bf48b..a1fea11 100644 --- a/tests/Commands/Generators/__snapshots__/MigrationMakeCommandTest__it_generates_correct_create_migration_file_content__1.txt +++ b/tests/__snapshots__/files/MigrationMakeCommandTest__it_automatically_detects_and_generates__create__migration_for_the_module__1.php @@ -11,7 +11,7 @@ */ public function up(): void { - Schema::create('posts', static function (Blueprint $table) { + Schema::create('articles', static function (Blueprint $table) { $table->id(); $table->string('title'); $table->text('excerpt'); @@ -28,6 +28,6 @@ public function up(): void */ public function down(): void { - Schema::dropIfExists('posts'); + Schema::dropIfExists('articles'); } }; diff --git a/tests/Commands/Generators/__snapshots__/MigrationMakeCommandTest__it_generates_correct_add_migration_file_content__1.txt b/tests/__snapshots__/files/MigrationMakeCommandTest__it_automatically_detects_and_generates__delete__migration_for_the_module__1.php similarity index 75% rename from tests/Commands/Generators/__snapshots__/MigrationMakeCommandTest__it_generates_correct_add_migration_file_content__1.txt rename to tests/__snapshots__/files/MigrationMakeCommandTest__it_automatically_detects_and_generates__delete__migration_for_the_module__1.php index 48da517..02dbdeb 100644 --- a/tests/Commands/Generators/__snapshots__/MigrationMakeCommandTest__it_generates_correct_add_migration_file_content__1.txt +++ b/tests/__snapshots__/files/MigrationMakeCommandTest__it_automatically_detects_and_generates__delete__migration_for_the_module__1.php @@ -11,8 +11,8 @@ */ public function up(): void { - Schema::table('posts', static function (Blueprint $table) { - $table->string('title'); + Schema::table('articles', static function (Blueprint $table) { + $table->dropColumn('title'); }); } @@ -22,8 +22,8 @@ public function up(): void */ public function down(): void { - Schema::table('posts', static function (Blueprint $table) { - $table->dropColumn('title'); + Schema::table('articles', static function (Blueprint $table) { + $table->string('title'); }); } diff --git a/tests/__snapshots__/files/MigrationMakeCommandTest__it_generates__add__migration_for_the_module__1.php b/tests/__snapshots__/files/MigrationMakeCommandTest__it_generates__add__migration_for_the_module__1.php new file mode 100644 index 0000000..34f727a --- /dev/null +++ b/tests/__snapshots__/files/MigrationMakeCommandTest__it_generates__add__migration_for_the_module__1.php @@ -0,0 +1,36 @@ +string('title'); + $table->text('excerpt'); + $table->integer('user_id')->unsigned(); + $table->foreign('user_id')->references('id')->on('users'); + + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('articles', static function (Blueprint $table) { + $table->dropColumn('title'); + $table->dropColumn('excerpt'); + $table->dropColumn('user_id'); + $table->dropForeign(['user_id']); + + }); + } +}; diff --git a/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_can_set_custom_model__65.txt b/tests/__snapshots__/files/MigrationMakeCommandTest__it_generates__add__migration_for_the_module_without_fields__1.php similarity index 68% rename from tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_can_set_custom_model__65.txt rename to tests/__snapshots__/files/MigrationMakeCommandTest__it_generates__add__migration_for_the_module_without_fields__1.php index f39c303..11a9d71 100644 --- a/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_can_set_custom_model__65.txt +++ b/tests/__snapshots__/files/MigrationMakeCommandTest__it_generates__add__migration_for_the_module_without_fields__1.php @@ -11,10 +11,8 @@ */ public function up(): void { - Schema::create('posts', static function (Blueprint $table) { - $table->id(); + Schema::table('articles', static function (Blueprint $table) { - $table->timestamps(); }); } @@ -23,6 +21,8 @@ public function up(): void */ public function down(): void { - Schema::dropIfExists('posts'); + Schema::table('articles', static function (Blueprint $table) { + + }); } }; diff --git a/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_generates_module_components__31.txt b/tests/__snapshots__/files/MigrationMakeCommandTest__it_generates__create__migration_for_the_module__1.php similarity index 74% rename from tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_generates_module_components__31.txt rename to tests/__snapshots__/files/MigrationMakeCommandTest__it_generates__create__migration_for_the_module__1.php index 13b5e38..a1fea11 100644 --- a/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_generates_module_components__31.txt +++ b/tests/__snapshots__/files/MigrationMakeCommandTest__it_generates__create__migration_for_the_module__1.php @@ -13,6 +13,11 @@ public function up(): void { Schema::create('articles', static function (Blueprint $table) { $table->id(); + $table->string('title'); + $table->text('excerpt'); + $table->text('content'); + $table->integer('user_id')->unsigned(); + $table->foreign('user_id')->references('id')->on('users'); $table->timestamps(); }); diff --git a/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_generates_module_components__31.txt b/tests/__snapshots__/files/MigrationMakeCommandTest__it_generates__create__migration_for_the_module_without_fields__1.php similarity index 100% rename from tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_generates_module_components__31.txt rename to tests/__snapshots__/files/MigrationMakeCommandTest__it_generates__create__migration_for_the_module_without_fields__1.php diff --git a/tests/__snapshots__/files/MigrationMakeCommandTest__it_generates__delete__migration_for_the_module__1.php b/tests/__snapshots__/files/MigrationMakeCommandTest__it_generates__delete__migration_for_the_module__1.php new file mode 100644 index 0000000..72d9761 --- /dev/null +++ b/tests/__snapshots__/files/MigrationMakeCommandTest__it_generates__delete__migration_for_the_module__1.php @@ -0,0 +1,36 @@ +dropColumn('title'); + $table->dropColumn('excerpt'); + $table->dropColumn('user_id'); + $table->dropForeign(['user_id']); + + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('articles', static function (Blueprint $table) { + $table->string('title'); + $table->text('excerpt'); + $table->integer('user_id')->unsigned(); + $table->foreign('user_id')->references('id')->on('users'); + + }); + } +}; diff --git a/tests/__snapshots__/files/MigrationMakeCommandTest__it_generates__delete__migration_for_the_module_without_fields__1.php b/tests/__snapshots__/files/MigrationMakeCommandTest__it_generates__delete__migration_for_the_module_without_fields__1.php new file mode 100644 index 0000000..11a9d71 --- /dev/null +++ b/tests/__snapshots__/files/MigrationMakeCommandTest__it_generates__delete__migration_for_the_module_without_fields__1.php @@ -0,0 +1,28 @@ +unsignedBigInteger('article_id')->index(); + $table->foreign('article_id')->references('id')->on('articles')->onDelete('cascade'); + $table->unsignedBigInteger('author_id')->index(); + $table->foreign('author_id')->references('id')->on('authors')->onDelete('cascade'); + $table->primary(['article_id', 'author_id']); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('article_author'); + } +}; diff --git a/tests/Commands/Generators/__snapshots__/MigrationMakeCommandTest__it_generates_correct_plain_migration_file_content__1.txt b/tests/__snapshots__/files/MigrationMakeCommandTest__it_generates__plain__migration_for_the_module__1.php similarity index 100% rename from tests/Commands/Generators/__snapshots__/MigrationMakeCommandTest__it_generates_correct_plain_migration_file_content__1.txt rename to tests/__snapshots__/files/MigrationMakeCommandTest__it_generates__plain__migration_for_the_module__1.php diff --git a/tests/__snapshots__/files/MigrationMakeCommandTest__it_generates__plain__migration_for_the_module_if_the_stub_is_not_recognized_by_name__1.php b/tests/__snapshots__/files/MigrationMakeCommandTest__it_generates__plain__migration_for_the_module_if_the_stub_is_not_recognized_by_name__1.php new file mode 100644 index 0000000..88fa2f3 --- /dev/null +++ b/tests/__snapshots__/files/MigrationMakeCommandTest__it_generates__plain__migration_for_the_module_if_the_stub_is_not_recognized_by_name__1.php @@ -0,0 +1,24 @@ +all(); + + if ($data) { + $articleComment->update($data); + } + + return $articleComment; + } + + public function asController(UpdateArticleCommentRequest $request, ArticleComment $articleComment): ArticleCommentResource + { + $articleComment = $this->handle($articleComment, $request->toDTO()); + + return new ArticleCommentResource($articleComment); + } +} diff --git a/tests/__snapshots__/files/ModuleMakeCommandTest__it_generates_a__api__module__11.php b/tests/__snapshots__/files/ModuleMakeCommandTest__it_generates_a__api__module__11.php new file mode 100644 index 0000000..74f5661 --- /dev/null +++ b/tests/__snapshots__/files/ModuleMakeCommandTest__it_generates_a__api__module__11.php @@ -0,0 +1,25 @@ +build(); + } + + public function asController(ViewArticleCommentRequest $request, ArticleComment $articleComment): ArticleCommentResource + { + return new ArticleCommentResource($this->handle($request, $articleComment)); + } +} diff --git a/tests/__snapshots__/files/ModuleMakeCommandTest__it_generates_a__api__module__12.php b/tests/__snapshots__/files/ModuleMakeCommandTest__it_generates_a__api__module__12.php new file mode 100644 index 0000000..3bbcd57 --- /dev/null +++ b/tests/__snapshots__/files/ModuleMakeCommandTest__it_generates_a__api__module__12.php @@ -0,0 +1,13 @@ +can('view-article-comment'); + } + + /** + * Determine whether the user can view the model. + */ + public function view(User $user, ArticleComment $articleComment): bool + { + return $user->can('view-article-comment'); + } + + /** + * Determine whether the user can create models. + */ + public function create(User $user): bool + { + return $user->can('create-article-comment'); + } + + /** + * Determine whether the user can update the model. + */ + public function update(User $user, ArticleComment $articleComment): bool + { + return $user->can('update-article-comment'); + } + + /** + * Determine whether the user can delete the model. + */ + public function delete(User $user, ArticleComment $articleComment): bool + { + return $user->can('delete-article-comment'); + } + + /** + * Determine whether the user can restore the model. + */ + public function restore(User $user, ArticleComment $articleComment): bool + { + return $user->can('delete-article-comment'); + } + + /** + * Determine whether the user can permanently delete the model. + */ + public function forceDelete(User $user, ArticleComment $articleComment): bool + { + return $user->can('force-delete-article-comment'); + } +} diff --git a/tests/__snapshots__/files/ModuleMakeCommandTest__it_generates_a__api__module__16.php b/tests/__snapshots__/files/ModuleMakeCommandTest__it_generates_a__api__module__16.php new file mode 100644 index 0000000..1696363 --- /dev/null +++ b/tests/__snapshots__/files/ModuleMakeCommandTest__it_generates_a__api__module__16.php @@ -0,0 +1,102 @@ +loadConfigurations(); + } + + /** + * Bootstrap services. + */ + public function boot(): void + { + $this->loadMigrations(); + // $this->loadCommands(); + // $this->loadTranslations(); + // $this->loadViews(); + } + + /** + * Register configuration files. + */ + public function loadConfigurations(): void + { + $sourcePath = __DIR__.'/../../config/article-comment.php'; + $configsPath = $this->app->configPath('article-comment.php'); + + $this->mergeConfigFrom($sourcePath, 'article-comment'); + + $this->publishes([ + $sourcePath => $configsPath + ], 'article-comment-config'); + } + + /** + * Register migrations. + */ + public function loadMigrations(): void + { + $sourcePath = __DIR__.'/../../database/migrations'; + $migrationsPath = $this->app->databasePath('migrations'); + + $this->loadMigrationsFrom($sourcePath); + + $this->publishes([ + $sourcePath => $migrationsPath + ], 'article-comment-migrations'); + } + + /** + * Register artisan commands. + */ + public function loadCommands(): void + { + if ($this->app->runningInConsole()) { + $this->loadCommandsFrom([ + 'Modules\\ArticleComment\\UI\\CLI\\Commands' => __DIR__.'/../UI/CLI/Commands', + ]); + } + } + + /** + * Register translations. + */ + public function loadTranslations(): void + { + $sourcePath = __DIR__.'/../../lang'; + $langPath = $this->app->langPath('modules/article-comment'); + + $this->loadTranslationsFrom($sourcePath, 'article-comment'); + + $this->publishes([ + $sourcePath => $langPath, + ], 'article-comment-translations'); + } + + /** + * Register views. + */ + public function loadViews(): void + { + $sourcePath = __DIR__.'/../../resources/views'; + $viewsPath = $this->app->resourcePath('views/modules/article-comment'); + + $this->loadViewsFrom( + array_merge($this->getPublishableViewPaths('article-comment'), [$sourcePath]), + 'article-comment' + ); + + $this->publishes([ + $sourcePath => $viewsPath + ], 'article-comment-views'); + } +} diff --git a/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_can_set_custom_model__44.txt b/tests/__snapshots__/files/ModuleMakeCommandTest__it_generates_a__api__module__17.php similarity index 63% rename from tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_can_set_custom_model__44.txt rename to tests/__snapshots__/files/ModuleMakeCommandTest__it_generates_a__api__module__17.php index 3d39542..ba84e41 100644 --- a/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_can_set_custom_model__44.txt +++ b/tests/__snapshots__/files/ModuleMakeCommandTest__it_generates_a__api__module__17.php @@ -1,16 +1,14 @@ namespace('App\\Modules\\Blog\\UI\\WEB\\Controllers') + // ->namespace('Modules\\ArticleComment\\UI\\WEB\\Controllers') ->group(function () { - $this->loadRoutesFromDirectory(module_path($this->moduleName, 'UI/WEB/Routes')); + $this->loadRoutesFromDirectory(__DIR__.'/../UI/WEB/routes'); }); } @@ -51,9 +49,9 @@ protected function mapApiRoutes(): void { Route::prefix('api') ->middleware('api') -// ->namespace('App\\Modules\\Blog\\UI\\API\\Controllers') + // ->namespace('Modules\\ArticleComment\\UI\\API\\Controllers') ->group(function () { - $this->loadRoutesFromDirectory(module_path($this->moduleName, 'UI/API/Routes')); + $this->loadRoutesFromDirectory(__DIR__.'/../UI/API/routes'); }); } } diff --git a/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_can_set_custom_model__45.txt b/tests/__snapshots__/files/ModuleMakeCommandTest__it_generates_a__api__module__18.php similarity index 61% rename from tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_can_set_custom_model__45.txt rename to tests/__snapshots__/files/ModuleMakeCommandTest__it_generates_a__api__module__18.php index 7a433b4..89a3dec 100644 --- a/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_can_set_custom_model__45.txt +++ b/tests/__snapshots__/files/ModuleMakeCommandTest__it_generates_a__api__module__18.php @@ -1,14 +1,14 @@ + * @return array */ protected function allowedAppends(): array { @@ -16,7 +16,7 @@ protected function allowedAppends(): array } /** - * @return array + * @return array */ protected function defaultAppends(): array { @@ -24,7 +24,7 @@ protected function defaultAppends(): array } /** - * @return array + * @return array */ protected function allowedFields(): array { @@ -34,7 +34,7 @@ protected function allowedFields(): array } /** - * @return array + * @return array */ protected function allowedIncludes(): array { @@ -42,7 +42,7 @@ protected function allowedIncludes(): array } /** - * @return array + * @return array */ protected function defaultIncludes(): array { diff --git a/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_can_set_custom_model__16.txt b/tests/__snapshots__/files/ModuleMakeCommandTest__it_generates_a__api__module__19.php similarity index 65% rename from tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_can_set_custom_model__16.txt rename to tests/__snapshots__/files/ModuleMakeCommandTest__it_generates_a__api__module__19.php index 05226fd..804bc9e 100644 --- a/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_can_set_custom_model__16.txt +++ b/tests/__snapshots__/files/ModuleMakeCommandTest__it_generates_a__api__module__19.php @@ -1,16 +1,16 @@ + * @return array */ protected function allowedAppends(): array { @@ -18,7 +18,7 @@ protected function allowedAppends(): array } /** - * @return array + * @return array */ protected function defaultAppends(): array { @@ -26,7 +26,7 @@ protected function defaultAppends(): array } /** - * @return array + * @return array */ protected function allowedFields(): array { @@ -36,7 +36,7 @@ protected function allowedFields(): array } /** - * @return array + * @return array */ protected function allowedFilters(): array { @@ -44,7 +44,7 @@ protected function allowedFilters(): array } /** - * @return array + * @return array */ protected function allowedIncludes(): array { @@ -52,7 +52,7 @@ protected function allowedIncludes(): array } /** - * @return array + * @return array */ protected function defaultIncludes(): array { @@ -60,7 +60,7 @@ protected function defaultIncludes(): array } /** - * @return array + * @return array */ protected function allowedSorts(): array { @@ -68,7 +68,7 @@ protected function allowedSorts(): array } /** - * @return array + * @return array */ protected function defaultSorts(): array { diff --git a/tests/__snapshots__/files/ModuleMakeCommandTest__it_generates_a__api__module__20.php b/tests/__snapshots__/files/ModuleMakeCommandTest__it_generates_a__api__module__20.php new file mode 100644 index 0000000..0c198a3 --- /dev/null +++ b/tests/__snapshots__/files/ModuleMakeCommandTest__it_generates_a__api__module__20.php @@ -0,0 +1,28 @@ +validated()); + } +} diff --git a/tests/__snapshots__/files/ModuleMakeCommandTest__it_generates_a__api__module__21.php b/tests/__snapshots__/files/ModuleMakeCommandTest__it_generates_a__api__module__21.php new file mode 100644 index 0000000..2710975 --- /dev/null +++ b/tests/__snapshots__/files/ModuleMakeCommandTest__it_generates_a__api__module__21.php @@ -0,0 +1,20 @@ +route('articleComment'); + return $articleComment && Gate::check('delete', $articleComment); + } +} diff --git a/tests/__snapshots__/files/ModuleMakeCommandTest__it_generates_a__api__module__22.php b/tests/__snapshots__/files/ModuleMakeCommandTest__it_generates_a__api__module__22.php new file mode 100644 index 0000000..16b668a --- /dev/null +++ b/tests/__snapshots__/files/ModuleMakeCommandTest__it_generates_a__api__module__22.php @@ -0,0 +1,20 @@ +route('articleComment'); + return $articleComment && Gate::check('update', $articleComment); + } + + public function toDTO(): UpdateArticleCommentDTO + { + return UpdateArticleCommentDTO::from($this->validated()); + } +} diff --git a/tests/__snapshots__/files/ModuleMakeCommandTest__it_generates_a__api__module__24.php b/tests/__snapshots__/files/ModuleMakeCommandTest__it_generates_a__api__module__24.php new file mode 100644 index 0000000..814263f --- /dev/null +++ b/tests/__snapshots__/files/ModuleMakeCommandTest__it_generates_a__api__module__24.php @@ -0,0 +1,20 @@ +route('articleComment'); + return $articleComment && Gate::check('view', $articleComment); + } +} diff --git a/tests/__snapshots__/files/ModuleMakeCommandTest__it_generates_a__api__module__25.php b/tests/__snapshots__/files/ModuleMakeCommandTest__it_generates_a__api__module__25.php new file mode 100644 index 0000000..368a4fd --- /dev/null +++ b/tests/__snapshots__/files/ModuleMakeCommandTest__it_generates_a__api__module__25.php @@ -0,0 +1,17 @@ +name('api.article_comments.create'); diff --git a/tests/__snapshots__/files/ModuleMakeCommandTest__it_generates_a__api__module__27.php b/tests/__snapshots__/files/ModuleMakeCommandTest__it_generates_a__api__module__27.php new file mode 100644 index 0000000..3098b2d --- /dev/null +++ b/tests/__snapshots__/files/ModuleMakeCommandTest__it_generates_a__api__module__27.php @@ -0,0 +1,7 @@ +name('api.article_comments.delete'); diff --git a/tests/__snapshots__/files/ModuleMakeCommandTest__it_generates_a__api__module__28.php b/tests/__snapshots__/files/ModuleMakeCommandTest__it_generates_a__api__module__28.php new file mode 100644 index 0000000..2fc2cd8 --- /dev/null +++ b/tests/__snapshots__/files/ModuleMakeCommandTest__it_generates_a__api__module__28.php @@ -0,0 +1,7 @@ +name('api.article_comments.list'); diff --git a/tests/__snapshots__/files/ModuleMakeCommandTest__it_generates_a__api__module__29.php b/tests/__snapshots__/files/ModuleMakeCommandTest__it_generates_a__api__module__29.php new file mode 100644 index 0000000..872915f --- /dev/null +++ b/tests/__snapshots__/files/ModuleMakeCommandTest__it_generates_a__api__module__29.php @@ -0,0 +1,7 @@ +name('api.article_comments.update'); diff --git a/tests/__snapshots__/files/ModuleMakeCommandTest__it_generates_a__api__module__3.json b/tests/__snapshots__/files/ModuleMakeCommandTest__it_generates_a__api__module__3.json new file mode 100644 index 0000000..e7f3966 --- /dev/null +++ b/tests/__snapshots__/files/ModuleMakeCommandTest__it_generates_a__api__module__3.json @@ -0,0 +1,32 @@ +{ + "name": "demo/article-comment", + "description": "article-comment module", + "version": "1.0.0", + "authors": [ + { + "name": "Example", + "email": "example@example.com" + } + ], + "autoload": { + "psr-4": { + "Modules\\ArticleComment\\": "src/", + "Modules\\ArticleComment\\Database\\Factories\\": "database/factories/", + "Modules\\ArticleComment\\Database\\Seeders\\": "database/seeders/" + } + }, + "autoload-dev": { + "psr-4": { + "Modules\\ArticleComment\\Tests\\": "tests/" + } + }, + "extra": { + "laravel": { + "providers": [ + "Modules\\ArticleComment\\Providers\\ArticleCommentServiceProvider", + "Modules\\ArticleComment\\Providers\\RouteServiceProvider" + ], + "aliases": [] + } + } +} diff --git a/tests/__snapshots__/files/ModuleMakeCommandTest__it_generates_a__api__module__30.php b/tests/__snapshots__/files/ModuleMakeCommandTest__it_generates_a__api__module__30.php new file mode 100644 index 0000000..b6557d5 --- /dev/null +++ b/tests/__snapshots__/files/ModuleMakeCommandTest__it_generates_a__api__module__30.php @@ -0,0 +1,7 @@ +name('api.article_comments.view'); diff --git a/tests/__snapshots__/files/ModuleMakeCommandTest__it_generates_a__api__module__31.php b/tests/__snapshots__/files/ModuleMakeCommandTest__it_generates_a__api__module__31.php new file mode 100644 index 0000000..7324e76 --- /dev/null +++ b/tests/__snapshots__/files/ModuleMakeCommandTest__it_generates_a__api__module__31.php @@ -0,0 +1,80 @@ + 'create-article-comment', + 'roles' => '', + ]; + + public function test_create_article_comment(): void + { + $this->actingAsTestUser(); + + $data = $this->getTestData(); + + $this->postJson(route('api.article_comments.create'), $data) + ->assertCreated() + ->assertJson(fn (AssertableJson $json) => + $json->has('data', fn (AssertableJson $json) => + $json->has('id') + ->whereAll($data) + ->etc() + ) + ); + + $this->assertDatabaseHas(ArticleComment::class, $data); + } + + public function test_create_article_comment_with_invalid_data(): void + { + $this->actingAsTestUser(); + + $this->postJson(route('api.article_comments.create'), []) + ->assertUnprocessable() + ->assertJsonValidationErrors([ + // TODO: add expected validation errors here + ]); + } + + public function test_create_article_comment_unauthenticated(): void + { + $data = $this->getTestData(); + + $this->postJson(route('api.article_comments.create'), $data) + ->assertUnauthorized(); + } + + public function test_create_article_comment_without_access(): void + { + $this->actingAsTestUserWithoutAccess(); + + $data = $this->getTestData(); + + $this->postJson(route('api.article_comments.create'), $data) + ->assertForbidden(); + } + + protected function getTestData(array $mergeData = []): array + { + return array_merge([ + // TODO: add fields here + ], $mergeData); + } +} diff --git a/tests/__snapshots__/files/ModuleMakeCommandTest__it_generates_a__api__module__32.php b/tests/__snapshots__/files/ModuleMakeCommandTest__it_generates_a__api__module__32.php new file mode 100644 index 0000000..8b6e412 --- /dev/null +++ b/tests/__snapshots__/files/ModuleMakeCommandTest__it_generates_a__api__module__32.php @@ -0,0 +1,62 @@ + 'delete-article-comment', + 'roles' => '', + ]; + + public function test_delete_article_comment(): void + { + $this->actingAsTestUser(); + + $articleComment = ArticleComment::factory()->create(); + + $this->deleteJson(route('api.article_comments.delete', ['articleComment' => $articleComment->getKey()])) + ->assertNoContent(); + + $this->assertNull(ArticleComment::find($articleComment->getKey())); + } + + public function test_delete_article_comment_unauthenticated(): void + { + $articleComment = ArticleComment::factory()->create(); + + $this->deleteJson(route('api.article_comments.delete', ['articleComment' => $articleComment->getKey()])) + ->assertUnauthorized(); + } + + public function test_delete_article_comment_without_access(): void + { + $this->actingAsTestUserWithoutAccess(); + + $articleComment = ArticleComment::factory()->create(); + + $this->deleteJson(route('api.article_comments.delete', ['articleComment' => $articleComment->getKey()])) + ->assertForbidden(); + } + + public function test_delete_not_existing_article_comment(): void + { + $this->actingAsTestUser(); + + $this->deleteJson(route('api.article_comments.delete', ['articleComment' => 7777])) + ->assertNotFound(); + } +} diff --git a/tests/__snapshots__/files/ModuleMakeCommandTest__it_generates_a__api__module__33.php b/tests/__snapshots__/files/ModuleMakeCommandTest__it_generates_a__api__module__33.php new file mode 100644 index 0000000..76e410b --- /dev/null +++ b/tests/__snapshots__/files/ModuleMakeCommandTest__it_generates_a__api__module__33.php @@ -0,0 +1,58 @@ + 'view-article-comment', + 'roles' => '', + ]; + + public function test_list_article_comments(): void + { + $this->actingAsTestUser(); + + ArticleComment::factory()->count(3)->create(); + + $this->getJson(route('api.article_comments.list')) + ->assertOk() + ->assertJsonStructure([ + 'links', + 'meta', + 'data' + ]) + ->assertJsonCount(3, 'data'); + } + + public function test_list_article_comments_unauthenticated(): void + { + ArticleComment::factory()->count(3)->create(); + + $this->getJson(route('api.article_comments.list')) + ->assertUnauthorized(); + } + + public function test_list_article_comments_without_access(): void + { + $this->actingAsTestUserWithoutAccess(); + + ArticleComment::factory()->count(3)->create(); + + $this->getJson(route('api.article_comments.list')) + ->assertForbidden(); + } +} diff --git a/tests/__snapshots__/files/ModuleMakeCommandTest__it_generates_a__api__module__34.php b/tests/__snapshots__/files/ModuleMakeCommandTest__it_generates_a__api__module__34.php new file mode 100644 index 0000000..eeb040d --- /dev/null +++ b/tests/__snapshots__/files/ModuleMakeCommandTest__it_generates_a__api__module__34.php @@ -0,0 +1,100 @@ + 'update-article-comment', + 'roles' => '', + ]; + + public function test_update_article_comment(): void + { + $this->actingAsTestUser(); + + $articleComment = ArticleComment::factory()->create(); + + $data = $this->getTestData(); + $expectedData = array_merge($data, [ + 'id' => $articleComment->getKey(), + ]); + + $this->patchJson(route('api.article_comments.update', ['articleComment' => $articleComment->getKey()]), $data) + ->assertOk() + ->assertJson(fn (AssertableJson $json) => + $json->has('data', fn (AssertableJson $json) => + $json->whereAll($expectedData) + ->etc() + ) + ); + + $this->assertDatabaseHas(ArticleComment::class, $expectedData); + } + + public function test_update_article_comment_with_invalid_data(): void + { + $this->actingAsTestUser(); + + $articleComment = ArticleComment::factory()->create(); + + $this->patchJson(route('api.article_comments.update', ['articleComment' => $articleComment->getKey()]), []) + ->assertUnprocessable() + ->assertJsonValidationErrors([ + // TODO: add expected validation errors here + ]); + } + + public function test_update_article_comment_unauthenticated(): void + { + $articleComment = ArticleComment::factory()->create(); + + $data = $this->getTestData(); + + $this->patchJson(route('api.article_comments.update', ['articleComment' => $articleComment->getKey()]), $data) + ->assertUnauthorized(); + } + + public function test_update_article_comment_without_access(): void + { + $this->actingAsTestUserWithoutAccess(); + + $articleComment = ArticleComment::factory()->create(); + + $data = $this->getTestData(); + + $this->patchJson(route('api.article_comments.update', ['articleComment' => $articleComment->getKey()]), $data) + ->assertForbidden(); + } + + public function test_update_non_existing_article_comment(): void + { + $this->actingAsTestUser(); + + $data = $this->getTestData(); + + $this->patchJson(route('api.article_comments.update', ['articleComment' => 7777]), $data) + ->assertNotFound(); + } + + protected function getTestData(array $mergeData = []): array + { + return array_merge([ + // TODO: add fields here + ], $mergeData); + } +} diff --git a/tests/__snapshots__/files/ModuleMakeCommandTest__it_generates_a__api__module__35.php b/tests/__snapshots__/files/ModuleMakeCommandTest__it_generates_a__api__module__35.php new file mode 100644 index 0000000..370958c --- /dev/null +++ b/tests/__snapshots__/files/ModuleMakeCommandTest__it_generates_a__api__module__35.php @@ -0,0 +1,68 @@ + 'view-article-comment', + 'roles' => '', + ]; + + public function test_view_article_comment(): void + { + $this->actingAsTestUser(); + + $articleComment = ArticleComment::factory()->create(); + $expectedData = $articleComment->toArray(); + + $this->getJson(route('api.article_comments.view', ['articleComment' => $articleComment->getKey()])) + ->assertOk() + ->assertJson(fn (AssertableJson $json) => + $json->has('data', fn (AssertableJson $json) => + $json->whereAll($expectedData) + ->etc() + ) + ); + } + + public function test_view_article_comment_unauthenticated(): void + { + $articleComment = ArticleComment::factory()->create(); + + $this->getJson(route('api.article_comments.view', ['articleComment' => $articleComment->getKey()])) + ->assertUnauthorized(); + } + + public function test_view_article_comment_without_access(): void + { + $this->actingAsTestUserWithoutAccess(); + + $articleComment = ArticleComment::factory()->create(); + + $this->getJson(route('api.article_comments.view', ['articleComment' => $articleComment->getKey()])) + ->assertForbidden(); + } + + public function test_view_not_existing_article_comment(): void + { + $this->actingAsTestUser(); + + $this->getJson(route('api.article_comments.view', ['articleComment' => 7777])) + ->assertNotFound(); + } +} diff --git a/tests/__snapshots__/files/ModuleMakeCommandTest__it_generates_a__api__module__4.php b/tests/__snapshots__/files/ModuleMakeCommandTest__it_generates_a__api__module__4.php new file mode 100644 index 0000000..28b67d9 --- /dev/null +++ b/tests/__snapshots__/files/ModuleMakeCommandTest__it_generates_a__api__module__4.php @@ -0,0 +1,33 @@ +|ArticleComment create($attributes = [], ?ArticleComment $parent = null) + * @method \Illuminate\Support\Collection createMany(iterable $records) + * @method ArticleComment createOne($attributes = []) + * @method \Illuminate\Support\Collection|ArticleComment make($attributes = [], ?ArticleComment $parent = null) + * @method ArticleComment makeOne($attributes = []) + */ +class ArticleCommentFactory extends Factory +{ + /** + * The name of the factory's corresponding model. + * + * @var class-string + */ + protected $model = ArticleComment::class; + + /** + * Define the model's default state. + */ + public function definition(): array + { + return [ + // TODO: add fields here + ]; + } +} diff --git a/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_can_set_custom_model__63.txt b/tests/__snapshots__/files/ModuleMakeCommandTest__it_generates_a__api__module__5.php similarity index 76% rename from tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_can_set_custom_model__63.txt rename to tests/__snapshots__/files/ModuleMakeCommandTest__it_generates_a__api__module__5.php index 917126a..6f9bd1b 100644 --- a/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_can_set_custom_model__63.txt +++ b/tests/__snapshots__/files/ModuleMakeCommandTest__it_generates_a__api__module__5.php @@ -11,7 +11,7 @@ */ public function up(): void { - Schema::create('comments', static function (Blueprint $table) { + Schema::create('article_comments', static function (Blueprint $table) { $table->id(); $table->timestamps(); @@ -23,6 +23,6 @@ public function up(): void */ public function down(): void { - Schema::dropIfExists('comments'); + Schema::dropIfExists('article_comments'); } }; diff --git a/tests/__snapshots__/files/ModuleMakeCommandTest__it_generates_a__api__module__6.php b/tests/__snapshots__/files/ModuleMakeCommandTest__it_generates_a__api__module__6.php new file mode 100644 index 0000000..7e1e80a --- /dev/null +++ b/tests/__snapshots__/files/ModuleMakeCommandTest__it_generates_a__api__module__6.php @@ -0,0 +1,45 @@ +handle(new CreatePermissionDTO( + name: 'view-article-comment', + display_name: 'View any "article-comments"', + group: 'article-comments' + )); + + $createPermissionAction->handle(new CreatePermissionDTO( + name: 'create-article-comment', + display_name: 'Create "article-comments"', + group: 'article-comments' + )); + + $createPermissionAction->handle(new CreatePermissionDTO( + name: 'update-article-comment', + display_name: 'Update any "article-comments"', + group: 'article-comments' + )); + + $createPermissionAction->handle(new CreatePermissionDTO( + name: 'delete-article-comment', + display_name: 'Delete any "article-comments"', + group: 'article-comments' + )); + + $createPermissionAction->handle(new CreatePermissionDTO( + name: 'force-delete-article-comment', + display_name: 'Force delete any "article-comments"', + group: 'article-comments' + )); + } +} diff --git a/tests/__snapshots__/files/ModuleMakeCommandTest__it_generates_a__api__module__7.php b/tests/__snapshots__/files/ModuleMakeCommandTest__it_generates_a__api__module__7.php new file mode 100644 index 0000000..27192d2 --- /dev/null +++ b/tests/__snapshots__/files/ModuleMakeCommandTest__it_generates_a__api__module__7.php @@ -0,0 +1,27 @@ +all()); + } + + public function asController(CreateArticleCommentRequest $request): JsonResponse + { + $articleComment = $this->handle($request->toDTO()); + + return (new ArticleCommentResource($articleComment))->created(); + } +} diff --git a/tests/__snapshots__/files/ModuleMakeCommandTest__it_generates_a__api__module__8.php b/tests/__snapshots__/files/ModuleMakeCommandTest__it_generates_a__api__module__8.php new file mode 100644 index 0000000..710f7e1 --- /dev/null +++ b/tests/__snapshots__/files/ModuleMakeCommandTest__it_generates_a__api__module__8.php @@ -0,0 +1,25 @@ +delete(); + } + + public function asController(DeleteArticleCommentRequest $request, ArticleComment $articleComment): Response + { + $this->handle($articleComment); + + return $this->noContent(); + } +} diff --git a/tests/__snapshots__/files/ModuleMakeCommandTest__it_generates_a__api__module__9.php b/tests/__snapshots__/files/ModuleMakeCommandTest__it_generates_a__api__module__9.php new file mode 100644 index 0000000..865dd73 --- /dev/null +++ b/tests/__snapshots__/files/ModuleMakeCommandTest__it_generates_a__api__module__9.php @@ -0,0 +1,28 @@ +build() + ->paginate(); + } + + public function asController(ListArticleCommentsRequest $request): AnonymousResourceCollection + { + return ArticleCommentResource::collection($this->handle($request)); + } +} diff --git a/tests/__snapshots__/files/ModuleMakeCommandTest__it_generates_a__base__module__1.json b/tests/__snapshots__/files/ModuleMakeCommandTest__it_generates_a__base__module__1.json new file mode 100644 index 0000000..d13e452 --- /dev/null +++ b/tests/__snapshots__/files/ModuleMakeCommandTest__it_generates_a__base__module__1.json @@ -0,0 +1,55 @@ +{ + "name": "laraneat/laraneat", + "type": "project", + "description": "The Laraneat framework.", + "keywords": [ + "laraneat", + "laravel", + "apiato", + "php", + "framework" + ], + "license": "MIT", + "require": { + "php": "^8.1", + "demo/article-comment": "*", + "laravel/framework": "^10.0" + }, + "require-dev": { + "phpunit/phpunit": "^10.5.9" + }, + "autoload": { + "classmap": [ + "database" + ], + "psr-4": { + "App\\": "app/", + "Modules\\": "modules/" + } + }, + "extra": { + "laravel": { + "dont-discover": [] + } + }, + "config": { + "optimize-autoloader": true, + "preferred-install": "dist", + "sort-packages": true, + "allow-plugins": { + "pestphp/pest-plugin": true, + "php-http/discovery": true + } + }, + "minimum-stability": "stable", + "prefer-stable": true, + "repositories": [ + { + "type": "path", + "url": "modules/*", + "options": { + "symlink": true + } + } + ] +} diff --git a/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_can_set_custom_model__14.txt b/tests/__snapshots__/files/ModuleMakeCommandTest__it_generates_a__base__module__10.php similarity index 63% rename from tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_can_set_custom_model__14.txt rename to tests/__snapshots__/files/ModuleMakeCommandTest__it_generates_a__base__module__10.php index 3d39542..ba84e41 100644 --- a/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_can_set_custom_model__14.txt +++ b/tests/__snapshots__/files/ModuleMakeCommandTest__it_generates_a__base__module__10.php @@ -1,16 +1,14 @@ namespace('App\\Modules\\Blog\\UI\\WEB\\Controllers') + // ->namespace('Modules\\ArticleComment\\UI\\WEB\\Controllers') ->group(function () { - $this->loadRoutesFromDirectory(module_path($this->moduleName, 'UI/WEB/Routes')); + $this->loadRoutesFromDirectory(__DIR__.'/../UI/WEB/routes'); }); } @@ -51,9 +49,9 @@ protected function mapApiRoutes(): void { Route::prefix('api') ->middleware('api') -// ->namespace('App\\Modules\\Blog\\UI\\API\\Controllers') + // ->namespace('Modules\\ArticleComment\\UI\\API\\Controllers') ->group(function () { - $this->loadRoutesFromDirectory(module_path($this->moduleName, 'UI/API/Routes')); + $this->loadRoutesFromDirectory(__DIR__.'/../UI/API/routes'); }); } } diff --git a/tests/__snapshots__/files/ModuleMakeCommandTest__it_generates_a__base__module__3.json b/tests/__snapshots__/files/ModuleMakeCommandTest__it_generates_a__base__module__3.json new file mode 100644 index 0000000..e7f3966 --- /dev/null +++ b/tests/__snapshots__/files/ModuleMakeCommandTest__it_generates_a__base__module__3.json @@ -0,0 +1,32 @@ +{ + "name": "demo/article-comment", + "description": "article-comment module", + "version": "1.0.0", + "authors": [ + { + "name": "Example", + "email": "example@example.com" + } + ], + "autoload": { + "psr-4": { + "Modules\\ArticleComment\\": "src/", + "Modules\\ArticleComment\\Database\\Factories\\": "database/factories/", + "Modules\\ArticleComment\\Database\\Seeders\\": "database/seeders/" + } + }, + "autoload-dev": { + "psr-4": { + "Modules\\ArticleComment\\Tests\\": "tests/" + } + }, + "extra": { + "laravel": { + "providers": [ + "Modules\\ArticleComment\\Providers\\ArticleCommentServiceProvider", + "Modules\\ArticleComment\\Providers\\RouteServiceProvider" + ], + "aliases": [] + } + } +} diff --git a/tests/__snapshots__/files/ModuleMakeCommandTest__it_generates_a__base__module__4.php b/tests/__snapshots__/files/ModuleMakeCommandTest__it_generates_a__base__module__4.php new file mode 100644 index 0000000..28b67d9 --- /dev/null +++ b/tests/__snapshots__/files/ModuleMakeCommandTest__it_generates_a__base__module__4.php @@ -0,0 +1,33 @@ +|ArticleComment create($attributes = [], ?ArticleComment $parent = null) + * @method \Illuminate\Support\Collection createMany(iterable $records) + * @method ArticleComment createOne($attributes = []) + * @method \Illuminate\Support\Collection|ArticleComment make($attributes = [], ?ArticleComment $parent = null) + * @method ArticleComment makeOne($attributes = []) + */ +class ArticleCommentFactory extends Factory +{ + /** + * The name of the factory's corresponding model. + * + * @var class-string + */ + protected $model = ArticleComment::class; + + /** + * Define the model's default state. + */ + public function definition(): array + { + return [ + // TODO: add fields here + ]; + } +} diff --git a/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_can_set_custom_model__64.txt b/tests/__snapshots__/files/ModuleMakeCommandTest__it_generates_a__base__module__5.php similarity index 76% rename from tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_can_set_custom_model__64.txt rename to tests/__snapshots__/files/ModuleMakeCommandTest__it_generates_a__base__module__5.php index f39c303..6f9bd1b 100644 --- a/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_can_set_custom_model__64.txt +++ b/tests/__snapshots__/files/ModuleMakeCommandTest__it_generates_a__base__module__5.php @@ -11,7 +11,7 @@ */ public function up(): void { - Schema::create('posts', static function (Blueprint $table) { + Schema::create('article_comments', static function (Blueprint $table) { $table->id(); $table->timestamps(); @@ -23,6 +23,6 @@ public function up(): void */ public function down(): void { - Schema::dropIfExists('posts'); + Schema::dropIfExists('article_comments'); } }; diff --git a/tests/__snapshots__/files/ModuleMakeCommandTest__it_generates_a__base__module__6.php b/tests/__snapshots__/files/ModuleMakeCommandTest__it_generates_a__base__module__6.php new file mode 100644 index 0000000..7e1e80a --- /dev/null +++ b/tests/__snapshots__/files/ModuleMakeCommandTest__it_generates_a__base__module__6.php @@ -0,0 +1,45 @@ +handle(new CreatePermissionDTO( + name: 'view-article-comment', + display_name: 'View any "article-comments"', + group: 'article-comments' + )); + + $createPermissionAction->handle(new CreatePermissionDTO( + name: 'create-article-comment', + display_name: 'Create "article-comments"', + group: 'article-comments' + )); + + $createPermissionAction->handle(new CreatePermissionDTO( + name: 'update-article-comment', + display_name: 'Update any "article-comments"', + group: 'article-comments' + )); + + $createPermissionAction->handle(new CreatePermissionDTO( + name: 'delete-article-comment', + display_name: 'Delete any "article-comments"', + group: 'article-comments' + )); + + $createPermissionAction->handle(new CreatePermissionDTO( + name: 'force-delete-article-comment', + display_name: 'Force delete any "article-comments"', + group: 'article-comments' + )); + } +} diff --git a/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_can_set_custom_model__11.txt b/tests/__snapshots__/files/ModuleMakeCommandTest__it_generates_a__base__module__7.php similarity index 67% rename from tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_can_set_custom_model__11.txt rename to tests/__snapshots__/files/ModuleMakeCommandTest__it_generates_a__base__module__7.php index 2a66463..1b97872 100644 --- a/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_can_set_custom_model__11.txt +++ b/tests/__snapshots__/files/ModuleMakeCommandTest__it_generates_a__base__module__7.php @@ -1,13 +1,13 @@ can('view-article-comment'); + } + + /** + * Determine whether the user can view the model. + */ + public function view(User $user, ArticleComment $articleComment): bool + { + return $user->can('view-article-comment'); + } + + /** + * Determine whether the user can create models. + */ + public function create(User $user): bool + { + return $user->can('create-article-comment'); + } + + /** + * Determine whether the user can update the model. + */ + public function update(User $user, ArticleComment $articleComment): bool + { + return $user->can('update-article-comment'); + } + + /** + * Determine whether the user can delete the model. + */ + public function delete(User $user, ArticleComment $articleComment): bool + { + return $user->can('delete-article-comment'); + } + + /** + * Determine whether the user can restore the model. + */ + public function restore(User $user, ArticleComment $articleComment): bool + { + return $user->can('delete-article-comment'); + } + + /** + * Determine whether the user can permanently delete the model. + */ + public function forceDelete(User $user, ArticleComment $articleComment): bool + { + return $user->can('force-delete-article-comment'); + } +} diff --git a/tests/__snapshots__/files/ModuleMakeCommandTest__it_generates_a__base__module__9.php b/tests/__snapshots__/files/ModuleMakeCommandTest__it_generates_a__base__module__9.php new file mode 100644 index 0000000..1696363 --- /dev/null +++ b/tests/__snapshots__/files/ModuleMakeCommandTest__it_generates_a__base__module__9.php @@ -0,0 +1,102 @@ +loadConfigurations(); + } + + /** + * Bootstrap services. + */ + public function boot(): void + { + $this->loadMigrations(); + // $this->loadCommands(); + // $this->loadTranslations(); + // $this->loadViews(); + } + + /** + * Register configuration files. + */ + public function loadConfigurations(): void + { + $sourcePath = __DIR__.'/../../config/article-comment.php'; + $configsPath = $this->app->configPath('article-comment.php'); + + $this->mergeConfigFrom($sourcePath, 'article-comment'); + + $this->publishes([ + $sourcePath => $configsPath + ], 'article-comment-config'); + } + + /** + * Register migrations. + */ + public function loadMigrations(): void + { + $sourcePath = __DIR__.'/../../database/migrations'; + $migrationsPath = $this->app->databasePath('migrations'); + + $this->loadMigrationsFrom($sourcePath); + + $this->publishes([ + $sourcePath => $migrationsPath + ], 'article-comment-migrations'); + } + + /** + * Register artisan commands. + */ + public function loadCommands(): void + { + if ($this->app->runningInConsole()) { + $this->loadCommandsFrom([ + 'Modules\\ArticleComment\\UI\\CLI\\Commands' => __DIR__.'/../UI/CLI/Commands', + ]); + } + } + + /** + * Register translations. + */ + public function loadTranslations(): void + { + $sourcePath = __DIR__.'/../../lang'; + $langPath = $this->app->langPath('modules/article-comment'); + + $this->loadTranslationsFrom($sourcePath, 'article-comment'); + + $this->publishes([ + $sourcePath => $langPath, + ], 'article-comment-translations'); + } + + /** + * Register views. + */ + public function loadViews(): void + { + $sourcePath = __DIR__.'/../../resources/views'; + $viewsPath = $this->app->resourcePath('views/modules/article-comment'); + + $this->loadViewsFrom( + array_merge($this->getPublishableViewPaths('article-comment'), [$sourcePath]), + 'article-comment' + ); + + $this->publishes([ + $sourcePath => $viewsPath + ], 'article-comment-views'); + } +} diff --git a/tests/__snapshots__/files/ModuleMakeCommandTest__it_generates_a__plain__module__1.json b/tests/__snapshots__/files/ModuleMakeCommandTest__it_generates_a__plain__module__1.json new file mode 100644 index 0000000..d13e452 --- /dev/null +++ b/tests/__snapshots__/files/ModuleMakeCommandTest__it_generates_a__plain__module__1.json @@ -0,0 +1,55 @@ +{ + "name": "laraneat/laraneat", + "type": "project", + "description": "The Laraneat framework.", + "keywords": [ + "laraneat", + "laravel", + "apiato", + "php", + "framework" + ], + "license": "MIT", + "require": { + "php": "^8.1", + "demo/article-comment": "*", + "laravel/framework": "^10.0" + }, + "require-dev": { + "phpunit/phpunit": "^10.5.9" + }, + "autoload": { + "classmap": [ + "database" + ], + "psr-4": { + "App\\": "app/", + "Modules\\": "modules/" + } + }, + "extra": { + "laravel": { + "dont-discover": [] + } + }, + "config": { + "optimize-autoloader": true, + "preferred-install": "dist", + "sort-packages": true, + "allow-plugins": { + "pestphp/pest-plugin": true, + "php-http/discovery": true + } + }, + "minimum-stability": "stable", + "prefer-stable": true, + "repositories": [ + { + "type": "path", + "url": "modules/*", + "options": { + "symlink": true + } + } + ] +} diff --git a/tests/__snapshots__/files/ModuleMakeCommandTest__it_generates_a__plain__module__3.json b/tests/__snapshots__/files/ModuleMakeCommandTest__it_generates_a__plain__module__3.json new file mode 100644 index 0000000..e7f3966 --- /dev/null +++ b/tests/__snapshots__/files/ModuleMakeCommandTest__it_generates_a__plain__module__3.json @@ -0,0 +1,32 @@ +{ + "name": "demo/article-comment", + "description": "article-comment module", + "version": "1.0.0", + "authors": [ + { + "name": "Example", + "email": "example@example.com" + } + ], + "autoload": { + "psr-4": { + "Modules\\ArticleComment\\": "src/", + "Modules\\ArticleComment\\Database\\Factories\\": "database/factories/", + "Modules\\ArticleComment\\Database\\Seeders\\": "database/seeders/" + } + }, + "autoload-dev": { + "psr-4": { + "Modules\\ArticleComment\\Tests\\": "tests/" + } + }, + "extra": { + "laravel": { + "providers": [ + "Modules\\ArticleComment\\Providers\\ArticleCommentServiceProvider", + "Modules\\ArticleComment\\Providers\\RouteServiceProvider" + ], + "aliases": [] + } + } +} diff --git a/tests/__snapshots__/files/ModuleMakeCommandTest__it_generates_a__plain__module__4.php b/tests/__snapshots__/files/ModuleMakeCommandTest__it_generates_a__plain__module__4.php new file mode 100644 index 0000000..1696363 --- /dev/null +++ b/tests/__snapshots__/files/ModuleMakeCommandTest__it_generates_a__plain__module__4.php @@ -0,0 +1,102 @@ +loadConfigurations(); + } + + /** + * Bootstrap services. + */ + public function boot(): void + { + $this->loadMigrations(); + // $this->loadCommands(); + // $this->loadTranslations(); + // $this->loadViews(); + } + + /** + * Register configuration files. + */ + public function loadConfigurations(): void + { + $sourcePath = __DIR__.'/../../config/article-comment.php'; + $configsPath = $this->app->configPath('article-comment.php'); + + $this->mergeConfigFrom($sourcePath, 'article-comment'); + + $this->publishes([ + $sourcePath => $configsPath + ], 'article-comment-config'); + } + + /** + * Register migrations. + */ + public function loadMigrations(): void + { + $sourcePath = __DIR__.'/../../database/migrations'; + $migrationsPath = $this->app->databasePath('migrations'); + + $this->loadMigrationsFrom($sourcePath); + + $this->publishes([ + $sourcePath => $migrationsPath + ], 'article-comment-migrations'); + } + + /** + * Register artisan commands. + */ + public function loadCommands(): void + { + if ($this->app->runningInConsole()) { + $this->loadCommandsFrom([ + 'Modules\\ArticleComment\\UI\\CLI\\Commands' => __DIR__.'/../UI/CLI/Commands', + ]); + } + } + + /** + * Register translations. + */ + public function loadTranslations(): void + { + $sourcePath = __DIR__.'/../../lang'; + $langPath = $this->app->langPath('modules/article-comment'); + + $this->loadTranslationsFrom($sourcePath, 'article-comment'); + + $this->publishes([ + $sourcePath => $langPath, + ], 'article-comment-translations'); + } + + /** + * Register views. + */ + public function loadViews(): void + { + $sourcePath = __DIR__.'/../../resources/views'; + $viewsPath = $this->app->resourcePath('views/modules/article-comment'); + + $this->loadViewsFrom( + array_merge($this->getPublishableViewPaths('article-comment'), [$sourcePath]), + 'article-comment' + ); + + $this->publishes([ + $sourcePath => $viewsPath + ], 'article-comment-views'); + } +} diff --git a/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_generates_module_components__12.txt b/tests/__snapshots__/files/ModuleMakeCommandTest__it_generates_a__plain__module__5.php similarity index 63% rename from tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_generates_module_components__12.txt rename to tests/__snapshots__/files/ModuleMakeCommandTest__it_generates_a__plain__module__5.php index 8e33d3a..ba84e41 100644 --- a/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_generates_module_components__12.txt +++ b/tests/__snapshots__/files/ModuleMakeCommandTest__it_generates_a__plain__module__5.php @@ -1,16 +1,14 @@ namespace('App\\Modules\\Article\\UI\\WEB\\Controllers') + // ->namespace('Modules\\ArticleComment\\UI\\WEB\\Controllers') ->group(function () { - $this->loadRoutesFromDirectory(module_path($this->moduleName, 'UI/WEB/Routes')); + $this->loadRoutesFromDirectory(__DIR__.'/../UI/WEB/routes'); }); } @@ -51,9 +49,9 @@ protected function mapApiRoutes(): void { Route::prefix('api') ->middleware('api') -// ->namespace('App\\Modules\\Article\\UI\\API\\Controllers') + // ->namespace('Modules\\ArticleComment\\UI\\API\\Controllers') ->group(function () { - $this->loadRoutesFromDirectory(module_path($this->moduleName, 'UI/API/Routes')); + $this->loadRoutesFromDirectory(__DIR__.'/../UI/API/routes'); }); } } diff --git a/tests/__snapshots__/files/ModuleTest__it_can_add_aliases__1.json b/tests/__snapshots__/files/ModuleTest__it_can_add_aliases__1.json new file mode 100644 index 0000000..da14fd9 --- /dev/null +++ b/tests/__snapshots__/files/ModuleTest__it_can_add_aliases__1.json @@ -0,0 +1,31 @@ +{ + "name": "laraneat/author", + "description": "Author module for testing", + "authors": [ + { + "name": "Salavat Salakhutdinov", + "email": "salahutdinov.salavat@gmail.com" + } + ], + "autoload": { + "psr-4": { + "Modules\\Author\\": "src/" + } + }, + "autoload-dev": { + "psr-4": { + "Modules\\Author\\Tests\\": "tests/" + } + }, + "extra": { + "laravel": { + "providers": [ + "Modules\\Author\\Providers\\AuthorServiceProvider", + "Modules\\Author\\Providers\\RouteServiceProvider" + ], + "aliases": { + "AuthorFacade": "Modules\\Author\\Facades\\SomeFacade" + } + } + } +} diff --git a/tests/__snapshots__/files/ModuleTest__it_can_add_aliases__2.json b/tests/__snapshots__/files/ModuleTest__it_can_add_aliases__2.json new file mode 100644 index 0000000..ebe9ac3 --- /dev/null +++ b/tests/__snapshots__/files/ModuleTest__it_can_add_aliases__2.json @@ -0,0 +1,33 @@ +{ + "name": "laraneat/author", + "description": "Author module for testing", + "authors": [ + { + "name": "Salavat Salakhutdinov", + "email": "salahutdinov.salavat@gmail.com" + } + ], + "autoload": { + "psr-4": { + "Modules\\Author\\": "src/" + } + }, + "autoload-dev": { + "psr-4": { + "Modules\\Author\\Tests\\": "tests/" + } + }, + "extra": { + "laravel": { + "providers": [ + "Modules\\Author\\Providers\\AuthorServiceProvider", + "Modules\\Author\\Providers\\RouteServiceProvider" + ], + "aliases": { + "AuthorFacade": "Modules\\Author\\Facades\\SomeFacade", + "foo": "Modules\\Author\\Services\\Foo", + "bar": "Modules\\Bar\\Services\\Bar" + } + } + } +} diff --git a/tests/__snapshots__/files/ModuleTest__it_can_add_aliases_via_ModuleConfigWriter__1.json b/tests/__snapshots__/files/ModuleTest__it_can_add_aliases_via_ModuleConfigWriter__1.json new file mode 100644 index 0000000..da14fd9 --- /dev/null +++ b/tests/__snapshots__/files/ModuleTest__it_can_add_aliases_via_ModuleConfigWriter__1.json @@ -0,0 +1,31 @@ +{ + "name": "laraneat/author", + "description": "Author module for testing", + "authors": [ + { + "name": "Salavat Salakhutdinov", + "email": "salahutdinov.salavat@gmail.com" + } + ], + "autoload": { + "psr-4": { + "Modules\\Author\\": "src/" + } + }, + "autoload-dev": { + "psr-4": { + "Modules\\Author\\Tests\\": "tests/" + } + }, + "extra": { + "laravel": { + "providers": [ + "Modules\\Author\\Providers\\AuthorServiceProvider", + "Modules\\Author\\Providers\\RouteServiceProvider" + ], + "aliases": { + "AuthorFacade": "Modules\\Author\\Facades\\SomeFacade" + } + } + } +} diff --git a/tests/__snapshots__/files/ModuleTest__it_can_add_aliases_via_ModuleConfigWriter__2.json b/tests/__snapshots__/files/ModuleTest__it_can_add_aliases_via_ModuleConfigWriter__2.json new file mode 100644 index 0000000..f9631aa --- /dev/null +++ b/tests/__snapshots__/files/ModuleTest__it_can_add_aliases_via_ModuleConfigWriter__2.json @@ -0,0 +1,32 @@ +{ + "name": "laraneat/author", + "description": "Author module for testing", + "authors": [ + { + "name": "Salavat Salakhutdinov", + "email": "salahutdinov.salavat@gmail.com" + } + ], + "autoload": { + "psr-4": { + "Modules\\Author\\": "src/" + } + }, + "autoload-dev": { + "psr-4": { + "Modules\\Author\\Tests\\": "tests/" + } + }, + "extra": { + "laravel": { + "providers": [ + "Modules\\Author\\Providers\\AuthorServiceProvider", + "Modules\\Author\\Providers\\RouteServiceProvider" + ], + "aliases": { + "AuthorFacade": "Modules\\Author\\Facades\\SomeFacade", + "bar": "Modules\\Bar\\Services\\Bar" + } + } + } +} diff --git a/tests/__snapshots__/files/ModuleTest__it_can_add_providers__1.json b/tests/__snapshots__/files/ModuleTest__it_can_add_providers__1.json new file mode 100644 index 0000000..da14fd9 --- /dev/null +++ b/tests/__snapshots__/files/ModuleTest__it_can_add_providers__1.json @@ -0,0 +1,31 @@ +{ + "name": "laraneat/author", + "description": "Author module for testing", + "authors": [ + { + "name": "Salavat Salakhutdinov", + "email": "salahutdinov.salavat@gmail.com" + } + ], + "autoload": { + "psr-4": { + "Modules\\Author\\": "src/" + } + }, + "autoload-dev": { + "psr-4": { + "Modules\\Author\\Tests\\": "tests/" + } + }, + "extra": { + "laravel": { + "providers": [ + "Modules\\Author\\Providers\\AuthorServiceProvider", + "Modules\\Author\\Providers\\RouteServiceProvider" + ], + "aliases": { + "AuthorFacade": "Modules\\Author\\Facades\\SomeFacade" + } + } + } +} diff --git a/tests/__snapshots__/files/ModuleTest__it_can_add_providers__2.json b/tests/__snapshots__/files/ModuleTest__it_can_add_providers__2.json new file mode 100644 index 0000000..4491948 --- /dev/null +++ b/tests/__snapshots__/files/ModuleTest__it_can_add_providers__2.json @@ -0,0 +1,33 @@ +{ + "name": "laraneat/author", + "description": "Author module for testing", + "authors": [ + { + "name": "Salavat Salakhutdinov", + "email": "salahutdinov.salavat@gmail.com" + } + ], + "autoload": { + "psr-4": { + "Modules\\Author\\": "src/" + } + }, + "autoload-dev": { + "psr-4": { + "Modules\\Author\\Tests\\": "tests/" + } + }, + "extra": { + "laravel": { + "providers": [ + "Modules\\Author\\Providers\\AuthorServiceProvider", + "Modules\\Author\\Providers\\RouteServiceProvider", + "Modules\\Author\\Providers\\FooServiceProvider", + "Modules\\Foo\\Providers\\BarServiceProvider" + ], + "aliases": { + "AuthorFacade": "Modules\\Author\\Facades\\SomeFacade" + } + } + } +} diff --git a/tests/__snapshots__/files/ModuleTest__it_can_add_providers_via_ModuleConfigWriter__1.json b/tests/__snapshots__/files/ModuleTest__it_can_add_providers_via_ModuleConfigWriter__1.json new file mode 100644 index 0000000..da14fd9 --- /dev/null +++ b/tests/__snapshots__/files/ModuleTest__it_can_add_providers_via_ModuleConfigWriter__1.json @@ -0,0 +1,31 @@ +{ + "name": "laraneat/author", + "description": "Author module for testing", + "authors": [ + { + "name": "Salavat Salakhutdinov", + "email": "salahutdinov.salavat@gmail.com" + } + ], + "autoload": { + "psr-4": { + "Modules\\Author\\": "src/" + } + }, + "autoload-dev": { + "psr-4": { + "Modules\\Author\\Tests\\": "tests/" + } + }, + "extra": { + "laravel": { + "providers": [ + "Modules\\Author\\Providers\\AuthorServiceProvider", + "Modules\\Author\\Providers\\RouteServiceProvider" + ], + "aliases": { + "AuthorFacade": "Modules\\Author\\Facades\\SomeFacade" + } + } + } +} diff --git a/tests/__snapshots__/files/ModuleTest__it_can_add_providers_via_ModuleConfigWriter__2.json b/tests/__snapshots__/files/ModuleTest__it_can_add_providers_via_ModuleConfigWriter__2.json new file mode 100644 index 0000000..af000ba --- /dev/null +++ b/tests/__snapshots__/files/ModuleTest__it_can_add_providers_via_ModuleConfigWriter__2.json @@ -0,0 +1,32 @@ +{ + "name": "laraneat/author", + "description": "Author module for testing", + "authors": [ + { + "name": "Salavat Salakhutdinov", + "email": "salahutdinov.salavat@gmail.com" + } + ], + "autoload": { + "psr-4": { + "Modules\\Author\\": "src/" + } + }, + "autoload-dev": { + "psr-4": { + "Modules\\Author\\Tests\\": "tests/" + } + }, + "extra": { + "laravel": { + "providers": [ + "Modules\\Author\\Providers\\AuthorServiceProvider", + "Modules\\Author\\Providers\\RouteServiceProvider", + "Modules\\Foo\\Providers\\BarServiceProvider" + ], + "aliases": { + "AuthorFacade": "Modules\\Author\\Facades\\SomeFacade" + } + } + } +} diff --git a/tests/__snapshots__/files/ModuleTest__it_can_set_aliases__1.json b/tests/__snapshots__/files/ModuleTest__it_can_set_aliases__1.json new file mode 100644 index 0000000..da14fd9 --- /dev/null +++ b/tests/__snapshots__/files/ModuleTest__it_can_set_aliases__1.json @@ -0,0 +1,31 @@ +{ + "name": "laraneat/author", + "description": "Author module for testing", + "authors": [ + { + "name": "Salavat Salakhutdinov", + "email": "salahutdinov.salavat@gmail.com" + } + ], + "autoload": { + "psr-4": { + "Modules\\Author\\": "src/" + } + }, + "autoload-dev": { + "psr-4": { + "Modules\\Author\\Tests\\": "tests/" + } + }, + "extra": { + "laravel": { + "providers": [ + "Modules\\Author\\Providers\\AuthorServiceProvider", + "Modules\\Author\\Providers\\RouteServiceProvider" + ], + "aliases": { + "AuthorFacade": "Modules\\Author\\Facades\\SomeFacade" + } + } + } +} diff --git a/tests/__snapshots__/files/ModuleTest__it_can_set_aliases__2.json b/tests/__snapshots__/files/ModuleTest__it_can_set_aliases__2.json new file mode 100644 index 0000000..8505b7d --- /dev/null +++ b/tests/__snapshots__/files/ModuleTest__it_can_set_aliases__2.json @@ -0,0 +1,32 @@ +{ + "name": "laraneat/author", + "description": "Author module for testing", + "authors": [ + { + "name": "Salavat Salakhutdinov", + "email": "salahutdinov.salavat@gmail.com" + } + ], + "autoload": { + "psr-4": { + "Modules\\Author\\": "src/" + } + }, + "autoload-dev": { + "psr-4": { + "Modules\\Author\\Tests\\": "tests/" + } + }, + "extra": { + "laravel": { + "providers": [ + "Modules\\Author\\Providers\\AuthorServiceProvider", + "Modules\\Author\\Providers\\RouteServiceProvider" + ], + "aliases": { + "foo": "Modules\\Author\\Services\\Foo", + "bar": "Modules\\Bar\\Services\\Bar" + } + } + } +} diff --git a/tests/__snapshots__/files/ModuleTest__it_can_set_providers__1.json b/tests/__snapshots__/files/ModuleTest__it_can_set_providers__1.json new file mode 100644 index 0000000..da14fd9 --- /dev/null +++ b/tests/__snapshots__/files/ModuleTest__it_can_set_providers__1.json @@ -0,0 +1,31 @@ +{ + "name": "laraneat/author", + "description": "Author module for testing", + "authors": [ + { + "name": "Salavat Salakhutdinov", + "email": "salahutdinov.salavat@gmail.com" + } + ], + "autoload": { + "psr-4": { + "Modules\\Author\\": "src/" + } + }, + "autoload-dev": { + "psr-4": { + "Modules\\Author\\Tests\\": "tests/" + } + }, + "extra": { + "laravel": { + "providers": [ + "Modules\\Author\\Providers\\AuthorServiceProvider", + "Modules\\Author\\Providers\\RouteServiceProvider" + ], + "aliases": { + "AuthorFacade": "Modules\\Author\\Facades\\SomeFacade" + } + } + } +} diff --git a/tests/__snapshots__/files/ModuleTest__it_can_set_providers__2.json b/tests/__snapshots__/files/ModuleTest__it_can_set_providers__2.json new file mode 100644 index 0000000..ab6c0e8 --- /dev/null +++ b/tests/__snapshots__/files/ModuleTest__it_can_set_providers__2.json @@ -0,0 +1,31 @@ +{ + "name": "laraneat/author", + "description": "Author module for testing", + "authors": [ + { + "name": "Salavat Salakhutdinov", + "email": "salahutdinov.salavat@gmail.com" + } + ], + "autoload": { + "psr-4": { + "Modules\\Author\\": "src/" + } + }, + "autoload-dev": { + "psr-4": { + "Modules\\Author\\Tests\\": "tests/" + } + }, + "extra": { + "laravel": { + "providers": [ + "Modules\\Author\\Providers\\FooServiceProvider", + "Modules\\Foo\\Providers\\BarServiceProvider" + ], + "aliases": { + "AuthorFacade": "Modules\\Author\\Facades\\SomeFacade" + } + } + } +} diff --git a/tests/__snapshots__/files/ModuleTest__it_can_update_aliases_via_ModuleConfigWriter__1.json b/tests/__snapshots__/files/ModuleTest__it_can_update_aliases_via_ModuleConfigWriter__1.json new file mode 100644 index 0000000..da14fd9 --- /dev/null +++ b/tests/__snapshots__/files/ModuleTest__it_can_update_aliases_via_ModuleConfigWriter__1.json @@ -0,0 +1,31 @@ +{ + "name": "laraneat/author", + "description": "Author module for testing", + "authors": [ + { + "name": "Salavat Salakhutdinov", + "email": "salahutdinov.salavat@gmail.com" + } + ], + "autoload": { + "psr-4": { + "Modules\\Author\\": "src/" + } + }, + "autoload-dev": { + "psr-4": { + "Modules\\Author\\Tests\\": "tests/" + } + }, + "extra": { + "laravel": { + "providers": [ + "Modules\\Author\\Providers\\AuthorServiceProvider", + "Modules\\Author\\Providers\\RouteServiceProvider" + ], + "aliases": { + "AuthorFacade": "Modules\\Author\\Facades\\SomeFacade" + } + } + } +} diff --git a/tests/__snapshots__/files/ModuleTest__it_can_update_aliases_via_ModuleConfigWriter__2.json b/tests/__snapshots__/files/ModuleTest__it_can_update_aliases_via_ModuleConfigWriter__2.json new file mode 100644 index 0000000..8505b7d --- /dev/null +++ b/tests/__snapshots__/files/ModuleTest__it_can_update_aliases_via_ModuleConfigWriter__2.json @@ -0,0 +1,32 @@ +{ + "name": "laraneat/author", + "description": "Author module for testing", + "authors": [ + { + "name": "Salavat Salakhutdinov", + "email": "salahutdinov.salavat@gmail.com" + } + ], + "autoload": { + "psr-4": { + "Modules\\Author\\": "src/" + } + }, + "autoload-dev": { + "psr-4": { + "Modules\\Author\\Tests\\": "tests/" + } + }, + "extra": { + "laravel": { + "providers": [ + "Modules\\Author\\Providers\\AuthorServiceProvider", + "Modules\\Author\\Providers\\RouteServiceProvider" + ], + "aliases": { + "foo": "Modules\\Author\\Services\\Foo", + "bar": "Modules\\Bar\\Services\\Bar" + } + } + } +} diff --git a/tests/__snapshots__/files/ModuleTest__it_can_update_providers_via_ModuleConfigWriter__1.json b/tests/__snapshots__/files/ModuleTest__it_can_update_providers_via_ModuleConfigWriter__1.json new file mode 100644 index 0000000..da14fd9 --- /dev/null +++ b/tests/__snapshots__/files/ModuleTest__it_can_update_providers_via_ModuleConfigWriter__1.json @@ -0,0 +1,31 @@ +{ + "name": "laraneat/author", + "description": "Author module for testing", + "authors": [ + { + "name": "Salavat Salakhutdinov", + "email": "salahutdinov.salavat@gmail.com" + } + ], + "autoload": { + "psr-4": { + "Modules\\Author\\": "src/" + } + }, + "autoload-dev": { + "psr-4": { + "Modules\\Author\\Tests\\": "tests/" + } + }, + "extra": { + "laravel": { + "providers": [ + "Modules\\Author\\Providers\\AuthorServiceProvider", + "Modules\\Author\\Providers\\RouteServiceProvider" + ], + "aliases": { + "AuthorFacade": "Modules\\Author\\Facades\\SomeFacade" + } + } + } +} diff --git a/tests/__snapshots__/files/ModuleTest__it_can_update_providers_via_ModuleConfigWriter__2.json b/tests/__snapshots__/files/ModuleTest__it_can_update_providers_via_ModuleConfigWriter__2.json new file mode 100644 index 0000000..ab6c0e8 --- /dev/null +++ b/tests/__snapshots__/files/ModuleTest__it_can_update_providers_via_ModuleConfigWriter__2.json @@ -0,0 +1,31 @@ +{ + "name": "laraneat/author", + "description": "Author module for testing", + "authors": [ + { + "name": "Salavat Salakhutdinov", + "email": "salahutdinov.salavat@gmail.com" + } + ], + "autoload": { + "psr-4": { + "Modules\\Author\\": "src/" + } + }, + "autoload-dev": { + "psr-4": { + "Modules\\Author\\Tests\\": "tests/" + } + }, + "extra": { + "laravel": { + "providers": [ + "Modules\\Author\\Providers\\FooServiceProvider", + "Modules\\Foo\\Providers\\BarServiceProvider" + ], + "aliases": { + "AuthorFacade": "Modules\\Author\\Facades\\SomeFacade" + } + } + } +} diff --git a/tests/__snapshots__/files/ModulesRepositoryTest__it_can_sync_modules_with_composer__1.json b/tests/__snapshots__/files/ModulesRepositoryTest__it_can_sync_modules_with_composer__1.json new file mode 100644 index 0000000..1977705 --- /dev/null +++ b/tests/__snapshots__/files/ModulesRepositoryTest__it_can_sync_modules_with_composer__1.json @@ -0,0 +1,45 @@ +{ + "name": "laraneat/laraneat", + "type": "project", + "description": "The Laraneat framework.", + "keywords": [ + "laraneat", + "laravel", + "apiato", + "php", + "framework" + ], + "license": "MIT", + "require": { + "php": "^8.1", + "laravel/framework": "^10.0" + }, + "require-dev": { + "phpunit/phpunit": "^10.5.9" + }, + "autoload": { + "classmap": [ + "database" + ], + "psr-4": { + "App\\": "app/", + "Modules\\": "modules/" + } + }, + "extra": { + "laravel": { + "dont-discover": [] + } + }, + "config": { + "optimize-autoloader": true, + "preferred-install": "dist", + "sort-packages": true, + "allow-plugins": { + "pestphp/pest-plugin": true, + "php-http/discovery": true + } + }, + "minimum-stability": "stable", + "prefer-stable": true +} diff --git a/tests/__snapshots__/files/ModulesRepositoryTest__it_can_sync_modules_with_composer__2.json b/tests/__snapshots__/files/ModulesRepositoryTest__it_can_sync_modules_with_composer__2.json new file mode 100644 index 0000000..57b4f73 --- /dev/null +++ b/tests/__snapshots__/files/ModulesRepositoryTest__it_can_sync_modules_with_composer__2.json @@ -0,0 +1,59 @@ +{ + "name": "laraneat/laraneat", + "type": "project", + "description": "The Laraneat framework.", + "keywords": [ + "laraneat", + "laravel", + "apiato", + "php", + "framework" + ], + "license": "MIT", + "require": { + "php": "^8.1", + "laraneat/article": "*", + "laraneat/article-category": "*", + "laraneat/author": "*", + "laraneat/empty": "*", + "laraneat/location": "*", + "laravel/framework": "^10.0" + }, + "require-dev": { + "phpunit/phpunit": "^10.5.9" + }, + "autoload": { + "classmap": [ + "database" + ], + "psr-4": { + "App\\": "app/", + "Modules\\": "modules/" + } + }, + "extra": { + "laravel": { + "dont-discover": [] + } + }, + "config": { + "optimize-autoloader": true, + "preferred-install": "dist", + "sort-packages": true, + "allow-plugins": { + "pestphp/pest-plugin": true, + "php-http/discovery": true + } + }, + "minimum-stability": "stable", + "prefer-stable": true, + "repositories": [ + { + "type": "path", + "url": "modules/*", + "options": { + "symlink": true + } + } + ] +} diff --git a/tests/__snapshots__/files/NotificationMakeCommandTest__it_generates__plain__notification_for_the_module__1.php b/tests/__snapshots__/files/NotificationMakeCommandTest__it_generates__plain__notification_for_the_module__1.php new file mode 100644 index 0000000..1b65cc0 --- /dev/null +++ b/tests/__snapshots__/files/NotificationMakeCommandTest__it_generates__plain__notification_for_the_module__1.php @@ -0,0 +1,36 @@ + + */ + public function via(object $notifiable): array + { + return ['mail', 'database']; + } + + /** + * Get the array representation of the notification. + */ + public function toArray(object $notifiable): array + { + return [ + // + ]; + } +} diff --git a/tests/Commands/Generators/__snapshots__/NotificationMakeCommandTest__it_generated_correct_queued_notification_file_with_content__1.txt b/tests/__snapshots__/files/NotificationMakeCommandTest__it_generates__queued__notification_for_the_module__1.php similarity index 56% rename from tests/Commands/Generators/__snapshots__/NotificationMakeCommandTest__it_generated_correct_queued_notification_file_with_content__1.txt rename to tests/__snapshots__/files/NotificationMakeCommandTest__it_generates__queued__notification_for_the_module__1.php index 9c1c072..14891e9 100644 --- a/tests/Commands/Generators/__snapshots__/NotificationMakeCommandTest__it_generated_correct_queued_notification_file_with_content__1.txt +++ b/tests/__snapshots__/files/NotificationMakeCommandTest__it_generates__queued__notification_for_the_module__1.php @@ -1,16 +1,19 @@ */ - public function via($notifiable): array + public function via(object $notifiable): array { return ['mail']; } @@ -27,18 +32,20 @@ public function via($notifiable): array /** * Get the mail representation of the notification. */ - public function toMail($notifiable): MailMessage + public function toMail(object $notifiable): MailMessage { return (new MailMessage) ->line('The introduction to the notification.') - ->action('Notification Action', 'https://laravel.com') + ->action('Notification Action', url('/')) ->line('Thank you for using our application!'); } /** * Get the array representation of the notification. + * + * @return array */ - public function toArray($notifiable): array + public function toArray(object $notifiable): array { return [ // diff --git a/tests/__snapshots__/files/ObserverMakeCommandTest__it_generates_observer_for_the_module__1.php b/tests/__snapshots__/files/ObserverMakeCommandTest__it_generates_observer_for_the_module__1.php new file mode 100644 index 0000000..5fc6ec5 --- /dev/null +++ b/tests/__snapshots__/files/ObserverMakeCommandTest__it_generates_observer_for_the_module__1.php @@ -0,0 +1,48 @@ +can('view-author'); + } + + /** + * Determine whether the user can view the model. + */ + public function view(User $user, Author $author): bool + { + return $user->can('view-author'); + } + + /** + * Determine whether the user can create models. + */ + public function create(User $user): bool + { + return $user->can('create-author'); + } + + /** + * Determine whether the user can update the model. + */ + public function update(User $user, Author $author): bool + { + return $user->can('update-author'); + } + + /** + * Determine whether the user can delete the model. + */ + public function delete(User $user, Author $author): bool + { + return $user->can('delete-author'); + } + + /** + * Determine whether the user can restore the model. + */ + public function restore(User $user, Author $author): bool + { + return $user->can('delete-author'); + } + + /** + * Determine whether the user can permanently delete the model. + */ + public function forceDelete(User $user, Author $author): bool + { + return $user->can('force-delete-author'); + } +} diff --git a/tests/__snapshots__/files/PolicyMakeCommandTest__it_generates_plain_policy_for_the_module__1.php b/tests/__snapshots__/files/PolicyMakeCommandTest__it_generates_plain_policy_for_the_module__1.php new file mode 100644 index 0000000..00de928 --- /dev/null +++ b/tests/__snapshots__/files/PolicyMakeCommandTest__it_generates_plain_policy_for_the_module__1.php @@ -0,0 +1,8 @@ +> + */ + protected $listen = []; + + /** + * Register any events for your application. + */ + public function boot(): void + { + // + } +} diff --git a/tests/__snapshots__/files/ProviderMakeCommandTest__it_generates__module__provider_for_the_module__1.php b/tests/__snapshots__/files/ProviderMakeCommandTest__it_generates__module__provider_for_the_module__1.php new file mode 100644 index 0000000..184b779 --- /dev/null +++ b/tests/__snapshots__/files/ProviderMakeCommandTest__it_generates__module__provider_for_the_module__1.php @@ -0,0 +1,102 @@ +loadConfigurations(); + } + + /** + * Bootstrap services. + */ + public function boot(): void + { + $this->loadMigrations(); + // $this->loadCommands(); + // $this->loadTranslations(); + // $this->loadViews(); + } + + /** + * Register configuration files. + */ + public function loadConfigurations(): void + { + $sourcePath = __DIR__.'/../../config/author.php'; + $configsPath = $this->app->configPath('author.php'); + + $this->mergeConfigFrom($sourcePath, 'author'); + + $this->publishes([ + $sourcePath => $configsPath + ], 'author-config'); + } + + /** + * Register migrations. + */ + public function loadMigrations(): void + { + $sourcePath = __DIR__.'/../../database/migrations'; + $migrationsPath = $this->app->databasePath('migrations'); + + $this->loadMigrationsFrom($sourcePath); + + $this->publishes([ + $sourcePath => $migrationsPath + ], 'author-migrations'); + } + + /** + * Register artisan commands. + */ + public function loadCommands(): void + { + if ($this->app->runningInConsole()) { + $this->loadCommandsFrom([ + 'Modules\\Author\\UI\\CLI\\Commands' => __DIR__.'/../UI/CLI/Commands', + ]); + } + } + + /** + * Register translations. + */ + public function loadTranslations(): void + { + $sourcePath = __DIR__.'/../../lang'; + $langPath = $this->app->langPath('modules/author'); + + $this->loadTranslationsFrom($sourcePath, 'author'); + + $this->publishes([ + $sourcePath => $langPath, + ], 'author-translations'); + } + + /** + * Register views. + */ + public function loadViews(): void + { + $sourcePath = __DIR__.'/../../resources/views'; + $viewsPath = $this->app->resourcePath('views/modules/author'); + + $this->loadViewsFrom( + array_merge($this->getPublishableViewPaths('author'), [$sourcePath]), + 'author' + ); + + $this->publishes([ + $sourcePath => $viewsPath + ], 'author-views'); + } +} diff --git a/tests/Commands/Generators/__snapshots__/ProviderMakeCommandTest__it_generated_correct_provider_file_with_content__1.txt b/tests/__snapshots__/files/ProviderMakeCommandTest__it_generates__plain__provider_for_the_module__1.php similarity index 53% rename from tests/Commands/Generators/__snapshots__/ProviderMakeCommandTest__it_generated_correct_provider_file_with_content__1.txt rename to tests/__snapshots__/files/ProviderMakeCommandTest__it_generates__plain__provider_for_the_module__1.php index cd61c64..f5a2650 100644 --- a/tests/Commands/Generators/__snapshots__/ProviderMakeCommandTest__it_generated_correct_provider_file_with_content__1.txt +++ b/tests/__snapshots__/files/ProviderMakeCommandTest__it_generates__plain__provider_for_the_module__1.php @@ -1,13 +1,13 @@ mapApiRoutes(); + $this->mapWebRoutes(); + } + + /** + * Define the "web" routes for the application. + * These routes all receive session state, CSRF protection, etc. + */ + protected function mapWebRoutes(): void + { + Route::middleware('web') + // ->namespace('Modules\\Author\\UI\\WEB\\Controllers') + ->group(function () { + $this->loadRoutesFromDirectory(__DIR__.'/../UI/WEB/routes'); + }); + } + + /** + * Define the "api" routes for the application. + * These routes are typically stateless. + */ + protected function mapApiRoutes(): void + { + Route::prefix('api') + ->middleware('api') + // ->namespace('Modules\\Author\\UI\\API\\Controllers') + ->group(function () { + $this->loadRoutesFromDirectory(__DIR__.'/../UI/API/routes'); + }); + } +} diff --git a/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_generates_module_components__14.txt b/tests/__snapshots__/files/QueryWizardMakeCommandTest__it_generates__eloquent__query_wizard_for_the_module__1.php similarity index 65% rename from tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_generates_module_components__14.txt rename to tests/__snapshots__/files/QueryWizardMakeCommandTest__it_generates__eloquent__query_wizard_for_the_module__1.php index b3b0b6d..63553d5 100644 --- a/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_generates_module_components__14.txt +++ b/tests/__snapshots__/files/QueryWizardMakeCommandTest__it_generates__eloquent__query_wizard_for_the_module__1.php @@ -1,16 +1,16 @@ + * @return array */ protected function allowedAppends(): array { @@ -18,7 +18,7 @@ protected function allowedAppends(): array } /** - * @return array + * @return array */ protected function defaultAppends(): array { @@ -26,7 +26,7 @@ protected function defaultAppends(): array } /** - * @return array + * @return array */ protected function allowedFields(): array { @@ -36,7 +36,7 @@ protected function allowedFields(): array } /** - * @return array + * @return array */ protected function allowedFilters(): array { @@ -44,7 +44,7 @@ protected function allowedFilters(): array } /** - * @return array + * @return array */ protected function allowedIncludes(): array { @@ -52,7 +52,7 @@ protected function allowedIncludes(): array } /** - * @return array + * @return array */ protected function defaultIncludes(): array { @@ -60,7 +60,7 @@ protected function defaultIncludes(): array } /** - * @return array + * @return array */ protected function allowedSorts(): array { @@ -68,7 +68,7 @@ protected function allowedSorts(): array } /** - * @return array + * @return array */ protected function defaultSorts(): array { diff --git a/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_generates_module_components__13.txt b/tests/__snapshots__/files/QueryWizardMakeCommandTest__it_generates__model__query_wizard_for_the_module__1.php similarity index 62% rename from tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_generates_module_components__13.txt rename to tests/__snapshots__/files/QueryWizardMakeCommandTest__it_generates__model__query_wizard_for_the_module__1.php index a2fccb5..3a876bd 100644 --- a/tests/Commands/Generators/__snapshots__/ComponentsMakeCommandTest__it_generates_module_components__13.txt +++ b/tests/__snapshots__/files/QueryWizardMakeCommandTest__it_generates__model__query_wizard_for_the_module__1.php @@ -1,14 +1,14 @@ + * @return array */ protected function allowedAppends(): array { @@ -16,7 +16,7 @@ protected function allowedAppends(): array } /** - * @return array + * @return array */ protected function defaultAppends(): array { @@ -24,7 +24,7 @@ protected function defaultAppends(): array } /** - * @return array + * @return array */ protected function allowedFields(): array { @@ -34,7 +34,7 @@ protected function allowedFields(): array } /** - * @return array + * @return array */ protected function allowedIncludes(): array { @@ -42,7 +42,7 @@ protected function allowedIncludes(): array } /** - * @return array + * @return array */ protected function defaultIncludes(): array { diff --git a/tests/__snapshots__/files/RequestMakeCommandTest__it_generates__create__api_request_for_the_module__1.php b/tests/__snapshots__/files/RequestMakeCommandTest__it_generates__create__api_request_for_the_module__1.php new file mode 100644 index 0000000..c17fe79 --- /dev/null +++ b/tests/__snapshots__/files/RequestMakeCommandTest__it_generates__create__api_request_for_the_module__1.php @@ -0,0 +1,28 @@ +validated()); + } +} diff --git a/tests/__snapshots__/files/RequestMakeCommandTest__it_generates__create__web_request_for_the_module__1.php b/tests/__snapshots__/files/RequestMakeCommandTest__it_generates__create__web_request_for_the_module__1.php new file mode 100644 index 0000000..80d4521 --- /dev/null +++ b/tests/__snapshots__/files/RequestMakeCommandTest__it_generates__create__web_request_for_the_module__1.php @@ -0,0 +1,28 @@ +validated()); + } +} diff --git a/tests/__snapshots__/files/RequestMakeCommandTest__it_generates__delete__api_request_for_the_module__1.php b/tests/__snapshots__/files/RequestMakeCommandTest__it_generates__delete__api_request_for_the_module__1.php new file mode 100644 index 0000000..d382341 --- /dev/null +++ b/tests/__snapshots__/files/RequestMakeCommandTest__it_generates__delete__api_request_for_the_module__1.php @@ -0,0 +1,20 @@ +route('author'); + return $author && Gate::check('delete', $author); + } +} diff --git a/tests/__snapshots__/files/RequestMakeCommandTest__it_generates__delete__web_request_for_the_module__1.php b/tests/__snapshots__/files/RequestMakeCommandTest__it_generates__delete__web_request_for_the_module__1.php new file mode 100644 index 0000000..057de9f --- /dev/null +++ b/tests/__snapshots__/files/RequestMakeCommandTest__it_generates__delete__web_request_for_the_module__1.php @@ -0,0 +1,20 @@ +route('author'); + return $author && Gate::check('delete', $author); + } +} diff --git a/tests/__snapshots__/files/RequestMakeCommandTest__it_generates__list__api_request_for_the_module__1.php b/tests/__snapshots__/files/RequestMakeCommandTest__it_generates__list__api_request_for_the_module__1.php new file mode 100644 index 0000000..db74862 --- /dev/null +++ b/tests/__snapshots__/files/RequestMakeCommandTest__it_generates__list__api_request_for_the_module__1.php @@ -0,0 +1,20 @@ +route('author'); + return $author && Gate::check('update', $author); + } + + public function toDTO(): UpdateAuthorDTO + { + return UpdateAuthorDTO::from($this->validated()); + } +} diff --git a/tests/__snapshots__/files/RequestMakeCommandTest__it_generates__update__web_request_for_the_module__1.php b/tests/__snapshots__/files/RequestMakeCommandTest__it_generates__update__web_request_for_the_module__1.php new file mode 100644 index 0000000..4e6f3b3 --- /dev/null +++ b/tests/__snapshots__/files/RequestMakeCommandTest__it_generates__update__web_request_for_the_module__1.php @@ -0,0 +1,28 @@ +route('author'); + return $author && Gate::check('update', $author); + } + + public function toDTO(): UpdateAuthorDTO + { + return UpdateAuthorDTO::from($this->validated()); + } +} diff --git a/tests/__snapshots__/files/RequestMakeCommandTest__it_generates__view__api_request_for_the_module__1.php b/tests/__snapshots__/files/RequestMakeCommandTest__it_generates__view__api_request_for_the_module__1.php new file mode 100644 index 0000000..3131436 --- /dev/null +++ b/tests/__snapshots__/files/RequestMakeCommandTest__it_generates__view__api_request_for_the_module__1.php @@ -0,0 +1,20 @@ +route('author'); + return $author && Gate::check('view', $author); + } +} diff --git a/tests/__snapshots__/files/ResourceMakeCommandTest__it_generates__collection__resource_for_the_module__1.php b/tests/__snapshots__/files/ResourceMakeCommandTest__it_generates__collection__resource_for_the_module__1.php new file mode 100644 index 0000000..66593f2 --- /dev/null +++ b/tests/__snapshots__/files/ResourceMakeCommandTest__it_generates__collection__resource_for_the_module__1.php @@ -0,0 +1,17 @@ +name('api.authors.delete'); diff --git a/tests/__snapshots__/files/RouteMakeCommandTest__it_generates__delete__web_route_for_the_module__1.php b/tests/__snapshots__/files/RouteMakeCommandTest__it_generates__delete__web_route_for_the_module__1.php new file mode 100644 index 0000000..951f87e --- /dev/null +++ b/tests/__snapshots__/files/RouteMakeCommandTest__it_generates__delete__web_route_for_the_module__1.php @@ -0,0 +1,7 @@ +name('web.authors.delete'); diff --git a/tests/__snapshots__/files/RouteMakeCommandTest__it_generates__get__api_route_for_the_module__1.php b/tests/__snapshots__/files/RouteMakeCommandTest__it_generates__get__api_route_for_the_module__1.php new file mode 100644 index 0000000..e38cc25 --- /dev/null +++ b/tests/__snapshots__/files/RouteMakeCommandTest__it_generates__get__api_route_for_the_module__1.php @@ -0,0 +1,7 @@ +name('web.authors.list'); diff --git a/tests/__snapshots__/files/RouteMakeCommandTest__it_generates__get__web_route_for_the_module__1.php b/tests/__snapshots__/files/RouteMakeCommandTest__it_generates__get__web_route_for_the_module__1.php new file mode 100644 index 0000000..e38cc25 --- /dev/null +++ b/tests/__snapshots__/files/RouteMakeCommandTest__it_generates__get__web_route_for_the_module__1.php @@ -0,0 +1,7 @@ +name('web.authors.list'); diff --git a/tests/__snapshots__/files/RouteMakeCommandTest__it_generates__options__api_route_for_the_module__1.php b/tests/__snapshots__/files/RouteMakeCommandTest__it_generates__options__api_route_for_the_module__1.php new file mode 100644 index 0000000..5369614 --- /dev/null +++ b/tests/__snapshots__/files/RouteMakeCommandTest__it_generates__options__api_route_for_the_module__1.php @@ -0,0 +1,7 @@ +name('api.authors.options'); diff --git a/tests/__snapshots__/files/RouteMakeCommandTest__it_generates__options__web_route_for_the_module__1.php b/tests/__snapshots__/files/RouteMakeCommandTest__it_generates__options__web_route_for_the_module__1.php new file mode 100644 index 0000000..5dd3e59 --- /dev/null +++ b/tests/__snapshots__/files/RouteMakeCommandTest__it_generates__options__web_route_for_the_module__1.php @@ -0,0 +1,7 @@ +name('web.authors.options'); diff --git a/tests/__snapshots__/files/RouteMakeCommandTest__it_generates__patch__api_route_for_the_module__1.php b/tests/__snapshots__/files/RouteMakeCommandTest__it_generates__patch__api_route_for_the_module__1.php new file mode 100644 index 0000000..640858f --- /dev/null +++ b/tests/__snapshots__/files/RouteMakeCommandTest__it_generates__patch__api_route_for_the_module__1.php @@ -0,0 +1,7 @@ +name('api.authors.update'); diff --git a/tests/__snapshots__/files/RouteMakeCommandTest__it_generates__patch__web_route_for_the_module__1.php b/tests/__snapshots__/files/RouteMakeCommandTest__it_generates__patch__web_route_for_the_module__1.php new file mode 100644 index 0000000..fda74ad --- /dev/null +++ b/tests/__snapshots__/files/RouteMakeCommandTest__it_generates__patch__web_route_for_the_module__1.php @@ -0,0 +1,7 @@ +name('web.authors.update'); diff --git a/tests/__snapshots__/files/RouteMakeCommandTest__it_generates__post__api_route_for_the_module__1.php b/tests/__snapshots__/files/RouteMakeCommandTest__it_generates__post__api_route_for_the_module__1.php new file mode 100644 index 0000000..57b2069 --- /dev/null +++ b/tests/__snapshots__/files/RouteMakeCommandTest__it_generates__post__api_route_for_the_module__1.php @@ -0,0 +1,7 @@ +name('api.authors.create'); diff --git a/tests/__snapshots__/files/RouteMakeCommandTest__it_generates__post__web_route_for_the_module__1.php b/tests/__snapshots__/files/RouteMakeCommandTest__it_generates__post__web_route_for_the_module__1.php new file mode 100644 index 0000000..e031af6 --- /dev/null +++ b/tests/__snapshots__/files/RouteMakeCommandTest__it_generates__post__web_route_for_the_module__1.php @@ -0,0 +1,7 @@ +name('web.authors.create'); diff --git a/tests/__snapshots__/files/RouteMakeCommandTest__it_generates__put__api_route_for_the_module__1.php b/tests/__snapshots__/files/RouteMakeCommandTest__it_generates__put__api_route_for_the_module__1.php new file mode 100644 index 0000000..dcd1387 --- /dev/null +++ b/tests/__snapshots__/files/RouteMakeCommandTest__it_generates__put__api_route_for_the_module__1.php @@ -0,0 +1,7 @@ +name('api.authors.update'); diff --git a/tests/__snapshots__/files/RouteMakeCommandTest__it_generates__put__web_route_for_the_module__1.php b/tests/__snapshots__/files/RouteMakeCommandTest__it_generates__put__web_route_for_the_module__1.php new file mode 100644 index 0000000..34f88ba --- /dev/null +++ b/tests/__snapshots__/files/RouteMakeCommandTest__it_generates__put__web_route_for_the_module__1.php @@ -0,0 +1,7 @@ +name('web.authors.update'); diff --git a/tests/__snapshots__/files/RuleMakeCommandTest__it_generates_rule_for_the_module__1.php b/tests/__snapshots__/files/RuleMakeCommandTest__it_generates_rule_for_the_module__1.php new file mode 100644 index 0000000..069e26b --- /dev/null +++ b/tests/__snapshots__/files/RuleMakeCommandTest__it_generates_rule_for_the_module__1.php @@ -0,0 +1,19 @@ +handle(new CreatePermissionDTO( + name: 'view-author', + display_name: 'View any "authors"', + group: 'authors' + )); + + $createPermissionAction->handle(new CreatePermissionDTO( + name: 'create-author', + display_name: 'Create "authors"', + group: 'authors' + )); + + $createPermissionAction->handle(new CreatePermissionDTO( + name: 'update-author', + display_name: 'Update any "authors"', + group: 'authors' + )); + + $createPermissionAction->handle(new CreatePermissionDTO( + name: 'delete-author', + display_name: 'Delete any "authors"', + group: 'authors' + )); + + $createPermissionAction->handle(new CreatePermissionDTO( + name: 'force-delete-author', + display_name: 'Force delete any "authors"', + group: 'authors' + )); + } +} diff --git a/tests/__snapshots__/files/SeederMakeCommandTest__it_generates__plain__seeder_for_the_module__1.php b/tests/__snapshots__/files/SeederMakeCommandTest__it_generates__plain__seeder_for_the_module__1.php new file mode 100644 index 0000000..835b293 --- /dev/null +++ b/tests/__snapshots__/files/SeederMakeCommandTest__it_generates__plain__seeder_for_the_module__1.php @@ -0,0 +1,13 @@ + 'create-author', + 'roles' => '', + ]; + + public function test_create_author(): void + { + $this->actingAsTestUser(); + + $data = $this->getTestData(); + + $this->postJson(route('api.authors.create'), $data) + ->assertCreated() + ->assertJson(fn (AssertableJson $json) => + $json->has('data', fn (AssertableJson $json) => + $json->has('id') + ->whereAll($data) + ->etc() + ) + ); + + $this->assertDatabaseHas(Author::class, $data); + } + + public function test_create_author_with_invalid_data(): void + { + $this->actingAsTestUser(); + + $this->postJson(route('api.authors.create'), []) + ->assertUnprocessable() + ->assertJsonValidationErrors([ + // TODO: add expected validation errors here + ]); + } + + public function test_create_author_unauthenticated(): void + { + $data = $this->getTestData(); + + $this->postJson(route('api.authors.create'), $data) + ->assertUnauthorized(); + } + + public function test_create_author_without_access(): void + { + $this->actingAsTestUserWithoutAccess(); + + $data = $this->getTestData(); + + $this->postJson(route('api.authors.create'), $data) + ->assertForbidden(); + } + + protected function getTestData(array $mergeData = []): array + { + return array_merge([ + // TODO: add fields here + ], $mergeData); + } +} diff --git a/tests/__snapshots__/files/TestMakeCommandTest__it_generates_create__web__test_for_the_module__1.php b/tests/__snapshots__/files/TestMakeCommandTest__it_generates_create__web__test_for_the_module__1.php new file mode 100644 index 0000000..9fdcca6 --- /dev/null +++ b/tests/__snapshots__/files/TestMakeCommandTest__it_generates_create__web__test_for_the_module__1.php @@ -0,0 +1,50 @@ + 'create-author', + 'roles' => '', + ]; + + protected function getTestData(array $mergeData = []): array + { + return array_merge([ + // TODO: add fields here + ], $mergeData); + } + + public function test_create_author(): void + { + $this->actingAsTestUser(); + + $data = $this->getTestData(); + + $this->post(route('web.authors.create'), $data) + ->assertCreated(); + + $this->assertDatabaseHas(Author::class, $data); + } + + public function test_create_author_without_access(): void + { + $this->actingAsTestUserWithoutAccess(); + + $data = $this->getTestData(); + + $this->post(route('web.authors.create'), $data) + ->assertForbidden(); + } +} diff --git a/tests/__snapshots__/files/TestMakeCommandTest__it_generates_delete__api__test_for_the_module__1.php b/tests/__snapshots__/files/TestMakeCommandTest__it_generates_delete__api__test_for_the_module__1.php new file mode 100644 index 0000000..eb267f3 --- /dev/null +++ b/tests/__snapshots__/files/TestMakeCommandTest__it_generates_delete__api__test_for_the_module__1.php @@ -0,0 +1,62 @@ + 'delete-author', + 'roles' => '', + ]; + + public function test_delete_author(): void + { + $this->actingAsTestUser(); + + $author = Author::factory()->create(); + + $this->deleteJson(route('api.authors.delete', ['author' => $author->getKey()])) + ->assertNoContent(); + + $this->assertNull(Author::find($author->getKey())); + } + + public function test_delete_author_unauthenticated(): void + { + $author = Author::factory()->create(); + + $this->deleteJson(route('api.authors.delete', ['author' => $author->getKey()])) + ->assertUnauthorized(); + } + + public function test_delete_author_without_access(): void + { + $this->actingAsTestUserWithoutAccess(); + + $author = Author::factory()->create(); + + $this->deleteJson(route('api.authors.delete', ['author' => $author->getKey()])) + ->assertForbidden(); + } + + public function test_delete_not_existing_author(): void + { + $this->actingAsTestUser(); + + $this->deleteJson(route('api.authors.delete', ['author' => 7777])) + ->assertNotFound(); + } +} diff --git a/tests/__snapshots__/files/TestMakeCommandTest__it_generates_delete__web__test_for_the_module__1.php b/tests/__snapshots__/files/TestMakeCommandTest__it_generates_delete__web__test_for_the_module__1.php new file mode 100644 index 0000000..e5783a9 --- /dev/null +++ b/tests/__snapshots__/files/TestMakeCommandTest__it_generates_delete__web__test_for_the_module__1.php @@ -0,0 +1,51 @@ + 'delete-author', + 'roles' => '', + ]; + + public function test_delete_author(): void + { + $this->actingAsTestUser(); + + $author = Author::factory()->create(); + + $this->delete(route('web.authors.delete', ['author' => $author->getKey()])) + ->assertNoContent(); + + $this->assertNull(Author::find($author->getKey())); + } + + public function test_delete_author_without_access(): void + { + $this->actingAsTestUserWithoutAccess(); + + $author = Author::factory()->create(); + + $this->delete(route('web.authors.delete', ['author' => $author->getKey()])) + ->assertForbidden(); + } + + public function test_delete_not_existing_author(): void + { + $this->actingAsTestUser(); + + $this->delete(route('web.authors.delete', ['author' => 7777])) + ->assertNotFound(); + } +} diff --git a/tests/__snapshots__/files/TestMakeCommandTest__it_generates_list__api__test_for_the_module__1.php b/tests/__snapshots__/files/TestMakeCommandTest__it_generates_list__api__test_for_the_module__1.php new file mode 100644 index 0000000..9ac136a --- /dev/null +++ b/tests/__snapshots__/files/TestMakeCommandTest__it_generates_list__api__test_for_the_module__1.php @@ -0,0 +1,58 @@ + 'view-author', + 'roles' => '', + ]; + + public function test_list_authors(): void + { + $this->actingAsTestUser(); + + Author::factory()->count(3)->create(); + + $this->getJson(route('api.authors.list')) + ->assertOk() + ->assertJsonStructure([ + 'links', + 'meta', + 'data' + ]) + ->assertJsonCount(3, 'data'); + } + + public function test_list_authors_unauthenticated(): void + { + Author::factory()->count(3)->create(); + + $this->getJson(route('api.authors.list')) + ->assertUnauthorized(); + } + + public function test_list_authors_without_access(): void + { + $this->actingAsTestUserWithoutAccess(); + + Author::factory()->count(3)->create(); + + $this->getJson(route('api.authors.list')) + ->assertForbidden(); + } +} diff --git a/tests/__snapshots__/files/TestMakeCommandTest__it_generates_plain__api__test_for_the_module__1.php b/tests/__snapshots__/files/TestMakeCommandTest__it_generates_plain__api__test_for_the_module__1.php new file mode 100644 index 0000000..2eddedd --- /dev/null +++ b/tests/__snapshots__/files/TestMakeCommandTest__it_generates_plain__api__test_for_the_module__1.php @@ -0,0 +1,28 @@ + '', + 'roles' => '', + ]; + + public function test(): void + { + // + } +} diff --git a/tests/__snapshots__/files/TestMakeCommandTest__it_generates_plain__cli__test_for_the_module__1.php b/tests/__snapshots__/files/TestMakeCommandTest__it_generates_plain__cli__test_for_the_module__1.php new file mode 100644 index 0000000..0b8a895 --- /dev/null +++ b/tests/__snapshots__/files/TestMakeCommandTest__it_generates_plain__cli__test_for_the_module__1.php @@ -0,0 +1,17 @@ + 'update-author', + 'roles' => '', + ]; + + public function test_update_author(): void + { + $this->actingAsTestUser(); + + $author = Author::factory()->create(); + + $data = $this->getTestData(); + $expectedData = array_merge($data, [ + 'id' => $author->getKey(), + ]); + + $this->patchJson(route('api.authors.update', ['author' => $author->getKey()]), $data) + ->assertOk() + ->assertJson(fn (AssertableJson $json) => + $json->has('data', fn (AssertableJson $json) => + $json->whereAll($expectedData) + ->etc() + ) + ); + + $this->assertDatabaseHas(Author::class, $expectedData); + } + + public function test_update_author_with_invalid_data(): void + { + $this->actingAsTestUser(); + + $author = Author::factory()->create(); + + $this->patchJson(route('api.authors.update', ['author' => $author->getKey()]), []) + ->assertUnprocessable() + ->assertJsonValidationErrors([ + // TODO: add expected validation errors here + ]); + } + + public function test_update_author_unauthenticated(): void + { + $author = Author::factory()->create(); + + $data = $this->getTestData(); + + $this->patchJson(route('api.authors.update', ['author' => $author->getKey()]), $data) + ->assertUnauthorized(); + } + + public function test_update_author_without_access(): void + { + $this->actingAsTestUserWithoutAccess(); + + $author = Author::factory()->create(); + + $data = $this->getTestData(); + + $this->patchJson(route('api.authors.update', ['author' => $author->getKey()]), $data) + ->assertForbidden(); + } + + public function test_update_non_existing_author(): void + { + $this->actingAsTestUser(); + + $data = $this->getTestData(); + + $this->patchJson(route('api.authors.update', ['author' => 7777]), $data) + ->assertNotFound(); + } + + protected function getTestData(array $mergeData = []): array + { + return array_merge([ + // TODO: add fields here + ], $mergeData); + } +} diff --git a/tests/__snapshots__/files/TestMakeCommandTest__it_generates_update__web__test_for_the_module__1.php b/tests/__snapshots__/files/TestMakeCommandTest__it_generates_update__web__test_for_the_module__1.php new file mode 100644 index 0000000..8d6b05d --- /dev/null +++ b/tests/__snapshots__/files/TestMakeCommandTest__it_generates_update__web__test_for_the_module__1.php @@ -0,0 +1,67 @@ + 'update-author', + 'roles' => '', + ]; + + protected function getTestData(array $mergeData = []): array + { + return array_merge([ + // TODO: add fields here + ], $mergeData); + } + + public function test_update_author(): void + { + $this->actingAsTestUser(); + + $author = Author::factory()->create(); + + $data = $this->getTestData(); + $expectedData = array_merge($data, [ + 'id' => $author->getKey(), + ]); + + $this->patch(route('web.authors.update', ['author' => $author->getKey()]), $data) + ->assertOk(); + + $this->assertDatabaseHas(Author::class, $expectedData); + } + + public function test_update_author_without_access(): void + { + $this->actingAsTestUserWithoutAccess(); + + $author = Author::factory()->create(); + + $data = $this->getTestData(); + + $this->patch(route('web.authors.update', ['author' => $author->getKey()]), $data) + ->assertForbidden(); + } + + public function test_update_non_existing_author(): void + { + $this->actingAsTestUser(); + + $data = $this->getTestData(); + + $this->patch(route('web.authors.update', ['author' => 7777]), $data) + ->assertNotFound(); + } +} diff --git a/tests/__snapshots__/files/TestMakeCommandTest__it_generates_view__api__test_for_the_module__1.php b/tests/__snapshots__/files/TestMakeCommandTest__it_generates_view__api__test_for_the_module__1.php new file mode 100644 index 0000000..7914dcc --- /dev/null +++ b/tests/__snapshots__/files/TestMakeCommandTest__it_generates_view__api__test_for_the_module__1.php @@ -0,0 +1,68 @@ + 'view-author', + 'roles' => '', + ]; + + public function test_view_author(): void + { + $this->actingAsTestUser(); + + $author = Author::factory()->create(); + $expectedData = $author->toArray(); + + $this->getJson(route('api.authors.view', ['author' => $author->getKey()])) + ->assertOk() + ->assertJson(fn (AssertableJson $json) => + $json->has('data', fn (AssertableJson $json) => + $json->whereAll($expectedData) + ->etc() + ) + ); + } + + public function test_view_author_unauthenticated(): void + { + $author = Author::factory()->create(); + + $this->getJson(route('api.authors.view', ['author' => $author->getKey()])) + ->assertUnauthorized(); + } + + public function test_view_author_without_access(): void + { + $this->actingAsTestUserWithoutAccess(); + + $author = Author::factory()->create(); + + $this->getJson(route('api.authors.view', ['author' => $author->getKey()])) + ->assertForbidden(); + } + + public function test_view_not_existing_author(): void + { + $this->actingAsTestUser(); + + $this->getJson(route('api.authors.view', ['author' => 7777])) + ->assertNotFound(); + } +} diff --git a/tests/fixtures/laravel/.gitignore b/tests/fixtures/laravel/.gitignore index cba67f7..8d29ab4 100644 --- a/tests/fixtures/laravel/.gitignore +++ b/tests/fixtures/laravel/.gitignore @@ -1,2 +1,2 @@ -app/Modules -modules_statuses.json +modules +vendor diff --git a/tests/fixtures/laravel/app/Console/.gitkeep b/tests/fixtures/laravel/app/Console/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/tests/fixtures/laravel/app/Exceptions/.gitkeep b/tests/fixtures/laravel/app/Exceptions/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/tests/fixtures/laravel/app/Http/Controllers/.gitkeep b/tests/fixtures/laravel/app/Http/Controllers/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/tests/fixtures/laravel/app/Http/Middleware/.gitkeep b/tests/fixtures/laravel/app/Http/Middleware/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/tests/fixtures/laravel/app/Models/.gitkeep b/tests/fixtures/laravel/app/Models/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/tests/fixtures/laravel/app/Providers/.gitkeep b/tests/fixtures/laravel/app/Providers/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/tests/fixtures/laravel/bootstrap/app.php b/tests/fixtures/laravel/bootstrap/app.php index f7f1d2c..151dd2e 100644 --- a/tests/fixtures/laravel/bootstrap/app.php +++ b/tests/fixtures/laravel/bootstrap/app.php @@ -3,7 +3,7 @@ use Orchestra\Testbench\Console\Commander; $APP_KEY = $_SERVER['APP_KEY'] ?? $_ENV['APP_KEY'] ?? 'AckfSECXIvnK5r28GVIWUAxmbBSjTsmF'; -$DB_CONNECTION = $_SERVER['DB_CONNECTION'] ?? $_ENV['DB_CONNECTION'] ?? 'testing'; +$DB_CONNECTION = $_SERVER['DB_CONNECTION'] ?? $_ENV['DB_CONNECTION'] ?? 'testing'; $config = ['env' => ['APP_KEY="'.$APP_KEY.'"', 'DB_CONNECTION="'.$DB_CONNECTION.'"'], 'providers' => []]; diff --git a/tests/fixtures/laravel/composer.json b/tests/fixtures/laravel/composer.json index 03d8029..1977705 100644 --- a/tests/fixtures/laravel/composer.json +++ b/tests/fixtures/laravel/composer.json @@ -11,37 +11,35 @@ ], "license": "MIT", "require": { - "php": "^8.0", - "laravel/framework": "^8.55", - "wikimedia/composer-merge-plugin": "^2.0.1" + "php": "^8.1", + "laravel/framework": "^10.0" }, "require-dev": { - "phpunit/phpunit": "^9.5.2" + "phpunit/phpunit": "^10.5.9" }, "autoload": { "classmap": [ - "database", - "tests/TestCase.php" + "database" ], "psr-4": { - "App\\": "app/" + "App\\": "app/", + "Modules\\": "modules/" } }, "extra": { "laravel": { "dont-discover": [] - }, - "merge-plugin": { - "include": [ - "app/Modules/*/composer.json" - ], - "recurse": true, - "replace": false, - "merge-dev": true, - "merge-extra": false, - "merge-extra-deep": false, - "merge-scripts": true } }, - "minimum-stability": "dev" + "config": { + "optimize-autoloader": true, + "preferred-install": "dist", + "sort-packages": true, + "allow-plugins": { + "pestphp/pest-plugin": true, + "php-http/discovery": true + } + }, + "minimum-stability": "stable", + "prefer-stable": true } diff --git a/tests/fixtures/stubs/InvalidJsonModule/module.json b/tests/fixtures/stubs/InvalidJsonModule/module.json deleted file mode 100644 index 916aa9b..0000000 --- a/tests/fixtures/stubs/InvalidJsonModule/module.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - a: 1 -} diff --git a/tests/fixtures/stubs/modules/invalid/without-name/composer.json b/tests/fixtures/stubs/modules/invalid/without-name/composer.json new file mode 100644 index 0000000..0906c69 --- /dev/null +++ b/tests/fixtures/stubs/modules/invalid/without-name/composer.json @@ -0,0 +1,20 @@ +{ + "name": "", + "description": "Invalid module without name", + "authors": [ + { + "name": "Salavat Salakhutdinov", + "email": "salahutdinov.salavat@gmail.com" + } + ], + "autoload": { + "psr-4": { + "Modules\\WithoutName\\": "src/" + } + }, + "autoload-dev": { + "psr-4": { + "Modules\\WithoutName\\Tests\\": "tests/" + } + } +} diff --git a/tests/fixtures/stubs/modules/invalid/without-namespace/composer.json b/tests/fixtures/stubs/modules/invalid/without-namespace/composer.json new file mode 100644 index 0000000..9b65e62 --- /dev/null +++ b/tests/fixtures/stubs/modules/invalid/without-namespace/composer.json @@ -0,0 +1,10 @@ +{ + "name": "laraneat/without-namespace", + "description": "Invalid module without namespace", + "authors": [ + { + "name": "Salavat Salakhutdinov", + "email": "salahutdinov.salavat@gmail.com" + } + ] +} diff --git a/tests/fixtures/stubs/modules/valid/article-category/composer.json b/tests/fixtures/stubs/modules/valid/article-category/composer.json new file mode 100644 index 0000000..792a48f --- /dev/null +++ b/tests/fixtures/stubs/modules/valid/article-category/composer.json @@ -0,0 +1,29 @@ +{ + "name": "laraneat/article-category", + "description": "ArticleCategory module for testing", + "authors": [ + { + "name": "Salavat Salakhutdinov", + "email": "salahutdinov.salavat@gmail.com" + } + ], + "autoload": { + "psr-4": { + "Modules\\ArticleCategory\\": "src/" + } + }, + "autoload-dev": { + "psr-4": { + "Modules\\ArticleCategory\\Tests\\": "tests/" + } + }, + "extra": { + "laravel": { + "providers": [ + "Modules\\ArticleCategory\\Providers\\ArticleCategoryServiceProvider", + "Modules\\ArticleCategory\\Providers\\RouteServiceProvider" + ], + "aliases": [] + } + } +} diff --git a/tests/fixtures/stubs/modules/valid/article-category/src/Providers/ArticleCategoryServiceProvider.php b/tests/fixtures/stubs/modules/valid/article-category/src/Providers/ArticleCategoryServiceProvider.php new file mode 100644 index 0000000..a845107 --- /dev/null +++ b/tests/fixtures/stubs/modules/valid/article-category/src/Providers/ArticleCategoryServiceProvider.php @@ -0,0 +1,89 @@ +loadMigrations(); + // $this->loadTranslations(); + // $this->loadCommands(); + // $this->loadViews(); + } + + /** + * Register translations. + */ + public function loadTranslations(): void + { + $sourcePath = realpath('../../lang'); + $langPath = $this->app->langPath('modules/' . $this->modulePackageName); + + $this->loadTranslationsFrom($sourcePath, $this->modulePackageName); + + $this->publishes([ + $sourcePath => $langPath, + ], 'article-category-translations'); + } + + /** + * Register views. + */ + public function loadViews(): void + { + $sourcePath = realpath('../../resources/views'); + $viewsPath = $this->app->resourcePath('views/modules/' . $this->modulePackageName); + + $this->loadViewsFrom( + array_merge($this->getPublishableViewPaths($this->modulePackageName), [$sourcePath]), + $this->modulePackageName + ); + + $this->publishes([ + $sourcePath => $viewsPath, + ], 'article-category-views'); + } + + /** + * Register migrations. + */ + public function loadMigrations(): void + { + $sourcePath = realpath('../../database/migrations'); + $migrationsPath = $this->app->databasePath('migrations'); + + $this->loadMigrationsFrom($sourcePath); + + $this->publishes([ + $sourcePath => $migrationsPath, + ], 'article-category-migrations'); + } + + /** + * Register artisan commands. + */ + public function loadCommands(): void + { + if ($this->app->runningInConsole()) { + $this->loadCommandsFrom([ + 'Modules\\ArticleCategory\\UI\\CLI\\Commands' => __DIR__.'/../UI/CLI/Commands', + ]); + } + } +} diff --git a/tests/fixtures/stubs/modules/valid/article-category/src/Providers/RouteServiceProvider.php b/tests/fixtures/stubs/modules/valid/article-category/src/Providers/RouteServiceProvider.php new file mode 100644 index 0000000..8cfa02b --- /dev/null +++ b/tests/fixtures/stubs/modules/valid/article-category/src/Providers/RouteServiceProvider.php @@ -0,0 +1,57 @@ +mapApiRoutes(); + $this->mapWebRoutes(); + } + + /** + * Define the "web" routes for the application. + * These routes all receive session state, CSRF protection, etc. + */ + protected function mapWebRoutes(): void + { + Route::middleware('web') + // ->namespace('Modules\\ArticleCategory\\UI\\WEB\\Controllers') + ->group(function () { + $this->loadRoutesFromDirectory(realpath('../UI/WEB/routes')); + }); + } + + /** + * Define the "api" routes for the application. + * These routes are typically stateless. + */ + protected function mapApiRoutes(): void + { + Route::prefix('api') + ->middleware('api') + // ->namespace('Modules\\ArticleCategory\\UI\\API\\Controllers') + ->group(function () { + $this->loadRoutesFromDirectory(realpath('../UI/API/routes')); + }); + } +} diff --git a/tests/fixtures/stubs/modules/valid/article-copy/composer.json b/tests/fixtures/stubs/modules/valid/article-copy/composer.json new file mode 100644 index 0000000..02d9b7b --- /dev/null +++ b/tests/fixtures/stubs/modules/valid/article-copy/composer.json @@ -0,0 +1,29 @@ +{ + "name": "laraneat/article", + "description": "Article module for testing", + "authors": [ + { + "name": "Salavat Salakhutdinov", + "email": "salahutdinov.salavat@gmail.com" + } + ], + "autoload": { + "psr-4": { + "Modules\\Article\\": "src/" + } + }, + "autoload-dev": { + "psr-4": { + "Modules\\Article\\Tests\\": "tests/" + } + }, + "extra": { + "laravel": { + "providers": [ + "Modules\\Article\\Providers\\ArticleServiceProvider", + "Modules\\Article\\Providers\\RouteServiceProvider" + ], + "aliases": [] + } + } +} diff --git a/tests/fixtures/stubs/modules/valid/article-copy/src/Providers/ArticleServiceProvider.php b/tests/fixtures/stubs/modules/valid/article-copy/src/Providers/ArticleServiceProvider.php new file mode 100644 index 0000000..48ccbe3 --- /dev/null +++ b/tests/fixtures/stubs/modules/valid/article-copy/src/Providers/ArticleServiceProvider.php @@ -0,0 +1,89 @@ +loadMigrations(); + // $this->loadTranslations(); + // $this->loadCommands(); + // $this->loadViews(); + } + + /** + * Register translations. + */ + public function loadTranslations(): void + { + $sourcePath = realpath('../../lang'); + $langPath = $this->app->langPath('modules/' . $this->modulePackageName); + + $this->loadTranslationsFrom($sourcePath, $this->modulePackageName); + + $this->publishes([ + $sourcePath => $langPath, + ], 'article-translations'); + } + + /** + * Register views. + */ + public function loadViews(): void + { + $sourcePath = realpath('../../resources/views'); + $viewsPath = $this->app->resourcePath('views/modules/' . $this->modulePackageName); + + $this->loadViewsFrom( + array_merge($this->getPublishableViewPaths($this->modulePackageName), [$sourcePath]), + $this->modulePackageName + ); + + $this->publishes([ + $sourcePath => $viewsPath, + ], 'article-views'); + } + + /** + * Register migrations. + */ + public function loadMigrations(): void + { + $sourcePath = realpath('../../database/migrations'); + $migrationsPath = $this->app->databasePath('migrations'); + + $this->loadMigrationsFrom($sourcePath); + + $this->publishes([ + $sourcePath => $migrationsPath, + ], 'article-migrations'); + } + + /** + * Register artisan commands. + */ + public function loadCommands(): void + { + if ($this->app->runningInConsole()) { + $this->loadCommandsFrom([ + 'Modules\\Article\\UI\\CLI\\Commands' => __DIR__.'/../UI/CLI/Commands', + ]); + } + } +} diff --git a/tests/fixtures/stubs/modules/valid/article-copy/src/Providers/RouteServiceProvider.php b/tests/fixtures/stubs/modules/valid/article-copy/src/Providers/RouteServiceProvider.php new file mode 100644 index 0000000..e20139c --- /dev/null +++ b/tests/fixtures/stubs/modules/valid/article-copy/src/Providers/RouteServiceProvider.php @@ -0,0 +1,57 @@ +mapApiRoutes(); + $this->mapWebRoutes(); + } + + /** + * Define the "web" routes for the application. + * These routes all receive session state, CSRF protection, etc. + */ + protected function mapWebRoutes(): void + { + Route::middleware('web') + // ->namespace('Modules\\Article\\UI\\WEB\\Controllers') + ->group(function () { + $this->loadRoutesFromDirectory(realpath('../UI/WEB/routes')); + }); + } + + /** + * Define the "api" routes for the application. + * These routes are typically stateless. + */ + protected function mapApiRoutes(): void + { + Route::prefix('api') + ->middleware('api') + // ->namespace('Modules\\Article\\UI\\API\\Controllers') + ->group(function () { + $this->loadRoutesFromDirectory(realpath('../UI/API/routes')); + }); + } +} diff --git a/tests/fixtures/stubs/modules/valid/article/composer.json b/tests/fixtures/stubs/modules/valid/article/composer.json new file mode 100644 index 0000000..02d9b7b --- /dev/null +++ b/tests/fixtures/stubs/modules/valid/article/composer.json @@ -0,0 +1,29 @@ +{ + "name": "laraneat/article", + "description": "Article module for testing", + "authors": [ + { + "name": "Salavat Salakhutdinov", + "email": "salahutdinov.salavat@gmail.com" + } + ], + "autoload": { + "psr-4": { + "Modules\\Article\\": "src/" + } + }, + "autoload-dev": { + "psr-4": { + "Modules\\Article\\Tests\\": "tests/" + } + }, + "extra": { + "laravel": { + "providers": [ + "Modules\\Article\\Providers\\ArticleServiceProvider", + "Modules\\Article\\Providers\\RouteServiceProvider" + ], + "aliases": [] + } + } +} diff --git a/tests/fixtures/stubs/modules/valid/article/src/Providers/ArticleServiceProvider.php b/tests/fixtures/stubs/modules/valid/article/src/Providers/ArticleServiceProvider.php new file mode 100644 index 0000000..48ccbe3 --- /dev/null +++ b/tests/fixtures/stubs/modules/valid/article/src/Providers/ArticleServiceProvider.php @@ -0,0 +1,89 @@ +loadMigrations(); + // $this->loadTranslations(); + // $this->loadCommands(); + // $this->loadViews(); + } + + /** + * Register translations. + */ + public function loadTranslations(): void + { + $sourcePath = realpath('../../lang'); + $langPath = $this->app->langPath('modules/' . $this->modulePackageName); + + $this->loadTranslationsFrom($sourcePath, $this->modulePackageName); + + $this->publishes([ + $sourcePath => $langPath, + ], 'article-translations'); + } + + /** + * Register views. + */ + public function loadViews(): void + { + $sourcePath = realpath('../../resources/views'); + $viewsPath = $this->app->resourcePath('views/modules/' . $this->modulePackageName); + + $this->loadViewsFrom( + array_merge($this->getPublishableViewPaths($this->modulePackageName), [$sourcePath]), + $this->modulePackageName + ); + + $this->publishes([ + $sourcePath => $viewsPath, + ], 'article-views'); + } + + /** + * Register migrations. + */ + public function loadMigrations(): void + { + $sourcePath = realpath('../../database/migrations'); + $migrationsPath = $this->app->databasePath('migrations'); + + $this->loadMigrationsFrom($sourcePath); + + $this->publishes([ + $sourcePath => $migrationsPath, + ], 'article-migrations'); + } + + /** + * Register artisan commands. + */ + public function loadCommands(): void + { + if ($this->app->runningInConsole()) { + $this->loadCommandsFrom([ + 'Modules\\Article\\UI\\CLI\\Commands' => __DIR__.'/../UI/CLI/Commands', + ]); + } + } +} diff --git a/tests/fixtures/stubs/modules/valid/article/src/Providers/RouteServiceProvider.php b/tests/fixtures/stubs/modules/valid/article/src/Providers/RouteServiceProvider.php new file mode 100644 index 0000000..e20139c --- /dev/null +++ b/tests/fixtures/stubs/modules/valid/article/src/Providers/RouteServiceProvider.php @@ -0,0 +1,57 @@ +mapApiRoutes(); + $this->mapWebRoutes(); + } + + /** + * Define the "web" routes for the application. + * These routes all receive session state, CSRF protection, etc. + */ + protected function mapWebRoutes(): void + { + Route::middleware('web') + // ->namespace('Modules\\Article\\UI\\WEB\\Controllers') + ->group(function () { + $this->loadRoutesFromDirectory(realpath('../UI/WEB/routes')); + }); + } + + /** + * Define the "api" routes for the application. + * These routes are typically stateless. + */ + protected function mapApiRoutes(): void + { + Route::prefix('api') + ->middleware('api') + // ->namespace('Modules\\Article\\UI\\API\\Controllers') + ->group(function () { + $this->loadRoutesFromDirectory(realpath('../UI/API/routes')); + }); + } +} diff --git a/tests/fixtures/stubs/modules/valid/author/composer.json b/tests/fixtures/stubs/modules/valid/author/composer.json new file mode 100644 index 0000000..da14fd9 --- /dev/null +++ b/tests/fixtures/stubs/modules/valid/author/composer.json @@ -0,0 +1,31 @@ +{ + "name": "laraneat/author", + "description": "Author module for testing", + "authors": [ + { + "name": "Salavat Salakhutdinov", + "email": "salahutdinov.salavat@gmail.com" + } + ], + "autoload": { + "psr-4": { + "Modules\\Author\\": "src/" + } + }, + "autoload-dev": { + "psr-4": { + "Modules\\Author\\Tests\\": "tests/" + } + }, + "extra": { + "laravel": { + "providers": [ + "Modules\\Author\\Providers\\AuthorServiceProvider", + "Modules\\Author\\Providers\\RouteServiceProvider" + ], + "aliases": { + "AuthorFacade": "Modules\\Author\\Facades\\SomeFacade" + } + } + } +} diff --git a/tests/fixtures/stubs/modules/valid/author/src/Facades/SomeFacade.php b/tests/fixtures/stubs/modules/valid/author/src/Facades/SomeFacade.php new file mode 100644 index 0000000..bee9723 --- /dev/null +++ b/tests/fixtures/stubs/modules/valid/author/src/Facades/SomeFacade.php @@ -0,0 +1,7 @@ +loadMigrations(); + // $this->loadTranslations(); + // $this->loadCommands(); + // $this->loadViews(); + } + + /** + * Register translations. + */ + public function loadTranslations(): void + { + $sourcePath = realpath('../../lang'); + $langPath = $this->app->langPath('modules/' . $this->modulePackageName); + + $this->loadTranslationsFrom($sourcePath, $this->modulePackageName); + + $this->publishes([ + $sourcePath => $langPath, + ], 'author-translations'); + } + + /** + * Register views. + */ + public function loadViews(): void + { + $sourcePath = realpath('../../resources/views'); + $viewsPath = $this->app->resourcePath('views/modules/' . $this->modulePackageName); + + $this->loadViewsFrom( + array_merge($this->getPublishableViewPaths($this->modulePackageName), [$sourcePath]), + $this->modulePackageName + ); + + $this->publishes([ + $sourcePath => $viewsPath, + ], 'author-views'); + } + + /** + * Register migrations. + */ + public function loadMigrations(): void + { + $sourcePath = realpath('../../database/migrations'); + $migrationsPath = $this->app->databasePath('migrations'); + + $this->loadMigrationsFrom($sourcePath); + + $this->publishes([ + $sourcePath => $migrationsPath, + ], 'author-migrations'); + } + + /** + * Register artisan commands. + */ + public function loadCommands(): void + { + if ($this->app->runningInConsole()) { + $this->loadCommandsFrom([ + 'Modules\\Author\\UI\\CLI\\Commands' => __DIR__.'/../UI/CLI/Commands', + ]); + } + } +} diff --git a/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_can_set_custom_model__14.txt b/tests/fixtures/stubs/modules/valid/author/src/Providers/RouteServiceProvider.php similarity index 63% rename from tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_can_set_custom_model__14.txt rename to tests/fixtures/stubs/modules/valid/author/src/Providers/RouteServiceProvider.php index 3d39542..a9eff6d 100644 --- a/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_can_set_custom_model__14.txt +++ b/tests/fixtures/stubs/modules/valid/author/src/Providers/RouteServiceProvider.php @@ -1,16 +1,16 @@ namespace('App\\Modules\\Blog\\UI\\WEB\\Controllers') + // ->namespace('Modules\\Author\\UI\\WEB\\Controllers') ->group(function () { - $this->loadRoutesFromDirectory(module_path($this->moduleName, 'UI/WEB/Routes')); + $this->loadRoutesFromDirectory(realpath('../UI/WEB/Routes')); }); } @@ -51,9 +51,9 @@ protected function mapApiRoutes(): void { Route::prefix('api') ->middleware('api') -// ->namespace('App\\Modules\\Blog\\UI\\API\\Controllers') + // ->namespace('Modules\\Author\\UI\\API\\Controllers') ->group(function () { - $this->loadRoutesFromDirectory(module_path($this->moduleName, 'UI/API/Routes')); + $this->loadRoutesFromDirectory(realpath('../UI/API/Routes')); }); } } diff --git a/tests/fixtures/stubs/modules/valid/empty-module/composer.json b/tests/fixtures/stubs/modules/valid/empty-module/composer.json new file mode 100644 index 0000000..be56de9 --- /dev/null +++ b/tests/fixtures/stubs/modules/valid/empty-module/composer.json @@ -0,0 +1,15 @@ +{ + "name": "laraneat/empty", + "description": "Empty module for testing", + "authors": [ + { + "name": "Salavat Salakhutdinov", + "email": "salahutdinov.salavat@gmail.com" + } + ], + "autoload": { + "psr-4": { + "Modules\\Empty\\": "src/" + } + } +} diff --git a/tests/fixtures/stubs/modules/valid/empty/composer.json b/tests/fixtures/stubs/modules/valid/empty/composer.json new file mode 100644 index 0000000..f8d6492 --- /dev/null +++ b/tests/fixtures/stubs/modules/valid/empty/composer.json @@ -0,0 +1,15 @@ +{ + "name": "empty/empty", + "description": "Empty module for testing", + "authors": [ + { + "name": "Salavat Salakhutdinov", + "email": "salahutdinov.salavat@gmail.com" + } + ], + "autoload": { + "psr-4": { + "Empty\\Empty\\": "src/" + } + } +} diff --git a/tests/fixtures/stubs/modules/valid/navigation/composer.json b/tests/fixtures/stubs/modules/valid/navigation/composer.json new file mode 100644 index 0000000..f27c35b --- /dev/null +++ b/tests/fixtures/stubs/modules/valid/navigation/composer.json @@ -0,0 +1,29 @@ +{ + "name": "laraneat/location", + "description": "GeoLocation module for testing", + "authors": [ + { + "name": "Salavat Salakhutdinov", + "email": "salahutdinov.salavat@gmail.com" + } + ], + "autoload": { + "psr-4": { + "Modules\\GeoLocation\\": "src/" + } + }, + "autoload-dev": { + "psr-4": { + "Modules\\GeoLocation\\Tests\\": "tests/" + } + }, + "extra": { + "laravel": { + "providers": [ + "Modules\\GeoLocation\\Providers\\GeoLocationServiceProvider", + "Modules\\GeoLocation\\Providers\\RouteServiceProvider" + ], + "aliases": [] + } + } +} diff --git a/tests/fixtures/stubs/modules/valid/navigation/src/Providers/GeoLocationServiceProvider.php b/tests/fixtures/stubs/modules/valid/navigation/src/Providers/GeoLocationServiceProvider.php new file mode 100644 index 0000000..979cd4c --- /dev/null +++ b/tests/fixtures/stubs/modules/valid/navigation/src/Providers/GeoLocationServiceProvider.php @@ -0,0 +1,89 @@ +loadMigrations(); + // $this->loadTranslations(); + // $this->loadCommands(); + // $this->loadViews(); + } + + /** + * Register translations. + */ + public function loadTranslations(): void + { + $sourcePath = realpath('../../lang'); + $langPath = $this->app->langPath('modules/' . $this->modulePackageName); + + $this->loadTranslationsFrom($sourcePath, $this->modulePackageName); + + $this->publishes([ + $sourcePath => $langPath, + ], 'geo-location-translations'); + } + + /** + * Register views. + */ + public function loadViews(): void + { + $sourcePath = realpath('../../resources/views'); + $viewsPath = $this->app->resourcePath('views/modules/' . $this->modulePackageName); + + $this->loadViewsFrom( + array_merge($this->getPublishableViewPaths($this->modulePackageName), [$sourcePath]), + $this->modulePackageName + ); + + $this->publishes([ + $sourcePath => $viewsPath, + ], 'geo-location-views'); + } + + /** + * Register migrations. + */ + public function loadMigrations(): void + { + $sourcePath = realpath('../../database/migrations'); + $migrationsPath = $this->app->databasePath('migrations'); + + $this->loadMigrationsFrom($sourcePath); + + $this->publishes([ + $sourcePath => $migrationsPath, + ], 'geo-location-migrations'); + } + + /** + * Register artisan commands. + */ + public function loadCommands(): void + { + if ($this->app->runningInConsole()) { + $this->loadCommandsFrom([ + 'Modules\\GeoLocation\\UI\\CLI\\Commands' => __DIR__.'/../UI/CLI/Commands', + ]); + } + } +} diff --git a/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_generates_module_components__12.txt b/tests/fixtures/stubs/modules/valid/navigation/src/Providers/RouteServiceProvider.php similarity index 64% rename from tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_generates_module_components__12.txt rename to tests/fixtures/stubs/modules/valid/navigation/src/Providers/RouteServiceProvider.php index 8e33d3a..7bbbe04 100644 --- a/tests/Commands/Generators/__snapshots__/ModuleMakeCommandTest__it_generates_module_components__12.txt +++ b/tests/fixtures/stubs/modules/valid/navigation/src/Providers/RouteServiceProvider.php @@ -1,16 +1,14 @@ namespace('App\\Modules\\Article\\UI\\WEB\\Controllers') + // ->namespace('Modules\\GeoLocation\\UI\\WEB\\Controllers') ->group(function () { - $this->loadRoutesFromDirectory(module_path($this->moduleName, 'UI/WEB/Routes')); + $this->loadRoutesFromDirectory(realpath('../UI/WEB/routes')); }); } @@ -51,9 +49,9 @@ protected function mapApiRoutes(): void { Route::prefix('api') ->middleware('api') -// ->namespace('App\\Modules\\Article\\UI\\API\\Controllers') + // ->namespace('Modules\\GeoLocation\\UI\\API\\Controllers') ->group(function () { - $this->loadRoutesFromDirectory(module_path($this->moduleName, 'UI/API/Routes')); + $this->loadRoutesFromDirectory(realpath('../UI/API/routes')); }); } } diff --git a/tests/fixtures/stubs/valid/Article/Actions/CreateArticleAction.php b/tests/fixtures/stubs/valid/Article/Actions/CreateArticleAction.php deleted file mode 100644 index abbd705..0000000 --- a/tests/fixtures/stubs/valid/Article/Actions/CreateArticleAction.php +++ /dev/null @@ -1,37 +0,0 @@ -handle( - // - ); - - return (new ArticleResource($article))->created(); - } -} diff --git a/tests/fixtures/stubs/valid/Article/Actions/DeleteArticleAction.php b/tests/fixtures/stubs/valid/Article/Actions/DeleteArticleAction.php deleted file mode 100644 index 2f15442..0000000 --- a/tests/fixtures/stubs/valid/Article/Actions/DeleteArticleAction.php +++ /dev/null @@ -1,34 +0,0 @@ -delete(); - } - - /** - * @param DeleteArticleRequest $request - * @param Article $article - * - * @return JsonResponse - */ - public function asController(DeleteArticleRequest $request, Article $article): JsonResponse - { - $this->handle($article); - - return $this->noContent(); - } -} diff --git a/tests/fixtures/stubs/valid/Article/Actions/ListArticlesAction.php b/tests/fixtures/stubs/valid/Article/Actions/ListArticlesAction.php deleted file mode 100644 index 1796517..0000000 --- a/tests/fixtures/stubs/valid/Article/Actions/ListArticlesAction.php +++ /dev/null @@ -1,36 +0,0 @@ -build() - ->jsonPaginate(); - } - - /** - * @param ListArticlesRequest $request - * - * @return ResourceCollection - */ - public function asController(ListArticlesRequest $request): ResourceCollection - { - return ArticleResource::collection($this->handle($request)); - } -} diff --git a/tests/fixtures/stubs/valid/Article/Actions/UpdateArticleAction.php b/tests/fixtures/stubs/valid/Article/Actions/UpdateArticleAction.php deleted file mode 100644 index b8d63ef..0000000 --- a/tests/fixtures/stubs/valid/Article/Actions/UpdateArticleAction.php +++ /dev/null @@ -1,46 +0,0 @@ -update($data); - - return $article; - } - - /** - * @param UpdateArticleRequest $request - * @param Article $article - * - * @return ArticleResource - */ - public function asController(UpdateArticleRequest $request, Article $article): ArticleResource - { - $sanitizedData = $request->sanitizeInput([ - // - ]); - - $article = $this->handle($article, $sanitizedData); - - return new ArticleResource($article); - } -} diff --git a/tests/fixtures/stubs/valid/Article/Actions/ViewArticleAction.php b/tests/fixtures/stubs/valid/Article/Actions/ViewArticleAction.php deleted file mode 100644 index d2965ff..0000000 --- a/tests/fixtures/stubs/valid/Article/Actions/ViewArticleAction.php +++ /dev/null @@ -1,35 +0,0 @@ -build(); - } - - /** - * @param ViewArticleRequest $request - * @param Article $article - * - * @return ArticleResource - */ - public function asController(ViewArticleRequest $request, Article $article): ArticleResource - { - return new ArticleResource($this->handle($request, $article)); - } -} diff --git a/tests/fixtures/stubs/valid/Article/Config/article-module.php b/tests/fixtures/stubs/valid/Article/Config/article-module.php deleted file mode 100644 index 1faef3e..0000000 --- a/tests/fixtures/stubs/valid/Article/Config/article-module.php +++ /dev/null @@ -1,8 +0,0 @@ - 'Article', - 'some' => [ - 'nested' => 'Hello world' - ] -]; diff --git a/tests/fixtures/stubs/valid/Article/Data/Factories/ArticleFactory.php b/tests/fixtures/stubs/valid/Article/Data/Factories/ArticleFactory.php deleted file mode 100644 index 60c7559..0000000 --- a/tests/fixtures/stubs/valid/Article/Data/Factories/ArticleFactory.php +++ /dev/null @@ -1,36 +0,0 @@ -id(); - - $table->timestamps(); - }); - } - - /** - * Reverse the migrations. - * - * @return void - */ - public function down() - { - Schema::dropIfExists('articles'); - } -} diff --git a/tests/fixtures/stubs/valid/Article/Data/Seeders/ArticlePermissionsSeeder_1.php b/tests/fixtures/stubs/valid/Article/Data/Seeders/ArticlePermissionsSeeder_1.php deleted file mode 100644 index 14ec389..0000000 --- a/tests/fixtures/stubs/valid/Article/Data/Seeders/ArticlePermissionsSeeder_1.php +++ /dev/null @@ -1,45 +0,0 @@ -handle(new CreatePermissionDTO( - name: 'view-article', - display_name: 'View any articles', - group: 'articles' - )); - - $createPermissionAction->handle(new CreatePermissionDTO( - name: 'create-article', - display_name: 'Create articles', - group: 'articles' - )); - - $createPermissionAction->handle(new CreatePermissionDTO( - name: 'update-article', - display_name: 'Update any articles', - group: 'articles' - )); - - $createPermissionAction->handle(new CreatePermissionDTO( - name: 'delete-article', - display_name: 'Delete any articles', - group: 'articles' - )); - - $createPermissionAction->handle(new CreatePermissionDTO( - name: 'force-delete-article', - display_name: 'Force delete any articles', - group: 'articles' - )); - } -} diff --git a/tests/fixtures/stubs/valid/Article/Models/Article.php b/tests/fixtures/stubs/valid/Article/Models/Article.php deleted file mode 100644 index 49b0ac2..0000000 --- a/tests/fixtures/stubs/valid/Article/Models/Article.php +++ /dev/null @@ -1,23 +0,0 @@ -can('view-article'); - } - - /** - * Determine whether the user can view the model. - * - * @param User $user - * @param Article $article - * @return \Illuminate\Auth\Access\Response|bool - */ - public function view(User $user, Article $article) - { - return $user->can('view-article'); - } - - /** - * Determine whether the user can create models. - * - * @param User $user - * @return \Illuminate\Auth\Access\Response|bool - */ - public function create(User $user) - { - return $user->can('create-article'); - } - - /** - * Determine whether the user can update the model. - * - * @param User $user - * @param Article $article - * @return \Illuminate\Auth\Access\Response|bool - */ - public function update(User $user, Article $article) - { - return $user->can('update-article'); - } - - /** - * Determine whether the user can delete the model. - * - * @param User $user - * @param Article $article - * @return \Illuminate\Auth\Access\Response|bool - */ - public function delete(User $user, Article $article) - { - return $user->can('delete-article'); - } - - /** - * Determine whether the user can restore the model. - * - * @param User $user - * @param Article $article - * @return \Illuminate\Auth\Access\Response|bool - */ - public function restore(User $user, Article $article) - { - return $user->can('delete-article'); - } - - /** - * Determine whether the user can permanently delete the model. - * - * @param User $user - * @param Article $article - * @return \Illuminate\Auth\Access\Response|bool - */ - public function forceDelete(User $user, Article $article) - { - return $user->can('force-delete-article'); - } -} diff --git a/tests/fixtures/stubs/valid/Article/Providers/ArticleServiceProvider.php b/tests/fixtures/stubs/valid/Article/Providers/ArticleServiceProvider.php deleted file mode 100644 index 5da4a0d..0000000 --- a/tests/fixtures/stubs/valid/Article/Providers/ArticleServiceProvider.php +++ /dev/null @@ -1,116 +0,0 @@ -app->register(RouteServiceProvider::class); - } - - /** - * Bootstrap services. - * - * @return void - */ - public function boot(): void - { - // $this->registerCommands(); - // $this->registerTranslations(); - // $this->registerViews(); - // $this->registerMigrations(); - } - - /** - * Get the services provided by the provider. - * - * @return array - */ - public function provides(): array - { - return []; - } - - /** - * Register artisan commands. - * - * @return void - */ - public function registerCommands(): void - { - if ($this->app->runningInConsole()) { - $this->loadCommands(module_path($this->moduleName, 'UI/CLI/Commands')); - } - } - - /** - * Register translations. - * - * @return void - */ - public function registerTranslations(): void - { - $sourcePath = module_path($this->moduleName, 'Resources/lang'); - $langPath = resource_path('lang/modules/' . $this->moduleNameLower); - - $this->loadTranslationsFrom($sourcePath, $this->moduleNameLower); - } - - /** - * Register views. - * - * @return void - */ - public function registerViews(): void - { - $sourcePath = module_path($this->moduleName, 'Resources/views'); - $viewsPath = resource_path('views/modules/' . $this->moduleNameLower); - - $this->publishes([ - $sourcePath => $viewsPath - ], 'views'); - - $this->loadViewsFrom( - array_merge($this->getPublishableViewPaths($this->moduleNameLower), [$sourcePath]), - $this->moduleNameLower - ); - } - - /** - * Register migrations. - * - * @return void - */ - public function registerMigrations(): void - { - $sourcePath = module_path($this->moduleName, 'Data/Migrations'); - $migrationsPath = database_path('migrations'); - - $this->publishes([ - $sourcePath => $migrationsPath - ], 'migrations'); - - $this->loadMigrationsFrom($sourcePath); - } -} diff --git a/tests/fixtures/stubs/valid/Article/Providers/DeferredServiceProvider.php b/tests/fixtures/stubs/valid/Article/Providers/DeferredServiceProvider.php deleted file mode 100644 index 653a61d..0000000 --- a/tests/fixtures/stubs/valid/Article/Providers/DeferredServiceProvider.php +++ /dev/null @@ -1,35 +0,0 @@ -bind('foo', function () { - return 'bar'; - }); - - app()->bind('deferred', function () { - return; - }); - } - - /** - * Get the services provided by the provider. - * - * @return array - */ - public function provides() - { - return ['deferred']; - } -} diff --git a/tests/fixtures/stubs/valid/Article/Providers/RouteServiceProvider.php b/tests/fixtures/stubs/valid/Article/Providers/RouteServiceProvider.php deleted file mode 100644 index 53f0711..0000000 --- a/tests/fixtures/stubs/valid/Article/Providers/RouteServiceProvider.php +++ /dev/null @@ -1,73 +0,0 @@ -mapApiRoutes(); - $this->mapWebRoutes(); - } - - /** - * Define the "web" routes for the application. - * - * These routes all receive session state, CSRF protection, etc. - * - * @return void - */ - protected function mapWebRoutes(): void - { - Route::middleware('web') -// ->namespace('App\\Modules\\Article\\UI\\WEB\\Controllers') - ->group(function () { - $this->loadRoutesFromDirectory(module_path($this->moduleName, 'UI/WEB/Routes')); - }); - } - - /** - * Define the "api" routes for the application. - * - * These routes are typically stateless. - * - * @return void - */ - protected function mapApiRoutes(): void - { - Route::prefix('api') - ->middleware('api') -// ->namespace('App\\Modules\\Article\\UI\\API\\Controllers') - ->group(function () { - $this->loadRoutesFromDirectory(module_path($this->moduleName, 'UI/API/Routes')); - }); - } -} diff --git a/tests/fixtures/stubs/valid/Article/UI/API/QueryWizards/ArticleQueryWizard.php b/tests/fixtures/stubs/valid/Article/UI/API/QueryWizards/ArticleQueryWizard.php deleted file mode 100644 index 746c94f..0000000 --- a/tests/fixtures/stubs/valid/Article/UI/API/QueryWizards/ArticleQueryWizard.php +++ /dev/null @@ -1,48 +0,0 @@ -user()->can('create', Article::class); - } -} diff --git a/tests/fixtures/stubs/valid/Article/UI/API/Requests/DeleteArticleRequest.php b/tests/fixtures/stubs/valid/Article/UI/API/Requests/DeleteArticleRequest.php deleted file mode 100644 index 6088a69..0000000 --- a/tests/fixtures/stubs/valid/Article/UI/API/Requests/DeleteArticleRequest.php +++ /dev/null @@ -1,19 +0,0 @@ -route('article'); - return $article && $this->user()->can('delete', $article); - } -} diff --git a/tests/fixtures/stubs/valid/Article/UI/API/Requests/ListArticlesRequest.php b/tests/fixtures/stubs/valid/Article/UI/API/Requests/ListArticlesRequest.php deleted file mode 100644 index f5f36cd..0000000 --- a/tests/fixtures/stubs/valid/Article/UI/API/Requests/ListArticlesRequest.php +++ /dev/null @@ -1,19 +0,0 @@ -user()->can('viewAny', Article::class); - } -} diff --git a/tests/fixtures/stubs/valid/Article/UI/API/Requests/UpdateArticleRequest.php b/tests/fixtures/stubs/valid/Article/UI/API/Requests/UpdateArticleRequest.php deleted file mode 100644 index 137cc3b..0000000 --- a/tests/fixtures/stubs/valid/Article/UI/API/Requests/UpdateArticleRequest.php +++ /dev/null @@ -1,19 +0,0 @@ -route('article'); - return $article && $this->user()->can('update', $article); - } -} diff --git a/tests/fixtures/stubs/valid/Article/UI/API/Requests/ViewArticleRequest.php b/tests/fixtures/stubs/valid/Article/UI/API/Requests/ViewArticleRequest.php deleted file mode 100644 index 4903a09..0000000 --- a/tests/fixtures/stubs/valid/Article/UI/API/Requests/ViewArticleRequest.php +++ /dev/null @@ -1,19 +0,0 @@ -route('article'); - return $article && $this->user()->can('view', $article); - } -} diff --git a/tests/fixtures/stubs/valid/Article/UI/API/Resources/ArticleResource.php b/tests/fixtures/stubs/valid/Article/UI/API/Resources/ArticleResource.php deleted file mode 100644 index 57aaa1c..0000000 --- a/tests/fixtures/stubs/valid/Article/UI/API/Resources/ArticleResource.php +++ /dev/null @@ -1,19 +0,0 @@ -name('api.articles.create'); diff --git a/tests/fixtures/stubs/valid/Article/UI/API/Routes/v1/delete_article.php b/tests/fixtures/stubs/valid/Article/UI/API/Routes/v1/delete_article.php deleted file mode 100644 index cceba8f..0000000 --- a/tests/fixtures/stubs/valid/Article/UI/API/Routes/v1/delete_article.php +++ /dev/null @@ -1,7 +0,0 @@ -name('api.articles.delete'); diff --git a/tests/fixtures/stubs/valid/Article/UI/API/Routes/v1/list_articles.php b/tests/fixtures/stubs/valid/Article/UI/API/Routes/v1/list_articles.php deleted file mode 100644 index 093f946..0000000 --- a/tests/fixtures/stubs/valid/Article/UI/API/Routes/v1/list_articles.php +++ /dev/null @@ -1,7 +0,0 @@ -name('api.articles.list'); diff --git a/tests/fixtures/stubs/valid/Article/UI/API/Routes/v1/update_article.php b/tests/fixtures/stubs/valid/Article/UI/API/Routes/v1/update_article.php deleted file mode 100644 index 7e76fc3..0000000 --- a/tests/fixtures/stubs/valid/Article/UI/API/Routes/v1/update_article.php +++ /dev/null @@ -1,7 +0,0 @@ -name('api.articles.update'); diff --git a/tests/fixtures/stubs/valid/Article/UI/API/Routes/v1/view_article.php b/tests/fixtures/stubs/valid/Article/UI/API/Routes/v1/view_article.php deleted file mode 100644 index 98fedcf..0000000 --- a/tests/fixtures/stubs/valid/Article/UI/API/Routes/v1/view_article.php +++ /dev/null @@ -1,7 +0,0 @@ -name('api.articles.view'); diff --git a/tests/fixtures/stubs/valid/Article/composer.json b/tests/fixtures/stubs/valid/Article/composer.json deleted file mode 100644 index 035ed79..0000000 --- a/tests/fixtures/stubs/valid/Article/composer.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "name": "laraneat/article", - "description": "Article module", - "authors": [ - { - "name": "Salakhutdinov Salavat", - "email": "salahutdinov.salavat@gmail.com" - } - ], - "autoload": { - "psr-4": { - "App\\Modules\\Article\\": "" - } - }, - "require": { - "php": "^8.0" - }, - "require-dev": { - "orchestra/testbench": "^6.2", - "phpunit/phpunit": "^9.5" - } -} diff --git a/tests/fixtures/stubs/valid/Article/module.json b/tests/fixtures/stubs/valid/Article/module.json deleted file mode 100644 index d843c3a..0000000 --- a/tests/fixtures/stubs/valid/Article/module.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "name": "Article", - "namespace": "App\\Modules\\Article", - "version": "0.1", - "alias": "article", - "description": "article module", - "keywords": [], - "priority": 0, - "providers": [ - "App\\Modules\\Article\\Providers\\ArticleServiceProvider", - "App\\Modules\\Article\\Providers\\DeferredServiceProvider" - ], - "aliases": {}, - "files": [], - "requires": [ - "required_module" - ] -} diff --git a/tests/fixtures/stubs/valid/Requirement/module.json b/tests/fixtures/stubs/valid/Requirement/module.json deleted file mode 100644 index 62a1dec..0000000 --- a/tests/fixtures/stubs/valid/Requirement/module.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "name": "Requirement", - "alias": "required_module", - "namespace": "App\\Modules\\Requirement", - "description": "module that Article requires", - "version": "0.1", - "keywords": [], - "order": 1, - "providers": [ - ], - "aliases": [], - "files": [], - "requires": [] -} diff --git a/tests/fixtures/stubs/valid/module.json b/tests/fixtures/stubs/valid/module.json deleted file mode 100644 index 16973d4..0000000 --- a/tests/fixtures/stubs/valid/module.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "name": "Order", - "alias": "order", - "namespace": "App\\Modules", - "description": "My demo module", - "version": "0.1", - "keywords": [ - "my", - "stub", - "module" - ], - "active": 1, - "order": 1, - "providers": [ - "App\\Modules\\Order\\Providers\\OrderServiceProvider", - "App\\Modules\\Order\\Providers\\EventServiceProvider", - "App\\Modules\\Order\\Providers\\RouteServiceProvider" - ], - "aliases": [], - "files": [] -}