Skip to content

Commit

Permalink
Add Pest tests
Browse files Browse the repository at this point in the history
  • Loading branch information
bencroker committed Jan 6, 2025
1 parent a706393 commit 388ed91
Show file tree
Hide file tree
Showing 12 changed files with 284 additions and 14 deletions.
3 changes: 2 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@
},
"require-dev": {
"craftcms/ecs": "dev-main",
"craftcms/phpstan": "dev-main"
"craftcms/phpstan": "dev-main",
"pestphp/pest": "^3.0"
},
"autoload": {
"psr-4": {
Expand Down
12 changes: 12 additions & 0 deletions phpunit.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/|version|/phpunit.xsd"
bootstrap="tests/bootstrap.php"
colors="true"
>
<testsuites>
<testsuite name="Test Suite">
<directory suffix="Test.php">tests</directory>
</testsuite>
</testsuites>
</phpunit>
32 changes: 21 additions & 11 deletions src/Http/Controllers/DatastarController.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,17 +21,13 @@ public function __construct(
) {
}

/**
* Default controller action.
*/
public function index(Request $request): StreamedResponse
{
$config = $request->input('config');

if (strtolower($request->header('Content-Type')) === 'application/json') {
// Clear out params to prevent them from being processed by controller actions.
$request->query->replace();
$request->request->replace();
}

$config = Config::fromHashed($config);
$hashedConfig = $request->input('config');
$config = Config::fromHashed($hashedConfig);
if ($config === null) {
throw new BadRequestHttpException('Submitted data was tampered.');
}
Expand All @@ -43,12 +39,26 @@ public function index(Request $request): StreamedResponse
$config->variables,
);

if (strtolower($request->header('Content-Type')) === 'application/json') {
// Clear out params to prevent them from being processed by controller actions.
$request->query->replace();
$request->request->replace();
}

$response = new StreamedResponse(function() use ($view, $variables) {
view($view, $variables)->render();
$this->stream($view, $variables);
});

$this->sse->setResponseHeaders($response);
$this->sse->prepareResponse($response);

return $response;
}

/**
* Streams the response.
*/
protected function stream(string $view, array $variables): void
{
view($view, $variables)->render();
}
}
4 changes: 2 additions & 2 deletions src/Services/Sse.php
Original file line number Diff line number Diff line change
Expand Up @@ -87,9 +87,9 @@ public function executeScript(string $script, array $options = []): void
}

