Repository: microsoft/vscode-edge-devtools
Pull request #377 (2021-05-05 00:01:06)

[Telemetry]: Adding telemetry to track if the target URL is localhost or not
Changes in this PR:
- Adds a function `reportUrlType()` that determines whether the url is localhost, file:// protocol, or other, and sends this property with the telemetry event
- `reportUrlType()` is called when the browser is launched (except if it's launching the start page)
- `reportUrlType()` is called each time a target's url changes if the target is a page
- Event listeners on the browser instance are created with the `on()` function now, since `addListener()` is deprecated


Patch:
diff --git a/src/extension.ts b/src/extension.ts
index 381e319..c08a047 100644
--- a/src/extension.ts
+++ b/src/extension.ts
@@ -1,7 +1,7 @@
 // Copyright (c) Microsoft Corporation. All rights reserved.
 // Licensed under the MIT License.
 
-import { Browser } from 'puppeteer-core';
+import { Browser, Target } from 'puppeteer-core';
 import * as vscode from 'vscode';
 import * as debugCore from 'vscode-chrome-debug-core';
 import TelemetryReporter from 'vscode-extension-telemetry';
@@ -25,10 +25,12 @@ import {
     LaunchConfig,
     openNewTab,
     SETTINGS_DEFAULT_ATTACH_INTERVAL,
+    SETTINGS_DEFAULT_URL,
     SETTINGS_STORE_NAME,
     SETTINGS_VIEW_NAME,
     getActiveDebugSessionId,
     getJsDebugCDPProxyWebsocketUrl,
+    reportUrlType,
 } from './utils';
 
 let telemetryReporter: Readonly<TelemetryReporter>;
@@ -329,12 +331,20 @@ export async function launch(context: vscode.ExtensionContext, launchUrl?: strin
             telemetryReporter.sendTelemetryEvent('command/launch/browser', browserProps);
 
         browserInstance = await launchBrowser(browserPath, port, url, userDataDir);
-        browserInstance.addListener('targetcreated', () => {
+        if (url !== SETTINGS_DEFAULT_URL) {
+            reportUrlType(url, telemetryReporter);
+        }
+        browserInstance.on('targetcreated', () => {
             cdpTargetsProvider.refresh();
         });
-        browserInstance.addListener('targetdestroyed', () => {
+        browserInstance.on('targetdestroyed', () => {
             cdpTargetsProvider.refresh();
         });
+        browserInstance.on('targetchanged',  (target: Target) => {
+            if (target.type() === 'page') {
+                reportUrlType(target.url(), telemetryReporter);
+            }
+        });
         await attach(context, url, config);
     }
 }
diff --git a/src/utils.ts b/src/utils.ts
index 90770e4..ad23dee 100644
--- a/src/utils.ts
+++ b/src/utils.ts
@@ -685,6 +685,21 @@ async function verifyFlavorPath(flavor: BrowserFlavor | undefined, platform: Pla
     }
 }
 
+export function reportUrlType(url: string, telemetryReporter: Readonly<TelemetryReporter>): void {
+    const localhostPattern = /^https?:\/\/localhost:/;
+    const ipPattern = /(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)/;
+    const filePattern = /^file:\/\//;
+    let urlType;
+    if (localhostPattern.exec(url) || ipPattern.exec(url)) {
+        urlType = 'localhost';
+    } else if (filePattern.exec(url)) {
+        urlType = 'file';
+    } else {
+        urlType = 'other';
+    }
+    telemetryReporter.sendTelemetryEvent('user/browserNavigation', { 'urlType': urlType });
+}
+
 (function initialize() {
     // insertion order matters.
     msEdgeBrowserMapping.set('Stable', {


Test patch:
diff --git a/test/extension.test.ts b/test/extension.test.ts
index 2fd19a8..34c8ce3 100644
--- a/test/extension.test.ts
+++ b/test/extension.test.ts
@@ -378,7 +378,7 @@ describe("extension", () => {
     });
 
     describe("launch", () => {
-        const fakeBrowser = {addListener: () => null};
+        const fakeBrowser = {on: () => null};
         let mockReporter: Mocked<Readonly<TelemetryReporter>>;
         let mockUtils: Partial<Mocked<typeof import("../src/utils")>>;
         let mockPanel: Partial<Mocked<typeof import("../src/devtoolsPanel")>>;
@@ -403,6 +403,7 @@ describe("extension", () => {
                 getLaunchJson: jest.fn(),
                 getJsDebugCDPProxyWebsocketUrl: jest.fn(),
                 buttonCode: { launch: '' },
+                reportUrlType: jest.fn(),
             };
 
             mockPanel = {
diff --git a/test/utils.test.ts b/test/utils.test.ts
index 6d1101b..3d4091b 100644
--- a/test/utils.test.ts
+++ b/test/utils.test.ts
@@ -6,7 +6,7 @@
 
 import * as path from "path";
 
-import { createFakeExtensionContext, createFakeGet, createFakeVSCode, Mocked } from "./helpers/helpers";
+import { createFakeExtensionContext, createFakeGet, createFakeTelemetryReporter, createFakeVSCode, Mocked } from "./helpers/helpers";
 import { BrowserFlavor, IRemoteTargetJson, IUserConfig } from "../src/utils";
 
 jest.mock("vscode", () => null, { virtual: true });
@@ -1036,4 +1036,35 @@ describe("utils", () => {
             expect(result).toBe(true);
         });
     });
+
+    describe('reportUrlType', () => {
+        it('correctly classifies the url type', async () => {
+            const reporter = createFakeTelemetryReporter();
+            const input = [
+                'http://localhost:8000',
+                'https://localhost:8080',
+                '127.0.0.1',
+                '123.123.123.123',
+                'file://path/to/resource',
+                'file:///path/to/resouce',
+                '123.456.789.0',
+                'https://www.bing.com'
+            ];
+            const expected = [
+                'localhost',
+                'localhost',
+                'localhost',
+                'localhost',
+                'file',
+                'file',
+                'other',
+                'other',
+            ];
+
+            for (let i = 0; i < input.length; i++) {
+                utils.reportUrlType(input[i], reporter);
+                expect(reporter.sendTelemetryEvent).toBeCalledWith('user/browserNavigation', { 'urlType': expected[i] })
+            }
+        });
+    });
 });