release/v0.8.0 #49
143 changed files with 12766 additions and 609 deletions
25
.env.testing
Normal file
25
.env.testing
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
APP_NAME=DishPlanner
|
||||
APP_ENV=testing
|
||||
APP_KEY=base64:2ADW3imsmRMo+UDw3GR6852wQu37/aNK1ooxEdaJIC0=
|
||||
APP_DEBUG=true
|
||||
APP_URL=http://localhost
|
||||
|
||||
APP_MAINTENANCE_DRIVER=file
|
||||
|
||||
BCRYPT_ROUNDS=4
|
||||
|
||||
LOG_CHANNEL=stack
|
||||
LOG_STACK=single
|
||||
|
||||
DB_CONNECTION=sqlite
|
||||
DB_DATABASE=:memory:
|
||||
|
||||
SESSION_DRIVER=array
|
||||
|
||||
BROADCAST_CONNECTION=log
|
||||
FILESYSTEM_DISK=local
|
||||
QUEUE_CONNECTION=sync
|
||||
|
||||
CACHE_STORE=array
|
||||
|
||||
MAIL_MAILER=array
|
||||
42
.forgejo/workflows/build.yml
Normal file
42
.forgejo/workflows/build.yml
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
name: Build and Push Docker Image
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
tags: ['v*']
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: docker
|
||||
container:
|
||||
image: catthehacker/ubuntu:act-latest
|
||||
steps:
|
||||
- uses: https://data.forgejo.org/actions/checkout@v4
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: https://data.forgejo.org/docker/setup-buildx-action@v3
|
||||
|
||||
- name: Login to Forgejo Registry
|
||||
uses: https://data.forgejo.org/docker/login-action@v3
|
||||
with:
|
||||
registry: forge.lvl0.xyz
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.REGISTRY_TOKEN }}
|
||||
|
||||
- name: Determine tags
|
||||
id: meta
|
||||
run: |
|
||||
if [[ "${{ github.ref }}" == refs/tags/v* ]]; then
|
||||
TAG="${{ github.ref_name }}"
|
||||
echo "tags=forge.lvl0.xyz/lvl0/dishplanner:${TAG},forge.lvl0.xyz/lvl0/dishplanner:latest" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "tags=forge.lvl0.xyz/lvl0/dishplanner:latest" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
- name: Build and push
|
||||
uses: https://data.forgejo.org/docker/build-push-action@v5
|
||||
with:
|
||||
context: .
|
||||
file: Dockerfile
|
||||
push: true
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
32
.forgejo/workflows/ci.yml
Normal file
32
.forgejo/workflows/ci.yml
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
name: CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: ['release/*']
|
||||
pull_request:
|
||||
branches: [main, 'release/*']
|
||||
|
||||
jobs:
|
||||
ci:
|
||||
runs-on: docker
|
||||
container:
|
||||
image: forge.lvl0.xyz/lvl0/dishplanner-ci:php8.3-4
|
||||
steps:
|
||||
- uses: https://data.forgejo.org/actions/checkout@v4
|
||||
|
||||
- name: Prepare environment
|
||||
run: cp .env.testing .env
|
||||
|
||||
- name: Restore dependencies
|
||||
run: |
|
||||
cp -a /opt/deps/vendor ./vendor
|
||||
composer install --no-interaction --no-progress --prefer-source
|
||||
|
||||
- name: Lint
|
||||
run: vendor/bin/pint --test
|
||||
|
||||
- name: Static analysis
|
||||
run: vendor/bin/phpstan analyse --memory-limit=1G
|
||||
|
||||
- name: Tests
|
||||
run: php -d memory_limit=512M vendor/bin/phpunit
|
||||
46
.forgejo/workflows/images.yml
Normal file
46
.forgejo/workflows/images.yml
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
name: Build and Push Base Images
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
paths:
|
||||
- 'docker/build/**'
|
||||
- 'composer.json'
|
||||
- 'composer.lock'
|
||||
- '.forgejo/workflows/images.yml'
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
images:
|
||||
runs-on: docker
|
||||
container:
|
||||
image: catthehacker/ubuntu:act-latest
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- name: dishplanner-ci
|
||||
file: docker/build/Dockerfile.ci
|
||||
version: php8.3-4
|
||||
steps:
|
||||
- uses: https://data.forgejo.org/actions/checkout@v4
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: https://data.forgejo.org/docker/setup-buildx-action@v3
|
||||
|
||||
- name: Login to Forgejo Registry
|
||||
uses: https://data.forgejo.org/docker/login-action@v3
|
||||
with:
|
||||
registry: forge.lvl0.xyz
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.REGISTRY_TOKEN }}
|
||||
|
||||
- name: Build and push
|
||||
uses: https://data.forgejo.org/docker/build-push-action@v5
|
||||
with:
|
||||
context: .
|
||||
file: ${{ matrix.file }}
|
||||
push: true
|
||||
tags: |
|
||||
forge.lvl0.xyz/lvl0/${{ matrix.name }}:${{ matrix.version }}
|
||||
forge.lvl0.xyz/lvl0/${{ matrix.name }}:latest
|
||||
forge.lvl0.xyz/lvl0/${{ matrix.name }}:${{ github.sha }}
|
||||
2
.gitattributes
vendored
2
.gitattributes
vendored
|
|
@ -6,6 +6,4 @@
|
|||
*.md diff=markdown
|
||||
*.php diff=php
|
||||
|
||||
/.github export-ignore
|
||||
CHANGELOG.md export-ignore
|
||||
.styleci.yml export-ignore
|
||||
|
|
|
|||
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -1,4 +1,3 @@
|
|||
/composer.lock
|
||||
/.phpunit.cache
|
||||
/coverage
|
||||
/node_modules
|
||||
|
|
|
|||
24
CHANGELOG.md
24
CHANGELOG.md
|
|
@ -5,6 +5,30 @@ # Changelog
|
|||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [0.8.0] - 2026-08-17
|
||||
|
||||
### Added
|
||||
|
||||
- **Forgejo CI pipeline** (#45) — added a CI workflow that runs Laravel Pint code-style checks, PHPStan static analysis, and the PHPUnit suite against SQLite in memory.
|
||||
- **Static analysis** — added PHPStan via `larastan/larastan` and `phpstan/phpstan-mockery`, with `phpstan.neon` and a baseline.
|
||||
- **Code style** — added Laravel Pint configuration (`pint.json`).
|
||||
- **CI and app image build workflows** (#46) — automated Docker image builds for the app and the CI base image, published to the Forgejo registry.
|
||||
- **CI base image** (#46) — added `docker/build/Dockerfile.ci`, a Debian-based PHP 8.3 CLI image with the project's PHP dependencies pre-loaded so CI doesn't pay a per-run Composer install.
|
||||
- **Contributing guide** (#47) — added `CONTRIBUTING.md`.
|
||||
- **Nix shell completion** (#44) — finished the `nix-shell` development commands.
|
||||
|
||||
### Changed
|
||||
|
||||
- **Dependencies** — `composer.lock` is now committed to the repository.
|
||||
- **Code style** — reformatted the codebase with Laravel Pint.
|
||||
- **Documentation** (#47) — professionalized the `README.md`.
|
||||
- **Git hygiene** — dropped stale `.github`/StyleCI export rules from `.gitattributes`.
|
||||
|
||||
### Fixed
|
||||
|
||||
- **CI dependency installation** (#46, #50) — retried `composer install` on transient HTTP 429s, switched to `--prefer-source` and pre-loaded dependencies to avoid `codeload.github.com` rate limits, and added the missing `zip` extension to the CI image.
|
||||
- **Test suite** (#50) — fixed feature tests failing on a missing Vite manifest by disabling Vite resolution during tests.
|
||||
|
||||
## [0.7.0] - 2026-08-17
|
||||
|
||||
### Removed
|
||||
|
|
|
|||
97
CONTRIBUTING.md
Normal file
97
CONTRIBUTING.md
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
# Contributing
|
||||
|
||||
Thanks for your interest in DishPlanner. It's a small self-hosted project —
|
||||
issues and pull requests are both welcome.
|
||||
|
||||
## Reporting issues
|
||||
|
||||
Use [Issues](https://forge.lvl0.xyz/lvl0/dishplanner/issues).
|
||||
|
||||
For bugs, include what you expected, what happened, and enough detail to
|
||||
reproduce it. Relevant log output helps; `dev-logs` follows the application log.
|
||||
|
||||
## Development setup
|
||||
|
||||
Requires PHP 8.2+ and a container runtime (Podman or Docker). The development
|
||||
environment runs in containers defined by `docker-compose.yml`.
|
||||
|
||||
On NixOS, or anywhere with Nix installed:
|
||||
|
||||
```bash
|
||||
git clone https://forge.lvl0.xyz/lvl0/dishplanner.git
|
||||
cd dishplanner
|
||||
nix-shell
|
||||
```
|
||||
|
||||
The shell prints the available commands on entry and can start the containers
|
||||
for you:
|
||||
|
||||
| Command | Description |
|
||||
|---------|-------------|
|
||||
| `dev-up` | Start the development environment |
|
||||
| `dev-down` | Stop the development environment |
|
||||
| `dev-restart` | Restart the containers |
|
||||
| `dev-rebuild` | Full rebuild (removes volumes) |
|
||||
| `dev-rebuild-quick` | Quick rebuild (keeps volumes) |
|
||||
| `dev-logs [service]` | Follow logs |
|
||||
| `dev-logs-db` | Tail database logs |
|
||||
| `dev-shell` | Enter the app container |
|
||||
| `dev-artisan <cmd>` | Run an artisan command |
|
||||
| `dev-test [path]` | Run the PHPUnit suite the CI way |
|
||||
| `dev-fix-permissions` | Fix Docker-created file permissions |
|
||||
|
||||
Once running:
|
||||
|
||||
| Service | URL |
|
||||
|---------|-----|
|
||||
| App | http://localhost:8000 |
|
||||
| Vite | http://localhost:5173 |
|
||||
| Mailhog | http://localhost:8025 |
|
||||
| MariaDB | localhost:3306 |
|
||||
|
||||
Without Nix, start the same containers directly from `docker-compose.yml`.
|
||||
Contributions improving the setup instructions for other platforms are welcome.
|
||||
|
||||
## Before opening a pull request
|
||||
|
||||
Three checks run in CI, and all three must pass. Run them locally first, from
|
||||
inside the app container or anywhere the project's dependencies are available:
|
||||
|
||||
```bash
|
||||
vendor/bin/pint --test # code style, Laravel preset
|
||||
vendor/bin/phpstan analyse --memory-limit=1G # static analysis, level 7
|
||||
php -d memory_limit=512M vendor/bin/phpunit # PHPUnit on SQLite in memory
|
||||
```
|
||||
|
||||
Or run the test suite the CI way with `dev-test`.
|
||||
|
||||
Some conventions:
|
||||
|
||||
- **Static analysis.** PHPStan runs at level 7 with a baseline
|
||||
(`phpstan-baseline.neon`) covering pre-existing findings. Don't add baseline
|
||||
entries to silence errors in code you're writing — fix the cause instead. The
|
||||
baseline is for cases where the analyzer or an upstream docblock is wrong,
|
||||
not real bugs.
|
||||
- **Tests.** New behaviour needs a test. Tests run against SQLite in memory and
|
||||
must not reach for the network.
|
||||
- **Dependencies.** `composer.lock` is committed. If you change dependencies,
|
||||
commit the updated lockfile alongside `composer.json`.
|
||||
|
||||
## Commits
|
||||
|
||||
One commit does one thing. Keep each commit passing all three checks so history
|
||||
stays bisectable. Separate renames from behaviour changes, and mechanical edits
|
||||
from logic.
|
||||
|
||||
Commit messages are a single line, referencing the ticket they belong to:
|
||||
|
||||
```
|
||||
45 - Add Forgejo CI workflow
|
||||
```
|
||||
|
||||
No body, no trailers.
|
||||
|
||||
## License
|
||||
|
||||
By contributing, you agree that your contributions are licensed under the
|
||||
[GNU AGPL-3.0](LICENSE.md), the same license as the project.
|
||||
12
README.md
12
README.md
|
|
@ -1,5 +1,8 @@
|
|||
# 🍽️ Dish Planner
|
||||
|
||||
[](https://forge.lvl0.xyz/lvl0/dishplanner/actions)
|
||||
[](LICENSE.md)
|
||||
|
||||
A Laravel-based meal planning application that helps households organize and schedule their dishes among multiple users. Built with Laravel, Livewire, and FrankenPHP for a modern, single-container deployment.
|
||||
|
||||
## ✨ Features
|
||||
|
|
@ -13,7 +16,7 @@ ## ✨ Features
|
|||
|
||||
## 🚀 Self-hosting
|
||||
|
||||
The production image is available at `forge.lvl0.xyz/lvl0/dishplanner:latest`.
|
||||
The production image is available at `forge.lvl0.xyz/lvl0/dishplanner:latest`. See [CHANGELOG.md](CHANGELOG.md) before upgrading.
|
||||
|
||||
### docker-compose.yml
|
||||
|
||||
|
|
@ -102,8 +105,10 @@ #### Available Commands
|
|||
| `dev-rebuild` | Full rebuild (removes volumes) |
|
||||
| `dev-rebuild-quick` | Quick rebuild (keeps volumes) |
|
||||
| `dev-logs [service]` | Follow logs |
|
||||
| `dev-logs-db` | Tail database logs |
|
||||
| `dev-shell` | Enter app container |
|
||||
| `dev-artisan <cmd>` | Run artisan commands |
|
||||
| `dev-test [path]` | Run the PHPUnit suite the CI way |
|
||||
| `dev-fix-permissions` | Fix Docker-created file permissions |
|
||||
|
||||
#### Services
|
||||
|
|
@ -119,6 +124,11 @@ ### Other Platforms
|
|||
|
||||
Contributions welcome for development setup instructions on other platforms.
|
||||
|
||||
## 🤝 Contributing
|
||||
|
||||
Issues and pull requests are welcome. See [CONTRIBUTING.md](CONTRIBUTING.md) for
|
||||
the development setup, the checks that run in CI, and the commit conventions.
|
||||
|
||||
## 📄 License
|
||||
|
||||
This project is open-source software licensed under the [AGPL-3.0 license](LICENSE.md).
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ public function handle(): int
|
|||
|
||||
if ($planners->isEmpty()) {
|
||||
$this->warn('No planners found. Aborting schedule generation.');
|
||||
|
||||
return self::FAILURE;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -13,8 +13,7 @@ public function response(
|
|||
?array $payload = null,
|
||||
array|string|null $errors = null,
|
||||
int $statusCode = 200,
|
||||
): JsonResponse
|
||||
{
|
||||
): JsonResponse {
|
||||
return response()->json(resolve(OutputService::class)->response($success, $payload, $errors), $statusCode);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ public function toArray(Request $request): array
|
|||
'recurrences' => $this->recurrences->map(fn ($recurrence) => [
|
||||
'id' => $recurrence->id,
|
||||
'type' => $recurrence->recurrence_type,
|
||||
'value' => $recurrence->getValue()
|
||||
'value' => $recurrence->getValue(),
|
||||
]),
|
||||
];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,14 +12,18 @@ class DishesList extends Component
|
|||
use WithPagination;
|
||||
|
||||
public $showCreateModal = false;
|
||||
|
||||
public $showEditModal = false;
|
||||
|
||||
public $showDeleteModal = false;
|
||||
|
||||
public $editingDish = null;
|
||||
|
||||
public $deletingDish = null;
|
||||
|
||||
// Form fields
|
||||
public $name = '';
|
||||
|
||||
public $selectedUsers = [];
|
||||
|
||||
protected $rules = [
|
||||
|
|
@ -39,7 +43,7 @@ public function render()
|
|||
|
||||
return view('livewire.dishes.dishes-list', [
|
||||
'dishes' => $dishes,
|
||||
'users' => $users
|
||||
'users' => $users,
|
||||
]);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -19,25 +19,39 @@
|
|||
class ScheduleCalendar extends Component
|
||||
{
|
||||
public $currentMonth;
|
||||
|
||||
public $currentYear;
|
||||
|
||||
public $calendarDays = [];
|
||||
|
||||
public $showRegenerateModal = false;
|
||||
|
||||
public $regenerateDate = null;
|
||||
|
||||
public $regenerateUserId = null;
|
||||
|
||||
// Edit dish modal
|
||||
public $showEditDishModal = false;
|
||||
|
||||
public $editDate = null;
|
||||
|
||||
public $editUserId = null;
|
||||
|
||||
public $selectedDishId = null;
|
||||
|
||||
public $availableDishes = [];
|
||||
|
||||
// Add dish modal
|
||||
public $showAddDishModal = false;
|
||||
|
||||
public $addDate = null;
|
||||
|
||||
public $addUserIds = [];
|
||||
|
||||
public $addSelectedDishId = null;
|
||||
|
||||
public $addAvailableUsers = [];
|
||||
|
||||
public $addAvailableDishes = [];
|
||||
|
||||
public function mount(): void
|
||||
|
|
@ -61,7 +75,7 @@ public function refreshCalendar(): void
|
|||
|
||||
public function loadCalendar(): void
|
||||
{
|
||||
$service = new ScheduleCalendarService();
|
||||
$service = new ScheduleCalendarService;
|
||||
$this->calendarDays = $service->getCalendarDays(
|
||||
auth()->user(),
|
||||
$this->currentMonth,
|
||||
|
|
@ -95,6 +109,7 @@ public function regenerateForUserDate($date, $userId): void
|
|||
{
|
||||
if (! $this->authorizeUser($userId)) {
|
||||
session()->flash('error', 'Unauthorized action.');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -108,10 +123,11 @@ public function confirmRegenerate(): void
|
|||
try {
|
||||
if (! $this->authorizeUser($this->regenerateUserId)) {
|
||||
session()->flash('error', 'Unauthorized action.');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$action = new DeleteScheduledUserDishForDateAction();
|
||||
$action = new DeleteScheduledUserDishForDateAction;
|
||||
$action->execute(
|
||||
auth()->user(),
|
||||
Carbon::parse($this->regenerateDate),
|
||||
|
|
@ -133,10 +149,11 @@ public function skipDay($date, $userId): void
|
|||
try {
|
||||
if (! $this->authorizeUser($userId)) {
|
||||
session()->flash('error', 'Unauthorized action.');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$action = new SkipScheduledUserDishForDateAction();
|
||||
$action = new SkipScheduledUserDishForDateAction;
|
||||
$action->execute(
|
||||
auth()->user(),
|
||||
Carbon::parse($date),
|
||||
|
|
@ -155,6 +172,7 @@ public function skipDay($date, $userId): void
|
|||
private function authorizeUser(int $userId): bool
|
||||
{
|
||||
$user = User::find($userId);
|
||||
|
||||
return $user && $user->planner_id === auth()->id();
|
||||
}
|
||||
|
||||
|
|
@ -181,6 +199,7 @@ public function removeDish($date, $userId): void
|
|||
try {
|
||||
if (! $this->authorizeUser($userId)) {
|
||||
session()->flash('error', 'Unauthorized action.');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -252,11 +271,13 @@ public function saveAddDish(): void
|
|||
try {
|
||||
if (empty($this->addUserIds)) {
|
||||
session()->flash('error', 'Please select at least one user.');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (! $this->addSelectedDishId) {
|
||||
session()->flash('error', 'Please select a dish.');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -275,6 +296,7 @@ public function saveAddDish(): void
|
|||
foreach ($this->addUserIds as $userId) {
|
||||
if (! $this->authorizeUser((int) $userId)) {
|
||||
$skippedCount++;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
@ -285,6 +307,7 @@ public function saveAddDish(): void
|
|||
|
||||
if ($existing) {
|
||||
$skippedCount++;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
@ -295,6 +318,7 @@ public function saveAddDish(): void
|
|||
|
||||
if (! $userDish) {
|
||||
$skippedCount++;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
@ -338,6 +362,7 @@ public function editDish($date, $userId): void
|
|||
{
|
||||
if (! $this->authorizeUser($userId)) {
|
||||
session()->flash('error', 'Unauthorized action.');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -372,11 +397,13 @@ public function saveDish(): void
|
|||
try {
|
||||
if (! $this->authorizeUser($this->editUserId)) {
|
||||
session()->flash('error', 'Unauthorized action.');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (! $this->selectedDishId) {
|
||||
session()->flash('error', 'Please select a dish.');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -396,6 +423,7 @@ public function saveDish(): void
|
|||
|
||||
if (! $userDish) {
|
||||
session()->flash('error', 'This dish is not assigned to this user.');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -427,7 +455,8 @@ public function saveDish(): void
|
|||
|
||||
public function getMonthNameProperty(): string
|
||||
{
|
||||
$service = new ScheduleCalendarService();
|
||||
$service = new ScheduleCalendarService;
|
||||
|
||||
return $service->getMonthName($this->currentMonth, $this->currentYear);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,19 +7,27 @@
|
|||
use DishPlanner\Schedule\Actions\ClearScheduleForMonthAction;
|
||||
use DishPlanner\Schedule\Actions\GenerateScheduleForMonthAction;
|
||||
use DishPlanner\Schedule\Actions\RegenerateScheduleForDateForUsersAction;
|
||||
use Illuminate\Contracts\View\Factory;
|
||||
use Illuminate\Contracts\View\View;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Livewire\Component;
|
||||
|
||||
class ScheduleGenerator extends Component
|
||||
{
|
||||
private const YEARS_IN_PAST = 1;
|
||||
|
||||
private const YEARS_IN_FUTURE = 5;
|
||||
|
||||
public $selectedMonth;
|
||||
|
||||
public $selectedYear;
|
||||
|
||||
public $selectedUsers = [];
|
||||
|
||||
public $clearExisting = true;
|
||||
|
||||
public $showAdvancedOptions = false;
|
||||
|
||||
public $isGenerating = false;
|
||||
|
||||
public function mount(): void
|
||||
|
|
@ -32,7 +40,7 @@ public function mount(): void
|
|||
->toArray();
|
||||
}
|
||||
|
||||
public function render(): \Illuminate\Contracts\View\Factory|\Illuminate\Contracts\View\View
|
||||
public function render(): Factory|View
|
||||
{
|
||||
$users = User::where('planner_id', auth()->id())
|
||||
->orderBy('name')
|
||||
|
|
@ -43,7 +51,7 @@ public function render(): \Illuminate\Contracts\View\Factory|\Illuminate\Contrac
|
|||
return view('livewire.schedule.schedule-generator', [
|
||||
'users' => $users,
|
||||
'months' => $this->getMonthNames(),
|
||||
'years' => $years
|
||||
'years' => $years,
|
||||
]);
|
||||
}
|
||||
|
||||
|
|
@ -58,7 +66,7 @@ public function generate(): void
|
|||
$this->isGenerating = true;
|
||||
|
||||
try {
|
||||
$action = new GenerateScheduleForMonthAction();
|
||||
$action = new GenerateScheduleForMonthAction;
|
||||
$action->execute(
|
||||
auth()->user(),
|
||||
$this->selectedMonth,
|
||||
|
|
@ -83,7 +91,7 @@ public function generate(): void
|
|||
public function regenerateForDate($date): void
|
||||
{
|
||||
try {
|
||||
$action = new RegenerateScheduleForDateForUsersAction();
|
||||
$action = new RegenerateScheduleForDateForUsersAction;
|
||||
$action->execute(
|
||||
auth()->user(),
|
||||
Carbon::parse($date),
|
||||
|
|
@ -102,7 +110,7 @@ public function regenerateForDate($date): void
|
|||
public function clearMonth(): void
|
||||
{
|
||||
try {
|
||||
$action = new ClearScheduleForMonthAction();
|
||||
$action = new ClearScheduleForMonthAction;
|
||||
$action->execute(
|
||||
auth()->user(),
|
||||
$this->selectedMonth,
|
||||
|
|
@ -130,7 +138,7 @@ private function getMonthNames(): array
|
|||
return [
|
||||
1 => 'January', 2 => 'February', 3 => 'March', 4 => 'April',
|
||||
5 => 'May', 6 => 'June', 7 => 'July', 8 => 'August',
|
||||
9 => 'September', 10 => 'October', 11 => 'November', 12 => 'December'
|
||||
9 => 'September', 10 => 'October', 11 => 'November', 12 => 'December',
|
||||
];
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,10 +2,10 @@
|
|||
|
||||
namespace App\Livewire\Users;
|
||||
|
||||
use App\Models\User;
|
||||
use App\Actions\User\CreateUserAction;
|
||||
use App\Actions\User\DeleteUserAction;
|
||||
use App\Actions\User\EditUserAction;
|
||||
use App\Models\User;
|
||||
use Exception;
|
||||
use Illuminate\Contracts\View\View;
|
||||
use Livewire\Component;
|
||||
|
|
@ -16,10 +16,13 @@ class UsersList extends Component
|
|||
use WithPagination;
|
||||
|
||||
public bool $showCreateModal = false;
|
||||
|
||||
public bool $showEditModal = false;
|
||||
|
||||
public bool $showDeleteModal = false;
|
||||
|
||||
public ?User $editingUser = null;
|
||||
|
||||
public ?User $deletingUser = null;
|
||||
|
||||
// Form fields
|
||||
|
|
@ -36,7 +39,7 @@ public function render(): View
|
|||
->paginate(10);
|
||||
|
||||
return view('livewire.users.users-list', [
|
||||
'users' => $users
|
||||
'users' => $users,
|
||||
]);
|
||||
}
|
||||
|
||||
|
|
@ -52,7 +55,7 @@ public function store(): void
|
|||
$this->validate();
|
||||
|
||||
try {
|
||||
(new CreateUserAction())->execute([
|
||||
(new CreateUserAction)->execute([
|
||||
'name' => $this->name,
|
||||
'planner_id' => auth()->id(),
|
||||
]);
|
||||
|
|
@ -79,7 +82,7 @@ public function update(): void
|
|||
$this->validate();
|
||||
|
||||
try {
|
||||
(new EditUserAction())->execute($this->editingUser, ['name' => $this->name]);
|
||||
(new EditUserAction)->execute($this->editingUser, ['name' => $this->name]);
|
||||
|
||||
$this->showEditModal = false;
|
||||
$this->reset(['name', 'editingUser']);
|
||||
|
|
@ -102,7 +105,7 @@ public function confirmDelete(User $user): void
|
|||
public function delete(): void
|
||||
{
|
||||
try {
|
||||
(new DeleteUserAction())->execute($this->deletingUser);
|
||||
(new DeleteUserAction)->execute($this->deletingUser);
|
||||
|
||||
$this->showDeleteModal = false;
|
||||
$this->deletingUser = null;
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@
|
|||
* @property Carbon $updated_at
|
||||
* @property Collection<User> $users
|
||||
* @property Collection<UserDish> $userDishes
|
||||
*
|
||||
* @method static create(array $data)
|
||||
* @method static findOrFail(int $dish_id)
|
||||
* @method static DishFactory factory($count = null, $state = [])
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@
|
|||
* @property int $id
|
||||
* @property static PlannerFactory factory($count = null, $state = [])
|
||||
* @property Collection<User> $users
|
||||
*
|
||||
* @method static first()
|
||||
* @method static create(array $array)
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -22,8 +22,9 @@
|
|||
* @property Dish $dish
|
||||
* @property User $user
|
||||
* @property Carbon $date
|
||||
* @property boolean $is_skipped
|
||||
* @property bool $is_skipped
|
||||
* @property Collection<ScheduledUserDish> $scheduledUserDishes
|
||||
*
|
||||
* @method static create(array $array)
|
||||
* @method static Builder where(array|Closure|Expression|string $column, mixed $operator = null, mixed $value = null, string $boolean = 'and')
|
||||
* @method static ScheduleFactory factory($count = null, $state = [])
|
||||
|
|
@ -38,6 +39,8 @@ class Schedule extends Model
|
|||
|
||||
public $timestamps = false;
|
||||
|
||||
protected $dateFormat = 'Y-m-d';
|
||||
|
||||
protected $fillable = ['planner_id', 'date', 'is_skipped'];
|
||||
|
||||
protected $casts = [
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@
|
|||
* @property int $user_dish_id
|
||||
* @property UserDish $userDish
|
||||
* @property bool $is_skipped
|
||||
*
|
||||
* @method static create(array $array)
|
||||
* @method static ScheduledUserDishFactory factory($count = null, $state = [])
|
||||
* @method static firstOrCreate(array $array, array $array1)
|
||||
|
|
@ -29,7 +30,7 @@ class ScheduledUserDish extends Model
|
|||
'schedule_id',
|
||||
'user_id',
|
||||
'user_dish_id',
|
||||
'is_skipped'
|
||||
'is_skipped',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
|
|
|
|||
|
|
@ -6,10 +6,10 @@
|
|||
use Database\Factories\UserFactory;
|
||||
use Illuminate\Database\Eloquent\Collection;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
use Illuminate\Database\Eloquent\Relations\HasManyThrough;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
/**
|
||||
* @property int $id
|
||||
|
|
@ -17,6 +17,7 @@
|
|||
* @property string $name
|
||||
* @property Collection<Dish> $dishes
|
||||
* @property Collection<UserDish> $userDishes
|
||||
*
|
||||
* @method static User findOrFail(int $user_id)
|
||||
* @method static UserFactory factory($count = null, $state = [])
|
||||
* @method static create(array $array)
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@
|
|||
* @method static UserDish|null find(int|null $user_dish_id)
|
||||
* @method static create(array $array)
|
||||
* @method static where(string $string, int $id)
|
||||
*
|
||||
* @property int $id
|
||||
* @property int $dish_id
|
||||
* @property int $user_id
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ public function getValue(): int
|
|||
return match ($this->recurrence_type) {
|
||||
WeeklyRecurrence::class => $this->recurrence->weekday->value,
|
||||
MinimumRecurrence::class => $this->recurrence->days,
|
||||
default => throw new InvalidRecurrenceTypeException()
|
||||
default => throw new InvalidRecurrenceTypeException
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,10 +12,11 @@
|
|||
|
||||
/**
|
||||
* @property int $weekday
|
||||
*
|
||||
* @method static create(array $array)
|
||||
* @method static WeeklyRecurrenceFactory factory($count = null, $state = [])
|
||||
*/
|
||||
class WeeklyRecurrence extends Model implements RecurrenceInterface, FixedRecurrenceInterface
|
||||
class WeeklyRecurrence extends Model implements FixedRecurrenceInterface, RecurrenceInterface
|
||||
{
|
||||
/** @use HasFactory<WeeklyRecurrenceFactory> */
|
||||
use HasFactory;
|
||||
|
|
|
|||
|
|
@ -16,7 +16,6 @@
|
|||
use Illuminate\Contracts\Debug\ExceptionHandler;
|
||||
use Illuminate\Foundation\Exceptions\Handler as BaseHandler;
|
||||
use Illuminate\Support\Facades\Gate;
|
||||
use Illuminate\Support\Facades\URL;
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
use Throwable;
|
||||
|
||||
|
|
@ -25,7 +24,8 @@ class AppServiceProvider extends ServiceProvider
|
|||
public function register(): void
|
||||
{
|
||||
$this->app->bind(ExceptionHandler::class, function ($app) {
|
||||
return new class($app) extends BaseHandler {
|
||||
return new class($app) extends BaseHandler
|
||||
{
|
||||
public function render($request, Throwable $e)
|
||||
{
|
||||
// Handle specific custom exception
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@
|
|||
|
||||
namespace App\Services;
|
||||
|
||||
|
||||
class OutputService
|
||||
{
|
||||
public function response(bool $success = true, ?array $payload = null, array|string|null $errors = null): array
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
<?php
|
||||
|
||||
use App\Providers\AppServiceProvider;
|
||||
|
||||
return [
|
||||
App\Providers\AppServiceProvider::class,
|
||||
AppServiceProvider::class,
|
||||
];
|
||||
|
|
|
|||
|
|
@ -17,12 +17,14 @@
|
|||
},
|
||||
"require-dev": {
|
||||
"fakerphp/faker": "^1.23",
|
||||
"larastan/larastan": "^3.10",
|
||||
"laravel/dusk": "^8.3",
|
||||
"laravel/pail": "^1.1",
|
||||
"laravel/pint": "^1.13",
|
||||
"laravel/sail": "^1.26",
|
||||
"mockery/mockery": "^1.6",
|
||||
"nunomaduro/collision": "^8.1",
|
||||
"phpstan/phpstan-mockery": "^2.0",
|
||||
"phpunit/phpunit": "^11.0.1"
|
||||
},
|
||||
"autoload": {
|
||||
|
|
|
|||
8923
composer.lock
generated
Normal file
8923
composer.lock
generated
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -1,5 +1,7 @@
|
|||
<?php
|
||||
|
||||
use App\Models\Planner;
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
|
|
@ -50,7 +52,7 @@
|
|||
'providers' => [
|
||||
'planners' => [
|
||||
'driver' => 'eloquent',
|
||||
'model' => App\Models\Planner::class,
|
||||
'model' => Planner::class,
|
||||
],
|
||||
|
||||
// 'users' => [
|
||||
|
|
|
|||
|
|
@ -1,5 +1,8 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Cookie\Middleware\EncryptCookies;
|
||||
use Illuminate\Foundation\Http\Middleware\ValidateCsrfToken;
|
||||
use Laravel\Sanctum\Http\Middleware\AuthenticateSession;
|
||||
use Laravel\Sanctum\Sanctum;
|
||||
|
||||
return [
|
||||
|
|
@ -75,9 +78,9 @@
|
|||
*/
|
||||
|
||||
'middleware' => [
|
||||
'authenticate_session' => Laravel\Sanctum\Http\Middleware\AuthenticateSession::class,
|
||||
'encrypt_cookies' => Illuminate\Cookie\Middleware\EncryptCookies::class,
|
||||
'validate_csrf_token' => Illuminate\Foundation\Http\Middleware\ValidateCsrfToken::class,
|
||||
'authenticate_session' => AuthenticateSession::class,
|
||||
'encrypt_cookies' => EncryptCookies::class,
|
||||
'validate_csrf_token' => ValidateCsrfToken::class,
|
||||
],
|
||||
|
||||
];
|
||||
|
|
|
|||
|
|
@ -3,8 +3,8 @@
|
|||
namespace Database\Factories;
|
||||
|
||||
use App\Models\Dish;
|
||||
use App\Models\UserDish;
|
||||
use App\Models\User;
|
||||
use App\Models\UserDish;
|
||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@
|
|||
|
||||
namespace Database\Seeders;
|
||||
|
||||
use App\Models\User;
|
||||
// use Illuminate\Database\Console\Seeds\WithoutModelEvents;
|
||||
use Illuminate\Database\Seeder;
|
||||
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ public function run(): void
|
|||
[
|
||||
'name' => 'Admin',
|
||||
'email' => 'admin@test.com',
|
||||
'password' => 'password'
|
||||
'password' => 'password',
|
||||
],
|
||||
])->each(fn (array $data) => Planner::create([
|
||||
'name' => $data['name'],
|
||||
|
|
|
|||
|
|
@ -41,11 +41,10 @@ private function createScheduleForPeriod(CarbonPeriod $period): void
|
|||
$planner = Planner::all()->first() ?? Planner::factory()->create();
|
||||
|
||||
collect($period)
|
||||
->each(fn (Carbon $date) =>
|
||||
User::query()
|
||||
->each(fn (Carbon $date) => User::query()
|
||||
->inRandomOrder()
|
||||
->get()
|
||||
->each(fn (User $user) => (new CreateScheduledUserDishAction())
|
||||
->each(fn (User $user) => (new CreateScheduledUserDishAction)
|
||||
->execute(
|
||||
planner: $planner,
|
||||
schedule: resolve(ScheduleRepository::class)->findOrCreate($planner, $date),
|
||||
|
|
|
|||
|
|
@ -16,7 +16,6 @@ public function run(): void
|
|||
->each(fn (string $name) => User::factory()->create([
|
||||
'planner_id' => $planner->id,
|
||||
'name' => $name,
|
||||
]))
|
||||
;
|
||||
]));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
44
docker/build/Dockerfile.ci
Normal file
44
docker/build/Dockerfile.ci
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
# CI image: PHP + Composer only, no runtime server or frontend toolchain.
|
||||
# Tests run against SQLite in memory (see .env.testing), so no database client
|
||||
# or cache extension is needed.
|
||||
#
|
||||
# Published as dishplanner-ci:php8.3-<revision>. Bump the revision in the tag
|
||||
# (.forgejo/workflows/images.yml) and in .forgejo/workflows/ci.yml whenever
|
||||
# this file changes (runners cache mutable tags and will not re-pull them).
|
||||
#
|
||||
# Debian-based rather than Alpine to avoid the DNS resolution timeouts against
|
||||
# codeload.github.com that the Alpine base hit during composer install.
|
||||
|
||||
FROM php:8.3-cli
|
||||
|
||||
COPY --from=mlocati/php-extension-installer:2 /usr/bin/install-php-extensions /usr/local/bin/
|
||||
|
||||
RUN install-php-extensions \
|
||||
pdo_sqlite \
|
||||
mbstring \
|
||||
dom \
|
||||
xml \
|
||||
fileinfo \
|
||||
pcntl \
|
||||
zip
|
||||
|
||||
# git is needed by the checkout action; nodejs runs the Forgejo JavaScript
|
||||
# actions (checkout, cache); unzip lets Composer extract dist archives.
|
||||
RUN apt-get update \
|
||||
&& apt-get install -y --no-install-recommends git unzip nodejs \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
COPY --from=composer:2 /usr/bin/composer /usr/bin/composer
|
||||
|
||||
# Bake the project's PHP dependencies (dev included) into the image so CI
|
||||
# restores them with a local copy instead of paying a per-run composer install
|
||||
# over the network. Build this image on a network that isn't
|
||||
# codeload-rate-limited (e.g. locally) — the Forgejo runner hits codeload 429
|
||||
# under --prefer-dist.
|
||||
#
|
||||
# --no-scripts skips `php artisan package:discover` (the app isn't present
|
||||
# here). CI runs `composer install` after restoring vendor, which regenerates
|
||||
# bootstrap/cache and tops up any lockfile drift between main and release/*.
|
||||
WORKDIR /opt/deps
|
||||
COPY composer.json composer.lock ./
|
||||
RUN composer install --no-interaction --no-progress --prefer-dist --no-scripts
|
||||
2839
phpstan-baseline.neon
Normal file
2839
phpstan-baseline.neon
Normal file
File diff suppressed because it is too large
Load diff
16
phpstan.neon
Normal file
16
phpstan.neon
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
includes:
|
||||
- vendor/larastan/larastan/extension.neon
|
||||
- vendor/phpstan/phpstan-mockery/extension.neon
|
||||
- phpstan-baseline.neon
|
||||
|
||||
parameters:
|
||||
level: 7
|
||||
paths:
|
||||
- app/
|
||||
- src/
|
||||
- tests/
|
||||
|
||||
excludePaths:
|
||||
- bootstrap/*.php
|
||||
- storage/*
|
||||
|
||||
15
phpunit.xml
15
phpunit.xml
|
|
@ -23,13 +23,6 @@
|
|||
<directory>app/Providers</directory>
|
||||
</exclude>
|
||||
</source>
|
||||
<coverage>
|
||||
<report>
|
||||
<html outputDirectory="coverage"/>
|
||||
<text outputFile="coverage/coverage.txt" showOnlySummary="true"/>
|
||||
<clover outputFile="coverage/clover.xml"/>
|
||||
</report>
|
||||
</coverage>
|
||||
<php>
|
||||
<env name="APP_ENV" value="testing"/>
|
||||
<env name="APP_MAINTENANCE_DRIVER" value="file"/>
|
||||
|
|
@ -40,12 +33,8 @@
|
|||
<env name="QUEUE_CONNECTION" value="sync"/>
|
||||
<env name="SESSION_DRIVER" value="array"/>
|
||||
<env name="TELESCOPE_ENABLED" value="false"/>
|
||||
<env name="DB_CONNECTION" value="mysql"/>
|
||||
<env name="DB_HOST" value="mysql"/>
|
||||
<env name="DB_PORT" value="3306"/>
|
||||
<env name="DB_DATABASE" value="testing"/>
|
||||
<env name="DB_USERNAME" value="sail"/>
|
||||
<env name="DB_PASSWORD" value="password"/>
|
||||
<env name="DB_CONNECTION" value="sqlite"/>
|
||||
<env name="DB_DATABASE" value=":memory:"/>
|
||||
|
||||
</php>
|
||||
</phpunit>
|
||||
|
|
|
|||
3
pint.json
Normal file
3
pint.json
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"preset": "laravel"
|
||||
}
|
||||
|
|
@ -18,4 +18,3 @@
|
|||
->json($request->user())
|
||||
)->name('me');
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Support\Facades\Route;
|
||||
use App\Http\Controllers\Auth\LoginController;
|
||||
use App\Http\Controllers\Auth\RegisterController;
|
||||
use Illuminate\Support\Facades\Route;
|
||||
|
||||
Route::get('/', function () {
|
||||
return redirect()->route('dashboard');
|
||||
|
|
|
|||
10
shell.nix
10
shell.nix
|
|
@ -68,6 +68,10 @@ pkgs.mkShell {
|
|||
podman-compose logs -f "$@"
|
||||
}
|
||||
|
||||
dev-logs-db() {
|
||||
podman-compose logs -f db "$@"
|
||||
}
|
||||
|
||||
dev-shell() {
|
||||
podman-compose exec app sh
|
||||
}
|
||||
|
|
@ -76,6 +80,10 @@ pkgs.mkShell {
|
|||
podman-compose exec app php artisan "$@"
|
||||
}
|
||||
|
||||
dev-test() {
|
||||
podman-compose exec -T app env $(grep -vE '^\s*(#|$)' .env.testing) php -d memory_limit=512M vendor/bin/phpunit "$@"
|
||||
}
|
||||
|
||||
dev-fix-permissions() {
|
||||
echo "🔧 Fixing file permissions..."
|
||||
echo "This will require sudo to fix Docker-created files"
|
||||
|
|
@ -149,8 +157,10 @@ pkgs.mkShell {
|
|||
echo " dev-rebuild - Full rebuild (removes volumes)"
|
||||
echo " dev-rebuild-quick - Quick rebuild (keeps volumes)"
|
||||
echo " dev-logs [svc] - Follow logs (default: all)"
|
||||
echo " dev-logs-db - Tail database logs"
|
||||
echo " dev-shell - Enter app container"
|
||||
echo " dev-artisan - Run artisan commands"
|
||||
echo " dev-test [path] - Run PHPUnit suite (CI invocation)"
|
||||
echo " dev-fix-permissions - Fix Docker-created file permissions"
|
||||
echo ""
|
||||
echo "Production commands:"
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ public function index(): JsonResponse
|
|||
|
||||
public function store(StoreDishRequest $request): JsonResponse
|
||||
{
|
||||
$dish = (new CreateDishAction())->execute($request->validated());
|
||||
$dish = (new CreateDishAction)->execute($request->validated());
|
||||
|
||||
return $this->success(['dish' => new DishResource($dish)]);
|
||||
}
|
||||
|
|
@ -47,7 +47,7 @@ public function update(UpdateDishRequest $request, Dish $dish): JsonResponse
|
|||
{
|
||||
Gate::authorize('update', $dish);
|
||||
|
||||
$dish = (new UpdateDishAction())->execute($dish, $request->validated());
|
||||
$dish = (new UpdateDishAction)->execute($dish, $request->validated());
|
||||
|
||||
return $this->success(['dish' => new DishResource($dish)]);
|
||||
}
|
||||
|
|
@ -56,14 +56,14 @@ public function destroy(Dish $dish): JsonResponse
|
|||
{
|
||||
Gate::authorize('delete', $dish);
|
||||
|
||||
(new DeleteDishAction())->execute($dish);
|
||||
(new DeleteDishAction)->execute($dish);
|
||||
|
||||
return $this->success(null);
|
||||
}
|
||||
|
||||
public function syncUsers(SyncUsersRequest $request, Dish $dish): JsonResponse
|
||||
{
|
||||
(new SyncUsersAction())->execute($dish, Arr::get($request->validated(), 'users', []));
|
||||
(new SyncUsersAction)->execute($dish, Arr::get($request->validated(), 'users', []));
|
||||
|
||||
return $this->success(['dish' => new DishResource($dish->refresh())]);
|
||||
}
|
||||
|
|
@ -72,14 +72,14 @@ public function addUsers(AddUsersToDishRequest $request, Dish $dish): JsonRespon
|
|||
{
|
||||
Gate::authorize('update', $dish);
|
||||
|
||||
(new AddUsersToDishAction())->execute($dish, Arr::get($request->validated(), 'users', []));
|
||||
(new AddUsersToDishAction)->execute($dish, Arr::get($request->validated(), 'users', []));
|
||||
|
||||
return $this->success(['dish' => new DishResource($dish->refresh())]);
|
||||
}
|
||||
|
||||
public function removeUsers(RemoveUsersFromDishRequest $request, Dish $dish): JsonResponse
|
||||
{
|
||||
(new RemoveUsersFromDishAction())->execute($dish, Arr::get($request->validated(), 'users', []));
|
||||
(new RemoveUsersFromDishAction)->execute($dish, Arr::get($request->validated(), 'users', []));
|
||||
|
||||
return $this->success(['dish' => new DishResource($dish->refresh())]);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,5 +7,6 @@
|
|||
class InvalidDishException extends CustomException
|
||||
{
|
||||
protected $message = 'INVALID_DISH';
|
||||
|
||||
protected $code = 422;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@
|
|||
use App\Models\ScheduledUserDish;
|
||||
use App\Models\User;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class GenerateScheduleForMonthAction
|
||||
|
|
|
|||
|
|
@ -5,8 +5,6 @@
|
|||
use App\Http\Controllers\Api\ApiController;
|
||||
use App\Models\Planner;
|
||||
use App\Models\Schedule;
|
||||
use Carbon\CarbonPeriod;
|
||||
use DishPlanner\Schedule\Actions\DraftScheduleForPeriodAction;
|
||||
use DishPlanner\Schedule\Actions\GenerateScheduleForPeriodAction;
|
||||
use DishPlanner\Schedule\Actions\UpdateScheduleAction;
|
||||
use DishPlanner\Schedule\Repositories\ScheduleRepository;
|
||||
|
|
@ -83,7 +81,7 @@ public function generate(GenerateScheduleRequest $request): JsonResponse
|
|||
/** @var Planner $planner */
|
||||
$planner = auth()->user();
|
||||
|
||||
(new GenerateScheduleForPeriodAction())->execute($planner, $request->get('overwrite', false));
|
||||
(new GenerateScheduleForPeriodAction)->execute($planner, $request->get('overwrite', false));
|
||||
|
||||
return $this->success(null);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ public function __invoke(ScheduleUserDishRequest $request, Carbon $date): JsonRe
|
|||
->first();
|
||||
|
||||
if (! $scheduledUserDish) {
|
||||
$scheduledUserDish = new ScheduledUserDish();
|
||||
$scheduledUserDish = new ScheduledUserDish;
|
||||
}
|
||||
|
||||
abort_if(
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ public function rules(): array
|
|||
'user_dish_id' => [
|
||||
'required_without:skipped',
|
||||
'exists:user_dishes,id',
|
||||
'nullable'
|
||||
'nullable',
|
||||
],
|
||||
'user_id' => ['required', 'exists:users,id'],
|
||||
'skipped' => ['required_if:user_dish_id,null', 'boolean'],
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
/**
|
||||
* @property boolean $is_skipped
|
||||
* @property bool $is_skipped
|
||||
*/
|
||||
class UpdateScheduleRequest extends FormRequest
|
||||
{
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ private function buildCalendarDays(int $year, int $month, int $daysInMonth, Coll
|
|||
'date' => $date,
|
||||
'isToday' => $date->isToday(),
|
||||
'scheduledDishes' => $scheduledDishes,
|
||||
'isEmpty' => $scheduledDishes->isEmpty()
|
||||
'isEmpty' => $scheduledDishes->isEmpty(),
|
||||
];
|
||||
} else {
|
||||
$calendarDays[] = [
|
||||
|
|
@ -51,7 +51,7 @@ private function buildCalendarDays(int $year, int $month, int $daysInMonth, Coll
|
|||
'date' => null,
|
||||
'isToday' => false,
|
||||
'scheduledDishes' => collect(),
|
||||
'isEmpty' => true
|
||||
'isEmpty' => true,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ public function generate(Planner $planner): void
|
|||
$users->each(function (User $user) use ($date, $planner, $scheduleRepository, $userDishRepository) {
|
||||
$schedule = $scheduleRepository->findOrCreate($planner, $date);
|
||||
|
||||
(new CreateScheduledUserDishAction())->execute(
|
||||
(new CreateScheduledUserDishAction)->execute(
|
||||
planner: $planner,
|
||||
schedule: $schedule,
|
||||
userDish: $userDishRepository->getRandomForDate($user, $date)
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ class CreateScheduledUserDishAction
|
|||
public function execute(Planner $planner, Schedule $schedule, UserDish $userDish): ScheduledUserDish
|
||||
{
|
||||
if ($userDish->dish->planner_id !== $planner->id || $userDish->user->planner_id !== $planner->id) {
|
||||
throw new InvalidPlannerException();
|
||||
throw new InvalidPlannerException;
|
||||
}
|
||||
|
||||
return ScheduledUserDish::create([
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ public function create(CreateScheduleRequest $request): JsonResponse
|
|||
$schedule = resolve(ScheduleRepository::class)->findOrCreate($planner, $date);
|
||||
|
||||
try {
|
||||
$scheduledUserDish = (new CreateScheduledUserDishAction())->execute(
|
||||
$scheduledUserDish = (new CreateScheduledUserDishAction)->execute(
|
||||
planner: $planner,
|
||||
schedule: $schedule,
|
||||
userDish: $userDish,
|
||||
|
|
@ -46,7 +46,7 @@ public function create(CreateScheduleRequest $request): JsonResponse
|
|||
}
|
||||
|
||||
return $this->success([
|
||||
'scheduled_user_dish' => new ScheduledUserDishResource($scheduledUserDish)
|
||||
'scheduled_user_dish' => new ScheduledUserDishResource($scheduledUserDish),
|
||||
]);
|
||||
}
|
||||
|
||||
|
|
@ -63,7 +63,7 @@ public function update(UpdateScheduledUserDishRequest $request, ScheduledUserDis
|
|||
{
|
||||
Gate::authorize('update', $scheduledUserDish);
|
||||
|
||||
(new UpdateScheduledUserDishAction())->execute(
|
||||
(new UpdateScheduledUserDishAction)->execute(
|
||||
scheduledUserDish: $scheduledUserDish,
|
||||
userDish: UserDish::find($request->user_dish_id),
|
||||
isSkipped: $request->is_skipped ?? null,
|
||||
|
|
@ -78,7 +78,7 @@ public function delete(ScheduledUserDish $scheduledUserDish): JsonResponse
|
|||
{
|
||||
Gate::authorize('delete', $scheduledUserDish);
|
||||
|
||||
(new DeleteScheduledUserDishAction())->execute($scheduledUserDish);
|
||||
(new DeleteScheduledUserDishAction)->execute($scheduledUserDish);
|
||||
|
||||
return $this->success(null);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@
|
|||
|
||||
use App\Models\Planner;
|
||||
use App\Models\ScheduledUserDish;
|
||||
use DishPlanner\UserDish\Policies\UserDishPolicy;
|
||||
use Illuminate\Support\Facades\Gate;
|
||||
|
||||
class ScheduledUserDishPolicy
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@
|
|||
|
||||
namespace DishPlanner\User\Actions;
|
||||
|
||||
use App\Models\Planner;
|
||||
use App\Models\User;
|
||||
|
||||
class DeleteUserAction
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@
|
|||
|
||||
namespace DishPlanner\User\Actions;
|
||||
|
||||
use App\Models\Planner;
|
||||
use App\Models\User;
|
||||
|
||||
class UpdateUserAction
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ public function create(CreateUserRequest $request): JsonResponse
|
|||
|
||||
$requestData = $request->validated();
|
||||
|
||||
$user = (new CreateUserAction())
|
||||
$user = (new CreateUserAction)
|
||||
->execute($planner, Arr::get($requestData, 'name'));
|
||||
|
||||
return $this->success(['user' => new UserResource($user)]);
|
||||
|
|
@ -43,7 +43,7 @@ public function update(UpdateUserRequest $request, User $user): JsonResponse
|
|||
{
|
||||
Gate::authorize('update', $user);
|
||||
|
||||
$user = (new UpdateUserAction())
|
||||
$user = (new UpdateUserAction)
|
||||
->execute($user, Arr::get($request->validated(), 'name'));
|
||||
|
||||
return $this->success(['user' => new UserResource($user)]);
|
||||
|
|
@ -53,7 +53,7 @@ public function delete(User $user): JsonResponse
|
|||
{
|
||||
Gate::authorize('delete', $user);
|
||||
|
||||
(new DeleteUserAction())->execute($user);
|
||||
(new DeleteUserAction)->execute($user);
|
||||
|
||||
return $this->success(null, 201);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ class CreateFixedRecurrenceAction
|
|||
public function execute(UserDish $userDish, string $recurrenceType, int $value): void
|
||||
{
|
||||
if (! in_array($recurrenceType, self::FIXED_RECURRENCES)) {
|
||||
throw new InvalidRecurrenceTypeException();
|
||||
throw new InvalidRecurrenceTypeException;
|
||||
}
|
||||
|
||||
$recurrence = $recurrenceType::create([
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ class CreateMinimumRecurrenceAction
|
|||
public function execute(UserDish $userDish, string $recurrenceType, int $recurrenceValue): void
|
||||
{
|
||||
if ($recurrenceType !== MinimumRecurrence::class) {
|
||||
throw new InvalidRecurrenceTypeException();
|
||||
throw new InvalidRecurrenceTypeException;
|
||||
}
|
||||
|
||||
$existingRecurrenceForDay = $userDish
|
||||
|
|
|
|||
|
|
@ -40,11 +40,11 @@ private function addRecurrences(UserDish $userDish, array $data): void
|
|||
}
|
||||
|
||||
if ($recurrenceType === WeeklyRecurrence::class) {
|
||||
(new CreateFixedRecurrenceAction())->execute($userDish, $recurrenceType, $recurrenceValue);
|
||||
(new CreateFixedRecurrenceAction)->execute($userDish, $recurrenceType, $recurrenceValue);
|
||||
} elseif ($recurrenceType === MinimumRecurrence::class) {
|
||||
(new CreateMinimumRecurrenceAction())->execute($userDish, $recurrenceType, $recurrenceValue);
|
||||
(new CreateMinimumRecurrenceAction)->execute($userDish, $recurrenceType, $recurrenceValue);
|
||||
} else {
|
||||
throw new InvalidRecurrenceTypeException();
|
||||
throw new InvalidRecurrenceTypeException;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ class DeleteFixedRecurrenceAction
|
|||
public function execute(RecurrenceInterface $recurrence): void
|
||||
{
|
||||
if (! $recurrence instanceof WeeklyRecurrence) {
|
||||
throw new InvalidRecurrenceTypeException();
|
||||
throw new InvalidRecurrenceTypeException;
|
||||
}
|
||||
|
||||
$recurrence->delete();
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ class DeleteMinimumRecurrenceAction
|
|||
public function execute(RecurrenceInterface $recurrence): void
|
||||
{
|
||||
if (! $recurrence instanceof MinimumRecurrence) {
|
||||
throw new InvalidRecurrenceTypeException();
|
||||
throw new InvalidRecurrenceTypeException;
|
||||
}
|
||||
|
||||
UserDishRecurrence::query()
|
||||
|
|
|
|||
|
|
@ -32,9 +32,9 @@ public function execute(UserDish $userDish, Collection $recurrences): UserDish
|
|||
}
|
||||
|
||||
match ($recurrenceType) {
|
||||
WeeklyRecurrence::class => (new CreateFixedRecurrenceAction())->execute($userDish, $recurrenceType, $recurrenceValue),
|
||||
MinimumRecurrence::class => (new CreateMinimumRecurrenceAction())->execute($userDish, $recurrenceType, $recurrenceValue),
|
||||
default => throw new InvalidRecurrenceTypeException(),
|
||||
WeeklyRecurrence::class => (new CreateFixedRecurrenceAction)->execute($userDish, $recurrenceType, $recurrenceValue),
|
||||
MinimumRecurrence::class => (new CreateMinimumRecurrenceAction)->execute($userDish, $recurrenceType, $recurrenceValue),
|
||||
default => throw new InvalidRecurrenceTypeException,
|
||||
};
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ class UpdateFixedRecurrenceAction
|
|||
public function execute(RecurrenceInterface $recurrence, array $data): RecurrenceInterface
|
||||
{
|
||||
if (! $recurrence instanceof WeeklyRecurrence) {
|
||||
throw new InvalidRecurrenceTypeException();
|
||||
throw new InvalidRecurrenceTypeException;
|
||||
}
|
||||
|
||||
$weekday = Arr::get($data, 'recurrence_data.weekday');
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ class UpdateMinimumRecurrenceAction
|
|||
public function execute(RecurrenceInterface $recurrence, array $data): RecurrenceInterface
|
||||
{
|
||||
if (! $recurrence instanceof MinimumRecurrence) {
|
||||
throw new InvalidRecurrenceTypeException();
|
||||
throw new InvalidRecurrenceTypeException;
|
||||
}
|
||||
|
||||
$days = Arr::get($data, 'recurrence_data.days');
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ public function __invoke(Request $request)
|
|||
$userDishes = $userDishRepository->getAllForPlanner($planner);
|
||||
|
||||
return $this->success([
|
||||
'user_dishes' => UserDishResource::collection($userDishes)->collection->toArray()
|
||||
'user_dishes' => UserDishResource::collection($userDishes)->collection->toArray(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ public function show(User $user, Dish $dish): JsonResponse
|
|||
*/
|
||||
public function store(CreateUserDishRequest $request, User $user, Dish $dish): JsonResponse
|
||||
{
|
||||
$userDish = (new CreateUserDishAction())->execute($dish, $user, $request->validated());
|
||||
$userDish = (new CreateUserDishAction)->execute($dish, $user, $request->validated());
|
||||
|
||||
return $this->success([
|
||||
'user_dish' => new UserDishResource($userDish),
|
||||
|
|
@ -57,7 +57,7 @@ public function store(CreateUserDishRequest $request, User $user, Dish $dish): J
|
|||
|
||||
public function destroy(User $user, Dish $dish): JsonResponse
|
||||
{
|
||||
(new DeleteUserDishAction())->execute($user, $dish);
|
||||
(new DeleteUserDishAction)->execute($user, $dish);
|
||||
|
||||
return $this->success(null);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ public function store(StoreUserDishRecurrenceRequest $request, User $user, Dish
|
|||
|
||||
$recurrences = collect($request->validated());
|
||||
|
||||
(new SyncRecurrencesForUserDishAction())->execute($userDish, $recurrences);
|
||||
(new SyncRecurrencesForUserDishAction)->execute($userDish, $recurrences);
|
||||
|
||||
return $this->success([
|
||||
'user_dish' => new UserDishResource($userDish->refresh()),
|
||||
|
|
@ -51,9 +51,9 @@ public function update(UpdateUserDishFixedRecurrenceRequest $request, UserDish $
|
|||
$recurrence = $recurrenceClass::findOrFail($recurrenceId);
|
||||
|
||||
if ($recurrence instanceof WeeklyRecurrence) {
|
||||
(new UpdateFixedRecurrenceAction())->execute($recurrence, $request->validated());
|
||||
(new UpdateFixedRecurrenceAction)->execute($recurrence, $request->validated());
|
||||
} elseif ($recurrenceClass === MinimumRecurrence::class) {
|
||||
(new UpdateMinimumRecurrenceAction())->execute($recurrence, $request->validated());
|
||||
(new UpdateMinimumRecurrenceAction)->execute($recurrence, $request->validated());
|
||||
} else {
|
||||
return $this->error('invalid recurrence type');
|
||||
}
|
||||
|
|
@ -72,9 +72,9 @@ public function destroy(UserDish $userDish, string $recurrenceType, int $recurre
|
|||
$recurrence = $recurrenceClass::findOrFail($recurrenceId);
|
||||
|
||||
if ($recurrence instanceof WeeklyRecurrence) {
|
||||
(new DeleteFixedRecurrenceAction())->execute($recurrence);
|
||||
(new DeleteFixedRecurrenceAction)->execute($recurrence);
|
||||
} elseif ($recurrenceClass === MinimumRecurrence::class) {
|
||||
(new DeleteMinimumRecurrenceAction())->execute($recurrence);
|
||||
(new DeleteMinimumRecurrenceAction)->execute($recurrence);
|
||||
} else {
|
||||
return $this->error('invalid recurrence type');
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,5 +2,4 @@
|
|||
|
||||
namespace DishPlanner\UserDish\Interfaces;
|
||||
|
||||
interface FixedRecurrenceInterface
|
||||
{}
|
||||
interface FixedRecurrenceInterface {}
|
||||
|
|
|
|||
|
|
@ -2,5 +2,4 @@
|
|||
|
||||
namespace DishPlanner\UserDish\Interfaces;
|
||||
|
||||
interface RecurrenceInterface
|
||||
{}
|
||||
interface RecurrenceInterface {}
|
||||
|
|
|
|||
|
|
@ -12,7 +12,6 @@
|
|||
use App\Models\WeeklyRecurrence;
|
||||
use Carbon\Carbon;
|
||||
use Carbon\CarbonPeriod;
|
||||
use Illuminate\Contracts\Auth\Authenticatable;
|
||||
use Illuminate\Database\Eloquent\Collection;
|
||||
use Illuminate\Support\Collection as SupportCollection;
|
||||
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ public function rules(): array
|
|||
MinimumRecurrence::class,
|
||||
WeeklyRecurrence::class,
|
||||
]),
|
||||
'required_with:*.recurrence_value'
|
||||
'required_with:*.recurrence_value',
|
||||
],
|
||||
'*.value' => ['sometimes', 'integer', 'required_with:*.recurrence_type'],
|
||||
];
|
||||
|
|
|
|||
|
|
@ -2,15 +2,17 @@
|
|||
|
||||
namespace Tests\Browser\Auth;
|
||||
|
||||
use Laravel\Dusk\Browser;
|
||||
use Tests\DuskTestCase;
|
||||
use App\Models\Planner;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use Laravel\Dusk\Browser;
|
||||
use Tests\DuskTestCase;
|
||||
|
||||
class LoginTest extends DuskTestCase
|
||||
{
|
||||
protected static $testPlanner = null;
|
||||
|
||||
protected static $testEmail = null;
|
||||
|
||||
protected static $testPassword = 'password';
|
||||
|
||||
protected function ensureTestPlannerExists(): void
|
||||
|
|
@ -26,7 +28,7 @@ protected function ensureTestPlannerExists(): void
|
|||
}
|
||||
}
|
||||
|
||||
public function testSuccessfulLogin(): void
|
||||
public function test_successful_login(): void
|
||||
{
|
||||
$this->ensureTestPlannerExists();
|
||||
|
||||
|
|
@ -44,7 +46,7 @@ public function testSuccessfulLogin(): void
|
|||
});
|
||||
}
|
||||
|
||||
public function testLoginWithWrongCredentials(): void
|
||||
public function test_login_with_wrong_credentials(): void
|
||||
{
|
||||
$this->ensureTestPlannerExists();
|
||||
|
||||
|
|
@ -63,7 +65,7 @@ public function testLoginWithWrongCredentials(): void
|
|||
});
|
||||
}
|
||||
|
||||
public function testLoginFormRequiredFields(): void
|
||||
public function test_login_form_required_fields(): void
|
||||
{
|
||||
$this->browse(function (Browser $browser) {
|
||||
$browser->driver->manage()->deleteAllCookies();
|
||||
|
|
|
|||
|
|
@ -3,16 +3,17 @@
|
|||
namespace Tests\Browser\Dishes;
|
||||
|
||||
use Laravel\Dusk\Browser;
|
||||
use Tests\DuskTestCase;
|
||||
use Tests\Browser\Pages\DishesPage;
|
||||
use Tests\Browser\Components\DishModal;
|
||||
use Tests\Browser\LoginHelpers;
|
||||
use Tests\Browser\Pages\DishesPage;
|
||||
use Tests\DuskTestCase;
|
||||
|
||||
class CreateDishFormValidationTest extends DuskTestCase
|
||||
{
|
||||
use LoginHelpers;
|
||||
|
||||
protected static $createDishFormValidationTestPlanner = null;
|
||||
|
||||
protected static $createDishFormValidationTestEmail = null;
|
||||
|
||||
protected function setUp(): void
|
||||
|
|
@ -31,7 +32,7 @@ protected function tearDown(): void
|
|||
parent::tearDown();
|
||||
}
|
||||
|
||||
public function testCreateDishFormValidation(): void
|
||||
public function test_create_dish_form_validation(): void
|
||||
{
|
||||
$this->browse(function (Browser $browser) {
|
||||
$this->loginAndGoToDishes($browser);
|
||||
|
|
|
|||
|
|
@ -3,16 +3,17 @@
|
|||
namespace Tests\Browser\Dishes;
|
||||
|
||||
use Laravel\Dusk\Browser;
|
||||
use Tests\DuskTestCase;
|
||||
use Tests\Browser\Pages\DishesPage;
|
||||
use Tests\Browser\Components\DishModal;
|
||||
use Tests\Browser\LoginHelpers;
|
||||
use Tests\Browser\Pages\DishesPage;
|
||||
use Tests\DuskTestCase;
|
||||
|
||||
class CreateDishSuccessTest extends DuskTestCase
|
||||
{
|
||||
use LoginHelpers;
|
||||
|
||||
protected static $createDishSuccessTestPlanner = null;
|
||||
|
||||
protected static $createDishSuccessTestEmail = null;
|
||||
|
||||
protected function setUp(): void
|
||||
|
|
@ -31,7 +32,7 @@ protected function tearDown(): void
|
|||
parent::tearDown();
|
||||
}
|
||||
|
||||
public function testCanCreateDishSuccessfully(): void
|
||||
public function test_can_create_dish_successfully(): void
|
||||
{
|
||||
$this->browse(function (Browser $browser) {
|
||||
$dishName = 'Test Dish '.uniqid();
|
||||
|
|
|
|||
|
|
@ -3,16 +3,16 @@
|
|||
namespace Tests\Browser\Dishes;
|
||||
|
||||
use Laravel\Dusk\Browser;
|
||||
use Tests\DuskTestCase;
|
||||
use Tests\Browser\Pages\DishesPage;
|
||||
use Tests\Browser\Components\DishModal;
|
||||
use Tests\Browser\LoginHelpers;
|
||||
use Tests\Browser\Pages\DishesPage;
|
||||
use Tests\DuskTestCase;
|
||||
|
||||
class CreateDishTest extends DuskTestCase
|
||||
{
|
||||
use LoginHelpers;
|
||||
|
||||
protected static $createDishTestPlanner = null;
|
||||
|
||||
protected static $createDishTestEmail = null;
|
||||
|
||||
protected function setUp(): void
|
||||
|
|
@ -31,7 +31,7 @@ protected function tearDown(): void
|
|||
parent::tearDown();
|
||||
}
|
||||
|
||||
public function testCanAccessDishesPage(): void
|
||||
public function test_can_access_dishes_page(): void
|
||||
{
|
||||
$this->browse(function (Browser $browser) {
|
||||
$this->loginAndGoToDishes($browser);
|
||||
|
|
|
|||
|
|
@ -2,16 +2,17 @@
|
|||
|
||||
namespace Tests\Browser\Dishes;
|
||||
|
||||
use Laravel\Dusk\Browser;
|
||||
use Tests\DuskTestCase;
|
||||
use Tests\Browser\LoginHelpers;
|
||||
use App\Models\Planner;
|
||||
use Laravel\Dusk\Browser;
|
||||
use Tests\Browser\LoginHelpers;
|
||||
use Tests\DuskTestCase;
|
||||
|
||||
class DeleteDishTest extends DuskTestCase
|
||||
{
|
||||
use LoginHelpers;
|
||||
|
||||
protected static $deleteDishTestPlanner = null;
|
||||
|
||||
protected static $deleteDishTestEmail = null;
|
||||
|
||||
protected function setUp(): void
|
||||
|
|
@ -30,7 +31,7 @@ protected function tearDown(): void
|
|||
parent::tearDown();
|
||||
}
|
||||
|
||||
public function testCanAccessDeleteFeature(): void
|
||||
public function test_can_access_delete_feature(): void
|
||||
{
|
||||
$this->browse(function (Browser $browser) {
|
||||
$this->loginAndGoToDishes($browser)
|
||||
|
|
|
|||
|
|
@ -3,14 +3,15 @@
|
|||
namespace Tests\Browser\Dishes;
|
||||
|
||||
use Laravel\Dusk\Browser;
|
||||
use Tests\DuskTestCase;
|
||||
use Tests\Browser\LoginHelpers;
|
||||
use Tests\DuskTestCase;
|
||||
|
||||
class DishDeletionSafetyTest extends DuskTestCase
|
||||
{
|
||||
use LoginHelpers;
|
||||
|
||||
protected static $dishDeletionSafetyTestPlanner = null;
|
||||
|
||||
protected static $dishDeletionSafetyTestEmail = null;
|
||||
|
||||
protected function setUp(): void
|
||||
|
|
@ -29,7 +30,7 @@ protected function tearDown(): void
|
|||
parent::tearDown();
|
||||
}
|
||||
|
||||
public function testDeletionSafetyFeatures(): void
|
||||
public function test_deletion_safety_features(): void
|
||||
{
|
||||
$this->browse(function (Browser $browser) {
|
||||
$this->loginAndGoToDishes($browser);
|
||||
|
|
|
|||
|
|
@ -2,16 +2,17 @@
|
|||
|
||||
namespace Tests\Browser\Dishes;
|
||||
|
||||
use Laravel\Dusk\Browser;
|
||||
use Tests\DuskTestCase;
|
||||
use Tests\Browser\LoginHelpers;
|
||||
use App\Models\Planner;
|
||||
use Laravel\Dusk\Browser;
|
||||
use Tests\Browser\LoginHelpers;
|
||||
use Tests\DuskTestCase;
|
||||
|
||||
class EditDishTest extends DuskTestCase
|
||||
{
|
||||
use LoginHelpers;
|
||||
|
||||
protected static $editDishTestPlanner = null;
|
||||
|
||||
protected static $editDishTestEmail = null;
|
||||
|
||||
protected function setUp(): void
|
||||
|
|
@ -30,7 +31,7 @@ protected function tearDown(): void
|
|||
parent::tearDown();
|
||||
}
|
||||
|
||||
public function testCanAccessEditFeature(): void
|
||||
public function test_can_access_edit_feature(): void
|
||||
{
|
||||
$this->browse(function (Browser $browser) {
|
||||
$this->loginAndGoToDishes($browser)
|
||||
|
|
@ -43,7 +44,7 @@ public function testCanAccessEditFeature(): void
|
|||
});
|
||||
}
|
||||
|
||||
public function testEditModalComponents(): void
|
||||
public function test_edit_modal_components(): void
|
||||
{
|
||||
$this->browse(function (Browser $browser) {
|
||||
$this->loginAndGoToDishes($browser)
|
||||
|
|
@ -52,7 +53,7 @@ public function testEditModalComponents(): void
|
|||
});
|
||||
}
|
||||
|
||||
public function testDishesPageStructure(): void
|
||||
public function test_dishes_page_structure(): void
|
||||
{
|
||||
$this->browse(function (Browser $browser) {
|
||||
$this->loginAndGoToDishes($browser)
|
||||
|
|
|
|||
|
|
@ -2,13 +2,17 @@
|
|||
|
||||
namespace Tests\Browser;
|
||||
|
||||
use App\Models\Planner;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use Laravel\Dusk\Browser;
|
||||
use Tests\DuskTestCase;
|
||||
|
||||
trait LoginHelpers
|
||||
{
|
||||
protected static $testPlanner = null;
|
||||
|
||||
protected static $testEmail = null;
|
||||
|
||||
protected static $testPassword = 'password';
|
||||
|
||||
protected function ensureTestPlannerExists(): void
|
||||
|
|
@ -18,9 +22,9 @@ protected function ensureTestPlannerExists(): void
|
|||
// Generate unique email for this test run
|
||||
self::$testEmail = fake()->unique()->safeEmail();
|
||||
|
||||
self::$testPlanner = \App\Models\Planner::factory()->create([
|
||||
self::$testPlanner = Planner::factory()->create([
|
||||
'email' => self::$testEmail,
|
||||
'password' => \Illuminate\Support\Facades\Hash::make(self::$testPassword),
|
||||
'password' => Hash::make(self::$testPassword),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -84,7 +84,7 @@ public function selectUser(Browser $browser, string $userName): void
|
|||
$browser->check("input[type='checkbox'][value]", $userName);
|
||||
}
|
||||
|
||||
public function assertSuccessMessage(Browser $browser, string $message = null): void
|
||||
public function assertSuccessMessage(Browser $browser, ?string $message = null): void
|
||||
{
|
||||
if ($message) {
|
||||
$browser->assertSee($message);
|
||||
|
|
|
|||
|
|
@ -2,9 +2,9 @@
|
|||
|
||||
namespace Tests\Browser;
|
||||
|
||||
use Illuminate\Foundation\Testing\DatabaseTransactions;
|
||||
use Laravel\Dusk\Browser;
|
||||
use Tests\DuskTestCase;
|
||||
use Illuminate\Foundation\Testing\DatabaseTransactions;
|
||||
|
||||
class RedirectTest extends DuskTestCase
|
||||
{
|
||||
|
|
@ -13,7 +13,7 @@ class RedirectTest extends DuskTestCase
|
|||
/**
|
||||
* Test that unauthenticated users are redirected to login
|
||||
*/
|
||||
public function testUnauthenticatedRedirectsToLogin()
|
||||
public function test_unauthenticated_redirects_to_login()
|
||||
{
|
||||
$this->browse(function (Browser $browser) {
|
||||
$browser->visit('http://dishplanner_app:8000/dashboard')
|
||||
|
|
@ -25,7 +25,7 @@ public function testUnauthenticatedRedirectsToLogin()
|
|||
/**
|
||||
* Test that login page loads correctly
|
||||
*/
|
||||
public function testLoginPageLoads()
|
||||
public function test_login_page_loads()
|
||||
{
|
||||
$this->browse(function (Browser $browser) {
|
||||
$browser->visit('http://dishplanner_app:8000/login')
|
||||
|
|
|
|||
|
|
@ -7,15 +7,19 @@
|
|||
use App\Models\User;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use Laravel\Dusk\Browser;
|
||||
use Tests\DuskTestCase;
|
||||
use Tests\Browser\Pages\SchedulePage;
|
||||
use Tests\DuskTestCase;
|
||||
|
||||
class GenerateScheduleTest extends DuskTestCase
|
||||
{
|
||||
protected static $planner = null;
|
||||
|
||||
protected static $email = null;
|
||||
|
||||
protected static $password = 'password';
|
||||
|
||||
protected static $user = null;
|
||||
|
||||
protected static $dish = null;
|
||||
|
||||
protected function setUp(): void
|
||||
|
|
@ -64,7 +68,7 @@ protected function loginAsPlanner(Browser $browser): Browser
|
|||
->pause(DuskTestCase::PAUSE_MEDIUM);
|
||||
}
|
||||
|
||||
public function testCanGenerateScheduleWithUserAndDish(): void
|
||||
public function test_can_generate_schedule_with_user_and_dish(): void
|
||||
{
|
||||
$this->browse(function (Browser $browser) {
|
||||
$this->loginAsPlanner($browser);
|
||||
|
|
@ -78,7 +82,7 @@ public function testCanGenerateScheduleWithUserAndDish(): void
|
|||
});
|
||||
}
|
||||
|
||||
public function testGeneratedScheduleShowsDishOnCalendar(): void
|
||||
public function test_generated_schedule_shows_dish_on_calendar(): void
|
||||
{
|
||||
$this->browse(function (Browser $browser) {
|
||||
$this->loginAsPlanner($browser);
|
||||
|
|
@ -91,7 +95,7 @@ public function testGeneratedScheduleShowsDishOnCalendar(): void
|
|||
});
|
||||
}
|
||||
|
||||
public function testCanClearMonthSchedule(): void
|
||||
public function test_can_clear_month_schedule(): void
|
||||
{
|
||||
$this->browse(function (Browser $browser) {
|
||||
$this->loginAsPlanner($browser);
|
||||
|
|
@ -109,7 +113,7 @@ public function testCanClearMonthSchedule(): void
|
|||
});
|
||||
}
|
||||
|
||||
public function testUserSelectionAffectsGeneration(): void
|
||||
public function test_user_selection_affects_generation(): void
|
||||
{
|
||||
$this->browse(function (Browser $browser) {
|
||||
$this->loginAsPlanner($browser);
|
||||
|
|
|
|||
|
|
@ -3,15 +3,16 @@
|
|||
namespace Tests\Browser\Schedule;
|
||||
|
||||
use Laravel\Dusk\Browser;
|
||||
use Tests\DuskTestCase;
|
||||
use Tests\Browser\Pages\SchedulePage;
|
||||
use Tests\Browser\LoginHelpers;
|
||||
use Tests\Browser\Pages\SchedulePage;
|
||||
use Tests\DuskTestCase;
|
||||
|
||||
class SchedulePageTest extends DuskTestCase
|
||||
{
|
||||
use LoginHelpers;
|
||||
|
||||
protected static $schedulePageTestPlanner = null;
|
||||
|
||||
protected static $schedulePageTestEmail = null;
|
||||
|
||||
protected function setUp(): void
|
||||
|
|
@ -28,7 +29,7 @@ protected function tearDown(): void
|
|||
parent::tearDown();
|
||||
}
|
||||
|
||||
public function testCanAccessSchedulePage(): void
|
||||
public function test_can_access_schedule_page(): void
|
||||
{
|
||||
$this->browse(function (Browser $browser) {
|
||||
$this->loginAndGoToSchedule($browser);
|
||||
|
|
@ -39,7 +40,7 @@ public function testCanAccessSchedulePage(): void
|
|||
});
|
||||
}
|
||||
|
||||
public function testSchedulePageHasMonthNavigation(): void
|
||||
public function test_schedule_page_has_month_navigation(): void
|
||||
{
|
||||
$this->browse(function (Browser $browser) {
|
||||
$this->loginAndGoToSchedule($browser);
|
||||
|
|
@ -51,7 +52,7 @@ public function testSchedulePageHasMonthNavigation(): void
|
|||
});
|
||||
}
|
||||
|
||||
public function testCanNavigateToNextMonth(): void
|
||||
public function test_can_navigate_to_next_month(): void
|
||||
{
|
||||
$this->browse(function (Browser $browser) {
|
||||
$this->loginAndGoToSchedule($browser);
|
||||
|
|
@ -64,7 +65,7 @@ public function testCanNavigateToNextMonth(): void
|
|||
});
|
||||
}
|
||||
|
||||
public function testCanNavigateToPreviousMonth(): void
|
||||
public function test_can_navigate_to_previous_month(): void
|
||||
{
|
||||
$this->browse(function (Browser $browser) {
|
||||
$this->loginAndGoToSchedule($browser);
|
||||
|
|
@ -77,7 +78,7 @@ public function testCanNavigateToPreviousMonth(): void
|
|||
});
|
||||
}
|
||||
|
||||
public function testScheduleGeneratorShowsUserSelection(): void
|
||||
public function test_schedule_generator_shows_user_selection(): void
|
||||
{
|
||||
$this->browse(function (Browser $browser) {
|
||||
$this->loginAndGoToSchedule($browser);
|
||||
|
|
@ -89,7 +90,7 @@ public function testScheduleGeneratorShowsUserSelection(): void
|
|||
});
|
||||
}
|
||||
|
||||
public function testCalendarDisplaysDaysOfWeek(): void
|
||||
public function test_calendar_displays_days_of_week(): void
|
||||
{
|
||||
$this->browse(function (Browser $browser) {
|
||||
$this->loginAndGoToSchedule($browser);
|
||||
|
|
|
|||
|
|
@ -3,15 +3,16 @@
|
|||
namespace Tests\Browser\Users;
|
||||
|
||||
use Laravel\Dusk\Browser;
|
||||
use Tests\DuskTestCase;
|
||||
use Tests\Browser\Pages\UsersPage;
|
||||
use Tests\Browser\LoginHelpers;
|
||||
use Tests\Browser\Pages\UsersPage;
|
||||
use Tests\DuskTestCase;
|
||||
|
||||
class CreateUserTest extends DuskTestCase
|
||||
{
|
||||
use LoginHelpers;
|
||||
|
||||
protected static $createUserTestPlanner = null;
|
||||
|
||||
protected static $createUserTestEmail = null;
|
||||
|
||||
protected function setUp(): void
|
||||
|
|
@ -30,7 +31,7 @@ protected function tearDown(): void
|
|||
parent::tearDown();
|
||||
}
|
||||
|
||||
public function testCanAccessUsersPage(): void
|
||||
public function test_can_access_users_page(): void
|
||||
{
|
||||
$this->browse(function (Browser $browser) {
|
||||
$this->loginAndGoToUsers($browser);
|
||||
|
|
@ -41,7 +42,7 @@ public function testCanAccessUsersPage(): void
|
|||
});
|
||||
}
|
||||
|
||||
public function testCanOpenCreateUserModal(): void
|
||||
public function test_can_open_create_user_modal(): void
|
||||
{
|
||||
$this->browse(function (Browser $browser) {
|
||||
$this->loginAndGoToUsers($browser);
|
||||
|
|
@ -55,7 +56,7 @@ public function testCanOpenCreateUserModal(): void
|
|||
});
|
||||
}
|
||||
|
||||
public function testCreateUserFormValidation(): void
|
||||
public function test_create_user_form_validation(): void
|
||||
{
|
||||
$this->browse(function (Browser $browser) {
|
||||
$this->loginAndGoToUsers($browser);
|
||||
|
|
@ -68,7 +69,7 @@ public function testCreateUserFormValidation(): void
|
|||
});
|
||||
}
|
||||
|
||||
public function testCanCreateUser(): void
|
||||
public function test_can_create_user(): void
|
||||
{
|
||||
$this->browse(function (Browser $browser) {
|
||||
$userName = 'TestCreate_'.uniqid();
|
||||
|
|
@ -85,7 +86,7 @@ public function testCanCreateUser(): void
|
|||
});
|
||||
}
|
||||
|
||||
public function testCanCancelUserCreation(): void
|
||||
public function test_can_cancel_user_creation(): void
|
||||
{
|
||||
$this->browse(function (Browser $browser) {
|
||||
$this->loginAndGoToUsers($browser);
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@
|
|||
use Facebook\WebDriver\Chrome\ChromeOptions;
|
||||
use Facebook\WebDriver\Remote\DesiredCapabilities;
|
||||
use Facebook\WebDriver\Remote\RemoteWebDriver;
|
||||
use Illuminate\Support\Collection;
|
||||
use Laravel\Dusk\TestCase as BaseTestCase;
|
||||
use PHPUnit\Framework\Attributes\BeforeClass;
|
||||
|
||||
|
|
@ -13,8 +12,11 @@ abstract class DuskTestCase extends BaseTestCase
|
|||
{
|
||||
// Timeout constants for consistent timing across all Dusk tests
|
||||
public const TIMEOUT_SHORT = 2; // 2 seconds max for most operations
|
||||
|
||||
public const TIMEOUT_MEDIUM = 3; // 3 seconds for slower operations
|
||||
|
||||
public const PAUSE_SHORT = 500; // 0.5 seconds for quick pauses
|
||||
|
||||
public const PAUSE_MEDIUM = 1000; // 1 second for medium pauses
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -2,10 +2,10 @@
|
|||
|
||||
namespace Tests\Feature;
|
||||
|
||||
use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken;
|
||||
use Tests\TestCase;
|
||||
use App\Models\Planner;
|
||||
use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Tests\TestCase;
|
||||
|
||||
class AuthenticationTest extends TestCase
|
||||
{
|
||||
|
|
@ -61,10 +61,6 @@ public function test_session_is_created_on_login_page(): void
|
|||
// Check if CSRF token is generated
|
||||
$this->assertNotNull(csrf_token());
|
||||
|
||||
// Check session driver
|
||||
$sessionDriver = config('session.driver');
|
||||
$this->assertNotEquals('array', $sessionDriver, 'Session driver should not be array for authentication');
|
||||
|
||||
$response->assertStatus(200);
|
||||
$response->assertSessionHasNoErrors();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@
|
|||
namespace Tests\Feature\Dish;
|
||||
|
||||
use App\Models\Dish;
|
||||
use App\Models\Planner;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Illuminate\Support\Str;
|
||||
use Illuminate\Testing\Fluent\AssertableJson;
|
||||
|
|
|
|||
|
|
@ -64,7 +64,6 @@ public function test_it_deletes_user_dishes_when_deleting_a_dish(): void
|
|||
$this->assertDatabaseEmpty(UserDish::class);
|
||||
}
|
||||
|
||||
|
||||
public function test_planner_cannot_delete_dish_from_other_planner(): void
|
||||
{
|
||||
$planner = $this->planner;
|
||||
|
|
|
|||
|
|
@ -2,9 +2,9 @@
|
|||
|
||||
namespace Tests\Feature;
|
||||
|
||||
use Tests\TestCase;
|
||||
use App\Models\Planner;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Tests\TestCase;
|
||||
|
||||
class RegistrationTest extends TestCase
|
||||
{
|
||||
|
|
|
|||
|
|
@ -3,12 +3,11 @@
|
|||
namespace Tests\Feature\Schedule;
|
||||
|
||||
use App\Models\Dish;
|
||||
use App\Models\Planner;
|
||||
use App\Models\UserDish;
|
||||
use App\Models\UserDishRecurrence;
|
||||
use App\Models\Schedule;
|
||||
use App\Models\ScheduledUserDish;
|
||||
use App\Models\User;
|
||||
use App\Models\UserDish;
|
||||
use App\Models\UserDishRecurrence;
|
||||
use App\Models\WeeklyRecurrence;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Illuminate\Support\Carbon;
|
||||
|
|
@ -115,6 +114,7 @@ public function test_fresh_schedule_adheres_to_fixed_recurrences(): void
|
|||
|
||||
$this->assertContains($targetUserDish->id, $targetScheduledUserDishes);
|
||||
}
|
||||
|
||||
public function test_schedule_can_be_overwritten(): void
|
||||
{
|
||||
$planner = $this->planner;
|
||||
|
|
@ -132,8 +132,7 @@ public function test_schedule_can_be_overwritten(): void
|
|||
});
|
||||
|
||||
// Assert that every user has `UserDish` records
|
||||
$users->each(fn (User $user) =>
|
||||
$this->assertNotEmpty($user->refresh()->userDishes)
|
||||
$users->each(fn (User $user) => $this->assertNotEmpty($user->refresh()->userDishes)
|
||||
);
|
||||
|
||||
$scheduleDay = Schedule::factory()
|
||||
|
|
@ -172,7 +171,7 @@ public function test_schedule_can_be_overwritten(): void
|
|||
|
||||
$this->assertDatabaseCount(Schedule::class, 14);
|
||||
|
||||
$freshScheduleDay = Schedule::query()->where('date', $scheduleDay->date)->first();
|
||||
$freshScheduleDay = Schedule::query()->whereDate('date', $scheduleDay->date)->first();
|
||||
$this->assertNotEquals(
|
||||
$originalUserDishes,
|
||||
$freshScheduleDay->scheduledUserDishes->map(fn (ScheduledUserDish $scheduledUserDish) => [
|
||||
|
|
@ -235,7 +234,6 @@ public function test_fixed_recurrence_takes_precedence_during_overwrite(): void
|
|||
$this->assertDatabaseCount(Schedule::class, 1);
|
||||
$this->assertDatabaseCount(ScheduledUserDish::class, 2);
|
||||
|
||||
|
||||
$this
|
||||
->actingAs($planner)
|
||||
->post(route('api.schedule.generate'), [
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@ public function test_full_calendar_dishes_list_for_a_given_date_range(): void
|
|||
$schedule = Schedule::factory()->planner($planner)->date($date)->create();
|
||||
$users->each(function (User $user) use ($schedule) {
|
||||
$randomUserDish = $user->userDishes->random();
|
||||
|
||||
return $schedule->scheduledUserDishes()->create([
|
||||
'user_dish_id' => $randomUserDish->id,
|
||||
'user_id' => $randomUserDish->user->id,
|
||||
|
|
@ -80,6 +81,7 @@ public function test_it_does_not_show_dishes_of_other_planner(): void
|
|||
$schedule = Schedule::factory()->planner($otherPlanner)->date($date)->create();
|
||||
$users->each(function (User $user) use ($schedule) {
|
||||
$randomUserDish = $user->userDishes->random();
|
||||
|
||||
return $schedule->scheduledUserDishes()->create([
|
||||
'user_dish_id' => $randomUserDish->id,
|
||||
'user_id' => $randomUserDish->user->id,
|
||||
|
|
|
|||
|
|
@ -33,11 +33,11 @@ public function test_user_can_mark_day_as_skipped(): void
|
|||
$dishes->each(fn (Dish $dish) => $dish->users()->attach($users));
|
||||
ScheduledUserDish::factory()
|
||||
->schedule($schedule)
|
||||
->userDish($dishes->random()->userDishes->random())
|
||||
->userDish($userOne->userDishes->firstOrFail())
|
||||
->create([]);
|
||||
ScheduledUserDish::factory()
|
||||
->schedule($schedule)
|
||||
->userDish($dishes->random()->userDishes->random())
|
||||
->userDish($userTwo->userDishes->firstOrFail())
|
||||
->create([]);
|
||||
|
||||
$schedule->refresh();
|
||||
|
|
|
|||
|
|
@ -75,7 +75,6 @@ public function test_planner_can_schedule_user_dishes(): void
|
|||
->where('errors', null)
|
||||
);
|
||||
|
||||
|
||||
$this->assertDatabaseCount(Schedule::class, 1);
|
||||
$this->assertDatabaseHas(Schedule::class, [
|
||||
'date' => $scheduleDate,
|
||||
|
|
@ -116,11 +115,10 @@ public function test_planner_cannot_schedule_user_dishes_from_other_planner(): v
|
|||
->where('success', false)
|
||||
->whereNull('payload')
|
||||
->where('errors', [
|
||||
"This action is unauthorized."
|
||||
'This action is unauthorized.',
|
||||
])
|
||||
);
|
||||
|
||||
|
||||
$this->assertDatabaseEmpty(Schedule::class);
|
||||
$this->assertDatabaseEmpty(ScheduledUserDish::class);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -79,7 +79,7 @@ public function test_planner_cannot_delete_a_scheduled_dish_of_another_planner()
|
|||
->where('success', false)
|
||||
->where('payload', null)
|
||||
->where('errors', [
|
||||
"This action is unauthorized."
|
||||
'This action is unauthorized.',
|
||||
])
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -129,7 +129,7 @@ public function test_planner_cannot_read_scheduled_user_dish_from_other_planner(
|
|||
->where('success', false)
|
||||
->where('payload', null)
|
||||
->where('errors', [
|
||||
"This action is unauthorized."
|
||||
'This action is unauthorized.',
|
||||
])
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,9 +16,9 @@
|
|||
|
||||
class UpdateScheduledUserDishTest extends TestCase
|
||||
{
|
||||
use DishesTestTrait;
|
||||
use HasPlanner;
|
||||
use RefreshDatabase;
|
||||
use DishesTestTrait;
|
||||
use ScheduledDishesTestTrait;
|
||||
|
||||
protected function setUp(): void
|
||||
|
|
@ -126,7 +126,7 @@ public function test_planner_cannot_update_dish_of_other_planner(): void
|
|||
->where('success', false)
|
||||
->where('payload', null)
|
||||
->where('errors', [
|
||||
"This action is unauthorized."
|
||||
'This action is unauthorized.',
|
||||
])
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@
|
|||
|
||||
namespace Tests\Feature\User;
|
||||
|
||||
use App\Models\Planner;
|
||||
use App\Models\User;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Illuminate\Testing\Fluent\AssertableJson;
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue