summaryrefslogtreecommitdiffstats
path: root/devtools/shared/commands/resource/tests/browser_resources_network_events_cache.js
blob: 6708ef19e12e50645a1db923ff5e72e5ddbbdff0 (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
225
226
227
228
229
230
231
232
233
234
235
236
/* Any copyright is dedicated to the Public Domain.
   http://creativecommons.org/publicdomain/zero/1.0/ */

"use strict";

// Test the ResourceCommand API internal cache / ignoreExistingResources around NETWORK_EVENT

const ResourceCommand = require("resource://devtools/shared/commands/resource/resource-command.js");

const EXAMPLE_DOMAIN = "https://example.com/";
const TEST_URI = `${URL_ROOT_SSL}network_document.html`;

add_task(async function () {
  info("Test basic NETWORK_EVENT resources against ResourceCommand cache");
  await testNetworkEventResourcesWithExistingResources();
  await testNetworkEventResourcesWithoutExistingResources();
});

async function testNetworkEventResourcesWithExistingResources() {
  info(`Tests for network event resources with the existing resources`);
  await testNetworkEventResourcesWithCachedRequest({
    ignoreExistingResources: false,
    // 1 available event fired, for the existing resource in the cache.
    // 1 available event fired, when live request is created.
    expectedResourcesOnAvailable: {
      [`${EXAMPLE_DOMAIN}cached_post.html`]: {
        resourceType: ResourceCommand.TYPES.NETWORK_EVENT,
        method: "POST",
        isNavigationRequest: false,
      },
      [`${EXAMPLE_DOMAIN}live_get.html`]: {
        resourceType: ResourceCommand.TYPES.NETWORK_EVENT,
        method: "GET",
        isNavigationRequest: false,
      },
    },
    // 1 update events fired, when live request is updated.
    expectedResourcesOnUpdated: {
      [`${EXAMPLE_DOMAIN}live_get.html`]: {
        resourceType: ResourceCommand.TYPES.NETWORK_EVENT,
        method: "GET",
      },
    },
  });
}

async function testNetworkEventResourcesWithoutExistingResources() {
  info(`Tests for network event resources without the existing resources`);
  await testNetworkEventResourcesWithCachedRequest({
    ignoreExistingResources: true,
    // 1 available event fired, when live request is created.
    expectedResourcesOnAvailable: {
      [`${EXAMPLE_DOMAIN}live_get.html`]: {
        resourceType: ResourceCommand.TYPES.NETWORK_EVENT,
        method: "GET",
        isNavigationRequest: false,
      },
    },
    // 1 update events fired, when live request is updated.
    expectedResourcesOnUpdated: {
      [`${EXAMPLE_DOMAIN}live_get.html`]: {
        resourceType: ResourceCommand.TYPES.NETWORK_EVENT,
        method: "GET",
      },
    },
  });
}

/**
 * This test helper is slightly complex as we workaround the fact
 * that the server is not able to record network request done in the past.
 * Because of that we have to start observer requests via ResourceCommand.watchResources
 * before doing a request, and, before doing the actual call to watchResources
 * we want to assert the behavior of.
 */
async function testNetworkEventResourcesWithCachedRequest(options) {
  const tab = await addTab(TEST_URI);
  const commands = await CommandsFactory.forTab(tab);
  await commands.targetCommand.startListening();

  const { resourceCommand } = commands;

  info(
    `Trigger some network requests *before* calling ResourceCommand.watchResources
     in order to assert the behavior of already existing network events.`
  );

  // Register a first empty listener in order to ensure populating ResourceCommand
  // internal cache of NETWORK_EVENT's. We can't retrieved past network requests
  // when calling server's `watchResources`.
  let resolveCachedRequestAvailable;
  const onCachedRequestAvailable = new Promise(
    r => (resolveCachedRequestAvailable = r)
  );
  const onAvailableToPopulateInternalCache = () => {};
  const onUpdatedToPopulateInternalCache = resolveCachedRequestAvailable;
  await resourceCommand.watchResources([resourceCommand.TYPES.NETWORK_EVENT], {
    ignoreExistingResources: true,
    onAvailable: onAvailableToPopulateInternalCache,
    onUpdated: onUpdatedToPopulateInternalCache,
  });

  // We can only trigger the requests once `watchResources` settles,
  // otherwise we might miss some events and they won't be present in the cache
  const cachedRequest = `await fetch("/cached_post.html", { method: "POST" });`;
  await triggerNetworkRequests(tab.linkedBrowser, [cachedRequest]);

  // We have to ensure that ResourceCommand processed the Resource for this first
  // cached request before calling watchResource a second time and report it.
  // Wait for the updated notification to avoid receiving it during the next call
  // to watchResources.
  await onCachedRequestAvailable;

  const actualResourcesOnAvailable = {};
  const actualResourcesOnUpdated = {};

  const {
    expectedResourcesOnAvailable,
    expectedResourcesOnUpdated,

    ignoreExistingResources,
  } = options;

  const onAvailable = resources => {
    for (const resource of resources) {
      is(
        resource.resourceType,
        resourceCommand.TYPES.NETWORK_EVENT,
        "Received a network event resource"
      );
      actualResourcesOnAvailable[resource.url] = resource;
    }
  };

  const onUpdated = updates => {
    for (const { resource } of updates) {
      is(
        resource.resourceType,
        resourceCommand.TYPES.NETWORK_EVENT,
        "Received a network update event resource"
      );
      actualResourcesOnUpdated[resource.url] = resource;
    }
  };

  await resourceCommand.watchResources([resourceCommand.TYPES.NETWORK_EVENT], {
    onAvailable,
    onUpdated,
    ignoreExistingResources,
  });

  info(
    `Trigger the rest of the requests *after* calling ResourceCommand.watchResources
     in order to assert the behavior of live network events.`
  );
  const liveRequest = `await fetch("/live_get.html", { method: "GET" });`;
  await triggerNetworkRequests(tab.linkedBrowser, [liveRequest]);

  info("Check the resources on available");

  await waitUntil(
    () =>
      Object.keys(actualResourcesOnAvailable).length ==
      Object.keys(expectedResourcesOnAvailable).length
  );

  is(
    Object.keys(actualResourcesOnAvailable).length,
    Object.keys(expectedResourcesOnAvailable).length,
    "Got the expected number of network events fired onAvailable"
  );

  // assert the resources emitted when the network event is created
  for (const key in expectedResourcesOnAvailable) {
    const expected = expectedResourcesOnAvailable[key];
    const actual = actualResourcesOnAvailable[key];
    assertResources(actual, expected);
  }

  info("Check the resources on updated");

  await waitUntil(
    () =>
      Object.keys(actualResourcesOnUpdated).length ==
      Object.keys(expectedResourcesOnUpdated).length
  );

  is(
    Object.keys(actualResourcesOnUpdated).length,
    Object.keys(expectedResourcesOnUpdated).length,
    "Got the expected number of network events fired onUpdated"
  );

  // assert the resources emitted when the network event is updated
  for (const key in expectedResourcesOnUpdated) {
    const expected = expectedResourcesOnUpdated[key];
    const actual = actualResourcesOnUpdated[key];
    assertResources(actual, expected);
    // assert that the resourceId for the the available and updated events match
    is(
      actual.resourceId,
      actualResourcesOnAvailable[key].resourceId,
      `Available and update resource ids for ${key} are the same`
    );
  }

  resourceCommand.unwatchResources([resourceCommand.TYPES.NETWORK_EVENT], {
    onAvailable,
    onUpdated,
    ignoreExistingResources,
  });

  resourceCommand.unwatchResources([resourceCommand.TYPES.NETWORK_EVENT], {
    onAvailable: onAvailableToPopulateInternalCache,
  });

  await commands.destroy();

  BrowserTestUtils.removeTab(tab);
}

function assertResources(actual, expected) {
  is(
    actual.resourceType,
    expected.resourceType,
    "The resource type is correct"
  );
  is(actual.method, expected.method, "The method is correct");
  if ("isNavigationRequest" in expected) {
    is(
      actual.isNavigationRequest,
      expected.isNavigationRequest,
      "The isNavigationRequest attribute is correct"
    );
  }
}