Skip to content

Commit

Permalink
Added CLI flag for junit output
Browse files Browse the repository at this point in the history
  • Loading branch information
cerdmann committed Sep 12, 2024
1 parent 583c2a2 commit ec02ff9
Show file tree
Hide file tree
Showing 13 changed files with 81,376 additions and 49,628 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,5 @@ venv
.DS_Store

cli_out.txt
output.json
output.json
junit.xml
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,12 @@ As this is intended to be used as a transformer for the Postman CLI output, usag
postman collection run 11111111-11111111-1111-1111-1111-111111111111 -e 11111111-11111111-1111-1111-1111-111111111111 | postman-cli-transformer output.json
```

To output both JSON and a Junit formatted xml file use:

```bash
postman collection run 11111111-11111111-1111-1111-1111-111111111111 -e 11111111-11111111-1111-1111-1111-111111111111 | postman-cli-transformer output.json --junit-out-file junit.xml
```

## Development

To contribute to this tool, first checkout the code. Then create a new virtual environment:
Expand Down
45 changes: 12 additions & 33 deletions postman_cli_transformer/cli.py
Original file line number Diff line number Diff line change
@@ -1,35 +1,23 @@
from datetime import datetime
import click
import json
import sys
import io

from postman_cli_transformer.decipherer import line_decipherer
from postman_cli_transformer.parsers import parse_test
from postman_cli_transformer.parsers import parse_url
from postman_cli_transformer.unicode_constants import *
from postman_cli_transformer.junit_transformer import junit_transform
from postman_cli_transformer.processor import Processor


@click.command()
@click.argument("output", type=click.File("w"), default="-", required=False)
@click.option(
"-uf",
"--unicode-out-file",
"--junit-out-file",
required=False,
type=click.File("w"),
help="""file location to output unicode codes of characters from STDIN.
Each charachter is represented as (<original character> - <unicode of character>)
Line breaks(10) are preserved but not given a representation""",
help="File location to output junit xml file from transformed CLI results.",
)
# @click.option(
# "-t",
# "--extract-tags",
# required=False,
# type=click.BOOL,
# help="""undeveloped, but will eventually extract tags from tests descriptions""",
# )
@click.version_option()
def cli(output, unicode_out_file):
def cli(output, junit_out_file):
"""This script will take as input the STDOUT from
a Postman CLI collection run and transform the
output text to a file containing the output data
Expand All @@ -45,31 +33,22 @@ def cli(output, unicode_out_file):
postman-cli-transformer foo.json
\b
Output to file foo.json and extract tags from tests:
postman-cli-transformer foo.json --extract-tags
Output json to file foo.json and output junit xml to file bar.xml :
postman-cli-transformer foo.json --junit-out-file bar.xml
"""

stdin_data = sys.stdin.read()

parsed_stdin = parse(stdin_data)

if unicode_out_file:
process_as_unicode(io.StringIO(stdin_data), unicode_out_file)

output.write(parsed_stdin)
output.flush()
if junit_out_file:
current_time_of_test_run = datetime.now().isoformat()

results = junit_transform(json.loads(parsed_stdin), current_time_of_test_run)
junit_out_file.write(results)

def process_as_unicode(file, output):
for line in file:
for char in line:
unicodeInt = ord(char)
if unicodeInt != 10:
output.write(" (%c - %s) " % (char, unicodeInt))
else:
output.write(char)

output.write(parsed_stdin)
output.flush()


Expand Down
4 changes: 4 additions & 0 deletions postman_cli_transformer/decipherer.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,10 @@ def line_decipherer(line):
if re.search(r"^[\s\d][0-9]. Assertion", line):
return LINE_TYPES.ERROR_LINE

if len(line.split()) >= 2:
if "Error" in line.split()[1]:
return LINE_TYPES.ERROR_LINE

if line[:21] == " ":
return LINE_TYPES.ERROR_LINE

Expand Down
111 changes: 111 additions & 0 deletions postman_cli_transformer/junit_transformer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
import string


def junit_transform(json_to_transform, time_of_tests):
transformed = []
transformed.append('<?xml version="1.0" encoding="UTF-8"?>')
collection_name = json_to_transform["collectionName"]
total_tests = get_total_number_of_requests(json_to_transform["folders"])
total_time = calculate_time(
json_to_transform["summary"]["totals"]["totalRunDuration"]
)
transformed.append(
'<testsuites name="%s" tests="%s" time="%s">'
% (collection_name, total_tests, total_time)
)
for folder in json_to_transform["folders"]:
folder_name = folder["name"]
for request in folder["requests"]:
test_suite_name = "%s / %s" % (folder_name, request["name"])
compressed_name = compress(test_suite_name)
number_of_tests = len(request["tests"])
number_of_failures = len(
[
test
for test in request["tests"]
if test["status"]["result"] == "FAILED"
]
)
request_time = sum(
[calculate_time(url["response"]["time"]) for url in request["urls"]]
)
transformed.append(
' <testsuite name="%s" timestamp="%s" tests="%s" failures="%s" errors="0" time="%s">'
% (
test_suite_name,
time_of_tests,
number_of_tests,
number_of_failures,
request_time,
)
)
for test in request["tests"]:
test_name = test["desc"]
if test["status"]["result"] == "FAILED":
transformed.append(
' <testcase name="%s" time="%s" classname="%s">'
% (test_name, request_time, compressed_name)
)
failure_type = test["status"]["details"]["type"]
error_details = test["status"]["details"]["detail"].split("\n")
test_description = error_details[0]
error_message = error_details[1]
del error_details[:2]
stack_trace = "\n".join(error_details)
transformed.append(
' <failure type="%s" message="%s">'
% (failure_type, error_message)
)

transformed.append(" <![CDATA[Failed 1 times.]]>")
transformed.append(
" <![CDATA[Collection name: %s.]]>" % collection_name
)
transformed.append(
" <![CDATA[Request name: %s.]]>" % test_suite_name
)
transformed.append(
" <![CDATA[Test description: %s.]]>" % test_description
)
transformed.append(
" <![CDATA[Error message: %s.]]>" % error_message
)
transformed.append(
" <![CDATA[Stacktrace: %s.]]>" % stack_trace
)
transformed.append(" </failure>")
transformed.append(" </testcase>")
else:
transformed.append(
' <testcase name="%s" time="%s" classname="%s"/>'
% (test_name, request_time, compressed_name)
)
transformed.append(" </testsuite>")

transformed.append("</testsuites>")

return "\n".join(transformed)


def get_total_number_of_requests(folders):
request_count = 0
for folder in folders:
request_count += len(folder["requests"])

return request_count


def calculate_time(time_in_ms):
try:
stripped_time = time_in_ms.rstrip("ms")
return int(stripped_time) / 1000
except (ValueError, Exception):
return 0


def compress(line):
forward_slash_removed = line.replace("/", "")
capitalized = string.capwords(forward_slash_removed)
strip_space = "".join(capitalized.split())

return strip_space
4 changes: 4 additions & 0 deletions postman_cli_transformer/parsers.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ def parse_test(test):
test_desc = test_result.lstrip(CHECKMARK_ICON).strip()
else:
failed_test = test_result.split(".")
if len(failed_test) == 1:
# Found this funky period when forcing a type error in postman
# collection run
failed_test = test_result.split("⠄")
test_status = {
"result": "FAILED",
"error_id": failed_test[0],
Expand Down
5 changes: 3 additions & 2 deletions postman_cli_transformer/processor.py
Original file line number Diff line number Diff line change
Expand Up @@ -174,10 +174,11 @@ def _process_rest_of_lines(self):
)
error_parts = line.split()

if "AssertionError" in line:
if "Error" in error_parts[1]:
error_type = error_parts[1]
self.processing_helper.error_id = error_parts[0].rstrip(".")
del error_parts[:2]
self.processing_helper.error_type = "AssertionError"
self.processing_helper.error_type = error_type

error_description = " ".join(error_parts)
self.processing_helper.error_lines.append(error_description)
Expand Down
Loading

0 comments on commit ec02ff9

Please sign in to comment.