Skip to content

Commit f248e5a

Browse files
committed
[#165] Added on_every_step option and @screenshots tag support to capture screenshots on each step.
1 parent 59faa49 commit f248e5a

11 files changed

Lines changed: 106 additions & 3 deletions

README.md

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,12 +135,41 @@ default:
135135
always_fullscreen: true
136136
```
137137
138+
### Capturing Screenshots After Every Step
139+
140+
To automatically capture a screenshot after every step, you can either:
141+
142+
1. **Enable globally** in configuration:
143+
144+
```yaml
145+
default:
146+
extensions:
147+
DrevOps\BehatScreenshotExtension:
148+
on_every_step: true
149+
```
150+
151+
2. **Enable per-scenario** using the `@screenshots` tag:
152+
153+
```gherkin
154+
@screenshots
155+
Scenario: My scenario with automatic screenshots
156+
Given I am on "http://example.com"
157+
When I click "Login"
158+
Then I should see "Welcome"
159+
# Screenshots will be captured after each of these steps
160+
```
161+
162+
The `@screenshots` tag takes precedence over the global configuration, allowing you to enable this feature for specific scenarios even when it's disabled globally.
163+
164+
**Note**: When both `on_every_step` and `on_failed` are enabled, only one screenshot is captured for failed steps (the failed screenshot) to avoid duplicates.
165+
138166
## Options
139167

140168
| Name | Default value | Description |
141169
|---------------------------|------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
142170
| `dir` | `%paths.base%/screenshots` | Path to directory to save screenshots. Directory structure will be created if the directory does not exist. Override with `BEHAT_SCREENSHOT_DIR` env var. |
143171
| `on_failed` | `true` | Capture screenshot on failed test. |
172+
| `on_every_step` | `false` | Automatically capture screenshots after every step. Can be enabled globally via config or per-scenario using the `@screenshots` tag. Only captures on passed steps to avoid duplicates with `on_failed`. |
144173
| `purge` | `false` | Remove all files from the screenshots directory on each test run. Useful during debugging of tests. |
145174
| `always_fullscreen` | `false` | Always use fullscreen screenshot capture for all screenshot steps, including regular screenshot steps. When enabled, all `I save screenshot` steps will behave like `I save fullscreen screenshot`. |
146175
| `fullscreen_algorithm` | `resize` | Algorithm to use for fullscreen screenshots. Options: `resize` (temporarily resizes browser window to full page height) or `stitch` (captures multiple screenshots while scrolling and stitches them together). The stitch algorithm requires GD extension but produces higher quality results. |

behat.yml.dist

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ default:
88
DrevOps\BehatScreenshotExtension:
99
dir: %paths.base%/screenshots
1010
on_failed: true
11+
on_every_step: false # Capture screenshot after every step
1112
purge: false
1213
always_fullscreen: false
1314
fullscreen_algorithm: resize # 'stitch' (only if GD ext available) or 'resize'

src/DrevOps/BehatScreenshotExtension/Context/Initializer/ScreenshotContextInitializer.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ class ScreenshotContextInitializer implements ContextInitializer {
3333
* Purge dir before start script.
3434
* @param bool $alwaysFullscreen
3535
* Always take fullscreen screenshots.
36+
* @param bool $onEveryStep
37+
* Capture screenshot after every step.
3638
* @param string $fullscreenAlgorithm
3739
* Algorithm to use for fullscreen screenshots ('stitch' or 'resize').
3840
* @param string $filenamePattern
@@ -50,6 +52,7 @@ public function __construct(
5052
private readonly string $failedPrefix,
5153
protected bool $purge,
5254
protected bool $alwaysFullscreen,
55+
protected bool $onEveryStep,
5356
protected string $fullscreenAlgorithm,
5457
protected string $filenamePattern,
5558
protected string $filenamePatternFailed,
@@ -77,6 +80,7 @@ public function initializeContext(Context $context): void {
7780
$this->onFailed,
7881
$this->failedPrefix,
7982
$this->alwaysFullscreen,
83+
$this->onEveryStep,
8084
$this->fullscreenAlgorithm,
8185
$this->filenamePattern,
8286
$this->filenamePatternFailed,

src/DrevOps/BehatScreenshotExtension/Context/ScreenshotAwareContextInterface.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ interface ScreenshotAwareContextInterface extends Context {
2222
* File name prefix for a failed test.
2323
* @param bool $always_fullscreen
2424
* Always take fullscreen screenshots.
25+
* @param bool $on_every_step
26+
* Capture screenshot after every step.
2527
* @param string $fullscreen_algorithm
2628
* Algorithm to use for fullscreen screenshots ('stitch' or 'resize').
2729
* @param string $filename_pattern
@@ -33,7 +35,7 @@ interface ScreenshotAwareContextInterface extends Context {
3335
*
3436
* @return $this
3537
*/
36-
public function setScreenshotParameters(string $dir, bool $on_failed, string $failed_prefix, bool $always_fullscreen, string $fullscreen_algorithm, string $filename_pattern, string $filename_pattern_failed, array $info_types): static;
38+
public function setScreenshotParameters(string $dir, bool $on_failed, string $failed_prefix, bool $always_fullscreen, bool $on_every_step, string $fullscreen_algorithm, string $filename_pattern, string $filename_pattern_failed, array $info_types): static;
3739

3840
/**
3941
* Save screenshot content into a file.

src/DrevOps/BehatScreenshotExtension/Context/ScreenshotContext.php

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,16 @@ class ScreenshotContext extends RawMinkContext implements ScreenshotAwareContext
3333
*/
3434
protected bool $alwaysFullscreen = FALSE;
3535

36+
/**
37+
* Capture screenshot after every step.
38+
*/
39+
protected bool $onEveryStep = FALSE;
40+
41+
/**
42+
* Whether the current scenario has the @screenshots tag.
43+
*/
44+
protected bool $scenarioHasScreenshotsTag = FALSE;
45+
3646
/**
3747
* Algorithm to use for fullscreen screenshots ('stitch' or 'resize').
3848
*/
@@ -75,11 +85,12 @@ class ScreenshotContext extends RawMinkContext implements ScreenshotAwareContext
7585
/**
7686
* {@inheritdoc}
7787
*/
78-
public function setScreenshotParameters(string $dir, bool $on_failed, string $failed_prefix, bool $always_fullscreen, string $fullscreen_algorithm, string $filename_pattern, string $filename_pattern_failed, array $info_types): static {
88+
public function setScreenshotParameters(string $dir, bool $on_failed, string $failed_prefix, bool $always_fullscreen, bool $on_every_step, string $fullscreen_algorithm, string $filename_pattern, string $filename_pattern_failed, array $info_types): static {
7989
$this->dir = $dir;
8090
$this->onFailed = $on_failed;
8191
$this->failedPrefix = $failed_prefix;
8292
$this->alwaysFullscreen = $always_fullscreen;
93+
$this->onEveryStep = $on_every_step;
8394
$this->fullscreenAlgorithm = $fullscreen_algorithm;
8495
$this->filenamePattern = $filename_pattern;
8596
$this->filenamePatternFailed = $filename_pattern_failed;
@@ -98,6 +109,18 @@ public function getDir(): string {
98109
return $this->dir;
99110
}
100111

112+
/**
113+
* Check if scenario has @screenshots tag.
114+
*
115+
* @param \Behat\Behat\Hook\Scope\BeforeScenarioScope $scope
116+
* Scenario scope.
117+
*
118+
* @BeforeScenario
119+
*/
120+
public function beforeScenarioCheckScreenshotsTag(BeforeScenarioScope $scope): void {
121+
$this->scenarioHasScreenshotsTag = $scope->getScenario()->hasTag('screenshots');
122+
}
123+
101124
/**
102125
* Init values required for screenshots.
103126
*
@@ -155,6 +178,28 @@ public function printLastResponseOnError(AfterStepScope $event): void {
155178
}
156179
}
157180

181+
/**
182+
* Capture screenshot after every step when enabled.
183+
*
184+
* @param \Behat\Behat\Hook\Scope\AfterStepScope $event
185+
* After step scope event.
186+
*
187+
* @throws \Behat\Mink\Exception\DriverException
188+
* @throws \Behat\Mink\Exception\UnsupportedDriverActionException
189+
*
190+
* @AfterStep
191+
*/
192+
public function captureScreenshotAfterStep(AfterStepScope $event): void {
193+
// Only capture if:
194+
// 1. Global config is enabled OR scenario has the @screenshots tag
195+
// 2. Step passed (we don't want duplicates with on_failed)
196+
if (($this->onEveryStep || $this->scenarioHasScreenshotsTag) && $event->getTestResult()->isPassed()) {
197+
$this->screenshot([
198+
'fullscreen' => $this->alwaysFullscreen,
199+
]);
200+
}
201+
}
202+
158203
/**
159204
* {@inheritdoc}
160205
*

src/DrevOps/BehatScreenshotExtension/ServiceContainer/BehatScreenshotExtension.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,10 @@ public function configure(ArrayNodeDefinition $builder): void {
7373
->cannotBeEmpty()
7474
->defaultValue(FALSE)
7575
->end()
76+
->scalarNode('on_every_step')
77+
->cannotBeEmpty()
78+
->defaultValue(FALSE)
79+
->end()
7680
->enumNode('fullscreen_algorithm')
7781
->values(['stitch', 'resize'])
7882
->defaultValue('resize')
@@ -103,6 +107,7 @@ public function load(ContainerBuilder $container, array $config): void {
103107
$config['failed_prefix'],
104108
$config['purge'],
105109
$config['always_fullscreen'],
110+
$config['on_every_step'],
106111
$config['fullscreen_algorithm'],
107112
$config['filename_pattern'],
108113
$config['filename_pattern_failed'],

tests/phpunit/Unit/BehatScreenshotExtensionTest.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ public function testLoad(): void {
3030
'failed_prefix' => 'failed_',
3131
'purge' => FALSE,
3232
'always_fullscreen' => FALSE,
33+
'on_every_step' => FALSE,
3334
'fullscreen_algorithm' => 'resize',
3435
'filename_pattern' => '{datetime:U}.{feature_file}.feature_{step_line}.{ext}',
3536
'filename_pattern_failed' => '{datetime:U}.{failed_prefix}{feature_file}.feature_{step_line}.{ext}',
@@ -50,6 +51,7 @@ public function testLoad(): void {
5051
$config['failed_prefix'],
5152
$config['purge'],
5253
$config['always_fullscreen'],
54+
$config['on_every_step'],
5355
$config['fullscreen_algorithm'],
5456
$config['filename_pattern'],
5557
$config['filename_pattern_failed'],
@@ -65,7 +67,7 @@ public function testConfigure(): void {
6567
$extension = new BehatScreenshotExtension();
6668
$extension->configure($builder);
6769

68-
$this->assertCount(9, $builder->getChildNodeDefinitions());
70+
$this->assertCount(10, $builder->getChildNodeDefinitions());
6971
}
7072

7173
}

tests/phpunit/Unit/Context/Initializer/ScreenshotContextInitializerTest.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ public function testInitializeContextNonScreenshotAware(): void {
3030
'failed_',
3131
TRUE,
3232
TRUE,
33+
FALSE,
3334
'resize',
3435
'{datetime:U}.{ext}',
3536
'{datetime:U}.{failed_prefix}{ext}',
@@ -55,6 +56,7 @@ public function testInitializeContext(): void {
5556
TRUE,
5657
'failed_',
5758
TRUE,
59+
FALSE,
5860
'resize',
5961
'{datetime:U}.{ext}',
6062
'{datetime:U}.{failed_prefix}{ext}',
@@ -68,6 +70,7 @@ public function testInitializeContext(): void {
6870
// don't purge.
6971
FALSE,
7072
TRUE,
73+
FALSE,
7174
'resize',
7275
'{datetime:U}.{ext}',
7376
'{datetime:U}.{failed_prefix}{ext}',
@@ -97,6 +100,7 @@ public function testInitializeContextWithEnv(): void {
97100
TRUE,
98101
'failed_',
99102
TRUE,
103+
FALSE,
100104
'resize',
101105
'{datetime:U}.{ext}',
102106
'{datetime:U}.{failed_prefix}{ext}',
@@ -133,6 +137,7 @@ public function testInitializeContextWithEnv(): void {
133137
// Not used due to ENV override.
134138
FALSE,
135139
TRUE,
140+
FALSE,
136141
'resize',
137142
'{datetime:U}.{ext}',
138143
'{datetime:U}.{failed_prefix}{ext}',
@@ -155,6 +160,7 @@ public function testInitializeContextWithEnv(): void {
155160
TRUE,
156161
'failed_',
157162
TRUE,
163+
FALSE,
158164
'resize',
159165
'{datetime:U}.{ext}',
160166
'{datetime:U}.{failed_prefix}{ext}',

tests/phpunit/Unit/ScreenshotContextInfoTest.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ public function testCompileInfo(array $info_types, array $expected_keys): void {
6767
TRUE,
6868
'failed_',
6969
FALSE,
70+
FALSE,
7071
'stitch',
7172
'{datetime:U}.test.{ext}',
7273
'{datetime:U}.{failed_prefix}test.{ext}',
@@ -112,6 +113,7 @@ public function testCompileInfoUrlException(): void {
112113
TRUE,
113114
'failed_',
114115
FALSE,
116+
FALSE,
115117
'stitch',
116118
'{datetime:U}.test.{ext}',
117119
'{datetime:U}.{failed_prefix}test.{ext}',
@@ -214,6 +216,7 @@ public function testMakeFileNameWithHostReplacement(): void {
214216
FALSE,
215217
'failed_',
216218
FALSE,
219+
FALSE,
217220
'stitch',
218221
'{url}.{ext}',
219222
'{failed_prefix}{url}.{ext}',

tests/phpunit/Unit/ScreenshotContextResizeTest.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,7 @@ public function testGetScreenshotFullscreenUsingResizeAlgorithm(): void {
145145
TRUE,
146146
'failed_',
147147
TRUE,
148+
FALSE,
148149
'resize',
149150
'{datetime:U}.{feature_file}.feature_{step_line}.{ext}',
150151
'{datetime:U}.{failed_prefix}{feature_file}.feature_{step_line}.{ext}',
@@ -201,6 +202,7 @@ public function testScreenshotWithResizeAlgorithm(): void {
201202
'failed_',
202203
// always_fullscreen = TRUE.
203204
TRUE,
205+
FALSE,
204206
'resize',
205207
'{datetime:U}.{feature_file}.feature_{step_line}.{ext}',
206208
'{datetime:U}.{failed_prefix}{feature_file}.feature_{step_line}.{ext}',

0 commit comments

Comments
 (0)