Skip to content

Commit 348f0c4

Browse files
Copilotswissspidy
andcommitted
Add diff output for failed Behat comparisons
Co-authored-by: swissspidy <[email protected]>
1 parent 404a1f4 commit 348f0c4

File tree

3 files changed

+75
-6
lines changed

3 files changed

+75
-6
lines changed

composer.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
"phpstan/phpstan-deprecation-rules": "^1.2 || ^2.0",
2121
"phpstan/phpstan-phpunit": "^1.4 || ^2.0",
2222
"phpstan/phpstan-strict-rules": "^1.6 || ^2.0",
23+
"sebastian/diff": "^4.0 || ^5.0 || ^6.0 || ^7.0",
2324
"swissspidy/phpstan-no-private": "^0.2.1 || ^1.0",
2425
"szepeviktor/phpstan-wordpress": "^v1.3.5",
2526
"wp-cli/config-command": "^1 || ^2",

src/Context/Support.php

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
use Behat\Behat\Exception\PendingException;
99
use Exception;
1010
use Mustangostang\Spyc;
11+
use SebastianBergmann\Diff\Differ;
12+
use SebastianBergmann\Diff\Output\UnifiedDiffOutputBuilder;
1113

1214
trait Support {
1315

@@ -120,6 +122,15 @@ protected function check_string( $output, $expected, $action, $message = false,
120122
if ( false === $message ) {
121123
$message = $output;
122124
}
125+
126+
// Add diff for 'be' comparisons to show exact differences.
127+
if ( 'be' === $action ) {
128+
$diff = $this->generate_diff( $expected, rtrim( $output, "\n" ) );
129+
if ( ! empty( $diff ) ) {
130+
$message .= "\n\n" . $diff;
131+
}
132+
}
133+
123134
throw new Exception( $message );
124135
}
125136
}
@@ -133,15 +144,21 @@ protected function check_string( $output, $expected, $action, $message = false,
133144
protected function compare_tables( $expected_rows, $actual_rows, $output ): void {
134145
// The first row is the header and must be present.
135146
if ( $expected_rows[0] !== $actual_rows[0] ) {
136-
throw new Exception( $output );
147+
$expected_table = implode( "\n", $expected_rows );
148+
$actual_table = implode( "\n", $actual_rows );
149+
$diff = $this->generate_diff( $expected_table, $actual_table );
150+
throw new Exception( $output . "\n\n" . $diff );
137151
}
138152

139153
unset( $actual_rows[0] );
140154
unset( $expected_rows[0] );
141155

142156
$missing_rows = array_diff( $expected_rows, $actual_rows );
143157
if ( ! empty( $missing_rows ) ) {
144-
throw new Exception( $output );
158+
$expected_table = implode( "\n", $expected_rows );
159+
$actual_table = implode( "\n", $actual_rows );
160+
$diff = $this->generate_diff( $expected_table, $actual_table );
161+
throw new Exception( $output . "\n\n" . $diff );
145162
}
146163
}
147164

@@ -290,4 +307,20 @@ protected function check_that_yaml_string_contains_yaml_string( $actual_yaml, $e
290307

291308
return $this->compare_contents( $expected_value, $actual_value );
292309
}
310+
311+
/**
312+
* Generate a unified diff between two strings.
313+
*
314+
* @param string $expected The expected string.
315+
* @param string $actual The actual string.
316+
* @return string The unified diff output.
317+
*/
318+
protected function generate_diff( $expected, $actual ) {
319+
$builder = new UnifiedDiffOutputBuilder(
320+
"--- Expected\n+++ Actual\n",
321+
false
322+
);
323+
$differ = new Differ( $builder );
324+
return $differ->diff( $expected, $actual );
325+
}
293326
}

src/Context/ThenStepDefinitions.php

Lines changed: 39 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -215,7 +215,17 @@ public function then_stdout_should_be_json_containing( PyStringNode $expected ):
215215
$expected = $this->replace_variables( (string) $expected );
216216

217217
if ( ! $this->check_that_json_string_contains_json_string( $output, $expected ) ) {
218-
throw new Exception( $this->result );
218+
$message = (string) $this->result;
219+
// Pretty print JSON for better diff readability.
220+
$expected_json = json_encode( json_decode( $expected ), JSON_PRETTY_PRINT );
221+
$actual_json = json_encode( json_decode( $output ), JSON_PRETTY_PRINT );
222+
if ( $expected_json && $actual_json ) {
223+
$diff = $this->generate_diff( $expected_json, $actual_json );
224+
if ( ! empty( $diff ) ) {
225+
$message .= "\n\n" . $diff;
226+
}
227+
}
228+
throw new Exception( $message );
219229
}
220230
}
221231

@@ -246,7 +256,17 @@ public function then_stdout_should_be_a_json_array_containing( PyStringNode $exp
246256

247257
$missing = array_diff( $expected_values, $actual_values );
248258
if ( ! empty( $missing ) ) {
249-
throw new Exception( $this->result );
259+
$message = (string) $this->result;
260+
// Pretty print JSON arrays for better diff readability.
261+
$expected_json = json_encode( $expected_values, JSON_PRETTY_PRINT );
262+
$actual_json = json_encode( $actual_values, JSON_PRETTY_PRINT );
263+
if ( $expected_json && $actual_json ) {
264+
$diff = $this->generate_diff( $expected_json, $actual_json );
265+
if ( ! empty( $diff ) ) {
266+
$message .= "\n\n" . $diff;
267+
}
268+
}
269+
throw new Exception( $message );
250270
}
251271
}
252272

@@ -276,7 +296,17 @@ public function then_stdout_should_be_csv_containing( TableNode $expected ): voi
276296
}
277297

278298
if ( ! $this->check_that_csv_string_contains_values( $output, $expected_rows ) ) {
279-
throw new Exception( $this->result );
299+
$message = (string) $this->result;
300+
// Convert expected rows to CSV format for diff.
301+
$expected_csv = '';
302+
foreach ( $expected_rows as $row ) {
303+
$expected_csv .= implode( ',', array_map( 'trim', $row ) ) . "\n";
304+
}
305+
$diff = $this->generate_diff( trim( $expected_csv ), trim( $output ) );
306+
if ( ! empty( $diff ) ) {
307+
$message .= "\n\n" . $diff;
308+
}
309+
throw new Exception( $message );
280310
}
281311
}
282312

@@ -303,7 +333,12 @@ public function then_stdout_should_be_yaml_containing( PyStringNode $expected ):
303333
$expected = $this->replace_variables( (string) $expected );
304334

305335
if ( ! $this->check_that_yaml_string_contains_yaml_string( $output, $expected ) ) {
306-
throw new Exception( $this->result );
336+
$message = (string) $this->result;
337+
$diff = $this->generate_diff( $expected, $output );
338+
if ( ! empty( $diff ) ) {
339+
$message .= "\n\n" . $diff;
340+
}
341+
throw new Exception( $message );
307342
}
308343
}
309344

0 commit comments

Comments
 (0)