Skip to content

Commit

Permalink
Refactored show_url into info_types to allow more information on …
Browse files Browse the repository at this point in the history
…screenshots.
  • Loading branch information
AlexSkrypnyk committed Jan 18, 2025
1 parent 64d929a commit 52f2864
Show file tree
Hide file tree
Showing 13 changed files with 153 additions and 68 deletions.
20 changes: 12 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,14 +95,16 @@ You may optionally specify size of browser window in the screenshot step:

## Options

| Name | Default value | Description |
|-------------------------|----------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------|
| `dir` | `%paths.base%/screenshots` | Path to directory to save screenshots. Directory structure will be created if the directory does not exist. |
| `fail` | `true` | Capture screenshot on test failure. |
| `fail_prefix` | `failed_` | Prefix failed screenshots with `fail_` string. Useful to distinguish failed and intended screenshots. |
| `purge` | `false` | Remove all files from the screenshots directory on each test run. Useful during debugging of tests. |
| `filenamePattern` | `{datetime:u}.{feature_file}.feature_{step_line}.{ext}` | File name pattern for successful assertions. |
| `filenamePatternFailed` | `{datetime:u}.{fail_prefix}{feature_file}.feature_{step_line}.{ext}` | File name pattern for failed assertions. |
| Name | Default value | Description |
|-------------------------|----------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------|
| `dir` | `%paths.base%/screenshots` | Path to directory to save screenshots. Directory structure will be created if the directory does not exist. |
| `fail` | `true` | Capture screenshot on test failure. |
| `fail_prefix` | `failed_` | Prefix failed screenshots with `fail_` string. Useful to distinguish failed and intended screenshots. |
| `purge` | `false` | Remove all files from the screenshots directory on each test run. Useful during debugging of tests. |
| `info_types` | `url`, `feature`, `step`, `datetime` | Show additional information on screenshots. Comma-separated list of `url`, `feature`, `step`, `datetime`, or remove to disable. Ordered as listed. |
| `filenamePattern` | `{datetime:u}.{feature_file}.feature_{step_line}.{ext}` | File name pattern for successful assertions. |
| `filenamePatternFailed` | `{datetime:u}.{fail_prefix}{feature_file}.feature_{step_line}.{ext}` | File name pattern for failed assertions. |
| `filenamePatternFailed` | `{datetime:u}.{fail_prefix}{feature_file}.feature_{step_line}.{ext}` | File name pattern for failed assertions. |

### Supported tokens

Expand Down Expand Up @@ -153,6 +155,8 @@ composer test-unit # Run unit tests.

docker run -d -p 4444:4444 selenium/standalone-chromium
composer test-bdd # Run BDD tests.

