Repository: firebase/firebase-tools
Pull request #1533 (2019-07-22 18:05:05)

Fix functions:shell logging
<!--

Thank you for contributing to the Firebase community! Please fill out the form below.

Run the linter and test suite
==============================
Run `npm test` to make sure your changes compile properly and the tests all pass on your local machine. We've hooked up this repo with continuous integration to double check those things for you. 

-->


### Description

Fixes an issue where `functions:shell` logs would sometimes be double-escaped.
	 
### Scenarios Tested

```js
exports.helloJSON = functions.https.onRequest(async (request, response) => {
    response.json({
        a: 1,
        b: {
            c: 2,
            d: 3
        }
    });
});
```

**Before (7.1.1)**
```bash
$ firebase functions:shell
# ...
firebase > helloJSON()
Sent request to function.
firebase > 
RESPONSE RECEIVED FROM FUNCTION: 200, "{\"a\":1,\"b\":{\"c\":2,\"d\":3}}"
```

**After**
```bash
$ npx firebase functions:shell
# ...
firebase > helloJSON()
Sent request to function.
firebase > 
RESPONSE RECEIVED FROM FUNCTION: 200, {
  "a": 1,
  "b": {
    "c": 2,
    "d": 3
  }
}
```

### Sample Commands

<!-- Proposing a change to commands or flags? Provide examples of how they will be used. -->


coveralls: 
[![Coverage Status](https://coveralls.io/builds/24723275/badge)](https://coveralls.io/builds/24723275)

Coverage decreased (-0.07%) to 61.658% when pulling **8bbadabac42d8eade5bdbe1ec366266dba337f67 on ss-functions-shell-logs** into **4e892112108b6cb4d7885ffaa2febf02db35ff0d on master**.


Patch:
diff --git a/changelog.txt b/changelog.txt
index 5718fd629..58f3bc44f 100644
--- a/changelog.txt
+++ b/changelog.txt
@@ -2,3 +2,4 @@
 * Allows emulated Cloud Functions to talk to production RTDB/Firestore if the emulators are not running.
 * Fixes an issue where internal logs would sometimes appear in stdout.
 * Improves RTDB emulator support for JWT algorithms and arbitrary Google Cloud auth tokens.
+* Fixes an issue where functions:shell logs could be double escaped.
\ No newline at end of file
diff --git a/src/localFunction.js b/src/localFunction.js
index f8310577b..8e7fb1d16 100644
--- a/src/localFunction.js
+++ b/src/localFunction.js
@@ -127,9 +127,22 @@ LocalFunction.prototype._requestCallBack = function(err, response, body) {
     return console.warn("\nERROR SENDING REQUEST: " + err);
   }
   var status = response ? response.statusCode + ", " : "";
-  return console.log(
-    "\nRESPONSE RECEIVED FROM FUNCTION: " + status + JSON.stringify(body, null, 2)
-  );
+
+  // If the body is a string we want to check if we can parse it as JSON
+  // and pretty-print it. We can't blindly stringify because stringifying
+  // a string results in some ugly escaping.
+  var bodyString = body;
+  if (typeof body === "string" || body instanceof string) {
+    try {
+      bodyString = JSON.stringify(JSON.parse(bodyString), null, 2);
+    } catch (e) {
+      // Ignore
+    }
+  } else {
+    bodyString = JSON.stringify(body, null, 2);
+  }
+
+  return console.log("\nRESPONSE RECEIVED FROM FUNCTION: " + status + bodyString);
 };
 
 LocalFunction.prototype._call = function(data, opts) {


Test patch:
diff --git a/scripts/triggers-end-to-end-tests/run.spec.js b/scripts/triggers-end-to-end-tests/run.spec.js
index 6031a4357..18bfd8425 100755
--- a/scripts/triggers-end-to-end-tests/run.spec.js
+++ b/scripts/triggers-end-to-end-tests/run.spec.js
@@ -30,9 +30,10 @@ const FIRESTORE_FUNCTION_LOG = "========== FIRESTORE FUNCTION ==========";
  * parallel emulator subprocesses.
  */
 const TEST_SETUP_TIMEOUT = 20000;
-const EMULATORS_STARTUP_DELAY_MS = 7000;
+const EMULATORS_STARTUP_DELAY_MS = 10000;
 const EMULATORS_WRITE_DELAY_MS = 5000;
-const EMULATORS_SHUTDOWN_DELAY_MS = 2000;
+const EMULATORS_SHUTDOWN_DELAY_MS = 5000;
+const EMULATOR_TEST_TIMEOUT = EMULATORS_WRITE_DELAY_MS * 2;
 
 /*
  * Realtime Database and Firestore documents we used to verify
@@ -170,9 +171,11 @@ describe("database and firestore emulator function triggers", function() {
   var test;
 
   before(function(done) {
+    this.timeout(TEST_SETUP_TIMEOUT);
+
     expect(FIREBASE_PROJECT).to.not.be.an("undefined");
     expect(FIREBASE_PROJECT).to.not.be.null;
-    this.timeout(TEST_SETUP_TIMEOUT);
+
     async.series(
       [
         function(done) {
@@ -285,7 +288,7 @@ describe("database and firestore emulator function triggers", function() {
   });
 
   it("should write to the database emulator", function(done) {
-    this.timeout(EMULATORS_WRITE_DELAY_MS);
+    this.timeout(EMULATOR_TEST_TIMEOUT);
 
     test.writeToRtdb(function(err, response) {
       expect(err).to.be.null;
@@ -295,7 +298,7 @@ describe("database and firestore emulator function triggers", function() {
   });
 
   it("should write to the firestore emulator", function(done) {
-    this.timeout(EMULATORS_WRITE_DELAY_MS * 2);
+    this.timeout(EMULATOR_TEST_TIMEOUT);
 
     test.writeToFirestore(function(err, response) {
       expect(err).to.be.null;