Initial commit of the immensely immense task of setting up functional tests for AzuraCast that will integrate with Travis CI and run upon each commit.

This commit is contained in:
Buster Silver 2016-10-18 18:21:33 -05:00
parent 62b575467c
commit 1f23fbc50a
33 changed files with 4624 additions and 205 deletions

3
.gitignore vendored
View File

@ -26,4 +26,5 @@ app/.env
# Ansible deployment files
/ansible/
/util/ansible/deploy.retry
/util/ansible/update.retry
/util/ansible/update.retry
tests/_output/*

18
.travis.yml Normal file
View File

@ -0,0 +1,18 @@
language: php
php:
- 7.0
cache:
directories:
- vendor
- $HOME/.composer/cache
sudo: true
install:
- chmod a+x install.sh
- install.sh
script:
- php codecept run

View File

@ -7,6 +7,9 @@
define("APP_IS_COMMAND_LINE", (PHP_SAPI == "cli"));
define("APP_IS_SECURE", (!APP_IS_COMMAND_LINE && (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == "on")) ? TRUE : FALSE);
if (!defined('APP_TESTING_MODE'))
define('APP_TESTING_MODE', false);
// General includes
define("APP_INCLUDE_BASE", dirname(__FILE__));
define("APP_INCLUDE_ROOT", realpath(APP_INCLUDE_BASE.'/..'));
@ -41,11 +44,9 @@ if (isset($_SERVER['HTTP_X_FORWARDED_PROTO']))
$autoloader = require(APP_INCLUDE_VENDOR . '/autoload.php');
$autoloader->add('App', APP_INCLUDE_LIB);
// Include general utility functions
include(APP_INCLUDE_VENDOR.'/packaged/helpers/src/includes/Phutil.php');
// Set up DI container.
$app_settings = [
'outputBuffering' => false,
'displayErrorDetails' => true,
'addContentLengthHeader' => false,
];
@ -216,6 +217,9 @@ $di['url'] = function($di) {
// Register session service.
$di['session'] = function($di) {
// Depends on cache driver.
$di->get('cache_driver');
return new \App\Session;
};
@ -304,29 +308,38 @@ if (!APP_IS_COMMAND_LINE)
}
// Set up application and routing.
$app = new \Slim\App($di);
$di['app'] = function($di) use ($modules) {
// Remove trailing slash from all URLs when routing.
$app->add(function (\Psr\Http\Message\RequestInterface $request, \Psr\Http\Message\ResponseInterface $response, callable $next) {
$uri = $request->getUri();
$path = $uri->getPath();
$app = new \Slim\App($di);
if ($path != '/' && substr($path, -1) == '/')
// Remove trailing slash from all URLs when routing.
$app->add(function (\Psr\Http\Message\RequestInterface $request, \Psr\Http\Message\ResponseInterface $response, callable $next)
{
// permanently redirect paths with a trailing slash
// to their non-trailing counterpart
$uri = $uri->withPath(substr($path, 0, -1));
return $response->withRedirect((string)$uri, 301);
$uri = $request->getUri();
$path = $uri->getPath();
if ($path != '/' && substr($path, -1) == '/')
{
// permanently redirect paths with a trailing slash
// to their non-trailing counterpart
$uri = $uri->withPath(substr($path, 0, -1));
return $response->withRedirect((string)$uri, 301);
}
return $next($request, $response);
});
// Loop through modules to configure routes.
foreach ($modules as $module)
{
$routes_file = APP_INCLUDE_MODULES . DIRECTORY_SEPARATOR . $module . DIRECTORY_SEPARATOR . 'routes.php';
if (file_exists($routes_file))
include($routes_file);
}
return $next($request, $response);
});
return $app;
// Loop through modules to configure routes.
foreach($modules as $module)
{
$routes_file = APP_INCLUDE_MODULES.DIRECTORY_SEPARATOR.$module.DIRECTORY_SEPARATOR.'routes.php';
};
if (file_exists($routes_file))
include($routes_file);
}
return $di;

View File

@ -69,7 +69,7 @@ class Auth
*/
public function isLoggedIn()
{
if (APP_IS_COMMAND_LINE)
if (APP_IS_COMMAND_LINE && !APP_TESTING_MODE)
return false;
$user = $this->getUser();
@ -135,7 +135,7 @@ class Auth
public function setUser(User $user)
{
// Prevent any previous identity from being used.
session_regenerate_id(TRUE);
// @session_regenerate_id(true);
$this->_session->user_id = $user->id;

View File

@ -12,10 +12,11 @@ class EntityManagerFactory
return false;
// Register custom data types.
Type::addType('json', 'App\Doctrine\Type\Json');
Type::addType('unixdatetime', 'App\Doctrine\Type\UnixDateTime');
Type::addType('binary_uuid', 'App\Doctrine\Type\BinaryUuid');
Type::addType('ip_integer', 'App\Doctrine\Type\IpAddrInteger');
if (!Type::hasType('json'))
Type::addType('json', 'App\Doctrine\Type\Json');
if (!Type::hasType('unixdatetime'))
Type::addType('unixdatetime', 'App\Doctrine\Type\UnixDateTime');
Type::overrideType('array', 'App\Doctrine\Type\SoftArray');
Type::overrideType('datetime', 'App\Doctrine\Type\UTCDateTime');
@ -24,7 +25,7 @@ class EntityManagerFactory
$config = new \Doctrine\ORM\Configuration;
// Handling for class names specified as platform types.
if ($options['conn']['platform'])
if (!empty($options['conn']['platform']))
{
$class_obj = new \ReflectionClass($options['conn']['platform']);
$options['conn']['platform'] = $class_obj->newInstance();

View File

@ -1,59 +0,0 @@
<?php
namespace App\Doctrine\Type;
use Doctrine\DBAL\Platforms\AbstractPlatform;
use Doctrine\DBAL\Types\BinaryType;
class BinaryUuid extends BinaryType
{
const BINARY_UUID = 'binary_uuid';
public function requiresSQLCommentHint(AbstractPlatform $platform)
{
return true;
}
public function convertToDatabaseValue($value, AbstractPlatform $platform)
{
return self::uuidToBin($value);
}
public function convertToPHPValue($value, AbstractPlatform $platform)
{
if ($value === null)
return null;
$value = (is_resource($value)) ? stream_get_contents($value, -1) : $value;
return self::binToUuid($value);
}
public function getName()
{
return self::BINARY_UUID;
}
/**
* Force all fields to be the BINARY type, length 16 (the UUID binary length).
*/
public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform)
{
if ($fieldDeclaration['length'] != 16)
die('"'.$fieldDeclaration['name'].'": Binary UUID fields must all have a length of 16.');
if (!$fieldDeclaration['fixed'])
die('"'.$fieldDeclaration['name'].'": Binary UUID fields must all be fixed length.');
return parent::getSQLDeclaration($fieldDeclaration, $platform);
}
public static function uuidToBin($uuid)
{
return pack("H*" , str_replace('-', '', $uuid));
}
public static function binToUuid($bin)
{
return preg_replace('/^([0-9a-f]{8})([0-9a-f]{4})([0-9a-f]{4})([0-9a-f]{4})([0-9a-f]{12})$/', '$1-$2-$3-$4-$5', bin2hex($bin));
}
}

View File

@ -1,66 +0,0 @@
<?php
namespace App\Doctrine\Type;
use Doctrine\DBAL\Platforms\AbstractPlatform;
use Doctrine\DBAL\Types\IntegerType;
/**
* Substitutes for MySQL's INET_NTOA and INET_ATON functions to handle IPs as unsigned integers.
*
* Class IpAddrInteger
* @package App\Doctrine\Type
*/
class IpAddrInteger extends IntegerType
{
const IP_INTEGER = 'ip_integer';
public function requiresSQLCommentHint(AbstractPlatform $platform)
{
return true;
}
public function convertToDatabaseValue($value, AbstractPlatform $platform)
{
return self::inetAtoN($value);
}
public function convertToPHPValue($value, AbstractPlatform $platform)
{
if ($value === null)
return null;
$value = (is_resource($value)) ? stream_get_contents($value, -1) : $value;
return self::inetNtoA($value);
}
public function getName()
{
return self::IP_INTEGER;
}
/**
* Force all fields to use unsigned integers (if DB layer supports it).
*/
public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform)
{
if (!$fieldDeclaration['unsigned'])
throw new \Exception('"'.$fieldDeclaration['name'].'": IPInteger fields must always be unsigned.');
return parent::getSQLDeclaration($fieldDeclaration, $platform);
}
public static function inetAtoN($ip)
{
$ip = trim($ip);
if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) return 0;
return sprintf("%u", ip2long($ip));
}
public static function inetNtoA($num)
{
$num = trim($num);
if ($num == "0") return "0.0.0.0";
return long2ip(-(4294967295 - ($num - 1)));
}
}

