Skip to content

Commit 0285e30

Browse files
authored
Merge pull request #280 from wp-cli/copilot/improve-behat-output-diffs
2 parents ef51980 + 4941f64 commit 0285e30

File tree

2 files changed

+79
-8
lines changed

2 files changed

+79
-8
lines changed

src/Context/Support.php

Lines changed: 32 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,12 @@ protected function check_string( $output, $expected, $action, $message = false,
120122
if ( false === $message ) {
121123
$message = $output;
122124
}
125+
126+
$diff = $this->generate_diff( $expected, rtrim( $output, "\n" ) );
127+
if ( ! empty( $diff ) ) {
128+
$message .= "\n\n" . $diff;
129+
}
130+
123131
throw new Exception( $message );
124132
}
125133
}
@@ -133,15 +141,21 @@ protected function check_string( $output, $expected, $action, $message = false,
133141
protected function compare_tables( $expected_rows, $actual_rows, $output ): void {
134142
// The first row is the header and must be present.
135143
if ( $expected_rows[0] !== $actual_rows[0] ) {
136-
throw new Exception( $output );
144+
$expected_table = implode( "\n", $expected_rows );
145+
$actual_table = implode( "\n", $actual_rows );
146+
$diff = $this->generate_diff( $expected_table, $actual_table );
147+
throw new Exception( $output . "\n\n" . $diff );
137148
}
138149

139150
unset( $actual_rows[0] );
140151
unset( $expected_rows[0] );
141152

142153
$missing_rows = array_diff( $expected_rows, $actual_rows );
143154
if ( ! empty( $missing_rows ) ) {
144-
throw new Exception( $output );
155+
$expected_table = implode( "\n", $expected_rows );
156+
$actual_table = implode( "\n", $actual_rows );
157+
$diff = $this->generate_diff( $expected_table, $actual_table );
158+
throw new Exception( $output . "\n\n" . $diff );
145159
}
146160
}
147161

@@ -290,4 +304,20 @@ protected function check_that_yaml_string_contains_yaml_string( $actual_yaml, $e
290304

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

src/Context/ThenStepDefinitions.php

Lines changed: 47 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -215,7 +215,21 @@ 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_decoded = json_decode( $expected );
221+
$actual_decoded = json_decode( $output );
222+
if ( null !== $expected_decoded && null !== $actual_decoded ) {
223+
$expected_json = json_encode( $expected_decoded, JSON_PRETTY_PRINT );
224+
$actual_json = json_encode( $actual_decoded, JSON_PRETTY_PRINT );
225+
if ( false !== $expected_json && false !== $actual_json ) {
226+
$diff = $this->generate_diff( $expected_json, $actual_json );
227+
if ( ! empty( $diff ) ) {
228+
$message .= "\n\n" . $diff;
229+
}
230+
}
231+
}
232+
throw new Exception( $message );
219233
}
220234
}
221235

@@ -246,7 +260,19 @@ public function then_stdout_should_be_a_json_array_containing( PyStringNode $exp
246260

247261
$missing = array_diff( $expected_values, $actual_values );
248262
if ( ! empty( $missing ) ) {
249-
throw new Exception( $this->result );
263+
$message = (string) $this->result;
264+
// Pretty print JSON arrays for better diff readability.
265+
if ( null !== $expected_values && null !== $actual_values ) {
266+
$expected_json = json_encode( $expected_values, JSON_PRETTY_PRINT );
267+
$actual_json = json_encode( $actual_values, JSON_PRETTY_PRINT );
268+
if ( false !== $expected_json && false !== $actual_json ) {
269+
$diff = $this->generate_diff( $expected_json, $actual_json );
270+
if ( ! empty( $diff ) ) {
271+
$message .= "\n\n" . $diff;
272+
}
273+
}
274+
}
275+
throw new Exception( $message );
250276
}
251277
}
252278

@@ -269,19 +295,29 @@ public function then_stdout_should_be_csv_containing( TableNode $expected ): voi
269295
$output = $this->result->stdout;
270296

271297
$expected_rows = $expected->getRows();
272-
foreach ( $expected as &$row ) {
298+
foreach ( $expected_rows as &$row ) {
273299
foreach ( $row as &$value ) {
274300
$value = $this->replace_variables( $value );
275301
}
276302
}
277303

278304
if ( ! $this->check_that_csv_string_contains_values( $output, $expected_rows ) ) {
279-
throw new Exception( $this->result );
305+
$message = (string) $this->result;
306+
// Convert expected rows to CSV format for diff.
307+
$expected_csv = '';
308+
foreach ( $expected_rows as $row ) {
309+
$expected_csv .= implode( ',', array_map( 'trim', $row ) ) . "\n";
310+
}
311+
$diff = $this->generate_diff( trim( $expected_csv ), trim( $output ) );
312+
if ( ! empty( $diff ) ) {
313+
$message .= "\n\n" . $diff;
314+
}
315+
throw new Exception( $message );
280316
}
281317
}
282318

283319
/**
284-
* Expect STDOUT to be YAML containig certain content.
320+
* Expect STDOUT to be YAML containing certain content.
285321
*
286322
* ```
287323
* Scenario: My example scenario
@@ -303,7 +339,12 @@ public function then_stdout_should_be_yaml_containing( PyStringNode $expected ):
303339
$expected = $this->replace_variables( (string) $expected );
304340

305341
if ( ! $this->check_that_yaml_string_contains_yaml_string( $output, $expected ) ) {
306-
throw new Exception( $this->result );
342+
$message = (string) $this->result;
343+
$diff = $this->generate_diff( $expected, $output );
344+
if ( ! empty( $diff ) ) {
345+
$message .= "\n\n" . $diff;
346+
}
347+
throw new Exception( $message );
307348
}
308349
}
309350

0 commit comments

Comments
 (0)