...

/

Project Challenge: Solution

Project Challenge: Solution

Let's look at the solution to the challenge given in the previous lesson.

We'll cover the following...

Solution

Here is the complete implementation of the challenge. Check it out!

<?php

use PHPUnit\Framework\TestCase;
use Symfony\Component\DomCrawler\Crawler;
use Symfony\Component\Panther\PantherTestCaseTrait;
use Symfony\Component\BrowserKit\HttpBrowser;

final class TasksTest extends TestCase
{
    use PantherTestCaseTrait;

    private HttpBrowser $client;

    // Helper function to check if current user has a task
    private function currentUserHasTask(string $task): bool
    {   
        // Make a request for the /list-tasks page
        $response = $this->client->request('GET', '/list-tasks');
        // Check if there is a task
        return strpos($response->filter('ul.tasks')->text(), $task) !== false;
    }

    protected function setUp(): void
    {
        $tasksJsonFile = __DIR__ . '/../data/tasks.json';
        if (is_file($tasksJsonFile)) {
            unlink($tasksJsonFile);
        }

        $this->client = self::createHttpBrowserClient(
            [
                'webServerDir' => __DIR__ . '/../public/'
            ]
        );
    }

    protected function tearDown(): void
    {
        $this->client->request('GET', '/logout');
    }

    /**
     * @test
     * @dataProvider securePaths
     */
    public function you_need_to_be_logged_in(string $path): void
    {
        // Fetching URLs
        $response = $this->client->request('GET', self::$baseUri . $path);
        // Check if redirected to /login page
        self::assertStringContainsString('/login', $response->getUri(), 'Expected to be redirected to the login page');
    }

    public function securePaths(): Generator
    {
        yield ['/list-tasks'];
        yield ['/create-task'];
    }

    /**
     * @test
     */
    public function after_logging_in_you_have_access_to_the_list_of_tasks(): void
    {
        // Login
        $this->login();
        // Redirecting to /list-tasks page
        $response = $this->client->request('GET', self::$baseUri . '/list-tasks');
        // Check if the page consists the right string
        self::assertStringContainsString('Tasks', $response->text());
    }

    /**
     * @test
     */
    public function you_can_create_a_task(): void
    {
        // Login
        $this->login();
        // Create a task
        $task = 'Build some Lego';

        $this->createTask($task);
        // Check if the task has been created
        self::assertTrue($this->currentUserHasTask($task));
    }

    /**
     * @test
     */
    public function you_can_edit_a_task(): void
    {
        // Login
        $this->login();
        // Create a Task
        $task = 'Build some Lego';
        $this->createTask($task);
        // Clicking edit button
        $response = $this->client->clickLink('Edit');
        // Check if the obtained task is equal to expected task
        self::assertEquals($task, $response->filter('input#task')->attr('value'));
        // Edit task
        $newTask = 'Buy some Lego';

        $this->client->submitForm(
            'Create',
            [
                'task' => $newTask
            ]
        );
        // Check if the task has been edited
        self::assertTrue($this->currentUserHasTask($newTask));
    }

    /**
     * @test
     */
    public function you_can_not_provide_an_empty_string_as_a_task(): void
    {
        // Login
        $this->login();
        // Create an empty string as a task
        $response = $this->createTask('');
        // Check if the right error message shows up
        self::assertStringContainsString(
            'Task can not be empty',
            $response->filter('strong')->text()
        );
    }

    /**
     * @test
     */
    public function you_can_not_see_other_tasks_of_other_users(): void
    {
        // Login as matthias
        $this->loginAs('matthias');
        // Create a task
        $task = 'Build some Lego';
        $this->createTask($task);
        // Login as tomas
        $this->loginAs('tomas');
        // Check if tomas can not see the task created by matthias
        self::assertFalse($this->currentUserHasTask($task));
    }

    /**
     * @test
     */
    public function you_can_not_edit_the_task_of_another_user(): void
    {
        // Login as matthias
        $this->loginAs('matthias');
        // Create a task
        $task = 'Build some Lego';
        $this->createTask($task);
        // Login as tomas
        $this->loginAs('tomas');
        
        // Task 1 will be owned by matthias, but tomas is the logged in user
        $response = $this->client->request('GET', self::$baseUri . '/edit-task?id=1');
        // Check if the right error is generated
        self::assertStringContainsString('You can not edit a task created by another user', $response->text());
    }

    /**
     * @test
     */
    public function you_can_mark_a_task_as_done_and_it_disappears_from_the_list(): void
    {
        // Log in as matthias
        $this->loginAs('matthias');
        // Create a task
        $task = 'Build some Lego';
        $this->createTask($task);
        // Click Done
        $this->client->submitForm('Done');
        // Check if the user has no task
        self::assertFalse($this->currentUserHasTask($task));
    }

    /**
     * @test
     */
    public function you_can_not_mark_the_task_of_another_user_as_done(): void
    {
        // Log in as matthias
        $this->loginAs('matthias');
        // Create a task
        $task = 'Build some Lego';
        $this->createTask($task);
        // Log in as tomas
        $this->loginAs('tomas');

        // Task 1 will be owned by matthias, but tomas is the logged in user
        $response = $this->client->request('POST', self::$baseUri . '/mark-as-done', ['id' => '1']);
        // Check if the right error message is generated
        self::assertStringContainsString('You can not mark a task created by another user as done', $response->text());
        // Log in as matthias
        $this->loginAs('matthias');
        // Check if the task is still there 
        self::assertTrue($this->currentUserHasTask($task));
    }

    // Helper functions to log in
    private function login(): void
    {
        $this->loginAs('matthias');
    }

    private function loginAs(string $username): void
    {
        $this->client->request('GET', self::$baseUri . '/login');

        $this->client->submitForm(
            'Submit',
            [
                'username' => $username,
                'password' => 'test'
            ]
        );
    }

    // Helper function to create task
    private function createTask(string $task): Crawler
    {
        $this->client->request('GET', self::$baseUri . '/create-task');

        return $this->client->submitForm(
            'Create',
            [
                'task' => $task
            ]
        );
    }
}

Explanation

Let’s have a look at the solution in detail.

tests/TasksTest.php file

Tests 1 & 2: You need to be logged in.

  • At line 49, we made requests for the /list-task and /create-task pages.
  • At line 51, we asserted if the unauthenticated user is redirected to the /login page.

Test 3: You get redirected to the list of tasks after logging in.

  • At line 66, we called the login function.
  • At line 68, we request the /list-tasks page.
  • At line 70, we
...