Skip to content

Commit d117986

Browse files
Support for tabel driven scenarios, and refactorization. (#366)
Signed-off-by: jensakejohansson <jens.johansson@systemverification.com>
1 parent 80dcd3f commit d117986

File tree

7 files changed

+437
-105
lines changed

7 files changed

+437
-105
lines changed

generator/generate.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,11 @@ type scenario struct {
153153
AfterScenarioHookFailure *hookFailure `json:"AfterScenarioHookFailure"`
154154
SkipErrors []string `json:"SkipErrors"`
155155
TableRowIndex int `json:"TableRowIndex"`
156+
ScenarioTableRowIndex int `json:"ScenarioTableRowIndex"`
157+
IsSpecTableDriven bool `json:"IsSpecTableDriven"`
158+
IsScenarioTableDriven bool `json:"IsScenarioTableDriven"`
159+
ScenarioDataTable *table `json:"ScenarioDataTable"`
160+
ScenarioTableRow *table `json:"ScenarioTableRow"`
156161
PreHookMessages []string `json:"PreHookMessages"`
157162
PostHookMessages []string `json:"PostHookMessages"`
158163
PreHookScreenshotFiles []string `json:"PreHookScreenshotFiles"`

generator/generate_test.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,23 @@ var wSpecCommentsWithCodeBlock = `<span><pre><code>{&#34;prop&#34;:&#34;value&#3
242242
var wScenarioContainerStartPassDiv = `<div class="scenario-container passed">`
243243
var wScenarioContainerStartFailDiv = `<div class="scenario-container failed">`
244244
var wScenarioContainerStartSkipDiv = `<div class="scenario-container skipped">`
245+
var wScenarioContainerTableDrivenHiddenDiv = `<div class="scenario-container passed hidden" data-scenario-row-index='1'>`
246+
var wScenarioContainerTableDrivenNestedDiv = `<div class="scenario-container passed hidden" data-scenario-row-index='1' data-tablerow='0'>`
247+
248+
var wScenarioDataTableDiv = `
249+
<table class="data-table scenario-data-table" data-spec-row-index='0'>
250+
<tr>
251+
<th>Browser</th><th>Status</th>
252+
</tr>
253+
<tbody data-rowCount=2>
254+
<tr class="row-selector passed selected" data-scenario-row-index='0'>
255+
<td>Chrome</td><td>Pass</td>
256+
</tr>
257+
<tr class="row-selector failed" data-scenario-row-index='1'>
258+
<td>Firefox</td><td>Fail</td>
259+
</tr>
260+
</tbody>
261+
</table>`
245262

246263
var wscenarioHeaderStartDiv = `<div class="scenario-head">
247264
<h3 class="head borderBottom">Scenario Heading</h3>
@@ -510,6 +527,13 @@ var reportGenTests = []reportGenTest{
510527
{"generate passing scenario container", "scenarioContainerStartDiv", &scenario{ExecutionStatus: pass, TableRowIndex: -1}, wScenarioContainerStartPassDiv},
511528
{"generate failed scenario container", "scenarioContainerStartDiv", &scenario{ExecutionStatus: fail, TableRowIndex: -1}, wScenarioContainerStartFailDiv},
512529
{"generate skipped scenario container", "scenarioContainerStartDiv", &scenario{ExecutionStatus: skip, TableRowIndex: -1}, wScenarioContainerStartSkipDiv},
530+
{"generate table driven scenario container", "scenarioContainerStartDiv", &scenario{ExecutionStatus: pass, IsScenarioTableDriven: true, ScenarioTableRowIndex: 1, TableRowIndex: -1}, wScenarioContainerTableDrivenHiddenDiv},
531+
{"generate nested table driven scenario container", "scenarioContainerStartDiv", &scenario{ExecutionStatus: pass, IsScenarioTableDriven: true, ScenarioTableRowIndex: 1, TableRowIndex: 0}, wScenarioContainerTableDrivenNestedDiv},
532+
{"generate scenario table for table driven scenario", "scenarioTableTag", &scenario{
533+
TableRowIndex: 0,
534+
ScenarioDataTable: &table{Headers: []string{"Browser", "Status"}, Rows: []*row{{Cells: []string{"Chrome", "Pass"}, Result: pass}, {Cells: []string{"Firefox", "Fail"}, Result: fail}}},
535+
IsScenarioTableDriven: true,
536+
}, wScenarioDataTableDiv},
513537
{"generate scenario header", "scenarioHeaderStartDiv", &scenario{Heading: "Scenario Heading", ExecutionTime: "00:01:01"}, wscenarioHeaderStartDiv},
514538
{"generate pass step start div", "stepStartDiv", newStep(pass), wPassStepStartDiv},
515539
{"generate fail step start div", "stepStartDiv", newStep(fail), wFailStepStartDiv},

generator/transform.go

Lines changed: 82 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -272,7 +272,35 @@ func (s bySceStatus) Swap(i, j int) {
272272
}
273273

274274
func (s bySceStatus) Less(i, j int) bool {
275-
return getSceState(s[i]) < getSceState(s[j])
275+
// If both scenarios are scenario-table-driven with the same heading, maintain their order by row index
276+
if s[i].IsScenarioTableDriven && s[j].IsScenarioTableDriven && s[i].Heading == s[j].Heading {
277+
return s[i].ScenarioTableRowIndex < s[j].ScenarioTableRowIndex
278+
}
279+
280+
stateI := s.getEffectiveState(i)
281+
stateJ := s.getEffectiveState(j)
282+
283+
return stateI < stateJ
284+
}
285+
286+
// getEffectiveState returns the worst state for a scenario, when the scenario is scenario-table-driven.
287+
func (s bySceStatus) getEffectiveState(index int) int {
288+
scenario := s[index]
289+
290+
if !scenario.IsScenarioTableDriven {
291+
return getSceState(scenario)
292+
}
293+
294+
worstState := getSceState(scenario)
295+
for _, otherScenario := range s {
296+
if otherScenario.IsScenarioTableDriven && otherScenario.Heading == scenario.Heading {
297+
state := getSceState(otherScenario)
298+
if state < worstState {
299+
worstState = state
300+
}
301+
}
302+
}
303+
return worstState
276304
}
277305

278306
func getSceState(s *scenario) int {
@@ -366,9 +394,14 @@ func toSpec(res *gm.ProtoSpecResult, projectRoot string) *spec {
366394
spec.Datatable = toTable(item.GetTable())
367395
isTableScanned = true
368396
case gm.ProtoItem_Scenario:
369-
spec.Scenarios = append(spec.Scenarios, toScenario(item.GetScenario(), -1))
397+
spec.Scenarios = append(spec.Scenarios, toScenario(item.GetScenario(), -1, nil))
370398
case gm.ProtoItem_TableDrivenScenario:
371-
spec.Scenarios = append(spec.Scenarios, toScenario(item.GetTableDrivenScenario().GetScenario(), int(item.GetTableDrivenScenario().GetTableRowIndex())))
399+
tableDrivenScenario := item.GetTableDrivenScenario()
400+
if tableDrivenScenario.GetIsScenarioTableDriven() && !tableDrivenScenario.GetIsSpecTableDriven() {
401+
spec.Scenarios = append(spec.Scenarios, toScenario(tableDrivenScenario.GetScenario(), -1, tableDrivenScenario))
402+
} else {
403+
spec.Scenarios = append(spec.Scenarios, toScenario(tableDrivenScenario.GetScenario(), int(item.TableDrivenScenario.GetTableRowIndex()), tableDrivenScenario))
404+
}
372405
}
373406
}
374407
for _, preHookFailure := range res.GetProtoSpec().GetPreHookFailures() {
@@ -381,11 +414,12 @@ func toSpec(res *gm.ProtoSpecResult, projectRoot string) *spec {
381414
if res.GetProtoSpec().GetIsTableDriven() && isTableScanned {
382415
computeTableDrivenStatuses(spec)
383416
}
417+
computeScenarioTableStatuses(spec)
384418
p, f, s := computeScenarioStatistics(spec)
385419
spec.PassedScenarioCount = p
386420
spec.FailedScenarioCount = f
387421
spec.SkippedScenarioCount = s
388-
sort.Sort(bySceStatus(spec.Scenarios))
422+
sort.Stable(bySceStatus(spec.Scenarios))
389423
return spec
390424
}
391425

@@ -428,6 +462,9 @@ func hasParseErrors(errors []*gm.Error) bool {
428462
}
429463

430464
func computeTableDrivenStatuses(spec *spec) {
465+
if spec.Datatable == nil || len(spec.Datatable.Rows) == 0 {
466+
return
467+
}
431468
for _, r := range spec.Datatable.Rows {
432469
r.Result = skip
433470
}
@@ -453,13 +490,47 @@ func SetRowFailures(failures []*hookFailure, spec *spec) {
453490
}
454491
}
455492

493+
func computeScenarioTableStatuses(spec *spec) {
494+
// Group scenarios, key: scenario heading, value: scenarios with same table
495+
scenarioTables := make(map[string][]*scenario)
496+
for _, s := range spec.Scenarios {
497+
if s.IsScenarioTableDriven {
498+
key := s.Heading
499+
scenarioTables[key] = append(scenarioTables[key], s)
500+
}
501+
}
502+
// Update row statuses based on scenario execution results
503+
for _, scenarios := range scenarioTables {
504+
if len(scenarios) == 0 || scenarios[0].ScenarioDataTable == nil {
505+
continue
506+
}
507+
table := scenarios[0].ScenarioDataTable
508+
for _, s := range scenarios {
509+
if s.ScenarioTableRowIndex >= 0 && s.ScenarioTableRowIndex < len(table.Rows) {
510+
row := table.Rows[s.ScenarioTableRowIndex]
511+
row.Result = s.ExecutionStatus
512+
if s.BeforeScenarioHookFailure != nil {
513+
row.Result = fail
514+
}
515+
if s.AfterScenarioHookFailure != nil {
516+
row.Result = fail
517+
}
518+
}
519+
}
520+
// Clear table reference for all subsequent scenarios to prevent duplicate table rendering in template
521+
for i := 1; i < len(scenarios); i++ {
522+
scenarios[i].ScenarioDataTable = nil
523+
}
524+
}
525+
}
526+
456527
func toScenarioSummary(s *spec) *summary {
457528
var sum = summary{Failed: s.FailedScenarioCount, Passed: s.PassedScenarioCount, Skipped: s.SkippedScenarioCount}
458529
sum.Total = sum.Failed + sum.Passed + sum.Skipped
459530
return &sum
460531
}
461532

462-
func toScenario(scn *gm.ProtoScenario, tableRowIndex int) *scenario {
533+
func toScenario(scn *gm.ProtoScenario, tableRowIndex int, tableDrivenScenario *gm.ProtoTableDrivenScenario) *scenario {
463534
scenario := &scenario{
464535
Heading: scn.GetScenarioHeading(),
465536
ExecutionTime: formatTime(scn.GetExecutionTime()),
@@ -475,6 +546,12 @@ func toScenario(scn *gm.ProtoScenario, tableRowIndex int) *scenario {
475546
PostHookMessages: scn.GetPostHookMessages(),
476547
RetriesCount: int(scn.RetriesCount),
477548
}
549+
if tableDrivenScenario.GetIsScenarioTableDriven() {
550+
scenario.IsScenarioTableDriven = tableDrivenScenario.GetIsScenarioTableDriven()
551+
scenario.ScenarioTableRowIndex = int(tableDrivenScenario.GetScenarioTableRowIndex())
552+
scenario.ScenarioDataTable = toTable(tableDrivenScenario.GetScenarioDataTable())
553+
scenario.ScenarioTableRow = toTable(tableDrivenScenario.GetScenarioTableRow())
554+
}
478555
for _, s := range scn.GetPreHookScreenshotFiles() {
479556
scenario.PreHookScreenshotFiles = append(scenario.PreHookScreenshotFiles, s)
480557
screenshotFiles = append(screenshotFiles, s)

generator/transform_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1081,7 +1081,7 @@ func TestToScenario(t *testing.T) {
10811081
RetriesCount: 4,
10821082
}
10831083

1084-
got := toScenario(scn, -1)
1084+
got := toScenario(scn, -1, nil)
10851085
if !reflect.DeepEqual(got, want) {
10861086
t.Errorf("want:\n%v\ngot:\n%v\n", want, got)
10871087
}
@@ -1109,7 +1109,7 @@ func TestToScenarioWithHookFailures(t *testing.T) {
11091109
TableRowIndex: -1,
11101110
}
11111111

1112-
got := toScenario(scnWithHookFailure, -1)
1112+
got := toScenario(scnWithHookFailure, -1, nil)
11131113
checkEqual(t, "", want, got)
11141114
}
11151115

plugin.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,5 +28,5 @@
2828
"scope": [
2929
"Execution"
3030
],
31-
"version": "4.3.5"
31+
"version": "4.4.0"
3232
}

0 commit comments

Comments
 (0)