Repository: cdent/gabbi

Issue:
If verbose: True and response content-type is json may as well pretty print the output
It kinda seems like if we know the content-type when being verbose about bodies, we may as well pretty print if it is json. It's not much effort to do so (there's already pretty printing happening in some test failure messages) and it is nice.

Is it too nice?

/cc @FND 


Discussion:

Patch:
diff --git a/gabbi/httpclient.py b/gabbi/httpclient.py
index 22869e5..88f873d 100644
--- a/gabbi/httpclient.py
+++ b/gabbi/httpclient.py
@@ -19,6 +19,7 @@ import sys
 
 import urllib3
 
+from gabbi.handlers import jsonhandler
 from gabbi import utils
 
 
@@ -138,11 +139,20 @@ class VerboseHttp(Http):
 
     def _print_body(self, headers, content):
         """Output body if not binary."""
-        if self._show_body and utils.not_binary(
-                utils.extract_content_type(headers)[0]):
+        content_type = utils.extract_content_type(headers)[0]
+        if self._show_body and utils.not_binary(content_type):
+            content = utils.decode_response_content(headers, content)
+            # TODO(cdent): Using the JSONHandler here instead of
+            # just the json module to make it clear that eventually
+            # we could pretty print any printable output by using a
+            # handler's loads() and dumps(). Not doing that now
+            # because it would be pointless (no other interesting
+            # handlers) and this approach may be entirely wrong.
+            if jsonhandler.JSONHandler.accepts(content_type):
+                data = jsonhandler.JSONHandler.loads(content)
+                content = jsonhandler.JSONHandler.dumps(data, pretty=True)
             self._verbose_output('')
-            self._verbose_output(
-                utils.decode_response_content(headers, content))
+            self._verbose_output(content)
 
     def _print_header(self, name, value, prefix='', stream=None):
         """Output one single header."""


Test patch:
diff --git a/gabbi/tests/gabbits_runner/test_verbose.yaml b/gabbi/tests/gabbits_runner/test_verbose.yaml
new file mode 100644
index 0000000..99b0b0f
--- /dev/null
+++ b/gabbi/tests/gabbits_runner/test_verbose.yaml
@@ -0,0 +1,18 @@
+tests:
+
+- name: POST data with verbose true
+  verbose: true
+  POST: /
+  request_headers:
+    content-type: application/json
+  data:
+    - our text
+
+- name: structured data
+  verbose: true
+  POST: /
+  request_headers:
+      content-type: application/json
+  data:
+     cow: moo
+     dog: bark
diff --git a/gabbi/tests/test_runner.py b/gabbi/tests/test_runner.py
index 2f313a4..bf882ab 100644
--- a/gabbi/tests/test_runner.py
+++ b/gabbi/tests/test_runner.py
@@ -228,6 +228,27 @@ class RunnerTest(unittest.TestCase):
             except SystemExit as err:
                 self.assertSuccess(err)
 
+    def test_verbose_output_formatting(self):
+        """Confirm that a verbose test handles output properly."""
+        sys.argv = ['gabbi-run', 'http://%s:%s/foo' % (self.host, self.port)]
+
+        sys.argv.append('--')
+        sys.argv.append('gabbi/tests/gabbits_runner/test_verbose.yaml')
+        with self.server():
+            try:
+                runner.run()
+            except SystemExit as err:
+                self.assertSuccess(err)
+
+        sys.stdout.seek(0)
+        output = sys.stdout.read()
+        self.assertIn('"our text"', output)
+        self.assertIn('"cow": "moo"', output)
+        self.assertIn('"dog": "bark"', output)
+        # confirm pretty printing
+        self.assertIn('{\n', output)
+        self.assertIn('}\n', output)
+
     def assertSuccess(self, exitError):
         errors = exitError.args[0]
         if errors:
diff --git a/gabbi/tests/test_verbose.yaml b/gabbi/tests/test_verbose.yaml
deleted file mode 100644
index 29a6c64..0000000
--- a/gabbi/tests/test_verbose.yaml
+++ /dev/null
@@ -1,9 +0,0 @@
-tests:
-
-  - name: POST data with verbose true
-    verbose: true
-    POST: /
-    request_headers:
-        content-type: application/json
-    data:
-        'text'