Skip to content

Commit f374d01

Browse files
committed
Support diff toolwindow in IntelliJ IDEA
For the toolwindow are required the "expected" and "actual" attributes in the test-failed-output message. This fixes issue: #2607
1 parent 2fa32d2 commit f374d01

File tree

3 files changed

+120
-1
lines changed

3 files changed

+120
-1
lines changed

CHANGELOG.md

+2
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
1010
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
1111

1212
## [Unreleased]
13+
### Changed
14+
- [JUnit] Support comparison of expected and actual values in IntelliJ IDEA ([#2607](https://github.com/cucumber/cucumber-jvm/issues/2607)
1315

1416
## [7.6.0] - 2022-08-08
1517
### Changed

cucumber-core/src/main/java/io/cucumber/core/plugin/TeamCityPlugin.java

+73-1
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,9 @@ public class TeamCityPlugin implements EventListener {
7777
+ "[testFinished timestamp = '%s' duration = '%s' name = '%s']";
7878
private static final String TEMPLATE_TEST_FAILED = TEAMCITY_PREFIX
7979
+ "[testFailed timestamp = '%s' duration = '%s' message = '%s' details = '%s' name = '%s']";
80+
81+
private static final String TEMPLATE_TEST_COMPARISON_FAILED = TEAMCITY_PREFIX
82+
+ "[testFailed timestamp = '%s' duration = '%s' message = '%s' details = '%s' expected = '%s' actual = '%s' name = '%s']";
8083
private static final String TEMPLATE_TEST_IGNORED = TEAMCITY_PREFIX
8184
+ "[testIgnored timestamp = '%s' duration = '%s' message = '%s' name = '%s']";
8285

@@ -101,6 +104,11 @@ public class TeamCityPlugin implements EventListener {
101104
private static final Pattern ANNOTATION_GLUE_CODE_LOCATION_PATTERN = Pattern.compile("^(.*)\\.(.*)\\([^:]*\\)");
102105
private static final Pattern LAMBDA_GLUE_CODE_LOCATION_PATTERN = Pattern.compile("^(.*)\\.(.*)\\(.*:.*\\)");
103106

107+
private static final Pattern[] COMPARE_PATTERNS = new Pattern[] {
108+
Pattern.compile("^expected:\\s*(.*)\\s+but was:\\s*(.*)",
109+
Pattern.DOTALL | Pattern.CASE_INSENSITIVE),
110+
};
111+
104112
private final PrintStream out;
105113
private final List<SnippetsSuggestedEvent> suggestions = new ArrayList<>();
106114
private final Map<URI, Collection<Node>> parsedTestSources = new HashMap<>();
@@ -281,7 +289,16 @@ private void printTestStepFinished(TestStepFinished event) {
281289
case AMBIGUOUS:
282290
case FAILED: {
283291
String details = printStackTrace(error);
284-
print(TEMPLATE_TEST_FAILED, timeStamp, duration, "Step failed", details, name);
292+
String message = error.getMessage();
293+
ComparisonFailureData comparisonFailureData = message != null
294+
? createExceptionNotification(message.trim())
295+
: null;
296+
if (comparisonFailureData != null) {
297+
print(TEMPLATE_TEST_COMPARISON_FAILED, timeStamp, duration, "Step failed", details,
298+
comparisonFailureData.getExpected(), comparisonFailureData.getActual(), name);
299+
} else {
300+
print(TEMPLATE_TEST_FAILED, timeStamp, duration, "Step failed", details, name);
301+
}
285302
break;
286303
}
287304
default:
@@ -290,6 +307,26 @@ private void printTestStepFinished(TestStepFinished event) {
290307
print(TEMPLATE_TEST_FINISHED, timeStamp, duration, name);
291308
}
292309

310+
private static ComparisonFailureData createExceptionNotification(String message, Pattern pattern) {
311+
final Matcher matcher = pattern.matcher(message);
312+
if (matcher.find()) {
313+
String expected = matcher.group(1);
314+
String actual = matcher.group(2);
315+
return new ComparisonFailureData(expected, actual);
316+
}
317+
return null;
318+
}
319+
320+
private static ComparisonFailureData createExceptionNotification(String message) {
321+
for (Pattern pattern : COMPARE_PATTERNS) {
322+
ComparisonFailureData result = createExceptionNotification(message, pattern);
323+
if (result != null) {
324+
return result;
325+
}
326+
}
327+
return null;
328+
}
329+
293330
private String extractName(TestStep step) {
294331
if (step instanceof PickleStepTestStep) {
295332
PickleStepTestStep pickleStepTestStep = (PickleStepTestStep) step;
@@ -420,4 +457,39 @@ private String escape(String source) {
420457
.replace("]", "|]");
421458
}
422459

460+
private static class ComparisonFailureData {
461+
private final String expected;
462+
463+
private final String actual;
464+
465+
ComparisonFailureData(String expected, String actual) {
466+
if (isWrappedWith(expected, '<', '>') && isWrappedWith(actual, '<', '>')) {
467+
expected = unwrap(expected);
468+
actual = unwrap(actual);
469+
}
470+
if (isWrappedWith(expected, '[', ']') && isWrappedWith(actual, '[', ']')) {
471+
expected = unwrap(expected);
472+
actual = unwrap(actual);
473+
}
474+
this.expected = expected;
475+
this.actual = actual;
476+
}
477+
478+
private static boolean isWrappedWith(String text, char startChar, char endChar) {
479+
return !text.isEmpty() &&
480+
text.charAt(0) == startChar && text.charAt(text.length() - 1) == endChar;
481+
}
482+
483+
private static String unwrap(String text) {
484+
return text.substring(1, text.length() - 1);
485+
}
486+
487+
public String getExpected() {
488+
return expected;
489+
}
490+
491+
public String getActual() {
492+
return actual;
493+
}
494+
}
423495
}

cucumber-core/src/test/java/io/cucumber/core/plugin/TeamCityPluginTest.java

+45
Original file line numberDiff line numberDiff line change
@@ -342,4 +342,49 @@ void should_print_system_failure_for_failed_hooks() {
342342
"##teamcity[testFinished timestamp = '1970-01-01T12:00:00.000+0000' name = 'Before All/After All']"));
343343
}
344344

345+
@Test
346+
void should_print_comparison_failure_for_failed_assert_equal() {
347+
Feature feature = TestFeatureParser.parse("path/test.feature", "" +
348+
"Feature: feature name\n" +
349+
" Scenario: scenario name\n" +
350+
" Given first step\n");
351+
352+
ByteArrayOutputStream out = new ByteArrayOutputStream();
353+
Runtime.builder()
354+
.withFeatureSupplier(new StubFeatureSupplier(feature))
355+
.withAdditionalPlugins(new TeamCityPlugin(new PrintStream(out)))
356+
.withEventBus(new TimeServiceEventBus(fixed(EPOCH, of("UTC")), UUID::randomUUID))
357+
.withBackendSupplier(new StubBackendSupplier(
358+
emptyList(),
359+
singletonList(
360+
new StubStepDefinition("first step", new RuntimeException("expected: <1> but was: <2>"))),
361+
emptyList()))
362+
.build()
363+
.run();
364+
365+
assertThat(out, bytesContainsString("expected = '1' actual = '2' name = 'first step']"));
366+
}
367+
368+
@Test
369+
void should_print_comparison_failure_for_failed_assert_equal_for_strings() {
370+
Feature feature = TestFeatureParser.parse("path/test.feature", "" +
371+
"Feature: feature name\n" +
372+
" Scenario: scenario name\n" +
373+
" Given first step\n");
374+
375+
ByteArrayOutputStream out = new ByteArrayOutputStream();
376+
Runtime.builder()
377+
.withFeatureSupplier(new StubFeatureSupplier(feature))
378+
.withAdditionalPlugins(new TeamCityPlugin(new PrintStream(out)))
379+
.withEventBus(new TimeServiceEventBus(fixed(EPOCH, of("UTC")), UUID::randomUUID))
380+
.withBackendSupplier(new StubBackendSupplier(
381+
emptyList(),
382+
singletonList(new StubStepDefinition("first step",
383+
new RuntimeException("expected: <[one value]> but was: <[another value]>"))),
384+
emptyList()))
385+
.build()
386+
.run();
387+
388+
assertThat(out, bytesContainsString("expected = 'one value' actual = 'another value' name = 'first step']"));
389+
}
345390
}

0 commit comments

Comments
 (0)