BEHAT_CLI_DEBUG=1 composer test-bdd # Run BDD tests with debug output.
```

---
Expand Down
5 changes: 5 additions & 0 deletions behat.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,11 @@ default:
dir: '%paths.base%/.logs/screenshots'
fail: true
purge: true
info_types:
- url
- feature
- step
- datetime

DVDoug\Behat\CodeCoverage\Extension:
filter:
Expand Down
5 changes: 5 additions & 0 deletions behat.yml.dist
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,10 @@ default:
dir: %paths.base%/screenshots
fail: true
purge: false
info_types:
- url
- feature
- step
- datetime
filenamePattern: '{datetime:u}.{feature_file}.feature_{step_line}.{ext}'
filenamePatternFailed: '{datetime:u}.{fail_prefix}{feature_file}.feature_{step_line}.{ext}'
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@ class ScreenshotContextInitializer implements ContextInitializer {
* File name pattern.
* @param string $filenamePatternFailed
* File name pattern failed.
* @param bool $showPath
* Show current URL in screenshots.
* @param array<int,string> $infoTypes
* Show these info types in the screenshot.
*
* @codeCoverageIgnore
*/
Expand All @@ -47,7 +47,7 @@ public function __construct(
protected bool $purge,
protected string $filenamePattern,
protected string $filenamePatternFailed,
protected bool $showPath = FALSE,
protected array $infoTypes = [],
) {
}

Expand All @@ -69,7 +69,7 @@ public function initializeContext(Context $context): void {
$this->failPrefix,
$this->filenamePattern,
$this->filenamePatternFailed,
$this->showPath
$this->infoTypes
);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,17 @@ interface ScreenshotAwareContextInterface extends Context {
* Directory to store screenshots.
* @param bool $fail
* Create screenshots on fail.
* @param string $failPrefix
* @param string $fail_prefix
* File name prefix for a failed test.
* @param string $filenamePattern
* @param string $filename_pattern
* File name pattern.
* @param string $filenamePatternFailed
* @param string $filename_pattern_failed
* File name pattern failed.
* @param bool $showPath
* Show path in the screenshot.
* @param array<int,string> $info_types
* Show these info types in the screenshot.
*
* @return $this
*/
public function setScreenshotParameters(string $dir, bool $fail, string $failPrefix, string $filenamePattern, string $filenamePatternFailed, bool $showPath): static;
public function setScreenshotParameters(string $dir, bool $fail, string $fail_prefix, string $filename_pattern, string $filename_pattern_failed, array $info_types): static;

}
74 changes: 53 additions & 21 deletions src/DrevOps/BehatScreenshotExtension/Context/ScreenshotContext.php
Original file line number Diff line number Diff line change
Expand Up @@ -45,16 +45,18 @@ class ScreenshotContext extends RawMinkContext implements ScreenshotAwareContext
protected string $filenamePatternFailed;

/**
* Show the path in the screenshot.
* Information types to be added to a screenshot.
*
* @var array<int,string>
*/
protected bool $showPath = FALSE;
protected array $infoTypes = [];

/**
* Debug information to be added to a screenshot.
* Information to be added to a screenshot.
*
* @var array<string, string>
*/
protected array $debugInformation = [];
protected array $info = [];

/**
* Before step scope.
Expand All @@ -64,13 +66,13 @@ class ScreenshotContext extends RawMinkContext implements ScreenshotAwareContext
/**
* {@inheritdoc}
*/
public function setScreenshotParameters(string $dir, bool $fail, string $failPrefix, string $filenamePattern, string $filenamePatternFailed, bool $showPath): static {
public function setScreenshotParameters(string $dir, bool $fail, string $fail_prefix, string $filename_pattern, string $filename_pattern_failed, array $info_types): static {
$this->dir = $dir;
$this->fail = $fail;
$this->failPrefix = $failPrefix;
$this->filenamePattern = $filenamePattern;
$this->filenamePatternFailed = $filenamePatternFailed;
$this->showPath = $showPath;
$this->failPrefix = $fail_prefix;
$this->filenamePattern = $filename_pattern;
$this->filenamePatternFailed = $filename_pattern_failed;
$this->infoTypes = $info_types;

return $this;
}
Expand Down Expand Up @@ -156,8 +158,8 @@ public function iSaveScreenshot(bool $is_failure = FALSE, ?string $filename = NU
$driver = $this->getSession()->getDriver();
$content = $driver->getContent();

$info = $this->renderDebugInformation();
$content = empty($info) ? $content : $info . '<br />' . $content;
$info = $this->renderInfo();
$content = empty($info) ? $content : nl2br($info) . "<hr/>\n" . $content;
}
catch (DriverException) {
// Do nothing if the driver does not have any content - most
Expand Down Expand Up @@ -237,35 +239,65 @@ public function getBeforeStepScope(): BeforeStepScope {
}

/**
* Adds debug information to context.
* Adds information to context.
*
* @param string $label
* Debug information label.
* @param string $value
* Debug information value.
*/
public function appendDebugInformation(string $label, string $value): void {
$this->debugInformation[$label] = $value;
public function appendInfo(string $label, string $value): void {
$this->info[$label] = $value;
}

/**
* Render debug information.
* Render information.
*
* @return string
* Rendered debug information.
*/
public function renderDebugInformation(): string {
if ($this->showPath) {
$this->appendDebugInformation('Current URL', $this->getSession()->getCurrentUrl());
}
public function renderInfo(): string {
$this->compileInfo();

// Use a non-HTML output to make this output universal.
return implode("\n", array_map(
fn($key, $value): string => sprintf('%s: %s', $key, $value),
array_keys($this->debugInformation),
$this->debugInformation,
array_keys($this->info),
$this->info,
));
}

/**
* Compile information.
*/
protected function compileInfo(): void {
foreach ($this->infoTypes as $type) {
if ($type === 'url') {
$this->appendInfo('Current URL', $this->getSession()->getCurrentUrl());
}
if ($type === 'feature') {
$this->appendInfo('Feature', (string) $this->getBeforeStepScope()->getFeature()->getTitle());
}
if ($type === 'step') {
$step = $this->getBeforeStepScope()->getStep();
$this->appendInfo('Step', sprintf('%s (line %d)', $step->getText(), $step->getLine()));
}
if ($type === 'datetime') {
$this->appendInfo('Datetime', date('Y-m-d H:i:s'));
}
}
}

/**
* Get screenshot directory.
*
* @return string
* Screenshot directory.
*/
public function getDir(): string {
return $this->dir;
}

/**
* Get current timestamp.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,9 +77,9 @@ public function configure(ArrayNodeDefinition $builder): void {
->cannotBeEmpty()
->defaultValue('{datetime:U}.{fail_prefix}{feature_file}.feature_{step_line}.{ext}')
->end()
->scalarNode('show_path')
->cannotBeEmpty()
->defaultValue(FALSE)
->arrayNode('info_types')
->defaultValue([])
->prototype('scalar')
->end();
// @formatter:on
// @phpcs:enable Drupal.WhiteSpace.ObjectOperatorIndent.Indent
Expand All @@ -96,7 +96,7 @@ public function load(ContainerBuilder $container, array $config): void {
$config['purge'],
$config['filenamePattern'],
$config['filenamePatternFailed'],
$config['show_path'],
$config['info_types'],
]);
$definition->addTag(ContextExtension::INITIALIZER_TAG, ['priority' => 0]);
$container->setDefinition('drevops_screenshot.screenshot_context_initializer', $definition);
Expand Down
39 changes: 30 additions & 9 deletions tests/behat/bootstrap/BehatCliTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@
use Behat\Behat\Hook\Scope\AfterScenarioScope;
use Behat\Behat\Hook\Scope\BeforeScenarioScope;
use Behat\Gherkin\Node\PyStringNode;
use DrevOps\BehatScreenshotExtension\Context\ScreenshotContext;
use PHPUnit\Framework\Assert;
use Symfony\Component\Filesystem\Filesystem;
use Symfony\Component\Finder\Finder;

/**
Expand All @@ -28,10 +30,29 @@ public function behatCliBeforeScenario(BeforeScenarioScope $scope): void {
}

/**
* @AfterScenario
* @AfterScenario @behatcli
*/
public function behatCliAfterScenarioPrintOutput(AfterScenarioScope $scope): void {
if ($scope->getFeature()->hasTag('behatcli') && static::behatCliIsDebug()) {
// Copy the screenshots to the working directory.
$context = $scope->getEnvironment()->getContext(ScreenshotContext::class);
if ($context instanceof ScreenshotContext) {
$src = $this->workingDir . DIRECTORY_SEPARATOR . 'screenshots';
if (is_dir($src)) {
$dst = $context->getDir() . '/behatcli_screenshots';
if (!is_readable($dst)) {
mkdir($dst, 0777, TRUE);
}

$finder = new Finder();
$fs = new Filesystem();

foreach ($finder->in($src)->files() as $file) {
$fs->copy($file->getRealPath(), $dst . DIRECTORY_SEPARATOR . $file->getFilename());
}
}
}

if (static::behatCliIsDebug()) {
print "-------------------- OUTPUT START --------------------" . PHP_EOL;
print PHP_EOL;
print $this->getOutput();
Expand Down Expand Up @@ -127,7 +148,7 @@ public function throwTestException($message) {
/**
* @Given /^scenario steps(?: tagged with "([^"]*)")?:$/
*/
public function behatCliWriteScenarioSteps(PyStringNode $content, $tags = ''): void {
public function behatCliWriteScenarioSteps(PyStringNode $content, string $tags = ''): void {
$content = strtr((string) $content, ["'''" => '"""']);

// Make sure that indentation in provided content is accurate.
Expand All @@ -144,7 +165,7 @@ public function behatCliWriteScenarioSteps(PyStringNode $content, $tags = ''): v

$content = <<<'EOL'
@behatcli
Feature: Stub feature';
Feature: Stub feature
{{ADDITIONAL_TAGS}}
Scenario: Stub scenario title
{{SCENARIO_CONTENT}}
Expand Down Expand Up @@ -269,7 +290,7 @@ public function behatCliAssertFailWithException(PyStringNode $message): void {
*
* @When :name environment variable is set to :value
*/
public function behatCliSetEnvironmentVariable($name, $value): void {
public function behatCliSetEnvironmentVariable(string $name, string $value): void {
$this->env[$name] = $value;
}

Expand Down Expand Up @@ -308,7 +329,7 @@ protected static function behatCliIsDebug(): string|false {
*
* @Given /^behat cli file wildcard "([^"]*)" should exist$/
*/
public function behatCliAssertFileShouldExist($wildcard): void {
public function behatCliAssertFileShouldExist(string $wildcard): void {
$wildcard = $this->workingDir . DIRECTORY_SEPARATOR . $wildcard;
$matches = glob($wildcard);

Expand All @@ -329,7 +350,7 @@ public function behatCliAssertFileShouldExist($wildcard): void {
*
* @Given /^behat screenshot file matching "([^"]*)" should contain:$/
*/
public function behatCliAssertFileShouldContain($wildcard, PyStringNode $text): void {
public function behatCliAssertFileShouldContain(string $wildcard, PyStringNode $text): void {
$wildcard = $this->workingDir . DIRECTORY_SEPARATOR . $wildcard;
$matches = glob($wildcard);

Expand Down Expand Up @@ -358,7 +379,7 @@ public function behatCliAssertFileShouldContain($wildcard, PyStringNode $text):
*
* @Given /^behat screenshot file matching "([^"]*)" should not contain:$/
*/
public function behatCliAssertFileNotShouldContain($wildcard, PyStringNode $text): void {
public function behatCliAssertFileNotShouldContain(string $wildcard, PyStringNode $text): void {
$wildcard = $this->workingDir . DIRECTORY_SEPARATOR . $wildcard;

$matches = glob($wildcard);
Expand All @@ -385,7 +406,7 @@ public function behatCliAssertFileNotShouldContain($wildcard, PyStringNode $text
*
* @Given /^behat cli file wildcard "([^"]*)" should not exist$/
*/
public function behatCliAssertFileShouldNotExist($wildcard): void {
public function behatCliAssertFileShouldNotExist(string $wildcard): void {
$wildcard = $this->workingDir . DIRECTORY_SEPARATOR . $wildcard;
$matches = glob($wildcard);

Expand Down
5 changes: 3 additions & 2 deletions tests/behat/features/html.feature
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
@html
Feature: HTML screenshots

Ensure that screenshots for HTML-base driver can be captured.
Expand All @@ -7,8 +8,8 @@ Feature: HTML screenshots
When I am on the screenshot test page
And the response status code should be 200
And I save screenshot
Then file wildcard "*.html.feature_9\.html" should exist
And file wildcard "*.html.feature_9\.png" should not exist
Then file wildcard "*.html.feature_10\.html" should exist
And file wildcard "*.html.feature_10\.png" should not exist

@phpserver
Scenario: Capture a screenshot with name using HTML-based driver
Expand Down
Loading

0 comments on commit 52f2864

Please sign in to comment.