Skip to content

Commit

Permalink
cleanup
Browse files Browse the repository at this point in the history
  • Loading branch information
holgerk committed Mar 30, 2024
1 parent 77820a9 commit 78c8555
Show file tree
Hide file tree
Showing 8 changed files with 96 additions and 95 deletions.
3 changes: 1 addition & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/)
and this project adheres to [Semantic Versioning](http://semver.org/).

## [Unreleased]
## [1.0.0] - 2024-03-30

## [Unreleased](first-commit-link)
### Added
- First version
49 changes: 21 additions & 28 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,22 @@
# Golden Testing for Pest
# Golden Testing for [Pest](https://pestphp.com)

[![run-tests](https://github.com/holgerk/pest-plugin-equal-golden/actions/workflows/tests.yml/badge.svg)](https://github.com/holgerk/pest-plugin-equal-golden/actions/workflows/tests.yml)

Golden Master Testing verifies software changes by comparing their output with a known good version.
This package helps to automate the creation of the good version by adding a `toEqualGolden` expectation to
**[Pest](https://pestphp.com)**.
The provided `toEqualGolden` assertion is similar to the `toEqual` assertion, but with automatic
expectation generation.

So, if you add:

```php
expect(['color' => 'golden'])
->toEqualGolden(null);
```
...to your test and execute it. The `null` is replaced by the actual value:

```php
expect(['color' => 'golden'])
->toEqualGolden(['color' => 'golden']);
```

In principle, it's about saving oneself the recurring work of writing or copying an expectation.

Expand All @@ -23,30 +35,6 @@ composer require holgerk/pest-plugin-equal-golden --dev
Just pass `null` to the `toEqualGolden` expectation and `null` will be automatically replaced during the
first test run.

Given you run this test:

```php
function makeCustomer(): array {
return [
'name' => 'Frank',
'favoriteColor' => 'red',
];
}
it('returns customer record', function () {
expect(makeCustomer())->toEqualGolden(null);
});
```
...after the first execution, the code is:
```php
// [...]
it('returns customer record', function () {
expect(makeCustomer())->toEqualGolden([
'name' => 'Frank',
'favoriteColor' => 'red',
]);
});
```

Later you can edit the expectation by hand or insert `null` again to have it automatically replaced.
If you want to regenerate all expectations at once you can add the argument: `--update-golden` to your pest
invocation.
Expand All @@ -61,6 +49,11 @@ Please see [CHANGELOG](CHANGELOG.md) for more information on what has changed re
Please see [CONTRIBUTING](CONTRIBUTING.md) for details.


## See Also

- [pest-plugin-snapshots](https://github.com/spatie/pest-plugin-snapshots)


## Credits

- [Nuno Maduro](https://github.com/nunomaduro)
Expand Down
4 changes: 2 additions & 2 deletions composer.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "holgerk/pest-plugin-assert-golden",
"description": "My awesome plugin",
"name": "holgerk/pest-plugin-equal-golden",
"description": "Similar to the `toEqual` assertion, but with automatic expectation generation.",
"keywords": [
"php",
"framework",
Expand Down
15 changes: 14 additions & 1 deletion src/Autoload.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,17 @@

declare(strict_types=1);

require_once __DIR__.'/Expectations.php';
use Holgerk\EqualGolden\Insertion;
use Holgerk\EqualGolden\Plugin;
use Pest\Expectation;
use Symfony\Component\VarExporter\VarExporter;

expect()->extend('toEqualGolden', function (mixed $golden): Expectation {
if ($golden === null || Plugin::$updateGolden) {
$golden = $this->value;
$replacement = VarExporter::export($golden);
Plugin::registerInsertion(Insertion::make($replacement));
}

return $this->toEqual($golden);
});
33 changes: 0 additions & 33 deletions src/Expectations.php

This file was deleted.

13 changes: 12 additions & 1 deletion src/Insertion.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,19 @@
/** @internal */
final class Insertion
{
public static function make(string $filePath, int $lineToFind, string $replacement): self
public static function make(string $replacement): self
{
$filePath = null;
$lineToFind = null;
foreach (debug_backtrace() as $stackItem) {
if (($stackItem['type'] ?? '') === '->' && ($stackItem['args'][0] ?? '') === 'toEqualGolden') {
$filePath = $stackItem['file'];
$lineToFind = $stackItem['line'];
break;
}
}
assert((bool) $filePath);

$parser = (new ParserFactory())->createForHostVersion();
$fileContent = file_get_contents($filePath);
$ast = $parser->parse($fileContent);
Expand Down
37 changes: 29 additions & 8 deletions src/Plugin.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,14 @@

namespace Holgerk\EqualGolden;

// use Pest\Contracts\Plugins\AddsOutput;
use Pest\Contracts\Plugins\AddsOutput;
use Pest\Contracts\Plugins\HandlesArguments;
use Pest\Contracts\Plugins\Terminable;
use Pest\Plugins\Concerns\HandleArguments;
use Symfony\Component\Console\Output\OutputInterface;

/** @internal */
final class Plugin implements Terminable, HandlesArguments
final class Plugin implements HandlesArguments, Terminable, AddsOutput
{
use HandleArguments;

Expand All @@ -19,6 +20,18 @@ final class Plugin implements Terminable, HandlesArguments

public static bool $updateGolden = false;

private OutputInterface $output;

public function __construct(OutputInterface $output)
{
$this->output = $output;
}

public static function registerInsertion(Insertion $insertion): void
{
self::$insertions[] = $insertion;
}

public function handleArguments(array $arguments): array
{
if ($this->hasArgument('--update-golden', $arguments)) {
Expand All @@ -29,23 +42,31 @@ public function handleArguments(array $arguments): array
return $arguments;
}

public static function registerInsertion(Insertion $insertion): void
public function terminate(): void
{
self::$insertions[] = $insertion;
$this->writeAndResetInsertions();
}

public function terminate(): void
public function addOutput(int $exitCode): int
{
$this->writeAndResetInsertions();
foreach (self::$insertions as $insertion) {
$this->output->writeln([
sprintf(
' <fg=white;options=bold;bg=blue> INFO </> Writing expectations to: %s.',
$insertion->file
),
]);
}

return $exitCode;
}

private function writeAndResetInsertions(): void
{
$insertions = self::$insertions;
self::$insertions = [];

// sort insertions from end of file to the beginning, so we do net mess up the positions
// through the replacements
// arrange insertions starting from the end of the file to prevent disrupting the positions during replacements
usort($insertions, function (Insertion $a, Insertion $b): int {
if ($a->file !== $b->file) {
return $a->file <=> $b->file;
Expand Down
37 changes: 17 additions & 20 deletions tests/ExpectationsTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,26 @@
use Holgerk\EqualGolden\Plugin;
use PHPUnit\Framework\Assert;
use PHPUnit\Framework\ExpectationFailedException;
use Symfony\Component\Console\Output\NullOutput;

beforeEach(fn () => Plugin::$updateGolden = false);

test('generate expectation', function (string $case) {
// GIVEN
$caseDirectory = __DIR__.'/cases/'.$case;
$inputFile = $caseDirectory.'/input.php';
$testFile = $caseDirectory.'/test.php';
$expectedFile = $caseDirectory.'/expected.php';
copy($inputFile, $testFile);

// WHEN
include $testFile;
(new Plugin(new NullOutput()))->terminate(); // <- forces write

// THEN
Assert::assertFileEquals($expectedFile, $testFile);
})->with(array_map('basename', glob(__DIR__.'/cases/*')));

test('pass', function () {
expect([1, 2, 3])->toEqualGolden([
1,
Expand All @@ -14,26 +31,6 @@
]);
});

foreach (array_map('basename', glob(__DIR__.'/cases/*')) as $case) {

test('generate golden - '.$case, function () use ($case) {
// GIVEN
$caseDirectory = __DIR__.'/cases/'.$case;
$inputFile = $caseDirectory.'/input.php';
$testFile = $caseDirectory.'/test.php';
$expectedFile = $caseDirectory.'/expected.php';
copy($inputFile, $testFile);

// WHEN
include $testFile;
(new Plugin())->terminate(); // <- forces write

// THEN
Assert::assertFileEquals($expectedFile, $testFile);
});

}

test('failures', function () {
expect([1, 2, 3])->toEqualGolden([4]);
})->throws(ExpectationFailedException::class);

0 comments on commit 78c8555

Please sign in to comment.