summaryrefslogtreecommitdiffstats
path: root/devtools/client/memory/test/browser/browser_memory_dominator_trees_01.js
blob: 39b984eadd70cfb31064412a537d7ee792bcce00 (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
/* Any copyright is dedicated to the Public Domain.
   http://creativecommons.org/publicdomain/zero/1.0/ */

// Sanity test for dominator trees, their focused nodes, and keyboard navigating
// through nodes across incrementally fetching subtrees.

"use strict";

const {
  dominatorTreeState,
  viewState,
} = require("resource://devtools/client/memory/constants.js");
const {
  expandDominatorTreeNode,
} = require("resource://devtools/client/memory/actions/snapshot.js");
const {
  changeView,
} = require("resource://devtools/client/memory/actions/view.js");

const TEST_URL =
  "http://example.com/browser/devtools/client/memory/test/browser/doc_big_tree.html";

this.test = makeMemoryTest(TEST_URL, async function ({ panel }) {
  // Taking snapshots and computing dominator trees is slow :-/
  requestLongerTimeout(4);

  const store = panel.panelWin.gStore;
  const { getState, dispatch } = store;
  const doc = panel.panelWin.document;

  dispatch(changeView(viewState.DOMINATOR_TREE));

  // Take a snapshot.

  const takeSnapshotButton = doc.getElementById("take-snapshot");
  EventUtils.synthesizeMouseAtCenter(takeSnapshotButton, {}, panel.panelWin);

  // Wait for the dominator tree to be computed and fetched.

  await waitUntilDominatorTreeState(store, [dominatorTreeState.LOADED]);
  ok(true, "Computed and fetched the dominator tree.");

  // Expand all the dominator tree nodes that are eagerly fetched, except for
  // the leaves which will trigger fetching their lazily loaded subtrees.

  const id = getState().snapshots[0].id;
  const root = getState().snapshots[0].dominatorTree.root;
  (function expandAllEagerlyFetched(node = root) {
    if (!node.moreChildrenAvailable || node.children) {
      dispatch(expandDominatorTreeNode(id, node));
    }

    if (node.children) {
      for (const child of node.children) {
        expandAllEagerlyFetched(child);
      }
    }
  })();

  // Find the deepest eagerly loaded node: one which has more children but none
  // of them are loaded.

  const deepest = (function findDeepest(node = root) {
    if (node.moreChildrenAvailable && !node.children) {
      return node;
    }

    if (node.children) {
      for (const child of node.children) {
        const found = findDeepest(child);
        if (found) {
          return found;
        }
      }
    }

    return null;
  })();

  ok(deepest, "Found the deepest node");
  ok(
    !getState().snapshots[0].dominatorTree.expanded.has(deepest.nodeId),
    "The deepest node should not be expanded"
  );

  // Select the deepest node.

  EventUtils.synthesizeMouseAtCenter(
    doc.querySelector(`.node-${deepest.nodeId}`),
    {},
    panel.panelWin
  );
  await waitUntilState(
    store,
    state => state.snapshots[0].dominatorTree.focused.nodeId === deepest.nodeId
  );
  ok(
    doc.querySelector(`.node-${deepest.nodeId}`).classList.contains("focused"),
    "The deepest node should be focused now"
  );

  // Expand the deepest node, which triggers an incremental fetch of its lazily
  // loaded subtree.

  EventUtils.synthesizeKey("VK_RIGHT", {}, panel.panelWin);
  await waitUntilState(store, state =>
    state.snapshots[0].dominatorTree.expanded.has(deepest.nodeId)
  );
  is(
    getState().snapshots[0].dominatorTree.state,
    dominatorTreeState.INCREMENTAL_FETCHING,
    "Expanding the deepest node should start an incremental fetch of its subtree"
  );
  ok(
    doc.querySelector(`.node-${deepest.nodeId}`).classList.contains("focused"),
    "The deepest node should still be focused after expansion"
  );

  // Wait for the incremental fetch to complete.

  await waitUntilState(
    store,
    state =>
      state.snapshots[0].dominatorTree.state === dominatorTreeState.LOADED
  );
  ok(true, "And the incremental fetch completes.");
  ok(
    doc.querySelector(`.node-${deepest.nodeId}`).classList.contains("focused"),
    "The deepest node should still be focused after we have loaded its children"
  );

  // Find the most up-to-date version of the node whose children we just
  // incrementally fetched.

  const newDeepest = (function findNewDeepest(
    node = getState().snapshots[0].dominatorTree.root
  ) {
    if (node.nodeId === deepest.nodeId) {
      return node;
    }

    if (node.children) {
      for (const child of node.children) {
        const found = findNewDeepest(child);
        if (found) {
          return found;
        }
      }
    }

    return null;
  })();

  ok(newDeepest, "We found the up-to-date version of deepest");
  ok(newDeepest.children, "And its children are loaded");
  ok(newDeepest.children.length, "And there are more than 0 children");

  const firstChild = newDeepest.children[0];
  ok(firstChild, "deepest should have a first child");
  ok(
    doc.querySelector(`.node-${firstChild.nodeId}`),
    "and the first child should exist in the dom"
  );

  // Select the newly loaded first child by pressing the right arrow once more.

  EventUtils.synthesizeKey("VK_RIGHT", {}, panel.panelWin);
  await waitUntilState(
    store,
    state => state.snapshots[0].dominatorTree.focused === firstChild
  );
  ok(
    doc
      .querySelector(`.node-${firstChild.nodeId}`)
      .classList.contains("focused"),
    "The first child should now be focused"
  );
});