feat: download data preview modal
This commit is contained in:
3
.env.test
Normal file
3
.env.test
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# define your env variables for the test env here
|
||||||
|
KERNEL_CLASS='App\Kernel'
|
||||||
|
APP_SECRET='$ecretf0rt3st'
|
||||||
5
.gitignore
vendored
5
.gitignore
vendored
@@ -19,3 +19,8 @@ bolt.db
|
|||||||
phpstan.neon
|
phpstan.neon
|
||||||
###< phpstan/phpstan ###
|
###< phpstan/phpstan ###
|
||||||
.php-cs-fixer.cache
|
.php-cs-fixer.cache
|
||||||
|
|
||||||
|
###> phpunit/phpunit ###
|
||||||
|
/phpunit.xml
|
||||||
|
/.phpunit.cache/
|
||||||
|
###< phpunit/phpunit ###
|
||||||
|
|||||||
4
assets/bootstrap.js
vendored
4
assets/bootstrap.js
vendored
@@ -1,5 +1,7 @@
|
|||||||
|
import PreviewContentDialog from "./components/preview-content-dialog.js";
|
||||||
import EpisodeContainer from './components/episode-container.js';
|
import EpisodeContainer from './components/episode-container.js';
|
||||||
import DownloadOptionTr from './components/download-option-tr.js';
|
import DownloadOptionTr from './components/download-option-tr.js';
|
||||||
|
import DownloadListRow from './components/download-list-row.js';
|
||||||
import MovieContainer from "./components/movie-container.js";
|
import MovieContainer from "./components/movie-container.js";
|
||||||
|
|
||||||
import { startStimulusApp } from '@symfony/stimulus-bundle';
|
import { startStimulusApp } from '@symfony/stimulus-bundle';
|
||||||
@@ -14,6 +16,8 @@ app.register('popover', Popover);
|
|||||||
app.register('dialog', Dialog);
|
app.register('dialog', Dialog);
|
||||||
app.register('dropdown', Dropdown);
|
app.register('dropdown', Dropdown);
|
||||||
|
|
||||||
|
customElements.define('preview-content-dialog', PreviewContentDialog, {extends: 'dialog'});
|
||||||
customElements.define('episode-container', EpisodeContainer);
|
customElements.define('episode-container', EpisodeContainer);
|
||||||
customElements.define('movie-container', MovieContainer);
|
customElements.define('movie-container', MovieContainer);
|
||||||
customElements.define('dl-tr', DownloadOptionTr, {extends: 'tr'});
|
customElements.define('dl-tr', DownloadOptionTr, {extends: 'tr'});
|
||||||
|
customElements.define('download-list-row', DownloadListRow, {extends: 'tr'});
|
||||||
|
|||||||
110
assets/components/download-list-row.js
Normal file
110
assets/components/download-list-row.js
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
export default class DownloadListRow extends HTMLTableRowElement {
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
this.downloadId = this.getAttribute('download-id');
|
||||||
|
this.imdbId = this.getAttribute('imdb-id');
|
||||||
|
this.mediaTitle = this.getAttribute('media-title');
|
||||||
|
this.url = this.getAttribute('url');
|
||||||
|
this.filename = this.getAttribute('filename');
|
||||||
|
this.status = this.getAttribute('status');
|
||||||
|
this.progress = this.getAttribute('progress');
|
||||||
|
this.mediaType = this.getAttribute('media-type');
|
||||||
|
this.episodeId = this.getAttribute('episode-id');
|
||||||
|
this.createdAt = this.getAttribute('created-at');
|
||||||
|
this.updatedAt = this.getAttribute('updated-at');
|
||||||
|
|
||||||
|
// this.previewContent = this.previewContent.bind(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
static get observedAttributes() {
|
||||||
|
return ['download-id', 'imdb-id', 'media-title', 'url', 'filename', 'status', 'progress', 'media-type', 'episode-id', 'created-at', 'updated-at'];
|
||||||
|
}
|
||||||
|
|
||||||
|
attributeChangedCallback(name, oldValue, newValue) {
|
||||||
|
if (oldValue !== newValue) {
|
||||||
|
this[name] = newValue;
|
||||||
|
this.setAttribute(name, newValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
previewContent() {
|
||||||
|
return `
|
||||||
|
<table class="table-auto flex flex-row">
|
||||||
|
<thead>
|
||||||
|
<tr class="flex flex-col">
|
||||||
|
<th class="px-4 py-2">
|
||||||
|
<div class="text-right whitespace-nowrap ">ID</div>
|
||||||
|
</th>
|
||||||
|
<th class="px-4 py-2">
|
||||||
|
<div class="text-right whitespace-nowrap ">IMDB ID</div>
|
||||||
|
</th>
|
||||||
|
<th class="px-4 py-2">
|
||||||
|
<div class="text-right whitespace-nowrap ">Title</div>
|
||||||
|
</th>
|
||||||
|
<th class="px-4 py-2">
|
||||||
|
<div class="text-right whitespace-nowrap ">URL</div>
|
||||||
|
</th>
|
||||||
|
<th class="px-4 py-2">
|
||||||
|
<div class="text-right whitespace-nowrap ">Filename</div>
|
||||||
|
</th>
|
||||||
|
<th class="px-4 py-2">
|
||||||
|
<div class="text-right whitespace-nowrap ">Status</div>
|
||||||
|
</th>
|
||||||
|
<th class="px-4 py-2">
|
||||||
|
<div class="text-right whitespace-nowrap ">Progress</div>
|
||||||
|
</th>
|
||||||
|
<th class="px-4 py-2">
|
||||||
|
<div class="text-right whitespace-nowrap ">Media Type</div>
|
||||||
|
</th>
|
||||||
|
<th class="px-4 py-2">
|
||||||
|
<div class="text-right whitespace-nowrap ">Episode ID</div>
|
||||||
|
</th>
|
||||||
|
<th class="px-4 py-2">
|
||||||
|
<div class="text-right whitespace-nowrap ">Created At</div>
|
||||||
|
</th>
|
||||||
|
<th class="px-4 py-2">
|
||||||
|
<div class="text-right whitespace-nowrap ">Updated At</div>
|
||||||
|
</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr class="flex flex-col">
|
||||||
|
<th class="px-4 py-2">
|
||||||
|
<div class="text-left whitespace-nowrap font-normal">${this.getAttribute('download-id') ?? "-"}</div>
|
||||||
|
</th>
|
||||||
|
<th class="px-4 py-2">
|
||||||
|
<div class="text-left whitespace-nowrap font-normal">${this.getAttribute('imdb-id') ?? "-"}</div>
|
||||||
|
</th>
|
||||||
|
<th class="px-4 py-2">
|
||||||
|
<div class="text-left whitespace-nowrap font-normal">${this.getAttribute('media-title') ?? "-"}</div>
|
||||||
|
</th>
|
||||||
|
<th class="px-4 py-2">
|
||||||
|
<div class="text-left whitespace-nowrap font-normal">${this.getAttribute('url') ?? "-"}</div>
|
||||||
|
</th>
|
||||||
|
<th class="px-4 py-2">
|
||||||
|
<div class="text-left whitespace-nowrap font-normal">${this.getAttribute('filename') ?? "-"}</div>
|
||||||
|
</th>
|
||||||
|
<th class="px-4 py-2">
|
||||||
|
<div class="text-left whitespace-nowrap font-normal">${this.getAttribute('status') ?? "-"}</div>
|
||||||
|
</th>
|
||||||
|
<th class="px-4 py-2">
|
||||||
|
<div class="text-left whitespace-nowrap font-normal">${this.getAttribute('progress') ?? "-"}</div>
|
||||||
|
</th>
|
||||||
|
<th class="px-4 py-2">
|
||||||
|
<div class="text-left whitespace-nowrap font-normal">${this.getAttribute('media-type') ?? "-"}</div>
|
||||||
|
</th>
|
||||||
|
<th class="px-4 py-2">
|
||||||
|
<div class="text-left whitespace-nowrap font-normal">${this.getAttribute('episode-id') ?? "-"}</div>
|
||||||
|
</th>
|
||||||
|
<th class="px-4 py-2">
|
||||||
|
<div class="text-left whitespace-nowrap font-normal">${this.getAttribute('created-at') ?? "-"}</div>
|
||||||
|
</th>
|
||||||
|
<th class="px-4 py-2">
|
||||||
|
<div class="text-left whitespace-nowrap font-normal">${this.getAttribute('updated-at') ?? "-"}</div>
|
||||||
|
</th>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
}
|
||||||
35
assets/components/preview-content-dialog.js
Normal file
35
assets/components/preview-content-dialog.js
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
export default class PreviewContentDialog extends HTMLDialogElement {
|
||||||
|
#headingEl;
|
||||||
|
#contentEl;
|
||||||
|
#closeBtnEl;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
this.#headingEl = this.querySelector('.modal-heading');
|
||||||
|
this.#contentEl = this.querySelector('.modal-content');
|
||||||
|
this.#closeBtnEl = this.querySelector('.modal-close');
|
||||||
|
|
||||||
|
this.setHeading = this.setHeading.bind(this);
|
||||||
|
this.setContent = this.setContent.bind(this);
|
||||||
|
|
||||||
|
document.addEventListener('showPreviewContentModal', (event) => {
|
||||||
|
this.display(event.detail);
|
||||||
|
});
|
||||||
|
document.addEventListener('hidePreviewContentModal', (e) => this.close());
|
||||||
|
this.#closeBtnEl.addEventListener('click', () => this.close());
|
||||||
|
}
|
||||||
|
|
||||||
|
setHeading(heading) {
|
||||||
|
this.#headingEl.innerHTML = heading;
|
||||||
|
}
|
||||||
|
|
||||||
|
setContent(content) {
|
||||||
|
this.#contentEl.innerHTML = content;
|
||||||
|
}
|
||||||
|
|
||||||
|
display({ heading, content }) {
|
||||||
|
this.setHeading(heading);
|
||||||
|
this.setContent(content);
|
||||||
|
this.showModal();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -20,13 +20,46 @@ export default class extends Controller {
|
|||||||
// Here you can add event listeners on the element or target elements,
|
// Here you can add event listeners on the element or target elements,
|
||||||
// add or remove classes, attributes, dispatch custom events, etc.
|
// add or remove classes, attributes, dispatch custom events, etc.
|
||||||
// this.fooTarget.addEventListener('click', this._fooBar)
|
// this.fooTarget.addEventListener('click', this._fooBar)
|
||||||
|
// this.element.addEventListener('click', (event) => {
|
||||||
|
// let previewContentModal = document.querySelector('#previewContentModal');
|
||||||
|
// // previewContentModal.setHeading(event.target.dataset['title']);
|
||||||
|
// // previewContentModal.setContent('<p>Testing this here thingy-ma-bob!</p>');
|
||||||
|
// // previewContentModal.showModal();
|
||||||
|
// let content, heading = ""
|
||||||
|
// if (event.target.tagName !== "TR") {
|
||||||
|
// content = event.target.parentElement.previewContent();
|
||||||
|
// heading = event.target.parentElement.mediaTitle;
|
||||||
|
// } else {
|
||||||
|
// content = event.target.previewContent();
|
||||||
|
// heading = event.target.mediaTitle;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// document.dispatchEvent(new CustomEvent('showPreviewContentModal', {detail: {heading: heading, content: content}}))
|
||||||
|
// })
|
||||||
}
|
}
|
||||||
|
|
||||||
downloadTargetConnected(target) {
|
downloadTargetConnected(target) {
|
||||||
let downloads = this.element.querySelectorAll('tbody tr');
|
let downloads = this.element.querySelectorAll('tbody tr');
|
||||||
if (downloads.length > 5) {
|
|
||||||
target.classList.add('hidden');
|
console.log(target)
|
||||||
}
|
|
||||||
|
downloads.forEach(download => {
|
||||||
|
console.log(download)
|
||||||
|
download.mediaTitle = download.getAttribute('media-title');
|
||||||
|
download.addEventListener('click', (event) => {
|
||||||
|
// let previewContentModal = document.querySelector('#previewContentModal');
|
||||||
|
let content, heading = ""
|
||||||
|
if (event.target.tagName !== "TR") {
|
||||||
|
content = event.target.parentElement.previewContent();
|
||||||
|
heading = event.target.parentElement.mediaTitle;
|
||||||
|
} else {
|
||||||
|
content = event.target.previewContent();
|
||||||
|
heading = event.target.mediaTitle;
|
||||||
|
}
|
||||||
|
|
||||||
|
document.dispatchEvent(new CustomEvent('showPreviewContentModal', {detail: {heading: heading, content: content}}))
|
||||||
|
})
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pauseDownload(data) {
|
pauseDownload(data) {
|
||||||
|
|||||||
23
bin/phpunit
Executable file
23
bin/phpunit
Executable file
@@ -0,0 +1,23 @@
|
|||||||
|
#!/usr/bin/env php
|
||||||
|
<?php
|
||||||
|
|
||||||
|
if (!ini_get('date.timezone')) {
|
||||||
|
ini_set('date.timezone', 'UTC');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_file(dirname(__DIR__).'/vendor/phpunit/phpunit/phpunit')) {
|
||||||
|
if (PHP_VERSION_ID >= 80000) {
|
||||||
|
require dirname(__DIR__).'/vendor/phpunit/phpunit/phpunit';
|
||||||
|
} else {
|
||||||
|
define('PHPUNIT_COMPOSER_INSTALL', dirname(__DIR__).'/vendor/autoload.php');
|
||||||
|
require PHPUNIT_COMPOSER_INSTALL;
|
||||||
|
PHPUnit\TextUI\Command::main();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!is_file(dirname(__DIR__).'/vendor/symfony/phpunit-bridge/bin/simple-phpunit.php')) {
|
||||||
|
echo "Unable to find the `simple-phpunit.php` script in `vendor/symfony/phpunit-bridge/bin/`.\n";
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
require dirname(__DIR__).'/vendor/symfony/phpunit-bridge/bin/simple-phpunit.php';
|
||||||
|
}
|
||||||
@@ -118,6 +118,7 @@
|
|||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"phpstan/phpstan": "^2.1",
|
"phpstan/phpstan": "^2.1",
|
||||||
|
"phpunit/phpunit": "^12.3",
|
||||||
"symfony/maker-bundle": "^1.62",
|
"symfony/maker-bundle": "^1.62",
|
||||||
"symfony/stopwatch": "7.3.*",
|
"symfony/stopwatch": "7.3.*",
|
||||||
"symfony/web-profiler-bundle": "7.3.*"
|
"symfony/web-profiler-bundle": "7.3.*"
|
||||||
|
|||||||
1564
composer.lock
generated
1564
composer.lock
generated
File diff suppressed because it is too large
Load Diff
44
phpunit.dist.xml
Normal file
44
phpunit.dist.xml
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
|
<!-- https://phpunit.readthedocs.io/en/latest/configuration.html -->
|
||||||
|
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:noNamespaceSchemaLocation="vendor/phpunit/phpunit/phpunit.xsd"
|
||||||
|
colors="true"
|
||||||
|
failOnDeprecation="true"
|
||||||
|
failOnNotice="true"
|
||||||
|
failOnWarning="true"
|
||||||
|
bootstrap="tests/bootstrap.php"
|
||||||
|
cacheDirectory=".phpunit.cache"
|
||||||
|
>
|
||||||
|
<php>
|
||||||
|
<ini name="display_errors" value="1" />
|
||||||
|
<ini name="error_reporting" value="-1" />
|
||||||
|
<server name="APP_ENV" value="test" force="true" />
|
||||||
|
<server name="SHELL_VERBOSITY" value="-1" />
|
||||||
|
</php>
|
||||||
|
|
||||||
|
<testsuites>
|
||||||
|
<testsuite name="Project Test Suite">
|
||||||
|
<directory>tests</directory>
|
||||||
|
</testsuite>
|
||||||
|
</testsuites>
|
||||||
|
|
||||||
|
<source ignoreSuppressionOfDeprecations="true"
|
||||||
|
ignoreIndirectDeprecations="true"
|
||||||
|
restrictNotices="true"
|
||||||
|
restrictWarnings="true"
|
||||||
|
>
|
||||||
|
<include>
|
||||||
|
<directory>src</directory>
|
||||||
|
</include>
|
||||||
|
|
||||||
|
<deprecationTrigger>
|
||||||
|
<method>Doctrine\Deprecations\Deprecation::trigger</method>
|
||||||
|
<method>Doctrine\Deprecations\Deprecation::delegateTriggerToBackend</method>
|
||||||
|
<function>trigger_deprecation</function>
|
||||||
|
</deprecationTrigger>
|
||||||
|
</source>
|
||||||
|
|
||||||
|
<extensions>
|
||||||
|
</extensions>
|
||||||
|
</phpunit>
|
||||||
@@ -47,6 +47,7 @@ readonly class MonitorTvShowHandler implements HandlerInterface
|
|||||||
&& null !== $episode->season
|
&& null !== $episode->season
|
||||||
)
|
)
|
||||||
;
|
;
|
||||||
|
|
||||||
$this->logger->info('> [MonitorTvShowHandler] Found ' . count($downloadedEpisodes) . ' downloaded episodes for title: ' . $monitor->getTitle());
|
$this->logger->info('> [MonitorTvShowHandler] Found ' . count($downloadedEpisodes) . ' downloaded episodes for title: ' . $monitor->getTitle());
|
||||||
|
|
||||||
// Compare against list from TMDB
|
// Compare against list from TMDB
|
||||||
|
|||||||
15
symfony.lock
15
symfony.lock
@@ -86,6 +86,21 @@
|
|||||||
"phpstan.dist.neon"
|
"phpstan.dist.neon"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"phpunit/phpunit": {
|
||||||
|
"version": "12.3",
|
||||||
|
"recipe": {
|
||||||
|
"repo": "github.com/symfony/recipes",
|
||||||
|
"branch": "main",
|
||||||
|
"version": "11.1",
|
||||||
|
"ref": "c6658a60fc9d594805370eacdf542c3d6b5c0869"
|
||||||
|
},
|
||||||
|
"files": [
|
||||||
|
".env.test",
|
||||||
|
"phpunit.dist.xml",
|
||||||
|
"tests/bootstrap.php",
|
||||||
|
"bin/phpunit"
|
||||||
|
]
|
||||||
|
},
|
||||||
"spomky-labs/pwa-bundle": {
|
"spomky-labs/pwa-bundle": {
|
||||||
"version": "1.2.5"
|
"version": "1.2.5"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -31,7 +31,7 @@
|
|||||||
</th>
|
</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody id="{{ table_body_id }}" class="divide-y divide-gray-200 dark:divide-gray-50">
|
<tbody id="{{ table_body_id }}" class="divide-y divide-gray-200 dark:divide-gray-50" data-download-list-target="download">
|
||||||
{% if this.downloads.items|length > 0 %}
|
{% if this.downloads.items|length > 0 %}
|
||||||
{% for download in this.downloads.items %}
|
{% for download in this.downloads.items %}
|
||||||
<twig:DownloadListRow download="{{ download }}" isWidget="{{ isWidget }}" />
|
<twig:DownloadListRow download="{{ download }}" isWidget="{{ isWidget }}" />
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
<tr{{ attributes }} class="hover:bg-gray-200" id="ad_download_{{ download.id }}">
|
<tr{{ attributes }} is="download-list-row" class="hover:bg-gray-200" id="ad_download_{{ download.id }}" data-title="{{ download.title }}"
|
||||||
|
download-id="{{ download.id }}" imdb-id="{{ download.imdbId }}" media-title="{{ download.title }}" url="{{ download.url }}" filename="{{ download.filename }}" status="{{ download.status }}" progress="{{ download.progress }}" media-type="{{ download.mediaType }}" episode-id="{{ download.episodeId }}" created-at="{{ download.createdAt|date('m/d/Y g:i a') }}" updated-at="{{ download.updatedAt|date }}"
|
||||||
|
data-filename="{{ download.filename }}" data-media-type="{{ download.mediaType }}" data-status="{{ download.status }}" data-progress="{{ download.progress }}"
|
||||||
|
>
|
||||||
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-800 dark:text-stone-800 truncate">
|
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-800 dark:text-stone-800 truncate">
|
||||||
{% if download.mediaType == "movies" %}
|
{% if download.mediaType == "movies" %}
|
||||||
{% set routeParams = {imdbId: download.imdbId, mediaType: download.mediaType} %}
|
{% set routeParams = {imdbId: download.imdbId, mediaType: download.mediaType} %}
|
||||||
|
|||||||
10
templates/components/PreviewModal.html.twig
Normal file
10
templates/components/PreviewModal.html.twig
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<dialog{{ attributes }} is="preview-content-dialog" class="py-3 px-4 w-full md:w-[50rem] rounded-md">
|
||||||
|
<div class="flex flex-row justify-end">
|
||||||
|
<twig:ux:icon name="ic:twotone-cancel" width="16.75px" height="16.75px" class="modal-close rounded-full align-middle text-red-600 hover:text-red-700" />
|
||||||
|
</div>
|
||||||
|
<h2 class="modal-heading mb-4 text-2xl font-bold">{{ heading|default('') }}</h2>
|
||||||
|
|
||||||
|
<div class="modal-content mb-4">
|
||||||
|
{% block content %}{% endblock %}
|
||||||
|
</div>
|
||||||
|
</dialog>
|
||||||
@@ -15,4 +15,6 @@
|
|||||||
<twig:DownloadList type="complete" :isWidget="false" :perPage="10"></twig:DownloadList>
|
<twig:DownloadList type="complete" :isWidget="false" :perPage="10"></twig:DownloadList>
|
||||||
</twig:Card>
|
</twig:Card>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<twig:PreviewModal id="previewModal" />
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
13
tests/Download/DownloadOptionEvaluatorTest.php
Normal file
13
tests/Download/DownloadOptionEvaluatorTest.php
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Tests\Download;
|
||||||
|
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
|
class DownloadOptionEvaluatorTest extends TestCase
|
||||||
|
{
|
||||||
|
public function testEpisodeExists(): void
|
||||||
|
{
|
||||||
|
$this->assertTrue(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
178
tests/Monitor/MonitorTvShowHandlerTest.php
Normal file
178
tests/Monitor/MonitorTvShowHandlerTest.php
Normal file
@@ -0,0 +1,178 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Tests\Monitor;
|
||||||
|
|
||||||
|
use App\Base\Service\MediaFiles;
|
||||||
|
use App\Monitor\Action\Command\MonitorTvShowCommand;
|
||||||
|
use App\Monitor\Action\Handler\MonitorTvEpisodeHandler;
|
||||||
|
use App\Monitor\Action\Handler\MonitorTvShowHandler;
|
||||||
|
use App\Monitor\Action\Result\MonitorTvShowResult;
|
||||||
|
use App\Monitor\Framework\Entity\Monitor;
|
||||||
|
use App\Monitor\Framework\Repository\MonitorRepository;
|
||||||
|
use App\Tmdb\Tmdb;
|
||||||
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
use Psr\Log\LoggerInterface;
|
||||||
|
|
||||||
|
class MonitorTvShowHandlerTest extends TestCase
|
||||||
|
{
|
||||||
|
private MonitorTvShowHandler $handler;
|
||||||
|
private MonitorRepository $monitorRepository;
|
||||||
|
private EntityManagerInterface $entityManager;
|
||||||
|
private MonitorTvEpisodeHandler $episodeHandler;
|
||||||
|
private MediaFiles $mediaFiles;
|
||||||
|
private LoggerInterface $logger;
|
||||||
|
private Tmdb $tmdb;
|
||||||
|
|
||||||
|
protected function setUp(): void
|
||||||
|
{
|
||||||
|
$this->monitorRepository = $this->createMock(MonitorRepository::class);
|
||||||
|
$this->entityManager = $this->createMock(EntityManagerInterface::class);
|
||||||
|
$this->episodeHandler = $this->createMock(MonitorTvEpisodeHandler::class);
|
||||||
|
$this->mediaFiles = $this->createMock(MediaFiles::class);
|
||||||
|
$this->logger = $this->createMock(LoggerInterface::class);
|
||||||
|
$this->tmdb = $this->createMock(Tmdb::class);
|
||||||
|
|
||||||
|
$this->handler = new MonitorTvShowHandler(
|
||||||
|
$this->monitorRepository,
|
||||||
|
$this->entityManager,
|
||||||
|
$this->episodeHandler,
|
||||||
|
$this->mediaFiles,
|
||||||
|
$this->logger,
|
||||||
|
$this->tmdb
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testEpisodeExists(): void
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
$monitor = $this->createMock(Monitor::class);
|
||||||
|
$monitor->method('getId')->willReturn(1);
|
||||||
|
$monitor->method('getTmdbId')->willReturn('63770');
|
||||||
|
$monitor->method('getSeason')->willReturn(10);
|
||||||
|
$monitor->method('getTitle')->willReturn('The Late Show with Stephen Colbert');
|
||||||
|
|
||||||
|
$this->monitorRepository->expects($this->once())
|
||||||
|
->method('find')
|
||||||
|
->with(1)
|
||||||
|
->willReturn($monitor);
|
||||||
|
|
||||||
|
$this->tmdb->expects($this->once())
|
||||||
|
->method('seasonDetails')
|
||||||
|
->with(63770, 10)
|
||||||
|
->willReturn((object)['episodes' => [$this->getTmdbEpisode()]]);
|
||||||
|
|
||||||
|
$this->mediaFiles->expects($this->once())
|
||||||
|
->method('findEpisodes')
|
||||||
|
->willReturn($this->getDownloadedEpisodes());
|
||||||
|
|
||||||
|
// Act
|
||||||
|
$command = new MonitorTvShowCommand(1);
|
||||||
|
$result = $this->handler->handle($command);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
$this->assertInstanceOf(MonitorTvShowResult::class, $result);
|
||||||
|
$this->assertEquals('OK', $result->status);
|
||||||
|
$this->assertTrue(isset($result->result['monitor']));
|
||||||
|
$this->assertSame($monitor, $result->result['monitor']);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testEpisodeDoesNotExist(): void
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
$monitor = $this->createMock(Monitor::class);
|
||||||
|
$monitor->method('getId')->willReturn(1);
|
||||||
|
$monitor->method('getTmdbId')->willReturn(63770);
|
||||||
|
$monitor->method('getSeason')->willReturn(10);
|
||||||
|
$monitor->method('getTitle')->willReturn('The Late Show with Stephen Colbert');
|
||||||
|
|
||||||
|
$this->monitorRepository->expects($this->once())
|
||||||
|
->method('find')
|
||||||
|
->with(1)
|
||||||
|
->willReturn($monitor);
|
||||||
|
|
||||||
|
$this->tmdb->expects($this->once())
|
||||||
|
->method('seasonDetails')
|
||||||
|
->with(63770, 10)
|
||||||
|
->willReturn((object)['episodes' => []]);
|
||||||
|
|
||||||
|
$this->mediaFiles->expects($this->once())
|
||||||
|
->method('findEpisodes')
|
||||||
|
->willReturn([]);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
$command = new MonitorTvShowCommand(1);
|
||||||
|
$result = $this->handler->handle($command);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
$this->assertInstanceOf(MonitorTvShowResult::class, $result);
|
||||||
|
$this->assertEquals('OK', $result->status);
|
||||||
|
$this->assertTrue(isset($result->result['monitor']));
|
||||||
|
$this->assertSame($monitor, $result->result['monitor']);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testHandleWithInvalidMonitorId(): void
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
$this->monitorRepository->expects($this->once())
|
||||||
|
->method('find')
|
||||||
|
->with(999)
|
||||||
|
->willReturn(null);
|
||||||
|
|
||||||
|
// Act & Assert
|
||||||
|
$this->expectException(\RuntimeException::class);
|
||||||
|
$this->expectExceptionMessage('Monitor not found');
|
||||||
|
|
||||||
|
$command = new MonitorTvShowCommand(999);
|
||||||
|
$this->handler->handle($command);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getTmdbEpisode(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
"air_date" => "2016-05-13",
|
||||||
|
"episode_number" => 142,
|
||||||
|
"episode_type" => "standard",
|
||||||
|
"id" => 1192242,
|
||||||
|
"name" => "Matt Bomer, Zach Woods, Nick Griffin",
|
||||||
|
"overview" => "",
|
||||||
|
"production_code" => "",
|
||||||
|
"runtime" => 45,
|
||||||
|
"season_number" => 1,
|
||||||
|
"show_id" => 63770,
|
||||||
|
"still_path" => null,
|
||||||
|
"vote_average" => 0.0,
|
||||||
|
"vote_count" => 0,
|
||||||
|
"crew" => [],
|
||||||
|
"guest_stars" => [],
|
||||||
|
"poster" => null,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getDownloadedEpisodes(): array
|
||||||
|
{
|
||||||
|
$episode1 = (object)[
|
||||||
|
"season" => 10,
|
||||||
|
"episode" => 142,
|
||||||
|
"resolution" => "1080p",
|
||||||
|
"codec" => "h264",
|
||||||
|
"group" => "jebaited.mkv",
|
||||||
|
"container" => "mkv",
|
||||||
|
"title" => "42 stephen colbert 2025 07 21 sandra oh",
|
||||||
|
"episodeName" => "E1 web",
|
||||||
|
];
|
||||||
|
|
||||||
|
$episode2 = (object)[
|
||||||
|
'season' => 10,
|
||||||
|
'episode' => 142,
|
||||||
|
'resolution' => '1080p',
|
||||||
|
'codec' => 'x265',
|
||||||
|
'group' => 'MeGusta[EZTVx.to].mkv',
|
||||||
|
'container' => 'mkv',
|
||||||
|
'title' => '42 Stephen Colbert 2025 07 21 Sandra Oh',
|
||||||
|
'episodeName' => 'E1 HEVC'
|
||||||
|
];
|
||||||
|
|
||||||
|
return [$episode1, $episode2];
|
||||||
|
}
|
||||||
|
}
|
||||||
13
tests/bootstrap.php
Normal file
13
tests/bootstrap.php
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Symfony\Component\Dotenv\Dotenv;
|
||||||
|
|
||||||
|
require dirname(__DIR__).'/vendor/autoload.php';
|
||||||
|
|
||||||
|
if (method_exists(Dotenv::class, 'bootEnv')) {
|
||||||
|
(new Dotenv())->bootEnv(dirname(__DIR__).'/.env');
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($_SERVER['APP_DEBUG']) {
|
||||||
|
umask(0000);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user