diff --git a/dev-utils/jasmine.js b/dev-utils/jasmine.js index 21f473fbd..80eb36b1f 100644 --- a/dev-utils/jasmine.js +++ b/dev-utils/jasmine.js @@ -66,3 +66,21 @@ export function describeIf(description, specDefinitions, condition) { return describeFn.apply(this, [description, specDefinitions]) } + +/** + * Custom Jasmine matcher to check if given field in a object can either be + * undefined or match object deeply + */ +export function toEqualOrUndefined() { + return { + compare: (actual, expected) => { + const result = {} + if (typeof actual === 'undefined') { + result.pass = true + } else { + result.pass = jasmine.matchersUtil.equals(actual, expected) + } + return result + } + } +} diff --git a/packages/rum-core/src/common/utils.js b/packages/rum-core/src/common/utils.js index bf2631f73..290269c2d 100644 --- a/packages/rum-core/src/common/utils.js +++ b/packages/rum-core/src/common/utils.js @@ -200,13 +200,34 @@ function getTimeOrigin() { return PERF.timing.fetchStart } -function getPageMetadata() { +function getNetworkInformation() { + const connection = navigator && navigator.connection + + if (!(connection && typeof connection === 'object')) { + return + } + /** + * Ignoring `type` and `downlinkMax` as they are only + * supported on Chrome OS + */ return { - page: { - referer: document.referrer, - url: window.location.href - } + downlink: connection.downlink, + effective_type: connection.effectiveType, + rtt: connection.rtt, + save_data: !!connection.saveData + } +} + +function getPageMetadata() { + const context = { + referer: document.referrer, + url: window.location.href + } + const networkInfo = getNetworkInformation() + if (networkInfo != null) { + context.netinfo = networkInfo } + return { page: context } } function stripQueryStringFromUrl(url) { diff --git a/packages/rum-core/test/common/context.spec.js b/packages/rum-core/test/common/context.spec.js index cc93d18da..6d88d92bb 100644 --- a/packages/rum-core/test/common/context.spec.js +++ b/packages/rum-core/test/common/context.spec.js @@ -28,6 +28,7 @@ import resourceEntries from '../fixtures/resource-entries' import Span from '../../src/performance-monitoring/span' import Transaction from '../../src/performance-monitoring/transaction' import { PAGE_LOAD } from '../../src/common/constants' +import { toEqualOrUndefined } from '../../../../dev-utils/jasmine' import { mockGetEntriesByType } from '../utils/globals-mock' describe('Context', () => { @@ -166,6 +167,7 @@ describe('Context', () => { }) it('should enrich transaction with context info based on type', () => { + jasmine.addMatchers({ toEqualOrUndefined }) const transaction = new Transaction('test', 'custom') const trContext = { tags: { tag1: 'tag1' } } transaction.addContext(trContext) @@ -182,12 +184,16 @@ describe('Context', () => { message: 'test' } } - addTransactionContext(transaction, configContext) - expect(transaction.context).toEqual({ - page: { + const pageContext = { + page: jasmine.objectContaining({ referer: jasmine.any(String), url: jasmine.any(String) - }, + }) + } + + addTransactionContext(transaction, configContext) + expect(transaction.context).toEqual({ + ...pageContext, ...userContext, ...trContext }) @@ -197,10 +203,7 @@ describe('Context', () => { pageloadTr.end() addTransactionContext(pageloadTr, configContext) expect(pageloadTr.context).toEqual({ - page: { - referer: jasmine.any(String), - url: jasmine.any(String) - }, + ...pageContext, response: { transfer_size: 26941, encoded_body_size: 105297, @@ -211,7 +214,12 @@ describe('Context', () => { }, ...userContext }) - + expect(pageloadTr.context.page.netinfo).toEqualOrUndefined({ + downlink: jasmine.any(Number), + effective_type: jasmine.any(String), + rtt: jasmine.any(Number), + save_data: jasmine.any(Boolean) + }) unmock() }) }) diff --git a/packages/rum-core/test/error-logging/error-logging.spec.js b/packages/rum-core/test/error-logging/error-logging.spec.js index 0529b97f6..80da1089d 100644 --- a/packages/rum-core/test/error-logging/error-logging.spec.js +++ b/packages/rum-core/test/error-logging/error-logging.spec.js @@ -26,6 +26,7 @@ import { createServiceFactory, createCustomEvent } from '../' import { ERRORS } from '../../src/common/constants' import { getGlobalConfig } from '../../../../dev-utils/test-config' +import { toEqualOrUndefined } from '../../../../dev-utils/jasmine' const { agentConfig } = getGlobalConfig('rum-core') @@ -149,6 +150,7 @@ describe('ErrorLogging', function() { } it('should include context info on error', () => { + jasmine.addMatchers({ toEqualOrUndefined }) const transaction = transactionService.startTransaction('test', 'dummy', { managed: true }) @@ -167,21 +169,24 @@ describe('ErrorLogging', function() { error: new Error(testErrorMessage) } const errorData = errorLogging.createErrorDataModel(errorEvent) - expect(errorData.context).toEqual( - jasmine.objectContaining({ - page: { - referer: jasmine.any(String), - url: jasmine.any(String) - }, - managed: true, - dummy: { - foo: 'bar', - bar: 20 - }, - user: { id: 12, username: 'test' } - }) - ) - transaction.end() + expect(errorData.context).toEqual({ + page: jasmine.objectContaining({ + referer: jasmine.any(String), + url: jasmine.any(String) + }), + managed: true, + dummy: { + foo: 'bar', + bar: 20 + }, + user: { id: 12, username: 'test' } + }) + expect(errorData.context.page.netinfo).toEqualOrUndefined({ + downlink: jasmine.any(Number), + effective_type: jasmine.any(String), + rtt: jasmine.any(Number), + save_data: jasmine.any(Boolean) + }) }) it('should support ErrorEvent', function(done) {