diff --git a/RoboFile.php b/RoboFile.php index e4c3b729..3e2140c3 100644 --- a/RoboFile.php +++ b/RoboFile.php @@ -105,7 +105,7 @@ class RoboFile extends Tasks { */ public function coverage() { - $this->_run(['vendor/bin/phpunit -c build']); + $this->_run(['phpdbg -qrr -- vendor/bin/phpunit -c build']); } /** @@ -114,7 +114,7 @@ class RoboFile extends Tasks { public function docs() { $cmd_parts = [ - 'phpdox', + 'vendor/bin/phpdox', ]; $this->_run($cmd_parts, ' && '); } diff --git a/app/views/404.php b/app/views/404.php index bba9aec9..a99e7313 100644 --- a/app/views/404.php +++ b/app/views/404.php @@ -1,4 +1,4 @@

404

-

Page Not Found

+

diff --git a/app/views/manga/details.php b/app/views/manga/details.php index 2b204b7b..11790bfe 100644 --- a/app/views/manga/details.php +++ b/app/views/manga/details.php @@ -35,4 +35,26 @@

+
+ 0): ?> +

Characters

+
+ + +
+ generate('character', ['slug' => $char['slug']]) ?> + a($link, $char['name']); ?> +
+ + img($char['image']['original'], [ + 'width' => '225' + ]) ?> + +
+ + +
+ +
+ \ No newline at end of file diff --git a/composer.json b/composer.json index 0c8adaf8..e71d64ee 100644 --- a/composer.json +++ b/composer.json @@ -22,7 +22,6 @@ "aura/session": "^2.0", "aviat/banker": "^1.0.0", "aviat/ion": "dev-master", - "filp/whoops": "^2.1.5", "monolog/monolog": "^1.0", "psr/http-message": "~1.0", "psr/log": "~1.0", @@ -34,19 +33,21 @@ "require-dev": { "pdepend/pdepend": "^2.2", "sebastian/phpcpd": "^3.0", - "theseer/phpdox": "0.9.0", + "theseer/phpdox": "dev-master", "phploc/phploc": "^3.0", "phpmd/phpmd": "^2.4", "phpunit/phpunit": "^6.0", "robmorgan/phinx": "~0.6.4", "consolidation/robo": "~1.0", "henrikbjorn/lurker": "^1.1.0", - "symfony/var-dumper": "^3.1", + "symfony/var-dumper": "^3.2", "squizlabs/php_codesniffer": "^3.0.0@beta", "phpstan/phpstan": "^0.6.4" }, "scripts": { + "build": "vendor/bin/robo build", "build:css": "cd public && npm run build && cd ..", + "clean": "vendor/bin/robo clean", "coverage": "phpdbg -qrr -- vendor/bin/phpunit -c build", "docs": "vendor/bin/phpdox", "phpstan": "phpstan analyse src tests", diff --git a/console b/console index 899cc7eb..3a7c90fc 100755 --- a/console +++ b/console @@ -1,19 +1,10 @@ #!/usr/bin/env php '\Aviat\AnimeClient\Command\CachePrime', - 'cache-clear' => '\Aviat\AnimeClient\Command\CacheClear', - 'clear-cache' => '\Aviat\AnimeClient\Command\CacheClear', - 'sync-lists' => '\Aviat\AnimeClient\Command\SyncKitsuWithMal', + 'cache-prime' => Command\CachePrime::class, + 'cache-clear' => Command\CacheClear::class, + 'clear-cache' => Command\CacheClear::class, + 'sync-lists' => Command\SyncKitsuWithMal::class, ]); $console->run(); \ No newline at end of file diff --git a/index.php b/index.php index e780cc78..48958f44 100644 --- a/index.php +++ b/index.php @@ -18,8 +18,6 @@ namespace Aviat\AnimeClient; use function Aviat\AnimeClient\loadToml; use Aviat\AnimeClient\AnimeClient; -use Whoops\Handler\PrettyPageHandler; -use Whoops\Run; // Work around the silly timezone error $timezone = ini_get('date.timezone'); @@ -36,18 +34,6 @@ $APP_DIR = _dir(__DIR__, 'app'); $APPCONF_DIR = _dir($APP_DIR, 'appConf'); $CONF_DIR = _dir($APP_DIR, 'config'); -// ------------------------------------------------------------------------- -// Setup error handling -// ------------------------------------------------------------------------- -$whoops = new Run(); - -// Set up default handler for general errors -$defaultHandler = new PrettyPageHandler(); -$whoops->pushHandler($defaultHandler); - -// Register as the error handler -$whoops->register(); - // ----------------------------------------------------------------------------- // Dependency Injection setup // ----------------------------------------------------------------------------- diff --git a/src/API/Enum/AnimeWatchingStatus/Kitsu.php b/src/API/Enum/AnimeWatchingStatus/Kitsu.php index 829fe2d5..b3447c29 100644 --- a/src/API/Enum/AnimeWatchingStatus/Kitsu.php +++ b/src/API/Enum/AnimeWatchingStatus/Kitsu.php @@ -24,7 +24,7 @@ use Aviat\Ion\Enum; class Kitsu extends Enum { const WATCHING = 'current'; const PLAN_TO_WATCH = 'planned'; - const COMPLETED = 'completed'; const ON_HOLD = 'on_hold'; const DROPPED = 'dropped'; + const COMPLETED = 'completed'; } \ No newline at end of file diff --git a/src/API/Kitsu/Model.php b/src/API/Kitsu/Model.php index b32b2a55..7f4f5457 100644 --- a/src/API/Kitsu/Model.php +++ b/src/API/Kitsu/Model.php @@ -196,8 +196,13 @@ class Model { */ public function getAnime(string $slug): array { - // @TODO catch non-existent anime $baseData = $this->getRawMediaData('anime', $slug); + + if (empty($baseData)) + { + return []; + } + $transformed = $this->animeTransformer->transform($baseData); $transformed['included'] = $baseData['included']; return $transformed; @@ -252,7 +257,15 @@ class Model { public function getManga(string $mangaId): array { $baseData = $this->getRawMediaData('manga', $mangaId); - return $this->mangaTransformer->transform($baseData); + + if (empty($baseData)) + { + return []; + } + + $transformed = $this->mangaTransformer->transform($baseData); + $transformed['included'] = $baseData['included']; + return $transformed; } /** @@ -386,13 +399,15 @@ class Model { if ( ! $cacheItem->isHit()) { - $output = [ - Title::WATCHING => $this->getAnimeList(KitsuWatchingStatus::WATCHING), - Title::PLAN_TO_WATCH => $this->getAnimeList(KitsuWatchingStatus::PLAN_TO_WATCH), - Title::ON_HOLD => $this->getAnimeList(KitsuWatchingStatus::ON_HOLD), - Title::DROPPED => $this->getAnimeList(KitsuWatchingStatus::DROPPED), - Title::COMPLETED => $this->getAnimeList(KitsuWatchingStatus::COMPLETED) - ]; + $output = []; + + $statuses = KitsuWatchingStatus::getConstList(); + + foreach ($statuses as $key => $status) + { + $mappedStatus = AnimeWatchingStatus::KITSU_TO_TITLE[$status]; + $output[$mappedStatus] = $this->getAnimeList($status) ?? []; + } $cacheItem->set($output); $cacheItem->save(); @@ -413,7 +428,16 @@ class Model { if ( ! $cacheItem->isHit()) { - $data = $this->getRawAnimeList($status); + $data = $this->getRawAnimeList($status) ?? []; + + // Bail out on no data + if (empty($data)) + { + $cacheItem->set([]); + $cacheItem->save(); + return $cacheItem->get(); + } + $included = JsonAPI::organizeIncludes($data['included']); $included = JsonAPI::inlineIncludedRelationships($included, 'anime'); @@ -616,6 +640,12 @@ class Model { ]; $data = $this->getRequest("{$type}/{$id}", $options); + + if (empty($data['data'])) + { + return []; + } + $baseData = $data['data']['attributes']; $baseData['included'] = $data['included']; return $baseData; @@ -637,11 +667,17 @@ class Model { ], 'include' => ($type === 'anime') ? 'genres,mappings,streamingLinks,animeCharacters.character' - : 'genres,mappings', + : 'genres,mappings,mangaCharacters.character,castings.character', ] ]; $data = $this->getRequest($type, $options); + + if (empty($data['data'])) + { + return []; + } + $baseData = $data['data'][0]['attributes']; $baseData['included'] = $data['included']; return $baseData; diff --git a/src/API/Mapping/AnimeWatchingStatus.php b/src/API/Mapping/AnimeWatchingStatus.php index 94c4648e..34b44930 100644 --- a/src/API/Mapping/AnimeWatchingStatus.php +++ b/src/API/Mapping/AnimeWatchingStatus.php @@ -24,6 +24,10 @@ use Aviat\AnimeClient\API\Enum\AnimeWatchingStatus\{ }; use Aviat\Ion\Enum; +/** + * Anime watching status mappings, among Kitsu, MAL, Page titles + * and url route segments + */ class AnimeWatchingStatus extends Enum { const KITSU_TO_MAL = [ Kitsu::WATCHING => MAL::WATCHING, diff --git a/src/API/Mapping/MangaReadingStatus.php b/src/API/Mapping/MangaReadingStatus.php index 57a447c1..7b01c2ce 100644 --- a/src/API/Mapping/MangaReadingStatus.php +++ b/src/API/Mapping/MangaReadingStatus.php @@ -24,6 +24,10 @@ use Aviat\AnimeClient\API\Enum\MangaReadingStatus\{ }; use Aviat\Ion\Enum; +/** + * Manga reading status mappings, among Kitsu, MAL, Page titles + * and url route segments + */ class MangaReadingStatus extends Enum { const MAL_TO_KITSU = [ Kitsu::READING => MAL::READING, diff --git a/src/Command/SyncKitsuWithMal.php b/src/Command/SyncKitsuWithMal.php index d0fdf42c..441396f7 100644 --- a/src/Command/SyncKitsuWithMal.php +++ b/src/Command/SyncKitsuWithMal.php @@ -54,38 +54,38 @@ class SyncKitsuWithMal extends BaseCommand { $this->setCache($this->container->get('cache')); $this->kitsuModel = $this->container->get('kitsu-model'); $this->malModel = $this->container->get('mal-model'); - - $malCount = count($this->getMALList()); + + $malCount = count($this->getMALAnimeList()); $kitsuCount = $this->getKitsuAnimeListPageCount(); - + $this->echoBox("Number of MAL list items: {$malCount}"); $this->echoBox("Number of Kitsu list items: {$kitsuCount}"); - - $data = $this->diffLists(); + + $data = $this->diffAnimeLists(); $this->echoBox("Number of items that need to be added to MAL: " . count($data)); - + if ( ! empty($data['addToMAL'])) { $this->echoBox("Adding missing list items to MAL"); - $this->createMALListItems($data['addToMAL']); + $this->createMALAnimeListItems($data['addToMAL']); } } - - public function getKitsuList() + + public function getKitsuAnimeList() { $count = $this->getKitsuAnimeListPageCount(); $size = 100; $pages = ceil($count / $size); - + $requests = []; - + // Set up requests for ($i = 0; $i < $pages; $i++) { $offset = $i * $size; $requests[] = $this->kitsuModel->getPagedAnimeList($size, $offset); } - + $promiseArray = (new Client())->requestMulti($requests); $responses = wait(all($promiseArray)); @@ -100,15 +100,15 @@ class SyncKitsuWithMal extends BaseCommand { return $output; } - public function getMALList() + public function getMALAnimeList() { return $this->malModel->getFullList(); } - + public function filterMappings(array $includes): array { $output = []; - + foreach($includes as $id => $mapping) { if ($mapping['externalSite'] === 'myanimelist/anime') @@ -116,15 +116,15 @@ class SyncKitsuWithMal extends BaseCommand { $output[$id] = $mapping; } } - + return $output; } - - public function formatMALList() + + public function formatMALAnimeList() { - $orig = $this->getMALList(); + $orig = $this->getMALAnimeList(); $output = []; - + foreach($orig as $item) { $output[$item['series_animedb_id']] = [ @@ -144,24 +144,24 @@ class SyncKitsuWithMal extends BaseCommand { ] ]; } - + return $output; } - - public function filterKitsuList() + + public function filterKitsuAnimeList() { $data = $this->kitsuModel->getFullAnimeList(); $includes = JsonAPI::organizeIncludes($data['included']); $includes['mappings'] = $this->filterMappings($includes['mappings']); - + $output = []; - + foreach($data['data'] as $listItem) { $animeId = $listItem['relationships']['anime']['data']['id']; $potentialMappings = $includes['anime'][$animeId]['relationships']['mappings']; $malId = NULL; - + foreach ($potentialMappings as $mappingId) { if (array_key_exists($mappingId, $includes['mappings'])) @@ -169,20 +169,20 @@ class SyncKitsuWithMal extends BaseCommand { $malId = $includes['mappings'][$mappingId]['externalId']; } } - + // Skip to the next item if there isn't a MAL ID if (is_null($malId)) { continue; } - + $output[$listItem['id']] = [ 'id' => $listItem['id'], 'malId' => $malId, 'data' => $listItem['attributes'], ]; } - + return $output; } @@ -191,32 +191,32 @@ class SyncKitsuWithMal extends BaseCommand { return $this->kitsuModel->getAnimeListCount(); } - public function diffLists() + public function diffAnimeLists() { // Get libraryEntries with media.mappings from Kitsu // Organize mappings, and ignore entries without mappings - $kitsuList = $this->filterKitsuList(); + $kitsuList = $this->filterKitsuAnimeList(); // Get MAL list data - $malList = $this->formatMALList(); - + $malList = $this->formatMALAnimeList(); + $itemsToAddToMAL = []; - + foreach($kitsuList as $kitsuItem) { if (array_key_exists($kitsuItem['malId'], $malList)) { - // Eventually, compare the list entries, and determine which + // Eventually, compare the list entries, and determine which // needs to be updated continue; } - + // Looks like this item only exists on Kitsu $itemsToAddToMAL[] = [ 'mal_id' => $kitsuItem['malId'], 'data' => $kitsuItem['data'] ]; - + } // Compare each list entry @@ -227,17 +227,17 @@ class SyncKitsuWithMal extends BaseCommand { // Otherwise, use rewatch count, then episode progress as critera for selecting the more up // to date entry // Based on the 'newer' entry, update the other api list item - + return [ 'addToMAL' => $itemsToAddToMAL, ]; } - public function createMALListItems($itemsToAdd) + public function createMALAnimeListItems($itemsToAdd) { $transformer = new ALT(); $requests = []; - + foreach($itemsToAdd as $item) { $data = $transformer->untransform($item); @@ -261,5 +261,4 @@ class SyncKitsuWithMal extends BaseCommand { } } } - } \ No newline at end of file diff --git a/src/Controller.php b/src/Controller.php index 05ab91eb..48d11e4a 100644 --- a/src/Controller.php +++ b/src/Controller.php @@ -47,7 +47,7 @@ class Controller { $this->config = $container->get('config'); $this->request = $container->get('request'); $this->response = $container->get('response'); - + $this->baseData = array_merge((array)$this->baseData, [ 'url' => $auraUrlGenerator, 'urlGenerator' => $urlGenerator, @@ -94,7 +94,6 @@ class Controller { /** * Show the login form * - * @codeCoverageIgnore * @param string $status * @return void */ diff --git a/src/Controller/Anime.php b/src/Controller/Anime.php index 90df958e..514a039c 100644 --- a/src/Controller/Anime.php +++ b/src/Controller/Anime.php @@ -82,8 +82,10 @@ class Anime extends BaseController { public function index($type = KitsuWatchingStatus::WATCHING, string $view = NULL) { $title = (array_key_exists($type, AnimeWatchingStatus::ROUTE_TO_TITLE)) - ? $this->config->get('whose_list') . - "'s Anime List · " . AnimeWatchingStatus::ROUTE_TO_TITLE[$type] + ? $this->formatTitle( + $this->config->get('whose_list') . "'s Anime List", + AnimeWatchingStatus::ROUTE_TO_TITLE[$type] + ) : ''; $viewMap = [ @@ -110,8 +112,10 @@ class Anime extends BaseController { { $this->setSessionRedirect(); $this->outputHTML('anime/add', [ - 'title' => $this->config->get('whose_list') . - "'s Anime List · Add", + 'title' => $this->formatTitle( + $this->config->get('whose_list') . "'s Anime List", + 'Add' + ), 'action_url' => $this->urlGenerator->url('anime/add'), 'status_list' => AnimeWatchingStatus::KITSU_TO_TITLE ]); @@ -158,8 +162,10 @@ class Anime extends BaseController { $this->setSessionRedirect(); $this->outputHTML('anime/edit', [ - 'title' => $this->config->get('whose_list') . - "'s Anime List · Edit", + 'title' => $this->formatTitle( + $this->config->get('whose_list') . "'s Anime List", + 'Edit' + ), 'item' => $item, 'statuses' => AnimeWatchingStatus::KITSU_TO_TITLE, 'action' => $this->container->get('url-generator') @@ -262,7 +268,17 @@ class Anime extends BaseController { { $data = $this->model->getAnime($animeId); $characters = []; - + + if (empty($data)) + { + return $this->notFound( + $this->config->get('whose_list') . + "'s Anime List · Anime · " . + 'Anime not found', + 'Anime Not Found' + ); + } + foreach($data['included'] as $included) { if ($included['type'] === 'characters') @@ -272,7 +288,11 @@ class Anime extends BaseController { } $this->outputHTML('anime/details', [ - 'title' => 'Anime · ' . $data['titles'][0], + 'title' => $this->formatTitle( + $this->config->get('whose_list') . "'s Anime List", + 'Anime', + $data['titles'][0] + ), 'characters' => $characters, 'data' => $data, ]); diff --git a/src/Controller/Character.php b/src/Controller/Character.php index 09c74796..4d60d0e4 100644 --- a/src/Controller/Character.php +++ b/src/Controller/Character.php @@ -18,6 +18,9 @@ namespace Aviat\AnimeClient\Controller; use Aviat\AnimeClient\Controller as BaseController; +/** + * Controller for character description pages + */ class Character extends BaseController { public function index(string $slug) @@ -26,15 +29,22 @@ class Character extends BaseController { $data = $model->getCharacter($slug); - if ( ! array_key_exists('data', $data)) + if (( ! array_key_exists('data', $data)) || empty($data['data'])) { - return $this->notFound(); + return $this->notFound( + $this->formatTitle( + 'Characters', + 'Character not found' + ), + 'Character Not Found' + ); } - // $this->outputJSON($data); $this->outputHTML('character', [ - 'title' => $this->config->get('whose_list') . - "'s Anime List · Characters · " . $data['data'][0]['attributes']['name'], + 'title' => $this->formatTitle( + 'Characters', + $data['data'][0]['attributes']['name'] + ), 'data' => $data['data'][0]['attributes'] ]); } diff --git a/src/Controller/Collection.php b/src/Controller/Collection.php index 1fcc2928..14c43993 100644 --- a/src/Controller/Collection.php +++ b/src/Controller/Collection.php @@ -122,7 +122,10 @@ class Collection extends BaseController { $this->outputHTML('collection/' . strtolower($action), [ 'action' => $action, 'action_url' => $this->urlGenerator->fullUrl('collection/' . strtolower($action)), - 'title' => $this->config->get('whose_list') . " Anime Collection · {$action}", + 'title' => $this->formatTitle( + $this->config->get('whose_list') . "'s Anime Collection", + $action + ), 'media_items' => $this->animeCollectionModel->getMediaTypeList(), 'item' => ($action === "Edit") ? $this->animeCollectionModel->get($id) : [] ]); diff --git a/src/Controller/Manga.php b/src/Controller/Manga.php index 7fee7393..e9bc0f01 100644 --- a/src/Controller/Manga.php +++ b/src/Controller/Manga.php @@ -71,7 +71,10 @@ class Manga extends Controller { { $statusTitle = MangaReadingStatus::ROUTE_TO_TITLE[$status]; - $title = $this->config->get('whose_list') . "'s Manga List · {$statusTitle}"; + $title = $this->formatTitle( + $this->config->get('whose_list') . "'s Manga List", + $statusTitle + ); $view_map = [ '' => 'cover', @@ -109,8 +112,10 @@ class Manga extends Controller { $this->setSessionRedirect(); $this->outputHTML('manga/add', [ - 'title' => $this->config->get('whose_list') . - "'s Manga List · Add", + 'title' => $this->formatTitle( + $this->config->get('whose_list') . "'s Manga List", + 'Add' + ), 'action_url' => $this->urlGenerator->url('manga/add'), 'status_list' => $statuses ]); @@ -155,7 +160,10 @@ class Manga extends Controller { { $this->setSessionRedirect(); $item = $this->model->getLibraryItem($id); - $title = $this->config->get('whose_list') . "'s Manga List · Edit"; + $title = $this->formatTitle( + $this->config->get('whose_list') . "'s Manga List", + 'Edit' + ); $this->outputHTML('manga/edit', [ 'title' => $title, @@ -261,9 +269,35 @@ class Manga extends Controller { public function details($manga_id) { $data = $this->model->getManga($manga_id); + $characters = []; + + if (empty($data)) + { + return $this->notFound( + $this->config->get('whose_list') . + "'s Manga List · Manga · " . + 'Manga not found', + 'Manga Not Found' + ); + } + + // dump($data); + + foreach($data['included'] as $included) + { + if ($included['type'] === 'characters') + { + $characters[$included['id']] = $included['attributes']; + } + } $this->outputHTML('manga/details', [ - 'title' => 'Manga · ' . $data['title'], + 'title' => $this->formatTitle( + $this->config->get('whose_list') . "'s Manga List", + 'Manga', + $data['title'] + ), + 'characters' => $characters, 'data' => $data, ]); } diff --git a/src/ControllerTrait.php b/src/ControllerTrait.php index 01acddd1..86474a3d 100644 --- a/src/ControllerTrait.php +++ b/src/ControllerTrait.php @@ -26,7 +26,7 @@ use Aviat\Ion\View\{HtmlView, HttpView, JsonView}; use InvalidArgumentException; trait ControllerTrait { - + use ContainerAware; /** @@ -80,7 +80,7 @@ trait ControllerTrait { 'other_type' => 'manga', 'menu_name' => '' ]; - + /** * Redirect to the default controller/url from an empty path * @@ -228,16 +228,20 @@ trait ControllerTrait { $view->appendOutput($this->loadPartial($view, $template, $data)); $view->appendOutput($this->loadPartial($view, 'footer', $data)); } - + /** * 404 action * * @return void */ - public function notFound() + public function notFound( + string $title = 'Sorry, page not found', + string $message = 'Page Not Found' + ) { $this->outputHTML('404', [ - 'title' => 'Sorry, page not found' + 'title' => $title, + 'message' => $message, ], NULL, 404); } @@ -270,17 +274,17 @@ trait ControllerTrait { public function setFlashMessage($message, $type = "info") { static $messages; - + if ( ! $messages) { $messages = []; } - + $messages[] = [ 'message_type' => $type, 'message' => $message ]; - + $this->session->setFlash('message', $messages); } @@ -297,10 +301,20 @@ trait ControllerTrait { ], NULL, 200); } + /** + * Helper for consistent page titles + * + * @param string ...$parts Title segements + * @return string + */ + public function formatTitle(string ...$parts) : string + { + return implode(' · ', $parts); + } + /** * Add a message box to the page * - * @codeCoverageIgnore * @param HtmlView $view * @param string $type * @param string $message diff --git a/src/Dispatcher.php b/src/Dispatcher.php index b8ba199a..3f3dffd2 100644 --- a/src/Dispatcher.php +++ b/src/Dispatcher.php @@ -106,7 +106,6 @@ class Dispatcher extends RoutingBase { /** * Handle the current route * - * @codeCoverageIgnore * @param object|null $route * @return void */ @@ -262,7 +261,7 @@ class Dispatcher extends RoutingBase { // Run the appropriate controller method $logger->debug('Dispatcher - controller arguments'); $logger->debug(print_r($params, TRUE)); - + call_user_func_array([$controller, $method], $params); } diff --git a/src/Model/API.php b/src/Model/API.php index e43b42de..a7508255 100644 --- a/src/Model/API.php +++ b/src/Model/API.php @@ -24,7 +24,6 @@ class API extends AbstractModel { /** * Sort the list entries by their title * - * @codeCoverageIgnore * @param array $array * @param string $sortKey * @return void diff --git a/tests/ControllerTest.php b/tests/ControllerTest.php index 6d3e7c84..57ba8dee 100644 --- a/tests/ControllerTest.php +++ b/tests/ControllerTest.php @@ -21,12 +21,13 @@ use Aura\Web\WebFactory; use Aviat\AnimeClient\Controller; use Aviat\AnimeClient\Controller\{ Anime as AnimeController, + Character as CharacterController, Collection as CollectionController, Manga as MangaController }; class ControllerTest extends AnimeClientTestCase { - + protected $BaseController; public function setUp() @@ -64,6 +65,10 @@ class ControllerTest extends AnimeClientTestCase { 'Aviat\AnimeClient\Controller', new MangaController($this->container) ); + $this->assertInstanceOf( + 'Aviat\AnimeClient\Controller', + new CharacterController($this->container) + ); $this->assertInstanceOf( 'Aviat\AnimeClient\Controller', new CollectionController($this->container) diff --git a/tests/ControllerTraitTest.php b/tests/ControllerTraitTest.php new file mode 100644 index 00000000..256acafd --- /dev/null +++ b/tests/ControllerTraitTest.php @@ -0,0 +1,39 @@ + + * @copyright 2015 - 2017 Timothy J. Warren + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @version 4.0 + * @link https://git.timshomepage.net/timw4mail/HummingBirdAnimeClient + */ + +namespace Aviat\AnimeClient\Tests; + +use Aviat\AnimeClient\ControllerTrait; + +class ControllerTraitTest extends AnimeClientTestCase { + + public function setUp() + { + parent::setUp(); + + $this->controller = new class { + use ControllerTrait; + }; + } + + public function testFormatTitle() + { + $this->assertEquals( + $this->controller->formatTitle('foo', 'bar', 'baz'), + 'foo · bar · baz' + ); + } +} \ No newline at end of file