Skip to content

Commit 103e092

Browse files
committed
Added compact testbox results output
Courtesy of @aliaspooryorik
1 parent 73e3116 commit 103e092

20 files changed

+278
-168
lines changed

color-schemes/testbox.hidden-tmTheme

+100-118
Original file line numberDiff line numberDiff line change
@@ -1,123 +1,105 @@
11
<?xml version="1.0" encoding="UTF-8"?>
2-
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
32
<plist version="1.0">
43
<dict>
5-
<key>name</key>
6-
<string>TestBox</string>
7-
<key>settings</key>
8-
<array>
9-
<dict>
10-
<key>settings</key>
11-
<dict>
12-
<key>background</key>
13-
<string>#0F0F0F</string>
14-
<key>caret</key>
15-
<string>#c0c5ce</string>
16-
<key>foreground</key>
17-
<string>#CDD3DE</string>
18-
<key>invisibles</key>
19-
<string>#65737e</string>
20-
<key>lineHighlight</key>
21-
<string>#65737e55</string>
22-
<key>selection</key>
23-
<string>#4f5b66</string>
24-
</dict>
25-
</dict>
26-
<dict>
27-
<key>name</key>
28-
<string>TestBox Header</string>
29-
<key>scope</key>
30-
<string>testbox.header</string>
31-
<key>settings</key>
32-
<dict>
33-
<key>fontStyle</key>
34-
<string></string>
35-
<key>foreground</key>
36-
<string></string>
37-
</dict>
38-
</dict>
39-
<dict>
40-
<key>name</key>
41-
<string>TestBox Totals</string>
42-
<key>scope</key>
43-
<string>testbox.totals</string>
44-
<key>settings</key>
45-
<dict>
46-
<key>fontStyle</key>
47-
<string></string>
48-
<key>foreground</key>
49-
<string></string>
50-
</dict>
51-
</dict>
52-
<dict>
53-
<key>name</key>
54-
<string>TestBox Success</string>
55-
<key>scope</key>
56-
<string>testbox.success</string>
57-
<key>settings</key>
58-
<dict>
59-
<key>foreground</key>
60-
<string>#008000</string>
61-
</dict>
62-
</dict>
63-
<dict>
64-
<key>name</key>
65-
<string>TestBox Failure</string>
66-
<key>scope</key>
67-
<string>testbox.failure</string>
68-
<key>settings</key>
69-
<dict>
70-
<key>foreground</key>
71-
<string>#FFA500</string>
72-
</dict>
73-
</dict>
74-
<dict>
75-
<key>name</key>
76-
<string>TestBox Error</string>
77-
<key>scope</key>
78-
<string>testbox.error</string>
79-
<key>settings</key>
80-
<dict>
81-
<key>foreground</key>
82-
<string>#FF0000</string>
83-
</dict>
84-
</dict>
85-
<dict>
86-
<key>name</key>
87-
<string>TestBox Skipped</string>
88-
<key>scope</key>
89-
<string>testbox.skipped</string>
90-
<key>settings</key>
91-
<dict>
92-
<key>foreground</key>
93-
<string>#0000FF</string>
94-
</dict>
95-
</dict>
96-
<dict>
97-
<key>name</key>
98-
<string>Stack</string>
99-
<key>scope</key>
100-
<string>testbox.stack</string>
101-
<key>settings</key>
102-
<dict>
103-
<key>fontStyle</key>
104-
<string>bold</string>
105-
<key>foreground</key>
106-
<string></string>
107-
</dict>
108-
</dict>
109-
<dict>
110-
<key>name</key>
111-
<string>TestBox Stack</string>
112-
<key>scope</key>
113-
<string>testbox.stack.testbox</string>
114-
<key>settings</key>
115-
<dict>
116-
<key>fontStyle</key>
117-
<string>italic</string>
118-
<key>foreground</key>
119-
<string></string>
120-
</dict>
121-
</dict> </array>
4+
<key>name</key>
5+
<string>TestBox</string>
6+
<key>settings</key>
7+
<array>
8+
<dict>
9+
<key>settings</key>
10+
<dict>
11+
<key>background</key>
12+
<string>#0F0F0F</string>
13+
<key>caret</key>
14+
<string>#c0c5ce</string>
15+
<key>foreground</key>
16+
<string>#CDD3DE</string>
17+
<key>invisibles</key>
18+
<string>#65737e</string>
19+
<key>lineHighlight</key>
20+
<string>#65737e55</string>
21+
<key>selection</key>
22+
<string>#4f5b66</string>
23+
</dict>
24+
</dict>
25+
<dict>
26+
<key>name</key>
27+
<string>TestBox Success</string>
28+
<key>scope</key>
29+
<string>testbox.success</string>
30+
<key>settings</key>
31+
<dict>
32+
<key>fontStyle</key>
33+
<string>bold</string>
34+
<key>foreground</key>
35+
<string>#008000</string>
36+
</dict>
37+
</dict>
38+
<dict>
39+
<key>name</key>
40+
<string>TestBox Failure</string>
41+
<key>scope</key>
42+
<string>testbox.failure</string>
43+
<key>settings</key>
44+
<dict>
45+
<key>fontStyle</key>
46+
<string>bold</string>
47+
<key>foreground</key>
48+
<string>#FFA500</string>
49+
</dict>
50+
</dict>
51+
<dict>
52+
<key>name</key>
53+
<string>TestBox Error</string>
54+
<key>scope</key>
55+
<string>testbox.error</string>
56+
<key>settings</key>
57+
<dict>
58+
<key>fontStyle</key>
59+
<string>bold</string>
60+
<key>foreground</key>
61+
<string>#FF0000</string>
62+
</dict>
63+
</dict>
64+
<dict>
65+
<key>name</key>
66+
<string>TestBox Skipped</string>
67+
<key>scope</key>
68+
<string>testbox.skipped</string>
69+
<key>settings</key>
70+
<dict>
71+
<key>fontStyle</key>
72+
<string>bold</string>
73+
<key>foreground</key>
74+
<string>#0000FF</string>
75+
</dict>
76+
</dict>
77+
<dict>
78+
<key>name</key>
79+
<string>Stack</string>
80+
<key>scope</key>
81+
<string>testbox.stack</string>
82+
<key>settings</key>
83+
<dict>
84+
<key>fontStyle</key>
85+
<string>bold</string>
86+
<key>foreground</key>
87+
<string></string>
88+
</dict>
89+
</dict>
90+
<dict>
91+
<key>name</key>
92+
<string>TestBox Stack</string>
93+
<key>scope</key>
94+
<string>testbox.stack.testbox</string>
95+
<key>settings</key>
96+
<dict>
97+
<key>fontStyle</key>
98+
<string>italic</string>
99+
<key>foreground</key>
100+
<string></string>
101+
</dict>
102+
</dict>
103+
</array>
122104
</dict>
123105
</plist>

