Add tests for pretty-printing Json subtypes

Bug: 406900
Change-Id: I12a1fcf00cf9381964e36325373647a318c51ec9
Reviewed-on: https://chromium-review.googlesource.com/c/devtools/devtools-frontend/+/4712053
Reviewed-by: Wolfgang Beyer <wolfi@chromium.org>
Commit-Queue: Ioana Forfotă <iforfota@google.com>
diff --git a/test/e2e/helpers/network-helpers.ts b/test/e2e/helpers/network-helpers.ts
index c929c6b..03c4f0a 100644
--- a/test/e2e/helpers/network-helpers.ts
+++ b/test/e2e/helpers/network-helpers.ts
@@ -132,3 +132,11 @@
 
   return [headerNameText.trim(), headerValueText];
 }
+
+export async function elementContainsTextWithSelector(
+    element: puppeteer.ElementHandle<Element>, textContent: string, selector: string): Promise<boolean> {
+  const selectedElements = await element.evaluate((node, selector) => {
+    return [...node.querySelectorAll(selector)].map(node => node.textContent || '') || [];
+  }, selector);
+  return selectedElements.includes(textContent);
+}
diff --git a/test/e2e/helpers/sources-helpers.ts b/test/e2e/helpers/sources-helpers.ts
index 8a8709a..1ebd636 100644
--- a/test/e2e/helpers/sources-helpers.ts
+++ b/test/e2e/helpers/sources-helpers.ts
@@ -871,3 +871,9 @@
 export async function waitForLines(lineCount: number): Promise<void> {
   await waitFor(new Array(lineCount).fill('.cm-line').join(' ~ '));
 }
+
+export async function isPrettyPrinted(): Promise<boolean> {
+  const prettyButton = await waitFor('[aria-label="Pretty print"]');
+  const isPretty = await prettyButton.evaluate(e => e.ariaPressed);
+  return isPretty === 'true';
+}
diff --git a/test/e2e/network/BUILD.gn b/test/e2e/network/BUILD.gn
index 5a76b7f..7c4ad89 100644
--- a/test/e2e/network/BUILD.gn
+++ b/test/e2e/network/BUILD.gn
@@ -6,6 +6,7 @@
 
 node_ts_library("network") {
   sources = [
+    "can-pretty-print-network_test.ts",
     "network-conditions_test.ts",
     "network-datagrid_test.ts",
     "network-filter_test.ts",
diff --git a/test/e2e/network/can-pretty-print-network_test.ts b/test/e2e/network/can-pretty-print-network_test.ts
new file mode 100644
index 0000000..09f1549
--- /dev/null
+++ b/test/e2e/network/can-pretty-print-network_test.ts
@@ -0,0 +1,90 @@
+// Copyright 2023 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import {assert} from 'chai';
+
+import {
+  click,
+  step,
+  waitFor,
+} from '../../shared/helper.js';
+import {
+  elementContainsTextWithSelector,
+  navigateToNetworkTab,
+  selectRequestByName,
+  waitForSomeRequestsToAppear,
+} from '../helpers/network-helpers.js';
+import {
+  isPrettyPrinted,
+  retrieveCodeMirrorEditorContent,
+} from '../helpers/sources-helpers.js';
+
+const PRETTY_PRINT_BUTTON = '[aria-label="Pretty print"]';
+
+describe('The Network Tab', function() {
+  it('can pretty print an inline json subtype file', async () => {
+    await navigateToNetworkTab('code-with-json-subtype-request.html');
+    await waitForSomeRequestsToAppear(2);
+    await selectRequestByName('json-subtype-ld.rawresponse');
+
+    const networkView = await waitFor('.network-item-view');
+    await click('#tab-headersComponent', {
+      root: networkView,
+    });
+
+    await click('[aria-label=Response][role="tab"]', {
+      root: networkView,
+    });
+    await waitFor('[aria-label=Response][role=tab][aria-selected=true]', networkView);
+
+    const editor = await waitFor('[aria-label="Code editor"]');
+
+    await step('can pretty-print a json subtype', async () => {
+      const textFromResponse = await retrieveCodeMirrorEditorContent();
+
+      const expectedTextFromResponse = [
+        '{',
+        '    "Keys": [',
+        '        {',
+        '            "Key1": "Value1",',
+        '            "Key2": "Value2",',
+        '            "Key3": true',
+        '        },',
+        '        {',
+        '            "Key1": "Value1",',
+        '            "Key2": "Value2",',
+        '            "Key3": false',
+        '        }',
+        '    ]',
+        '}',
+      ];
+
+      assert.deepStrictEqual(textFromResponse, expectedTextFromResponse);
+    });
+
+    await step('can highlight the pretty-printed text', async () => {
+      assert.isTrue(await isPrettyPrinted());
+      assert.isTrue(await elementContainsTextWithSelector(editor, '"Value1"', '.token-string'));
+
+      assert.isTrue(await elementContainsTextWithSelector(editor, 'true', '.token-atom'));
+    });
+
+    await click(PRETTY_PRINT_BUTTON);
+
+    await step('can un-pretty-print a json subtype', async () => {
+      const actualNotPrettyText = await retrieveCodeMirrorEditorContent();
+      const expectedNotPrettyText =
+          '{"Keys": [{"Key1": "Value1","Key2": "Value2","Key3": true},{"Key1": "Value1","Key2": "Value2","Key3": false}]}';
+
+      assert.strictEqual(expectedNotPrettyText, actualNotPrettyText.toString());
+    });
+
+    await step('can highlight the un-pretty-printed text', async () => {
+      assert.isFalse(await isPrettyPrinted());
+      assert.isTrue(await elementContainsTextWithSelector(editor, '"Value1"', '.token-string'));
+
+      assert.isTrue(await elementContainsTextWithSelector(editor, 'true', '.token-atom'));
+    });
+  });
+});
diff --git a/test/e2e/resources/network/BUILD.gn b/test/e2e/resources/network/BUILD.gn
index e6adfde..41c6c36 100644
--- a/test/e2e/resources/network/BUILD.gn
+++ b/test/e2e/resources/network/BUILD.gn
@@ -9,6 +9,7 @@
     "cacheable.rawresponse",
     "chunked.txt",
     "chunked_sync.html",
+    "code-with-json-subtype-request.html",
     "coffees.json",
     "csp-report-only.rawresponse",
     "embedded_requests.html",
@@ -18,6 +19,7 @@
     "hello.html",
     "image.svg",
     "infinite-requests.html",
+    "json-subtype-ld.rawresponse",
     "last-modified.html",
     "last-modified.rawresponse",
     "requests.html",
diff --git a/test/e2e/resources/network/code-with-json-subtype-request.html b/test/e2e/resources/network/code-with-json-subtype-request.html
new file mode 100644
index 0000000..6d96923
--- /dev/null
+++ b/test/e2e/resources/network/code-with-json-subtype-request.html
@@ -0,0 +1,8 @@
+<!--
+  Copyright 2023 The Chromium Authors. All rights reserved.
+  Use of this source code is governed by a BSD-style license that can be
+  found in the LICENSE file.
+-->
+<script>
+  fetch("json-subtype-ld.rawresponse");
+</script>
diff --git a/test/e2e/resources/network/json-subtype-ld.rawresponse b/test/e2e/resources/network/json-subtype-ld.rawresponse
new file mode 100644
index 0000000..23d2f65
--- /dev/null
+++ b/test/e2e/resources/network/json-subtype-ld.rawresponse
@@ -0,0 +1,6 @@
+200
+Date: Sun, 10 Oct 2010 23:26:07 GMT
+Connection: close
+Content-Type: application/ld+json; charset=utf-8
+
+{"Keys": [{"Key1": "Value1","Key2": "Value2","Key3": true},{"Key1": "Value1","Key2": "Value2","Key3": false}]}
\ No newline at end of file
diff --git a/test/e2e/sources/can-pretty-print-sourcecode_test.ts b/test/e2e/sources/can-pretty-print-sourcecode_test.ts
index e99c5f1..65333fb 100644
--- a/test/e2e/sources/can-pretty-print-sourcecode_test.ts
+++ b/test/e2e/sources/can-pretty-print-sourcecode_test.ts
@@ -16,9 +16,11 @@
   waitForNone,
 } from '../../shared/helper.js';
 import {beforeEach, describe, it} from '../../shared/mocha-extensions.js';
+import {elementContainsTextWithSelector} from '../helpers/network-helpers.js';
 import {openGoToLineQuickOpen} from '../helpers/quick_open-helpers.js';
 import {
   addBreakpointForLine,
+  isPrettyPrinted,
   openSourceCodeEditorForFile,
   retrieveCodeMirrorEditorContent,
   retrieveTopCallFrameScriptLocation,
@@ -88,6 +90,54 @@
     });
   });
 
