Skip to content

Commit 4bd7c23

Browse files
authored
Merge pull request #30 from szhajdu/configure-headers
Add configurable default headers + maintenance
2 parents 7fda0eb + bfd1ce6 commit 4bd7c23

21 files changed

Lines changed: 731 additions & 95 deletions

File tree

.github/workflows/ci.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ jobs:
99
strategy:
1010
max-parallel: 10
1111
matrix:
12-
php: [ '8.0', '8.1', '8.2', '8.3' ]
12+
php: [ '8.0', '8.1', '8.2', '8.3', '8.4', '8.5' ]
1313

1414
steps:
1515
- name: Set up PHP
@@ -20,7 +20,7 @@ jobs:
2020
tools: composer:v2
2121

2222
- name: Checkout code
23-
uses: actions/checkout@v4
23+
uses: actions/checkout@v5
2424

2525
- name: Download dependencies
2626
run: composer update --no-interaction --prefer-dist

.github/workflows/static-analysis.yml

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,12 @@ jobs:
99

1010
steps:
1111
- name: Checkout code
12-
uses: actions/checkout@v4
12+
uses: actions/checkout@v5
1313

1414
- name: Setup PHP
1515
uses: shivammathur/setup-php@v2
1616
with:
17-
php-version: '8.1'
17+
php-version: '8.5'
1818
coverage: none
1919

2020
- name: Download dependencies
@@ -32,12 +32,12 @@ jobs:
3232

3333
steps:
3434
- name: Checkout code
35-
uses: actions/checkout@v4
35+
uses: actions/checkout@v5
3636

3737
- name: Setup PHP
3838
uses: shivammathur/setup-php@v2
3939
with:
40-
php-version: '8.1'
40+
php-version: '8.5'
4141
coverage: none
4242

4343
- name: Download dependencies
@@ -55,12 +55,12 @@ jobs:
5555

5656
steps:
5757
- name: Checkout code
58-
uses: actions/checkout@v4
58+
uses: actions/checkout@v5
5959

6060
- name: Setup PHP
6161
uses: shivammathur/setup-php@v2
6262
with:
63-
php-version: '8.1'
63+
php-version: '8.5'
6464
coverage: none
6565

6666
- name: Download dependencies

