browser_aboutdebugging_telemetry_connection_attempt.js (7946B)
1 /* Any copyright is dedicated to the Public Domain. 2 * http://creativecommons.org/publicdomain/zero/1.0/ */ 3 4 "use strict"; 5 6 /* import-globals-from helper-telemetry.js */ 7 Services.scriptloader.loadSubScript( 8 CHROME_URL_ROOT + "helper-telemetry.js", 9 this 10 ); 11 12 const USB_RUNTIME = { 13 id: "runtime-id-1", 14 deviceName: "Device A", 15 name: "Runtime 1", 16 shortName: "R1", 17 }; 18 19 /** 20 * Check that telemetry events for connection attempts are correctly recorded in various 21 * scenarios: 22 * - successful connection 23 * - successful connection after showing the timeout warning 24 * - failed connection 25 * - connection timeout 26 */ 27 add_task(async function testSuccessfulConnectionAttempt() { 28 const { doc, mocks, runtimeId, sessionId, tab } = 29 await setupConnectionAttemptTest(); 30 31 await connectToRuntime(USB_RUNTIME.deviceName, doc); 32 33 const connectionEvents = checkTelemetryEvents( 34 [ 35 { method: "runtime_connected", extras: { runtime_id: runtimeId } }, 36 { 37 method: "connection_attempt", 38 extras: getEventExtras("start", runtimeId), 39 }, 40 { 41 method: "connection_attempt", 42 extras: getEventExtras("success", runtimeId), 43 }, 44 { method: "select_page", extras: { page_type: "runtime" } }, 45 ], 46 sessionId 47 ).filter(({ method }) => method === "connection_attempt"); 48 49 checkConnectionId(connectionEvents); 50 51 await removeUsbRuntime(USB_RUNTIME, mocks, doc); 52 await removeTab(tab); 53 }); 54 55 add_task(async function testFailedConnectionAttempt() { 56 const { doc, mocks, runtimeId, sessionId, tab } = 57 await setupConnectionAttemptTest(); 58 mocks.runtimeClientFactoryMock.createClientForRuntime = async () => { 59 throw new Error("failed"); 60 }; 61 62 info( 63 "Try to connect to the runtime and wait for the connection error message" 64 ); 65 const usbRuntimeSidebarItem = findSidebarItemByText( 66 USB_RUNTIME.deviceName, 67 doc 68 ); 69 const connectButton = 70 usbRuntimeSidebarItem.querySelector(".qa-connect-button"); 71 connectButton.click(); 72 await waitUntil(() => 73 usbRuntimeSidebarItem.querySelector(".qa-connection-error") 74 ); 75 76 const connectionEvents = checkTelemetryEvents( 77 [ 78 { 79 method: "connection_attempt", 80 extras: getEventExtras("start", runtimeId), 81 }, 82 { 83 method: "connection_attempt", 84 extras: getEventExtras("failed", runtimeId), 85 }, 86 ], 87 sessionId 88 ).filter(({ method }) => method === "connection_attempt"); 89 90 checkConnectionId(connectionEvents); 91 92 await removeUsbRuntime(USB_RUNTIME, mocks, doc); 93 await removeTab(tab); 94 }); 95 96 add_task(async function testPendingConnectionAttempt() { 97 info("Set timeout preferences to avoid cancelling the connection"); 98 await pushPref( 99 "devtools.aboutdebugging.test-connection-timing-out-delay", 100 100 101 ); 102 await pushPref( 103 "devtools.aboutdebugging.test-connection-cancel-delay", 104 100000 105 ); 106 107 const { doc, mocks, runtimeId, sessionId, tab } = 108 await setupConnectionAttemptTest(); 109 110 info("Simulate a pending connection"); 111 let resumeConnection; 112 const resumeConnectionPromise = new Promise(r => { 113 resumeConnection = r; 114 }); 115 mocks.runtimeClientFactoryMock.createClientForRuntime = async runtime => { 116 await resumeConnectionPromise; 117 return mocks._clients[runtime.type][runtime.id]; 118 }; 119 120 info("Click on the connect button and wait for the warning message"); 121 const usbRuntimeSidebarItem = findSidebarItemByText( 122 USB_RUNTIME.deviceName, 123 doc 124 ); 125 const connectButton = 126 usbRuntimeSidebarItem.querySelector(".qa-connect-button"); 127 connectButton.click(); 128 await waitUntil(() => doc.querySelector(".qa-connection-not-responding")); 129 130 info("Resume the connection and wait for the connection to succeed"); 131 resumeConnection(); 132 await waitUntil( 133 () => !usbRuntimeSidebarItem.querySelector(".qa-connect-button") 134 ); 135 136 const connectionEvents = checkTelemetryEvents( 137 [ 138 { method: "runtime_connected", extras: { runtime_id: runtimeId } }, 139 { 140 method: "connection_attempt", 141 extras: getEventExtras("start", runtimeId), 142 }, 143 { 144 method: "connection_attempt", 145 extras: getEventExtras("not responding", runtimeId), 146 }, 147 { 148 method: "connection_attempt", 149 extras: getEventExtras("success", runtimeId), 150 }, 151 { method: "select_page", extras: { page_type: "runtime" } }, 152 ], 153 sessionId 154 ).filter(({ method }) => method === "connection_attempt"); 155 checkConnectionId(connectionEvents); 156 157 await removeUsbRuntime(USB_RUNTIME, mocks, doc); 158 await removeTab(tab); 159 }); 160 161 add_task(async function testCancelledConnectionAttempt() { 162 info("Set timeout preferences to quickly cancel the connection"); 163 await pushPref( 164 "devtools.aboutdebugging.test-connection-timing-out-delay", 165 100 166 ); 167 await pushPref("devtools.aboutdebugging.test-connection-cancel-delay", 1000); 168 169 const { doc, mocks, runtimeId, sessionId, tab } = 170 await setupConnectionAttemptTest(); 171 172 info("Simulate a connection timeout"); 173 mocks.runtimeClientFactoryMock.createClientForRuntime = async () => { 174 await new Promise(() => {}); 175 }; 176 177 info("Click on the connect button and wait for the error message"); 178 const usbRuntimeSidebarItem = findSidebarItemByText( 179 USB_RUNTIME.deviceName, 180 doc 181 ); 182 const connectButton = 183 usbRuntimeSidebarItem.querySelector(".qa-connect-button"); 184 connectButton.click(); 185 await waitUntil(() => 186 usbRuntimeSidebarItem.querySelector(".qa-connection-timeout") 187 ); 188 189 const connectionEvents = checkTelemetryEvents( 190 [ 191 { 192 method: "connection_attempt", 193 extras: getEventExtras("start", runtimeId), 194 }, 195 { 196 method: "connection_attempt", 197 extras: getEventExtras("not responding", runtimeId), 198 }, 199 { 200 method: "connection_attempt", 201 extras: getEventExtras("cancelled", runtimeId), 202 }, 203 ], 204 sessionId 205 ).filter(({ method }) => method === "connection_attempt"); 206 checkConnectionId(connectionEvents); 207 208 await removeUsbRuntime(USB_RUNTIME, mocks, doc); 209 await removeTab(tab); 210 }); 211 212 function checkConnectionId(connectionEvents) { 213 const connectionId = connectionEvents[0].extras.connection_id; 214 ok( 215 !!connectionId, 216 "Found a valid connection id in the first connection_attempt event" 217 ); 218 for (const evt of connectionEvents) { 219 is( 220 evt.extras.connection_id, 221 connectionId, 222 "All connection_attempt events share the same connection id" 223 ); 224 } 225 } 226 227 // Small helper to create the expected event extras object for connection_attempt events 228 function getEventExtras(status, runtimeId) { 229 return { 230 connection_type: "usb", 231 runtime_id: runtimeId, 232 status, 233 }; 234 } 235 236 // Open about:debugging, setup telemetry, mocks and create a mocked USB runtime. 237 async function setupConnectionAttemptTest() { 238 const mocks = new Mocks(); 239 setupTelemetryTest(); 240 241 const { tab, document } = await openAboutDebugging(); 242 243 const sessionId = getOpenEventSessionId(); 244 ok(!isNaN(sessionId), "Open event has a valid session id"); 245 246 mocks.createUSBRuntime(USB_RUNTIME.id, { 247 deviceName: USB_RUNTIME.deviceName, 248 name: USB_RUNTIME.name, 249 shortName: USB_RUNTIME.shortName, 250 }); 251 mocks.emitUSBUpdate(); 252 253 info("Wait for the runtime to appear in the sidebar"); 254 await waitUntil(() => findSidebarItemByText(USB_RUNTIME.shortName, document)); 255 const evts = checkTelemetryEvents( 256 [ 257 { method: "device_added", extras: {} }, 258 { method: "runtime_added", extras: {} }, 259 ], 260 sessionId 261 ); 262 263 const runtimeId = evts.filter(e => e.method === "runtime_added")[0].extras 264 .runtime_id; 265 return { doc: document, mocks, runtimeId, sessionId, tab }; 266 } 267 268 async function removeUsbRuntime(runtime, mocks, doc) { 269 mocks.removeRuntime(runtime.id); 270 mocks.emitUSBUpdate(); 271 await waitUntil( 272 () => 273 !findSidebarItemByText(runtime.name, doc) && 274 !findSidebarItemByText(runtime.shortName, doc) 275 ); 276 }