Skip to content

Commit 71ce1d0

Browse files
committed
refactor(test): switch to PHPUnit 10 for PHP 8.5
1 parent 2a958f1 commit 71ce1d0

22 files changed

Lines changed: 2092 additions & 1279 deletions

β€Žcomposer.jsonβ€Ž

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,8 @@
4242
"psr/http-message": "^1.0|^2.0"
4343
},
4444
"require-dev": {
45+
"phpunit/phpunit": "^10.5",
4546
"doctrine/coding-standard": "^14.0",
46-
"pestphp/pest": "^2.36",
4747
"phpstan/phpstan": "^1.12",
4848
"squizlabs/php_codesniffer": "^4.0"
4949
},
@@ -63,7 +63,7 @@
6363
"phpstan analyse -l max src tests"
6464
],
6565
"lint:fix": "phpcbf",
66-
"tests": "XDEBUG_MODE=coverage pest --coverage --coverage-html coverage_html --coverage-clover coverage.xml",
66+
"tests": "XDEBUG_MODE=coverage phpunit --coverage-html coverage_html --coverage-clover coverage.xml",
6767
"all": [
6868
"@composer run lint:fix",
6969
"@composer run lint",

β€Žphpcs.xml.distβ€Ž

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@
1616
<!-- Include full Doctrine Coding Standard -->
1717
<rule ref="Doctrine"/>
1818

19-
<!-- Remove this specific rule for tests as it causes troubles with Pest -->
20-
<rule ref="SlevomatCodingStandard.Functions.StaticClosure.ClosureNotStatic">
19+
<!-- We want to allow snake_case -->
20+
<rule ref="PSR1.Methods.CamelCapsMethodName.NotCamelCaps">
2121
<exclude-pattern>tests/**</exclude-pattern>
2222
</rule>
2323
</ruleset>

β€Žphpstan.neonβ€Ž

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,30 @@
11
parameters:
22
ignoreErrors:
33
-
4-
message: '#Call to an undefined method Pest\\Expectation<string\|null>::toContainFormValue\(\).#'
4+
message: '#Method Gotenberg\\Test\\Modules\\ChromiumPdfTest::provideUrlData\(\) should return array#'
55
path: tests/*
66
-
7-
message: '#Call to an undefined method Pest\\Expectation<string\|null>::toContainFormFile\(\).#'
7+
message: '#Method Gotenberg\\Test\\Modules\\ChromiumPdfTest::provideHtmlData\(\) should return array#'
88
path: tests/*
99
-
10-
message: '#Undefined variable: \$this#'
10+
message: '#Method Gotenberg\\Test\\Modules\\ChromiumPdfTest::provideMarkdownData\(\) should return array#'
11+
path: tests/*
12+
-
13+
message: '#Method Gotenberg\\Test\\Modules\\ChromiumScreenshotTest::provideUrlData\(\) should return array#'
14+
path: tests/*
15+
-
16+
message: '#Method Gotenberg\\Test\\Modules\\ChromiumScreenshotTest::provideHtmlData\(\) should return array#'
17+
path: tests/*
18+
-
19+
message: '#Method Gotenberg\\Test\\Modules\\ChromiumScreenshotTest::provideMarkdownData\(\) should return array#'
20+
path: tests/*
21+
22+
-
23+
message: '#Method Gotenberg\\Test\\Modules\\PdfEnginesTest::provideMergeData\(\) should return array#'
24+
path: tests/*
25+
-
26+
message: '#Method Gotenberg\\Test\\Modules\\PdfEnginesTest::provideSplitData\(\) should return array#'
27+
path: tests/*
28+
-
29+
message: '#Method Gotenberg\\Test\\Modules\\PdfEnginesTest::provideConvertData\(\) should return array#'
1130
path: tests/*

β€Žtests/ApiModuleTest.phpβ€Ž

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,22 @@
22

33
declare(strict_types=1);
44

5-
use Gotenberg\Test\DummyApiModule;
5+
namespace Gotenberg\Test;
66

7-
it(
8-
'creates a valid request with a trace header',
9-
function (): void {
7+
use Gotenberg\Test\Helpers\Dummies\DummyApiModule;
8+
use PHPUnit\Framework\Attributes\Test;
9+
10+
final class ApiModuleTest extends TestCase
11+
{
12+
#[Test]
13+
public function it_creates_a_valid_request_with_a_trace_header(): void
14+
{
1015
$dummy = new DummyApiModule('https://my.url/');
1116
$request = $dummy
1217
->trace('debug')
1318
->build();
1419

15-
expect($dummy->getUrl())->toBe('https://my.url');
16-
expect($request->getHeader('Gotenberg-Trace'))->toMatchArray(['debug']);
17-
},
18-
);
20+
$this->assertSame('https://my.url', $dummy->getUrl());
21+
$this->assertSame(['debug'], $request->getHeader('Gotenberg-Trace'));
22+
}
23+
}

β€Žtests/GotenbergTest.phpβ€Ž

Lines changed: 65 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -2,69 +2,96 @@
22

33
declare(strict_types=1);
44

5+
namespace Gotenberg\Test;
6+
57
use Gotenberg\Exceptions\GotenbergApiErrored;
68
use Gotenberg\Exceptions\NoOutputFileInResponse;
79
use Gotenberg\Gotenberg;
8-
use Gotenberg\Test\DummyClient;
10+
use Gotenberg\Test\Helpers\Dummies\DummyClient;
911
use GuzzleHttp\Psr7\Request;
1012
use GuzzleHttp\Psr7\Response;
13+
use PHPUnit\Framework\Attributes\DataProvider;
14+
use PHPUnit\Framework\Attributes\Test;
15+
16+
use function sys_get_temp_dir;
17+
use function unlink;
18+
19+
use const DIRECTORY_SEPARATOR;
1120

12-
it(
13-
'sends a request',
14-
function (): void {
21+
final class GotenbergTest extends TestCase
22+
{
23+
#[Test]
24+
public function it_sends_a_request(): void
25+
{
1526
$response = new Response(200, ['Gotenberg-Trace' => 'debug']);
1627
$client = new DummyClient($response);
1728

1829
$response = Gotenberg::send(new Request('POST', 'https://my.url'), $client);
1930

20-
expect($response)->not()->toBeNull();
21-
},
22-
);
31+
$this->assertNotNull($response);
32+
}
2333

24-
it(
25-
'sends a request and throws an exception if response is not 2xx',
26-
function (bool $withTrace): void {
34+
#[Test]
35+
#[DataProvider('provideTraceData')]
36+
public function it_sends_a_request_and_throws_an_exception_if_response_is_not_2xx(bool $withTrace): void
37+
{
2738
$response = new Response(400, $withTrace ? ['Gotenberg-Trace' => 'debug'] : [], 'Bad Request');
2839
$client = new DummyClient($response);
2940

3041
try {
3142
Gotenberg::send(new Request('POST', 'https://my.url'), $client);
43+
$this->fail('Exception was expected but not thrown.');
3244
} catch (GotenbergApiErrored $e) {
33-
expect($e->getCode())->toEqual(400);
34-
expect($e->getMessage())->toEqual('Bad Request');
35-
expect($e->getGotenbergTrace())->toEqual($withTrace ? 'debug' : '');
36-
expect($e->getResponse())->toBe($response);
37-
38-
throw $e;
45+
$this->assertSame(400, $e->getCode());
46+
$this->assertSame('Bad Request', $e->getMessage());
47+
$this->assertSame($withTrace ? 'debug' : '', $e->getGotenbergTrace());
48+
$this->assertSame($response, $e->getResponse());
3949
}
40-
},
41-
)->with([
42-
'with trace' => [ true ],
43-
'without trace' => [ false ],
44-
])->throws(GotenbergApiErrored::class);
45-
46-
it(
47-
'saves the output file',
48-
function (): void {
50+
}
51+
52+
/** @return array<string, array{bool}> */
53+
public static function provideTraceData(): array
54+
{
55+
return [
56+
'with trace' => [true],
57+
'without trace' => [false],
58+
];
59+
}
60+
61+
#[Test]
62+
public function it_saves_the_output_file(): void
63+
{
4964
$response = new Response(200, ['Content-Disposition' => 'attachment; filename=my.pdf']);
5065
$client = new DummyClient($response);
5166

52-
$filename = Gotenberg::save(new Request('POST', 'https://my.url'), sys_get_temp_dir(), $client);
67+
$tempDir = sys_get_temp_dir();
68+
$filename = Gotenberg::save(new Request('POST', 'https://my.url'), $tempDir, $client);
69+
70+
$filePath = $tempDir . DIRECTORY_SEPARATOR . 'my.pdf';
5371

54-
expect(unlink(sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'my.pdf'))->toBeTrue();
55-
expect($filename)->toEqual('my.pdf');
56-
},
57-
);
72+
// verify file exists and delete it
73+
$this->assertTrue(unlink($filePath));
74+
$this->assertSame('my.pdf', $filename);
75+
}
5876