View File

@ -101,8 +101,11 @@ class Form
if (empty($options['groups']))
$options['groups'] = array();
$options['groups'][] = ['elements' => $options['elements']];
unset($options['elements']);
if (!empty($options['elements']))
{
$options['groups'][] = ['elements' => $options['elements']];
unset($options['elements']);
}
// Standardize some field input.
$field_type_lookup = [

View File

@ -46,14 +46,16 @@ class Controller
$this->url = $di['url'];
$this->em = $di['em'];
$this->view->reset();
$common_views_dir = APP_INCLUDE_MODULES.'/'.$module.'/views/scripts';
if (is_dir($common_views_dir))
{
$this->view->addFolder('common', $common_views_dir);
$this->view->setFolder('common', $common_views_dir);
$controller_views_dir = $common_views_dir.'/'.$controller;
if (is_dir($controller_views_dir))
$this->view->addFolder('controller', $controller_views_dir);
$this->view->setFolder('controller', $controller_views_dir);
}
}
@ -66,6 +68,14 @@ class Controller
/** @var array */
protected $params;
/**
* Handle the MVC-style dispatching of a controller action.
*
* @param Request $request
* @param Response $response
* @param $args
* @return Response
*/
public function dispatch(Request $request, Response $response, $args)
{
$this->request = $request;
@ -93,8 +103,11 @@ class Controller
return $action_result;
$template = $this->view->render('controller::'.$this->action);
$this->response->getBody()->write($template);
return $this->response;
$body = $this->response->getBody();
$body->write($template);
return $this->response->withBody($body);
}
public function __get($key)
@ -241,9 +254,11 @@ class Controller
$this->response = $this->response->withHeader('Content-type', 'text/html; charset=utf-8');
$template = $this->view->render($template_name, $template_args);
$this->response->getBody()->write($template);
return $this->response;
$body = $this->response->getBody();
$body->write($template);
return $this->response->withBody($body);
}
/**
@ -283,10 +298,12 @@ class Controller
{
$this->doNotRender();
$this->response = $this->response->withHeader('Content-type', 'application/json; charset=utf-8');
$this->response->getBody()->write(json_encode($json_data));
$body = $this->response->getBody();
$body->write(json_encode($json_data));
return $this->response;
return $this->response
->withHeader('Content-type', 'application/json; charset=utf-8')
->withBody($body);
}
/**
@ -334,8 +351,10 @@ class Controller
if ($file_name !== null)
$this->response = $this->response->withHeader('Content-Disposition', 'attachment; filename='.$file_name);
$this->response->getBody()->write($file_data);
return $this->response;
$body = $this->response->getBody();
$body->write($file_data);
return $this->response->withBody($body);
}
/* URL Redirection */

