Skip to content
Snippets Groups Projects
Commit 98a02347 authored by Thomas Flori's avatar Thomas Flori
Browse files

add test and ci setup and test locating views

parent 3178aaef
No related branches found
No related tags found
No related merge requests found
language: php
php:
- 7.1
- 7.2
sudo: false
cache:
directories:
- $HOME/.composer/cache
matrix:
fast_finish: true
before_script:
- composer install --no-interaction
- sh -c 'if [ "$TRAVIS_PHP_VERSION" = "7.1" ]; then composer require satooshi/php-coveralls:~0.6@stable; fi;'
- mkdir -p build/logs
script:
- composer code-style
- vendor/bin/phpunit -c phpunit.xml --coverage-clover=build/logs/clover.xml --coverage-text
- sh -c 'if [ "$TRAVIS_PHP_VERSION" = "7.1" ]; then php vendor/bin/coveralls -v; fi;'
......@@ -10,6 +10,13 @@
}
},
"require-dev": {
"phpunit/phpunit": "^7.4"
"phpunit/phpunit": "^7.4",
"mockery/mockery": "^1.2",
"squizlabs/php_codesniffer": "^3.3"
},
"autoload-dev": {
"psr-4": {
"Syna\\Test\\": "tests"
}
}
}
<?xml version="1.0" encoding="UTF-8"?>
<phpunit bootstrap="vendor/autoload.php"
backupGlobals="false"
backupStaticAttributes="false"
verbose="true"
colors="true"
convertErrorsToExceptions="true"
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
processIsolation="false"
stopOnFailure="false"
printerClass="\Syna\Test\Printer">
<testsuite name="tests">
<directory>./tests/</directory>
</testsuite>
<filter>
<whitelist processUncoveredFilesFromWhitelist="true">
<directory suffix=".php">./src/</directory>
</whitelist>
</filter>
</phpunit>
......@@ -36,7 +36,7 @@ class ViewLocator
public function add($name, $path): self
{
if (file_exists($path)) {
if (!file_exists($path)) {
throw new \LogicException('File ' . $path . ' does not exist');
}
......@@ -53,7 +53,7 @@ class ViewLocator
foreach (array_reverse($this->paths) as $path) {
$viewPath = $path . DIRECTORY_SEPARATOR . $name . $this->extension;
if (file_exists($viewPath)) {
$this->map[$name] = $viewPath;
$this->map[$name] = realpath($viewPath);
return true;
}
}
......
<?php
namespace Syna\Test\Locating;
use Syna\Test\TestCase;
use Syna\ViewLocator;
class LocatingViewsTest extends TestCase
{
/** @test */
public function locatesPhtmlTemplatesByDefault()
{
$this->createTemplate('greeting.phtml', 'Hello <?= $name ?>!');
$locator = new ViewLocator($this->templatePath);
$result = $locator->has('greeting');
self::assertTrue($result);
}
/** @test */
public function returnsFullPathToTemplate()
{
$fullPath = $this->createTemplate('greeting.phtml', 'Hello <?= $name ?>!');
$locator = new ViewLocator($this->templatePath);
$path = $locator->getPath('greeting');
self::assertSame($fullPath, $path);
}
/** @test */
public function isNotConfusedByAdditionalSlashes()
{
$fullPath = $this->createTemplate('form/text.phtml', '<input type="text" name="<?= $e($name) ?>" />');
$locator = new ViewLocator($this->templatePath);
$path = $locator->getPath('/form//text');
self::assertSame($fullPath, $path);
}
/** @test */
public function throwsWhenViewIsNotAvailable()
{
$locator = new ViewLocator($this->templatePath);
self::expectException(\Exception::class);
self::expectExceptionMessage('View test not found');
$locator->getPath('test');
}
/** @test */
public function viewsCanManuallyBeAddedWithDifferentNames()
{
$fullPath = $this->createTemplate('greeting.phtml', 'Hello <?= $name ?>!');
$locator = new ViewLocator('/etc');
$locator->add('greet', $fullPath);
self::assertSame($fullPath, $locator->getPath('greet'));
}
/** @test */
public function throwsWhenFileDoesNotExist()
{
$locator = new ViewLocator('/');
self::expectException(\LogicException::class);
self::expectExceptionMessage('File /var/template.phtml does not exist');
$locator->add('template', '/var/template.phtml');
}
/** @test */
public function searchesTemplatesInReversedOrder()
{
$defaultPath = $this->createTemplate('default/greeting.phtml', 'Hello <?= $name ?>!');
$themedPath = $this->createTemplate('theme/greeting.phtml', '<p>Hello <?= $name ?>!</p>');
$locator = new ViewLocator($this->templatePath . '/default');
$locator->addPath($this->templatePath . '/theme');
$path = $locator->getPath('greeting');
self::assertSame($themedPath, $path);
}
/** @test */
public function addFallbackPathWithPrepend()
{
$defaultPath = $this->createTemplate('default/greeting.phtml', 'Hello <?= $name ?>!');
$this->createTemplate('fallback/greeting.phtml', 'Hello <?= $name ?>!');
$fallbackPath = $this->createTemplate('fallback/welcome.phtml', 'Welcome <?= $name ?>!');
$locator = new ViewLocator($this->templatePath . '/default');
$locator->prependPath($this->templatePath . '/fallback');
self::assertSame($defaultPath, $locator->getPath('greeting'));
self::assertSame($fallbackPath, $locator->getPath('welcome'));
}
}
<?php
namespace Syna\Test;
use PHPUnit\Framework\AssertionFailedError;
use PHPUnit\Framework\Test;
use PHPUnit\Framework\Warning;
use PHPUnit\TextUI\ResultPrinter;
class Printer extends ResultPrinter
{
/**
* Replacement symbols for test statuses.
*
* @var array
*/
protected static $symbols = [
'E' => "\e[31m!\e[0m", // red !
'F' => "\e[31m\xe2\x9c\x96\e[0m", // red X
'W' => "\e[33mW\e[0m", // yellow W
'I' => "\e[33mI\e[0m", // yellow I
'R' => "\e[33mR\e[0m", // yellow R
'S' => "\e[36mS\e[0m", // cyan S
'.' => "\e[32m\xe2\x9c\x94\e[0m", // green checkmark
];
/**
* Structure of the outputted test row.
*
* @var string
*/
protected $testRow = '';
/** @var string */
protected $previousClassName = '';
/**
* {@inheritdoc}
*/
protected function writeProgress(string $progress): void
{
if ($this->hasReplacementSymbol($progress)) {
$progress = static::$symbols[$progress];
}
$this->write(" {$progress} {$this->testRow}" . PHP_EOL);
$this->column++;
$this->numTestsRun++;
}
/**
* {@inheritdoc}
*/
public function addError(Test $test, \Throwable $e, float $time): void
{
$this->buildTestRow(get_class($test), $test->getName(), $time, 'fg-red');
parent::addError($test, $e, $time);
}
/**
* {@inheritdoc}
*/
public function addFailure(Test $test, AssertionFailedError $e, float $time): void
{
$this->buildTestRow(get_class($test), $test->getName(), $time, 'fg-red');
parent::addFailure($test, $e, $time);
}
/**
* {@inheritdoc}
*/
public function addWarning(Test $test, Warning $e, float $time): void
{
$this->buildTestRow(get_class($test), $test->getName(), $time, 'fg-yellow');
parent::addWarning($test, $e, $time);
}
/**
* {@inheritdoc}
*/
public function addIncompleteTest(Test $test, \Throwable $e, float $time): void
{
$this->buildTestRow(get_class($test), $test->getName(), $time, 'fg-yellow');
parent::addIncompleteTest($test, $e, $time);
}
/**
* {@inheritdoc}
*/
public function addRiskyTest(Test $test, \Throwable $e, float $time): void
{
$this->buildTestRow(get_class($test), $test->getName(), $time, 'fg-yellow');
parent::addRiskyTest($test, $e, $time);
}
/**
* {@inheritdoc}
*/
public function addSkippedTest(Test $test, \Throwable $e, float $time): void
{
$this->buildTestRow(get_class($test), $test->getName(), $time, 'fg-cyan');
parent::addSkippedTest($test, $e, $time);
}
/**
* {@inheritdoc}
*/
public function endTest(Test $test, float $time): void
{
list($className, $methodName) = \PHPUnit\Util\Test::describe($test);
$this->buildTestRow($className, $methodName, $time);
parent::endTest($test, $time);
}
/**
* {@inheritdoc}
*
* We'll handle the coloring ourselves.
*/
protected function writeProgressWithColor(string $color, string $buffer): void
{
$this->writeProgress($buffer);
}
/**
* Formats the results for a single test.
*
* @param $className
* @param $methodName
* @param $time
* @param $color
*/
protected function buildTestRow($className, $methodName, $time, $color = 'fg-white')
{
if ($className != $this->previousClassName) {
$this->write(PHP_EOL . $this->formatWithColor('fg-magenta', $className) . PHP_EOL);
$this->previousClassName = $className;
}
$this->testRow = sprintf(
"(%s) %s",
$this->formatTestDuration($time),
$this->formatWithColor($color, "{$this->formatMethodName($methodName)}")
);
}
/**
* Makes the method name more readable.
*
* @param $method
* @return mixed
*/
protected function formatMethodName($method)
{
return ucfirst(
$this->splitCamels(
$this->splitSnakes($method)
)
);
}
/**
* Replaces underscores in snake case with spaces.
*
* @param $name
* @return string
*/
protected function splitSnakes($name)
{
return str_replace('_', ' ', $name);
}
/**
* Splits camel-cased names while handling caps sections properly.
*
* @param $name
* @return string
*/
protected function splitCamels($name)
{
return preg_replace('/(?<=[a-z])(?=[A-Z])|(?<=[A-Z])(?=[A-Z][a-z])/', ' $1', $name);
}
/**
* Colours the duration if the test took longer than 500ms.
*
* @param $time
* @return string
*/
protected function formatTestDuration($time)
{
$testDurationInMs = round($time * 1000);
$duration = $testDurationInMs > 500
? $this->formatWithColor('fg-yellow', $testDurationInMs)
: $testDurationInMs;
return sprintf('%s ms', $duration);
}
/**
* Verifies if we have a replacement symbol available.
*
* @param $progress
* @return bool
*/
protected function hasReplacementSymbol($progress)
{
return in_array($progress, array_keys(static::$symbols));
}
/**
* Checks if the class name is in format Class::method.
*
* @param $testName
* @return bool
*/
protected function hasCompoundClassName($testName)
{
return !empty($testName) && strpos($testName, '::') > -1;
}
}
<?php
namespace Syna\Test;
use Mockery\Adapter\Phpunit\MockeryTestCase;
class TestCase extends MockeryTestCase
{
protected $templatePath;
protected function setUp()
{
$this->templatePath = '/tmp/syna-test';
if (file_exists($this->templatePath)) {
system('rm -Rf ' . $this->templatePath);
}
mkdir($this->templatePath);
}
protected function createTemplate(string $path, string $content): string
{
$path = $this->templatePath . DIRECTORY_SEPARATOR . $path;
if (!file_exists(dirname($path))) {
mkdir(dirname($path), 0755, true);
}
file_put_contents($path, $content);
return $path;
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment