Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
52 / 52
100.00% covered (success)
100.00%
11 / 11
CRAP
100.00% covered (success)
100.00%
1 / 1
LibraryCreateBookContext
100.00% covered (success)
100.00%
52 / 52
100.00% covered (success)
100.00%
11 / 11
15
100.00% covered (success)
100.00%
1 / 1
 whenISubmitTheBookCreationFormWithTitleAndAuthorAndIsbn
100.00% covered (success)
100.00%
12 / 12
100.00% covered (success)
100.00%
1 / 1
1
 whenICreateABookViaApiWithTitleAndAuthorAndIsbn
100.00% covered (success)
100.00%
12 / 12
100.00% covered (success)
100.00%
1 / 1
1
 thenIShouldBeRedirected
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 thenIShouldNotBeRedirected
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 thenIShouldSeeAnErrorMessage
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 thenIShouldGetAStatusCode
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 thenIShouldGetAStatusCodeWithError
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
1
 thenTheBookShouldBeCreatedSuccessfully
100.00% covered (success)
100.00%
13 / 13
100.00% covered (success)
100.00%
1 / 1
5
 iShouldSee
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 iShouldSeeASubmitButton
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
 getRepository
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2
3// ╔════════════════════════════════════════════════════════════╗
4// ║ MIT Licence (#Expat) - https://opensource.org/licenses/MIT ║
5// ║ Copyright 2026 Frederic Poeydomenge <dyno@phexium.com>     ║
6// ╚════════════════════════════════════════════════════════════╝
7
8declare(strict_types=1);
9
10namespace Tests\AppDemo\Acceptance;
11
12use AppDemo\Library\Application\Api\CreateBookController as CreateBookControllerApi;
13use AppDemo\Library\Application\Http\CreateBookController as CreateBookControllerHtml;
14use AppDemo\Library\Domain\Author;
15use AppDemo\Library\Domain\BookRepository;
16use AppDemo\Library\Domain\ISBN;
17use AppDemo\Library\Domain\Title;
18use Assert\Assert;
19use Behat\Step\Then;
20use Behat\Step\When;
21use Exception;
22use Nyholm\Psr7\Response;
23use Nyholm\Psr7\ServerRequest as Request;
24use Tests\AppDemo\Acceptance\Trait\BrowsePageTrait;
25
26final class LibraryCreateBookContext extends BehatContext
27{
28    use BrowsePageTrait;
29
30    private Response $response;
31
32    private int $initialBookCount;
33
34    private string $bookTitle;
35
36    private string $bookAuthor;
37
38    private string $bookIsbn;
39
40    private Exception $exception;
41
42    #[When('I submit the book creation form with title ":title" and author ":author" and ISBN ":isbn"')]
43    public function whenISubmitTheBookCreationFormWithTitleAndAuthorAndIsbn(string $title, string $author, string $isbn): void
44    {
45        $this->bookTitle = $title;
46        $this->bookAuthor = $author;
47        $this->bookIsbn = $isbn;
48
49        $this->initialBookCount = $this->getRepository()->findAll()->count();
50
51        $controller = self::$container->get(CreateBookControllerHtml::class);
52
53        $request = new Request('POST', '/');
54        $request = $request->withParsedBody([
55            'title' => $title,
56            'author' => $author,
57            'isbn' => $isbn,
58        ]);
59
60        $this->response = $controller->createBook($request, new Response());
61    }
62
63    #[When('I create a book via API with title ":title" and author ":author" and ISBN ":isbn"')]
64    public function whenICreateABookViaApiWithTitleAndAuthorAndIsbn(string $title, string $author, string $isbn): void
65    {
66        $this->bookTitle = $title;
67        $this->bookAuthor = $author;
68        $this->bookIsbn = $isbn;
69
70        $this->initialBookCount = $this->getRepository()->findAll()->count();
71
72        $controller = self::$container->get(CreateBookControllerApi::class);
73
74        $request = new Request('POST', '/');
75        $request = $request->withParsedBody([
76            'title' => $title,
77            'author' => $author,
78            'isbn' => $isbn,
79        ]);
80
81        $this->response = $controller->index($request, new Response());
82    }
83
84    #[Then('I should be redirected')]
85    public function thenIShouldBeRedirected(): void
86    {
87        Assert::that($this->response->getStatusCode())->same(302);
88    }
89
90    #[Then('I should not be redirected')]
91    public function thenIShouldNotBeRedirected(): void
92    {
93        Assert::that($this->response->getStatusCode())->notSame(302);
94    }
95
96    #[Then('I should see an error message ":errorMessage"')]
97    public function thenIShouldSeeAnErrorMessage(string $errorMessage): void
98    {
99        $body = (string) $this->response->getBody();
100        Assert::that($body)->contains(htmlentities($errorMessage));
101    }
102
103    #[Then('I should get a :statusCode status code')]
104    public function thenIShouldGetAStatusCode(int $statusCode): void
105    {
106        Assert::that($this->response->getStatusCode())->same($statusCode);
107    }
108
109    #[Then('I should get a :statusCode status code with error ":errorMessage"')]
110    public function thenIShouldGetAStatusCodeWithError(int $statusCode, string $errorMessage = ''): void
111    {
112        Assert::that($this->response->getStatusCode())->same($statusCode);
113
114        $body = json_decode((string) $this->response->getBody());
115        $error = $body->error;
116        Assert::that($error)->contains($errorMessage);
117    }
118
119    #[Then('/the book should( not)? be created/')]
120    public function thenTheBookShouldBeCreatedSuccessfully(bool $not = false): void
121    {
122        $shouldBeCreated = $not === false;
123
124        $newBookCount = $this->getRepository()->findAll()->count();
125        Assert::that($newBookCount)->same($this->initialBookCount + ($shouldBeCreated ? 1 : 0));
126
127        if (!$shouldBeCreated) {
128            return;
129        }
130
131        $books = $this->getRepository()->findAll();
132        $found = array_any(
133            $books->items(),
134            fn ($book): bool => $book->getTitle()->equals(Title::fromString($this->bookTitle))
135                && $book->getAuthor()->equals(Author::fromString($this->bookAuthor))
136                && $book->getIsbn()->equals(ISBN::fromString($this->bookIsbn))
137        );
138
139        Assert::that($found)->true();
140    }
141
142    #[Then('I should see :text')]
143    public function iShouldSee(string $text): void
144    {
145        $body = (string) $this->response->getBody();
146        Assert::that($body)->contains($text);
147    }
148
149    #[Then('I should see a submit button')]
150    public function iShouldSeeASubmitButton(): void
151    {
152        $body = (string) $this->response->getBody();
153        Assert::that($body)->contains('<button');
154        Assert::that($body)->contains('type="submit"');
155    }
156
157    private function getRepository(): BookRepository
158    {
159        return self::$container->get(BookRepository::class);
160    }
161}