Skip to content

Commit

Permalink
feat: Add support for parsing steps in JUnit XML format
Browse files Browse the repository at this point in the history
- Implemented `parseSteps` function to parse steps from JUnit XML properties
- Introduced unit tests to verify the correct parsing of steps

#59
  • Loading branch information
gibiw committed Dec 12, 2024
1 parent 0f93da6 commit f250ecd
Show file tree
Hide file tree
Showing 2 changed files with 205 additions and 2 deletions.
81 changes: 79 additions & 2 deletions internal/parsers/junit/junit.go
Original file line number Diff line number Diff line change
Expand Up @@ -154,11 +154,16 @@ func convertTestSuites(testSuites TestSuites) []models.Result {
}

fields := make(map[string]string)

for k := range testCase.Properties.Property {
if isStepProperty(testCase.Properties.Property[k].Name) {
continue
}

fields[testCase.Properties.Property[k].Name] = testCase.Properties.Property[k].Value
}

steps := parseSteps(testCase.Properties)

result := models.Result{
Title: testCase.Name,
Signature: &signature,
Expand All @@ -169,7 +174,7 @@ func convertTestSuites(testSuites TestSuites) []models.Result {
StackTrace: stackTrace,
},
Attachments: make([]models.Attachment, 0),
Steps: make([]models.Step, 0),
Steps: steps,
StepType: "text",
Params: make(map[string]string),
Muted: false,
Expand Down Expand Up @@ -205,3 +210,75 @@ func convertTestSuites(testSuites TestSuites) []models.Result {

return results
}

// parseSteps parses the steps from the properties
func parseSteps(properties Properties) []models.Step {
steps := make([]models.Step, 0)
parentSteps := make(map[string][]models.Step)

for _, prop := range properties.Property {
if isStepProperty(prop.Name) {
status := extractStepStatus(prop.Name)
path := strings.Split(prop.Value, "/")

if isSimpleStep(path) {
step := createStep(prop.Value, status)
assignParentSteps(parentSteps, prop.Value, &step)
steps = append(steps, step)
} else {
currentStepName := path[len(path)-1]
parentStepName := path[len(path)-2]
addChildStepToParent(parentSteps, parentStepName, currentStepName, status)
}
}
}

return steps
}

// isStepProperty checks if the property name is a step.
func isStepProperty(name string) bool {
return strings.HasPrefix(name, "step[") && strings.HasSuffix(name, "]")
}

// extractStepStatus extracts the step status from the property name.
func extractStepStatus(name string) string {
return name[5 : len(name)-1]
}

// isSimpleStep checks if the step is a simple step (without child steps).
func isSimpleStep(path []string) bool {
return len(path) == 1
}

// createStep creates a new step with the given action and status.
func createStep(action, status string) models.Step {
return models.Step{
Data: models.Data{
Action: action,
},
Execution: models.StepExecution{
Status: status,
},
}
}

// assignParentSteps assigns parent steps to the current step if any.
func assignParentSteps(parentSteps map[string][]models.Step, action string, step *models.Step) {
if childSteps, exists := parentSteps[action]; exists {
step.Steps = childSteps
delete(parentSteps, action)
}
}

// addChildStepToParent adds a child step to the parent step map.
func addChildStepToParent(stepMap map[string][]models.Step, parentStepName, stepName, status string) {
if _, exists := stepMap[stepName]; !exists {
stepMap[parentStepName] = append(stepMap[parentStepName], createStep(stepName, status))
} else {
step := createStep(stepName, status)
step.Steps = stepMap[stepName]
delete(stepMap, stepName)
stepMap[parentStepName] = append(stepMap[parentStepName], step)
}
}
126 changes: 126 additions & 0 deletions internal/parsers/junit/junit_test.go
Original file line number Diff line number Diff line change
@@ -1 +1,127 @@
package junit

import (
models "github.com/qase-tms/qasectl/internal/models/result"
"reflect"
"testing"
)

func TestParseSteps(t *testing.T) {
tests := []struct {
name string
properties Properties
expected []models.Step
}{
{
name: "Empty properties",
properties: Properties{},
expected: []models.Step{},
},
{
name: "Single step",
properties: Properties{
Property: []Property{
{Name: "step[passed]", Value: "A"},
},
},
expected: []models.Step{
{
Data: models.Data{Action: "A"},
Execution: models.StepExecution{
Status: "passed",
},
Steps: nil,
},
},
},
{
name: "Multiple steps",
properties: Properties{
Property: []Property{
{Name: "step[passed]", Value: "A"},
{Name: "step[failed]", Value: "B"},
},
},
expected: []models.Step{
{
Data: models.Data{Action: "A"},
Execution: models.StepExecution{
Status: "passed",
},
},
{
Data: models.Data{Action: "B"},
Execution: models.StepExecution{
Status: "failed",
},
},
},
},
{
name: "Nested steps",
properties: Properties{
Property: []Property{
{Name: "step[passed]", Value: "C/D"},
{Name: "step[passed]", Value: "C"},
},
},
expected: []models.Step{
{
Data: models.Data{Action: "C"},
Execution: models.StepExecution{
Status: "passed",
},
Steps: []models.Step{
{
Data: models.Data{Action: "D"},
Execution: models.StepExecution{
Status: "passed",
},
},
},
},
},
},
{
name: "Duplicate steps",
properties: Properties{
Property: []Property{
{Name: "step[passed]", Value: "A"},
{Name: "step[passed]", Value: "A"},
},
},
expected: []models.Step{
{
Data: models.Data{Action: "A"},
Execution: models.StepExecution{
Status: "passed",
},
},
{
Data: models.Data{Action: "A"},
Execution: models.StepExecution{
Status: "passed",
},
},
},
},
{
name: "Invalid property name",
properties: Properties{
Property: []Property{
{Name: "invalid[step]", Value: "A"},
},
},
expected: []models.Step{},
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := parseSteps(tt.properties)
if !reflect.DeepEqual(result, tt.expected) {
t.Errorf("got %+v, want %+v", result, tt.expected)
}
})
}
}

0 comments on commit f250ecd

Please sign in to comment.