View File

@ -35,6 +35,18 @@ class ErrorHandler
$home_url = $di['url']->named('home');
return $res->withStatus(302)->withHeader('Location', $home_url);
}
elseif (APP_IS_COMMAND_LINE)
{
$body = $res->getBody();
$body->write(json_encode([
'code' => $e->getCode(),
'message' => $e->getMessage(),
'stack_trace' => $e->getTraceAsString(),
]));
return $res->withStatus(500)
->withBody($body);
}
else
{
$show_debug = false;
@ -48,8 +60,6 @@ class ErrorHandler
if (APP_APPLICATION_ENV != 'production')
$show_debug = true;
$res->withStatus(500);
if ($show_debug)
{
$view = $di->get('view');
@ -62,19 +72,24 @@ class ErrorHandler
$run = new \Whoops\Run;
$run->pushHandler($handler);
$res->getBody()->write($run->handleException($e));
$body = $res->getBody();
$body->write($run->handleException($e));
return $res;
return $res->withStatus(500)
->withBody($body);
}
else
{
$view = $di->get('view');
$view->exception = $e;
$template = $view->render('system/error_general');
$res->getBody()->write($template);
return $res;
$body = $res->getBody();
$body->write($template);
return $res->withStatus(500)
->withBody($body);
}
}
}

