loading...

Rocket — becoming a known developer… and building an app to help make it happen — Part 4

mattkingshott profile image Matt Kingshott 👨🏻‍💻 Originally published at itnext.io on ・6 min read

Rocket — becoming a known (Laravel) developer… and building an application to help make it happen — Part 4

In this series, we will be reviewing the steps that we as developers need to take in order to create a suitable web presence and build an audience that we can communicate ideas, projects and commercial offerings with.

In addition to theory articles discussing marketing, presentation and methods of engagement, we will also be building Rocket — a Laravel application that will allow you and other developers to create a personal site and grow an audience through social media and article publishing… let’s dive in!

Today’s agenda

I know I had previously intended this article to cover the “projects” section of the application, however I’ve decided to tackle the authentication system as it has been sitting there partially untouched since the beginning. Let’s fix that today by adding a nice login form, user installation, and some testing.

Step #1 — Installing a user

Rocket is an unconventional Laravel application as it only has one official user. As such, the standard authentication scaffolding (with support for password resets, user registration, email verification, password confirmation etc.) isn’t really necessary. Instead, all we really need is to be able to login and logout.

However, we still need a user to make this work. Rather than expose a form to register a user, we can instead use an Artisan command that a developer can run as part of the Rocket installation process. Let’s do that now:

php artisan make:command InstallCommand

Next, we’ll add the logic to prompt the developer for the password they want to use, and we’ll then create the user using some configuration values:

use App\Models\User;

class InstallCommand extends Command
{
    /**
     * Execute the console command.
     *
     */
    public function handle()
    {
        $password = $this->secret('Please enter a password.');

        User::create([
            'name' => config('rocket.name'),
            'email' => config('rocket.email'),
            'password' => bcrypt($password),
        ]);

        $this->info('Rocket is installed.');
    }
}

That’s good enough for now, however if we find that we have additional steps that are required to set up Rocket, we can also add them here. Sweet!

Step #2 — Changing the password

From time to time, it may be necessary to change the user’s password. We need to make that possible since we aren’t providing the standard password resets. Let’s create another command to deal with that:

php artisan make:command PasswordCommand

As with the install command, the logic we need to run is very simple. We need to prompt for the password and update the user model:

use App\Models\User;

class PasswordCommand extends Command
{
    /**
     * Execute the console command.
     *
     */
    public function handle()
    {
        $password = $this->secret('Please enter a password');

        User::first()->update(['password' => bcrypt($password)]);

        $this->info('The password has been changed.');
    }
}

Perfect. We now have a simple, yet reliable means to create the application user and change their password at any time.

Step #3 — Designing the login form

We can still take advantage of the native LoginController provided by the Auth scaffolding in Laravel, so we’re all good there. All we need to do, is create a new login form (using Tailwind to design it):

{{-- Content --}}
<div class="w-full md:w-2/3 lg:w-1/2 xl:w-2/5">

    {{-- Logo --}}
    <img alt="Logo"
         class="mb-8 hidden md:block h-12"
         src="{{ asset('img/logo.png') }}">

    {{-- Title --}}
    <h1 class="text-xl font-medium text-blue-700 mb-4">
        Sign in to your account
    </h1>

    {{-- Information --}}
    <p class="text-gray-700 leading-normal mb-8">
        If you have forgotten your current login password, 
        you'll need to run the password Artisan command.
    </p>

    {{-- Form --}}
    <x-form url="/login"
            class="w-full bg-gray-100 shadow-md rounded-lg p-6">

        {{-- Email Address --}}
        <x-textbox id="email"
                   class="mb-4"
                   autocomplete="email"
                   :value="old('email')"
                   placeholder="Email address">
        </x-textbox>

        {{-- Password --}}
        <x-textbox class="mb-4"
                   id="password"
                   type="password"
                   placeholder="Password"
                   autocomplete="current-password">
        </x-textbox>

        {{-- Remember --}}
        <x-checkbox id="remember\_me"
                    text="Remember Me"
                    :value="old('remember\_me')">
        </x-checkbox>

        {{-- Button --}}
        <x-button text="Login"
                  align="right"
                  action="this.closest('form').submit()">
        </x-button>

    </x-form>

</div>

It’s a fairly simple design. We have the application logo, a title and a reminder on how to change the password if we’ve forgotten it. We then have a panel with an email and a password field, as well as button to sign us in.

One thing to note, is that I’ve added a new checkbox Blade component. The code for this is a bit too complex to digest in this article, so I’ve omitted it. However, you may want to check it out in the repo to familiarise yourself.

