Skip to content

Commit 2f5ad8d

Browse files
committed
hook exceptions
1 parent 9b8b807 commit 2f5ad8d

15 files changed

Lines changed: 535 additions & 88 deletions

File tree

CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ Back to [Readme](README.md).
1212
### Fixed
1313

1414
* Wrong encoding of text attachments (#407)
15-
*
15+
* Hooks were not displayed when they did not have content leading to seemingly wrong skipped scenario times (#408)
16+
* Aligned exception display in hooks
1617

1718
## [4.1.0](https://github.com/trivago/cluecumber-report-plugin/tree/v4.1.0) - 2026-06-18
1819

engine/src/main/java/com/trivago/cluecumber/engine/json/pojo/Result.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,4 +127,16 @@ public String returnDurationString() {
127127
public String returnErrorMessageWithClickableLinks() {
128128
return RenderingUtils.turnUrlsIntoLinks(RenderingUtils.escapeHTML(errorMessage));
129129
}
130+
131+
/**
132+
* Get the first line of the error message for collapsed exception display.
133+
*
134+
* @return The escaped error message summary.
135+
*/
136+
public String returnErrorMessageSummary() {
137+
if (!hasErrorMessage()) {
138+
return "";
139+
}
140+
return RenderingUtils.escapeHTML(errorMessage.split("\n")[0].trim());
141+
}
130142
}

engine/src/main/java/com/trivago/cluecumber/engine/rendering/pages/renderering/ScenarioDetailsPageRenderer.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ public String getRenderedContent(
8080
scenarioDetailsPageCollection.setExpandSubSections(propertyManager.isExpandSubSections());
8181
scenarioDetailsPageCollection.setExpandPreviousScenarioRuns(propertyManager.isExpandPreviousScenarioRuns());
8282
scenarioDetailsPageCollection.setGroupPreviousScenarioRuns(propertyManager.isGroupPreviousScenarioRuns());
83+
scenarioDetailsPageCollection.setExpandErrorMessages(propertyManager.isExpandErrorMessages());
8384

8485
addChartJsonToReportDetails(scenarioDetailsPageCollection);
8586

engine/src/main/resources/template/css/cluecumber.css

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -934,9 +934,9 @@ h1, h2, h3, h4, h5 {
934934
}
935935

936936
:is(
937-
.cluecumber-list-item .firstException,
938-
.stepHook .firstException,
939-
.scenarioHook .cluecumber-list-item .firstException,
937+
.cluecumber-list-item .firstException.firstException-detail,
938+
.stepHook .firstException.firstException-detail,
939+
.scenarioHook .cluecumber-list-item .firstException.firstException-detail,
940940
:is(.scenarioAttachment-header, .scenarioOutputs-header)
941941
) .exception-links-top {
942942
flex-shrink: 0;
@@ -948,11 +948,11 @@ h1, h2, h3, h4, h5 {
948948
}
949949

950950
:is(
951-
.firstException .exception-links-top,
951+
.firstException:not(.firstException-detail) .exception-links-top,
952952
.scenarioDocstring .exception-links-top,
953-
.cluecumber-list-item .firstException .exception-links-top,
954-
.stepHook .firstException .exception-links-top,
955-
.scenarioHook .cluecumber-list-item .firstException .exception-links-top
953+
.cluecumber-list-item .firstException:not(.firstException-detail) .exception-links-top,
954+
.stepHook .firstException:not(.firstException-detail) .exception-links-top,
955+
.scenarioHook .cluecumber-list-item .firstException:not(.firstException-detail) .exception-links-top
956956
) .cluecumber-exception-action {
957957
width: 100%;
958958
min-width: var(--cluecumber-action-min-width);
@@ -962,6 +962,15 @@ h1, h2, h3, h4, h5 {
962962
width: auto;
963963
}
964964

965+
:is(
966+
.cluecumber-list-item .firstException.firstException-detail,
967+
.stepHook .firstException.firstException-detail,
968+
.scenarioHook .cluecumber-list-item .firstException.firstException-detail
969+
) .exception-links-top .cluecumber-exception-action {
970+
width: auto;
971+
min-width: var(--cluecumber-action-min-width);
972+
}
973+
965974
:is(
966975
.cluecumber-list-item .firstException.firstException-detail,
967976
.stepHook .firstException.firstException-detail,
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<div class="firstExceptionContainer">
2+
<div class="firstException exc-summary cluecumber-text-small">
3+
<div class="exception-content">
4+
<pre class="exception-pre">{{ exceptionSummary|raw }}</pre>
5+
</div>
6+
<div class="exception-links-top">
7+
<button type="button" class="cluecumber-exception-action secondary outline"
8+
data-toggle="collapse" data-target="#exc_{{ exceptionId }}"
9+
aria-expanded="false"
10+
aria-controls="exc_{{ exceptionId }}">More</button>
11+
</div>
12+
</div>
13+
<div class="collapse {{ expandErrorMessages ? "show" : "" }}" id="exc_{{ exceptionId }}">
14+
<div class="firstException firstException-expanded cluecumber-text-small">
15+
<div class="exception-content">
16+
<pre class="exception-pre"
17+
id="exc_pre_{{ exceptionId }}">{{ exceptionFull|raw }}</pre>
18+
</div>
19+
<div class="exception-links-top">
20+
<button type="button" class="cluecumber-exception-action secondary outline"
21+
data-toggle="collapse" data-target="#exc_{{ exceptionId }}"
22+
aria-expanded="true"
23+
aria-controls="exc_{{ exceptionId }}">Less</button>
24+
<button type="button" class="cluecumber-exception-action secondary outline"
25+
onclick="copyText('exc_pre_{{ exceptionId }}')">Copy</button>
26+
</div>
27+
</div>
28+
</div>
29+
</div>
Lines changed: 1 addition & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,3 @@
11
{% if exceptionElement.firstException is not empty %}
2-
<div class="firstExceptionContainer">
3-
<div class="firstException exc-summary cluecumber-text-small">
4-
<div class="exception-content">
5-
<pre class="exception-pre">{{ exceptionElement.firstExceptionSummary|raw }}</pre>
6-
</div>
7-
<div class="exception-links-top">
8-
<button type="button" class="cluecumber-exception-action secondary outline"
9-
data-toggle="collapse" data-target="#exc_{{ exceptionElement.scenarioIndex }}"
10-
aria-expanded="false"
11-
aria-controls="exc_{{ exceptionElement.scenarioIndex }}">More</button>
12-
</div>
13-
</div>
14-
<div class="collapse {{ expandErrorMessages ? "show" : "" }}" id="exc_{{ exceptionElement.scenarioIndex }}">
15-
<div class="firstException firstException-expanded cluecumber-text-small">
16-
<div class="exception-content">
17-
<pre class="exception-pre"
18-
id="exc_pre_{{ exceptionElement.scenarioIndex }}">{{ exceptionElement.firstException|raw }}</pre>
19-
</div>
20-
<div class="exception-links-top">
21-
<button type="button" class="cluecumber-exception-action secondary outline"
22-
data-toggle="collapse" data-target="#exc_{{ exceptionElement.scenarioIndex }}"
23-
aria-expanded="true"
24-
aria-controls="exc_{{ exceptionElement.scenarioIndex }}">Less</button>
25-
<button type="button" class="cluecumber-exception-action secondary outline"
26-
onclick="copyText('exc_pre_{{ exceptionElement.scenarioIndex }}')">Copy</button>
27-
</div>
28-
</div>
29-
</div>
30-
</div>
2+
{% include "macros/scenario-exception-block" with {"exceptionId": exceptionElement.scenarioIndex, "exceptionSummary": exceptionElement.firstExceptionSummary, "exceptionFull": exceptionElement.firstException} %}
313
{% endif %}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
2+
{% if hook.result.hasErrorMessage() %}
3+
<div class="cluecumber-step-full">
4+
{% set exceptionSummary = hook.result.returnErrorMessageSummary() %}
5+
{% set exceptionFull = hook.result.returnErrorMessageWithClickableLinks() %}
6+
{% include "macros/scenario-exception-block" with {"exceptionId": exceptionId, "exceptionSummary": exceptionSummary, "exceptionFull": exceptionFull} %}
7+
</div>
8+
{% endif %}
Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11

22
{% for hook in hooks %}
3-
{% if hook.hasContent() %}
43
<div class="stepHook collapse {{ expandStepHooks ? "show" : "" }}" id="step_{{ stepIndex }}_stepHooks">
54
<div class="row row_{{ hook.consolidatedStatusString }} table-row-{{ hook.consolidatedStatusString }}">
65
<div class="cluecumber-step-name">
@@ -12,12 +11,10 @@
1211
<div class="cluecumber-step-status cluecumber-align-right">
1312
{{ common.status(hook.consolidatedStatusString) }}
1413
</div>
15-
{% set step = hook %}
16-
{% include "macros/scenario-error-message" %}
17-
{% set sectionId = "hook" %}
18-
{% include "macros/scenario-output" %}
19-
{% include "macros/scenario-attachments" %}
14+
{% set hookExceptionId = "step_" ~ stepIndex ~ "_hook_" ~ loop.index %}
15+
{% include "macros/scenario-hook-error-message" with {"hook": hook, "exceptionId": hookExceptionId} %}
16+
{% include "macros/scenario-output" with {"step": hook, "sectionId": "hook"} %}
17+
{% include "macros/scenario-attachments" with {"step": hook} %}
2018
</div>
2119
</div>
22-
{% endif %}
2320
{% endfor %}

engine/src/main/resources/template/scenario-detail.peb

Lines changed: 16 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -77,13 +77,13 @@ limitations under the License.
7777
Sections
7878
</button>
7979
{% endif %}
80-
{% if element.hasHooks() and element.hasHooksWithContent() %}
80+
{% if element.hasHooks() %}
8181
<button class="secondary outline" type="button" id="before-after-hooks-button"
8282
data-cluecumber-item="before-after-hooks-button">{{ expandBeforeAfterHooks ? 'Hide' : 'Show' }}
8383
Hooks
8484
</button>
8585
{% endif %}
86-
{% if element.hasStepHooks() and element.hasStepHooksWithContent() %}
86+
{% if element.hasStepHooks() %}
8787
<button class="secondary outline" type="button" id="step-hooks-button"
8888
data-cluecumber-item="step-hooks-button">{{ expandStepHooks ? 'Hide' : 'Show' }} Step Hooks
8989
</button>
@@ -108,12 +108,11 @@ limitations under the License.
108108
{% endif %}
109109
{{ page.cardEnd() }}
110110

111-
{% if element.before is not empty and element.anyBeforeHookHasContent() %}
111+
{% if element.before is not empty %}
112112
<div class="scenarioHook collapse {{ expandBeforeAfterHooks ? 'show' : '' }}">
113113
{{ page.cardStart("12", "Before Hooks", "", "") }}
114114
<li class="cluecumber-list-item">
115115
{% for before in element.before %}
116-
{% if before.hasContent() or before.isFailed() %}
117116
<div class="row row_{{ before.consolidatedStatusString }} table-row-{{ before.consolidatedStatusString }}">
118117
<div class="cluecumber-step-name">
119118
<span>{{ loop.index + 1 }}.</span>
@@ -125,11 +124,11 @@ limitations under the License.
125124
<div class="cluecumber-step-status cluecumber-align-right">
126125
{{ common.status(before.consolidatedStatusString) }}
127126
</div>
128-
{% set step = before %}{% include "macros/scenario-error-message" %}
127+
{% set hookExceptionId = "before_" ~ loop.index %}
128+
{% include "macros/scenario-hook-error-message" with {"hook": before, "exceptionId": hookExceptionId} %}
129129
{% set step = before %}{% set sectionId = "before" %}{% include "macros/scenario-output" %}
130130
{% set step = before %}{% include "macros/scenario-attachments" %}
131131
</div>
132-
{% endif %}
133132
{% endfor %}
134133
</li>
135134
{{ page.cardEnd() }}
@@ -174,7 +173,7 @@ limitations under the License.
174173
class="keyword">{{ step.keyword }}</span> {{ stepName|raw }}</a>
175174
</span>
176175
</span>
177-
{% if step.hasSubSections() or (step.docString is not null and step.docString.value is not null) or step.hasHooksWithContent() %}
176+
{% if step.hasSubSections() or (step.docString is not null and step.docString.value is not null) or step.hasHooks() %}
178177
<div class="exception-links-top">
179178
{% if step.hasSubSections() %}
180179
<button type="button" class="cluecumber-exception-action secondary outline sectionExpansionButton"
@@ -188,7 +187,7 @@ limitations under the License.
188187
aria-expanded="false"
189188
data-target="#step_{{ step.index }}_docstring">DocString</button>
190189
{% endif %}
191-
{% if step.hasHooksWithContent() %}
190+
{% if step.hasHooks() %}
192191
<button type="button" class="cluecumber-exception-action secondary outline stepHooksExpansionButton"
193192
data-toggle="collapse"
194193
aria-expanded="false"
@@ -280,7 +279,7 @@ limitations under the License.
280279
class="keyword">{{ step.keyword }}</span> {{ stepName|raw }}</a>
281280
</span>
282281
</span>
283-
{% if step.hasSubSections() or (step.docString is not null and step.docString.value is not null) or step.hasHooksWithContent() %}
282+
{% if step.hasSubSections() or (step.docString is not null and step.docString.value is not null) or step.hasHooks() %}
284283
<div class="exception-links-top">
285284
{% if step.hasSubSections() %}
286285
<button type="button" class="cluecumber-exception-action secondary outline sectionExpansionButton"
@@ -294,7 +293,7 @@ limitations under the License.
294293
aria-expanded="false"
295294
data-target="#step_{{ step.index }}_docstring">DocString</button>
296295
{% endif %}
297-
{% if step.hasHooksWithContent() %}
296+
{% if step.hasHooks() %}
298297
<button type="button" class="cluecumber-exception-action secondary outline stepHooksExpansionButton"
299298
data-toggle="collapse"
300299
aria-expanded="false"
@@ -348,12 +347,11 @@ limitations under the License.
348347
{{ page.cardEnd() }}
349348
{% endif %}
350349

351-
{% if element.after is not empty and element.anyAfterHookHasContent() %}
350+
{% if element.after is not empty %}
352351
<div class="scenarioHook collapse {{ expandBeforeAfterHooks ? 'show' : '' }}">
353352
{{ page.cardStart("12", "After Hooks", "", "") }}
354353
<li class="cluecumber-list-item">
355354
{% for after in element.after %}
356-
{% if after.hasContent() or after.isFailed() %}
357355
<div class="row row_{{ after.consolidatedStatusString }} table-row-{{ after.consolidatedStatusString }}">
358356
<div class="cluecumber-step-name">
359357
<span>{{ loop.index + 1 }}.</span>
@@ -365,11 +363,11 @@ limitations under the License.
365363
<div class="cluecumber-step-status cluecumber-align-right">
366364
{{ common.status(after.consolidatedStatusString) }}
367365
</div>
368-
{% set step = after %}{% include "macros/scenario-error-message" %}
366+
{% set hookExceptionId = "after_" ~ loop.index %}
367+
{% include "macros/scenario-hook-error-message" with {"hook": after, "exceptionId": hookExceptionId} %}
369368
{% set step = after %}{% set sectionId = "after" %}{% include "macros/scenario-output" %}
370369
{% set step = after %}{% include "macros/scenario-attachments" %}
371370
</div>
372-
{% endif %}
373371
{% endfor %}
374372
</li>
375373
{{ page.cardEnd() }}
@@ -380,10 +378,10 @@ limitations under the License.
380378
<script>
381379
let classState = {};
382380

383-
{% if element.hasHooks() and element.hasHooksWithContent() %}
381+
{% if element.hasHooks() %}
384382
classState['scenarioHook'] = {{ expandBeforeAfterHooks ? "true" : "false" }};
385383
{% endif %}
386-
{% if element.hasStepHooks() and element.hasStepHooksWithContent() %}
384+
{% if element.hasStepHooks() %}
387385
classState['.stepHook'] = {{ expandStepHooks ? "true" : "false" }};
388386
{% endif %}
389387
{% if element.hasSubSections() %}
@@ -449,7 +447,7 @@ limitations under the License.
449447
});
450448
{% endif %}
451449

452-
{% if element.hasHooks() and element.hasHooksWithContent() %}
450+
{% if element.hasHooks() %}
453451
document.getElementById('before-after-hooks-button').addEventListener('click', function (event) {
454452
toggleHooks('scenarioHook', {{ expandBeforeAfterHooks ? "true" : "false" }});
455453
event.target.textContent = classState['scenarioHook'] ? 'Hide Hooks' : 'Show Hooks';
@@ -475,7 +473,7 @@ limitations under the License.
475473
});
476474
{% endif %}
477475

478-
{% if element.hasStepHooks() and element.hasStepHooksWithContent() %}
476+
{% if element.hasStepHooks() %}
479477
document.getElementById('step-hooks-button').addEventListener('click', function (event) {
480478
toggleCollapsableSection('.stepHook', {{ expandStepHooks ? "true" : "false" }});
481479
event.target.textContent = classState['.stepHook'] ? 'Hide Step Hooks' : 'Show Step Hooks';

engine/src/main/resources/template/scenario-summary.peb

Lines changed: 0 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -177,32 +177,6 @@ limitations under the License.
177177
});
178178
}
179179
}
180-
181-
// More/Less: hide summary first when expanding; show summary on Less click (not on hidden.bs.collapse)
182-
document.querySelectorAll('.firstExceptionContainer').forEach(function(container) {
183-
const collapseEl = container.querySelector('.collapse');
184-
const collapseId = collapseEl ? collapseEl.id : null;
185-
if (!collapseId) return;
186-
const moreButton = container.querySelector('[data-toggle="collapse"][data-target="#' + collapseId + '"][aria-expanded="false"]');
187-
if (moreButton) {
188-
moreButton.addEventListener('click', function() {
189-
container.classList.remove('error-show-summary');
190-
container.classList.add('error-expanding');
191-
}, true);
192-
}
193-
if (collapseEl) {
194-
collapseEl.addEventListener('shown.bs.collapse', function() {
195-
container.classList.remove('error-expanding');
196-
});
197-
}
198-
container.addEventListener('click', function(e) {
199-
const button = e.target.closest('[data-toggle="collapse"][data-target="#' + collapseId + '"]');
200-
if (button && button.getAttribute('aria-expanded') === 'true') {
201-
container.classList.remove('error-expanding');
202-
container.classList.add('error-show-summary');
203-
}
204-
}, true);
205-
});
206180
});
207181
</script>
208182
{% include "macros/page-end" %}

0 commit comments

Comments
 (0)