PLATFORM
  • Tails

    Create websites with TailwindCSS

  • Wave

    Start building the next great SAAS

  • Pines

    Alpine & Tailwind UI Library

  • Auth

    Plug'n Play Authentication for Laravel

  • Designer comingsoon

    Create website designs with AI

  • DevBlog comingsoon

    Blog platform for developers

  • Static

    Build a simple static website

  • SaaS Adventure

    21-day program to build a SAAS

Question By
Unsolved

Test failing with Session is missing expected key [errors]

ookma-kyi

May 27th, 2024 08:21 PM

I have this test that checks that the active field is either 0 or 1:

it('requires valid data', function ($active) {
    // Arrange
    Belt::factory()->create([
        'min_xp' => 0,
    ]);

    $character = Character::factory()->create();

    $this->actingAs($character->user)
        ->put(route('characters.update', ['character' => $character]), ['active' => $active])
        ->assertInvalid('active');
})->with([
    null,
    1,
    1.5,
    str_repeat('a', 2),
]);

However it is failing with Session is missing expected key [errors].. Here is my Vue:

<template>
    <AppLayout title="My Characters">
            <div class="grid row mt-5">
                <div class="col-span-6 m-auto">
                    <div class="block max-w-sm p-6 bg-white border border-gray-200 rounded-lg shadow hover:bg-gray-100 dark:bg-gray-800 dark:border-gray-700 dark:hover:bg-gray-700">
                        <h1 class="text-center mb-3">
                            <i aria-hidden="true"></i>  Characters
                        </h1>
                        <div v-if="characters.length === 0">
                            <p>You have no characters, why not create a new character?</p>
                            <Link :href="route('characters.create')" class="inline-block border border-blue-500 rounded py-1 px-3 bg-blue-500 text-white" role="link" title="Create a new character">Create a new character</Link>
                        </div>
                        <table v-else class="table-auto border-spacing-2 text-center">
                            <thead>
                            <tr>
                                <th scope="col">Name</th>
                                <th scope="col">Active</th>
                                <th scope="col">Options</th>
                            </tr>
                            </thead>
                            <tbody>
                            <tr v-for="character in characters" :key="character.id">
                                <th scope="row">{{ character.name }}</th>
                                <td><span v-if="character.active">X</span></td>
                                <td>
                                    <Link :href="route('characters.show', {character: character})" class="text-sm text-gray-600 hover:text-gray-900 dark:text-gray-400 dark:hover:text-white focus:outline focus:outline-2 focus:rounded-sm focus:outline-red-500">Show</Link>&nbsp;
                                    <Link v-if="!character.active" @click="switchCharacter(character.id)" class="text-sm text-gray-600 hover:text-gray-900 dark:text-gray-400 dark:hover:text-white focus:outline focus:outline-2 focus:rounded-sm focus:outline-red-500">Make active</Link></td>
                            </tr>
                            </tbody>
                        </table>
                    </div>
                </div>
            </div>
    </AppLayout>
</template>
<script setup>
import AppLayout from "@/Layouts/AppLayout.vue";
import {Link} from "@inertiajs/vue3";

defineProps(['characters']);

const switchCharacter = (characterId) => {
    fetch(`/characters/${characterId}`, {
        method: 'PUT',
        headers: {
            'Content-Type': 'application/json',
            'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').getAttribute('content')
        },
        body: JSON.stringify({})
    })
        .then(response => {
            console.log(response); // Log the response for debugging
            if (response.ok) {
                // Handle success, maybe update the UI
                alert('Character switched successfully!');
                window.location.reload(); // Reload the page to reflect changes
            } else {
                // Handle error
                alert('Failed to switch character.');
            }
        })
        .catch(error => {
            console.error('Error:', error);
            alert('Failed to switch character.');
        });
}
</script>

And here is my route action:

    /**
     * Update the specified resource in storage.
     */
    public function update(Request $request, Character $character)
    {
        // if the character is already the active
        if ($character->active) {

            // then redirect to the characters index
            return redirect('characters');
        }

        // get the player's active character if any
        $activecharacter = Character::where('user_id', auth()->user()->id)
            ->where('active', 1)
            ->first(['id', 'active']);

        // if the play has an active character
        if ($activecharacter) {

            // mark the previous character as not active
            $activecharacter->update(['active' => 0]);
        }

        // set the selected character as the active one
        $character->update(['active' => 1]);

        // then redirect to the characters index
        return response(200);
    }

I am using this as my base. It was suggested here that I add validation to my route action. However as per my response to the ai:

@LaryAI Unfortunately, the issue persists. If I add validation the route no longer works. Additionally, there is no actual input to validate because there is no actual input required by the user besides clicking on a link/button which already has the character id baked in.

Any ideas?

bobbyiliev

May 28th, 2024 03:57 AM

Hey!

What I would try to do here is to dd the character and see if it actually has all of the required properties after being created by the Character::factory.

That way you will be able to isolate if the problem is with the character itself or something in the vue component.

Also, if you don't need actual validation, you can modify the test to assert the expected behavior directly.

it('sets active character and redirects', function () {
    $character = Character::factory()->create();
    $this->actingAs($character->user)
        ->put(route('characters.update', ['character' => $character]), ['active' => 1])
        ->assertRedirect('characters'); // Assert redirect to characters route
});

But your best bet would be to just use a debugging tools like dd or logging to inspect the request data and validation errors during test execution and take it from there.

Let me know how it goes!

- Bobby

ookma-kyi

May 28th, 2024 11:36 AM

I changed the last part to ->assertRedirect('characters'); and am now getting the following error:

Expected response status code [201, 301, 302, 303, 307, 308] but received 200.
Failed asserting that false is true.

Also here is out output of dd:

App\Models\Character {#6756
  #connection: "mysql"
  #table: null
  #primaryKey: "id"
  #keyType: "int"
  +incrementing: true
  #with: []
  #withCount: []
  +preventsLazyLoading: false
  #perPage: 15
  +exists: true
  +wasRecentlyCreated: true
  #escapeWhenCastingToString: false
  #attributes: array:10 [
    "user_id" => 23
    "name" => "B"
    "xp" => 543
    "belt_id" => 18
    "wins" => 11
    "loses" => 83
    "draws" => 78
    "updated_at" => "2024-05-28 18:42:44"
    "created_at" => "2024-05-28 18:42:44"
    "id" => 10
  ]
  #original: array:10 [
    "user_id" => 23
    "name" => "B"
    "xp" => 543
    "belt_id" => 18
    "wins" => 11
    "loses" => 83
    "draws" => 78
    "updated_at" => "2024-05-28 18:42:44"
    "created_at" => "2024-05-28 18:42:44"
    "id" => 10
  ]
  #changes: []
  #casts: array:1 [
    "deleted_at" => "datetime"
  ]
  #classCastCache: []
  #attributeCastCache: []
  #dateFormat: null
  #appends: []
  #dispatchesEvents: []
  #observables: []
  #relations: []
  #touches: []
  +timestamps: true
  +usesUniqueIds: false
  #hidden: []
  #visible: []
  #fillable: array:4 [
    0 => "user_id"
    1 => "active"
    2 => "name"
    3 => "belt_id"
  ]
  #guarded: array:1 [
    0 => "*"
  ]
  #forceDeleting: false
} // tests\Feature\Controllers\CharacterController\UpdateTest.php:77

Seems I forgot to update my factory, one second...

bobbyiliev

May 28th, 2024 11:59 AM

Hey!

Ok so it looks like that you are not hitting the redirect, so you should assert that the response is 200 rather than the redirect. How do things look after that?