Step #4 — Adding some tests

Everything’s looking good at this point. All that’s left, is for us to write some tests to confirm our code is working properly. Let’s begin with a couple of tests for the commands that we’ve written. We’ll start with the install command:

use App\Models\User;
use Illuminate\Support\Facades\Hash;

/** @test */
public function it_can_install_the_application()
{
    $this->artisan('rocket:install')
        ->expectsQuestion('Please enter a password', 'password')
        ->expectsOutput('Rocket is installed.');

    $this->assertEquals(
        config('rocket.name'), User::first()->name
    );

    $this->assertEquals(
        config('rocket.email'), User::first()->email
    );

    $this->assertTrue(
        Hash::check('password', User::first()->password)
    );
}

This is fairly simple. We’re running the command, checking for the question, providing a password and then confirming that a user was created with the correct parameters. Excellent! Now, let’s do the PasswordCommand:

use App\Models\User;
use Illuminate\Support\Facades\Hash;

/** @test */
public function it_can_change_the_password()
{
    $user = factory(User::class, 1)
          ->create(['password' => bcrypt('test')])->first();

    $this->assertTrue(Hash::check('test', $user->password));

    $this->artisan('rocket:password')
        ->expectsQuestion('Please enter a password?', 'password')
        ->expectsOutput('The password has been changed.');

    $this->assertTrue(
        Hash::check('password', $user->fresh()->password)
    );
}

Here, we’re seeding the database with a user that has a password of ‘test’. We then confirm the password is ‘test’. Next, we run the command, check for the question, and provide a new password of ‘password’. We then confirm that the user’s password has now been changed to ‘password’.

Step #5 — Using browser tests

While still technically a part of step #4, I wanted to highlight that for our next test, we’re going to be using Laravel Dusk. Dusk allows us to fire up Chrome, or other browsers if you wish, and tell it do certain things that we can then run assertions against. It’s incredibly useful and I use it in all my projects.

It’s particularly critical when using heavy JavaScript applications such as Vue or React apps, or even full-blown SPAs. While Rocket doesn’t fit into those categories, I still want to confirm that the user interface works as expected. I’ve found many bugs through Dusk that I wouldn’t have found if I’d just run tests on the server side with PHPUnit.

Since we’re using the native LoginController provided by Laravel, we don’t really need to test that, however we do still need to test that our user interface / login form allows us to sign in correctly:

use App\Models\User;
use App\Types\DuskTest;
use Laravel\Dusk\Browser;

class LoginTest extends DuskTest
{
    /** @test */
    public function a_user_can_sign_in()
    {
        $this->browse(function(Browser $browser) {
            $user = factory(User::class, 1)->create()->first();

            $browser->visit('/login')
                ->assertTitle(config('rocket.name') . ' - Login')
                ->assertSee('Sign in');

            $browser->type('email', $user->email)
                ->type('password', 'password')
                ->check('remember\_me')
                ->press('#btn-login')
                ->pause(500);

            $browser->assertPathIs('/dashboard')
                ->assertSee('Dashboard');
        });
    }
}

Okay, let’s break this down. First, we call the browse method, which tells the testing framework to launch the browser and provides it to us as an instance of $browser that we can interact with.

Next, we’re creating a user, which is standard stuff.

We then tell the browser to visit the login url, and confirm that the page title is what we expect, and that we can see ‘Sign in’ somewhere on the page.

The next section concerns the form. We tell the browser to type the user’s email into the email field, then we do the same with the password. After that, we check the remember_me checkbox, and press the login button.

We then wait for 500 milliseconds to give the browser time to work (while not essential, I find this to be a useful step to prevent problems).

Finally, we confirm that the browser has been redirected to the dashboard url and that we can see ‘Dashboard’ somewhere on the page.

Wrapping up

We now have a proper authentication system, and can add a user and change their password whenever we need to. Since that’s now done, we can finally move on to the “projects” section.

That’s our next job in part #5. I hope you’re excited for it!

To ensure you’re notified when it comes out, why not go ahead and follow me here on Medium, or better yet, on Twitter, where I’ll also be posting additional updates as well as links to new articles.

Thanks, and have a great day! 😎

Posted on Apr 8 '19 by:

mattkingshott profile

Matt Kingshott 👨🏻‍💻

@mattkingshott

Founder. Developer. Writer. Lunatic. Created Pulse, IodineJS, Axiom, and more. #PHP #Laravel #Vue #TailwindCSS

Discussion

markdown guide