From 1ed5d7362bef3b6eefcd290a7ec850e11e40531f Mon Sep 17 00:00:00 2001 From: Saeed Vaziry Date: Sun, 18 May 2025 21:12:06 +0200 Subject: [PATCH] #591 - server-providers --- .../Controllers/ServerProviderController.php | 40 +++- resources/js/components/form-successful.tsx | 10 + resources/js/components/ui/badge.tsx | 2 +- resources/js/layouts/settings/layout.tsx | 7 +- .../profile/components/update-password.tsx | 8 +- .../{update-user.tsx => update-profile.tsx} | 10 +- resources/js/pages/profile/index.tsx | 4 +- .../projects/components/project-form.tsx | 16 +- .../server-providers/components/columns.tsx | 185 ++++++++++++++++++ ...ovider.tsx => connect-server-provider.tsx} | 26 +-- resources/js/pages/server-providers/index.tsx | 41 ++++ .../servers/components/create-server.tsx | 21 +- .../js/pages/users/components/actions.tsx | 2 +- .../components/{form.tsx => user-form.tsx} | 14 +- resources/js/pages/users/index.tsx | 2 +- resources/js/types/server-provider.d.ts | 3 +- tests/Feature/ServerProvidersTest.php | 47 +++-- tests/Feature/UserTest.php | 4 +- 18 files changed, 349 insertions(+), 93 deletions(-) create mode 100644 resources/js/components/form-successful.tsx rename resources/js/pages/profile/components/{update-user.tsx => update-profile.tsx} (86%) create mode 100644 resources/js/pages/server-providers/components/columns.tsx rename resources/js/pages/server-providers/components/{create-server-provider.tsx => connect-server-provider.tsx} (87%) create mode 100644 resources/js/pages/server-providers/index.tsx rename resources/js/pages/users/components/{form.tsx => user-form.tsx} (90%) diff --git a/app/Http/Controllers/ServerProviderController.php b/app/Http/Controllers/ServerProviderController.php index 5cdf1248..aeab46dc 100644 --- a/app/Http/Controllers/ServerProviderController.php +++ b/app/Http/Controllers/ServerProviderController.php @@ -3,14 +3,20 @@ namespace App\Http\Controllers; use App\Actions\ServerProvider\CreateServerProvider; +use App\Actions\ServerProvider\DeleteServerProvider; +use App\Actions\ServerProvider\EditServerProvider; use App\Http\Resources\ServerProviderResource; use App\Models\ServerProvider; use Illuminate\Http\JsonResponse; use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; use Illuminate\Http\Resources\Json\ResourceCollection; +use Inertia\Inertia; +use Inertia\Response; +use Spatie\RouteAttributes\Attributes\Delete; use Spatie\RouteAttributes\Attributes\Get; use Spatie\RouteAttributes\Attributes\Middleware; +use Spatie\RouteAttributes\Attributes\Patch; use Spatie\RouteAttributes\Attributes\Post; use Spatie\RouteAttributes\Attributes\Prefix; @@ -18,10 +24,18 @@ #[Middleware(['auth'])] class ServerProviderController extends Controller { - public function index(): void {} + #[Get('/', name: 'server-providers')] + public function index(): Response + { + $this->authorize('viewAny', ServerProvider::class); - #[Get('/', name: 'server-providers.all')] - public function all(): ResourceCollection + return Inertia::render('server-providers/index', [ + 'serverProviders' => ServerProviderResource::collection(ServerProvider::getByProjectId(user()->current_project_id)->simplePaginate(config('web.pagination_size'))), + ]); + } + + #[Get('/json', name: 'server-providers.json')] + public function json(): ResourceCollection { $this->authorize('viewAny', ServerProvider::class); @@ -38,6 +52,16 @@ public function store(Request $request): RedirectResponse return back()->with('success', 'Server provider created.'); } + #[Patch('/{serverProvider}', name: 'server-providers.update')] + public function update(Request $request, ServerProvider $serverProvider): RedirectResponse + { + $this->authorize('update', $serverProvider); + + app(EditServerProvider::class)->edit($serverProvider, user()->currentProject, $request->all()); + + return back()->with('success', 'Server provider updated.'); + } + #[Get('/{serverProvider}/regions', name: 'server-providers.regions')] public function regions(ServerProvider $serverProvider): JsonResponse { @@ -53,4 +77,14 @@ public function plans(ServerProvider $serverProvider, string $region): JsonRespo return response()->json($serverProvider->provider()->plans($region)); } + + #[Delete('{serverProvider}', name: 'server-providers.destroy')] + public function destroy(ServerProvider $serverProvider): RedirectResponse + { + $this->authorize('delete', $serverProvider); + + app(DeleteServerProvider::class)->delete($serverProvider); + + return to_route('server-providers')->with('success', 'Server provider deleted.'); + } } diff --git a/resources/js/components/form-successful.tsx b/resources/js/components/form-successful.tsx new file mode 100644 index 00000000..dc07b9e5 --- /dev/null +++ b/resources/js/components/form-successful.tsx @@ -0,0 +1,10 @@ +import { CheckIcon } from 'lucide-react'; +import { Transition } from '@headlessui/react'; + +export default function FormSuccessful({ successful }: { successful: boolean }) { + return ( + + + + ); +} diff --git a/resources/js/components/ui/badge.tsx b/resources/js/components/ui/badge.tsx index 255f081c..640a060e 100644 --- a/resources/js/components/ui/badge.tsx +++ b/resources/js/components/ui/badge.tsx @@ -5,7 +5,7 @@ import { cva, type VariantProps } from 'class-variance-authority'; import { cn } from '@/lib/utils'; const badgeVariants = cva( - 'inline-flex items-center justify-center rounded-md border px-2 py-0.5 text-xs font-medium w-fit whitespace-nowrap shrink-0 [&>svg]:size-3 gap-1 [&>svg]:pointer-events-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive transition-[color,box-shadow] overflow-auto', + 'uppercase inline-flex items-center justify-center rounded-md border px-2 py-0.5 text-xs font-medium w-fit whitespace-nowrap shrink-0 [&>svg]:size-3 gap-1 [&>svg]:pointer-events-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive transition-[color,box-shadow] overflow-auto', { variants: { variant: { diff --git a/resources/js/layouts/settings/layout.tsx b/resources/js/layouts/settings/layout.tsx index 94b47ea3..65d4997a 100644 --- a/resources/js/layouts/settings/layout.tsx +++ b/resources/js/layouts/settings/layout.tsx @@ -1,5 +1,5 @@ import { type BreadcrumbItem, type NavItem } from '@/types'; -import { ListIcon, UserIcon, UsersIcon } from 'lucide-react'; +import { CloudIcon, ListIcon, UserIcon, UsersIcon } from 'lucide-react'; import { ReactNode } from 'react'; import Layout from '@/layouts/app/layout'; @@ -19,6 +19,11 @@ const sidebarNavItems: NavItem[] = [ href: route('projects'), icon: ListIcon, }, + { + title: 'Server Providers', + href: route('server-providers'), + icon: CloudIcon, + }, ]; export default function SettingsLayout({ children, breadcrumbs }: { children: ReactNode; breadcrumbs?: BreadcrumbItem[] }) { diff --git a/resources/js/pages/profile/components/update-password.tsx b/resources/js/pages/profile/components/update-password.tsx index b592f49f..11ec4c90 100644 --- a/resources/js/pages/profile/components/update-password.tsx +++ b/resources/js/pages/profile/components/update-password.tsx @@ -1,5 +1,4 @@ import InputError from '@/components/ui/input-error'; -import { Transition } from '@headlessui/react'; import { useForm } from '@inertiajs/react'; import { FormEventHandler, useRef } from 'react'; @@ -8,7 +7,8 @@ import { Input } from '@/components/ui/input'; import { Label } from '@/components/ui/label'; import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from '@/components/ui/card'; import { Form, FormField, FormFields } from '@/components/ui/form'; -import { CheckIcon, LoaderCircleIcon } from 'lucide-react'; +import { LoaderCircleIcon } from 'lucide-react'; +import FormSuccessful from '@/components/form-successful'; export default function UpdatePassword() { const passwordInput = useRef(null); @@ -96,11 +96,9 @@ export default function UpdatePassword() { - - - ); diff --git a/resources/js/pages/profile/components/update-user.tsx b/resources/js/pages/profile/components/update-profile.tsx similarity index 86% rename from resources/js/pages/profile/components/update-user.tsx rename to resources/js/pages/profile/components/update-profile.tsx index 3498fa35..2cc5db08 100644 --- a/resources/js/pages/profile/components/update-user.tsx +++ b/resources/js/pages/profile/components/update-profile.tsx @@ -4,18 +4,18 @@ import { Input } from '@/components/ui/input'; import InputError from '@/components/ui/input-error'; import { useForm, usePage } from '@inertiajs/react'; import { Button } from '@/components/ui/button'; -import { Transition } from '@headlessui/react'; import type { SharedData } from '@/types'; import { FormEventHandler } from 'react'; import { Form, FormField, FormFields } from '@/components/ui/form'; -import { CheckIcon, LoaderCircleIcon } from 'lucide-react'; +import { LoaderCircleIcon } from 'lucide-react'; +import FormSuccessful from '@/components/form-successful'; type ProfileForm = { name: string; email: string; }; -export default function UpdateUser() { +export default function UpdateProfile() { const { auth } = usePage().props; const { data, setData, patch, errors, processing, recentlySuccessful } = useForm>({ @@ -71,11 +71,9 @@ export default function UpdateUser() { - - - ); diff --git a/resources/js/pages/profile/index.tsx b/resources/js/pages/profile/index.tsx index 01cb9cea..8a1e9ae9 100644 --- a/resources/js/pages/profile/index.tsx +++ b/resources/js/pages/profile/index.tsx @@ -2,7 +2,7 @@ import { Head } from '@inertiajs/react'; import SettingsLayout from '@/layouts/settings/layout'; import Container from '@/components/container'; import UpdatePassword from '@/pages/profile/components/update-password'; -import UpdateUser from '@/pages/profile/components/update-user'; +import UpdateProfile from '@/pages/profile/components/update-profile'; import Heading from '@/components/heading'; export default function Profile() { @@ -11,7 +11,7 @@ export default function Profile() { - + diff --git a/resources/js/pages/projects/components/project-form.tsx b/resources/js/pages/projects/components/project-form.tsx index d7d303f2..56f18236 100644 --- a/resources/js/pages/projects/components/project-form.tsx +++ b/resources/js/pages/projects/components/project-form.tsx @@ -10,14 +10,14 @@ import { } from '@/components/ui/dialog'; import { FormEventHandler, ReactNode, useState } from 'react'; import { Button } from '@/components/ui/button'; -import { CheckIcon, LoaderCircle } from 'lucide-react'; +import { LoaderCircle } from 'lucide-react'; import { useForm } from '@inertiajs/react'; import { Form, FormField, FormFields } from '@/components/ui/form'; import { Label } from '@/components/ui/label'; import { Input } from '@/components/ui/input'; import InputError from '@/components/ui/input-error'; import { Project } from '@/types/project'; -import { Transition } from '@headlessui/react'; +import FormSuccessful from '@/components/form-successful'; export default function ProjectForm({ project, children }: { project?: Project; children: ReactNode }) { const [open, setOpen] = useState(false); @@ -58,16 +58,7 @@ export default function ProjectForm({ project, children }: { project?: Project; - - - - + diff --git a/resources/js/pages/server-providers/components/columns.tsx b/resources/js/pages/server-providers/components/columns.tsx new file mode 100644 index 00000000..13f7f115 --- /dev/null +++ b/resources/js/pages/server-providers/components/columns.tsx @@ -0,0 +1,185 @@ +import { ColumnDef } from '@tanstack/react-table'; +import DateTime from '@/components/date-time'; +import { ServerProvider } from '@/types/server-provider'; +import { Badge } from '@/components/ui/badge'; +import { + Dialog, + DialogClose, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogTitle, + DialogTrigger, +} from '@/components/ui/dialog'; +import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuSeparator, DropdownMenuTrigger } from '@/components/ui/dropdown-menu'; +import { Button } from '@/components/ui/button'; +import { useForm } from '@inertiajs/react'; +import { LoaderCircleIcon, MoreVerticalIcon } from 'lucide-react'; +import FormSuccessful from '@/components/form-successful'; +import { FormEvent, useState } from 'react'; +import InputError from '@/components/ui/input-error'; +import { Form, FormField, FormFields } from '@/components/ui/form'; +import { Label } from '@/components/ui/label'; +import { Input } from '@/components/ui/input'; +import { Checkbox } from '@/components/ui/checkbox'; + +function Edit({ serverProvider }: { serverProvider: ServerProvider }) { + const [open, setOpen] = useState(false); + const form = useForm({ + name: serverProvider.name, + global: serverProvider.global, + }); + + const submit = (e: FormEvent) => { + e.preventDefault(); + form.patch(route('server-providers.update', serverProvider.id)); + }; + return ( + + + e.preventDefault()}>Edit + + + + Edit {serverProvider.name} + Edit server provider + +
+ + + + form.setData('name', e.target.value)} /> + + + +
+ form.setData('global', !form.data.global)} /> + +
+ +
+
+
+ + + + + + +
+
+ ); +} + +function Delete({ serverProvider }: { serverProvider: ServerProvider }) { + const [open, setOpen] = useState(false); + const form = useForm(); + + const submit = () => { + form.delete(route('server-providers.destroy', serverProvider.id), { + onSuccess: () => { + setOpen(false); + }, + }); + }; + return ( + + + e.preventDefault()}> + Delete + + + + + Delete {serverProvider.name} + Delete server provider + +
+

+ Are you sure you want to delete {serverProvider.name}? +

+ +
+ + + + + + +
+
+ ); +} + +export const columns: ColumnDef[] = [ + { + accessorKey: 'id', + header: 'ID', + enableColumnFilter: true, + enableSorting: true, + enableHiding: true, + }, + { + accessorKey: 'provider', + header: 'Provider', + enableColumnFilter: true, + enableSorting: true, + }, + { + accessorKey: 'name', + header: 'Name', + enableColumnFilter: true, + enableSorting: true, + }, + { + accessorKey: 'global', + header: 'Global', + enableColumnFilter: true, + enableSorting: true, + cell: ({ row }) => { + return
{row.original.global ? yes : no}
; + }, + }, + { + accessorKey: 'created_at', + header: 'Created at', + enableColumnFilter: true, + enableSorting: true, + cell: ({ row }) => { + return ; + }, + }, + { + id: 'actions', + enableColumnFilter: false, + enableSorting: false, + cell: ({ row }) => { + return ( +
+ + + + + + + + + + +
+ ); + }, + }, +]; diff --git a/resources/js/pages/server-providers/components/create-server-provider.tsx b/resources/js/pages/server-providers/components/connect-server-provider.tsx similarity index 87% rename from resources/js/pages/server-providers/components/create-server-provider.tsx rename to resources/js/pages/server-providers/components/connect-server-provider.tsx index 89944504..4d6b6d91 100644 --- a/resources/js/pages/server-providers/components/create-server-provider.tsx +++ b/resources/js/pages/server-providers/components/connect-server-provider.tsx @@ -1,4 +1,4 @@ -import { LoaderCircle, PlusIcon, WifiIcon } from 'lucide-react'; +import { LoaderCircle } from 'lucide-react'; import { Button } from '@/components/ui/button'; import { Dialog, @@ -11,7 +11,7 @@ import { DialogTrigger, } from '@/components/ui/dialog'; import { useForm, usePage } from '@inertiajs/react'; -import { FormEventHandler, useEffect, useState } from 'react'; +import { FormEventHandler, ReactNode, useEffect, useState } from 'react'; import { Label } from '@/components/ui/label'; import { Select, SelectContent, SelectGroup, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'; import InputError from '@/components/ui/input-error'; @@ -26,16 +26,16 @@ type ServerProviderForm = { global: boolean; }; -export default function CreateServerProvider({ - trigger, +export default function ConnectServerProvider({ providers, defaultProvider, onProviderAdded, + children, }: { - trigger: 'icon' | 'button'; providers: string[]; defaultProvider?: string; onProviderAdded?: () => void; + children: ReactNode; }) { const [open, setOpen] = useState(false); @@ -65,21 +65,11 @@ export default function CreateServerProvider({ return ( - - - + {children} - Connect - Connect to a new server provider + Connect to server provider + Connect to a new server provider
diff --git a/resources/js/pages/server-providers/index.tsx b/resources/js/pages/server-providers/index.tsx new file mode 100644 index 00000000..f162ef3a --- /dev/null +++ b/resources/js/pages/server-providers/index.tsx @@ -0,0 +1,41 @@ +import SettingsLayout from '@/layouts/settings/layout'; +import { Head, usePage } from '@inertiajs/react'; +import Container from '@/components/container'; +import Heading from '@/components/heading'; +import { Button } from '@/components/ui/button'; +import React from 'react'; +import ConnectServerProvider from '@/pages/server-providers/components/connect-server-provider'; +import { DataTable } from '@/components/data-table'; +import { columns } from '@/pages/server-providers/components/columns'; +import { ServerProvider } from '@/types/server-provider'; + +type Page = { + serverProviders: { + data: ServerProvider[]; + }; + configs: { + server_providers: string[]; + }; +}; + +export default function ServerProviders() { + const page = usePage(); + + return ( + + + +
+ +
+ + + +
+
+ + +
+
+ ); +} diff --git a/resources/js/pages/servers/components/create-server.tsx b/resources/js/pages/servers/components/create-server.tsx index f8cdb801..c1aa0a2b 100644 --- a/resources/js/pages/servers/components/create-server.tsx +++ b/resources/js/pages/servers/components/create-server.tsx @@ -1,4 +1,4 @@ -import { ClipboardCheckIcon, ClipboardIcon, LoaderCircle, TriangleAlert } from 'lucide-react'; +import { ClipboardCheckIcon, ClipboardIcon, LoaderCircle, TriangleAlert, WifiIcon } from 'lucide-react'; import { Button } from '@/components/ui/button'; import { Sheet, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetTitle, SheetTrigger } from '@/components/ui/sheet'; import { useForm, usePage } from '@inertiajs/react'; @@ -9,7 +9,7 @@ import InputError from '@/components/ui/input-error'; import { Input } from '@/components/ui/input'; import { Alert, AlertDescription } from '@/components/ui/alert'; import { ServerProvider } from '@/types/server-provider'; -import CreateServerProvider from '@/pages/server-providers/components/create-server-provider'; +import ConnectServerProvider from '@/pages/server-providers/components/connect-server-provider'; import axios from 'axios'; import { Form, FormField, FormFields } from '@/components/ui/form'; import type { SharedData } from '@/types'; @@ -40,9 +40,9 @@ export default function CreateServer({ children }: { children: React.ReactNode } port: 22, region: '', plan: '', - webserver: '', - database: '', - php: '', + webserver: 'nginx', + database: 'none', + php: 'none', }); const submit: FormEventHandler = (e) => { @@ -62,7 +62,7 @@ export default function CreateServer({ children }: { children: React.ReactNode } const [serverProviders, setServerProviders] = useState([]); const fetchServerProviders = async () => { - const serverProviders = await axios.get(route('server-providers.all')); + const serverProviders = await axios.get(route('server-providers.json')); setServerProviders(serverProviders.data); }; const selectProvider = (provider: string) => { @@ -150,12 +150,15 @@ export default function CreateServer({ children }: { children: React.ReactNode } - item !== 'custom')} defaultProvider={form.data.provider} onProviderAdded={fetchServerProviders} - /> + > + + diff --git a/resources/js/pages/users/components/actions.tsx b/resources/js/pages/users/components/actions.tsx index 6592f1ec..c0ff21f0 100644 --- a/resources/js/pages/users/components/actions.tsx +++ b/resources/js/pages/users/components/actions.tsx @@ -4,7 +4,7 @@ import { Button } from '@/components/ui/button'; import { MoreVerticalIcon } from 'lucide-react'; import DeleteUser from '@/pages/users/components/delete-user'; import Projects from '@/pages/users/components/projects'; -import UserForm from '@/pages/users/components/form'; +import UserForm from '@/pages/users/components/user-form'; export default function UserActions({ user }: { user: User }) { return ( diff --git a/resources/js/pages/users/components/form.tsx b/resources/js/pages/users/components/user-form.tsx similarity index 90% rename from resources/js/pages/users/components/form.tsx rename to resources/js/pages/users/components/user-form.tsx index dbcbb3da..2beca973 100644 --- a/resources/js/pages/users/components/form.tsx +++ b/resources/js/pages/users/components/user-form.tsx @@ -10,7 +10,7 @@ import { } from '@/components/ui/dialog'; import { FormEventHandler, ReactNode, useState } from 'react'; import { Button } from '@/components/ui/button'; -import { CheckIcon, LoaderCircle } from 'lucide-react'; +import { LoaderCircle } from 'lucide-react'; import { useForm } from '@inertiajs/react'; import { Form, FormField, FormFields } from '@/components/ui/form'; import { Label } from '@/components/ui/label'; @@ -18,7 +18,7 @@ import { Input } from '@/components/ui/input'; import InputError from '@/components/ui/input-error'; import { Select, SelectContent, SelectGroup, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'; import { User } from '@/types/user'; -import { Transition } from '@headlessui/react'; +import FormSuccessful from '@/components/form-successful'; export default function UserForm({ user, children }: { user?: User; children: ReactNode }) { const [open, setOpen] = useState(false); @@ -100,15 +100,6 @@ export default function UserForm({ user, children }: { user?: User; children: Re
- - - diff --git a/resources/js/pages/users/index.tsx b/resources/js/pages/users/index.tsx index e8bf5730..df417935 100644 --- a/resources/js/pages/users/index.tsx +++ b/resources/js/pages/users/index.tsx @@ -4,7 +4,7 @@ import Container from '@/components/container'; import Heading from '@/components/heading'; import UsersList from '@/pages/users/components/list'; import { Button } from '@/components/ui/button'; -import UserForm from '@/pages/users/components/form'; +import UserForm from '@/pages/users/components/user-form'; export default function Users() { return ( diff --git a/resources/js/types/server-provider.d.ts b/resources/js/types/server-provider.d.ts index 158a7a66..5e8f2172 100644 --- a/resources/js/types/server-provider.d.ts +++ b/resources/js/types/server-provider.d.ts @@ -1,8 +1,9 @@ export interface ServerProvider { id: number; user_id: number; - name: string; provider: string; + name: string; + global: boolean; connected: boolean; project_id?: number; created_at: string; diff --git a/tests/Feature/ServerProvidersTest.php b/tests/Feature/ServerProvidersTest.php index e27a7bb5..a0ad7efe 100644 --- a/tests/Feature/ServerProvidersTest.php +++ b/tests/Feature/ServerProvidersTest.php @@ -3,11 +3,10 @@ namespace Tests\Feature; use App\Enums\ServerProvider; -use App\Web\Pages\Settings\ServerProviders\Index; -use App\Web\Pages\Settings\ServerProviders\Widgets\ServerProvidersList; use Illuminate\Foundation\Testing\RefreshDatabase; use Illuminate\Support\Facades\Http; -use Livewire\Livewire; +use Inertia\Testing\AssertableInertia; +use JsonException; use Tests\TestCase; class ServerProvidersTest extends TestCase @@ -15,7 +14,11 @@ class ServerProvidersTest extends TestCase use RefreshDatabase; /** + * @param array $input + * * @dataProvider data + * + * @throws JsonException */ public function test_connect_provider(string $provider, array $input): void { @@ -30,10 +33,8 @@ public function test_connect_provider(string $provider, array $input): void ], $input ); - Livewire::test(Index::class) - ->callAction('create', $data) - ->assertHasNoActionErrors() - ->assertSuccessful(); + $this->post(route('server-providers.store'), $data) + ->assertSessionHasNoErrors(); $this->assertDatabaseHas('server_providers', [ 'provider' => $provider, @@ -43,6 +44,8 @@ public function test_connect_provider(string $provider, array $input): void } /** + * @param array $input + * * @dataProvider data */ public function test_cannot_connect_to_provider(string $provider, array $input): void @@ -60,10 +63,8 @@ public function test_cannot_connect_to_provider(string $provider, array $input): ], $input ); - Livewire::test(Index::class) - ->callAction('create', $data) - ->assertActionHalted('create') - ->assertNotified(); + $this->post(route('server-providers.store'), $data) + ->assertSessionHasErrors('provider'); $this->assertDatabaseMissing('server_providers', [ 'provider' => $provider, @@ -75,17 +76,19 @@ public function test_see_providers_list(): void { $this->actingAs($this->user); - $provider = \App\Models\ServerProvider::factory()->create([ + \App\Models\ServerProvider::factory()->create([ 'user_id' => $this->user->id, ]); - $this->get(Index::getUrl()) + $this->get(route('server-providers')) ->assertSuccessful() - ->assertSee($provider->profile); + ->assertInertia(fn (AssertableInertia $page) => $page->component('server-providers/index')); } /** * @dataProvider data + * + * @throws JsonException */ public function test_delete_provider(string $provider): void { @@ -96,9 +99,9 @@ public function test_delete_provider(string $provider): void 'provider' => $provider, ]); - Livewire::test(ServerProvidersList::class) - ->callTableAction('delete', $provider->id) - ->assertSuccessful(); + $this->delete(route('server-providers.destroy', $provider)) + ->assertSessionHasNoErrors() + ->assertRedirect(route('server-providers')); $this->assertDatabaseMissing('server_providers', [ 'id' => $provider->id, @@ -121,15 +124,19 @@ public function test_cannot_delete_provider(string $provider): void 'provider_id' => $provider->id, ]); - Livewire::test(ServerProvidersList::class) - ->callTableAction('delete', $provider->id) - ->assertNotified('This server provider is being used by a server.'); + $this->delete(route('server-providers.destroy', $provider)) + ->assertSessionHasErrors([ + 'provider' => 'This server provider is being used by a server.', + ]); $this->assertDatabaseHas('server_providers', [ 'id' => $provider->id, ]); } + /** + * @return array>> + */ public static function data(): array { return [ diff --git a/tests/Feature/UserTest.php b/tests/Feature/UserTest.php index e60b517c..df8306c3 100644 --- a/tests/Feature/UserTest.php +++ b/tests/Feature/UserTest.php @@ -6,7 +6,7 @@ use App\Models\Project; use App\Models\User; use Illuminate\Foundation\Testing\RefreshDatabase; -use Inertia\Testing\AssertableInertia as Assert; +use Inertia\Testing\AssertableInertia; use Tests\TestCase; class UserTest extends TestCase @@ -41,7 +41,7 @@ public function test_see_users_list(): void $this->get(route('users')) ->assertSuccessful() - ->assertInertia(fn (Assert $page) => $page->component('users/index')); + ->assertInertia(fn (AssertableInertia $page) => $page->component('users/index')); } public function test_must_be_admin_to_see_users_list(): void