Skip to content

Commit

Permalink
Added show_path configuration to extension to allow adding current …
Browse files Browse the repository at this point in the history
…path to screenshot files.
  • Loading branch information
richardgaunt committed Jan 11, 2025
1 parent fefd5b7 commit 5aaf760
Show file tree
Hide file tree
Showing 8 changed files with 174 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,14 @@ class ScreenshotContextInitializer implements ContextInitializer {
* File name pattern.
* @param string $filenamePatternFailed
* File name pattern failed.
* @param bool $showPath
* Show current path in screenshots.
* @param bool $needsPurging
* Check if need to actually purge.
*
* @codeCoverageIgnore
*/
public function __construct(protected string $dir, protected bool $fail, private readonly string $failPrefix, protected bool $purge, protected string $filenamePattern, protected string $filenamePatternFailed, protected bool $needsPurging = TRUE) {
public function __construct(protected string $dir, protected bool $fail, private readonly string $failPrefix, protected bool $purge, protected string $filenamePattern, protected string $filenamePatternFailed, protected bool $showPath = FALSE, protected bool $needsPurging = TRUE) {
}

/**
Expand All @@ -44,7 +46,7 @@ public function __construct(protected string $dir, protected bool $fail, private
public function initializeContext(Context $context): void {
if ($context instanceof ScreenshotAwareContextInterface) {
$dir = $this->resolveScreenshotDir();
$context->setScreenshotParameters($dir, $this->fail, $this->failPrefix, $this->filenamePattern, $this->filenamePatternFailed);
$context->setScreenshotParameters($dir, $this->fail, $this->failPrefix, $this->filenamePattern, $this->filenamePatternFailed, $this->showPath);
if ($this->shouldPurge() && $this->needsPurging) {
$this->purgeFilesInDir($dir);
$this->needsPurging = FALSE;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,11 @@ interface ScreenshotAwareContextInterface extends Context {
* File name pattern.
* @param string $filenamePatternFailed
* File name pattern failed.
* @param bool $showPath
* Show path in the screenshot.
*
* @return $this
*/
public function setScreenshotParameters(string $dir, bool $fail, string $failPrefix, string $filenamePattern, string $filenamePatternFailed): static;
public function setScreenshotParameters(string $dir, bool $fail, string $failPrefix, string $filenamePattern, string $filenamePatternFailed, bool $showPath): static;

}
66 changes: 62 additions & 4 deletions src/DrevOps/BehatScreenshotExtension/Context/ScreenshotContext.php
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,18 @@ class ScreenshotContext extends RawMinkContext implements ScreenshotAwareContext
*/
protected string $failPrefix = '';

/**
* Show the path in the screenshot.
*/
protected bool $showPath = FALSE;

/**
* Debug information to be outputted in screenshot.
*
* @var array<string, string>
*/
protected array $debugInformation = [];

/**
* Before step scope.
*/
Expand All @@ -57,12 +69,13 @@ class ScreenshotContext extends RawMinkContext implements ScreenshotAwareContext
/**
* {@inheritdoc}
*/
public function setScreenshotParameters(string $dir, bool $fail, string $failPrefix, string $filenamePattern, string $filenamePatternFailed): static {
public function setScreenshotParameters(string $dir, bool $fail, string $failPrefix, string $filenamePattern, string $filenamePatternFailed, bool $showPath): static {
$this->dir = $dir;
$this->fail = $fail;
$this->failPrefix = $failPrefix;
$this->filenamePattern = $filenamePattern;
$this->filenamePatternFailed = $filenamePatternFailed;
$this->showPath = $showPath;

return $this;
}
Expand Down Expand Up @@ -126,7 +139,7 @@ public function beforeStepInit(BeforeStepScope $scope): void {
*/
public function printLastResponseOnError(AfterStepScope $event): void {
if ($this->fail && !$event->getTestResult()->isPassed()) {
$this->iSaveScreenshot(TRUE);
$this->iSaveScreenshot(TRUE, NULL);
}
}

Expand All @@ -148,10 +161,9 @@ public function printLastResponseOnError(AfterStepScope $event): void {
* @When I save screenshot
*/
public function iSaveScreenshot(bool $fail = FALSE, ?string $filename = NULL): void {
$driver = $this->getSession()->getDriver();
$fileName = $this->makeFileName('html', $filename, $fail);
try {
$data = $driver->getContent();
$data = $this->getResponseHtml();
}
catch (DriverException) {
// Do not do anything if the driver does not have any content - most
Expand All @@ -165,6 +177,7 @@ public function iSaveScreenshot(bool $fail = FALSE, ?string $filename = NULL): v
// driver that is shipped with Behat, throw exception. For such drivers,
// screenshot stored as an HTML page (without referenced assets).
try {
$driver = $this->getSession()->getDriver();
$data = $driver->getScreenshot();
// Preserve filename, but change the extension - this is to group
// content and screenshot files together by name.
Expand Down Expand Up @@ -240,6 +253,51 @@ public function getCurrentTime(): int {
return time();
}

/**
* Gets the debug information for screenshot.
*
* @return string
* Information to prepend to screenshot
*/
protected function getDebugInformation(): string {
return implode("\n", array_map(
fn($key, $value): string => sprintf('%s: %s', $key, $value),
array_keys($this->debugInformation),
$this->debugInformation,
));
}

/**
* Gets last response content with any debug information.
*
* @return string
* Response content with debug information.
*
* @throws \Behat\Mink\Exception\DriverException
* @throws \Behat\Mink\Exception\UnsupportedDriverActionException
*/
protected function getResponseHtml(): string {
if ($this->showPath) {
$this->addDebugInformation('Current path', $this->getSession()->getCurrentUrl());
}

$driver = $this->getSession()->getDriver();

return $this->getDebugInformation() . $driver->getContent();
}

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

/**
* Save screenshot data into a file.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,10 @@ public function configure(ArrayNodeDefinition $builder): void {
->scalarNode('filenamePatternFailed')
->cannotBeEmpty()
->defaultValue('{datetime:U}.{fail_prefix}{feature_file}.feature_{step_line}.{ext}')
->end()
->scalarNode('show_path')
->cannotBeEmpty()
->defaultValue(FALSE)
->end();
// @formatter:on
// @phpcs:enable Drupal.WhiteSpace.ObjectOperatorIndent.Indent
Expand All @@ -92,6 +96,7 @@ public function load(ContainerBuilder $container, array $config): void {
$config['purge'],
$config['filenamePattern'],
$config['filenamePatternFailed'],
$config['show_path'],
]);
$definition->addTag(ContextExtension::INITIALIZER_TAG, ['priority' => 0]);
$container->setDefinition('drevops_screenshot.screenshot_context_initializer', $definition);
Expand Down
54 changes: 54 additions & 0 deletions tests/behat/bootstrap/BehatCliTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -321,6 +321,60 @@ public function behatCliAssertFileShouldExist($wildcard): void {
}
}

/**
* Checks whether a screenshot file matching pattern exists and contains text.
*
* @param string $wildcard
* File name with a wildcard.
* @param \Behat\Gherkin\Node\PyStringNode $text
* Text in the file.
*
* @Given /^behat screenshot file matching "([^"]*)" should contain:$/
*/
public function behatCliAssertFileShouldContain($wildcard, PyStringNode $text): void {
$wildcard = $this->workingDir . DIRECTORY_SEPARATOR . $wildcard;
$matches = glob($wildcard);
if (empty($matches)) {
throw new \Exception(sprintf("Unable to find screenshot file matching wildcard '%s'.", $wildcard));
}
$path = $matches[0];
$file_content = trim(file_get_contents($path));

// Normalize the line endings in the output.
if ("\n" !== PHP_EOL) {
$file_content = str_replace(PHP_EOL, "\n", $file_content);
}

Assert::assertStringContainsString($this->getExpectedOutput($text), $file_content);
}

/**
* Checks whether a screenshot file exists and does not contain given text.
*
* @param string $wildcard
* File name with a wildcard.
* @param \Behat\Gherkin\Node\PyStringNode $text
* Text in the file.
*
* @Given /^behat screenshot file matching "([^"]*)" should not contain:$/
*/
public function behatCliAssertFileNotShouldContain($wildcard, PyStringNode $text): void {
$wildcard = $this->workingDir . DIRECTORY_SEPARATOR . $wildcard;
$matches = glob($wildcard);
if (empty($matches)) {
throw new \Exception(sprintf("Unable to find screenshot file matching wildcard '%s'.", $wildcard));
}
$path = $matches[0];
$file_content = trim(file_get_contents($path));

// Normalize the line endings in the output.
if ("\n" !== PHP_EOL) {
$file_content = str_replace(PHP_EOL, "\n", $file_content);
}

Assert::assertStringNotContainsString($this->getExpectedOutput($text), $file_content);
}

/**
* Checks whether a file wildcard at provided path does not exist.
*
Expand Down
38 changes: 38 additions & 0 deletions tests/behat/features/behatcli_screenshot.feature
Original file line number Diff line number Diff line change
Expand Up @@ -260,3 +260,41 @@ Feature: Screenshot context
And behat cli file wildcard "screenshots_custom/*.failed_stub.feature_7\.html" should exist
# Assert that the file from the previous run is not present.
And behat cli file wildcard "screenshots_custom/*.failed_stub.feature_6\.html" should not exist

Scenario: Test Screenshot context with 'show_path' set to 'true' will output current path to screenshot files.
Given screenshot context behat configuration with value:
"""
DrevOps\BehatScreenshotExtension:
purge: true
show_path: true
"""
And scenario steps tagged with "@phpserver":
"""
When I am on the phpserver test page
And the response status code should be 404
"""
When I run "behat --no-colors --strict"
Then it should fail
And behat screenshot file matching "screenshots/*.failed_stub.feature_6\.html" should contain:
"""
Current path: http://0.0.0.0:8888/screenshot.html
"""

Scenario: Test Screenshot context with 'show_path' set to 'false' will not output current path to screenshot files.
Given screenshot context behat configuration with value:
"""
DrevOps\BehatScreenshotExtension:
purge: true
show_path: false
"""
And scenario steps tagged with "@phpserver":
"""
When I am on the phpserver test page
And the response status code should be 404
"""
When I run "behat --no-colors --strict"
Then it should fail
And behat screenshot file matching "screenshots/*.failed_stub.feature_6\.html" should not contain:
"""
Current path: http://0.0.0.0:8888/screenshot.html
"""
4 changes: 3 additions & 1 deletion tests/phpunit/Unit/BehatScreenshotExtensionTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ public function testLoad(): void {
'purge' => FALSE,
'filenamePattern' => '{datetime:U}.{feature_file}.feature_{step_line}.{ext}',
'filenamePatternFailed' => '{datetime:U}.{fail_prefix}{feature_file}.feature_{step_line}.{ext}',
'show_path' => FALSE,
];

$extension = new BehatScreenshotExtension();
Expand All @@ -48,6 +49,7 @@ public function testLoad(): void {
$config['purge'],
$config['filenamePattern'],
$config['filenamePatternFailed'],
$config['show_path'],
],
$definition->getArguments()
);
Expand All @@ -59,7 +61,7 @@ public function testConfigure(): void {
$extension = new BehatScreenshotExtension();
$extension->configure($builder);

$this->assertCount(6, $builder->getChildNodeDefinitions());
$this->assertCount(7, $builder->getChildNodeDefinitions());
}

}
6 changes: 5 additions & 1 deletion tests/phpunit/Unit/ScreenshotContextTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,8 @@ public function testPrintLastResponseOnError(): void {
TRUE,
'failed_',
'{datetime:U}.{feature_file}.feature_{step_line}.{ext}',
'{datetime:U}.{fail_prefix}{feature_file}.feature_{step_line}.{ext}'
'{datetime:U}.{fail_prefix}{feature_file}.feature_{step_line}.{ext}',
TRUE
);
$screenshot_context = $this->createPartialMock(ScreenshotContext::class, ['iSaveScreenshot']);
$screenshot_context->setScreenshotParameters(
Expand All @@ -95,6 +96,7 @@ public function testPrintLastResponseOnError(): void {
'failed_',
'{datetime:U}.{feature_file}.feature_{step_line}.{ext}',
'{datetime:U}.{fail_prefix}{feature_file}.feature_{step_line}.{ext}',
TRUE,
);
$screenshot_context->expects($this->once())->method('iSaveScreenshot');
$screenshot_context->printLastResponseOnError($scope);
Expand Down Expand Up @@ -161,6 +163,7 @@ public function testSaveScreenshotData(string $filename, string $data): void {
'failed_',
'{datetime:U}.{feature_file}.feature_{step_line}.{ext}',
'{datetime:U}.{fail_prefix}{feature_file}.feature_{step_line}.{ext}',
TRUE,
);
$screenshot_context_reflection = new \ReflectionClass($screenshot_context);
$method = $screenshot_context_reflection->getMethod('saveScreenshotData');
Expand Down Expand Up @@ -259,6 +262,7 @@ public function testMakeFileName(
$fail_prefix,
$file_name_pattern,
$file_name_pattern_failed,
TRUE,
);

$screenshot_context_reflection = new \ReflectionClass($screenshot_context);
Expand Down

0 comments on commit 5aaf760

Please sign in to comment.