lighthouse 12.0.0 → 12.1.0-dev.20240619

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (219) hide show
  1. package/cli/run.js +5 -38
  2. package/cli/test/smokehouse/lighthouse-runners/bundle.js +1 -1
  3. package/cli/test/smokehouse/smokehouse.js +11 -1
  4. package/core/audits/accessibility/aria-allowed-role.js +7 -9
  5. package/core/audits/byte-efficiency/byte-efficiency-audit.d.ts +2 -2
  6. package/core/audits/byte-efficiency/byte-efficiency-audit.js +3 -3
  7. package/core/audits/byte-efficiency/offscreen-images.js +1 -1
  8. package/core/audits/byte-efficiency/render-blocking-resources.d.ts +3 -3
  9. package/core/audits/byte-efficiency/render-blocking-resources.js +10 -10
  10. package/core/audits/dobetterweb/uses-http2.d.ts +2 -2
  11. package/core/audits/dobetterweb/uses-http2.js +5 -5
  12. package/core/audits/long-tasks.d.ts +7 -6
  13. package/core/audits/long-tasks.js +5 -4
  14. package/core/audits/metrics/first-meaningful-paint.d.ts +1 -17
  15. package/core/audits/metrics/first-meaningful-paint.js +5 -47
  16. package/core/audits/metrics/interactive.d.ts +1 -1
  17. package/core/audits/metrics/interactive.js +1 -1
  18. package/core/audits/predictive-perf.js +0 -6
  19. package/core/audits/prioritize-lcp-image.d.ts +1 -1
  20. package/core/audits/prioritize-lcp-image.js +2 -2
  21. package/core/audits/redirects.js +2 -3
  22. package/core/audits/third-party-facades.js +1 -1
  23. package/core/audits/third-party-summary.js +1 -1
  24. package/core/audits/uses-rel-preconnect.js +2 -2
  25. package/core/audits/uses-rel-preload.js +9 -9
  26. package/core/computed/critical-request-chains.js +3 -3
  27. package/core/computed/document-urls.js +3 -2
  28. package/core/computed/load-simulator.d.ts +4 -4
  29. package/core/computed/load-simulator.js +3 -54
  30. package/core/computed/main-resource.js +3 -2
  31. package/core/computed/metrics/interactive.d.ts +6 -0
  32. package/core/computed/metrics/interactive.js +2 -2
  33. package/core/computed/metrics/lantern-first-contentful-paint.d.ts +4 -4
  34. package/core/computed/metrics/lantern-first-contentful-paint.js +3 -3
  35. package/core/computed/metrics/lantern-interactive.d.ts +4 -4
  36. package/core/computed/metrics/lantern-interactive.js +6 -6
  37. package/core/computed/metrics/lantern-largest-contentful-paint.d.ts +4 -4
  38. package/core/computed/metrics/lantern-largest-contentful-paint.js +3 -3
  39. package/core/computed/metrics/lantern-max-potential-fid.d.ts +4 -4
  40. package/core/computed/metrics/lantern-max-potential-fid.js +3 -3
  41. package/core/computed/metrics/lantern-metric.d.ts +21 -2
  42. package/core/computed/metrics/lantern-metric.js +39 -4
  43. package/core/computed/metrics/lantern-speed-index.d.ts +4 -4
  44. package/core/computed/metrics/lantern-speed-index.js +4 -4
  45. package/core/computed/metrics/lantern-total-blocking-time.d.ts +4 -4
  46. package/core/computed/metrics/lantern-total-blocking-time.js +3 -3
  47. package/core/computed/metrics/timing-summary.js +0 -6
  48. package/core/computed/metrics/total-blocking-time.js +3 -1
  49. package/core/computed/navigation-insights.d.ts +2 -20
  50. package/core/computed/network-analysis.d.ts +0 -5
  51. package/core/computed/network-analysis.js +2 -42
  52. package/core/computed/page-dependency-graph.d.ts +5 -3
  53. package/core/computed/page-dependency-graph.js +17 -7
  54. package/core/computed/processed-navigation.d.ts +1 -1
  55. package/core/computed/tbt-impact-tasks.js +4 -2
  56. package/core/computed/trace-engine-result.d.ts +0 -11
  57. package/core/computed/trace-engine-result.js +5 -19
  58. package/core/config/constants.d.ts +28 -53
  59. package/core/config/constants.js +2 -43
  60. package/core/gather/driver/navigation.js +1 -1
  61. package/core/gather/driver/network-monitor.d.ts +2 -1
  62. package/core/gather/driver/network-monitor.js +4 -4
  63. package/core/gather/driver/target-manager.d.ts +1 -1
  64. package/core/gather/driver.d.ts +0 -12
  65. package/core/gather/driver.js +1 -26
  66. package/core/gather/gatherers/inspector-issues.js +1 -0
  67. package/core/gather/gatherers/root-causes.js +0 -1
  68. package/core/gather/session.d.ts +5 -3
  69. package/core/gather/session.js +22 -1
  70. package/core/lib/asset-saver.d.ts +13 -9
  71. package/core/lib/asset-saver.js +77 -30
  72. package/core/lib/bf-cache-strings.js +2 -2
  73. package/core/lib/lantern/{base-node.d.ts → BaseNode.d.ts} +7 -9
  74. package/core/lib/lantern/{base-node.js → BaseNode.js} +6 -6
  75. package/core/lib/lantern/BaseNode.test.js +385 -0
  76. package/core/lib/lantern/CpuNode.d.ts +43 -0
  77. package/core/lib/lantern/{cpu-node.js → CpuNode.js} +8 -9
  78. package/core/lib/lantern/{lantern-error.d.ts → LanternError.d.ts} +1 -1
  79. package/core/lib/lantern/{metric.d.ts → Metric.d.ts} +23 -25
  80. package/core/lib/lantern/{metric.js → Metric.js} +14 -16
  81. package/core/lib/lantern/{network-node.d.ts → NetworkNode.d.ts} +6 -7
  82. package/core/lib/lantern/{network-node.js → NetworkNode.js} +7 -9
  83. package/core/lib/lantern/PageDependencyGraph.d.ts +82 -0
  84. package/core/lib/lantern/{page-dependency-graph.js → PageDependencyGraph.js} +185 -50
  85. package/core/lib/lantern/PageDependencyGraph.test.js +654 -0
  86. package/core/{computed/metrics/tbt-utils.d.ts → lib/lantern/TBTUtils.d.ts} +1 -1
  87. package/core/lib/lantern/TBTUtils.test.d.ts +2 -0
  88. package/core/lib/lantern/TBTUtils.test.js +130 -0
  89. package/core/lib/lantern/TraceEngineComputationData.d.ts +25 -0
  90. package/core/lib/lantern/TraceEngineComputationData.js +466 -0
  91. package/core/lib/lantern/lantern.d.ts +19 -7
  92. package/core/lib/lantern/lantern.js +21 -1
  93. package/core/lib/lantern/metrics/{first-contentful-paint.d.ts → FirstContentfulPaint.d.ts} +11 -11
  94. package/core/lib/lantern/metrics/{first-contentful-paint.js → FirstContentfulPaint.js} +11 -13
  95. package/core/lib/lantern/metrics/FirstContentfulPaint.test.js +54 -0
  96. package/core/lib/lantern/metrics/Interactive.d.ts +20 -0
  97. package/core/lib/lantern/metrics/{interactive.js → Interactive.js} +23 -26
  98. package/core/lib/lantern/metrics/Interactive.test.js +56 -0
  99. package/core/lib/lantern/metrics/LargestContentfulPaint.d.ts +19 -0
  100. package/core/lib/lantern/metrics/{largest-contentful-paint.js → LargestContentfulPaint.js} +16 -19
  101. package/core/lib/lantern/metrics/LargestContentfulPaint.test.js +42 -0
  102. package/core/lib/lantern/metrics/MaxPotentialFID.d.ts +24 -0
  103. package/core/lib/lantern/metrics/{max-potential-fid.js → MaxPotentialFID.js} +12 -13
  104. package/core/lib/lantern/metrics/MetricTestUtils.d.ts +19 -0
  105. package/core/lib/lantern/metrics/MetricTestUtils.js +48 -0
  106. package/core/lib/lantern/metrics/{speed-index.d.ts → SpeedIndex.d.ts} +7 -13
  107. package/core/lib/lantern/metrics/{speed-index.js → SpeedIndex.js} +14 -16
  108. package/core/lib/lantern/metrics/SpeedIndex.test.js +83 -0
  109. package/core/lib/lantern/metrics/TotalBlockingTime.d.ts +25 -0
  110. package/core/lib/lantern/metrics/{total-blocking-time.js → TotalBlockingTime.js} +13 -16
  111. package/core/lib/lantern/metrics/__snapshots__/FirstContentfulPaint.test.js.snap +11 -0
  112. package/core/lib/lantern/metrics/__snapshots__/Interactive.test.js.snap +17 -0
  113. package/core/lib/lantern/metrics/metrics.d.ts +9 -0
  114. package/core/lib/lantern/metrics/metrics.js +16 -0
  115. package/core/lib/lantern/{simulator/connection-pool.d.ts → simulation/ConnectionPool.d.ts} +15 -15
  116. package/core/lib/lantern/{simulator/connection-pool.js → simulation/ConnectionPool.js} +25 -25
  117. package/core/lib/lantern/simulation/ConnectionPool.test.js +195 -0
  118. package/core/lib/lantern/simulation/Constants.d.ts +52 -0
  119. package/core/lib/lantern/simulation/Constants.js +48 -0
  120. package/core/lib/lantern/{simulator/dns-cache.d.ts → simulation/DNSCache.d.ts} +3 -3
  121. package/core/lib/lantern/{simulator/dns-cache.js → simulation/DNSCache.js} +2 -2
  122. package/core/lib/lantern/simulation/DNSCache.test.js +72 -0
  123. package/core/lib/lantern/{simulator/network-analyzer.d.ts → simulation/NetworkAnalyzer.d.ts} +34 -21
  124. package/core/lib/lantern/{simulator/network-analyzer.js → simulation/NetworkAnalyzer.js} +106 -57
  125. package/core/lib/lantern/simulation/NetworkAnalyzer.test.js +475 -0
  126. package/core/lib/lantern/{simulator/simulator-timing-map.d.ts → simulation/SimulationTimingMap.d.ts} +7 -7
  127. package/core/lib/lantern/{simulator/simulator-timing-map.js → simulation/SimulationTimingMap.js} +5 -6
  128. package/core/lib/lantern/{simulator/simulator.d.ts → simulation/Simulator.d.ts} +21 -19
  129. package/core/lib/lantern/{simulator/simulator.js → simulation/Simulator.js} +100 -45
  130. package/core/lib/lantern/simulation/Simulator.test.js +434 -0
  131. package/core/lib/lantern/simulation/TCPConnection.test.d.ts +2 -0
  132. package/core/lib/lantern/simulation/TCPConnection.test.js +374 -0
  133. package/core/lib/lantern/{simulator/tcp-connection.d.ts → simulation/TcpConnection.d.ts} +2 -2
  134. package/core/lib/lantern/{simulator/tcp-connection.js → simulation/TcpConnection.js} +1 -1
  135. package/core/lib/lantern/simulation/simulation.d.ts +21 -0
  136. package/core/lib/lantern/simulation/simulation.js +28 -0
  137. package/core/lib/lantern/types/lantern.d.ts +145 -20
  138. package/core/lib/lantern-trace-saver.d.ts +4 -4
  139. package/core/lib/lantern-trace-saver.js +4 -4
  140. package/core/lib/navigation-error.js +3 -3
  141. package/core/lib/network-recorder.js +2 -2
  142. package/core/lib/network-request.d.ts +29 -3
  143. package/core/lib/network-request.js +20 -13
  144. package/core/lib/tracehouse/trace-processor.d.ts +0 -2
  145. package/core/lib/tracehouse/trace-processor.js +1 -31
  146. package/core/lib/traces/metric-trace-events.js +0 -5
  147. package/dist/report/bundle.esm.js +1 -1
  148. package/dist/report/flow.js +1 -1
  149. package/dist/report/standalone.js +1 -1
  150. package/package.json +14 -13
  151. package/report/renderer/performance-category-renderer.js +1 -1
  152. package/shared/localization/locales/ar-XB.json +43 -4
  153. package/shared/localization/locales/ar.json +43 -4
  154. package/shared/localization/locales/bg.json +43 -4
  155. package/shared/localization/locales/ca.json +44 -5
  156. package/shared/localization/locales/cs.json +43 -4
  157. package/shared/localization/locales/da.json +43 -4
  158. package/shared/localization/locales/de.json +43 -4
  159. package/shared/localization/locales/el.json +43 -4
  160. package/shared/localization/locales/en-GB.json +42 -3
  161. package/shared/localization/locales/en-US.json +4 -4
  162. package/shared/localization/locales/en-XA.json +43 -4
  163. package/shared/localization/locales/en-XL.json +4 -4
  164. package/shared/localization/locales/es-419.json +43 -4
  165. package/shared/localization/locales/es.json +42 -3
  166. package/shared/localization/locales/fi.json +43 -4
  167. package/shared/localization/locales/fil.json +42 -3
  168. package/shared/localization/locales/fr.json +42 -3
  169. package/shared/localization/locales/he.json +48 -9
  170. package/shared/localization/locales/hi.json +43 -4
  171. package/shared/localization/locales/hr.json +43 -4
  172. package/shared/localization/locales/hu.json +42 -3
  173. package/shared/localization/locales/id.json +42 -3
  174. package/shared/localization/locales/it.json +43 -4
  175. package/shared/localization/locales/ja.json +42 -3
  176. package/shared/localization/locales/ko.json +43 -4
  177. package/shared/localization/locales/lt.json +42 -3
  178. package/shared/localization/locales/lv.json +43 -4
  179. package/shared/localization/locales/nl.json +44 -5
  180. package/shared/localization/locales/no.json +42 -3
  181. package/shared/localization/locales/pl.json +43 -4
  182. package/shared/localization/locales/pt-PT.json +43 -4
  183. package/shared/localization/locales/pt.json +43 -4
  184. package/shared/localization/locales/ro.json +43 -4
  185. package/shared/localization/locales/ru.json +43 -4
  186. package/shared/localization/locales/sk.json +43 -4
  187. package/shared/localization/locales/sl.json +42 -3
  188. package/shared/localization/locales/sr-Latn.json +43 -4
  189. package/shared/localization/locales/sr.json +43 -4
  190. package/shared/localization/locales/sv.json +42 -3
  191. package/shared/localization/locales/ta.json +42 -3
  192. package/shared/localization/locales/te.json +43 -4
  193. package/shared/localization/locales/th.json +44 -5
  194. package/shared/localization/locales/tr.json +43 -4
  195. package/shared/localization/locales/uk.json +43 -4
  196. package/shared/localization/locales/vi.json +43 -4
  197. package/shared/localization/locales/zh-HK.json +43 -4
  198. package/shared/localization/locales/zh-TW.json +43 -4
  199. package/shared/localization/locales/zh.json +43 -4
  200. package/third-party/chromium-synchronization/inspector-issueAdded-types-test.js +1 -0
  201. package/tsconfig-all.json +1 -0
  202. package/tsconfig.json +11 -7
  203. package/types/artifacts.d.ts +23 -25
  204. package/types/gatherer.d.ts +2 -3
  205. package/types/lhr/lhr.d.ts +2 -0
  206. package/core/computed/metrics/first-meaningful-paint.d.ts +0 -21
  207. package/core/computed/metrics/first-meaningful-paint.js +0 -44
  208. package/core/computed/metrics/lantern-first-meaningful-paint.d.ts +0 -25
  209. package/core/computed/metrics/lantern-first-meaningful-paint.js +0 -43
  210. package/core/lib/lantern/cpu-node.d.ts +0 -44
  211. package/core/lib/lantern/metrics/first-meaningful-paint.d.ts +0 -6
  212. package/core/lib/lantern/metrics/first-meaningful-paint.js +0 -64
  213. package/core/lib/lantern/metrics/interactive.d.ts +0 -26
  214. package/core/lib/lantern/metrics/largest-contentful-paint.d.ts +0 -19
  215. package/core/lib/lantern/metrics/max-potential-fid.d.ts +0 -30
  216. package/core/lib/lantern/metrics/total-blocking-time.d.ts +0 -31
  217. package/core/lib/lantern/page-dependency-graph.d.ts +0 -58
  218. /package/core/lib/lantern/{lantern-error.js → LanternError.js} +0 -0
  219. /package/core/{computed/metrics/tbt-utils.js → lib/lantern/TBTUtils.js} +0 -0