59-
it(
60-
'throws an exception if there is no attachment',
61-
function (string|null $contentDisposition): void {
77+
#[Test]
78+
#[DataProvider('provideContentDispositionData')]
79+
public function it_throws_an_exception_if_there_is_no_attachment(string|null $contentDisposition): void
80+
{
6281
$response = new Response(200, $contentDisposition === null ? [] : ['Content-Disposition' => $contentDisposition]);
6382
$client = new DummyClient($response);
6483

84+
$this->expectException(NoOutputFileInResponse::class);
85+
6586
Gotenberg::save(new Request('POST', 'https://my.url'), sys_get_temp_dir(), $client);
66-
},
67-
)->with([
68-
'without content disposition' => [ null ],
69-
'with content disposition' => [ 'no attachment' ],
70-
])->throws(NoOutputFileInResponse::class);
87+
}
88+
89+
/** @return array<string, array{0: string|null}> */
90+
public static function provideContentDispositionData(): array
91+
{
92+
return [
93+
'without content disposition' => [null],
94+
'with content disposition' => ['no attachment'],
95+
];
96+
}
97+
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Gotenberg\Test\Helpers\Constraints;
6+
7+
use PHPUnit\Framework\Constraint\Constraint;
8+
9+
use function is_string;
10+
use function mb_strlen;
11+
use function sprintf;
12+
use function str_contains;
13+
14+
final class FormFileConstraint extends Constraint
15+
{
16+
public function __construct(
17+
private readonly string $filename,
18+
private readonly string $content,
19+
private readonly string|null $contentType = null,
20+
private readonly string $fieldName = 'files',
21+
) {
22+
}
23+
24+
protected function matches(mixed $other): bool
25+
{
26+
if (! is_string($other)) {
27+
return false;
28+
}
29+
30+
$length = mb_strlen($this->content);
31+
32+
$needle = 'Content-Disposition: form-data; name="'
33+
. $this->fieldName
34+
. '"; filename="'
35+
. $this->filename
36+
. '" Content-Length: '
37+
. $length;
38+
39+
if ($this->contentType !== null) {
40+
$needle .= ' Content-Type: ' . $this->contentType;
41+
}
42+
43+
return str_contains($other, $needle);
44+
}
45+
46+
public function toString(): string
47+
{
48+
return sprintf(
49+
'contains the form file "%s" with content length %d for field "%s"',
50+
$this->filename,
51+
mb_strlen($this->content),
52+
$this->fieldName,
53+
);
54+
}
55+
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Gotenberg\Test\Helpers\Constraints;
6+
7+
use PHPUnit\Framework\Constraint\Constraint;
8+
9+
use function is_string;
10+
use function mb_strlen;
11+
use function sprintf;
12+
use function str_contains;
13+
14+
final class FormValueConstraint extends Constraint
15+
{
16+
public function __construct(
17+
private readonly string $name,
18+
private readonly string $value,
19+
) {
20+
}
21+
22+
protected function matches(mixed $other): bool
23+
{
24+
if (! is_string($other)) {
25+
return false;
26+
}
27+
28+
$length = mb_strlen($this->value);
29+
30+
$needle = 'Content-Disposition: form-data; name="'
31+
. $this->name
32+
. '" Content-Length: '
33+
. $length
34+
. ' '
35+
. $this->value;
36+
37+
return str_contains($other, $needle);
38+
}
39+
40+
public function toString(): string
41+
{
42+
return sprintf(
43+
'contains the form value "%s" for field "%s"',
44+
$this->value,
45+
$this->name,
46+
);
47+
}
48+
}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
declare(strict_types=1);
44

5-
namespace Gotenberg\Test;
5+
namespace Gotenberg\Test\Helpers\Dummies;
66

77
use Gotenberg\ApiModule;
88
use Psr\Http\Message\RequestInterface;
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
declare(strict_types=1);
44

5-
namespace Gotenberg\Test;
5+
namespace Gotenberg\Test\Helpers\Dummies;
66

77
use Psr\Http\Client\ClientInterface;
88
use Psr\Http\Message\RequestInterface;
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
declare(strict_types=1);
44

5-
namespace Gotenberg\Test;
5+
namespace Gotenberg\Test\Helpers\Dummies;
66

77
use Gotenberg\Index;
88

0 commit comments

Comments
Β (0)