diff --git a/Classes/Command/AcceptanceTestsCommand.php b/Classes/Command/AcceptanceTestsCommand.php new file mode 100644 index 0000000..3a4c1b6 --- /dev/null +++ b/Classes/Command/AcceptanceTestsCommand.php @@ -0,0 +1,199 @@ +setDescription('Prepare extensions to run acceptance tests'); + $this->filesystem = GeneralUtility::makeInstance(Filesystem::class); + } + + /** + * @throws \JsonException + */ + protected function execute(InputInterface $input, OutputInterface $output): int + { + $this->package = $this->packageResolver->resolvePackage($this->askForExtensionKey()); + + $packageKey = $this->package->getPackageKey(); + $targetPackagePath = $this->package->getPackagePath(); + + if ($this->updateComposerFile($targetPackagePath)) { + $this->io->writeln('Updated composer.json for EXT:' . $packageKey . ''); + } else { + $this->io->writeln('Failed to update composer.json for EXT:' . $packageKey . ''); + } + + $this->io->writeln('Selected package: ' . $packageKey); + $finder = GeneralUtility::makeInstance(Finder::class); + + $codeTemplatePath = '/Resources/Private/CodeTemplates/AcceptanceTests'; + $templatePath = $this->packageResolver->resolvePackage('b13/make')->getPackagePath() . $codeTemplatePath; + + $this->filesystem->mkdir([ + $targetPackagePath . '/Tests/Acceptance/Fixtures', + $targetPackagePath . '/Tests/Acceptance/Application', + $targetPackagePath . '/Tests/Acceptance/Support/Extension' + ]); + + // Create public folder which is required for e.g. acceptance tests to work + $publicFolderPath = $targetPackagePath . '/Resources/Public'; + if (!is_dir($publicFolderPath)) { + $createPublic = $this->io->confirm( + 'Resource/Public is necessary e.g. for acceptance tests. Do you want to create it now?', + true + ); + + if ($createPublic) { + $this->filesystem->mkdir([$publicFolderPath]); + // Ensure the folder will be detected by git and committed + $this->filesystem->touch([$publicFolderPath . '/.gitkeep']); + } + } + + $files = $finder->in($templatePath)->files(); + + foreach ($files as $file) { + $target = $targetPackagePath . 'Tests' . explode('AcceptanceTests', $file->getRealPath())[1]; + + if (!is_file($target)) { + $content = $file->getContents(); + $this->substituteMarkersAndSave($content, $target); + } else { + $overwrite = $this->io->confirm('File exists ' . basename($target) . '. Do you want to overwrite it?'); + if ($overwrite) { + $content = $file->getContents(); + $this->substituteMarkersAndSave($content, $target); + } + } + } + + return 0; + } + + /** + * Extend/prepare composer.json of the extension + * for acceptance tests + * + * @throws \JsonException + */ + protected function updateComposerFile(string $packagePath): bool + { + $composerFile = $packagePath . '/composer.json'; + $composerJson = file_get_contents($composerFile); + $composer = json_decode($composerJson, true); + if (json_last_error() !== JSON_ERROR_NONE) { + throw new \JsonException('Could not parse ' . $composerFile); + } + + $namespace = rtrim($this->getNamespace(), '\\'); + + $addToComposerFile = [ + 'require-dev' => [ + 'codeception/codeception' => '^4.1', + 'codeception/module-asserts' => '^1.2', + 'codeception/module-webdriver' => '^1.1', + 'typo3/testing-framework' => '^6.16.2' + ], + 'autoload-dev' => [ + 'psr-4' => [ + $namespace . '\\Tests\\' => 'Tests/' + ] + ], + 'config' => [ + 'vendor-dir' => '.Build/vendor', + 'bin-dir' => '.Build/bin', + ], + 'extra' => [ + 'typo3/cms' => [ + 'app-dir' => '.Build', + 'web-dir' => '.Build/Web', + ] + ] + ]; + + $enhancedComposer = $this->enhanceComposerFile($composer, $addToComposerFile); + + return GeneralUtility::writeFile($composerFile, json_encode($enhancedComposer, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), true); + } + + /** + * Substitute marker values and save file to extension_key/Tests/ + * + * @param string $content + * @param string $target + */ + protected function substituteMarkersAndSave(string $content, string $target): void + { + $markerService = GeneralUtility::makeInstance(MarkerBasedTemplateService::class); + $templateContent = $markerService->substituteMarker( + $content, + '{{NAMESPACE}}', + rtrim($this->getNamespace(), '\\') + ); + $templateContent = $markerService->substituteMarker( + $templateContent, + '{{EXTENSION_KEY}}', + $this->package->getPackageKey() + ); + + try { + $this->filesystem->dumpFile($target, $templateContent); + } catch (IOException $exception) { + $this->io->writeln('Failed to save file in ' . $target . ''); + } + } + + protected function getNamespace(): string + { + return (string)key((array)($this->package->getValueFromComposerManifest('autoload')->{'psr-4'} ?? [])); + } + + private function enhanceComposerFile(array &$composer, array &$addToComposerFile): array + { + foreach ($addToComposerFile as $key => $value) { + if (is_array($value) && isset($composer[$key])) { + $this->enhanceComposerFile($composer[$key], $value); + } else { + $composer[$key] = $value; + } + } + + return $composer; + } +} diff --git a/Configuration/Services.yaml b/Configuration/Services.yaml index fafe6ed..15bf9fa 100644 --- a/Configuration/Services.yaml +++ b/Configuration/Services.yaml @@ -51,3 +51,9 @@ services: command: 'make:testing:setup' description: 'Create a docker based testing environment setup' schedulable: false + B13\Make\Command\AcceptanceTestsCommand: + tags: + - name: 'console.command' + command: 'make:testing:acceptance' + description: 'Create files required to run acceptance tests' + schedulable: false diff --git a/Resources/Private/CodeTemplates/AcceptanceTests/Acceptance/Application.suite.yml b/Resources/Private/CodeTemplates/AcceptanceTests/Acceptance/Application.suite.yml new file mode 100644 index 0000000..810a6cd --- /dev/null +++ b/Resources/Private/CodeTemplates/AcceptanceTests/Acceptance/Application.suite.yml @@ -0,0 +1,23 @@ +class_name: ApplicationTester +modules: + enabled: + - WebDriver: + url: http://web:8000/typo3temp/var/tests/acceptance + browser: chrome + wait: 1 + host: chrome + window_size: 1400x800 + - \TYPO3\TestingFramework\Core\Acceptance\Helper\Acceptance + - \TYPO3\TestingFramework\Core\Acceptance\Helper\Login: + sessions: + # This sessions must exist in the database fixture to get a logged in state. + editor: ff83dfd81e20b34c27d3e97771a4525a + admin: 886526ce72b86870739cc41991144ec1 + - Asserts + +extensions: + enabled: + - {{NAMESPACE}}\Tests\Acceptance\Support\Extension\ExtensionEnvironment + +groups: + AcceptanceTests-Job-*: AcceptanceTests-Job-* diff --git a/Resources/Private/CodeTemplates/AcceptanceTests/Acceptance/Application/ExampleCest.php b/Resources/Private/CodeTemplates/AcceptanceTests/Acceptance/Application/ExampleCest.php new file mode 100644 index 0000000..5af86a6 --- /dev/null +++ b/Resources/Private/CodeTemplates/AcceptanceTests/Acceptance/Application/ExampleCest.php @@ -0,0 +1,30 @@ +useExistingSession('admin'); + $I->switchToMainFrame(); + } + + /** + * @param ApplicationTester $I + * @throws \Exception + */ + public function seeTestExample(ApplicationTester $I): void + { + $I->click('Page'); + $I->switchToContentFrame(); + $I->see('Web>Page module', '.callout-info'); + } +} diff --git a/Resources/Private/CodeTemplates/AcceptanceTests/Acceptance/Fixtures/be_sessions.xml b/Resources/Private/CodeTemplates/AcceptanceTests/Acceptance/Fixtures/be_sessions.xml new file mode 100644 index 0000000..4160903 --- /dev/null +++ b/Resources/Private/CodeTemplates/AcceptanceTests/Acceptance/Fixtures/be_sessions.xml @@ -0,0 +1,19 @@ + + + + + 9869d429fc72742a476d5073d006d45dfb732962d9c024423efafef537e1c5bd + [DISABLED] + 1 + 1777777777 + + + + + f4c02f70058e79a8e7b523a266d4291007deacba6b2ca2536dd72d2fbb23696a + [DISABLED] + 2 + 1777777777 + + + diff --git a/Resources/Private/CodeTemplates/AcceptanceTests/Acceptance/Fixtures/pages.xml b/Resources/Private/CodeTemplates/AcceptanceTests/Acceptance/Fixtures/pages.xml new file mode 100644 index 0000000..43a50f7 --- /dev/null +++ b/Resources/Private/CodeTemplates/AcceptanceTests/Acceptance/Fixtures/pages.xml @@ -0,0 +1,24 @@ + + + + + 1 + 0 + Home + + 1 + 0 + 1 + 15 + + + 2 + 1 + 128 + Subpage + subpage + 0 + 1 + 15 + + diff --git a/Resources/Private/CodeTemplates/AcceptanceTests/Acceptance/Fixtures/sys_template.xml b/Resources/Private/CodeTemplates/AcceptanceTests/Acceptance/Fixtures/sys_template.xml new file mode 100644 index 0000000..7d9b169 --- /dev/null +++ b/Resources/Private/CodeTemplates/AcceptanceTests/Acceptance/Fixtures/sys_template.xml @@ -0,0 +1,16 @@ + + + + 1 + 1 + Root Template + 0 + 1 + 3 + +page = PAGE +page.10 = TEXT +page.10.data = page:title + + + diff --git a/Resources/Private/CodeTemplates/AcceptanceTests/Acceptance/Support/ApplicationTester.php b/Resources/Private/CodeTemplates/AcceptanceTests/Acceptance/Support/ApplicationTester.php new file mode 100644 index 0000000..2704bd8 --- /dev/null +++ b/Resources/Private/CodeTemplates/AcceptanceTests/Acceptance/Support/ApplicationTester.php @@ -0,0 +1,17 @@ + [ + 'core', + 'extbase', + 'filelist', + 'setup', + 'frontend', + 'fluid', + 'recordlist', + 'backend', + 'install', + ], + 'testExtensionsToLoad' => [ + 'typo3conf/ext/{{EXTENSION_KEY}}', + ], + 'xmlDatabaseFixtures' => [ + 'PACKAGE:typo3/testing-framework/Resources/Core/Acceptance/Fixtures/be_users.xml', + 'PACKAGE:../Web/typo3conf/ext/{{EXTENSION_KEY}}/Tests/Acceptance/Fixtures/pages.xml', + 'PACKAGE:../Web/typo3conf/ext/{{EXTENSION_KEY}}/Tests/Acceptance/Fixtures/be_sessions.xml', + 'PACKAGE:../Web/typo3conf/ext/{{EXTENSION_KEY}}/Tests/Acceptance/Fixtures/sys_template.xml', + ], + 'configurationToUseInTestInstance' => [ + 'MAIL' => [ + 'transport' => NullTransport::class, + ], + ], + + // Link files/folders required for your acceptance tests to run + // 'pathsToLinkInTestInstance' => [ + // 'typo3conf/ext/{{EXTENSION_KEY}}/Tests/Acceptance/Fixtures/sites' => 'typo3conf/sites', + // ] + ]; +} diff --git a/Resources/Private/CodeTemplates/AcceptanceTests/codeception.yml b/Resources/Private/CodeTemplates/AcceptanceTests/codeception.yml new file mode 100644 index 0000000..6509bba --- /dev/null +++ b/Resources/Private/CodeTemplates/AcceptanceTests/codeception.yml @@ -0,0 +1,13 @@ +namespace: {{NAMESPACE}}\Tests\Acceptance\Support +paths: + tests: Acceptance + data: . + log: ../.Build/Web/typo3temp/var/tests/AcceptanceReports + support: Acceptance/Support +settings: + colors: true + memory_limit: 1024M +extensions: + enabled: + - Codeception\Extension\RunFailed + - Codeception\Extension\Recorder