diff --git a/CHANGELOG.md b/CHANGELOG.md
index 91aad481260c..f5c68097199a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -91,8 +91,6 @@
([#11221](https://github.com/Microsoft/vscode-python/issues/11221))
1. Lazy load types from `jupyterlab/services` and similar `npm modules`.
([#11297](https://github.com/Microsoft/vscode-python/issues/11297))
-1. Update telemetry on errors and exceptions to use [vscode-extension-telemetry](https://www.npmjs.com/package/vscode-extension-telemetry).
- ([#11436](https://github.com/Microsoft/vscode-python/issues/11436))
1. Remove IJMPConnection implementation while maintaining tests written for it.
([#11470](https://github.com/Microsoft/vscode-python/issues/11470))
1. Implement an IJupyterVariables provider for the debugger.
diff --git a/gulpfile.js b/gulpfile.js
index a290b648282a..f2f91140595b 100644
--- a/gulpfile.js
+++ b/gulpfile.js
@@ -287,9 +287,7 @@ function getAllowedWarningsForWebPack(buildConfig) {
'WARNING in ./node_modules/@jupyterlab/services/node_modules/ws/lib/validation.js',
'WARNING in ./node_modules/any-promise/register.js',
'WARNING in ./node_modules/log4js/lib/appenders/index.js',
- 'WARNING in ./node_modules/log4js/lib/clustering.js',
- 'WARNING in ./node_modules/diagnostic-channel-publishers/dist/src/azure-coretracing.pub.js',
- 'WARNING in ./node_modules/applicationinsights/out/AutoCollection/NativePerformance.js'
+ 'WARNING in ./node_modules/log4js/lib/clustering.js'
];
case 'extension':
return [
@@ -302,16 +300,10 @@ function getAllowedWarningsForWebPack(buildConfig) {
'remove-files-plugin@1.4.0:',
'WARNING in ./node_modules/@jupyterlab/services/node_modules/ws/lib/buffer-util.js',
'WARNING in ./node_modules/@jupyterlab/services/node_modules/ws/lib/validation.js',
- 'WARNING in ./node_modules/@jupyterlab/services/node_modules/ws/lib/Validation.js',
- 'WARNING in ./node_modules/diagnostic-channel-publishers/dist/src/azure-coretracing.pub.js',
- 'WARNING in ./node_modules/applicationinsights/out/AutoCollection/NativePerformance.js'
+ 'WARNING in ./node_modules/@jupyterlab/services/node_modules/ws/lib/Validation.js'
];
case 'debugAdapter':
- return [
- 'WARNING in ./node_modules/vscode-uri/lib/index.js',
- 'WARNING in ./node_modules/diagnostic-channel-publishers/dist/src/azure-coretracing.pub.js',
- 'WARNING in ./node_modules/applicationinsights/out/AutoCollection/NativePerformance.js'
- ];
+ return ['WARNING in ./node_modules/vscode-uri/lib/index.js'];
default:
throw new Error('Unknown WebPack Configuration');
}
diff --git a/package-lock.json b/package-lock.json
index 76382dd41cdc..3c950b27ef58 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -4657,14 +4657,13 @@
}
},
"applicationinsights": {
- "version": "1.7.4",
- "resolved": "https://registry.npmjs.org/applicationinsights/-/applicationinsights-1.7.4.tgz",
- "integrity": "sha512-XFLsNlcanpjFhHNvVWEfcm6hr7lu9znnb6Le1Lk5RE03YUV9X2B2n2MfM4kJZRrUdV+C0hdHxvWyv+vWoLfY7A==",
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/applicationinsights/-/applicationinsights-1.0.6.tgz",
+ "integrity": "sha512-VQT3kBpJVPw5fCO5n+WUeSx0VHjxFtD7znYbILBlVgOS9/cMDuGFmV2Br3ObzFyZUDGNbEfW36fD1y2/vAiCKw==",
"requires": {
- "cls-hooked": "^4.2.2",
- "continuation-local-storage": "^3.2.1",
"diagnostic-channel": "0.2.0",
- "diagnostic-channel-publishers": "^0.3.3"
+ "diagnostic-channel-publishers": "0.2.1",
+ "zone.js": "0.7.6"
}
},
"aproba": {
@@ -5231,28 +5230,11 @@
"integrity": "sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ==",
"dev": true
},
- "async-hook-jl": {
- "version": "1.7.6",
- "resolved": "https://registry.npmjs.org/async-hook-jl/-/async-hook-jl-1.7.6.tgz",
- "integrity": "sha512-gFaHkFfSxTjvoxDMYqDuGHlcRyUuamF8s+ZTtJdDzqjws4mCt7v0vuV79/E2Wr2/riMQgtG4/yUtXWs1gZ7JMg==",
- "requires": {
- "stack-chain": "^1.3.7"
- }
- },
"async-limiter": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.0.tgz",
"integrity": "sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg=="
},
- "async-listener": {
- "version": "0.6.10",
- "resolved": "https://registry.npmjs.org/async-listener/-/async-listener-0.6.10.tgz",
- "integrity": "sha512-gpuo6xOyF4D5DE5WvyqZdPA3NGhiT6Qf07l7DCB0wwDEsLvDIbCr6j9S5aj5Ch96dLace5tXVzWBZkxU/c5ohw==",
- "requires": {
- "semver": "^5.3.0",
- "shimmer": "^1.1.0"
- }
- },
"async-settle": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/async-settle/-/async-settle-1.0.0.tgz",
@@ -6825,16 +6807,6 @@
}
}
},
- "cls-hooked": {
- "version": "4.2.2",
- "resolved": "https://registry.npmjs.org/cls-hooked/-/cls-hooked-4.2.2.tgz",
- "integrity": "sha512-J4Xj5f5wq/4jAvcdgoGsL3G103BtWpZrMo8NEinRltN+xpTZdI+M38pyQqhuFU/P792xkMFvnKSf+Lm81U1bxw==",
- "requires": {
- "async-hook-jl": "^1.7.6",
- "emitter-listener": "^1.0.1",
- "semver": "^5.4.1"
- }
- },
"clsx": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/clsx/-/clsx-1.0.4.tgz",
@@ -7176,15 +7148,6 @@
"resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz",
"integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA=="
},
- "continuation-local-storage": {
- "version": "3.2.1",
- "resolved": "https://registry.npmjs.org/continuation-local-storage/-/continuation-local-storage-3.2.1.tgz",
- "integrity": "sha512-jx44cconVqkCEEyLSKWwkvUXwO561jXMa3LPjTPsm5QR22PA0/mhe33FT4Xb5y74JDvt/Cq+5lm8S8rskLv9ZA==",
- "requires": {
- "async-listener": "^0.6.0",
- "emitter-listener": "^1.1.1"
- }
- },
"convert-source-map": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.6.0.tgz",
@@ -8727,9 +8690,9 @@
}
},
"diagnostic-channel-publishers": {
- "version": "0.3.4",
- "resolved": "https://registry.npmjs.org/diagnostic-channel-publishers/-/diagnostic-channel-publishers-0.3.4.tgz",
- "integrity": "sha512-SZ1zMfFiEabf4Qx0Og9V1gMsRoqz3O+5ENkVcNOfI+SMJ3QhQsdEoKX99r0zvreagXot2parPxmrwwUM/ja8ug=="
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/diagnostic-channel-publishers/-/diagnostic-channel-publishers-0.2.1.tgz",
+ "integrity": "sha1-ji1geottef6IC1SLxYzGvrKIxPM="
},
"diagnostics": {
"version": "1.1.1",
@@ -9096,14 +9059,6 @@
"minimalistic-crypto-utils": "^1.0.0"
}
},
- "emitter-listener": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/emitter-listener/-/emitter-listener-1.1.2.tgz",
- "integrity": "sha512-Bt1sBAGFHY9DKY+4/2cV6izcKJUf5T7/gkdmkxzX/qv9CcGH8xSwVRW5mtX03SWJtRTWSOpzCuWN9rBFYZepZQ==",
- "requires": {
- "shimmer": "^1.2.0"
- }
- },
"emoji-regex": {
"version": "7.0.3",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz",
@@ -20713,11 +20668,6 @@
}
}
},
- "shimmer": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/shimmer/-/shimmer-1.2.1.tgz",
- "integrity": "sha512-sQTKC1Re/rM6XyFM6fIAGHRPVGvyXfgzIDvzoq608vM+jeyVD0Tu1E6Np0Kc2zAIFWIj963V2800iF/9LPieQw=="
- },
"shortid": {
"version": "2.2.14",
"resolved": "https://registry.npmjs.org/shortid/-/shortid-2.2.14.tgz",
@@ -21310,11 +21260,6 @@
"figgy-pudding": "^3.5.1"
}
},
- "stack-chain": {
- "version": "1.3.7",
- "resolved": "https://registry.npmjs.org/stack-chain/-/stack-chain-1.3.7.tgz",
- "integrity": "sha1-0ZLJ/06moiyUxN1FkXHj8AzqEoU="
- },
"stack-trace": {
"version": "0.0.10",
"resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz",
@@ -24618,11 +24563,11 @@
"integrity": "sha512-+OMm11R1bGYbpIJ5eQIkwoDGFF4GvBz3Ztl6/VM+/RNNb2Gjk2c0Ku+oMmfhlTmTlPCpgHBsH4JqVCbUYhu5bA=="
},
"vscode-extension-telemetry": {
- "version": "0.1.4",
- "resolved": "https://registry.npmjs.org/vscode-extension-telemetry/-/vscode-extension-telemetry-0.1.4.tgz",
- "integrity": "sha512-9U2pUZ/YwZBfA8CkBrHwMxjnq9Ab+ng8daJWJzEQ6CAxlZyRhmck23bx2lqqpEwGWJCiuceQy4k0Me6llEB4zw==",
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/vscode-extension-telemetry/-/vscode-extension-telemetry-0.1.0.tgz",
+ "integrity": "sha512-WVCnP+uLxlqB6UD98yQNV47mR5Rf79LFxpuZhSPhEf0Sb4tPZed3a63n003/dchhOwyCTCBuNN4n8XKJkLEI1Q==",
"requires": {
- "applicationinsights": "1.7.4"
+ "applicationinsights": "1.0.6"
}
},
"vscode-jsonrpc": {
@@ -25736,6 +25681,11 @@
}
}
}
+ },
+ "zone.js": {
+ "version": "0.7.6",
+ "resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.7.6.tgz",
+ "integrity": "sha1-+7w50+AmHQmG8boGMG6zrrDSIAk="
}
}
}
diff --git a/package.json b/package.json
index 892c9fe62aa1..e2d8413913ee 100644
--- a/package.json
+++ b/package.json
@@ -2980,7 +2980,7 @@
"untildify": "^3.0.2",
"vscode-debugadapter": "^1.28.0",
"vscode-debugprotocol": "^1.28.0",
- "vscode-extension-telemetry": "0.1.4",
+ "vscode-extension-telemetry": "0.1.0",
"vscode-jsonrpc": "^5.0.1",
"vscode-languageclient": "^6.2.0-next.2",
"vscode-languageserver": "^6.2.0-next.2",
diff --git a/src/client/telemetry/index.ts b/src/client/telemetry/index.ts
index 386f3fb352e9..89a918976ca9 100644
--- a/src/client/telemetry/index.ts
+++ b/src/client/telemetry/index.ts
@@ -1,14 +1,15 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
-
+// tslint:disable:no-reference no-any import-name no-any function-name
+///
import type { JSONObject } from '@phosphor/coreutils';
+import { basename as pathBasename, sep as pathSep } from 'path';
import * as stackTrace from 'stack-trace';
-// tslint:disable-next-line: import-name
-import TelemetryReporter from 'vscode-extension-telemetry/lib/telemetryReporter';
+import TelemetryReporter from 'vscode-extension-telemetry';
import { DiagnosticCodes } from '../application/diagnostics/constants';
import { IWorkspaceService } from '../common/application/types';
-import { AppinsightsKey, isTestExecution, PVSC_EXTENSION_ID } from '../common/constants';
+import { AppinsightsKey, EXTENSION_ROOT_DIR, isTestExecution, PVSC_EXTENSION_ID } from '../common/constants';
import { traceError, traceInfo } from '../common/logger';
import { TerminalShellType } from '../common/terminal/types';
import { StopWatch } from '../common/utils/stopWatch';
@@ -27,12 +28,10 @@ import { TestProvider } from '../testing/common/types';
import { EventName, PlatformErrors } from './constants';
import { LinterTrigger, TestTool } from './types';
-// tslint:disable: no-any
-
/**
* Checks whether telemetry is supported.
* Its possible this function gets called within Debug Adapter, vscode isn't available in there.
- * Within DA, there's a completely different way to send telemetry.
+ * Withiin DA, there's a completely different way to send telemetry.
* @returns {boolean}
*/
function isTelemetrySupported(): boolean {
@@ -64,12 +63,14 @@ function getTelemetryReporter() {
const extensionId = PVSC_EXTENSION_ID;
// tslint:disable-next-line:no-require-imports
const extensions = (require('vscode') as typeof import('vscode')).extensions;
+ // tslint:disable-next-line:no-non-null-assertion
const extension = extensions.getExtension(extensionId)!;
+ // tslint:disable-next-line:no-unsafe-any
const extensionVersion = extension.packageJSON.version;
// tslint:disable-next-line:no-require-imports
const reporter = require('vscode-extension-telemetry').default as typeof TelemetryReporter;
- return (telemetryReporter = new reporter(extensionId, extensionVersion, AppinsightsKey, true));
+ return (telemetryReporter = new reporter(extensionId, extensionVersion, AppinsightsKey));
}
export function clearTelemetryReporter() {
@@ -87,46 +88,45 @@ export function sendTelemetryEvent
= {};
- let eventNameSent = eventName as string;
- if (ex) {
- // When sending telemetry events for exceptions no need to send custom properties.
+ if (ex && (eventName as any) !== 'ERROR') {
+ // When sending `ERROR` telemetry event no need to send custom properties.
// Else we have to review all properties every time as part of GDPR.
// Assume we have 10 events all with their own properties.
// As we have errors for each event, those properties are treated as new data items.
// Hence they need to be classified as part of the GDPR process, and thats unnecessary and onerous.
- eventNameSent = 'ERROR';
- customProperties = { originalEventName: eventName as string, stackTrace: serializeStackTrace(ex) };
- reporter.sendTelemetryErrorEvent(eventNameSent, customProperties, measures, []);
- } else {
- if (properties) {
- const data = properties as any;
- Object.getOwnPropertyNames(data).forEach((prop) => {
- if (data[prop] === undefined || data[prop] === null) {
- return;
- }
- try {
- // If there are any errors in serializing one property, ignore that and move on.
- // Else nothing will be sent.
- customProperties[prop] =
- typeof data[prop] === 'string'
- ? data[prop]
- : typeof data[prop] === 'object'
- ? 'object'
- : data[prop].toString();
- } catch (ex) {
- traceError(`Failed to serialize ${prop} for ${eventName}`, ex);
- }
- });
- }
-
- reporter.sendTelemetryEvent(eventNameSent, customProperties, measures);
+ const props: Record = {};
+ props.stackTrace = getStackTrace(ex);
+ props.originalEventName = (eventName as any) as string;
+ reporter.sendTelemetryEvent('ERROR', props, measures);
}
-
+ const customProperties: Record = {};
+ if (properties) {
+ // tslint:disable-next-line:prefer-type-cast no-any
+ const data = properties as any;
+ Object.getOwnPropertyNames(data).forEach((prop) => {
+ if (data[prop] === undefined || data[prop] === null) {
+ return;
+ }
+ try {
+ // If there are any errors in serializing one property, ignore that and move on.
+ // Else nothign will be sent.
+ // tslint:disable-next-line:prefer-type-cast no-any no-unsafe-any
+ (customProperties as any)[prop] =
+ typeof data[prop] === 'string'
+ ? data[prop]
+ : typeof data[prop] === 'object'
+ ? 'object'
+ : data[prop].toString();
+ } catch (ex) {
+ traceError(`Failed to serialize ${prop} for ${eventName}`, ex);
+ }
+ });
+ }
+ reporter.sendTelemetryEvent((eventName as any) as string, customProperties, measures);
if (process.env && process.env.VSC_PYTHON_LOG_TELEMETRY) {
traceInfo(
- `Telemetry Event : ${eventNameSent} Measures: ${JSON.stringify(measures)} Props: ${JSON.stringify(
+ `Telemetry Event : ${eventName} Measures: ${JSON.stringify(measures)} Props: ${JSON.stringify(
customProperties
)} `
);
@@ -246,12 +246,32 @@ export function sendTelemetryWhenDone${filename.substring(EXTENSION_ROOT_DIR.length)}`;
+ } else {
+ // We don't really care about files outside our extension.
+ filename = `${pathSep}${pathBasename(filename)}`;
+ }
+ return filename;
+}
+
+function sanitizeName(name: string): string {
+ if (name.indexOf('/') === -1 && name.indexOf('\\') === -1) {
+ return name;
+ } else {
+ return '';
+ }
+}
+
+function getStackTrace(ex: Error): string {
+ // We aren't showing the error message (ex.message) since it might
+ // contain PII.
let trace = '';
for (const frame of stackTrace.parse(ex)) {
- const filename = frame.getFileName();
+ let filename = frame.getFileName();
if (filename) {
+ filename = sanitizeFilename(filename);
const lineno = frame.getLineNumber();
const colno = frame.getColumnNumber();
trace += `\n\tat ${getCallsite(frame)} ${filename}:${lineno}:${colno}`;
@@ -277,7 +297,7 @@ function getCallsite(frame: stackTrace.StackFrame) {
parts.push(frame.getFunctionName());
}
}
- return parts.join('.');
+ return parts.map(sanitizeName).join('.');
}
// Map all events to their properties
diff --git a/src/client/telemetry/vscode-extension-telemetry.d.ts b/src/client/telemetry/vscode-extension-telemetry.d.ts
new file mode 100644
index 000000000000..a5d3d6294739
--- /dev/null
+++ b/src/client/telemetry/vscode-extension-telemetry.d.ts
@@ -0,0 +1,33 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+declare module 'vscode-extension-telemetry' {
+ export default class TelemetryReporter {
+ /**
+ * Constructs a new telemetry reporter
+ * @param {string} extensionId All events will be prefixed with this event name
+ * @param {string} extensionVersion Extension version to be reported with each event
+ * @param {string} key The application insights key
+ */
+ // tslint:disable-next-line:no-empty
+ constructor(extensionId: string, extensionVersion: string, key: string);
+
+ /**
+ * Sends a telemetry event
+ * @param {string} eventName The event name
+ * @param {object} properties An associative array of strings
+ * @param {object} measures An associative array of numbers
+ */
+ // tslint:disable-next-line:member-access
+ public sendTelemetryEvent(
+ eventName: string,
+ properties?: {
+ [key: string]: string;
+ },
+ measures?: {
+ [key: string]: number;
+ // tslint:disable-next-line:no-empty
+ }
+ ): void;
+ }
+}
diff --git a/src/test/telemetry/index.unit.test.ts b/src/test/telemetry/index.unit.test.ts
index 0bc9d757649e..aad20386af82 100644
--- a/src/test/telemetry/index.unit.test.ts
+++ b/src/test/telemetry/index.unit.test.ts
@@ -23,22 +23,16 @@ suite('Telemetry', () => {
public static eventName: string[] = [];
public static properties: Record[] = [];
public static measures: {}[] = [];
- public static errorProps: string[] | undefined;
public static clear() {
Reporter.eventName = [];
Reporter.properties = [];
Reporter.measures = [];
- Reporter.errorProps = undefined;
}
public sendTelemetryEvent(eventName: string, properties?: {}, measures?: {}) {
Reporter.eventName.push(eventName);
Reporter.properties.push(properties!);
Reporter.measures.push(measures!);
}
- public sendTelemetryErrorEvent(eventName: string, properties?: {}, measures?: {}, errorProps?: string[]) {
- this.sendTelemetryEvent(eventName, properties, measures);
- Reporter.errorProps = errorProps;
- }
}
setup(() => {
@@ -100,7 +94,7 @@ suite('Telemetry', () => {
expect(Reporter.measures).to.deep.equal([measures]);
expect(Reporter.properties).to.deep.equal([properties]);
});
- test('Send Telemetry with properties', () => {
+ test('Send Telemetry', () => {
rewiremock.enable();
rewiremock('vscode-extension-telemetry').with({ default: Reporter });
@@ -128,33 +122,31 @@ suite('Telemetry', () => {
originalEventName: eventName
};
- expect(Reporter.eventName).to.deep.equal(['ERROR']);
- expect(Reporter.measures).to.deep.equal([measures]);
+ expect(Reporter.eventName).to.deep.equal(['ERROR', eventName]);
+ expect(Reporter.measures).to.deep.equal([measures, measures]);
expect(Reporter.properties[0].stackTrace).to.be.length.greaterThan(1);
delete Reporter.properties[0].stackTrace;
- expect(Reporter.properties).to.deep.equal([expectedErrorProperties]);
- expect(Reporter.errorProps).to.deep.equal([]);
+ expect(Reporter.properties).to.deep.equal([expectedErrorProperties, properties]);
});
- test('Send Error Telemetry with stack trace', () => {
+ test('Send Error Telemetry', () => {
rewiremock.enable();
const error = new Error('Boo');
- const root = EXTENSION_ROOT_DIR.replace(/\\/g, '/');
error.stack = [
'Error: Boo',
- `at Context.test (${root}/src/test/telemetry/index.unit.test.ts:50:23)`,
- `at callFn (${root}/node_modules/mocha/lib/runnable.js:372:21)`,
- `at Test.Runnable.run (${root}/node_modules/mocha/lib/runnable.js:364:7)`,
- `at Runner.runTest (${root}/node_modules/mocha/lib/runner.js:455:10)`,
- `at ${root}/node_modules/mocha/lib/runner.js:573:12`,
- `at next (${root}/node_modules/mocha/lib/runner.js:369:14)`,
- `at ${root}/node_modules/mocha/lib/runner.js:379:7`,
- `at next (${root}/node_modules/mocha/lib/runner.js:303:14)`,
- `at ${root}/node_modules/mocha/lib/runner.js:342:7`,
- `at done (${root}/node_modules/mocha/lib/runnable.js:319:5)`,
- `at callFn (${root}/node_modules/mocha/lib/runnable.js:395:7)`,
- `at Hook.Runnable.run (${root}/node_modules/mocha/lib/runnable.js:364:7)`,
- `at next (${root}/node_modules/mocha/lib/runner.js:317:10)`,
- `at Immediate. (${root}/node_modules/mocha/lib/runner.js:347:5)`,
+ `at Context.test (${EXTENSION_ROOT_DIR}/src/test/telemetry/index.unit.test.ts:50:23)`,
+ `at callFn (${EXTENSION_ROOT_DIR}/node_modules/mocha/lib/runnable.js:372:21)`,
+ `at Test.Runnable.run (${EXTENSION_ROOT_DIR}/node_modules/mocha/lib/runnable.js:364:7)`,
+ `at Runner.runTest (${EXTENSION_ROOT_DIR}/node_modules/mocha/lib/runner.js:455:10)`,
+ `at ${EXTENSION_ROOT_DIR}/node_modules/mocha/lib/runner.js:573:12`,
+ `at next (${EXTENSION_ROOT_DIR}/node_modules/mocha/lib/runner.js:369:14)`,
+ `at ${EXTENSION_ROOT_DIR}/node_modules/mocha/lib/runner.js:379:7`,
+ `at next (${EXTENSION_ROOT_DIR}/node_modules/mocha/lib/runner.js:303:14)`,
+ `at ${EXTENSION_ROOT_DIR}/node_modules/mocha/lib/runner.js:342:7`,
+ `at done (${EXTENSION_ROOT_DIR}/node_modules/mocha/lib/runnable.js:319:5)`,
+ `at callFn (${EXTENSION_ROOT_DIR}/node_modules/mocha/lib/runnable.js:395:7)`,
+ `at Hook.Runnable.run (${EXTENSION_ROOT_DIR}/node_modules/mocha/lib/runnable.js:364:7)`,
+ `at next (${EXTENSION_ROOT_DIR}/node_modules/mocha/lib/runner.js:317:10)`,
+ `at Immediate. (${EXTENSION_ROOT_DIR}/node_modules/mocha/lib/runner.js:347:5)`,
'at runCallback (timers.js:789:20)',
'at tryOnImmediate (timers.js:751:5)',
'at processImmediate [as _immediateCallback] (timers.js:722:5)'
@@ -175,30 +167,112 @@ suite('Telemetry', () => {
const stackTrace = Reporter.properties[0].stackTrace;
delete Reporter.properties[0].stackTrace;
- expect(Reporter.eventName).to.deep.equal(['ERROR']);
- expect(Reporter.measures).to.deep.equal([measures]);
- expect(Reporter.properties).to.deep.equal([expectedErrorProperties]);
+ expect(Reporter.eventName).to.deep.equal(['ERROR', eventName]);
+ expect(Reporter.measures).to.deep.equal([measures, measures]);
+ expect(Reporter.properties).to.deep.equal([expectedErrorProperties, properties]);
+ expect(stackTrace).to.be.length.greaterThan(1);
+
+ const expectedStack = [
+ 'at Context.test /src/test/telemetry/index.unit.test.ts:50:23\n\tat callFn /node_modules/mocha/lib/runnable.js:372:21',
+ 'at Test.Runnable.run /node_modules/mocha/lib/runnable.js:364:7',
+ 'at Runner.runTest /node_modules/mocha/lib/runner.js:455:10',
+ 'at /node_modules/mocha/lib/runner.js:573:12',
+ 'at next /node_modules/mocha/lib/runner.js:369:14',
+ 'at /node_modules/mocha/lib/runner.js:379:7',
+ 'at next /node_modules/mocha/lib/runner.js:303:14',
+ 'at /node_modules/mocha/lib/runner.js:342:7',
+ 'at done /node_modules/mocha/lib/runnable.js:319:5',
+ 'at callFn /node_modules/mocha/lib/runnable.js:395:7',
+ 'at Hook.Runnable.run /node_modules/mocha/lib/runnable.js:364:7',
+ 'at next /node_modules/mocha/lib/runner.js:317:10',
+ 'at Immediate /node_modules/mocha/lib/runner.js:347:5',
+ 'at runCallback /timers.js:789:20',
+ 'at tryOnImmediate /timers.js:751:5',
+ 'at processImmediate [as _immediateCallback] /timers.js:722:5'
+ ].join('\n\t');
+
+ expect(stackTrace).to.be.equal(expectedStack);
+ });
+ test('Ensure non extension file paths are stripped from stack trace', () => {
+ rewiremock.enable();
+ const error = new Error('Boo');
+ error.stack = [
+ 'Error: Boo',
+ `at Context.test (${EXTENSION_ROOT_DIR}/src/test/telemetry/index.unit.test.ts:50:23)`,
+ 'at callFn (c:/one/two/user/node_modules/mocha/lib/runnable.js:372:21)',
+ 'at Test.Runnable.run (/usr/Paul/Homer/desktop/node_modules/mocha/lib/runnable.js:364:7)',
+ 'at Runner.runTest (\\wowwee/node_modules/mocha/lib/runner.js:455:10)',
+ `at Immediate. (${EXTENSION_ROOT_DIR}/node_modules/mocha/lib/runner.js:347:5)`
+ ].join('\n\t');
+ rewiremock('vscode-extension-telemetry').with({ default: Reporter });
+
+ const eventName = 'Testing';
+ const properties = { hello: 'world', foo: 'bar' };
+ const measures = { start: 123, end: 987 };
+
+ // tslint:disable-next-line:no-any
+ sendTelemetryEvent(eventName as any, measures, properties as any, error);
+
+ const expectedErrorProperties = {
+ originalEventName: eventName
+ };
+
+ const stackTrace = Reporter.properties[0].stackTrace;
+ delete Reporter.properties[0].stackTrace;
+
+ expect(Reporter.eventName).to.deep.equal(['ERROR', eventName]);
+ expect(Reporter.measures).to.deep.equal([measures, measures]);
+ expect(Reporter.properties).to.deep.equal([expectedErrorProperties, properties]);
+ expect(stackTrace).to.be.length.greaterThan(1);
+
+ const expectedStack = [
+ 'at Context.test /src/test/telemetry/index.unit.test.ts:50:23',
+ 'at callFn /runnable.js:372:21',
+ 'at Test.Runnable.run /runnable.js:364:7',
+ 'at Runner.runTest /runner.js:455:10',
+ 'at Immediate /node_modules/mocha/lib/runner.js:347:5'
+ ].join('\n\t');
+
+ expect(stackTrace).to.be.equal(expectedStack);
+ });
+ test('Ensure non function names containing file names (unlikely, but for sake of completeness) are stripped from stack trace', () => {
+ rewiremock.enable();
+ const error = new Error('Boo');
+ error.stack = [
+ 'Error: Boo',
+ `at Context.test (${EXTENSION_ROOT_DIR}/src/test/telemetry/index.unit.test.ts:50:23)`,
+ 'at callFn (c:/one/two/user/node_modules/mocha/lib/runnable.js:372:21)',
+ 'at Test./usr/Paul/Homer/desktop/node_modules/mocha/lib/runnable.run (/usr/Paul/Homer/desktop/node_modules/mocha/lib/runnable.js:364:7)',
+ 'at Runner.runTest (\\wowwee/node_modules/mocha/lib/runner.js:455:10)',
+ `at Immediate. (${EXTENSION_ROOT_DIR}/node_modules/mocha/lib/runner.js:347:5)`
+ ].join('\n\t');
+ rewiremock('vscode-extension-telemetry').with({ default: Reporter });
+
+ const eventName = 'Testing';
+ const properties = { hello: 'world', foo: 'bar' };
+ const measures = { start: 123, end: 987 };
+
+ // tslint:disable-next-line:no-any
+ sendTelemetryEvent(eventName as any, measures, properties as any, error);
+
+ const expectedErrorProperties = {
+ originalEventName: eventName
+ };
+
+ const stackTrace = Reporter.properties[0].stackTrace;
+ delete Reporter.properties[0].stackTrace;
+
+ expect(Reporter.eventName).to.deep.equal(['ERROR', eventName]);
+ expect(Reporter.measures).to.deep.equal([measures, measures]);
+ expect(Reporter.properties).to.deep.equal([expectedErrorProperties, properties]);
expect(stackTrace).to.be.length.greaterThan(1);
- expect(Reporter.errorProps).to.deep.equal([]);
const expectedStack = [
- `at Context.test ${root}/src/test/telemetry/index.unit.test.ts:50:23`,
- `at callFn ${root}/node_modules/mocha/lib/runnable.js:372:21`,
- `at Test.Runnable.run ${root}/node_modules/mocha/lib/runnable.js:364:7`,
- `at Runner.runTest ${root}/node_modules/mocha/lib/runner.js:455:10`,
- `at ${root}/node_modules/mocha/lib/runner.js:573:12`,
- `at next ${root}/node_modules/mocha/lib/runner.js:369:14`,
- `at ${root}/node_modules/mocha/lib/runner.js:379:7`,
- `at next ${root}/node_modules/mocha/lib/runner.js:303:14`,
- `at ${root}/node_modules/mocha/lib/runner.js:342:7`,
- `at done ${root}/node_modules/mocha/lib/runnable.js:319:5`,
- `at callFn ${root}/node_modules/mocha/lib/runnable.js:395:7`,
- `at Hook.Runnable.run ${root}/node_modules/mocha/lib/runnable.js:364:7`,
- `at next ${root}/node_modules/mocha/lib/runner.js:317:10`,
- `at Immediate ${root}/node_modules/mocha/lib/runner.js:347:5`,
- 'at runCallback timers.js:789:20',
- 'at tryOnImmediate timers.js:751:5',
- 'at processImmediate [as _immediateCallback] timers.js:722:5'
+ 'at Context.test /src/test/telemetry/index.unit.test.ts:50:23',
+ 'at callFn /runnable.js:372:21',
+ 'at .run /runnable.js:364:7',
+ 'at Runner.runTest /runner.js:455:10',
+ 'at Immediate /node_modules/mocha/lib/runner.js:347:5'
].join('\n\t');
expect(stackTrace).to.be.equal(expectedStack);
diff --git a/src/test/testing/helper.ts b/src/test/testing/helper.ts
index 3f9ffbf44c89..d973f28d07d6 100644
--- a/src/test/testing/helper.ts
+++ b/src/test/testing/helper.ts
@@ -32,7 +32,7 @@ export function lookForTestFile(tests: Tests, testFile: string) {
// Only "/" (forward slash) in the given filename is affected.
//
// This helps with readability in test code. It allows us to use
-// literals for filenames and dirnames instead of path.join().
+// literals for filenames and dirnames instead of oath.join().
export function fixPath(filename: string): string {
return filename.replace(/\//, sep);
}