Skip to content

Commit

Permalink
[FEATURE] Add command 'make:acceptance'
Browse files Browse the repository at this point in the history
The command 'make:acceptance' has been added
to create basic files and config to run
acceptance tests.
  • Loading branch information
ochorocho committed Apr 29, 2022
1 parent 99b1e4a commit 1a70405
Show file tree
Hide file tree
Showing 12 changed files with 375 additions and 1 deletion.
2 changes: 1 addition & 1 deletion Build/php-cs-fixer.php
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<?php

$config = \TYPO3\CodingStandards\CsFixerConfig::create();
$config->getFinder()->exclude(['var']);
$config->getFinder()->exclude(['var', 'Resources/Private/CodeTemplates']);
return $config;
168 changes: 168 additions & 0 deletions Classes/Command/AcceptanceTestsCommand.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
<?php

declare(strict_types=1);

/*
* This file is part of TYPO3 CMS-based extension "b13/make" by b13.
*
* 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.
*/

namespace B13\Make\Command;

use B13\Make\PackageResolver;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
use Symfony\Component\Filesystem\Exception\IOException;
use Symfony\Component\Filesystem\Filesystem;
use Symfony\Component\Finder\Finder;
use TYPO3\CMS\Core\Package\PackageInterface;
use TYPO3\CMS\Core\Service\MarkerBasedTemplateService;
use TYPO3\CMS\Core\Utility\GeneralUtility;

/**
* Command for creating a new backend controller component
*/
class AcceptanceTestsCommand extends AbstractCommand
{
protected Filesystem $filesystem;
protected SymfonyStyle $io;
protected PackageInterface $package;

protected function configure(): void
{
parent::configure();
$this->setDescription('Prepare extensions to run acceptance tests');
$this->filesystem = GeneralUtility::makeInstance(Filesystem::class);
}

protected function execute(InputInterface $input, OutputInterface $output): int
{
$this->io = new SymfonyStyle($input, $output);
$packages = $this->getPackageResolver()->getPackageManager()->getActivePackages();

$choices = array_reduce($packages, function ($result, PackageInterface $package) {
if ($package->getPackageMetaData()->getPackageType() === 'typo3-cms-extension') {
$packageKey = $package->getPackageKey();
$result[$packageKey] = $packageKey;
}
return $result;
}, []);

$selectedPackageName = $this->io->choice('Select a package to create acceptance tests for', $choices);
$this->package = $this->getPackageResolver()->resolvePackage($selectedPackageName);

$packageKey = $this->package->getPackageKey();
$this->io->writeln('Selected package: ' . $packageKey);
$finder = GeneralUtility::makeInstance(Finder::class);

$targetPackage = $this->package->getPackagePath();
$codeTemplatePath = '/Resources/Private/CodeTemplates/AcceptanceTests';
$templatePath = $this->getPackageResolver()->resolvePackage('b13/make')->getPackagePath() . $codeTemplatePath;

$this->filesystem->mkdir([
$targetPackage . '/Tests/Acceptance',
$targetPackage . '/Tests/Acceptance/Fixtures',
$targetPackage . '/Tests/Acceptance/Application',
$targetPackage . '/Tests/Acceptance/Support/Extension'
]);

// Create public folder which is required for e.g. acceptance tests to work
$publicFolderPath = $targetPackage . '/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 = $targetPackage . '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);
}
}
}

if ($this->updateComposerFile($targetPackage)) {
$this->io->writeln('<info>Updated composer.json for EXT:' . $packageKey . '</info>');
} else {
$this->io->writeln('<comment>Failed to update composer.json for EXT:' . $packageKey . '</comment>');
}

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, 512, JSON_THROW_ON_ERROR);
$namespace = rtrim($this->getNamespace(), '\\');

// @todo: if a value already exists ask for permission to change it?!
$composer['require-dev']['codeception/codeception'] = '^4.1';
$composer['require-dev']['codeception/module-asserts'] = '^1.2';
$composer['require-dev']['codeception/module-webdriver'] = '^1.1';
$composer['require-dev']['typo3/testing-framework'] = '^6.16.2';

$composer['autoload-dev']['psr-4'][$namespace . '\\Tests\\'] = 'Tests/';

$composer['config']['vendor-dir'] = '.Build/vendor';
$composer['config']['bin-dir'] = '.Build/bin';

$composer['extra']['typo3/cms']['app-dir'] = '.Build';
$composer['extra']['typo3/cms']['web-dir'] = '.Build/Web';

return GeneralUtility::writeFile($composerFile, json_encode($composer, 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('<error>Failed to save file in ' . $target . '</error>');
}
}

