Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #81 from greenpeace/planet-5167-bdd
PLANET-5167: Introduction of features descriptions as tests
- Loading branch information
Showing
18 changed files
with
1,287 additions
and
114 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,122 @@ | ||
<?php | ||
|
||
namespace Command; | ||
|
||
use \Symfony\Component\Console\Command\Command; | ||
use \Codeception\CustomCommandInterface; | ||
use \Selector\Admin\GutenbergEditor; | ||
use \Selector\Admin\GutenbergEditor\BlockSelector; | ||
use Selector\Admin\GutenbergEditor\Sidebar; | ||
use \Symfony\Component\Console\Input\InputInterface; | ||
use \Symfony\Component\Console\Output\OutputInterface; | ||
use \Symfony\Component\Console\Helper\Table; | ||
use \Symfony\Component\Console\Helper\TableCell; | ||
use \Symfony\Component\Console\Helper\TableSeparator; | ||
|
||
/** | ||
* Prints all css/xpath selectors available | ||
*/ | ||
class Selectors extends Command implements CustomCommandInterface | ||
{ | ||
/** | ||
* Selectors classes to list | ||
* | ||
* @var string[] | ||
*/ | ||
private $selectorClasses = [ | ||
GutenbergEditor::class, | ||
BlockSelector::class, | ||
Sidebar::class, | ||
]; | ||
|
||
/** | ||
* Returns the name of the command | ||
* | ||
* @return string | ||
*/ | ||
public static function getCommandName(): string | ||
{ | ||
return "p4:selectors"; | ||
} | ||
|
||
/** | ||
* Configures the current command. | ||
*/ | ||
protected function configure(): void | ||
{ | ||
parent::configure(); | ||
} | ||
|
||
/** | ||
* Returns the description for the command. | ||
* | ||
* @return string The description for the command | ||
*/ | ||
public function getDescription(): string | ||
{ | ||
return "Listing P4 selectors"; | ||
} | ||
|
||
/** | ||
* Displays a list of available selectors | ||
* Highlights variable parts | ||
* | ||
* @return int | ||
*/ | ||
protected function execute(InputInterface $input, OutputInterface $output): int | ||
{ | ||
$table = new Table($output); | ||
$table->setHeaders(['Method', 'Selector']); | ||
//$table->setColumnMaxWidth(1, 70); | ||
|
||
while ($class = current($this->selectorClasses)) { | ||
$table->addRows([ | ||
[new TableCell(sprintf('<options=bold>%s</>', $class), ['colspan' => 2])], | ||
new TableSeparator() | ||
]); | ||
$table->addRows(array_map( | ||
function ($key, $val) use ($class) { return [ | ||
$this->constToMethod($key, $class), | ||
str_replace('%s', '<fg=yellow;options=bold>%s</>', $val) | ||
]; } , | ||
$class::keys(), | ||
$class::values() | ||
)); | ||
|
||
next($this->selectorClasses); | ||
if (current($this->selectorClasses)) { | ||
$table->addRows([new TableSeparator()]); | ||
} | ||
} | ||
$table->render(); | ||
|
||
return self::SUCCESS; | ||
} | ||
|
||
/** | ||
* Qualifies php-enum const as methods, check if they have specific parameters declaration | ||
* | ||
* @param string $const Constant name | ||
* @param string $class Class name | ||
* | ||
* @return string | ||
*/ | ||
private function constToMethod(string $const, string $class): string | ||
{ | ||
try { | ||
$rfl = new \ReflectionMethod($class, $const); | ||
$params = implode(', ', array_map( | ||
function ($param) use ($class) { | ||
$ns = (new \ReflectionClass($class))->getNamespaceName(); | ||
return str_replace($ns . '\\', '', $param->getType()) | ||
. ' $' . $param->getName(); | ||
}, | ||
$rfl->getParameters() | ||
)); | ||
|
||
return $const . '(<fg=yellow>' . $params . '</>)'; | ||
} catch (\ReflectionException $e) { | ||
return $const . '()'; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,143 @@ | ||
<?php | ||
|
||
namespace Command; | ||
|
||
use \Codeception\CustomCommandInterface; | ||
use \Codeception\Test\Loader\Gherkin; | ||
use Codeception\Util\Annotation; | ||
use \Symfony\Component\Console\Command\Command; | ||
use \Symfony\Component\Console\Helper\Table; | ||
use Symfony\Component\Console\Helper\TableCell; | ||
use Symfony\Component\Console\Helper\TableSeparator; | ||
use \Symfony\Component\Console\Input\InputArgument; | ||
use \Symfony\Component\Console\Input\InputInterface; | ||
use \Symfony\Component\Console\Input\InputOption; | ||
use \Symfony\Component\Console\Output\OutputInterface; | ||
|
||
/** | ||
* Prints all steps from all Gherkin contexts for a specific suite | ||
* Extended `gherkin:steps` to categorize steps and print examples | ||
* | ||
* ``` | ||
* codecept p4:steps acceptance | ||
* ``` | ||
* | ||
*/ | ||
class Steps extends Command implements CustomCommandInterface | ||
{ | ||
use \Codeception\Command\Shared\Config; | ||
use \Codeception\Command\Shared\Style; | ||
|
||
/** | ||
* returns the name of the command | ||
* | ||
* @return string | ||
*/ | ||
public static function getCommandName(): string | ||
{ | ||
return "p4:steps"; | ||
} | ||
|
||
/** | ||
* Configures the current command. | ||
*/ | ||
protected function configure(): void | ||
{ | ||
$this->setDefinition( | ||
[ | ||
new InputArgument('suite', InputArgument::REQUIRED, 'suite to scan for feature files'), | ||
new InputOption('config', 'c', InputOption::VALUE_OPTIONAL, 'Use custom path for config'), | ||
new InputOption('implementation', 'i', InputOption::VALUE_NONE, 'Display implementation'), | ||
] | ||
); | ||
parent::configure(); | ||
} | ||
|
||
/** | ||
* Returns the description for the command. | ||
* | ||
* @return string The description for the command | ||
*/ | ||
public function getDescription(): string | ||
{ | ||
return 'Listing P4 steps'; | ||
} | ||
|
||
/** | ||
* Displays a list of implemented steps | ||
* | ||
* @return int | ||
*/ | ||
public function execute(InputInterface $input, OutputInterface $output): int | ||
{ | ||
|
||
$this->addStyles($output); | ||
$suite = $input->getArgument('suite'); | ||
$config = $this->getSuiteConfig($suite); | ||
$config['describe_steps'] = true; | ||
$implementation = $input->getOption('implementation'); | ||
|
||
$loader = new Gherkin($config); | ||
$steps = $loader->getSteps(); | ||
|
||
$colspan = ['colspan' => $implementation ? 3 : 2]; | ||
$prevClass = null; | ||
foreach ($steps as $name => $context) { | ||
$table = new Table($output); | ||
$table->setHeaders($implementation | ||
? ['Step', 'Examples', 'Implementation'] | ||
: ['Step', 'Examples'] | ||
); | ||
$output->writeln("Steps from <bold>$name</bold> context:"); | ||
|
||
foreach ($context as $step => $callable) { | ||
if (count($callable) < 2) { | ||
continue; | ||
} | ||
|
||
[$class, $method] = $callable; | ||
if ($class !== $prevClass) { | ||
$this->addClassheader($table, $class, $colspan, null !== $prevClass); | ||
$prevClass = $class; | ||
} | ||
|
||
$methodAnnotation = Annotation::forMethod($class, $method); | ||
$examples = $methodAnnotation->fetchAll('example'); | ||
$method = $class . '::' . $method; | ||
|
||
$implementation | ||
? $table->addRow([$step, implode("\n", $examples), $method]) | ||
: $table->addRow([$step, implode("\n", $examples)]); | ||
} | ||
$table->render(); | ||
} | ||
|
||
if (!isset($table)) { | ||
$output->writeln("No steps are defined, start creating them by running <bold>gherkin:snippets</bold>"); | ||
} | ||
|
||
return self::SUCCESS; | ||
} | ||
|
||
private function addClassheader( | ||
Table $table, | ||
string $class, | ||
array $attrs, | ||
bool $addTopSeparator | ||
): void { | ||
if ($addTopSeparator) { | ||
$table->addRow([new TableSeparator($attrs)]); | ||
} | ||
|
||
$classComments = (new \ReflectionClass($class))->getDocComment(); | ||
preg_match_all('/ \* (.*)/', $classComments, $matches); | ||
|
||
$table->addRows([ | ||
[new TableCell( | ||
sprintf('<options=bold>%s</>', $matches[1][0] ?? $class), | ||
$attrs | ||
)], | ||
[new TableSeparator($attrs)] | ||
]); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Page\Acceptance\Admin; | ||
|
||
use Selector\Admin\GutenbergEditor as EditorSelector; | ||
use Selector\Admin\GutenbergEditor\BlockName; | ||
use Selector\Admin\GutenbergEditor\BlockSection; | ||
use Selector\Admin\GutenbergEditor\BlockSelector; | ||
|
||
class GutenbergEditor | ||
{ | ||
/** | ||
* @var \AcceptanceTester; | ||
*/ | ||
protected $tester; | ||
|
||
public function __construct(\AcceptanceTester $I) | ||
{ | ||
$this->tester = $I; | ||
} | ||
|
||
public function addBlock(BlockSection $blockSection, BlockName $blockName): void | ||
{ | ||
$I = $this->tester; | ||
$blockButton = (string) BlockSelector::BLOCK($blockName); | ||
|
||
$this->openBlockSelector(); | ||
$I->click((string) BlockSelector::SECTION($blockSection)); | ||
$I->waitForElement($blockButton, 1); | ||
$I->click($blockButton); | ||
} | ||
|
||
public function openBlockSelector(): void | ||
{ | ||
$I = $this->tester; | ||
$I->click((string) BlockSelector::MAIN_BUTTON()); | ||
} | ||
} |
Oops, something went wrong.