readme.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -213,7 +213,7 @@ If the variant for running the tests in a given directory is used, `directory=do
213213

214214
If these settings are not found in your project file, the root folder(s) of your project will be searched for a `box.json` file. If found, and if that file contains a `testbox` object, then its settings will be used.
215215

216-
Results are displayed in a results pane. If there are errors or failures you can step backwards and forwards through the file stack traces via <kbd>F4</kbd> and <kbd>SHIFT</kbd>+<kbd>F4</kbd>. As each file is selected, Sublime Text opens that file and jumps to the line indicated. You can also double click on any file, and Sublime Text will open it for you. Since the path to your files might look different to Sublime Text and the CFML application server (for example, if the server is running on a virtual machine), there is one more setting that maps the path as it appears to the server to the path as it appears to Sublime Text. Use `testbox.results.server_root` and `testbox.results.sublime_root` to specify the respective root paths to your project, so that Sublime Text can accurately open the files in stack traces.
216+
Results are displayed in a results pane. There is a setting that controls the verbosity of the output: `testbox.reporter`, which has two options: "text" and "compacttext" ("compacttext" being the default). If there are errors or failures you can step backwards and forwards through the file stack traces via <kbd>F4</kbd> and <kbd>SHIFT</kbd>+<kbd>F4</kbd>. As each file is selected, Sublime Text opens that file and jumps to the line indicated. You can also double click on any file, and Sublime Text will open it for you. Since the path to your files might look different to Sublime Text and the CFML application server (for example, if the server is running on a virtual machine), there is one more setting that maps the path as it appears to the server to the path as it appears to Sublime Text. Use `testbox.results.server_root` and `testbox.results.sublime_root` to specify the respective root paths to your project, so that Sublime Text can accurately open the files in stack traces.
217217

218218
All of the settings for TestBox can be seen in the default package settings.
219219

settings/cfml_package.sublime-settings

+1
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@
8484
"testbox": {
8585
"runner": "http://localhost:8888/testbox/system/Testbox.cfc?method=runremote",
8686
"tests_root": "",
87+
"reporter": "compacttext", // options are `compacttext` and `text`
8788
"results": {"server_root": "", "sublime_root": ""}
8889
},
8990

src/testbox/test_runner.py

+52-21
Original file line numberDiff line numberDiff line change
@@ -9,17 +9,22 @@
99
from functools import partial
1010
from .. import utils
1111

12-
RESULTS_TEMPLATES = {"logo": "", "bundle": "", "results": "", "global_exception": "", "legend": ""}
12+
RESULT_TEMPLATES = {
13+
"compacttext": {"logo": "", "bundle": "", "results": "", "global_exception": "", "legend": ""},
14+
"text": {"logo": "", "bundle": "", "results": "", "global_exception": "", "legend": ""}
15+
}
1316

1417

1518
def _plugin_loaded():
1619
sublime.set_timeout_async(load)
1720

1821

1922
def load():
20-
global RESULTS_TEMPLATES
21-
for template in RESULTS_TEMPLATES:
22-
RESULTS_TEMPLATES[template] = sublime.load_resource("Packages/" + utils.get_plugin_name() + "/templates/testbox/" + template + ".txt").replace("\r", "")
23+
global RESULT_TEMPLATES
24+
for n in RESULT_TEMPLATES:
25+
for t in RESULT_TEMPLATES[n]:
26+
r = "Packages/" + utils.get_plugin_name() + "/templates/testbox/" + n + "/" + t + ".txt"
27+
RESULT_TEMPLATES[n][t] = sublime.load_resource(r).replace("\r", "")
2328

2429

2530
class TestboxCommand(sublime_plugin.WindowCommand):
@@ -78,6 +83,7 @@ def setup_tests(self, project_file_dir, runner_url, test_bundle):
7883
self.output_view = self.window.create_output_panel("testbox")
7984

8085
testbox_results = self.get_testbox_results(project_file_dir)
86+
reporter = self.get_setting("reporter")
8187

8288
self.output_view.settings().set("result_file_regex", testbox_results["server_root"] + "([^\\s].*):([0-9]+)$")
8389
self.output_view.settings().set("result_base_dir", testbox_results["sublime_root"])
@@ -94,18 +100,18 @@ def setup_tests(self, project_file_dir, runner_url, test_bundle):
94100
self.window.create_output_panel("testbox")
95101

96102
self.window.run_command("show_panel", {"panel": "output.testbox"})
97-
self.output_view.run_command("append", {"characters": RESULTS_TEMPLATES["logo"], "force": True, "scroll_to_end": True})
103+
self.output_view.run_command("append", {"characters": RESULT_TEMPLATES[reporter]["logo"], "force": True, "scroll_to_end": True})
98104

99105
# run async
100-
sublime.set_timeout_async(partial(self.run_tests, runner_url, test_bundle))
106+
sublime.set_timeout_async(partial(self.run_tests, runner_url, test_bundle, reporter))
101107

102-
def run_tests(self, runner_url, test_bundle):
108+
def run_tests(self, runner_url, test_bundle, reporter):
103109
sublime.status_message("TestBox: running tests")
104110
print("TestBox: " + runner_url)
105111

106112
try:
107113
json_string = urllib.request.urlopen(runner_url).read().decode("utf-8")
108-
result_string = render_result(json.loads(json_string), test_bundle)
114+
result_string = render_result(json.loads(json_string), test_bundle, reporter)
109115
except:
110116
result_string = "\nError when trying to fetch:\n" + runner_url
111117
result_string += "\nPlease verify the URL is valid and returns the test results in JSON."
@@ -190,24 +196,25 @@ def get_dotted_directory(self, root, directory):
190196
# result rendering
191197

192198

193-
def render_result(result_data, test_bundle):
199+
def render_result(result_data, test_bundle, reporter):
194200
# lowercase all the keys since we can't guarantee the casing coming from CFML
195201
result_data = lcase_keys(result_data)
196-
result_string = sublime.expand_variables(RESULTS_TEMPLATES["results"], filter_stats_dict(result_data)) + "\n"
202+
padToLen = 7 if reporter == "compacttext" else 0
203+
result_string = sublime.expand_variables(RESULT_TEMPLATES[reporter]["results"], filter_stats_dict(result_data, padToLen))
197204

198205
for bundle in result_data["bundlestats"]:
199206
if len(test_bundle) and bundle["path"] != test_bundle:
200207
continue
201208

202-
result_string += "\n" + sublime.expand_variables(RESULTS_TEMPLATES["bundle"], filter_stats_dict(bundle)) + "\n"
209+
result_string += sublime.expand_variables(RESULT_TEMPLATES[reporter]["bundle"], filter_stats_dict(bundle))
203210

204211
if isinstance(bundle["globalexception"], dict):
205-
result_string += "\n" + sublime.expand_variables(RESULTS_TEMPLATES["global_exception"], filter_exception_dict(bundle["globalexception"])) + "\n"
212+
result_string += sublime.expand_variables(RESULT_TEMPLATES[reporter]["global_exception"], filter_exception_dict(bundle["globalexception"])) + "\n"
206213

207214
for suite in bundle["suitestats"]:
208-
result_string += "\n" + gen_suite_report(suite)
215+
result_string += gen_suite_report(bundle, suite, reporter)
209216

210-
result_string += "\n" + RESULTS_TEMPLATES["legend"]
217+
result_string += "\n" + RESULT_TEMPLATES[reporter]["legend"]
211218
return result_string
212219

213220

@@ -219,10 +226,28 @@ def lcase_keys(source):
219226
return source
220227

221228

222-
def filter_stats_dict(source):
223-
stat_keys = ["path", "totalduration", "totalbundles", "totalsuites", "totalspecs"]
224-
stat_keys.extend(["totalpass", "totalfail", "totalerror", "totalskipped"])
225-
return {key: str(source[key]) for key in stat_keys if key in source}
229+
def filter_stats_dict(source, padToLen=0):
230+
stat_keys = [
231+
"path",
232+
"totalduration",
233+
"totalbundles",
234+
"totalsuites",
235+
"totalspecs",
236+
"totalpass",
237+
"totalfail",
238+
"totalerror",
239+
"totalskipped"
240+
]
241+
242+
def paddedText(key, toLen):
243+
txt = str(source[key])
244+
if key == "totalduration":
245+
txt = txt + " ms"
246+
if toLen == 0 or len(txt) >= toLen:
247+
return txt
248+
return txt + " " * (toLen - len(txt))
249+
250+
return {key: paddedText(key, padToLen) for key in stat_keys if key in source}
226251

227252

228253
def filter_exception_dict(source):
@@ -234,7 +259,7 @@ def get_status_bit(status):
234259
status_dict = {"Failed": "!", "Error": "X", "Skipped": "-"}
235260
if status in status_dict:
236261
return status_dict[status]
237-
return "+"
262+
return "P"
238263

239264

240265
def build_stacktrace(stack, tabs):
@@ -244,11 +269,17 @@ def build_stacktrace(stack, tabs):
244269
return stacktrace
245270

246271

247-
def gen_suite_report(suiteStats, level=0):
272+
def gen_suite_report(bundleStats, suiteStats, reporter, level=0):
273+
if reporter == "compacttext" and bundleStats["totalfail"] + bundleStats["totalerror"] == 0:
274+
return ""
275+
248276
tabs = " " * level
249277
report = tabs + "(" + get_status_bit(suiteStats["status"]) + ")" + suiteStats["name"] + "\n"
250278
level += 1
251279
for spec in suiteStats["specstats"]:
280+
if reporter == "compacttext" and spec["status"] not in ["Failed", "Error"]:
281+
continue
282+
252283
tabs = " " * level
253284
report += tabs + "(" + get_status_bit(spec["status"]) + ")" + spec["name"] + " (" + str(spec["totalduration"]) + " ms)" + "\n"
254285

@@ -262,6 +293,6 @@ def gen_suite_report(suiteStats, level=0):
262293
report += build_stacktrace(spec["error"]["tagcontext"], tabs)
263294

264295
for nestedSuite in suiteStats["suitestats"]:
265-
report += gen_suite_report(nestedSuite, level + 1) + "\n"
296+
report += gen_suite_report(bundleStats, nestedSuite, reporter, level + 1).strip() + "\n"
266297

267298
return report

0 commit comments

Comments
 (0)