Skip to content

Commit

Permalink
Add unit tests and the related github action
Browse files Browse the repository at this point in the history
  • Loading branch information
J-Ben87 committed Feb 21, 2023
1 parent ed0c3aa commit 1f09f9b
Show file tree
Hide file tree
Showing 12 changed files with 664 additions and 1 deletion.
60 changes: 60 additions & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
name: 'Tests'

on:
push:
branches: ['*']

jobs:
unit:
name: 'Unit tests'
runs-on: 'ubuntu-latest'
env:
SYMFONY_REQUIRE: '${{matrix.symfony-require}}'

strategy:
matrix:
php-version: ['7.4', '8.0', '8.1', '8.2']
symfony-require: ['5.4.*', '6.0.*', '6.1.*', '6.2.*']

steps:
- name: 'Setup PHP'
uses: 'shivammathur/setup-php@v2'
with:
php-version: '${{ matrix.php-version }}'
coverage: 'none'

- name: 'Checkout sources'
uses: 'actions/checkout@v3'

- name: 'Install dependencies'
run: 'composer install -q --no-ansi --no-interaction --no-scripts --no-progress --prefer-dist'

- name: 'Execute Unit tests'
run: 'vendor/bin/phpunit'

coverage:
name: 'Code coverage'
runs-on: 'ubuntu-latest'
env:
SYMFONY_REQUIRE: '6.2.*'

steps:
- name: 'Setup PHP'
uses: 'shivammathur/setup-php@v2'
with:
php-version: '8.2'
coverage: 'pcov'

- name: 'Checkout sources'
uses: 'actions/checkout@v3'

- name: 'Install dependencies'
run: 'composer install -q --no-ansi --no-interaction --no-scripts --no-progress --prefer-dist'

- name: 'Execute Unit tests'
run: 'vendor/bin/phpunit --coverage-clover=coverage.xml'

- name: 'Upload coverage file'
uses: 'codecov/codecov-action@v3'
with:
files: 'coverage.xml'
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
PrestaImageBundle
=================

