Refactor, increase test coverage
timw4mail/HummingBirdAnimeClient/pipeline/head This commit looks good Details

This commit is contained in:
Timothy Warren 2020-12-10 15:59:37 -05:00
parent 0029dd2fb8
commit 292d9bbaaf
44 changed files with 194 additions and 409 deletions

View File

@ -30,7 +30,7 @@ class RoboFile extends Tasks {
*
* @var array
*/
protected $taskDirs = [
protected array $taskDirs = [
'build/logs',
'build/pdepend',
'build/phpdox',
@ -41,7 +41,7 @@ class RoboFile extends Tasks {
*
* @var array
*/
protected $cleanDirs = [
protected array $cleanDirs = [
'coverage',
'docs',
'phpdoc',

View File

@ -255,6 +255,13 @@ $routes = [
'path' => '/logout',
'action' => 'logout',
],
'history' => [
'controller' => 'history',
'path' => '/history/{type}',
'tokens' => [
'type' => SLUG_PATTERN
]
],
'increment' => [
'path' => '/{controller}/increment',
'action' => 'increment',
@ -288,19 +295,12 @@ $routes = [
],
],
'list' => [
'path' => '/{controller}/{type}{/view}',
'path' => '/{controller}/{status}{/view}',
'tokens' => [
'type' => ALPHA_SLUG_PATTERN,
'status' => ALPHA_SLUG_PATTERN,
'view' => ALPHA_SLUG_PATTERN,
],
],
'history' => [
'controller' => 'history',
'path' => '/history/{type}',
'tokens' => [
'type' => SLUG_PATTERN
]
],
'index_redirect' => [
'path' => '/',
'action' => 'redirectToDefaultRoute',

View File

@ -21,7 +21,7 @@ use function Amp\Promise\wait;
use InvalidArgumentException;
use Amp\Http\Client\Request;
use Aviat\AnimeClient\API\Anilist;
use Aviat\AnimeClient\Anilist;
use Aviat\AnimeClient\API\Mapping\{AnimeWatchingStatus, MangaReadingStatus};
use Aviat\AnimeClient\Types\FormItem;
use Aviat\Ion\Json;

View File

@ -18,7 +18,7 @@ namespace Aviat\AnimeClient\API\Anilist;
use Amp\Http\Client\Request;
use Amp\Http\Client\Response;
use Aviat\AnimeClient\API\Anilist;
use Aviat\AnimeClient\Anilist;
use Aviat\Ion\Di\ContainerAware;
use Aviat\Ion\Di\ContainerInterface;
use Aviat\Ion\Json;

View File

@ -23,7 +23,6 @@ trait RequestBuilderTrait {
/**
* The request builder for the Anilist API
* @var RequestBuilder
*/
protected RequestBuilder $requestBuilder;
@ -33,7 +32,7 @@ trait RequestBuilderTrait {
* @param RequestBuilder $requestBuilder
* @return self
*/
public function setRequestBuilder($requestBuilder): self
public function setRequestBuilder(RequestBuilder $requestBuilder): self
{
$this->requestBuilder = $requestBuilder;
return $this;

View File

@ -24,33 +24,15 @@ class MediaListEntry extends AbstractType {
*/
public $id;
/**
* @var string|null
*/
public ?string $notes;
/**
* @var bool
*/
public ?bool $private;
/**
* @var int
*/
public int $progress;
/**
* @var int
*/
public ?int $repeat;
/**
* @var string
*/
public string $status;
/**
* @var int
*/
public ?int $score;
}

View File

@ -33,7 +33,7 @@ use Aviat\AnimeClient\API\Kitsu\Transformer\{
MangaListTransformer,
MangaTransformer
};
use Aviat\AnimeClient\Enum\ListType;
use Aviat\AnimeClient\Enum\MediaType;
use Aviat\AnimeClient\Kitsu as K;
use Aviat\AnimeClient\Types\Anime;
use Aviat\AnimeClient\Types\MangaPage;
@ -319,7 +319,7 @@ final class Model {
if ($list === NULL)
{
$data = $this->getList(ListType::ANIME, $status) ?? [];
$data = $this->getList(MediaType::ANIME, $status) ?? [];
// Bail out on no data
if (empty($data))
@ -352,7 +352,7 @@ final class Model {
*/
public function getAnimeListCount(string $status = '') : int
{
return $this->getListCount(ListType::ANIME, $status);
return $this->getListCount(MediaType::ANIME, $status);
}
/**
@ -462,7 +462,7 @@ final class Model {
if ($list === NULL)
{
$data = $this->getList(ListType::MANGA, $status) ?? [];
$data = $this->getList(MediaType::MANGA, $status) ?? [];
// Bail out on no data
if (empty($data))
@ -495,7 +495,7 @@ final class Model {
*/
public function getMangaListCount(string $status = '') : int
{
return $this->getListCount(ListType::MANGA, $status);
return $this->getListCount(MediaType::MANGA, $status);
}
/**

View File

@ -14,7 +14,7 @@
* @link https://git.timshomepage.net/timw4mail/HummingBirdAnimeClient
*/
namespace Aviat\AnimeClient\API;
namespace Aviat\AnimeClient;
use Aviat\AnimeClient\API\Enum\{
AnimeWatchingStatus\Kitsu as KAWS,

View File

@ -250,7 +250,7 @@ function getResponse ($request): Response
* @param bool $webp
* @return string
*/
function getLocalImg ($kitsuUrl, $webp = TRUE): string
function getLocalImg (string $kitsuUrl, $webp = TRUE): string
{
if (empty($kitsuUrl) || ( ! is_string($kitsuUrl)))
{
@ -360,7 +360,7 @@ function clearCache(CacheInterface $cache): bool
Kitsu::AUTH_TOKEN_REFRESH_CACHE_KEY,
], NULL);
$userData = array_filter((array)$userData, fn ($value) => $value !== NULL);
$userData = array_filter((array)$userData, static fn ($value) => $value !== NULL);
$cleared = $cache->clear();
$saved = ( ! empty($userData))

View File

@ -25,7 +25,8 @@ use Aviat\AnimeClient\API\{
use Aviat\AnimeClient\API;
use Aviat\AnimeClient\API\Anilist;
use Aviat\AnimeClient\API\Mapping\{AnimeWatchingStatus, MangaReadingStatus};
use Aviat\AnimeClient\Enum\{ListType, SyncAction};
use Aviat\AnimeClient\Enum;
use Aviat\AnimeClient\Enum\{MediaType, SyncAction};
use Aviat\AnimeClient\Types\FormItem;
use Aviat\Ion\Di\Exception\ContainerException;
use Aviat\Ion\Di\Exception\NotFoundException;
@ -73,7 +74,7 @@ final class SyncLists extends BaseCommand {
{
$this->init();
foreach ([ListType::ANIME, ListType::MANGA] as $type)
foreach ([MediaType::ANIME, MediaType::MANGA] as $type)
{
// Main Sync flow
$this->fetchCount($type);
@ -100,7 +101,7 @@ final class SyncLists extends BaseCommand {
$this->setCache($this->container->get('cache'));
$config = $this->container->get('config');
$anilistEnabled = $config->get([API::ANILIST, 'enabled']);
$anilistEnabled = $config->get([Enum\API::ANILIST, 'enabled']);
// We can't sync kitsu against itself!
if ( ! $anilistEnabled)
@ -165,8 +166,8 @@ final class SyncLists extends BaseCommand {
$this->clearLine();
return [
API::ANILIST => $anilist,
API::KITSU => $kitsu,
Enum\API::ANILIST => $anilist,
Enum\API::KITSU => $kitsu,
];
}
@ -182,17 +183,17 @@ final class SyncLists extends BaseCommand {
$this->echo('Normalizing List Data');
$progress = new Widgets\ProgressBar($this->getConsole(), 2, 50, FALSE);
$kitsu = $this->transformKitsu($type, $data[API::KITSU]);
$kitsu = $this->transformKitsu($type, $data[Enum\API::KITSU]);
$progress->incr();
$anilist = $this->transformAnilist($type, $data[API::ANILIST]);
$anilist = $this->transformAnilist($type, $data[Enum\API::ANILIST]);
$progress->incr();
$this->clearLine();
return [
API::ANILIST => $anilist,
API::KITSU => $kitsu,
Enum\API::ANILIST => $anilist,
Enum\API::KITSU => $kitsu,
];
}
@ -207,7 +208,7 @@ final class SyncLists extends BaseCommand {
{
$this->echo('Comparing List Items');
return $this->compareLists($type, $data[API::ANILIST], $data[API::KITSU]);
return $this->compareLists($type, $data[Enum\API::ANILIST], $data[Enum\API::KITSU]);
}
/**
@ -280,8 +281,8 @@ final class SyncLists extends BaseCommand {
private function fetchAnilist(string $type): array
{
static $list = [
ListType::ANIME => NULL,
ListType::MANGA => NULL,
MediaType::ANIME => NULL,
MediaType::MANGA => NULL,
];
// This uses a static so I don't have to fetch this list twice for a count
@ -435,12 +436,12 @@ final class SyncLists extends BaseCommand {
continue;
}
if (in_array(API::KITSU, $item['updateType'], TRUE))
if (in_array(Enum\API::KITSU, $item['updateType'], TRUE))
{
$kitsuUpdateItems[] = $item['data'];
}
if (in_array(API::ANILIST, $item['updateType'], TRUE))
if (in_array(Enum\API::ANILIST, $item['updateType'], TRUE))
{
$anilistUpdateItems[] = $item['data'];
}
@ -448,7 +449,7 @@ final class SyncLists extends BaseCommand {
continue;
}
$statusMap = ($type === ListType::ANIME) ? AnimeWatchingStatus::class : MangaReadingStatus::class;
$statusMap = ($type === MediaType::ANIME) ? AnimeWatchingStatus::class : MangaReadingStatus::class;
// Looks like this item only exists on Kitsu
$kItem = $kitsuItem['data'];
@ -528,7 +529,7 @@ final class SyncLists extends BaseCommand {
if ($kitsuItem['data']['status'] === 'completed' && $kitsuItem['data']['reconsuming'] === TRUE)
{
$update['data']['reconsuming'] = FALSE;
$return['updateType'][] = API::KITSU;
$return['updateType'][] = Enum\API::KITSU;
}
// If status is the same, and progress count is different, use greater progress
@ -537,12 +538,12 @@ final class SyncLists extends BaseCommand {
if ($diff['progress'] === self::KITSU_GREATER)
{
$update['data']['progress'] = $kitsuItem['data']['progress'];
$return['updateType'][] = API::ANILIST;
$return['updateType'][] = Enum\API::ANILIST;
}
else if($diff['progress'] === self::ANILIST_GREATER)
{
$update['data']['progress'] = $anilistItem['data']['progress'];
$return['updateType'][] = API::KITSU;
$return['updateType'][] = Enum\API::KITSU;
}
}
@ -552,12 +553,12 @@ final class SyncLists extends BaseCommand {
if ($dateDiff === self::KITSU_GREATER)
{
$update['data']['status'] = $kitsuItem['data']['status'];
$return['updateType'][] = API::ANILIST;
$return['updateType'][] = Enum\API::ANILIST;
}
else if ($dateDiff === self::ANILIST_GREATER)
{
$update['data']['status'] = $anilistItem['data']['status'];
$return['updateType'][] = API::KITSU;
$return['updateType'][] = Enum\API::KITSU;
}
}
@ -574,7 +575,7 @@ final class SyncLists extends BaseCommand {
$update['data']['progress'] = $kitsuItem['data']['progress'];
}
$return['updateType'][] = API::ANILIST;
$return['updateType'][] = Enum\API::ANILIST;
}
else if($dateDiff === self::ANILIST_GREATER)
{
@ -585,7 +586,7 @@ final class SyncLists extends BaseCommand {
$update['data']['progress'] = $kitsuItem['data']['progress'];
}
$return['updateType'][] = API::KITSU;
$return['updateType'][] = Enum\API::KITSU;
}
}
@ -599,12 +600,12 @@ final class SyncLists extends BaseCommand {
)
{
$update['data']['ratingTwenty'] = $kitsuItem['data']['ratingTwenty'];
$return['updateType'][] = API::ANILIST;
$return['updateType'][] = Enum\API::ANILIST;
}
else if($dateDiff === self::ANILIST_GREATER && $anilistItem['data']['rating'] !== 0)
{
$update['data']['ratingTwenty'] = $anilistItem['data']['rating'] * 2;
$return['updateType'][] = API::KITSU;
$return['updateType'][] = Enum\API::KITSU;
}
}
@ -614,12 +615,12 @@ final class SyncLists extends BaseCommand {
if ( ! empty($kitsuItem['data']['notes']))
{
$update['data']['notes'] = $kitsuItem['data']['notes'];
$return['updateType'][] = API::ANILIST;
$return['updateType'][] = Enum\API::ANILIST;
}
else
{
$update['data']['notes'] = $anilistItem['data']['notes'];
$return['updateType'][] = API::KITSU;
$return['updateType'][] = Enum\API::KITSU;
}
}
@ -629,12 +630,12 @@ final class SyncLists extends BaseCommand {
if ($diff['reconsumeCount'] === self::KITSU_GREATER)
{
$update['data']['reconsumeCount'] = $kitsuItem['data']['reconsumeCount'];
$return['updateType'][] = API::ANILIST;
$return['updateType'][] = Enum\API::ANILIST;
}
else if ($diff['reconsumeCount'] === self::ANILIST_GREATER)
{
$update['data']['reconsumeCount'] = $anilistItem['data']['reconsumeCount'];
$return['updateType'][] = API::KITSU;
$return['updateType'][] = Enum\API::KITSU;
}
}
@ -645,8 +646,8 @@ final class SyncLists extends BaseCommand {
}
$return['meta'] = [
API::KITSU => $kitsuItem['data'],
API::ANILIST => $anilistItem['data'],
Enum\API::KITSU => $kitsuItem['data'],
Enum\API::ANILIST => $anilistItem['data'],
'dateDiff' => $dateDiff,
'diff' => $diff,
];
@ -656,7 +657,7 @@ final class SyncLists extends BaseCommand {
// Fill in missing data values for update
// so I don't have to create a really complex graphql query
// to handle each combination of fields
if ($return['updateType'][0] === API::ANILIST)
if ($return['updateType'][0] === Enum\API::ANILIST)
{
// Anilist GraphQL expects a rating from 1-100
$prevData = [
@ -673,7 +674,7 @@ final class SyncLists extends BaseCommand {
$return['data']['data'] = array_merge($prevData, $return['data']['data']);
}
else if ($return['updateType'][0] === API::KITSU)
else if ($return['updateType'][0] === Enum\API::KITSU)
{
$prevData = [
'notes' => $anilistItem['data']['notes'],
@ -707,7 +708,7 @@ final class SyncLists extends BaseCommand {
* @param string $type
* @throws Throwable
*/
private function updateKitsuListItems(array $itemsToUpdate, string $action = SyncAction::UPDATE, string $type = ListType::ANIME): void
private function updateKitsuListItems(array $itemsToUpdate, string $action = SyncAction::UPDATE, string $type = MediaType::ANIME): void
{
$requester = new ParallelAPIRequest();
foreach($itemsToUpdate as $item)
@ -776,7 +777,7 @@ final class SyncLists extends BaseCommand {
* @param string $type
* @throws Throwable
*/
private function updateAnilistListItems(array $itemsToUpdate, string $action = SyncAction::UPDATE, string $type = ListType::ANIME): void
private function updateAnilistListItems(array $itemsToUpdate, string $action = SyncAction::UPDATE, string $type = MediaType::ANIME): void
{
$requester = new ParallelAPIRequest();

View File

@ -69,11 +69,11 @@ final class UpdateThumbnails extends ClearThumbnails {
public function getImageList(): array
{
$animeIds = array_map(
fn ($item) => $item['media']['id'],
static fn ($item) => $item['media']['id'],
$this->kitsuModel->getThumbList('ANIME')
);
$mangaIds = array_map(
fn ($item) => $item['media']['id'],
static fn ($item) => $item['media']['id'],
$this->kitsuModel->getThumbList('MANGA')
);

View File

@ -26,6 +26,7 @@ final class VerticalTabs {
* also used to generate id attributes
* @param array $tabData The data used to create the tab content, indexed by the tab label
* @param callable $cb The function to generate the tab content
* @param string $className
* @return string
*/
public function __invoke(

View File

@ -46,49 +46,41 @@ class Controller {
/**
* The authentication object
* @var Auth $auth ;
*/
protected Auth $auth;
/**
* Cache manager
* @var CacheInterface
*/
protected CacheInterface $cache;
/**
* The global configuration object
* @var ConfigInterface $config
*/
public ConfigInterface $config;
/**
* Request object
* @var ServerRequestInterface $request
*/
protected ServerRequestInterface $request;
/**
* Url generation class
* @var UrlGenerator
*/
protected UrlGenerator $urlGenerator;
/**
* Aura url generator
* @var Generator
*/
protected Generator $url;
/**
* Session segment
* @var Segment
*/
protected Segment $session;
/**
* Common data to be sent to views
* @var array
*/
protected array $baseData = [];

View File

@ -66,17 +66,17 @@ final class Anime extends BaseController {
/**
* Show a portion, or all of the anime list
*
* @param string|int $type - The section of the list
* @param string $view - List or cover view
* @param string|int $status - The section of the list
* @param string|null $view - List or cover view
* @throws ContainerException
* @throws NotFoundException
* @throws InvalidArgumentException
* @throws Throwable
* @return void
*/
public function index($type = KitsuWatchingStatus::WATCHING, string $view = NULL): void
public function index($status = KitsuWatchingStatus::WATCHING, ?string $view = NULL): void
{
if ( ! in_array($type, [
if ( ! in_array($status, [
'all',
'watching',
'plan_to_watch',
@ -88,10 +88,10 @@ final class Anime extends BaseController {
$this->errorPage(404, 'Not Found', 'Page not found');
}
$title = array_key_exists($type, AnimeWatchingStatus::ROUTE_TO_TITLE)
$title = array_key_exists($status, AnimeWatchingStatus::ROUTE_TO_TITLE)
? $this->formatTitle(
$this->config->get('whose_list') . "'s Anime List",
AnimeWatchingStatus::ROUTE_TO_TITLE[$type]
AnimeWatchingStatus::ROUTE_TO_TITLE[$status]
)
: '';
@ -100,8 +100,8 @@ final class Anime extends BaseController {
'list' => 'list'
];
$data = ($type !== 'all')
? $this->model->getList(AnimeWatchingStatus::ROUTE_TO_KITSU[$type])
$data = ($status !== 'all')
? $this->model->getList(AnimeWatchingStatus::ROUTE_TO_KITSU[$status])
: $this->model->getAllLists();
$this->outputHTML('anime/' . $viewMap[$view], [
@ -305,17 +305,17 @@ final class Anime extends BaseController {
/**
* View details of an anime
*
* @param string $animeId
* @param string $id
* @throws ContainerException
* @throws NotFoundException
* @throws InvalidArgumentException
* @return void
*/
public function details(string $animeId): void
public function details(string $id): void
{
try
{
$data = $this->model->getAnime($animeId);
$data = $this->model->getAnime($id);
if ($data->isEmpty())
{
@ -349,7 +349,7 @@ final class Anime extends BaseController {
}
}
public function random()
public function random(): void
{
try
{

View File

@ -94,7 +94,7 @@ final class AnimeCollection extends BaseController {
* @throws InvalidArgumentException
* @return void
*/
public function view($view): void
public function view(string $view = ''): void
{
$viewMap = [
'' => 'cover',

View File

@ -303,19 +303,16 @@ final class Manga extends Controller {
/**
* View details of an manga
*
* @param string $manga_id
* @param string $id
* @throws ContainerException
* @throws NotFoundException
* @throws InvalidArgumentException
* @throws Throwable
* @return void
*/
public function details($manga_id): void
public function details(string $id): void
{
$data = $this->model->getManga($manga_id);
$staff = [];
$characters = [];
$data = $this->model->getManga($id);
if ($data->isEmpty())
{
$this->notFound(
@ -333,9 +330,7 @@ final class Manga extends Controller {
'Manga',
$data['title']
),
'characters' => $characters,
'data' => $data,
'staff' => $staff,
]);
}
@ -351,9 +346,6 @@ final class Manga extends Controller {
public function random(): void
{
$data = $this->model->getRandomManga();
$staff = [];
$characters = [];
if ($data->isEmpty())
{
$this->notFound(
@ -371,9 +363,7 @@ final class Manga extends Controller {
'Manga',
$data['title']
),
'characters' => $characters,
'data' => $data,
'staff' => $staff,
]);
}
}

View File

@ -14,9 +14,11 @@
* @link https://git.timshomepage.net/timw4mail/HummingBirdAnimeClient
*/
namespace Aviat\AnimeClient;
namespace Aviat\AnimeClient\Enum;
class API {
use Aviat\Ion\Enum;
final class API extends Enum {
public const ANILIST = 'anilist';
public const KITSU = 'kitsu';
}

View File

@ -19,9 +19,9 @@ namespace Aviat\AnimeClient\Enum;
use Aviat\Ion\Enum as BaseEnum;
/**
* Types of lists
* Types of media
*/
final class ListType extends BaseEnum {
final class MediaType extends BaseEnum {
public const ANIME = 'anime';
public const DRAMA = 'drama';
public const MANGA = 'manga';

View File

@ -49,7 +49,8 @@ final class Picture {
];
/**
* Create the html for an html picture element
* Create the html for an html picture element.
* Uses .webp images with fallback
*
* @param string $uri
* @param string $fallbackExt

View File

@ -187,14 +187,14 @@ final class Kitsu {
$host = parse_url($url, \PHP_URL_HOST);
$links[] = [
'meta' => static::getServiceMetaData($host),
'meta' => self::getServiceMetaData($host),
'link' => $streamingLink['url'],
'subs' => $streamingLink['subs'],
'dubs' => $streamingLink['dubs']
];
}
usort($links, fn ($a, $b) => $a['meta']['name'] <=> $b['meta']['name']);
usort($links, static fn ($a, $b) => $a['meta']['name'] <=> $b['meta']['name']);
return $links;
}
@ -282,7 +282,7 @@ final class Kitsu {
*/
protected static function getServiceMetaData(string $hostname = NULL): array
{
$hostname = str_replace('www.', '', $hostname);
$hostname = str_replace('www.', '', $hostname ?? '');
$serviceMap = [
'animelab.com' => [
@ -412,11 +412,11 @@ final class Kitsu {
/**
* Determine if an alternate title is unique enough to list
*
* @param string $title
* @param string|null $title
* @param array $existingTitles
* @return bool
*/
private static function titleIsUnique(string $title = NULL, array $existingTitles = []): bool
private static function titleIsUnique(?string $title = NULL, array $existingTitles = []): bool
{
if (empty($title))
{

View File

@ -134,6 +134,7 @@ final class AnimeCollection extends Collection {
}
// Organize the media types into groups
// @TODO: make this more database-driven, rather than hardcoded
return [
'Common' => [
2 => $flatList[2], // Blu-ray

View File

@ -28,7 +28,7 @@ abstract class DB {
* The database connection information array
* @var array $dbConfig
*/
protected $dbConfig = [];
protected array $dbConfig = [];
/**
* Constructor

View File

@ -22,34 +22,16 @@ use Aviat\AnimeClient\API\Kitsu\Enum\AnimeAiringStatus;
* Type representing an anime within a watch list
*/
class Anime extends AbstractType {
/**
* @var string
*/
public ?string $age_rating;
/**
* @var string
*/
public ?string $age_rating_guide;
/**
* @var string
*/
public ?string $cover_image;
/**
* @var string|int
*/
public ?int $episode_count;
/**
* @var string|int
*/
public ?int $episode_length;
/**
* @var array
*/
public array $genres = [];
/**
@ -57,67 +39,33 @@ class Anime extends AbstractType {
*/
public $id = '';
/**
* @var array
*/
public array $included = [];
/**
* @var string
*/
public ?string $show_type;
/**
* @var string
*/
public ?string $slug;
/**
* @var AnimeAiringStatus
*/
public string $status = AnimeAiringStatus::FINISHED_AIRING;
/**
* @var array
*/
public ?array $streaming_links = [];
/**
* @var string
*/
public ?string $synopsis;
/**
* @var string
*/
public ?string $title;
/**
* @var array
*/
public array $titles = [];
/**
* @var array
*/
public array $titles_more = [];
/**
* @var string
*/
public ?string $trailer_id;
/**
* Length of the entire series in seconds
*
* @var int|null
*/
public ?int $total_length;
/**
* Kitsu detail page url
*
* @var string
*/
public ?string $url;
}

View File

@ -20,14 +20,8 @@ namespace Aviat\AnimeClient\Types;
* Type representing an anime watch list item
*/
final class AnimeListItem extends AbstractType {
/**
* @var string
*/
public ?string $id;
/**
* @var string
*/
public ?string $mal_id;
/**
@ -35,47 +29,26 @@ final class AnimeListItem extends AbstractType {
*/
public $anilist_item_id;
/**
* @var array
*/
public array $episodes = [
'length' => 0,
'total' => 0,
'watched' => '',
];
/**
* @var array
*/
public array $airing = [
'status' => '',
'started' => '',
'ended' => '',
];
/**
* @var Anime
*/
public ?Anime $anime;
/**
* @var string
*/
public ?string $notes;
/**
* @var bool
*/
public bool $private = FALSE;
/**
* @var bool
*/
public bool $rewatching = FALSE;
/**
* @var int
*/
public int $rewatched = 0;
/**
@ -85,10 +58,8 @@ final class AnimeListItem extends AbstractType {
/**
* One of Aviat\AnimeClient\API\Enum\AnimeWatchingStatus
*
* @var string
*/
public $watching_status;
public string $watching_status;
public function setAnime($anime): void
{

View File

@ -20,18 +20,9 @@ namespace Aviat\AnimeClient\Types;
* Type representing an Anime object for a detail page
*/
final class AnimePage extends Anime {
/**
* @var array
*/
public array $characters = [];
/**
* @var array
*/
public array $links = [];
/**
* @var array
*/
public array $staff = [];
}

View File

@ -20,14 +20,8 @@ namespace Aviat\AnimeClient\Types;
* Type representing a character for display
*/
final class Character extends AbstractType {
/**
* @var array
*/
public array $castings = [];
/**
* @var string
*/
public ?string $description;
/**
@ -35,29 +29,14 @@ final class Character extends AbstractType {
*/
public $id;
/**
* @var array
*/
public array $included = [];
/**
* @var Media
*/
public ?Media $media;
/**
* @var string
*/
public ?string $name;
/**
* @var array
*/
public array $names = [];
/**
* @var array
*/
public array $otherNames = [];
public function setMedia ($media): void

View File

@ -17,13 +17,7 @@
namespace Aviat\AnimeClient\Types;
final class Characters extends AbstractType {
/**
* @var array
*/
public array $main = [];
/**
* @var array
*/
public array $supporting = [];
}

View File

@ -22,28 +22,16 @@ class Config extends AbstractType {
// Config files/namespaces
// ------------------------------------------------------------------------
/**
* @var Config\Anilist
*/
public ?Config\Anilist $anilist;
/**
* @var Config\Cache
*/
public ?Config\Cache $cache;
/**
* @var Config\Database
*/
public ?Config\Database $database;
// ------------------------------------------------------------------------
// Settings in config.toml
// ------------------------------------------------------------------------
/**
* @var string
*/
public ?string $asset_path; // Path to public folder for urls
/**
@ -60,8 +48,6 @@ class Config extends AbstractType {
/**
* Default Anime list status page, values are listed in
* Aviat\AnimeClient\API\Enum\AnimeWatchingStatus\Title
*
* @var string
*/
public ?string $default_anime_list_path;
@ -75,8 +61,6 @@ class Config extends AbstractType {
/**
* Default Manga list status page, values are listed in
* Aviat\AnimeClient\API\Enum\MangaReadingStatus\Title
*
* @var string
*/
public ?string $default_manga_list_path;
@ -85,25 +69,13 @@ class Config extends AbstractType {
*/
public ?string $default_view_type;
/**
* @var string
*/
public ?string $kitsu_username;
/**
* @var bool
*/
public bool $secure_urls = TRUE;
/**
* @var bool
*/
public $show_anime_collection = FALSE;
public bool $show_anime_collection = FALSE;
/**
* @var bool
*/
public $show_manga_collection = FALSE;
public bool $show_manga_collection = FALSE;
/**
* CSS theme: light, dark, or auto-switching
@ -112,9 +84,6 @@ class Config extends AbstractType {
*/
public ?string $theme;
/**
* @var string
*/
public ?string $whose_list;
// ------------------------------------------------------------------------
@ -135,34 +104,16 @@ class Config extends AbstractType {
// Generated config values
// ------------------------------------------------------------------------
/**
* @var string
*/
public ?string $asset_dir; // Path to public folder for local files
/**
* @var string
*/
public ?string $base_config_dir;
/**
* @var string
*/
public ?string $config_dir;
/**
* @var string
*/
public ?string $data_cache_path;
/**
* @var string
*/
public ?string $img_cache_path;
/**
* @var string
*/
public ?string $view_path;
public function setAnilist ($data): void

View File

@ -19,38 +19,17 @@ namespace Aviat\AnimeClient\Types\Config;
use Aviat\AnimeClient\Types\AbstractType;
class Anilist extends AbstractType {
/**
* @var bool
*/
public $enabled = FALSE;
public bool $enabled = FALSE;
/**
* @var string
*/
public $client_id;
public ?string $client_id;
/**
* @var string
*/
public $client_secret;
public ?string $client_secret;
/**
* @var string
*/
public $access_token;
public ?string $access_token;
/**
* @var string
*/
public $access_token_expires;
public ?int $access_token_expires;
/**
* @var string
*/
public $refresh_token;
public ?string $refresh_token;
/**
* @var string
*/
public $username;
public ?string $username;
}

View File

@ -22,21 +22,24 @@ class Cache extends AbstractType {
/**
* @var string
*/
public $driver;
public string $driver = 'null';
public $host;
public ?string $host;
/**
* @var string|int|null
*/
public $port;
public $database;
public ?string $database;
/**
* @var array
*/
public $connection = [];
public array $connection = [];
/**
* @var array
*/
public $options = [];
public array $options = [];
}

View File

@ -22,35 +22,35 @@ class Database extends AbstractType {
/**
* @var string
*/
public $type;
public string $type = 'sqlite';
/**
* @var string
* @var string|null
*/
public $host;
public ?string $host;
/**
* @var string
* @var string|null
*/
public $user;
public ?string $user;
/**
* @var string
* @var string|null
*/
public $pass;
public ?string $pass;
/**
* @var string|int
* @var string|int|null
*/
public $port;
/**
* @var string
* @var string|null
*/
public $database;
public ?string $database;
/**
* @var string
* @var string|null
*/
public $file;
public ?string $file;
}

View File

@ -25,19 +25,13 @@ class FormItem extends AbstractType {
*/
public $id;
/**
* @var string
*/
public ?string $anilist_item_id;
/**
* @var string|int
*/
public $mal_id;
public $mal_id;
/**
* @var FormItemData
*/
public ?FormItemData $data;
public function setData($value): void

View File

@ -20,14 +20,8 @@ namespace Aviat\AnimeClient\Types;
* Type representing a Media object for editing/syncing
*/
class FormItemData extends AbstractType {
/**
* @var string
*/
public ?string $notes;
/**
* @var bool
*/
public ?bool $private = FALSE;
/**
@ -50,9 +44,6 @@ class FormItemData extends AbstractType {
*/
public $reconsumeCount;
/**
* @var bool
*/
public bool $reconsuming = FALSE;
/**
@ -62,8 +53,6 @@ class FormItemData extends AbstractType {
/**
* W3C Format Date string
*
* @var string
*/
public ?string $updatedAt;
}

View File

@ -20,47 +20,47 @@ use DateTimeImmutable;
class HistoryItem extends AbstractType {
/**
* @var string Title of the anime/manga
* Title of the anime/manga
*/
public string $title = '';
/**
* @var string The url of the cover image
* The url of the cover image
*/
public string $coverImg = '';
/**
* @var string The type of action done
* The type of action done
*/
public string $action = '';
/**
* @var bool Is this item a combination of items?
* Is this item a combination of items?
*/
public bool $isAggregate = FALSE;
/**
* @var string The kind of history event
* The kind of history event
*/
public string $kind = '';
/**
* @var DateTimeImmutable When the item was last updated
* When the item was last updated
*/
public ?DateTimeImmutable $updated = NULL;
/**
* @var array Range of updated times for the aggregated item
* Range of updated times for the aggregated item
*/
public array $dateRange = [];
/**
* @var string Url to details page
* Url to details page
*/
public string $url = '';
/**
* @var array The item before transformation
* The item before transformation
*/
public array $original = [];
}

View File

@ -17,13 +17,7 @@
namespace Aviat\AnimeClient\Types;
final class Media extends AbstractType {
/**
* @var array
*/
public array $anime = [];
/**
* @var array
*/
public array $manga = [];
}

View File

@ -20,48 +20,21 @@ namespace Aviat\AnimeClient\Types;
* Type representing a Kitsu user for display
*/
final class User extends AbstractType {
/**
* @var string
*/
public ?string $about;
/**
* @var string
*/
public ?string $avatar;
/**
* @var array
*/
public ?array $favorites;
/**
* @var string
*/
public ?string $location;
/**
* @var string
*/
public ?string $name;
/**
* @var string
*/
public ?string $slug;
/**
* @var array
*/
public ?array $stats;
/**
* @var array
*/
public ?array $waifu;
/**
* @var string
*/
public ?string $website;
}

View File

@ -49,7 +49,7 @@ class Event {
// Call each subscriber with the provided arguments
if (array_key_exists($eventName, static::$eventMap))
{
array_walk(static::$eventMap[$eventName], fn ($fn) => $fn(...$args));
array_walk(static::$eventMap[$eventName], static fn ($fn) => $fn(...$args));
}
}
}

View File

@ -37,7 +37,7 @@ class Friend {
* Reflection class of the object
* @var ReflectionClass
*/
private $_reflect_;
private ReflectionClass $_reflect_;
/**
* Create a friend object

View File

@ -180,6 +180,7 @@ class HttpView implements ViewInterface{
/**
* Send the appropriate response
*
* @codeCoverageIgnore
* @throws DoubleRenderException
* @throws \InvalidArgumentException
* @return void

View File

@ -92,4 +92,10 @@ class UtilTest extends AnimeClientTestCase {
]);
$this->assertEquals(!$expected, $this->util->isFormPage());
}
public function testAriaCurrent(): void
{
$this->assertEquals('true', Util::ariaCurrent(true));
$this->assertEquals('false', Util::ariaCurrent(false));
}
}

29
tests/Ion/EventTest.php Normal file
View File

@ -0,0 +1,29 @@
<?php declare(strict_types=1);
/**
* Hummingbird Anime List Client
*
* An API client for Kitsu to manage anime and manga watch lists
*
* PHP version 7.4
*
* @package HummingbirdAnimeClient
* @author Timothy J. Warren <tim@timshomepage.net>
* @copyright 2015 - 2020 Timothy J. Warren
* @license http://www.opensource.org/licenses/mit-license.html MIT License
* @version 5.1
* @link https://git.timshomepage.net/timw4mail/HummingBirdAnimeClient
*/
namespace Aviat\Ion\Tests;
use Aviat\Ion\Event;
use PHPUnit\Framework\TestCase;
class EventTest extends TestCase {
public function testEmit(): void
{
Event::on('test-event', fn ($fired) => $this->assertTrue($fired));
Event::emit('test-event', [true]);
}
}

View File

@ -29,7 +29,7 @@ class HtmlViewTest extends HttpViewTest {
$this->view = new TestHtmlView($this->container);
}
public function testRenderTemplate()
public function testRenderTemplate(): void
{
$path = _dir(self::TEST_VIEW_DIR, 'test_view.php');
$expected = '<tag>foo</tag>';

View File

@ -32,7 +32,7 @@ class HttpViewTest extends IonTestCase {
$this->friend = new Friend($this->view);
}
public function testGetOutput()
public function testGetOutput():void
{
$this->friend->setOutput('foo');
$this->assertEquals('foo', $this->friend->getOutput());
@ -42,34 +42,34 @@ class HttpViewTest extends IonTestCase {
$this->assertTrue($this->friend->hasRendered);
}
public function testSetOutput()
public function testSetOutput():void
{
$same = $this->view->setOutput('<h1></h1>');
$this->assertEquals($same, $this->view);
$this->assertEquals('<h1></h1>', $this->view->getOutput());
}
public function testAppendOutput()
public function testAppendOutput():void
{
$this->view->setOutput('<h1>');
$this->view->appendOutput('</h1>');
$this->assertEquals('<h1></h1>', $this->view->getOutput());
}
public function testSetStatusCode()
public function testSetStatusCode():void
{
$view = $this->view->setStatusCode(404);
$this->assertEquals(404, $view->response->getStatusCode());
}
public function testAddHeader()
public function testAddHeader():void
{
$view = $this->view->addHeader('foo', 'bar');
$this->assertTrue($view->response->hasHeader('foo'));
$this->assertEquals(['bar'], $view->response->getHeader('foo'));
}
public function testSendDoubleRenderException()
public function testSendDoubleRenderException():void
{
$this->expectException(DoubleRenderException::class);
$this->expectExceptionMessage('A view can only be rendered once, because headers can only be sent once.');
@ -81,7 +81,7 @@ class HttpViewTest extends IonTestCase {
$this->view->send();
}
public function test__toStringDoubleRenderException()
public function test__toStringDoubleRenderException():void
{
$this->expectException(DoubleRenderException::class);
$this->expectExceptionMessage('A view can only be rendered once, because headers can only be sent once.');
@ -92,4 +92,18 @@ class HttpViewTest extends IonTestCase {
// Second render
$this->view->__toString();
}
public function testRedirect(): void
{
$this->friend->redirect('http://example.com');
$this->assertInstanceOf(\Laminas\Diactoros\Response\RedirectResponse::class, $this->friend->response);
}
public function testOutput(): void
{
$this->friend->setOutput('<h1></h1>');
$this->friend->send();
$this->assertTrue($this->friend->hasRendered);
}
}

View File

@ -28,7 +28,7 @@ class JsonViewTest extends HttpViewTest {
$this->friend = new Friend($this->view);
}
public function testSetOutputJSON()
public function testSetOutputJSON():void
{
// Extend view class to remove destructor which does output
$view = new TestJsonView();
@ -40,7 +40,7 @@ class JsonViewTest extends HttpViewTest {
$this->assertEquals($expected, $view->getOutput());
}
public function testSetOutput()
public function testSetOutput():void
{
// Directly set string
$view = new TestJsonView();
@ -50,7 +50,7 @@ class JsonViewTest extends HttpViewTest {
$this->assertEquals($expected, $view->getOutput());
}
public function testOutput()
public function testOutputType():void
{
$this->assertEquals('application/json', $this->friend->contentType);
}