Skip to content

Commit 7ed5c44

Browse files
committed
Add browser tests for appointment scheduling
1 parent 6972857 commit 7ed5c44

File tree

11 files changed

+170
-44
lines changed

11 files changed

+170
-44
lines changed

.env.dusk.local

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
APP_NAME="Vet Clinic"
2+
APP_ENV=local
3+
APP_KEY=base64:9KDkHF6Lgad26mlBRfLQOCdun5j8IhLO+FpU307o1Mk=
4+
APP_DEBUG=true
5+
APP_URL=http://localhost:8000
6+
7+
APP_LOCALE=en
8+
APP_FALLBACK_LOCALE=en
9+
APP_FAKER_LOCALE=en_US
10+
11+
APP_MAINTENANCE_DRIVER=file
12+
# APP_MAINTENANCE_STORE=database
13+
14+
PHP_CLI_SERVER_WORKERS=4
15+
16+
BCRYPT_ROUNDS=12
17+
18+
LOG_CHANNEL=stack
19+
LOG_STACK=single
20+
LOG_DEPRECATIONS_CHANNEL=null
21+
LOG_LEVEL=debug
22+
23+
DB_CONNECTION=sqlite
24+
DB_DATABASE_NAME=database_dusk.sqlite
25+
# DB_HOST=127.0.0.1
26+
# DB_PORT=3306
27+
# DB_DATABASE=laravel
28+
# DB_USERNAME=root
29+
# DB_PASSWORD=
30+
31+
SESSION_DRIVER=database
32+
SESSION_LIFETIME=120
33+
SESSION_ENCRYPT=false
34+
SESSION_PATH=/
35+
SESSION_DOMAIN=null
36+
37+
BROADCAST_CONNECTION=log
38+
FILESYSTEM_DISK=local
39+
QUEUE_CONNECTION=database
40+
41+
CACHE_STORE=database
42+
# CACHE_PREFIX=
43+
44+
MEMCACHED_HOST=127.0.0.1
45+
46+
REDIS_CLIENT=phpredis
47+
REDIS_HOST=127.0.0.1
48+
REDIS_PASSWORD=null
49+
REDIS_PORT=6379
50+
51+
MAIL_MAILER=smtp
52+
MAIL_SCHEME=null
53+
MAIL_HOST=127.0.0.1
54+
MAIL_PORT=1025
55+
MAIL_USERNAME=null
56+
MAIL_PASSWORD=null
57+
MAIL_FROM_ADDRESS="[email protected]"
58+
MAIL_FROM_NAME="${APP_NAME}"
59+
60+
AWS_ACCESS_KEY_ID=
61+
AWS_SECRET_ACCESS_KEY=
62+
AWS_DEFAULT_REGION=us-east-1
63+
AWS_BUCKET=
64+
AWS_USE_PATH_STYLE_ENDPOINT=false
65+
66+
VITE_APP_NAME="${APP_NAME}"
67+
68+
DUSK_HEADLESS_DISABLED=true

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@ npm install
2424
# Create .env file (and ask before overwritting)
2525
cp -i .env.example .env
2626

27+
# Create the database file used for browser tests (set by DB_DATABASE_NAME in .env.dusk.local)
28+
touch database/database_dusk.sqlite
29+
2730
# Create app key
2831
php artisan key:generate
2932