.php-cs-fixer.dist.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,9 @@
4949
],
5050
'phpdoc_separation' => true,
5151
'phpdoc_single_line_var_spacing' => true,
52-
'phpdoc_to_comment' => true,
52+
'phpdoc_to_comment' => [
53+
'ignored_tags' => ['var'],
54+
],
5355
'phpdoc_trim' => true,
5456
'phpdoc_var_without_name' => true,
5557
'return_type_declaration' => [

CHANGELOG-4.0.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,18 @@ All notable changes to this project will be documented in this file.
44
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
55
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
66

7+
## [4.3.0] - 2026-03-20
8+
### Added
9+
- PHP 8.4 support
10+
- PHP 8.5 support
11+
- Configurable default headers support via `headers` module configuration
12+
- Improved test coverage with unit and functional tests
13+
14+
### Changed
15+
- Upgraded PHPStan to v2
16+
- Upgraded Psalm to v6
17+
- Updated static analysis workflow to use PHP 8.5
18+
719
## [4.2.0] - 2024-03-15
820
### Added
921
- PHP 8.3 support

README.md

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,23 @@ modules:
5151
```
5252
5353
The `application` property is a relative path to file which returns your `Slim\App` instance.
54+
55+
You can also configure default headers that will be sent with every request using the `headers` option:
56+
57+
```yaml
58+
modules:
59+
enabled:
60+
- REST:
61+
depends: DoclerLabs\CodeceptionSlimModule\Module\Slim
62+
63+
config:
64+
DoclerLabs\CodeceptionSlimModule\Module\Slim:
65+
application: path/to/application.php
66+
headers:
67+
Content-Type: application/json
68+
Accept: application/json
69+
```
70+
5471
Here is the minimum `application.php` content:
5572

5673
```php
@@ -68,7 +85,6 @@ return $app;
6885
## Testing your API endpoints
6986

7087
```php
71-
7288
class UserCest
7389
{
7490
public function getUserReturnsWithEmail(FunctionalTester $I): void
@@ -86,3 +102,32 @@ class UserCest
86102
}
87103
}
88104
```
105+
106+
### With default headers
107+
108+
When you configure default `headers` in your suite configuration, you no longer need to set them manually in each test:
109+
110+
```php
111+
class UserCest
112+
{
113+
// Content-Type and Accept headers are already set via module config
114+
public function getUserReturnsWithEmail(FunctionalTester $I): void
115+
{
116+
$I->sendGET('/users/John');
117+
118+
$I->seeResponseCodeIs(200);
119+
$I->seeResponseContainsJson(
120+
[
121+
'email' => 'john.doe@example.com',
122+
]
123+
);
124+
}
125+
126+
public function createUserReturnsCreated(FunctionalTester $I): void
127+
{
128+
$I->sendPOST('/users', ['name' => 'Jane', 'email' => 'jane.doe@example.com']);
129+
130+
$I->seeResponseCodeIs(201);
131+
}
132+
}
133+
```

composer.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,8 @@
2929
"autoload-dev": {
3030
"psr-4": {
3131
"DoclerLabs\\CodeceptionSlimModule\\Test\\": "test/support",
32-
"DoclerLabs\\CodeceptionSlimModule\\Test\\Functional\\": "test/suite/functional"
32+
"DoclerLabs\\CodeceptionSlimModule\\Test\\Functional\\": "test/suite/functional",
33+
"DoclerLabs\\CodeceptionSlimModule\\Test\\Unit\\": "test/suite/unit"
3334
}
3435
},
3536
"config": {

phpstan.neon.dist

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,12 @@ includes:
33

44
parameters:
55
level: max
6-
checkMissingIterableValueType: false
6+
ignoreErrors:
7+
-
8+
identifier: missingType.iterableValue
79
paths:
810
- src
911
fileExtensions:
1012
- php
13+
bootstrapFiles:
14+
- vendor/autoload.php

psalm.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,9 @@
1313
<directory name="vendor" />
1414
</ignoreFiles>
1515
</projectFiles>
16+
17+
<issueHandlers>
18+
<MissingOverrideAttribute errorLevel="suppress" />
19+
<UnusedClass errorLevel="suppress" />
20+
</issueHandlers>
1621
</psalm>

src/Lib/Connector/SlimPsr7.php

Lines changed: 20 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
namespace DoclerLabs\CodeceptionSlimModule\Lib\Connector;
66

7+
use Psr\Container\ContainerInterface;
78
use Psr\Http\Message\UploadedFileInterface;
89
use Slim\App;
910
use Slim\Psr7\Cookies;
@@ -16,11 +17,15 @@
1617
use Symfony\Component\BrowserKit\Request as BrowserKitRequest;
1718
use Symfony\Component\BrowserKit\Response as BrowserKitResponse;
1819

20+
/**
21+
* @extends AbstractBrowser<BrowserKitRequest, BrowserKitResponse>
22+
*/
1923
class SlimPsr7 extends AbstractBrowser
2024
{
21-
/** @var App */
22-
private $app;
25+
/** @var App<ContainerInterface|null> */
26+
private App $app;
2327

28+
/** @param App<ContainerInterface|null> $app */
2429
public function setApp(App $app): void
2530
{
2631
$this->app = $app;
@@ -104,33 +109,29 @@ private function convertToHeaders(array $serverVariables): Headers
104109
/**
105110
* Convert uploaded file list to UploadedFile instances.
106111
*
107-
* @param array $files List of uploaded file instances, that implements `Psr\Http\Message\UploadedFileInterface`,
108-
* or meta data about uploaded file items from $_FILES, indexed with field name.
109-
*
110112
* @return array<string, UploadedFileInterface>
111113
*/
112114
private function convertFiles(array $files): array
113115
{
114116
$uploadedFiles = [];
115117
foreach ($files as $fieldName => $file) {
116118
if ($file instanceof UploadedFileInterface) {
117-
$uploadedFiles[$fieldName] = $file;
118-
} elseif (!isset($file['tmp_name']) && !isset($file['name'])) {
119-
$uploadedFiles[$fieldName] = $this->createUploadedFile($file);
119+
$uploadedFiles[(string)$fieldName] = $file;
120+
} elseif (
121+
is_array($file)
122+
&& isset($file['tmp_name'], $file['name'])
123+
) {
124+
/** @var array{tmp_name: string, name: string, type?: string|null, size?: int|null, error?: int} $file */
125+
$uploadedFiles[(string)$fieldName] = new UploadedFile(
126+
$file['tmp_name'],
127+
$file['name'],
128+
$file['type'] ?? null,
129+
$file['size'] ?? null,
130+
$file['error'] ?? UPLOAD_ERR_OK
131+
);
120132
}
121133
}
122134

123135
return $uploadedFiles;
124136
}
125-
126-
private function createUploadedFile(array $file): UploadedFile
127-
{
128-
return new UploadedFile(
129-
$file['tmp_name'],
130-
$file['name'],
131-
$file['type'],
132-
$file['size'],
133-
$file['error']
134-
);
135-
}
136137
}

src/Module/Slim.php

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
use Codeception\Lib\Framework;
1111
use Codeception\TestInterface;
1212
use DoclerLabs\CodeceptionSlimModule\Lib\Connector\SlimPsr7;
13+
use Psr\Container\ContainerInterface;
1314
use Slim\App;
1415

1516
/**
@@ -20,13 +21,16 @@
2021
* ### Slim 4.x
2122
*
2223
* * application - Relative path to file which bootstrap and returns your `Slim\App` instance.
24+
* * headers - Default headers to be sent with every request (optional).
2325
*
2426
* #### Example (`test/suite/functional.suite.yml`)
2527
* ```yaml
2628
* modules:
2729
* config:
2830
* DoclerLabs\CodeceptionSlimModule\Module\Slim:
2931
* application: 'app/bootstrap.php'
32+
* headers:
33+
* Content-Type: application/json
3034
* ```
3135
*
3236
* ## Public Properties
@@ -45,22 +49,26 @@
4549
* config:
4650
* DoclerLabs\CodeceptionSlimModule\Module\Slim:
4751
* application: 'app/bootstrap.php'
52+
* headers:
53+
* Content-Type: application/json
4854
* ```
4955
*/
5056
class Slim extends Framework
5157
{
52-
/** @var App */
53-
public $app;
58+
/** @var App<ContainerInterface|null> */
59+
public App $app;
5460

55-
/** @var array */
5661
protected array $requiredFields = ['application'];
5762

58-
/** @var string */
59-
private $applicationPath;
63+
protected array $config = ['headers' => []];
64+
65+
private string $applicationPath;
6066

6167
public function _initialize(): void
6268
{
63-
$applicationPath = Configuration::projectDir() . $this->config['application'];
69+
/** @var string $configApplication */
70+
$configApplication = $this->config['application'];
71+
$applicationPath = Configuration::projectDir() . $configApplication;
6472
if (!is_readable($applicationPath)) {
6573
throw new ModuleConfigException(
6674
static::class,
@@ -75,11 +83,10 @@ public function _initialize(): void
7583

7684
public function _before(TestInterface $test): void
7785
{
78-
/* @noinspection PhpIncludeInspection */
79-
$this->app = require $this->applicationPath;
86+
$app = require $this->applicationPath;
8087

8188
// Check if app instance is ready.
82-
if (!$this->app instanceof App) {
89+
if (!$app instanceof App) {
8390
throw new ConfigurationException(
8491
sprintf(
8592
"Unable to bootstrap slim application.\n Application file must return with `%s` instance.",
@@ -88,11 +95,17 @@ public function _before(TestInterface $test): void
8895
);
8996
}
9097

98+
$this->app = $app;
99+
91100
$connector = new SlimPsr7();
92101
$connector->setApp($this->app);
93102

94103
$this->client = $connector;
95104

105+
/** @var array<string, string> $headers */
106+
$headers = $this->config['headers'];
107+
$this->headers = $headers;
108+
96109
parent::_before($test);
97110
}
98111
}

0 commit comments

Comments
 (0)