protected function getPackageResolver(): PackageResolver
{
return GeneralUtility::makeInstance(PackageResolver::class);
}

protected function getNamespace(): string
{
return (string)key((array)($this->package->getValueFromComposerManifest('autoload')->{'psr-4'} ?? []));
}
}
5 changes: 5 additions & 0 deletions Classes/PackageResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,9 @@ public function resolvePackage(string $extensionkey): ?PackageInterface
return null;
}
}

public function getPackageManager(): PackageManager
{
return $this->packageManager;
}
}
7 changes: 7 additions & 0 deletions Configuration/Services.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -44,3 +44,10 @@ services:
command: 'make:middleware'
description: 'Create a PSR-15 middleware'
schedulable: false

B13\Make\Command\AcceptanceTestsCommand:
tags:
- name: 'console.command'
command: 'make:acceptance'
description: 'Create files required to run acceptance tests'
schedulable: false
Original file line number Diff line number Diff line change
@@ -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-*
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?php

declare(strict_types=1);

namespace {{NAMESPACE}}\Tests\Acceptance\Backend;

use {{NAMESPACE}}\Tests\Acceptance\Support\ApplicationTester;

class ExampleCest
{
/**
* @param ApplicationTester $I
*/
public function _before(ApplicationTester $I)
{
$I->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');
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<dataset>
<be_sessions>
<!-- hash_hmac('sha256', '886526ce72b86870739cc41991144ec1', sha1('iAmInvalid' . 'core-session-backend')) -->
<ses_id>9869d429fc72742a476d5073d006d45dfb732962d9c024423efafef537e1c5bd</ses_id>
<ses_iplock>[DISABLED]</ses_iplock>
<ses_userid>1</ses_userid>
<ses_tstamp>1777777777</ses_tstamp>
<ses_data></ses_data>
</be_sessions>
<be_sessions>
<!-- hash_hmac('sha256', 'ff83dfd81e20b34c27d3e97771a4525a', sha1('iAmInvalid' . 'core-session-backend')) -->
<ses_id>f4c02f70058e79a8e7b523a266d4291007deacba6b2ca2536dd72d2fbb23696a</ses_id>
<ses_iplock>[DISABLED]</ses_iplock>
<ses_userid>2</ses_userid>
<ses_tstamp>1777777777</ses_tstamp>
<ses_data></ses_data>
</be_sessions>
</dataset>
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<dataset>
<!-- Creates a page + subpage -->
<pages>
<uid>1</uid>
<pid>0</pid>
<title>Home</title>
<slug></slug>
<doktype>1</doktype>
<deleted>0</deleted>
<is_siteroot>1</is_siteroot>
<perms_everybody>15</perms_everybody>
</pages>
<pages>
<uid>2</uid>
<pid>1</pid>
<sorting>128</sorting>
<title>Subpage</title>
<slug>subpage</slug>
<deleted>0</deleted>
<doktype>1</doktype>
<perms_everybody>15</perms_everybody>
</pages>
</dataset>
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<dataset>
<sys_template>
<uid>1</uid>
<pid>1</pid>
<title>Root Template</title>
<deleted>0</deleted>
<root>1</root>
<clear>3</clear>
<config>
page = PAGE
page.10 = TEXT
page.10.data = page:title
</config>
</sys_template>
</dataset>
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?php

declare(strict_types=1);

namespace {{NAMESPACE}}\Tests\Acceptance\Support;

use {{NAMESPACE}}\Tests\Acceptance\Support\_generated\ApplicationTesterActions;
use TYPO3\TestingFramework\Core\Acceptance\Step\FrameSteps;

/**
* Default backend admin or editor actor in the backend
*/
class ApplicationTester extends \Codeception\Actor
{
use ApplicationTesterActions;
use FrameSteps;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
<?php

declare(strict_types=1);

namespace {{NAMESPACE}}\Tests\Acceptance\Support\Extension;

use Symfony\Component\Mailer\Transport\NullTransport;
use TYPO3\TestingFramework\Core\Acceptance\Extension\BackendEnvironment;

/**
* Load various core extensions and styleguide and call styleguide generator
*/
class ExtensionEnvironment extends BackendEnvironment
{
/**
* Load a list of core extensions
*
* @var array
*/
protected $localConfig = [
'coreExtensionsToLoad' => [
'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',
// ]
];
}
13 changes: 13 additions & 0 deletions Resources/Private/CodeTemplates/AcceptanceTests/codeception.yml
Original file line number Diff line number Diff line change
@@ -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

0 comments on commit 1a70405

Please sign in to comment.