View File

@ -34,6 +34,12 @@ class View extends \League\Plates\Engine
protected $rendered = false;
protected $disabled = false;
public function reset()
{
$this->rendered = false;
$this->disabled = false;
}
public function disable()
{
$this->disabled = true;
@ -74,4 +80,12 @@ class View extends \League\Plates\Engine
return parent::render($name, $data);
}
public function setFolder($name, $directory, $fallback = false)
{
if ($this->folders->exists($name))
$this->folders->remove($name);
$this->folders->add($name, $directory, $fallback);
return $this;
}
}

View File

@ -20,7 +20,8 @@ class Session
if (!$this->isActive())
return false;
$this->_is_started = session_start();
$this->_is_started = @session_start();
return $this->_is_started;
}
@ -117,7 +118,7 @@ class Session
*/
public function isActive()
{
if (APP_IS_COMMAND_LINE)
if (APP_IS_COMMAND_LINE && !APP_TESTING_MODE)
return false;
if ($this->_prevent_sessions)

View File

@ -0,0 +1,167 @@
<?php
/**
* Based on Herloct's Slim 3.0 Connector
* https://github.com/herloct/codeception-slim-module
*/
namespace App\Tests;
use Codeception\Lib\Connector\Shared\PhpSuperGlobalsConverter;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Message\UploadedFileInterface;
use Slim\App;
use Slim\Http\Environment;
use Slim\Http\Headers;
use Slim\Http\Cookies;
use Slim\Http\RequestBody;
use Slim\Http\Stream;
use Slim\Http\UploadedFile;
use Slim\Http\Uri;
use Symfony\Component\BrowserKit\Client;
use Symfony\Component\BrowserKit\Request as BrowserKitRequest;
use Symfony\Component\BrowserKit\Response as BrowserKitResponse;
class Connector extends Client
{
use PhpSuperGlobalsConverter;
/**
* @var App
*/
protected $app;
/**
* @param App $app
*/
public function setApp(App $app)
{
$this->app = $app;
}
/**
* Makes a request.
*
* @param BrowserKitRequest $request An origin request instance
*
* @return BrowserKitResponse An origin response instance
*/
public function doRequest($request)
{
$_COOKIE = $request->getCookies();
$_SERVER = $request->getServer();
$_FILES = $this->remapFiles($request->getFiles());
$uri = str_replace('http://localhost', '', $request->getUri());
$_REQUEST = $this->remapRequestParameters($request->getParameters());
if (strtoupper($request->getMethod()) == 'GET')
{
$_GET = $_REQUEST;
$_POST = [];
}
else
{
$_GET = [];
$_POST = $_REQUEST;
}
$_SERVER['REQUEST_METHOD'] = strtoupper($request->getMethod());
$_SERVER['REQUEST_URI'] = $uri;
$slimRequest = $this->convertRequest($request);
$container = $this->app->getContainer();
/* @var $slimResponse ResponseInterface */
$slimResponse = $container->get('response');
// reset body stream
$slimResponse = $slimResponse->withBody(new Stream(fopen('php://temp', 'w+')));
$slimResponse = $this->app->process($slimRequest, $slimResponse);
return new BrowserKitResponse(
(string) $slimResponse->getBody(),
$slimResponse->getStatusCode(),
$slimResponse->getHeaders()
);
}
/**
* Convert to PSR-7's ServerRequestInterface.
*
* @param BrowserKitRequest $request
* @return ServerRequestInterface
*/
protected function convertRequest(BrowserKitRequest $request)
{
$environment = Environment::mock($request->getServer());
$uri = Uri::createFromString($request->getUri());
$headers = Headers::createFromEnvironment($environment);
$cookies = Cookies::parseHeader($headers->get('Cookie', []));
$container = $this->app->getContainer();
/* @var $slimRequest ServerRequestInterface */
$slimRequest = $container->get('request');
$slimRequest = $slimRequest->withMethod($request->getMethod())
->withUri($uri)
->withUploadedFiles($this->convertFiles($request->getFiles()))
->withCookieParams($cookies);
foreach ($headers->keys() as $key) {
$slimRequest = $slimRequest->withHeader($key, $headers->get($key));
}
if ($request->getContent() !== null) {
$body = new RequestBody();
$body->write($request->getContent());
$slimRequest = $slimRequest
->withBody($body);
}
$parsed = [];
if ($request->getMethod() !== 'GET') {
$parsed = $request->getParameters();
}
// make sure we do not overwrite a request with a parsed body
if (!$slimRequest->getParsedBody()) {
$slimRequest = $slimRequest
->withParsedBody($parsed);
}
return $slimRequest;
}
/**
* Convert to PSR-7's UploadedFileInterface.
*
* @param array $files
* @return array
*/
protected function convertFiles(array $files)
{
$fileObjects = [];
foreach ($files as $fieldName => $file) {
if ($file instanceof UploadedFileInterface) {
$fileObjects[$fieldName] = $file;
} elseif (!isset($file['tmp_name']) && !isset($file['name'])) {
$fileObjects[$fieldName] = $this->convertFiles($file);
} else {
$fileObjects[$fieldName] = new UploadedFile(
$file['tmp_name'],
$file['name'],
$file['type'],
$file['size'],
$file['error']
);
}
}
return $fileObjects;
}
}

View File

@ -0,0 +1,80 @@
<?php
/**
* Based on Herloct's Slim 3.0 Connector
* https://github.com/herloct/codeception-slim-module
*/
namespace App\Tests;
use Doctrine\ORM\EntityManagerInterface;
use Interop\Container\ContainerInterface;
use Codeception\Configuration;
use Codeception\TestInterface;
use Codeception\Lib\Framework;
use Codeception\Lib\Interfaces\DoctrineProvider;
use Slim\App;
class Module extends Framework implements DoctrineProvider
{
protected $requiredFields = ['container'];
/**
* @var ContainerInterface
*/
public $container;
/**
* @var App
*/
public $app;
/**
* @var EntityManagerInterface
*/
public $em;
public function _initialize()
{
if (!defined('APP_TESTING_MODE'))
define('APP_TESTING_MODE', true);
$cwd = getcwd();
chdir(Configuration::projectDir());
$this->container = include Configuration::projectDir() . $this->config['container'];
chdir($cwd);
$this->app = $this->container->get('app');
$this->em = $this->container->get('em');
parent::_initialize();
}
public function _before(TestInterface $test)
{
$this->client = new Connector();
$this->client->setApp($this->app);
parent::_before($test);
}
public function _after(TestInterface $test)
{
if (session_status() === PHP_SESSION_ACTIVE) {
session_write_close();
}
$_GET = [];
$_POST = [];
$_COOKIE = [];
parent::_after($test);
}
/**
* @return EntityManagerInterface
*/
public function _getEntityManager()
{
return $this->em;
}
}

View File

@ -31,7 +31,10 @@ class Url
*/
public function current($absolute = false)
{
return $this->getUrl($_SERVER['REQUEST_URI'], $absolute);
if (!empty($_SERVER['REQUEST_URI']))
return $this->getUrl($_SERVER['REQUEST_URI'], $absolute);
else
return '';
}
/**
@ -50,7 +53,10 @@ class Url
*/
public function referrer($default_url = null)
{
return $this->getUrl($_SERVER['HTTP_REFERER']);
if (isset($_SERVER['HTTP_REFERER']))
return $this->getUrl($_SERVER['HTTP_REFERER']);
return null;
}
/**

View File

@ -96,10 +96,12 @@ class Station extends \App\Doctrine\Entity
{
$this->radio_base_dir = $new_dir;
mkdir($this->radio_base_dir, 0777);
mkdir($this->getRadioMediaDir(), 0777);
mkdir($this->getRadioPlaylistsDir(), 0777);
mkdir($this->getRadioConfigDir(), 0777);
$radio_dirs = [$this->radio_base_dir, $this->getRadioMediaDir(), $this->getRadioPlaylistsDir(), $this->getRadioConfigDir()];
foreach($radio_dirs as $radio_dir)
{
if (!file_exists($radio_dir))
mkdir($radio_dir, 0777);
}
}
}

View File

@ -128,7 +128,7 @@ class SetupController extends BaseController
$existing_settings = $settings_repo->fetchArray(FALSE);
$form->setDefaults($existing_settings);
if (!empty($_POST) && $form->isValid($_POST))
if ($this->request->getMethod() == 'POST' && $form->isValid($this->request->getQueryParams()))
{
$data = $form->getValues();

View File

@ -6,7 +6,7 @@
<h3 class="text-left"><?=sprintf(_('Welcome to %s!'), $config->application->name) ?></h3>
<p class="text-left"><?=_('Please log in to continue.') ?></p>
<form action="" method="post">
<form id="login-form" action="" method="post">
<div class="input-group m-b-20">
<span class="input-group-addon"><i class="zmdi zmdi-account"></i></span>
<div class="fg-line">

View File

@ -6,7 +6,7 @@
<h3 class="text-left"><?=sprintf(_('Welcome to %s!'), $config->application->name) ?>!</h3>
<P class="text-left"><?=_('Begin setup by creating a Super Administrator account.') ?></p>
<form action="" method="post">
<form id="login-form" action="" method="post">
<div class="input-group m-b-20">
<span class="input-group-addon"><i class="zmdi zmdi-account"></i></span>
<div class="fg-line">

21
codeception.yml Normal file
View File

@ -0,0 +1,21 @@
actor: Tester
paths:
tests: tests
log: tests/_output
data: tests/_data
support: tests/_support
envs: tests/_envs
settings:
bootstrap: _bootstrap.php
colors: true
memory_limit: 1024M
extensions:
enabled:
- Codeception\Extension\RunFailed
modules:
config:
Db:
dsn: ''
user: ''
password: ''
dump: tests/_data/dump.sql

View File

@ -23,6 +23,7 @@
"packaged/helpers": "^1.5"
},
"require-dev": {
"codeception/codeception": "^2.2"
},
"authors": [
{

1637
composer.lock generated

File diff suppressed because it is too large Load Diff

7
tests/_bootstrap.php Normal file
View File

@ -0,0 +1,7 @@
<?php
// This is global bootstrap for autoloading
$autoloader = require __DIR__.'/../vendor/autoload.php';
$autoloader->add('App', __DIR__.'/../app/library');
$autoloader->addClassMap([
'CestAbstract' => __DIR__.'/functional/CestAbstract.php',
]);

2
tests/_output/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
*
!.gitignore

View File

@ -0,0 +1,26 @@
<?php
/**
* Inherited Methods
* @method void wantToTest($text)
* @method void wantTo($text)
* @method void execute($callable)
* @method void expectTo($prediction)
* @method void expect($prediction)
* @method void amGoingTo($argumentation)
* @method void am($role)
* @method void lookForwardTo($achieveValue)
* @method void comment($description)
* @method \Codeception\Lib\Friend haveFriend($name, $actorClass = NULL)
*
* @SuppressWarnings(PHPMD)
*/
class FunctionalTester extends \Codeception\Actor
{
use _generated\FunctionalTesterActions;
/**
* Define custom actions here
*/
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,14 @@
# Codeception Test Suite Configuration
#
# Suite for functional (integration) tests
# Emulate web requests and make application process them
# Include one of framework modules (Symfony2, Yii2, Laravel5) to use it
class_name: FunctionalTester
modules:
enabled:
- \App\Tests\Module:
container: app/bootstrap.php
- Doctrine2:
depends: \App\Tests\Module
error_level: "E_ALL & ~E_NOTICE & ~E_STRICT & ~E_DEPRECATED"

View File

@ -0,0 +1,96 @@
<?php
use Slim\App;
use Interop\Container\ContainerInterface;
use Doctrine\ORM\EntityManagerInterface;
abstract class CestAbstract
{
/**
* @var ContainerInterface
*/
protected $di;
/**
* @var EntityManagerInterface
*/
protected $em;
protected function _inject(\App\Tests\Module $tests_module)
{
$this->di = $tests_module->container;
$this->em = $tests_module->em;
}
public function _before(FunctionalTester $I)
{
if (!empty($this->login_cookie))
{
$I->setCookie('PHPSESSID', $this->login_cookie);
}
}
public function _after(FunctionalTester $I)
{}
protected $login_username = 'azuracast@azuracast.com';
protected $login_password = 'AzuraCastFunctionalTests!';
protected $login_cookie = null;
protected function setupIncomplete(FunctionalTester $I)
{
$settings_repo = $this->em->getRepository(\Entity\Settings::class);
$settings_repo->setSetting('setup_complete', 0);
$this->_cleanTables();
}
protected function setupComplete(FunctionalTester $I)
{
$settings_repo = $this->em->getRepository(\Entity\Settings::class);
$settings_repo->setSetting('setup_complete', time());
$this->_cleanTables();
}
protected function _cleanTables()
{
$clean_tables = ['Entity\User', 'Entity\Role', 'Entity\Station'];
foreach($clean_tables as $clean_table)
$this->em->createQuery('DELETE FROM '.$clean_table.' t')->execute();
}
protected function login(FunctionalTester $I)
{
if (empty($this->login_cookie))
{
$I->wantTo('Log in to the application.');
$I->amOnPage('/');
$I->seeInCurrentUrl('/login');
$I->submitForm('#login-form', [
'username' => $this->login_username,
'password' => $this->login_password,
]);
$I->seeInSource('Logged in');
$this->login_cookie = $I->grabCookie('PHPSESSID');
}
}
protected function logout(FunctionalTester $I)
{
if (!empty($this->login_cookie))
{
$I->wantTo('Log out of the application.');
$I->amOnPage('/logout');
$I->seeInCurrentUrl('/login');
$this->login_cookie = null;
}
}
}

View File

@ -0,0 +1,70 @@
<?php
/**
* @group frontend
*/
class Frontend_SetupCest extends CestAbstract
{
/**
* @before setupIncomplete
* @after setupRegister
* @after setupStation
* @after setupSettings
*/
public function setupStart(FunctionalTester $I)
{
$I->wantTo('Check for a setup redirect.');
$I->amOnPage('/');
$I->see('Begin setup');
$I->seeCurrentUrlEquals('/setup/register');
}
protected function setupRegister(FunctionalTester $I)
{
$I->wantTo('Create a user account.');
$I->amOnPage('/setup/register');
$I->submitForm('#login-form', [
'username' => $this->login_username,
'password' => $this->login_password,
]);
$I->seeInSource('continue the setup process');
$I->seeInRepository('Entity\User', ['email' => $this->login_username]);
$this->login_cookie = $I->grabCookie('PHPSESSID');
}
protected function setupStation(FunctionalTester $I)
{
$I->wantTo('Set up a station.');
$I->amOnPage('/setup/station');
$I->seeCurrentUrlEquals('/setup/station');
$I->see('continue the setup process');
$I->submitForm('.form', [
'app_form' => [
'name' => 'Functional Test Radio',
'description' => 'Test radio station.',
],
]);
$I->seeCurrentUrlEquals('/setup/settings');
}
protected function setupSettings(FunctionalTester $I)
{
$I->wantTo('Set up site settings.');
$I->amOnPage('/setup/settings');
$I->submitForm('.form', []);
$I->wantTo('See a set up site.');
$I->seeResponseCodeIs(200);
$I->seeCurrentUrlEquals('/');
$I->seeInSource('Setup is now complete!');
}
}

View File

@ -0,0 +1,2 @@
<?php
/* Placeholder */

View File

@ -26,10 +26,11 @@
- php7.0-cli
- php7.0-gd
- php7.0-curl
- php7.0-xml
- php7.0-zip
- php7.0-mysqlnd
- php7.0-intl
- php7.0-xml # IceCast XML config
- php7.0-zip # Composer installs
- php7.0-mysqlnd # MySQL Native Driver (Doctrine)
- php7.0-mbstring # Codeception Tests
- php7.0-intl # Localization
- name: List locales
shell: "cd {{ www_base }}/app/locale/; for i in $(ls -d */); do echo ${i%%/}; done"

View File

@ -2,7 +2,7 @@
error_reporting(E_ALL & ~E_NOTICE & ~E_STRICT);
ini_set('display_errors', 1);
require dirname(__FILE__).'/../app/bootstrap.php';
$di = require dirname(__FILE__).'/../app/bootstrap.php';
$em = $di['em'];
$db = $em->getConnection();

View File

@ -1,6 +1,9 @@
<?php
error_reporting(E_ALL & ~E_NOTICE & ~E_STRICT);
require __DIR__ . '/../app/bootstrap.php';
$di = require __DIR__ . '/../app/bootstrap.php';
/** @var \Slim\App $app */
$app = $di['app'];
$app->run();