@@ -0,0 +1,654 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2024 Google LLC
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+
7
+ import assert from 'assert/strict';
8
+
9
+ import * as Lantern from './lantern.js';
10
+
11
+ const {PageDependencyGraph, NetworkRequestTypes} = Lantern;
12
+
13
+ function createRequest(
14
+ requestId,
15
+ url,
16
+ rendererStartTime = 0,
17
+ initiator = null,
18
+ resourceType = NetworkRequestTypes.Document,
19
+ fromWorker = false
20
+ ) {
21
+ const networkEndTime = rendererStartTime + 50;
22
+ return {
23
+ requestId,
24
+ url,
25
+ rendererStartTime,
26
+ networkEndTime,
27
+ initiator,
28
+ resourceType,
29
+ fromWorker,
30
+ };
31
+ }
32
+
33
+ const TOPLEVEL_TASK_NAME = 'TaskQueueManager::ProcessTaskFromWorkQueue';
34
+ describe('PageDependencyGraph', () => {
35
+ let traceEvents;
36
+ let URL;
37
+
38
+ function addTaskEvents(startTs, duration, evts) {
39
+ const mainEvent = {
40
+ name: TOPLEVEL_TASK_NAME,
41
+ tid: 1,
42
+ ts: startTs * 1000,
43
+ dur: duration * 1000,
44
+ args: {},
45
+ };
46
+
47
+ traceEvents.push(mainEvent);
48
+
49
+ let i = 0;
50
+ for (const evt of evts) {
51
+ i++;
52
+ traceEvents.push({
53
+ name: evt.name,
54
+ ts: (evt.ts * 1000) || (startTs * 1000 + i),
55
+ args: {data: evt.data},
56
+ });
57
+ }
58
+ }
59
+
60
+ beforeEach(() => {
61
+ traceEvents = [];
62
+ URL = {requestedUrl: 'https://example.com/', mainDocumentUrl: 'https://example.com/'};
63
+ });
64
+
65
+ describe('#getNetworkNodeOutput', () => {
66
+ const request1 = createRequest(1, 'https://example.com/');
67
+ const request2 = createRequest(2, 'https://example.com/page');
68
+ const request3 = createRequest(3, 'https://example.com/page');
69
+ const networkRequests = [request1, request2, request3];
70
+
71
+ it('should create network nodes', () => {
72
+ const networkNodeOutput = PageDependencyGraph.getNetworkNodeOutput(networkRequests);
73
+ for (let i = 0; i < networkRequests.length; i++) {
74
+ const node = networkNodeOutput.nodes[i];
75
+ assert.ok(node, `did not create node at index ${i}`);
76
+ assert.equal(node.id, i + 1);
77
+ assert.equal(node.type, 'network');
78
+ assert.equal(node.request, networkRequests[i]);
79
+ }
80
+ });
81
+
82
+ it('should ignore worker requests', () => {
83
+ const workerRequest = createRequest(4, 'https://example.com/worker.js', 0, null, 'Script', true);
84
+ const recordsWithWorker = [
85
+ ...networkRequests,
86
+ workerRequest,
87
+ ];
88
+
89
+ const networkNodeOutput = PageDependencyGraph.getNetworkNodeOutput(recordsWithWorker);
90
+
91
+ expect(networkNodeOutput.nodes).toHaveLength(3);
92
+ expect(networkNodeOutput.nodes.map(node => node.request)).not.toContain(workerRequest);
93
+ });
94
+
95
+ it('should index nodes by ID', () => {
96
+ const networkNodeOutput = PageDependencyGraph.getNetworkNodeOutput(networkRequests);
97
+ const indexedById = networkNodeOutput.idToNodeMap;
98
+ for (const request of networkRequests) {
99
+ assert.equal(indexedById.get(request.requestId).request, request);
100
+ }
101
+ });
102
+
103
+ it('should index nodes by URL', () => {
104
+ const networkNodeOutput = PageDependencyGraph.getNetworkNodeOutput(networkRequests);
105
+ const nodes = networkNodeOutput.nodes;
106
+ const indexedByUrl = networkNodeOutput.urlToNodeMap;
107
+ assert.deepEqual(indexedByUrl.get('https://example.com/'), [nodes[0]]);
108
+ assert.deepEqual(indexedByUrl.get('https://example.com/page'), [nodes[1], nodes[2]]);
109
+ });
110
+
111
+ it('should index nodes by frame', () => {
112
+ const networkNodeOutput = PageDependencyGraph.getNetworkNodeOutput([
113
+ {...createRequest(1, 'https://example.com/'), documentURL: 'https://example.com/', frameId: 'A'},
114
+ {...createRequest(2, 'https://example.com/page'), documentURL: 'https://example.com/', frameId: 'A'},
115
+ {...createRequest(3, 'https://example.com/page2'), documentURL: 'https://example.com/page2', frameId: 'C',
116
+ resourceType: NetworkRequestTypes.XHR},
117
+ {...createRequest(4, 'https://example.com/page3'), documentURL: 'https://example.com/page3', frameId: 'D'},
118
+ {...createRequest(4, 'https://example.com/page4'), documentURL: 'https://example.com/page4', frameId: undefined},
119
+ {...createRequest(4, 'https://example.com/page5'), documentURL: 'https://example.com/page5', frameId: 'collision'},
120
+ {...createRequest(4, 'https://example.com/page6'), documentURL: 'https://example.com/page6', frameId: 'collision'},
121
+ ]);
122
+
123
+ const nodes = networkNodeOutput.nodes;
124
+ const indexedByFrame = networkNodeOutput.frameIdToNodeMap;
125
+ expect([...indexedByFrame.entries()]).toEqual([
126
+ ['A', nodes[0]],
127
+ ['D', nodes[3]],
128
+ ['collision', null],
129
+ ]);
130
+ });
131
+ });
132
+
133
+ describe('#getCPUNodes', () => {
134
+ it('should create CPU nodes', () => {
135
+ addTaskEvents(0, 100, [
136
+ {name: 'MyCustomEvent'},
137
+ {name: 'OtherEvent'},
138
+ {name: 'OutsideTheWindow', ts: 200},
139
+ {name: 'OrphanedEvent'}, // should be ignored since we stopped at OutsideTheWindow
140
+ ]);
141
+
142
+ addTaskEvents(250, 50, [
143
+ {name: 'LaterEvent'},
144
+ ]);
145
+
146
+ assert.equal(traceEvents.length, 7);
147
+ const nodes = PageDependencyGraph.getCPUNodes(traceEvents);
148
+ assert.equal(nodes.length, 2);
149
+
150
+ const node1 = nodes[0];
151
+ assert.equal(node1.id, '1.0');
152
+ assert.equal(node1.type, 'cpu');
153
+ assert.equal(node1.event, traceEvents[0]);
154
+ assert.equal(node1.childEvents.length, 2);
155
+ assert.equal(node1.childEvents[1].name, 'OtherEvent');
156
+
157
+ const node2 = nodes[1];
158
+ assert.equal(node2.id, '1.250000');
159
+ assert.equal(node2.type, 'cpu');
160
+ assert.equal(node2.event, traceEvents[5]);
161
+ assert.equal(node2.childEvents.length, 1);
162
+ assert.equal(node2.childEvents[0].name, 'LaterEvent');
163
+ });
164
+
165
+ it('should correct overlapping tasks', () => {
166
+ addTaskEvents(0, 500, [
167
+ {name: 'MyCustomEvent'},
168
+ {name: 'OtherEvent'},
169
+ ]);
170
+
171
+ addTaskEvents(400, 50, [
172
+ {name: 'OverlappingEvent'},
173
+ ]);
174
+
175
+ assert.equal(traceEvents.length, 5);
176
+ const nodes = PageDependencyGraph.getCPUNodes(traceEvents);
177
+ assert.equal(nodes.length, 2);
178
+
179
+ const node1 = nodes[0];
180
+ assert.equal(node1.id, '1.0');
181
+ assert.equal(node1.type, 'cpu');
182
+ assert.equal(node1.event, traceEvents[0]);
183
+ assert.equal(node1.childEvents.length, 2);
184
+ assert.equal(node1.childEvents[0].name, 'MyCustomEvent');
185
+ assert.equal(node1.childEvents[1].name, 'OtherEvent');
186
+
187
+ const node2 = nodes[1];
188
+ assert.equal(node2.id, '1.400000');
189
+ assert.equal(node2.type, 'cpu');
190
+ assert.equal(node2.event, traceEvents[3]);
191
+ assert.equal(node2.childEvents.length, 1);
192
+ assert.equal(node2.childEvents[0].name, 'OverlappingEvent');
193
+ });
194
+ });
195
+
196
+ describe('#createGraph', () => {
197
+ it('should compute a simple network graph', () => {
198
+ const request1 = createRequest(1, 'https://example.com/', 0);
199
+ const request2 = createRequest(2, 'https://example.com/page', 5);
200
+ const request3 = createRequest(3, 'https://example.com/page2', 5);
201
+ const request4 = createRequest(4, 'https://example.com/page3', 10, {url: 'https://example.com/page'});
202
+ const networkRequests = [request1, request2, request3, request4];
203
+
204
+ addTaskEvents(0, 0, []);
205
+
206
+ const graph = PageDependencyGraph.createGraph(traceEvents, networkRequests, URL);
207
+ const nodes = [];
208
+ graph.traverse(node => nodes.push(node));
209
+
210
+ assert.equal(nodes.length, 4);
211
+ assert.deepEqual(nodes.map(node => node.id), [1, 2, 3, 4]);
212
+ assert.deepEqual(nodes[0].getDependencies(), []);
213
+ assert.deepEqual(nodes[1].getDependencies(), [nodes[0]]);
214
+ assert.deepEqual(nodes[2].getDependencies(), [nodes[0]]);
215
+ assert.deepEqual(nodes[3].getDependencies(), [nodes[1]]);
216
+ });
217
+
218
+ it('should compute a simple network and CPU graph', () => {
219
+ const request1 = createRequest(1, 'https://example.com/', 0);
220
+ const request2 = createRequest(2, 'https://example.com/page', 50);
221
+ const request3 = createRequest(3, 'https://example.com/page2', 50);
222
+ const request4 = createRequest(4, 'https://example.com/page3', 300, null, NetworkRequestTypes.XHR);
223
+ const networkRequests = [request1, request2, request3, request4];
224
+
225
+ addTaskEvents(200, 200, [
226
+ {name: 'EvaluateScript', data: {url: 'https://example.com/page'}},
227
+ {name: 'ResourceSendRequest', data: {requestId: 4}},
228
+ ]);
229
+
230
+ addTaskEvents(700, 50, [
231
+ {name: 'InvalidateLayout', data: {stackTrace: [{url: 'https://example.com/page2'}]}},
232
+ {name: 'XHRReadyStateChange', data: {readyState: 4, url: 'https://example.com/page3'}},
233
+ ]);
234
+
235
+ const graph = PageDependencyGraph.createGraph(traceEvents, networkRequests, URL);
236
+ const nodes = [];
237
+ graph.traverse(node => nodes.push(node));
238
+
239
+ const getIds = nodes => nodes.map(node => node.id);
240
+ const getDependencyIds = node => getIds(node.getDependencies());
241
+
242
+ assert.equal(nodes.length, 6);
243
+ assert.deepEqual(getIds(nodes), [1, 2, 3, 4, '1.200000', '1.700000']);
244
+ assert.deepEqual(getDependencyIds(nodes[0]), []);
245
+ assert.deepEqual(getDependencyIds(nodes[1]), [1]);
246
+ assert.deepEqual(getDependencyIds(nodes[2]), [1]);
247
+ assert.deepEqual(getDependencyIds(nodes[3]), [1, '1.200000']);
248
+ assert.deepEqual(getDependencyIds(nodes[4]), [2]);
249
+ assert.deepEqual(getDependencyIds(nodes[5]), [3, 4]);
250
+ });
251
+
252
+ it('should compute a network graph with duplicate URLs', () => {
253
+ const request1 = createRequest(1, 'https://example.com/', 0);
254
+ const request2 = createRequest(2, 'https://example.com/page', 5);
255
+ const request3 = createRequest(3, 'https://example.com/page', 5); // duplicate URL
256
+ const request4 = createRequest(4, 'https://example.com/page3', 10, {url: 'https://example.com/page'});
257
+ const networkRequests = [request1, request2, request3, request4];
258
+
259
+ addTaskEvents(0, 0, []);
260
+
261
+ const graph = PageDependencyGraph.createGraph(traceEvents, networkRequests, URL);
262
+ const nodes = [];
263
+ graph.traverse(node => nodes.push(node));
264
+
265
+ assert.equal(nodes.length, 4);
266
+ assert.deepEqual(nodes.map(node => node.id), [1, 2, 3, 4]);
267
+ assert.deepEqual(nodes[0].getDependencies(), []);
268
+ assert.deepEqual(nodes[1].getDependencies(), [nodes[0]]);
269
+ assert.deepEqual(nodes[2].getDependencies(), [nodes[0]]);
270
+ assert.deepEqual(nodes[3].getDependencies(), [nodes[0]]); // should depend on rootNode instead
271
+ });
272
+
273
+ it('should be forgiving without cyclic dependencies', () => {
274
+ const request1 = createRequest(1, 'https://example.com/', 0);
275
+ const request2 = createRequest(2, 'https://example.com/page', 250, null, NetworkRequestTypes.XHR);
276
+ const request3 = createRequest(3, 'https://example.com/page2', 210);
277
+ const request4 = createRequest(4, 'https://example.com/page3', 590);
278
+ const request5 = createRequest(5, 'https://example.com/page4', 595, null, NetworkRequestTypes.XHR);
279
+ const networkRequests = [request1, request2, request3, request4, request5];
280
+
281
+ addTaskEvents(200, 200, [
282
+ // CPU 1.2 should depend on Network 1
283
+ {name: 'EvaluateScript', data: {url: 'https://example.com/'}},
284
+
285
+ // Network 2 should depend on CPU 1.2, but 1.2 should not depend on Network 1
286
+ {name: 'ResourceSendRequest', data: {requestId: 2}},
287
+ {name: 'XHRReadyStateChange', data: {readyState: 4, url: 'https://example.com/page'}},
288
+
289
+ // CPU 1.2 should not depend on Network 3 because it starts after CPU 1.2
290
+ {name: 'EvaluateScript', data: {url: 'https://example.com/page2'}},
291
+ ]);
292
+
293
+ addTaskEvents(600, 150, [
294
+ // CPU 1.6 should depend on Network 4 even though it ends at 410ms
295
+ {name: 'InvalidateLayout', data: {stackTrace: [{url: 'https://example.com/page3'}]}},
296
+ // Network 5 should not depend on CPU 1.6 because it started before CPU 1.6
297
+ {name: 'ResourceSendRequest', data: {requestId: 5}},
298
+ ]);
299
+
300
+ const graph = PageDependencyGraph.createGraph(traceEvents, networkRequests, URL);
301
+ const nodes = [];
302
+ graph.traverse(node => nodes.push(node));
303
+
304
+ const getDependencyIds = node => node.getDependencies().map(node => node.id);
305
+
306
+ assert.equal(nodes.length, 7);
307
+ assert.deepEqual(getDependencyIds(nodes[0]), []);
308
+ assert.deepEqual(getDependencyIds(nodes[1]), [1, '1.200000']);
309
+ assert.deepEqual(getDependencyIds(nodes[2]), [1]);
310
+ assert.deepEqual(getDependencyIds(nodes[3]), [1]);
311
+ assert.deepEqual(getDependencyIds(nodes[4]), [1]);
312
+ assert.deepEqual(getDependencyIds(nodes[5]), [1]);
313
+ assert.deepEqual(getDependencyIds(nodes[6]), [4]);
314
+ });
315
+
316
+ it('should not install timer dependency on itself', () => {
317
+ const request1 = createRequest(1, 'https://example.com/', 0);
318
+ const networkRequests = [request1];
319
+
320
+ addTaskEvents(200, 200, [
321
+ // CPU 1.2 should depend on Network 1
322
+ {name: 'EvaluateScript', data: {url: 'https://example.com/'}},
323
+ // CPU 1.2 will install and fire it's own timer, but should not depend on itself
324
+ {name: 'TimerInstall', data: {timerId: 'timer1'}},
325
+ {name: 'TimerFire', data: {timerId: 'timer1'}},
326
+ ]);
327
+
328
+ const graph = PageDependencyGraph.createGraph(traceEvents, networkRequests, URL);
329
+ const nodes = [];
330
+ graph.traverse(node => nodes.push(node));
331
+
332
+ const getDependencyIds = node => node.getDependencies().map(node => node.id);
333
+
334
+ assert.equal(nodes.length, 2);
335
+ assert.deepEqual(getDependencyIds(nodes[0]), []);
336
+ assert.deepEqual(getDependencyIds(nodes[1]), [1]);
337
+ });
338
+
339
+ it('should prune short tasks', () => {
340
+ const request0 = createRequest(0, 'https://example.com/page0', 0);
341
+ const request1 = createRequest(1, 'https://example.com/', 100, null, NetworkRequestTypes.Script);
342
+ const request2 = createRequest(2, 'https://example.com/page', 200, null, NetworkRequestTypes.XHR);
343
+ const request3 = createRequest(3, 'https://example.com/page2', 300, null, NetworkRequestTypes.Script);
344
+ const request4 = createRequest(4, 'https://example.com/page3', 400, null, NetworkRequestTypes.XHR);
345
+ const networkRequests = [request0, request1, request2, request3, request4];
346
+ URL = {requestedUrl: 'https://example.com/page0', mainDocumentUrl: 'https://example.com/page0'};
347
+
348
+ // Long task, should be kept in the output.
349
+ addTaskEvents(120, 50, [
350
+ {name: 'EvaluateScript', data: {url: 'https://example.com/'}},
351
+ {name: 'ResourceSendRequest', data: {requestId: 2}},
352
+ {name: 'XHRReadyStateChange', data: {readyState: 4, url: 'https://example.com/page'}},
353
+ ]);
354
+
355
+ // Short task, should be pruned, but the 3->4 relationship should be retained
356
+ addTaskEvents(350, 5, [
357
+ {name: 'EvaluateScript', data: {url: 'https://example.com/page2'}},
358
+ {name: 'ResourceSendRequest', data: {requestId: 4}},
359
+ {name: 'XHRReadyStateChange', data: {readyState: 4, url: 'https://example.com/page3'}},
360
+ ]);
361
+
362
+ const graph = PageDependencyGraph.createGraph(traceEvents, networkRequests, URL);
363
+ const nodes = [];
364
+ graph.traverse(node => nodes.push(node));
365
+
366
+ const getDependencyIds = node => node.getDependencies().map(node => node.id);
367
+
368
+ assert.equal(nodes.length, 6);
369
+
370
+ assert.deepEqual(getDependencyIds(nodes[0]), []);
371
+ assert.deepEqual(getDependencyIds(nodes[1]), [0]);
372
+ assert.deepEqual(getDependencyIds(nodes[2]), [0, '1.120000']);
373
+ assert.deepEqual(getDependencyIds(nodes[3]), [0]);
374
+ assert.deepEqual(getDependencyIds(nodes[4]), [0, 3]);
375
+
376
+ assert.equal('1.120000', nodes[5].id);
377
+ assert.deepEqual(getDependencyIds(nodes[5]), [1]);
378
+ });
379
+
380
+ it('should not prune highly-connected short tasks', () => {
381
+ const request0 = createRequest(0, 'https://example.com/page0', 0);
382
+ const request1 = {
383
+ ...createRequest(1, 'https://example.com/', 100, null, NetworkRequestTypes.Document),
384
+ documentURL: 'https://example.com/',
385
+ frameId: 'frame1',
386
+ };
387
+ const request2 = {
388
+ ...createRequest(2, 'https://example.com/page', 200, null, NetworkRequestTypes.Script),
389
+ documentURL: 'https://example.com/',
390
+ frameId: 'frame1',
391
+ };
392
+ const request3 = createRequest(3, 'https://example.com/page2', 300, null, NetworkRequestTypes.XHR);
393
+ const request4 = createRequest(4, 'https://example.com/page3', 400, null, NetworkRequestTypes.XHR);
394
+ const networkRequests = [request0, request1, request2, request3, request4];
395
+ URL = {requestedUrl: 'https://example.com/page0', mainDocumentUrl: 'https://example.com/page0'};
396
+
397
+ // Short task, evaluates script (2) and sends two XHRs.
398
+ addTaskEvents(220, 5, [
399
+ {name: 'EvaluateScript', data: {url: 'https://example.com/page', frame: 'frame1'}},
400
+
401
+ {name: 'ResourceSendRequest', data: {requestId: 3}},
402
+ {name: 'XHRReadyStateChange', data: {readyState: 4, url: 'https://example.com/page2'}},
403
+
404
+ {name: 'ResourceSendRequest', data: {requestId: 4}},
405
+ {name: 'XHRReadyStateChange', data: {readyState: 4, url: 'https://example.com/page3'}},
406
+ ]);
407
+
408
+ const graph = PageDependencyGraph.createGraph(traceEvents, networkRequests, URL);
409
+ const nodes = [];
410
+ graph.traverse(node => nodes.push(node));
411
+
412
+ const getDependencyIds = node => node.getDependencies().map(node => node.id);
413
+
414
+ assert.equal(nodes.length, 6);
415
+
416
+ assert.deepEqual(getDependencyIds(nodes[0]), []);
417
+ assert.deepEqual(getDependencyIds(nodes[1]), [0]);
418
+ assert.deepEqual(getDependencyIds(nodes[2]), [0]);
419
+ assert.deepEqual(getDependencyIds(nodes[3]), [0, '1.220000']);
420
+ assert.deepEqual(getDependencyIds(nodes[4]), [0, '1.220000']);
421
+
422
+ assert.equal('1.220000', nodes[5].id);
423
+ assert.deepEqual(getDependencyIds(nodes[5]), [1, 2]);
424
+ });
425
+
426
+ it('should not prune short, first tasks of critical events', () => {
427
+ const request0 = createRequest(0, 'https://example.com/page0', 0);
428
+ const networkRequests = [request0];
429
+ URL = {requestedUrl: 'https://example.com/page0', mainDocumentUrl: 'https://example.com/page0'};
430
+
431
+ const makeShortEvent = firstEventName => {
432
+ const startTs = traceEvents.length * 100;
433
+ addTaskEvents(startTs, 5, [
434
+ {name: firstEventName, data: {url: 'https://example.com/page0'}},
435
+ ]);
436
+ };
437
+
438
+ const criticalEventNames = [
439
+ 'Paint',
440
+ 'Layout',
441
+ 'ParseHTML',
442
+ ];
443
+ for (const eventName of criticalEventNames) {
444
+ makeShortEvent(eventName);
445
+ makeShortEvent(eventName);
446
+ }
447
+
448
+ const graph = PageDependencyGraph.createGraph(traceEvents, networkRequests, URL);
449
+ const cpuNodes = [];
450
+ graph.traverse(node => node.type === 'cpu' && cpuNodes.push(node));
451
+
452
+ expect(cpuNodes.map(node => {
453
+ return {
454
+ id: node.id,
455
+ name: node.childEvents[0].name,
456
+ };
457
+ })).toEqual([
458
+ {
459
+ id: '1.0',
460
+ name: 'Paint',
461
+ },
462
+ {
463
+ // ID jumps by 4 between each because each node has 2 CPU tasks and we skip the 2nd of each event type
464
+ id: '1.400000',
465
+ name: 'Layout',
466
+ },
467
+ {
468
+ id: '1.800000',
469
+ name: 'ParseHTML',
470
+ },
471
+ ]);
472
+ });
473
+
474
+ it('should set isMainDocument on request with mainDocumentUrl', () => {
475
+ const request1 = createRequest(1, 'https://example.com/', 0, null, NetworkRequestTypes.Other);
476
+ const request2 = createRequest(2, 'https://example.com/page', 5, null, NetworkRequestTypes.Document);
477
+ // Add in another unrelated + early request to make sure we pick the correct chain
478
+ const request3 = createRequest(3, 'https://example.com/page2', 0, null, NetworkRequestTypes.Other);
479
+ request2.redirects = [request1];
480
+ const networkRequests = [request1, request2, request3];
481
+ URL = {requestedUrl: 'https://example.com/', mainDocumentUrl: 'https://example.com/page'};
482
+
483
+ addTaskEvents(0, 0, []);
484
+
485
+ const graph = PageDependencyGraph.createGraph(traceEvents, networkRequests, URL);
486
+ const nodes = [];
487
+ graph.traverse(node => nodes.push(node));
488
+
489
+ assert.equal(nodes.length, 3);
490
+ assert.equal(nodes[0].id, 1);
491
+ assert.equal(nodes[0].isMainDocument(), false);
492
+ assert.equal(nodes[1].isMainDocument(), true);
493
+ assert.equal(nodes[2].isMainDocument(), false);
494
+ });
495
+
496
+ it('should link up script initiators', () => {
497
+ const request1 = createRequest(1, 'https://example.com/', 0);
498
+ const request2 = createRequest(2, 'https://example.com/page', 5);
499
+ const request3 = createRequest(3, 'https://example.com/page2', 5);
500
+ const request4 = createRequest(4, 'https://example.com/page3', 20);
501
+ // Set multiple initiator requests through script stack.
502
+ request4.initiator = {
503
+ type: 'script',
504
+ stack: {callFrames: [{url: 'https://example.com/page'}], parent: {parent: {callFrames: [{url: 'https://example.com/page2'}]}}},
505
+ };
506
+ // Also set the initiatorRequest that Lighthouse's network-recorder.js creates.
507
+ // This should be ignored and only used as a fallback.
508
+ request4.initiatorRequest = request1;
509
+ const networkRequests = [request1, request2, request3, request4];
510
+
511
+ addTaskEvents(0, 0, []);
512
+
513
+ const graph = PageDependencyGraph.createGraph(traceEvents, networkRequests, URL);
514
+ const nodes = [];
515
+ graph.traverse(node => nodes.push(node));
516
+
517
+ assert.equal(nodes.length, 4);
518
+ assert.deepEqual(nodes.map(node => node.id), [1, 2, 3, 4]);
519
+ assert.deepEqual(nodes[0].getDependencies(), []);
520
+ assert.deepEqual(nodes[1].getDependencies(), [nodes[0]]);
521
+ assert.deepEqual(nodes[2].getDependencies(), [nodes[0]]);
522
+ assert.deepEqual(nodes[3].getDependencies(), [nodes[1], nodes[2]]);
523
+ });
524
+
525
+ it('should link up script initiators only when timing is valid', () => {
526
+ const request1 = createRequest(1, 'https://example.com/', 0);
527
+ const request2 = createRequest(2, 'https://example.com/page', 500);
528
+ const request3 = createRequest(3, 'https://example.com/page2', 500);
529
+ const request4 = createRequest(4, 'https://example.com/page3', 20);
530
+ request4.initiator = {
531
+ type: 'script',
532
+ stack: {callFrames: [{url: 'https://example.com/page'}], parent: {parent: {callFrames: [{url: 'https://example.com/page2'}]}}},
533
+ };
534
+ const networkRequests = [request1, request2, request3, request4];
535
+
536
+ addTaskEvents(0, 0, []);
537
+
538
+ const graph = PageDependencyGraph.createGraph(traceEvents, networkRequests, URL);
539
+ const nodes = [];
540
+ graph.traverse(node => nodes.push(node));
541
+
542
+ assert.equal(nodes.length, 4);
543
+ assert.deepEqual(nodes.map(node => node.id), [1, 2, 3, 4]);
544
+ assert.deepEqual(nodes[0].getDependencies(), []);
545
+ assert.deepEqual(nodes[1].getDependencies(), [nodes[0]]);
546
+ assert.deepEqual(nodes[2].getDependencies(), [nodes[0]]);
547
+ assert.deepEqual(nodes[3].getDependencies(), [nodes[0]]);
548
+ });
549
+
550
+ it('should link up script initiators with prefetch requests', () => {
551
+ const request1 = createRequest(1, 'https://a.com/1', 0);
552
+ const request2Prefetch = createRequest(2, 'https://a.com/js', 5);
553
+ const request2Fetch = createRequest(3, 'https://a.com/js', 10);
554
+ const request3 = createRequest(4, 'https://a.com/4', 20);
555
+ // Set the initiator to an ambiguous URL (there are 2 requests for https://a.com/js)
556
+ request3.initiator = {
557
+ type: 'script',
558
+ stack: {callFrames: [{url: 'https://a.com/js'}], parent: {parent: {callFrames: [{url: 'js'}]}}},
559
+ };
560
+ // Set the initiatorRequest that it should fallback to.
561
+ request3.initiatorRequest = request2Fetch;
562
+ const networkRequests = [request1, request2Prefetch, request2Fetch, request3];
563
+ URL = {requestedUrl: 'https://a.com/1', mainDocumentUrl: 'https://a.com/1'};
564
+
565
+ addTaskEvents(0, 0, []);
566
+
567
+ const graph = PageDependencyGraph.createGraph(traceEvents, networkRequests, URL);
568
+ const nodes = [];
569
+ graph.traverse(node => nodes.push(node));
570
+
571
+ assert.equal(nodes.length, 4);
572
+ assert.deepEqual(nodes.map(node => node.id), [1, 2, 3, 4]);
573
+ assert.deepEqual(nodes[0].getDependencies(), []);
574
+ assert.deepEqual(nodes[1].getDependencies(), [nodes[0]]);
575
+ assert.deepEqual(nodes[2].getDependencies(), [nodes[0]]);
576
+ assert.deepEqual(nodes[3].getDependencies(), [nodes[2]]);
577
+ });
578
+
579
+ it('should not link up initiators with circular dependencies', () => {
580
+ const rootRequest = createRequest(1, 'https://a.com', 0);
581
+ // jsRequest1 initiated by jsRequest2
582
+ // *AND*
583
+ // jsRequest2 initiated by jsRequest1
584
+ const jsRequest1 = createRequest(2, 'https://a.com/js1', 1, {url: 'https://a.com/js2'});
585
+ const jsRequest2 = createRequest(3, 'https://a.com/js2', 1, {url: 'https://a.com/js1'});
586
+ const networkRequests = [rootRequest, jsRequest1, jsRequest2];
587
+ URL = {requestedUrl: 'https://a.com', mainDocumentUrl: 'https://a.com'};
588
+
589
+ addTaskEvents(0, 0, []);
590
+
591
+ const graph = PageDependencyGraph.createGraph(traceEvents, networkRequests, URL);
592
+ const nodes = [];
593
+ graph.traverse(node => nodes.push(node));
594
+ nodes.sort((a, b) => a.id - b.id);
595
+
596
+ assert.equal(nodes.length, 3);
597
+ assert.deepEqual(nodes.map(node => node.id), [1, 2, 3]);
598
+ assert.deepEqual(nodes[0].getDependencies(), []);
599
+ // We don't know which of the initiators to trust in a cycle, so for now we
600
+ // trust the earliest one (mostly because it's simplest).
601
+ // In the wild so far we've only seen this for self-referential relationships.
602
+ // If the evidence changes, then feel free to change these expectations :)
603
+ assert.deepEqual(nodes[1].getDependencies(), [nodes[2]]);
604
+ assert.deepEqual(nodes[2].getDependencies(), [nodes[0]]);
605
+ });
606
+
607
+ it('should not link up initiatorRequests with circular dependencies', () => {
608
+ const rootRequest = createRequest(1, 'https://a.com', 0);
609
+ // jsRequest1 initiated by jsRequest2
610
+ // *AND*
611
+ // jsRequest2 initiated by jsRequest1
612
+ const jsRequest1 = createRequest(2, 'https://a.com/js1', 1);
613
+ const jsRequest2 = createRequest(3, 'https://a.com/js2', 1);
614
+ jsRequest1.initiatorRequest = jsRequest2;
615
+ jsRequest2.initiatorRequest = jsRequest1;
616
+ const networkRequests = [rootRequest, jsRequest1, jsRequest2];
617
+ URL = {requestedUrl: 'https://a.com', mainDocumentUrl: 'https://a.com'};
618
+
619
+ addTaskEvents(0, 0, []);
620
+
621
+ const graph = PageDependencyGraph.createGraph(traceEvents, networkRequests, URL);
622
+ const nodes = [];
623
+ graph.traverse(node => nodes.push(node));
624
+ nodes.sort((a, b) => a.id - b.id);
625
+
626
+ assert.equal(nodes.length, 3);
627
+ assert.deepEqual(nodes.map(node => node.id), [1, 2, 3]);
628
+ assert.deepEqual(nodes[0].getDependencies(), []);
629
+ assert.deepEqual(nodes[1].getDependencies(), [nodes[2]]);
630
+ assert.deepEqual(nodes[2].getDependencies(), [nodes[0]]);
631
+ });
632
+
633
+ it('should find root if it is not the first node', () => {
634
+ const request1 = createRequest(1, 'https://example.com/', 0, null, NetworkRequestTypes.Other);
635
+ const request2 = createRequest(2, 'https://example.com/page', 5, null, NetworkRequestTypes.Document);
636
+ const networkRequests = [request1, request2];
637
+ URL = {requestedUrl: 'https://example.com/page', mainDocumentUrl: 'https://example.com/page'};
638
+
639
+ // Evaluated before root request.
640
+ addTaskEvents(0.1, 50, [
641
+ {name: 'EvaluateScript'},
642
+ ]);
643
+
644
+ const graph = PageDependencyGraph.createGraph(traceEvents, networkRequests, URL);
645
+ const nodes = [];
646
+ graph.traverse(node => nodes.push(node));
647
+
648
+ assert.equal(nodes.length, 1);
649
+ assert.deepEqual(nodes.map(node => node.id), [2]);
650
+ assert.deepEqual(nodes[0].getDependencies(), []);
651
+ assert.deepEqual(nodes[0].getDependents(), []);
652
+ });
653
+ });
654
+ });
@@ -41,4 +41,4 @@ export function calculateTbtImpactForEvent(event: {
41
41
  end: number;
42
42
  duration: number;
43
43
  } | undefined): number;
44
- //# sourceMappingURL=tbt-utils.d.ts.map
44
+ //# sourceMappingURL=TBTUtils.d.ts.map
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=TBTUtils.test.d.ts.map