summaryrefslogtreecommitdiffstats
path: root/devtools/client/inspector/extensions/test/head_devtools_inspector_sidebar.js
blob: 73467c3e31c06b440ae2c1b3531388440a43cb94 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

/* exported getExtensionSidebarActors, expectNoSuchActorIDs,
            waitForObjectInspector, testSetExpressionSidebarPanel, assertTreeView,
            assertObjectInspector, moveMouseOnObjectInspectorDOMNode,
            moveMouseOnPanelCenter, clickOpenInspectorIcon */

"use strict";

const ACCORDION_LABEL_SELECTOR = ".accordion-header-label";
const ACCORDION_CONTENT_SELECTOR = ".accordion-content";

// Retrieve the array of all the objectValueGrip actors from the
// inspector extension sidebars state
// (used in browser_ext_devtools_panels_elements_sidebar.js).
function getExtensionSidebarActors(inspector) {
  const state = inspector.store.getState();

  const actors = [];

  for (const sidebarId of Object.keys(state.extensionsSidebar)) {
    const sidebarState = state.extensionsSidebar[sidebarId];

    if (
      sidebarState.viewMode === "object-value-grip-view" &&
      sidebarState.objectValueGrip &&
      sidebarState.objectValueGrip.actor
    ) {
      actors.push(sidebarState.objectValueGrip.actor);
    }
  }

  return actors;
}

// Test that the specified objectValueGrip actors have been released
// on the remote debugging server
// (used in browser_ext_devtools_panels_elements_sidebar.js).
async function expectNoSuchActorIDs(client, actors) {
  info(`Test that all the objectValueGrip actors have been released`);
  for (const actor of actors) {
    await Assert.rejects(
      client.request({ to: actor, type: "requestTypes" }),
      err => err.message == `No such actor for ID: ${actor}`
    );
  }
}

function waitForObjectInspector(panelDoc, waitForNodeWithType = "object") {
  const selector = `.object-inspector .objectBox-${waitForNodeWithType}`;
  return TestUtils.waitForCondition(() => {
    return !!panelDoc.querySelectorAll(selector).length;
  }, `Wait for objectInspector's node type "${waitForNodeWithType}" to be loaded`);
}

// Helper function used inside the sidebar.setExtensionPage test case.
async function testSetExtensionPageSidebarPanel(panelDoc, expectedURL) {
  const selector = "iframe.inspector-extension-sidebar-page";
  const iframesCount = await TestUtils.waitForCondition(() => {
    return panelDoc.querySelectorAll(selector).length;
  }, "Wait for the extension page iframe");

  is(
    iframesCount,
    1,
    "Got the expected number of iframes in the extension panel"
  );

  const iframeWindow = panelDoc.querySelector(selector).contentWindow;
  await TestUtils.waitForCondition(() => {
    return iframeWindow.document.readyState === "complete";
  }, "Wait for the extension page iframe to complete to load");

  is(
    iframeWindow.location.href,
    expectedURL,
    "Got the expected url in the extension panel iframe"
  );
}

// Helper function used inside the sidebar.setObjectValueGrip test case.
async function testSetExpressionSidebarPanel(panel, expected) {
  const { nodesLength, propertiesNames, rootTitle } = expected;

  await waitForObjectInspector(panel);

  const objectInspectors = [...panel.querySelectorAll(".tree")];
  is(
    objectInspectors.length,
    1,
    "There is the expected number of object inspectors"
  );
  const [objectInspector] = objectInspectors;

  await TestUtils.waitForCondition(() => {
    return objectInspector.querySelectorAll(".node").length >= nodesLength;
  }, "Wait the objectInspector to have been fully rendered");

  const oiNodes = objectInspector.querySelectorAll(".node");

  is(
    oiNodes.length,
    nodesLength,
    "Got the expected number of nodes in the tree"
  );
  const propertiesNodes = [
    ...objectInspector.querySelectorAll(".object-label"),
  ].map(el => el.textContent);
  is(
    JSON.stringify(propertiesNodes),
    JSON.stringify(propertiesNames),
    "Got the expected property names"
  );

  if (rootTitle) {
    // Also check that the ObjectInspector is rendered inside
    // an Accordion component with the expected title.
    const accordion = panel.querySelector(".accordion");

    ok(accordion, "Got an Accordion component as expected");

    is(
      accordion.querySelector(ACCORDION_CONTENT_SELECTOR).firstChild,
      objectInspector,
      "The ObjectInspector should be inside the Accordion content"
    );

    is(
      accordion.querySelector(ACCORDION_LABEL_SELECTOR).textContent,
      rootTitle,
      "The Accordion has the expected label"
    );
  } else {
    // Also check that there is no Accordion component rendered
    // inside the sidebar panel.
    ok(
      !panel.querySelector(".accordion"),
      "Got no Accordion component as expected"
    );
  }
}

function assertTreeView(panelDoc, expectedContent) {
  const { expectedTreeTables, expectedStringCells, expectedNumberCells } =
    expectedContent;

  if (expectedTreeTables) {
    is(
      panelDoc.querySelectorAll("table.treeTable").length,
      expectedTreeTables,
      "The panel document contains the expected number of TreeView components"
    );
  }

  if (expectedStringCells) {
    is(
      panelDoc.querySelectorAll("table.treeTable .stringCell").length,
      expectedStringCells,
      "The panel document contains the expected number of string cells."
    );
  }

  if (expectedNumberCells) {
    is(
      panelDoc.querySelectorAll("table.treeTable .numberCell").length,
      expectedNumberCells,
      "The panel document contains the expected number of number cells."
    );
  }
}

async function assertObjectInspector(panelDoc, expectedContent) {
  const { expectedDOMNodes, expectedOpenInspectors } = expectedContent;

  // Get and verify the DOMNode and the "open inspector"" icon
  // rendered inside the ObjectInspector.
  const nodes = panelDoc.querySelectorAll(".objectBox-node");
  const nodeOpenInspectors = panelDoc.querySelectorAll(
    ".objectBox-node .open-inspector"
  );

  is(
    nodes.length,
    expectedDOMNodes,
    "Found the expected number of ObjectInspector DOMNodes"
  );
  is(
    nodeOpenInspectors.length,
    expectedOpenInspectors,
    "Found the expected nuber of open-inspector icons inside the ObjectInspector"
  );
}

function moveMouseOnObjectInspectorDOMNode(panelDoc, nodeIndex = 0) {
  const nodes = panelDoc.querySelectorAll(".objectBox-node");
  const node = nodes[nodeIndex];

  ok(node, "Found the ObjectInspector DOMNode");

  EventUtils.synthesizeMouseAtCenter(
    node,
    { type: "mousemove" },
    node.ownerDocument.defaultView
  );
}

function moveMouseOnPanelCenter(panelDoc) {
  EventUtils.synthesizeMouseAtCenter(
    panelDoc,
    { type: "mousemove" },
    panelDoc.window
  );
}

function clickOpenInspectorIcon(panelDoc, nodeIndex = 0) {
  const nodes = panelDoc.querySelectorAll(".objectBox-node .open-inspector");
  const node = nodes[nodeIndex];

  ok(node, "Found the ObjectInspector open-inspector icon");

  node.click();
}