/**
* Sets the response headers.
* Prepares the response for server sent events.
*/
public function setResponseHeaders(StreamedResponse $response): void
public function prepareResponse(StreamedResponse $response): void
{
foreach (ServerSentEventGenerator::HEADERS as $name => $value) {
$response->headers->set($name, $value);
Expand Down
1 change: 1 addition & 0 deletions tests/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/test-results.xml
5 changes: 5 additions & 0 deletions tests/Architecture/ArchitectureTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<?php

arch()
->expect('Putyourlightson\Datastar')
->not->toUse(['die', 'dd', 'dump', 'var_dump']);
103 changes: 103 additions & 0 deletions tests/Feature/SignalsTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
<?php

use Putyourlightson\Datastar\Models\Signals;
use Putyourlightson\Datastar\Services\Sse;

beforeEach(function() {
$sse = Mockery::mock(Sse::class);
app()->instance(Sse::class, $sse);
});

test('Test getting a signal value', function() {
$signals = new Signals(['a' => 1]);
expect($signals->get('a'))
->toBe(1);
});

test('Test getting a signal value using a magic call', function() {
$signals = new Signals(['a' => 1]);
expect($signals->a)
->toBe(1);
});

test('Test getting a nested signal value', function() {
$signals = new Signals(['a' => ['b' => ['c' => 1]]]);
expect($signals->get('a.b.c'))
->toBe(1);
});

test('Test getting a missing signal value', function() {
$signals = new Signals(['a' => 1]);
expect($signals->get('x'))
->toBeNull();
});

test('Test getting a missing signal value using a magic call', function() {
$signals = new Signals(['a' => 1]);
expect($signals->x)
->toBeNull();
});

test('Test adding a signal', function() {
app(Sse::class)->shouldReceive('mergeSignals');
$signals = new Signals([]);
$signals->set('a', 1);
expect($signals->get('a'))
->toBe(1);
});

test('Test adding a signal using a magic call', function() {
app(Sse::class)->shouldReceive('mergeSignals');
$signals = new Signals([]);
$signals->a(1);
expect($signals->get('a'))
->toBe(1);
});

test('Test modifying an existing signal', function() {
app(Sse::class)->shouldReceive('mergeSignals');
$signals = new Signals(['a' => 1]);
$signals->set('a', 2);
expect($signals->get('a'))
->toBe(2);
});

test('Test modifying an existing signal using a magic call', function() {
app(Sse::class)->shouldReceive('mergeSignals');
$signals = new Signals(['a' => 1]);
$signals->a(2);
expect($signals->get('a'))
->toBe(2);
});

test('Test adding a nested signal', function() {
app(Sse::class)->shouldReceive('mergeSignals');
$signals = new Signals([]);
$signals->set('a.b.c', 1);
expect($signals->get('a.b.c'))
->toBe(1);
});

test('Test modifying an existing nested signal', function() {
app(Sse::class)->shouldReceive('mergeSignals');
$signals = new Signals(['a' => ['b' => ['c' => 1]]]);
$signals->set('a.b.c', 2);
expect($signals->get('a.b.c'))
->toBe(2);
});

test('Test removing a signal value', function() {
app(Sse::class)->shouldReceive('removeSignals');
$signals = new Signals(['a' => 1]);
$signals->remove('a');
expect($signals->getValues())
->toBe([]);
});

test('Test removing a nested signal value', function() {
app(Sse::class)->shouldReceive('removeSignals');
$signals = new Signals(['a' => ['b' => ['c' => 1]]]);
$signals->remove('a.b.c');
expect($signals->getValues())
->toBe(['a' => ['b' => []]]);
});
13 changes: 13 additions & 0 deletions tests/Feature/SseTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php

/**
* Tests the SSE service.
*/

use Putyourlightson\Datastar\Services\Sse;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;

test('Test that calling an SSE method when another one is in process throws an exception', function() {
app(Sse::class)->setSseInProcess('mergeFragments');
app(Sse::class)->mergeSignals([]);
})->throws(BadRequestHttpException::class);
44 changes: 44 additions & 0 deletions tests/Feature/VariableTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<?php

use Putyourlightson\Datastar\Helpers\Datastar;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;

test('Test creating an action', function(string $method) {
$helper = new Datastar();
$value = $helper->$method('view');
expect($value)
->toStartWith("@$method(")
->toContain('view');

if ($method === 'get') {
expect($value)
->not->toContain('X-CSRF-TOKEN');
} else {
expect($value)
->toContain('X-CSRF-TOKEN');
}
})->with([
'get',
'post',
'put',
'patch',
'delete',
]);

test('Test creating an action containing an array of primitive variables', function() {
$helper = new Datastar();
$value = $helper->get('template', ['x' => 1, 'y' => 'string', 'z' => true]);
expect($value)
->toContain('1', 'string', 'true');
});

test('Test that creating an action containing a reserved variable name throws an exception', function() {
$helper = new Datastar();
$signalsVariableName = config('datastar.signalsVariableName');
$helper->get('template', [$signalsVariableName => 1]);
})->throws(BadRequestHttpException::class);

test('Test that creating an action containing an object variable throws an exception', function() {
$helper = new Datastar();
$helper->get('template', ['object' => new stdClass()]);
})->throws(BadRequestHttpException::class);
39 changes: 39 additions & 0 deletions tests/Pest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<?php

use Illuminate\Foundation\Testing\TestCase;

/*
|--------------------------------------------------------------------------
| Test Case
|--------------------------------------------------------------------------
|
| The closure you provide to your test functions is always bound to a specific PHPUnit test
| case class. By default, that class is "PHPUnit\Framework\TestCase". Of course, you may
| need to change it using the "pest()" function to bind a different classes or traits.
|
*/

uses(TestCase::class)
->in('./');

/*
|--------------------------------------------------------------------------
| Expectations
|--------------------------------------------------------------------------
|
| When you're writing tests, you often need to check that values meet certain conditions. The
| "expect()" function gives you access to a set of "expectations" methods that you can use
| to assert different things. Of course, you may extend the Expectation API at any time.
|
*/

/*
|--------------------------------------------------------------------------
| Functions
|--------------------------------------------------------------------------
|
| While Pest is very powerful out-of-the-box, you may have some testing code specific to your
| project that you don't want to repeat in every file. Here you can also expose helpers as
| global functions to help you to reduce the number of lines of code in your test files.
|
*/
37 changes: 37 additions & 0 deletions tests/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# Testing

## Static Analysis

To run static analysis on the plugin, install [PHPStan for Craft CMS](https://github.com/craftcms/phpstan) and run the following command from the root of your project.

```shell
./vendor/bin/phpstan analyse -c vendor/putyourlightson/craft-datastar-module/phpstan.neon --memory-limit 1G
```

## Easy Coding Standard

To run the Easy Coding Standard on the plugin, install [ECS for Craft CMS](https://github.com/craftcms/ecs) and run the following command from the root of your project.

```shell
./vendor/bin/ecs check -c vendor/putyourlightson/craft-datastar-module/ecs.php
```

## Pest Tests

To run Pest tests, first install [Craft Pest](https://craft-pest.com/) core as a dev dependency.

```shell
composer require markhuot/craft-pest-core:^2.0.0 --dev
```

Then run the following command from the root of your project.

```shell
php php vendor/bin/pest --configuration=vendor/putyourlightson/craft-datastar-module/phpunit.xml --test-directory=vendor/putyourlightson/craft-datastar-module/tests
```

Or to run a specific test.

```shell
php php vendor/bin/pest --configuration=vendor/putyourlightson/craft-datastar-module/phpunit.xml --test-directory=vendor/putyourlightson/craft-datastar-module/tests --filter=StoreTest
```
5 changes: 5 additions & 0 deletions tests/bootstrap.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<?php

define('PROJECT_VENDOR_DIR', getenv('PROJECT_VENDOR_DIR') ?: dirname(__DIR__) . '/vendor');

require realpath(PROJECT_VENDOR_DIR . '/autoload.php');

0 comments on commit 388ed91

Please sign in to comment.