Skip to content
This repository was archived by the owner on Oct 3, 2023. It is now read-only.

Commit 1b3a192

Browse files
crdgonzalezcadraffensperger
authored andcommitted
Add unit/integration testing for @opencensus/web-instrumentation-zone (#104)
1 parent a0d573f commit 1b3a192

9 files changed

Lines changed: 493 additions & 27 deletions

File tree

packages/opencensus-web-core/test/test-tracing.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ describe('Tracing', () => {
2626
let newExporter: webTypes.Exporter;
2727

2828
beforeEach(() => {
29-
tracing = new Tracing();
29+
tracing = Tracing.instance;
3030
tracer = tracing.tracer;
3131
spyOn(tracer, 'registerSpanEventListener');
3232
spyOn(tracer, 'unregisterSpanEventListener');
@@ -39,6 +39,7 @@ describe('Tracing', () => {
3939

4040
describe('default exporter', () => {
4141
it('is a no-op exporter', () => {
42+
tracing.unregisterExporter(tracing.exporter);
4243
expect(tracing.exporter).toBe(NOOP_EXPORTER);
4344
});
4445
});

packages/opencensus-web-instrumentation-zone/karma.conf.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,14 +26,14 @@ module.exports = (config) => {
2626
browsers: ['ChromeHeadless'],
2727
frameworks: ['jasmine'],
2828
reporters: ['spec', 'coverage-istanbul'],
29-
files: ['test/index.ts'],
30-
preprocessors: {'test/index.ts': ['webpack']},
29+
files: ['test/index.ts', 'node_modules/zone.js/dist/zone.js'],
30+
preprocessors: { 'test/index.ts': ['webpack'] },
3131
// Use webpack so that tree-shaking will remove all Node.js dependencies of
3232
// the `@opencensus/core` library, since they are not actually used in this
3333
// package's compiled JS code. Only the TypeScript interfaces from
3434
// `@opecensus/core` are used.
3535
webpack: webpackConfig,
36-
webpackMiddleware: {noInfo: true},
36+
webpackMiddleware: { noInfo: true },
3737
coverageIstanbulReporter: {
3838
reports: ['json', 'text-summary'],
3939
dir: path.join(__dirname, 'coverage'),

packages/opencensus-web-instrumentation-zone/package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
"build:dev": "webpack --config ./webpack/dev-bundles.config.js",
1010
"build": "webpack",
1111
"start:webpack-server": "webpack-dev-server --config ./webpack/dev-bundles.config.js",
12+
"test": "karma start --single-run",
13+
"test:start": "karma start",
1214
"codecov": "codecov -f coverage/*.json",
1315
"clean": "rimraf build/*",
1416
"check": "gts check",

packages/opencensus-web-instrumentation-zone/src/export-interaction-tracker.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,5 +40,5 @@ function setupExporter() {
4040

4141
export function startInteractionTracker() {
4242
setupExporter();
43-
return new InteractionTracker();
43+
InteractionTracker.startTracking();
4444
}

packages/opencensus-web-instrumentation-zone/src/interaction-tracker.ts

Lines changed: 43 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -24,18 +24,22 @@ import { AsyncTask } from './zone-types';
2424
import {
2525
OnPageInteractionStopwatch,
2626
startOnPageInteraction,
27-
} from './on-page-interaction';
27+
} from './on-page-interaction-stop-watch';
2828

2929
// Allows us to monkey patch Zone prototype without TS compiler errors.
3030
declare const Zone: ZoneType & { prototype: Zone };
3131

3232
// Delay of 50 ms to reset currentEventTracingZone.
33-
const RESET_TRACING_ZONE_DELAY = 50;
33+
export const RESET_TRACING_ZONE_DELAY = 50;
3434

3535
export class InteractionTracker {
3636
// Allows to track several events triggered by the same user interaction in the right Zone.
3737
private currentEventTracingZone?: Zone;
3838

39+
// Used to reset the currentEventTracingZone when the interaction
40+
// finishes before it is reset automatically.
41+
private currentResetTracingZoneTimeout?: number;
42+
3943
// Map of interaction Ids to stopwatches.
4044
private readonly interactions: {
4145
[index: string]: OnPageInteractionStopwatch;
@@ -46,23 +50,26 @@ export class InteractionTracker {
4650
[index: string]: number;
4751
} = {};
4852

49-
constructor() {
53+
private static singletonInstance: InteractionTracker;
54+
55+
private constructor() {
5056
this.patchZoneRunTask();
5157
this.patchZoneScheduleTask();
5258
this.patchZoneCancelTask();
5359
this.patchHistoryApi();
5460
}
5561

62+
static startTracking(): void {
63+
if (!this.singletonInstance) this.singletonInstance = new this();
64+
}
65+
5666
private patchZoneRunTask() {
5767
const runTask = Zone.prototype.runTask;
5868
Zone.prototype.runTask = (
5969
task: AsyncTask,
6070
applyThis: unknown,
6171
applyArgs: unknown
6272
) => {
63-
console.warn('Running task');
64-
console.log(task);
65-
6673
const interceptingElement = getTrackedElement(task);
6774
const interactionName = resolveInteractionName(
6875
interceptingElement,
@@ -87,7 +94,6 @@ export class InteractionTracker {
8794
try {
8895
return runTask.call(task.zone as {}, task, applyThis, applyArgs);
8996
} finally {
90-
console.log('Run task finished.');
9197
if (
9298
interceptingElement ||
9399
(shouldCountTask(task) && isTrackedTask(task))
@@ -155,7 +161,7 @@ export class InteractionTracker {
155161
tracing.tracer.startRootSpan(spanOptions, root => {
156162
// As startRootSpan creates the zone and Zone.current corresponds to the
157163
// new zone, we have to set the currentEventTracingZone with the Zone.current
158-
// to capture the new zone, also, start the `OnPageInteraction` to capture the
164+
// to capture the new zone, also, start the `OnPageInteraction` to capture the
159165
// new root span.
160166
this.currentEventTracingZone = Zone.current;
161167
this.interactions[traceId] = startOnPageInteraction({
@@ -170,14 +176,19 @@ export class InteractionTracker {
170176

171177
// Timeout to reset currentEventTracingZone to allow the creation of a new
172178
// zone for a new user interaction.
173-
Zone.root.run(() =>
174-
setTimeout(
175-
() => (this.currentEventTracingZone = undefined),
179+
Zone.root.run(() => {
180+
// Store the timeout in case the interaction finishes before
181+
// this callback runs.
182+
this.currentResetTracingZoneTimeout = window.setTimeout(
183+
() => this.resetCurrentTracingZone(),
176184
RESET_TRACING_ZONE_DELAY
177-
)
178-
);
179-
console.log('New zone:');
180-
console.log(this.currentEventTracingZone);
185+
);
186+
});
187+
}
188+
189+
private resetCurrentTracingZone() {
190+
this.currentEventTracingZone = undefined;
191+
this.currentResetTracingZoneTimeout = undefined;
181192
}
182193

183194
/** Increments the count of outstanding tasks for a given interaction id. */
@@ -233,6 +244,14 @@ export class InteractionTracker {
233244
this.interactionCompletionTimeouts[interactionId] = setTimeout(() => {
234245
this.completeInteraction(interactionId);
235246
delete this.interactionCompletionTimeouts[interactionId];
247+
// In case the interaction finished beforeresetCurrentTracingZone is called,
248+
// this in order to allow the creating of a new interaction.
249+
if (this.currentResetTracingZoneTimeout) {
250+
Zone.root.run(() => {
251+
clearTimeout(this.currentResetTracingZoneTimeout);
252+
this.resetCurrentTracingZone();
253+
});
254+
}
236255
});
237256
});
238257
}
@@ -375,10 +394,15 @@ function resolveInteractionName(
375394
function shouldCountTask(task: Task): boolean {
376395
if (!task.data) return false;
377396

378-
// Don't count periodic tasks with a delay greater than 1 s.
379-
if (task.data.isPeriodic && (task.data.delay && task.data.delay >= 1000)) {
380-
return false;
381-
}
397+
// Don't count periodic tasks like setInterval as they will be repeatedly
398+
// called. This will cause that the interaction never finishes, then would be
399+
// imposible to measure the stability of the interaction.
400+
// This case only applies for `setInterval` as we support `setTimeout`.
401+
// TODO: ideally OpenCensus Web can manage this kind of tasks, so for example
402+
// if a periodic task ends up doing some work in the future it will still
403+
// be associated with that same older tracing zone. This is something we have to
404+
// think of.
405+
if (task.data.isPeriodic) return false;
382406

383407
// We're only interested in macroTasks and microTasks.
384408
return task.type === 'macroTask' || task.type === 'microTask';

packages/opencensus-web-instrumentation-zone/src/on-page-interaction.ts renamed to packages/opencensus-web-instrumentation-zone/src/on-page-interaction-stop-watch.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,17 +43,20 @@ export class OnPageInteractionStopwatch {
4343
return this.taskCount;
4444
}
4545

46-
/** Stops the stopwatch and record the xhr response. */
46+
/**
47+
* Stops the stopwatch, fills root span attributes and ends the span.
48+
* If has remaining tasks do not end the root span.
49+
*/
4750
stopAndRecord(): void {
51+
if (this.hasRemainingTasks()) return;
52+
4853
const rootSpan = this.data.rootSpan;
4954
rootSpan.addAttribute('EventType', this.data.eventType);
5055
rootSpan.addAttribute('TargetElement', this.data.target.tagName);
5156
rootSpan.addAttribute(ATTRIBUTE_HTTP_URL, this.data.startLocationHref);
5257
rootSpan.addAttribute(ATTRIBUTE_HTTP_PATH, this.data.startLocationPath);
5358
rootSpan.addAttribute(ATTRIBUTE_HTTP_USER_AGENT, navigator.userAgent);
5459
rootSpan.end();
55-
console.log('End of tracking. The interaction is stable.');
56-
console.log('Time to stable: ' + this.data.rootSpan.duration + ' ms.');
5760
}
5861
}
5962

packages/opencensus-web-instrumentation-zone/test/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,5 @@
1616

1717
// This file is an entry point for the webpack test configuration, so this
1818
// should import from all test files.
19+
import './test-on-page-interaction-stop-watch';
20+
import './test-interaction-tracker';

0 commit comments

Comments
 (0)