![quality](https://github.com/prestaconcept/PrestaImageBundle/actions/workflows/quality.yml/badge.svg)
![tests](https://github.com/prestaconcept/PrestaImagebundle/actions/workflows/tests.yml/badge.svg)
![quality](https://github.com/prestaconcept/PrestaImagebundle/actions/workflows/quality.yml/badge.svg)
[![codecov](https://codecov.io/gh/prestaconcept/PrestaImagebundle/branch/4.x/graph/badge.svg?token=ls4VjT51Pi)](https://codecov.io/gh/prestaconcept/PrestaImagebundle)
[![Latest Stable Version](https://poser.pugx.org/presta/image-bundle/v/stable.png)](https://packagist.org/packages/presta/image-bundle)
[![Total Downloads](https://poser.pugx.org/presta/image-bundle/downloads.png)](https://packagist.org/packages/presta/image-bundle)

Expand Down
8 changes: 8 additions & 0 deletions src/Form/DataTransformer/Base64ToImageTransformer.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,16 @@ public function reverseTransform($value): ?UploadedFile

$filepath = tempnam(sys_get_temp_dir(), 'UploadedFile');
if (!\is_string($filepath)) {
// @codeCoverageIgnoreStart
throw new \RuntimeException('Could not generate a valid temporary file path.');
// @codeCoverageIgnoreEnd
}

$file = fopen($filepath, 'w');
if (!\is_resource($file)) {
// @codeCoverageIgnoreStart
throw new \RuntimeException("Could not open the \"$filepath\" file in \"w\" mode.");
// @codeCoverageIgnoreEnd
}

stream_filter_append($file, 'convert.base64-decode');
Expand All @@ -48,12 +52,16 @@ public function reverseTransform($value): ?UploadedFile
fclose($file);

if (!\is_string($filename)) {
// @codeCoverageIgnoreStart
throw new \RuntimeException('Could not get the generated file uri from metadata.');
// @codeCoverageIgnoreEnd
}

$mimeType = mime_content_type($filename);
if (!\is_string($mimeType)) {
// @codeCoverageIgnoreStart
throw new \RuntimeException('Could not guess the image mime type.');
// @codeCoverageIgnoreEnd
}

$extension = str_replace('image/', '', $mimeType);
Expand Down
Empty file removed tests/.gitignore
Empty file.
30 changes: 30 additions & 0 deletions tests/App/Model/Book.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?php

declare(strict_types=1);

namespace Presta\ImageBundle\Tests\App\Model;

use Symfony\Component\HttpFoundation\File\File;

final class Book
{
public ?File $image = null;
public ?string $imageName = null;

private function __construct()
{
}

public static function withoutFile(): self
{
return new self();
}

public static function withFile(string $imageName): self
{
$book = new self();
$book->imageName = $imageName;

return $book;
}
}
Binary file added tests/App/Resources/images/A.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
82 changes: 82 additions & 0 deletions tests/Unit/Form/DataTransformer/Base64ToImageTransformerTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
<?php

declare(strict_types=1);

namespace Presta\ImageBundle\Tests\Unit\Form\DataTransformer;

use Presta\ImageBundle\Form\DataTransformer\Base64ToImageTransformer;
use PHPUnit\Framework\TestCase;
use Symfony\Component\HttpFoundation\File\File;

final class Base64ToImageTransformerTest extends TestCase
{
/**
* @dataProvider validOriginalValues
*
* @param mixed $value
*/
public function testTransformingAValidValue(string $mimeType, $value): void
{
$transformer = new Base64ToImageTransformer();
$transformedValue = $transformer->transform($value);

self::assertIsArray($transformedValue);
self::assertArrayHasKey('base64', $transformedValue);
self::assertStringStartsWith("data:$mimeType;base64,", $transformedValue['base64']);
}

/**
* @dataProvider invalidOriginalValues
*
* @param mixed $expected
* @param mixed $value
*/
public function testTransformingAnInvalidValue($expected, $value): void
{
$transformer = new Base64ToImageTransformer();

self::assertSame($expected, $transformer->transform($value));
}

/**
* @dataProvider invalidTransformedValues
*
* @param mixed $expected
* @param mixed $value
*/
public function testReverseTransform($expected, $value): void
{
$transformer = new Base64ToImageTransformer();

self::assertSame($expected, $transformer->reverseTransform($value));
}

public function validOriginalValues(): iterable
{
yield 'a ' . File::class . ' object related to a file on the filesystem should return a base64' => [
'image/jpeg',
new File(dirname(__DIR__) . '/../../App/Resources/images/A.jpg'),
];
}

public function invalidOriginalValues(): iterable
{
yield 'an empty value (null) should return an empty (null) base64' => [['base64' => null], null];
yield 'a value different from '
. File::class
. ' should return an empty (null) base64' => [['base64' => null], new \stdClass()];
yield 'a '
. File::class
. ' object not related to a file on the filesystem should return an empty (null) base64' => [
['base64' => null],
new File('/tmp/foo.png', false),
];
}

public function invalidTransformedValues(): iterable
{
yield 'an empty value (null) should return null' => [null, null];
yield 'a value different from an array should return null' => [null, null];
yield 'an array without a "base64" key should return null' => [null, null];
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
<?php

declare(strict_types=1);

namespace Presta\ImageBundle\Tests\Unit\Form\EventListener\ImageType;

use Presta\ImageBundle\Exception\UnexpectedTypeException;
use Presta\ImageBundle\Form\EventListener\ImageType\AddDeleteCheckboxListener;
use Presta\ImageBundle\Form\Type\ImageType;
use Presta\ImageBundle\Tests\App\Model\Book;
use Presta\ImageBundle\Tests\Unit\Form\ImageTypeTestCase;
use Symfony\Component\Form\Extension\Core\Type\FormType;
use Symfony\Component\Form\FormEvent;

final class AddDeleteCheckboxListenerTest extends ImageTypeTestCase
{
/**
* @dataProvider deletableData
*/
public function testAnImageTypeChildShouldHaveADeleteCheckboxIfCreated(Book $data): void
{
$this->storage
->expects($this->once())
->method('resolvePath')
->with($data, 'image')
->willReturn('/tmp/foo.png')
;

$form = $this->factory->create(FormType::class, $data)->add('image', ImageType::class);

$listener = new AddDeleteCheckboxListener($this->storage, 'foo', 'messages');
$listener(new FormEvent($form->get('image'), $form->get('image')->getData()));

$this->assertTrue($form->get('image')->has('delete'));
}

/**
* @dataProvider notDeletableData
*
* @param mixed $data
*/
public function testAnImageTypeChildShouldNotHaveADeleteCheckboxIfCreated($data): void
{
$this->storage->method('resolvePath')->willReturn(null);

$form = $this->factory->create(FormType::class, $data)->add('image', ImageType::class);

$listener = new AddDeleteCheckboxListener($this->storage, 'foo', 'messages');
$listener(new FormEvent($form->get('image'), $form->get('image')->getData()));

$this->assertFalse($form->get('image')->has('delete'));
}

public function testShouldCauseAnExceptionIfCreatedAsRootForm(): void
{
$this->expectException(\RuntimeException::class);

$form = $this->factory->create(ImageType::class);

$listener = new AddDeleteCheckboxListener($this->storage, 'foo', 'messages');
$listener(new FormEvent($form, $form->getData()));
}

public function testShouldCauseAnExceptionIfCreatedWithAnArrayAsData(): void
{
$this->expectException(UnexpectedTypeException::class);

$form = $this->factory->create(FormType::class, [])->add('image', ImageType::class);

$listener = new AddDeleteCheckboxListener($this->storage, 'foo', 'messages');
$listener(new FormEvent($form->get('image'), $form->get('image')->getData()));
}

public function deletableData(): iterable
{
yield 'an object related to a file stored on the filesystem' => [Book::withoutFile()];
}

public function notDeletableData(): iterable
{
yield 'no data (null)' => [null];
yield 'an object not related to a file stored on the filesystem' => [Book::withoutFile()];
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
<?php

declare(strict_types=1);

namespace Presta\ImageBundle\Tests\Unit\Form\EventListener\ImageType;

use Presta\ImageBundle\Form\EventListener\ImageType\ClearBase64OnDeleteListener;
use Presta\ImageBundle\Form\Type\ImageType;
use Presta\ImageBundle\Tests\Unit\Form\ImageTypeTestCase;
use Symfony\Component\Form\FormEvent;

final class ClearBase64OnDeleteListenerTest extends ImageTypeTestCase
{
/**
* @dataProvider deletableSubmittedData
*/
public function testShouldClearTheSubmittedBase64DataIfSubmitted(array $submittedData): void
{
$form = $this->factory->create();
$event = new FormEvent($form, array_merge($submittedData, ['base64' => 'foo']));

$listener = new ClearBase64OnDeleteListener();
$listener($event);

$data = $event->getData();
\assert(\is_array($data));

$this->assertArrayHasKey('base64', $data);
$this->assertNull($data['base64']);
}

/**
* @dataProvider notDeletableSubmittedData
*/
public function testShouldNotClearTheSubmittedBase64DataIfSubmitted(array $submittedData): void
{
$form = $this->factory->create();
$event = new FormEvent($form, array_merge($submittedData, ['base64' => 'foo']));

$listener = new ClearBase64OnDeleteListener();
$listener($event);

$data = $event->getData();
\assert(\is_array($data));

$this->assertSame('foo', $data['base64']);
}

public function testShouldEndUpWithNullBase64DataIfSubmittedWithNullData(): void
{
$form = $this->factory->create()->add('image', ImageType::class, self::ALLOW_DELETE_OPTIONS);
$event = new FormEvent($form, null);

$listener = new ClearBase64OnDeleteListener();
$listener($event);

$this->assertNull($event->getData());
}

public function deletableSubmittedData(): iterable
{
yield 'the "delete" checkbox checked' => [
['delete' => true],
];
}

public function notDeletableSubmittedData(): iterable
{
yield 'no "delete" checkbox data' => [
[],
];
yield 'the "delete" checkbox not checked' => [
['delete' => false],
];
}
}
Loading

0 comments on commit 1f09f9b

Please sign in to comment.