diff --git a/.github/workflows/composer/action.yml b/.github/workflows/composer/action.yml index d6a1be73..3c323b50 100644 --- a/.github/workflows/composer/action.yml +++ b/.github/workflows/composer/action.yml @@ -7,7 +7,7 @@ inputs: runs: using: "composite" steps: - - uses: actions/cache@v3 + - uses: actions/cache@v4 with: path: /tmp/composer-cache key: ${{ runner.os }}-${{ hashFiles('**/composer.json') }} diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index a889f7dc..79a91d83 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -1,6 +1,11 @@ --- +name: Testing and linting + on: push: + branches: + - main + - develop pull_request: workflow_dispatch: schedule: @@ -9,35 +14,34 @@ on: env: TYPO3_EXTENSION_KEY: html5mediakit MAIN_PHP_VERSION: 8.3 + CI: true jobs: "composer-validate": name: "Composer validate" runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: ./.github/workflows/composer with: php_version: "${{ env.MAIN_PHP_VERSION }}" - - run: | - bash .Build/bin/t3_run_tests.sh -s composerValidate -p ${{ env.MAIN_PHP_VERSION }} + - run: composer validate "composer-normalize": name: "Composer normalize" runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: ./.github/workflows/composer with: php_version: "${{ env.MAIN_PHP_VERSION }}" - - run: | - bash .Build/bin/t3_run_tests.sh -s composerNormalize -n -p ${{ env.MAIN_PHP_VERSION }} + - run: composer normalize --dry-run --diff "check-codestyle-codesniffer": name: "PHP_CodeSniffer check" runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: ./.github/workflows/composer with: php_version: "${{ env.MAIN_PHP_VERSION }}" @@ -45,95 +49,117 @@ jobs: with: php-version: "${{ env.MAIN_PHP_VERSION }}" - run: | - bash .Build/bin/t3_check_codestyle.sh PerCodeStyleT3Ext + bash bin/t3_check_codestyle.sh PerCodeStyleT3Ext "check-codestyle-php-cs-fixer": name: "PHP CS Fixer check" runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: ./.github/workflows/composer with: php_version: "${{ env.MAIN_PHP_VERSION }}" - run: | - bash .Build/bin/t3_run_tests.sh -s cgl -n -p ${{ env.MAIN_PHP_VERSION }} + bash bin/t3_run_tests.sh -s cgl -n -p ${{ env.MAIN_PHP_VERSION }} "php-unit-tests": name: "PHP Unit tests" strategy: matrix: - php_version: ["8.1", "8.3"] + php_version: ["8.2", "8.3"] runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: ./.github/workflows/composer with: php_version: ${{ matrix.php_version }} - run: | - bash .Build/bin/t3_run_tests.sh -s unit -p ${{ matrix.php_version }} + bash bin/t3_run_tests.sh -s unit -p ${{ matrix.php_version }} - "php-functional-tests-sqlite-main": + "php-functional-tests-sqlite": name: "Functional tests on SQLite" runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: ./.github/workflows/composer with: php_version: "${{ env.MAIN_PHP_VERSION }}" - run: | - bash .Build/bin/t3_run_tests.sh -s functional -d sqlite -p ${{ env.MAIN_PHP_VERSION }} + bash bin/t3_run_tests.sh -s functional -d sqlite -p ${{ env.MAIN_PHP_VERSION }} "php-functional-tests-mariadb-main": name: "Functional tests on MariaDB" runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: ./.github/workflows/composer with: php_version: ${{ env.MAIN_PHP_VERSION }} - run: | - bash .Build/bin/t3_run_tests.sh -s functional -d mariadb -p ${{ env.MAIN_PHP_VERSION }} + bash bin/t3_run_tests.sh -s functional -d mariadb -p ${{ env.MAIN_PHP_VERSION }} "php-acceptance-tests-mariadb": name: "Acceptance tests on MariaDB" runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: ./.github/workflows/composer with: php_version: ${{ env.MAIN_PHP_VERSION }} + - uses: shivammathur/setup-php@v2 + with: + php-version: "${{ env.MAIN_PHP_VERSION }}" + extensions: intl zip - run: | - bash .Build/bin/t3_run_tests.sh -s acceptance -d mariadb -t Backend -p ${{ env.MAIN_PHP_VERSION }} + rm -f .Build/Web/typo3/sysext/* .Build/Web/typo3conf/ext/* + composer run post-autoload-dump + bash bin/t3_run_tests.sh -s acceptance -d mariadb -p ${{ env.MAIN_PHP_VERSION }} -b docker "php-lint": name: "PHP linting" strategy: matrix: - php_version: ["8.1", "8.3"] + php_version: ["8.2", "8.3"] runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: ./.github/workflows/composer with: php_version: ${{ matrix.php_version }} - run: | - bash .Build/bin/t3_run_tests.sh -s lintPhp -p ${{ matrix.php_version }} + bash bin/t3_run_tests.sh -s lintPhp -p ${{ matrix.php_version }} + + "phpstan": + name: "PHPStan" + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: ./.github/workflows/composer + with: + php_version: "${{ env.MAIN_PHP_VERSION }}" + - uses: shivammathur/setup-php@v2 + with: + php-version: "${{ env.MAIN_PHP_VERSION }}" + extensions: intl zip + - run: | + php -dxdebug.mode=off bin/codecept build -c Tests/codeception.yml + php -dxdebug.mode=off bin/phpstan analyse -c Build/phpstan/phpstan.ci.neon --no-progress --no-interaction --memory-limit 4G --debug "coverage-phpunit": name: "Test coverage by Unit Tests" runs-on: ubuntu-latest if: github.ref == 'refs/heads/develop' steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: ./.github/workflows/composer with: php_version: ${{ env.MAIN_PHP_VERSION }} - run: | - bash .Build/bin/t3_run_tests.sh -s unit -p ${{ env.MAIN_PHP_VERSION }} -x -z coverage -e "--coverage-clover Logs/clover-unit.xml --coverage-filter ../Classes" + bash bin/t3_run_tests.sh -s unit -p ${{ env.MAIN_PHP_VERSION }} -X coverage -- --coverage-clover Logs/clover-unit.xml --coverage-filter ../Classes - uses: actions/upload-artifact@v3 with: name: coverage-phpunit - path: .Build/Logs/clover-unit.xml + path: Logs/clover-unit.xml retention-days: 1 "coverage-functional": @@ -141,16 +167,16 @@ jobs: runs-on: ubuntu-latest if: github.ref == 'refs/heads/develop' steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: ./.github/workflows/composer with: php_version: ${{ env.MAIN_PHP_VERSION }} - run: | - bash .Build/bin/t3_run_tests.sh -s functional -d mariadb -p ${{ env.MAIN_PHP_VERSION }} -x -z coverage -e "--coverage-clover Logs/clover-functional.xml --coverage-filter ../Classes" + bash bin/t3_run_tests.sh -s functional -d mariadb -p ${{ env.MAIN_PHP_VERSION }} -X coverage -- --coverage-clover Logs/clover-functional.xml --coverage-filter ../Classes - uses: actions/upload-artifact@v3 with: name: coverage-functional - path: .Build/Logs/clover-functional.xml + path: Logs/clover-functional.xml retention-days: 1 "coverage-upload": @@ -163,7 +189,7 @@ jobs: env: CC_TEST_REPORTER_ID: ${{ secrets.CC_TEST_REPORTER_ID }} steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: actions/download-artifact@v3 with: name: coverage-phpunit @@ -186,11 +212,11 @@ jobs: name: "Scan for deprecated and breaking code using typo3scan" runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: ./.github/workflows/composer with: php_version: "${{ env.MAIN_PHP_VERSION }}" - uses: shivammathur/setup-php@v2 with: php-version: "${{ env.MAIN_PHP_VERSION }}" - - run: php .Build/bin/typo3scan scan --target 12 . + - run: php bin/typo3scan scan . diff --git a/.gitignore b/.gitignore index 577a1dba..ed16a163 100644 --- a/.gitignore +++ b/.gitignore @@ -3,5 +3,6 @@ /.cache /.php-cs-fixer.cache /.phpunit.result.cache +/bin /composer.lock /var diff --git a/Build/cleanup_for_ter.sh b/Build/cleanup_for_ter.sh index ce2f4418..515bf21b 100644 --- a/Build/cleanup_for_ter.sh +++ b/Build/cleanup_for_ter.sh @@ -19,6 +19,7 @@ rm -Rf Build rm -Rf Tests rm -Rf typo3temp rm -f .codeclimate.yml +rm -f .crowdin.yml rm -f .editorconfig rm -f .gitignore rm ready_for_release.txt diff --git a/Build/phpstan/phpstan.ci.neon b/Build/phpstan/phpstan.ci.neon new file mode 100644 index 00000000..31088e91 --- /dev/null +++ b/Build/phpstan/phpstan.ci.neon @@ -0,0 +1,12 @@ +includes: + - phpstan.neon + +parameters: + # CI needs to calculate phpstan a-new each time anyways. No point in caching this. + # We write this to /tmp within container which is not cached by CI. + tmpDir: /tmp + + parallel: + # @todo Process timeout raised as a intermediate solution. This should be further investigated if we can mitigate + # the occurring subprocess timeout issue through other configurations and minimize the execution time again. + processTimeout: 900.0 diff --git a/Build/phpstan/phpstan.local.neon b/Build/phpstan/phpstan.local.neon new file mode 100644 index 00000000..d89a1f1d --- /dev/null +++ b/Build/phpstan/phpstan.local.neon @@ -0,0 +1,2 @@ +includes: + - phpstan.neon diff --git a/Build/phpstan/phpstan.neon b/Build/phpstan/phpstan.neon new file mode 100644 index 00000000..73a462ec --- /dev/null +++ b/Build/phpstan/phpstan.neon @@ -0,0 +1,17 @@ +includes: + - ../../.Build/vendor/bnf/phpstan-psr-container/extension.neon + - ../../.Build/vendor/friendsoftypo3/phpstan-typo3/extension.neon + - ../../.Build/vendor/phpstan/phpstan-phpunit/extension.neon + +parameters: + # Use local .cache dir instead of /tmp + tmpDir: ../../.cache/phpstan + + level: 5 + + paths: + - ../../Classes + - ../../Tests + + excludePaths: + - ../../Tests/Acceptance/Support diff --git a/Classes/Controller/MediaController.php b/Classes/Controller/MediaController.php index 746b5c68..d2685106 100644 --- a/Classes/Controller/MediaController.php +++ b/Classes/Controller/MediaController.php @@ -25,7 +25,6 @@ use TYPO3\CMS\Extbase\Mvc\Controller\ActionController; use TYPO3\CMS\Extbase\Utility\LocalizationUtility; use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer; -use RuntimeException; /** * Controller for rendering media. @@ -54,6 +53,7 @@ public function renderMediaAction(): ResponseInterface $contentObject = $this->getCurrentContentObject(); try { + /** @extensionScannerIgnoreLine */ $uid = $contentObject->data['_LOCALIZED_UID'] ?? $contentObject->data['uid']; $media = $this->mediaRepository->findOneByContentElementUid($uid); } catch (MediaException $mediaException) { @@ -68,6 +68,7 @@ public function renderMediaForRelatedTableAction(): ResponseInterface $contentObject = $this->getCurrentContentObject(); try { + /** @extensionScannerIgnoreLine */ $media = $this->mediaRepository->findOneByParentRecord($contentObject->data); } catch (MediaException $mediaException) { return $this->htmlResponse($this->translate('exception.' . $mediaException->getCode())); @@ -98,15 +99,13 @@ private function renderMedia(Media $media): ResponseInterface $contentObject = $this->getCurrentContentObject(); $contentObject->lastChanged($media->getTstamp()); - if ($mediaType->equals(MediaType::VIDEO)) { + if ($mediaType === MediaType::VIDEO) { return (new ForwardResponse('video'))->withArguments(['video' => $media->getUid()]); } - if ($mediaType->equals(MediaType::AUDIO)) { + if ($mediaType === MediaType::AUDIO) { return (new ForwardResponse('audio'))->withArguments(['audio' => $media->getUid()]); } - - throw new RuntimeException('An invalid media type is used.'); // @codeCoverageIgnore } /** diff --git a/Classes/Domain/Model/Enumeration/MediaType.php b/Classes/Domain/Model/Enumeration/MediaType.php index 2555ee48..9616f664 100644 --- a/Classes/Domain/Model/Enumeration/MediaType.php +++ b/Classes/Domain/Model/Enumeration/MediaType.php @@ -14,26 +14,24 @@ * The TYPO3 project - inspiring people to share! * * */ -use TYPO3\CMS\Core\Type\Enumeration; - /** * Enumeration of allowed media types. * * @codeCoverageIgnore No code to test. */ -class MediaType extends Enumeration +enum MediaType: string { /** * Media type "audio" for MP3 / OGG files. * * @const */ - public const AUDIO = 'audio'; + case AUDIO = 'audio'; /** * Media type "video" for MP4 / OGV files. * * @const */ - public const VIDEO = 'video'; + case VIDEO = 'video'; } diff --git a/Classes/Domain/Repository/MediaRepository.php b/Classes/Domain/Repository/MediaRepository.php index c96a8691..7d8b99f6 100644 --- a/Classes/Domain/Repository/MediaRepository.php +++ b/Classes/Domain/Repository/MediaRepository.php @@ -16,6 +16,7 @@ use Sto\Html5mediakit\Domain\Model\Media; use Sto\Html5mediakit\Exception\MediaMissingException; +use TYPO3\CMS\Core\Context\LanguageAspect; use TYPO3\CMS\Extbase\Persistence\QueryInterface; use TYPO3\CMS\Extbase\Persistence\Repository; use InvalidArgumentException; @@ -36,7 +37,14 @@ public function findOneByContentElementUid(int $contentElementUid): Media // We do not want to do any language overlay in our query because the content element UID // is already the UID of the translated content element. - $query->getQuerySettings()->setLanguageOverlayMode(false); + $languageAspect = $query->getQuerySettings()->getLanguageAspect(); + $languageAspect = new LanguageAspect( + $languageAspect->getId(), + $languageAspect->getContentId(), + LanguageAspect::OVERLAYS_OFF, + $languageAspect->getFallbackChain(), + ); + $query->getQuerySettings()->setLanguageAspect($languageAspect); $query->matching($query->equals('contentElement', $contentElementUid)); diff --git a/Configuration/TCA/tx_html5mediakit_domain_model_media.php b/Configuration/TCA/tx_html5mediakit_domain_model_media.php index 02668ecb..bfbf4ee6 100644 --- a/Configuration/TCA/tx_html5mediakit_domain_model_media.php +++ b/Configuration/TCA/tx_html5mediakit_domain_model_media.php @@ -3,7 +3,7 @@ declare(strict_types=1); use Sto\Html5mediakit\Domain\Model\Enumeration\MediaType; -use TYPO3\CMS\Core\Resource\AbstractFile; +use TYPO3\CMS\Core\Resource\FileType; $languagePrefix = 'LLL:EXT:html5mediakit/Resources/Private/Language/locallang_db.xlf:'; $languagePrefixColumn = $languagePrefix . 'tx_html5mediakit_domain_model_media.'; @@ -46,12 +46,12 @@ ], 'overrideChildTca' => [ 'types' => [ - AbstractFile::FILETYPE_APPLICATION => ['showitem' => $showitem], - AbstractFile::FILETYPE_AUDIO => ['showitem' => $showitem], - AbstractFile::FILETYPE_IMAGE => ['showitem' => $showitem], - AbstractFile::FILETYPE_TEXT => ['showitem' => $showitem], - AbstractFile::FILETYPE_UNKNOWN => ['showitem' => $showitem], - AbstractFile::FILETYPE_VIDEO => ['showitem' => $showitem], + FileType::APPLICATION->value => ['showitem' => $showitem], + FileType::AUDIO->value => ['showitem' => $showitem], + FileType::IMAGE->value => ['showitem' => $showitem], + FileType::TEXT->value => ['showitem' => $showitem], + FileType::UNKNOWN->value => ['showitem' => $showitem], + FileType::VIDEO->value => ['showitem' => $showitem], ], ], 'security' => ['ignorePageTypeRestriction' => true], @@ -75,8 +75,8 @@ 'typeicon_column' => 'type', 'typeicon_classes' => [ 'default' => 'mimetypes-media-video', - MediaType::VIDEO => 'mimetypes-media-video', - MediaType::AUDIO => 'mimetypes-media-audio', + MediaType::VIDEO->value => 'mimetypes-media-video', + MediaType::AUDIO->value => 'mimetypes-media-audio', ], 'hideTable' => true, 'languageField' => 'sys_language_uid', @@ -94,14 +94,14 @@ 'items' => [ [ 'label' => $languagePrefixColumn . 'type.I.video', - 'value' => MediaType::VIDEO, + 'value' => MediaType::VIDEO->value, ], [ 'label' => $languagePrefixColumn . 'type.I.audio', - 'value' => MediaType::AUDIO, + 'value' => MediaType::AUDIO->value, ], ], - 'default' => MediaType::VIDEO, + 'default' => MediaType::VIDEO->value, 'size' => 1, 'maxitems' => 1, ], diff --git a/Configuration/TSconfig/Page/RTE.rte_htmlarea.pagets b/Configuration/TSconfig/Page/RTE.rte_htmlarea.pagets deleted file mode 100644 index 8c66630b..00000000 --- a/Configuration/TSconfig/Page/RTE.rte_htmlarea.pagets +++ /dev/null @@ -1,28 +0,0 @@ -// configure a minimal RTE for the description field that -// only allows basic formatting and links -RTE.config.tx_html5mediakit_domain_model_media.description { - showButtons = bold,italic,link,chMode,removeformat,undo,redo - contextMenu.showButtons = insertparagraphbefore,insertparagraphafter - - proc { - allowTags = i,em,b,strong,br,p,a - denyTags = u,img,div,center,pre,font,hr,sub,sup,li,ul,ol,blockquote,strike,span - dontProtectUnknownTags_rte = 1 - preserveDIVSections = 0 - - entryHTMLparser_db = 1 - entryHTMLparser_db { - - tags { - i.allowedAttribs = 0 - em.allowedAttribs = 0 - b.allowedAttribs = 0 - strong.allowedAttribs = 0 - br.allowedAttribs = 0 - p.allowedAttribs = 0 - } - - keepNonMatchedTags = 0 - } - } -} diff --git a/Configuration/TSconfig/Page/mod.wizards.newContentElement.pagets b/Configuration/page.tsconfig similarity index 100% rename from Configuration/TSconfig/Page/mod.wizards.newContentElement.pagets rename to Configuration/page.tsconfig diff --git a/Tests/Acceptance/Backend.suite.yml b/Tests/Acceptance/Application.suite.yml similarity index 89% rename from Tests/Acceptance/Backend.suite.yml rename to Tests/Acceptance/Application.suite.yml index 985235a0..25b1017f 100644 --- a/Tests/Acceptance/Backend.suite.yml +++ b/Tests/Acceptance/Application.suite.yml @@ -2,7 +2,7 @@ actor: BackendTester modules: enabled: - WebDriver: - url: '%typo3TestingAcceptanceBaseUrl%/typo3temp/var/tests/acceptance' + url: '%typo3TestingAcceptanceBaseUrl%' browser: chrome wait: 1 host: chrome diff --git a/Tests/Acceptance/Backend/ContentCreationCest.php b/Tests/Acceptance/Application/ContentCreationCest.php similarity index 90% rename from Tests/Acceptance/Backend/ContentCreationCest.php rename to Tests/Acceptance/Application/ContentCreationCest.php index 824a0cdc..4f040c70 100644 --- a/Tests/Acceptance/Backend/ContentCreationCest.php +++ b/Tests/Acceptance/Application/ContentCreationCest.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Sto\Html5mediakit\Tests\Acceptance\Backend; +namespace Sto\Html5mediakit\Tests\Acceptance\Application; use Sto\Html5mediakit\Tests\Acceptance\Support\BackendTester; use Sto\Html5mediakit\Tests\Acceptance\Support\Helper\ModalDialog; @@ -27,8 +27,8 @@ public function html5MediaCanBeCreated(BackendTester $I, PageTree $pageTree, Mod $modalDialog->canSeeDialog(); $I->executeJS( - 'document.querySelector(\'typo3-backend-new-content-element-wizard\').shadowRoot' - . '.querySelector(\'button[data-identifier="common_html5mediakit_mediarenderer"]\').click()', + 'document.querySelector(\'typo3-backend-new-record-wizard\').shadowRoot' + . '.querySelector(\'button[data-identifier="default_html5mediakit_mediarenderer"]\').click()', ); $I->switchToContentFrame(); diff --git a/Tests/Acceptance/Support/Extension/BackendHtml5mediakitEnvironment.php b/Tests/Acceptance/Support/Extension/BackendHtml5mediakitEnvironment.php index ffe00268..4266f4c5 100644 --- a/Tests/Acceptance/Support/Extension/BackendHtml5mediakitEnvironment.php +++ b/Tests/Acceptance/Support/Extension/BackendHtml5mediakitEnvironment.php @@ -5,8 +5,8 @@ namespace Sto\Html5mediakit\Tests\Acceptance\Support\Extension; use Codeception\Event\SuiteEvent; -use TYPO3\TestingFramework\Core\Acceptance\Extension\BackendEnvironment; use RuntimeException; +use TYPO3\TestingFramework\Core\Acceptance\Extension\BackendEnvironment; class BackendHtml5mediakitEnvironment extends BackendEnvironment { @@ -40,15 +40,21 @@ public function bootstrapTypo3Environment(SuiteEvent $suiteEvent): void throw new RuntimeException('TYPO3_PATH_ROOT environment variable is not set'); } + $sysextDir = rtrim(ORIGINAL_ROOT, '/') . '/typo3/sysext'; + $rootHtaccess = $sysextDir . '/install/Resources/Private/FolderStructureTemplateFiles/root-htaccess'; + + if (!file_exists($rootHtaccess)) { + throw new RuntimeException('File not found: ' . $rootHtaccess); + } + + copy($rootHtaccess, $typo3RootPath . '/.htaccess'); + $putenvCode = PHP_EOL . 'putenv(\'TYPO3_PATH_ROOT=' . $typo3RootPath . '\');' . PHP_EOL . 'putenv(\'TYPO3_PATH_APP=' . $typo3RootPath . '\');' . PHP_EOL . PHP_EOL; - $indexFiles = [ - 'index.php', - 'typo3/index.php', - ]; + $indexFiles = ['index.php']; foreach ($indexFiles as $indexFile) { $indexPath = $typo3RootPath . '/' . $indexFile; diff --git a/Tests/Acceptance/Support/Helper/AbstractTree.php b/Tests/Acceptance/Support/Helper/AbstractTree.php new file mode 100644 index 00000000..f7c9eab9 --- /dev/null +++ b/Tests/Acceptance/Support/Helper/AbstractTree.php @@ -0,0 +1,149 @@ + [role="treeitem"]'; + + // Selectors + public static $treeSelector = ''; + + /** + * @var AcceptanceTester + */ + protected $tester; + + /** + * Check if the pagetree is visible end return the web element object. + * + * @return RemoteWebElement + */ + public function getPageTreeElement() + { + $I = $this->tester; + $I->switchToIFrame(); + return $I->executeInSelenium(static function (RemoteWebDriver $webdriver) { + return $webdriver->findElement(WebDriverBy::cssSelector(static::$treeSelector)); + }); + } + + /** + * Open the given hierarchical path in the pagetree and click the last page. + * + * Example to open "styleguide -> elements basic" page: + * [ + * 'styleguide TCA demo', + * 'elements basic', + * ] + * + * @param string[] $path + */ + public function openPath(array $path): void + { + $context = $this->getPageTreeElement(); + + $this->waitForNodes(); + + // Collapse all opened paths (might be opened due to localstorage) + do { + $toggled = false; + + try { + // Collapse last opened node element, that is not the root (=first node) + $context->findElement( + WebDriverBy::xpath( + '(.//*[position()>1 and @role="treeitem" and' + . ' */typo3-backend-icon/@identifier="actions-chevron-down"])[last()]/*[@class="node-toggle"]', + ), + )->click(); + $toggled = true; + } catch (NoSuchElementException $e) { + // Element not found so it may be already opened... + } catch (ElementNotVisibleException $e) { + // Element not found so it may be already opened... + } catch (ElementNotInteractableException $e) { + // Another possible exception if the chevron isn't there ... depends on facebook driver version + } + } while ($toggled); + + foreach ($path as $pageName) { + $context = $this->ensureTreeNodeIsOpen($pageName, $context); + } + $context->findElement(WebDriverBy::cssSelector(static::$treeItemAnchorSelector))->click(); + } + + /** + * Waits until tree nodes are rendered. + */ + public function waitForNodes(): void + { + $this->tester->waitForElement(static::$treeSelector . ' ' . static::$treeItemSelector, 5); + } + + /** + * Search for an element with the given link text in the provided context. + * + * @return RemoteWebElement + */ + protected function ensureTreeNodeIsOpen(string $nodeText, RemoteWebElement $context) + { + $I = $this->tester; + $I->wait(0.1); + $I->see($nodeText, static::$treeItemSelector); + + /** @var RemoteWebElement $context */ + $context = $I->executeInSelenium(static function () use ( + $nodeText, + $context + ) { + return $context->findElement( + WebDriverBy::xpath('//*[@class=\'node-name\'][text()=\'' . $nodeText . '\']/../../..'), + ); + }); + + try { + $context->findElement( + WebDriverBy::cssSelector( + '.node-toggle > typo3-backend-icon[identifier=\'actions-chevron-right\']', + ), + )->click(); + } catch (NoSuchElementException $e) { + // Element not found so it may be already opened... + } catch (ElementNotVisibleException $e) { + // Element not found so it may be already opened... + } catch (ElementNotInteractableException $e) { + // Another possible exception if the chevron isn't there ... depends on facebook driver version + } + + return $context; + } +} diff --git a/Tests/Acceptance/Support/Helper/PageTree.php b/Tests/Acceptance/Support/Helper/PageTree.php index c79b05d9..51a866b2 100644 --- a/Tests/Acceptance/Support/Helper/PageTree.php +++ b/Tests/Acceptance/Support/Helper/PageTree.php @@ -2,18 +2,40 @@ declare(strict_types=1); +/* + * This file is part of the TYPO3 CMS project. + * + * It is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, either version 2 + * of the License, or any later version. + * + * For the full copyright and license information, please read the + * LICENSE.txt file that was distributed with this source code. + * + * The TYPO3 project - inspiring people to share! + */ + namespace Sto\Html5mediakit\Tests\Acceptance\Support\Helper; use Sto\Html5mediakit\Tests\Acceptance\Support\BackendTester; -use TYPO3\TestingFramework\Core\Acceptance\Helper\AbstractPageTree; -class PageTree extends AbstractPageTree +final class PageTree extends AbstractTree { + public static $treeSelector = '#typo3-pagetree-treeContainer'; + /** - * Inject our core AcceptanceTester actor into ModalDialog. + * Inject our core AcceptanceTester actor into PageTree. */ public function __construct(BackendTester $I) { $this->tester = $I; } + + /** + * Get node identifier of given page. + */ + public function getPageXPathByPageName(string $pageName): string + { + return '//*[@class="node-name" and text()=\'' . $pageName . '\']/..'; + } } diff --git a/Tests/Functional/Controller/MediaController/AudioTest.php b/Tests/Functional/Controller/MediaController/AudioTest.php index d29d6f26..54b394da 100644 --- a/Tests/Functional/Controller/MediaController/AudioTest.php +++ b/Tests/Functional/Controller/MediaController/AudioTest.php @@ -38,7 +38,7 @@ private function assertResponseContainsFallbackLinks(string $responseBody): void { foreach ($this->formats as $extension) { /** @noinspection HtmlUnknownTarget */ - $expectedSource = sprintf('media.%1$s', $extension); + $expectedSource = sprintf('media.%1$s', $extension); self::assertStringContainsString($expectedSource, $responseBody); } } @@ -47,7 +47,7 @@ private function assertResponseContainsSources(string $responseBody): void { foreach ($this->formats as $mimeType => $extension) { /** @noinspection HtmlUnknownTarget */ - $expectedSource = sprintf('', $extension, $mimeType); + $expectedSource = sprintf('', $extension, $mimeType); self::assertStringContainsString($expectedSource, $responseBody); } } diff --git a/Tests/Functional/Controller/MediaController/RelatedTableTest.php b/Tests/Functional/Controller/MediaController/RelatedTableTest.php index 88089989..d90bbb3f 100644 --- a/Tests/Functional/Controller/MediaController/RelatedTableTest.php +++ b/Tests/Functional/Controller/MediaController/RelatedTableTest.php @@ -21,6 +21,7 @@ public function testMediaControllerShowsVideo(): void $container = $this->getContainer(); $contentObject = new ContentObjectRenderer(); + // @extensionScannerIgnoreLine $contentObject->data = [ 'parent_table' => 'a_dummy_table', 'parent_record' => 23, diff --git a/Tests/Functional/Controller/MediaController/VideoTest.php b/Tests/Functional/Controller/MediaController/VideoTest.php index 8108ebc9..88527ff0 100644 --- a/Tests/Functional/Controller/MediaController/VideoTest.php +++ b/Tests/Functional/Controller/MediaController/VideoTest.php @@ -10,14 +10,14 @@ class VideoTest extends AbstractMediaControllerTestCase { private array $expectedTracks = [ [ - 'src' => '/tracks/subtitles-en.vtt', + 'src' => 'tracks/subtitles-en.vtt', 'kind' => 'subtitles', 'srclang' => 'en', 'label' => 'English', 'default' => true, ], [ - 'src' => '/tracks/subtitles-de.vtt', + 'src' => 'tracks/subtitles-de.vtt', 'kind' => 'subtitles', 'srclang' => 'de', 'label' => 'German', @@ -41,7 +41,7 @@ public function testMediaControllerRendersVideo(): void $videoElement = $this->getSingleElement($videoContent, 'video'); - self::assertSame('/video/poster.png', $videoElement->attr('poster')); + self::assertSame('video/poster.png', $videoElement->attr('poster')); $this->assertVideoContainsSources($videoElement); @@ -68,7 +68,7 @@ private function assertFallbacktextContainsFallbackLinks(Crawler $fallbackText): { foreach ($this->formats as $extension) { /** @noinspection HtmlUnknownTarget */ - $fallbackLink = $fallbackText->filter(sprintf('a[href="/video/media.%s"]', $extension)); + $fallbackLink = $fallbackText->filter(sprintf('a[href="video/media.%s"]', $extension)); self::assertCount(1, $fallbackLink); self::assertSame('media.' . $extension, $fallbackLink->text()); } @@ -92,7 +92,7 @@ private function assertVideoContainsSources(Crawler $videoElement): void foreach ($this->formats as $mimeType => $extension) { $source = $videoElement->filter(sprintf('source[type="video/%s"]', $mimeType)); self::assertCount(1, $source); - self::assertSame('/video/media.' . $extension, $source->attr('src')); + self::assertSame('video/media.' . $extension, $source->attr('src')); } } diff --git a/Tests/Functional/Fixtures/Database/translation.csv b/Tests/Functional/Fixtures/Database/translation.csv index ecaa9909..d7b56f59 100644 --- a/Tests/Functional/Fixtures/Database/translation.csv +++ b/Tests/Functional/Fixtures/Database/translation.csv @@ -6,11 +6,11 @@ pages,,,,,,,, ,20,1,copy mode default,/html5mediakit/copy-mode,1,0,0,0 ,21,1,copy mode de,/html5mediakit/copy-mode,1,1,20,20 tt_content,,,,,,,,, -,uid,pid,CType,header,tx_html5mediakit_media,sys_language_uid,l18n_parent,l10n_source,t3_origuid -,10,10,html5mediakit_mediarenderer,connected default,1,0,0,0,0 -,11,10,html5mediakit_mediarenderer,connected en,1,1,10,10,10 -,20,20,html5mediakit_mediarenderer,copy default,1,0,0,0,0 -,21,20,html5mediakit_mediarenderer,copy en,1,1,0,0,0 +,uid,pid,CType,header,tx_html5mediakit_media,sys_language_uid,l18n_parent,l10n_source +,10,10,html5mediakit_mediarenderer,connected default,1,0,0,0 +,11,10,html5mediakit_mediarenderer,connected en,1,1,10,10 +,20,20,html5mediakit_mediarenderer,copy default,1,0,0,0 +,21,20,html5mediakit_mediarenderer,copy en,1,1,0,0 tx_html5mediakit_domain_model_media,,,,,,,,,,,,, ,uid,pid,content_element,type,caption,description,mp3,ogg,h264,ogv,web_m,sys_language_uid,l10n_parent ,10,10,10,audio,caption connected default,
Some decription with formatting
,1,0,0,0,0,0,0 diff --git a/Tests/Unit/Controller/MediaControllerTest.php b/Tests/Unit/Controller/MediaControllerTest.php index b953f9bc..c921e2df 100644 --- a/Tests/Unit/Controller/MediaControllerTest.php +++ b/Tests/Unit/Controller/MediaControllerTest.php @@ -39,6 +39,7 @@ public function testVideoActionAssignsVideoToView(): void $dummyVideo = $this->createMock(Video::class); $viewMock = $this->createMock(ViewInterface::class); $viewMock->expects(self::once())->method('assign')->with('video', $dummyVideo); + $viewMock->expects(self::once())->method('render')->willReturn(''); $this->mediaController->setView($viewMock); $this->mediaController->videoAction($dummyVideo); } diff --git a/Tests/Unit/Domain/Model/MediaTest.php b/Tests/Unit/Domain/Model/MediaTest.php index f0c875d9..6562424e 100644 --- a/Tests/Unit/Domain/Model/MediaTest.php +++ b/Tests/Unit/Domain/Model/MediaTest.php @@ -89,7 +89,7 @@ public function testGetTstampReturnsExpectedValue(): void public function testGetTypeReturnsExpectedValue(): void { - $theType = new MediaType(MediaType::AUDIO); + $theType = MediaType::AUDIO; $this->media->setType($theType); diff --git a/Tests/Unit/ViewHelpers/VideoViewHelperTest.php b/Tests/Unit/ViewHelpers/VideoViewHelperTest.php index 771ffd96..025a19a5 100644 --- a/Tests/Unit/ViewHelpers/VideoViewHelperTest.php +++ b/Tests/Unit/ViewHelpers/VideoViewHelperTest.php @@ -6,7 +6,6 @@ use Sto\Html5mediakit\Domain\Model\Video; use Sto\Html5mediakit\ViewHelpers\VideoViewHelper; -use TYPO3\CMS\Core\Resource\FileInterface; use TYPO3\CMS\Extbase\Domain\Model\FileReference; use TYPO3\TestingFramework\Core\Unit\UnitTestCase; @@ -28,7 +27,7 @@ public function testNoEmptyAttributeIsRendered(): void public function testPosterAttributeIsRendered(): void { - $resourceMock = $this->getMockBuilder(FileInterface::class)->getMockForAbstractClass(); + $resourceMock = $this->createMock(\TYPO3\CMS\Core\Resource\FileReference::class); $resourceMock->method('getPublicUrl')->willReturn('/my/img/src.png'); $posterMock = $this->getMockBuilder(FileReference::class)->getMock(); diff --git a/Tests/parameters.yml b/Tests/parameters.yml index fbfd031f..9a28342f 100644 --- a/Tests/parameters.yml +++ b/Tests/parameters.yml @@ -3,4 +3,4 @@ # These values can be overridden by environment variables, # e.g. in Build/testing-docker/local/docker-compose.yml # -typo3TestingAcceptanceBaseUrl: http://web:8000 +typo3TestingAcceptanceBaseUrl: http://web diff --git a/composer.json b/composer.json index 144aa72b..59b9592a 100644 --- a/composer.json +++ b/composer.json @@ -4,24 +4,27 @@ "license": "GPL-3.0-or-later", "type": "typo3-cms-extension", "require": { - "php": "^8.1", - "typo3/cms-core": "^12.4", - "typo3/cms-extbase": "*", - "typo3/cms-frontend": "*" + "php": "^8.2", + "typo3/cms-core": "~13.1.0", + "typo3/cms-extbase": "~13.1.0", + "typo3/cms-frontend": "~13.1.0" }, "require-dev": { + "bnf/phpstan-psr-container": "^1.0", "de-swebhosting/php-codestyle": "^5.2", - "de-swebhosting/typo3-extension-buildtools": "dev-TYPO3_12", - "ergebnis/composer-normalize": "^2.28", - "friendsofphp/php-cs-fixer": "^3.14", + "de-swebhosting/typo3-extension-buildtools": "dev-TYPO3_13", + "ergebnis/composer-normalize": "^2.42", + "friendsofphp/php-cs-fixer": "^3.54", + "friendsoftypo3/phpstan-typo3": "^0.9.0", "michielroos/typo3scan": "^1.7", - "squizlabs/php_codesniffer": "^3.7", + "phpstan/phpstan": "^1.10", + "phpstan/phpstan-phpunit": "^1.3", + "squizlabs/php_codesniffer": "^3.9", "symfony/dom-crawler": "^6.3", "typo3/cms-filelist": "*", - "typo3/cms-fluid-styled-content": "*" - }, - "replace": { - "typo3-ter/html5mediakit": "self.version" + "typo3/cms-fluid-styled-content": "~13.1.0", + "typo3/cms-install": "*", + "typo3/testing-framework": "dev-main" }, "autoload": { "psr-4": { @@ -40,7 +43,7 @@ "typo3/class-alias-loader": true, "typo3/cms-composer-installers": true }, - "bin-dir": ".Build/bin", + "bin-dir": "bin", "vendor-dir": ".Build/vendor" }, "extra": { diff --git a/ext_emconf.php b/ext_emconf.php index 53a96f4e..4c44419e 100644 --- a/ext_emconf.php +++ b/ext_emconf.php @@ -15,7 +15,7 @@ 'version' => '12.1.1', 'constraints' => [ 'depends' => [ - 'typo3' => '12.4.0-12.4.99', + 'typo3' => '13.1.0-13.1.99', 'extbase' => '', ], 'conflicts' => [], diff --git a/ext_localconf.php b/ext_localconf.php index 4b4de289..5d966a39 100644 --- a/ext_localconf.php +++ b/ext_localconf.php @@ -19,18 +19,7 @@ [] ); -\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::addPageTSConfig( - '