+  it('can pretty print an inline json subtype file', async () => {
+    await openSourceCodeEditorForFile('json-subtype-ld.rawresponse', '../network/json-subtype-ld.rawresponse');
+    const editor = await waitFor('[aria-label="Code editor"]');
+
+    await step('can pretty-print a json subtype', async () => {
+      const expectedPrettyLines = [
+        '{',
+        '    "Keys": [',
+        '        {',
+        '            "Key1": "Value1",',
+        '            "Key2": "Value2",',
+        '            "Key3": true',
+        '        },',
+        '        {',
+        '            "Key1": "Value1",',
+        '            "Key2": "Value2",',
+        '            "Key3": false',
+        '        }',
+        '    ]',
+        '}',
+      ];
+      const actualPrettyText = await retrieveCodeMirrorEditorContent();
+      assert.deepStrictEqual(expectedPrettyLines, actualPrettyText);
+    });
+
+    await step('can highlight the pretty-printed text', async () => {
+      assert.isTrue(await isPrettyPrinted());
+      assert.isTrue(await elementContainsTextWithSelector(editor, '"Value1"', '.token-string'));
+
+      assert.isTrue(await elementContainsTextWithSelector(editor, 'true', '.token-atom'));
+    });
+
+    await step('can un-pretty-print a json subtype file', async () => {
+      await click(PRETTY_PRINT_BUTTON);
+      const expectedNotPrettyLines =
+          '{"Keys": [{"Key1": "Value1","Key2": "Value2","Key3": true},{"Key1": "Value1","Key2": "Value2","Key3": false}]}';
+      const actualNotPrettyText = await retrieveCodeMirrorEditorContent();
+      assert.strictEqual(expectedNotPrettyLines, actualNotPrettyText.toString());
+    });
+
+    await step('can highlight the un-pretty-printed text', async () => {
+      assert.isFalse(await isPrettyPrinted());
+      assert.isTrue(await elementContainsTextWithSelector(editor, '"Value1"', '.token-string'));
+
+      assert.isTrue(await elementContainsTextWithSelector(editor, 'true', '.token-atom'));
+    });
+  });
+
   it('can show error icons for pretty-printed file', async () => {
     await openSourceCodeEditorForFile('minified-errors.js', 'minified-errors.html');