config/database.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434
'sqlite' => [
3535
'driver' => 'sqlite',
3636
'url' => env('DB_URL'),
37-
'database' => env('DB_DATABASE', database_path('database.sqlite')),
37+
'database' => env('DB_DATABASE', database_path(env('DB_DATABASE_NAME', 'database.sqlite'))),
3838
'prefix' => '',
3939
'foreign_key_constraints' => env('DB_FOREIGN_KEYS', true),
4040
'busy_timeout' => null,

database/factories/AppointmentFactory.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ public function definition(): array
3030

3131
public function assigned(): self
3232
{
33-
return $this->state(function (array $attributes) {
33+
return $this->state(function () {
3434
return [
3535
'receptionist_id' => User::factory()->receptionist(),
3636
'medic_id' => User::factory()->medic(),

database/factories/UserFactory.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ public function unverified(): static
4545

4646
public function receptionist(): self
4747
{
48-
return $this->state(function (array $attributes) {
48+
return $this->state(function () {
4949
return [
5050
'type' => UserType::Receptionist,
5151
];
@@ -54,7 +54,7 @@ public function receptionist(): self
5454

5555
public function medic(): self
5656
{
57-
return $this->state(function (array $attributes) {
57+
return $this->state(function () {
5858
return [
5959
'type' => UserType::Medic,
6060
];

resources/js/pages/client/ScheduleAppointment.vue

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,7 @@ function createAnimalType(item: string) {
140140
<UFormField label="Type" name="animal.type" class="w-full" required>
141141
<UInputMenu
142142
v-model="form.animal.type"
143+
data-input-field-name="animal.type"
143144
placeholder="Your pet type"
144145
create-item="always"
145146
:items="animalTypes"
@@ -178,12 +179,7 @@ function createAnimalType(item: string) {
178179
<UInput v-model="form.appointment.preferred_date" type="date" class="w-full" />
179180
</UFormField>
180181
<UFormField name="appointment.preferred_time">
181-
<UCheckboxGroup
182-
v-model="form.appointment.preferred_time"
183-
orientation="horizontal"
184-
name="appointment.preferredTime"
185-
:items="timeOfDay"
186-
/>
182+
<UCheckboxGroup v-model="form.appointment.preferred_time" orientation="horizontal" :items="timeOfDay" />
187183
</UFormField>
188184
</div>
189185
<!-- Appointment: symptoms-->
Lines changed: 56 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,64 @@
11
<?php
22

3+
use App\TimeOfDay;
4+
use Carbon\Carbon;
35
use Laravel\Dusk\Browser;
6+
use Tests\TestCase;
7+
use Tests\TestData\AppointmentSchedule;
48

5-
test('example', function () {
9+
$VALID_APPOINTMENT = AppointmentSchedule::valid();
10+
11+
test('can visit page', function () {
612
$this->browse(function (Browser $browser) {
713
$browser->visit('/')
8-
->assertSee('Laravel');
14+
->assertSee(config('app.name'));
15+
});
16+
});
17+
18+
test('can schedule appointment', function () use ($VALID_APPOINTMENT) {
19+
$data = collect($VALID_APPOINTMENT);
20+
$data_dot = $data->dot();
21+
$this->browse(function (Browser $browser) use ($data, $data_dot) {
22+
$browser->visit('/');
23+
24+
// Type fields that can be typed
25+
// Use the collection and except before flattening to dot notation so appointment.preferred_time does not get flattened as well
26+
$data->except(['animal.type', 'appointment.preferred_date', 'appointment.preferred_time'])
27+
->dot()
28+
->each(fn ($value, $key) => $browser->type($key, $value));
29+
30+
// Manually set the value of fields like date (depends on the browser format)
31+
$data_dot->only(['appointment.preferred_date'])
32+
->each(function ($value, $key) use ($browser) {
33+
$browser->type($key, Carbon::createFromFormat(TestCase::DATE_FORMAT, $value)->format('mdY'));
34+
});
35+
36+
// Manually type data in select inputs
37+
$data_dot->only(['animal.type'])
38+
->each(function ($value, $key) use ($browser) {
39+
$browser->keys("[data-input-field-name='$key']",
40+
$value,
41+
'{enter}',
42+
);
43+
});
44+
45+
// Check checkboxes
46+
collect([
47+
...Arr::map(
48+
$data['appointment']['preferred_time'],
49+
fn (string $value) => TimeOfDay::from($value)->name
50+
),
51+
])
52+
->each(function ($label) use ($browser) {
53+
$browser->click("[aria-label='$label']");
54+
});
55+
56+
// Submit
57+
$browser->click('button[type=submit]')
58+
->waitForText('Success');
959
});
60+
61+
$this->assertDatabaseCount('clients', 1);
62+
$this->assertDatabaseCount('animals', 1);
63+
$this->assertDatabaseCount('appointments', 1);
1064
});

tests/Feature/Client/ScheduleAppointmentTest.php

Lines changed: 4 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,10 @@
11
<?php
22

33
use App\Models\Client;
4-
use App\TimeOfDay;
5-
use Database\Factories\AnimalFactory;
64
use Tests\TestCase;
5+
use Tests\TestData\AppointmentSchedule;
76

8-
$VALID_APPOINTMENT = [
9-
'client' => [
10-
'name' => fake()->name,
11-
'email' => fake()->email,
12-
],
13-
'animal' => [
14-
'name' => fake()->randomElement(AnimalFactory::NAMES),
15-
'type' => fake()->randomElement(AnimalFactory::TYPES),
16-
'age_years' => fake()->numberBetween(1, 20),
17-
'age_months' => fake()->numberBetween(0, 12),
18-
],
19-
'appointment' => [
20-
'preferred_date' => fake()->dateTimeInInterval(now(), '+30 days')->format('Y-m-d'),
21-
'preferred_time' => [fake()->randomElement(TimeOfDay::selectable())->value],
22-
'symptoms' => fake()->realText(),
23-
],
24-
];
7+
$VALID_APPOINTMENT = AppointmentSchedule::valid();
258

269
function scheduleAppointment(TestCase $self, array $data): void
2710
{
@@ -47,12 +30,7 @@ function scheduleAppointment(TestCase $self, array $data): void
4730
test('can schedule appointment', function () use ($VALID_APPOINTMENT) {
4831
$data = $VALID_APPOINTMENT;
4932

50-
// Try to schedule an appointment
51-
$response = $this
52-
->post(route('public.schedule-appointment'), $data);
53-
54-
// Check for redirect. Since we're using Inertia, it redirects the user to the homepage on success
55-
$response->assertRedirect(route('public.home'));
33+
scheduleAppointment($this, $data);
5634

5735
// Check if data was saved
5836
$this->assertDatabaseHas('clients', $data['client']);
@@ -90,11 +68,7 @@ function scheduleAppointment(TestCase $self, array $data): void
9068

9169
// Try to schedule an appointment, twice, with the same data
9270
for ($i = 0; $i < 2; $i++) {
93-
$response = $this
94-
->post(route('public.schedule-appointment'), $data);
95-
96-
// Check for redirect. Since we're using Inertia, it redirects the user to the homepage on success
97-
$response->assertRedirect(route('public.home'));
71+
scheduleAppointment($this, $data);
9872
}
9973

10074
// Check if the client was updated

tests/Pest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<?php
22

33
pest()->extend(Tests\DuskTestCase::class)
4-
// ->use(Illuminate\Foundation\Testing\DatabaseMigrations::class)
4+
->use(Illuminate\Foundation\Testing\DatabaseMigrations::class)
55
->in('Browser');
66

77
/*

tests/TestCase.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,5 @@
66

77
abstract class TestCase extends BaseTestCase
88
{
9-
//
9+
const string DATE_FORMAT = 'Y-m-d';
1010
}

0 commit comments

Comments
 (0)