@adobe/rum-distiller
Advanced tools
Comparing version 1.10.0 to 1.11.0
@@ -0,1 +1,10 @@ | ||
# [1.11.0](https://github.com/adobe/rum-distiller/compare/v1.10.0...v1.11.0) (2024-11-27) | ||
### Features | ||
* test coverage for consent.js ([87d46ca](https://github.com/adobe/rum-distiller/commit/87d46cae8997ef0b71c0826c95b77a8b230c36d1)) | ||
* test coverage for stats.js ([cb93c52](https://github.com/adobe/rum-distiller/commit/cb93c525ef1b4c191fc0b2caf28899759246aa31)) | ||
* test coverage for utils.js ([31ccdcd](https://github.com/adobe/rum-distiller/commit/31ccdcdc9873c8f30ebadefbf84fd55aa7a4cd08)) | ||
# [1.10.0](https://github.com/adobe/rum-distiller/compare/v1.9.1...v1.10.0) (2024-11-26) | ||
@@ -2,0 +11,0 @@ |
@@ -62,2 +62,3 @@ /* | ||
} | ||
export default function classifyConsent(cssSelector) { | ||
@@ -64,0 +65,0 @@ if (!cssSelector) return undefined; |
{ | ||
"name": "@adobe/rum-distiller", | ||
"version": "1.10.0", | ||
"version": "1.11.0", | ||
"scripts": { | ||
@@ -5,0 +5,0 @@ "test": "node --test --experimental-test-coverage --test-reporter=lcov --test-reporter-destination=lcov.info --test-reporter=spec --test-reporter-destination=stdout --test-reporter=junit --test-reporter-destination=junit.xml", |
@@ -55,3 +55,3 @@ /* | ||
function standardNormalCDF(x) { | ||
export function standardNormalCDF(x) { | ||
// Approximation of the standard normal CDF using the Hastings algorithm | ||
@@ -133,2 +133,3 @@ const t = 1 / (1 + 0.2316419 * Math.abs(x)); | ||
} | ||
/** | ||
@@ -196,2 +197,5 @@ * @typedef {Object} MeanVariance | ||
export function tTest(left, right) { | ||
if (left.length === 0 || right.length === 0) { | ||
throw new Error('Array must contain at least one element.'); | ||
} | ||
const { mean: meanLeft, variance: varianceLeft } = calcMeanVariance(left); | ||
@@ -205,2 +209,3 @@ const { mean: meanRight, variance: varianceRight } = calcMeanVariance(right); | ||
} | ||
export function roundToConfidenceInterval( | ||
@@ -207,0 +212,0 @@ total, |
@@ -15,5 +15,29 @@ /* | ||
import { | ||
computeConversionRate, isKnownFacet, | ||
computeConversionRate, isKnownFacet, scoreBundle, reclassifyAcquisition, addCalculatedProps, scoreCWV, | ||
} from '../utils.js'; | ||
// need to confirm if results are as expected | ||
describe('reclassifyAcquisition', () => { | ||
it('should reclassify utm source to acquisition', () => { | ||
const result = reclassifyAcquisition({ source: 'utm_source', target: 'some_target', checkpoint: 'utm' }); | ||
assert.deepStrictEqual(result, { source: 'utm_source', target:'some_target', checkpoint: 'utm' }); | ||
}); | ||
it('should reclassify paid source to acquisition', () => { | ||
const result = reclassifyAcquisition({ source: 'some_source', target: 'some_target', checkpoint: 'paid' }); | ||
assert.deepStrictEqual(result, { checkpoint: 'acquisition', source: 'paid' }); | ||
}); | ||
it('should reclassify email source to acquisition', () => { | ||
const result = reclassifyAcquisition({ source: 'some_source', target: 'some_target', checkpoint: 'email' }); | ||
assert.deepStrictEqual(result, { source: 'some_source', target: 'some_target', checkpoint: 'email' }); | ||
}); | ||
it('should return original values if no reclassification is done', () => { | ||
const result = reclassifyAcquisition({ source: 'some_source', target: 'some_target', checkpoint: 'other' }); | ||
assert.deepStrictEqual(result, { source: 'some_source', target: 'some_target', checkpoint: 'other' }); | ||
}); | ||
}); | ||
describe('computeConversionRate', () => { | ||
@@ -57,1 +81,150 @@ it('its 10% for 1 conversion and 10 visits', () => { | ||
}); | ||
describe('scoreBundle', () => { | ||
it('should return null if no CWV metrics have a value', () => { | ||
const bundle = {}; | ||
const result = scoreBundle(bundle); | ||
assert.strictEqual(result, null); | ||
}); | ||
it('should return "good" if all CWV metrics are good', () => { | ||
const bundle = { | ||
cwvLCP: 2.0, | ||
cwvCLS: 0.05, | ||
cwvINP: 150, | ||
}; | ||
const result = scoreBundle(bundle); | ||
assert.strictEqual(result, 'good'); | ||
}); | ||
it('should return "ni" if all CWV metrics are good or ni', () => { | ||
const bundle = { | ||
cwvLCP: 3.0, | ||
cwvCLS: 0.2, | ||
cwvINP: 150, | ||
}; | ||
const result = scoreBundle(bundle); | ||
assert.strictEqual(result, 'ni'); | ||
}); | ||
it('should return "poor" if any CWV metric is poor', () => { | ||
const bundle = { | ||
cwvLCP: 5.0, | ||
cwvCLS: 0.3, | ||
cwvINP: 600, | ||
}; | ||
const result = scoreBundle(bundle); | ||
assert.strictEqual(result, 'poor'); | ||
}); | ||
it('should handle mixed CWV metrics correctly', () => { | ||
const bundle = { | ||
cwvLCP: 2.0, | ||
cwvCLS: 0.3, | ||
cwvINP: 150, | ||
}; | ||
const result = scoreBundle(bundle); | ||
assert.strictEqual(result, 'poor'); | ||
}); | ||
}); | ||
describe('addCalculatedProps', () => { | ||
it('should set visit to true and source to (direct) for enter checkpoint with empty source', () => { | ||
const bundle = { | ||
events: [ | ||
{ checkpoint: 'enter', source: '' }, | ||
], | ||
}; | ||
const result = addCalculatedProps(bundle); | ||
assert.strictEqual(result.visit, true); | ||
assert.strictEqual(result.events[0].source, '(direct)'); | ||
}); | ||
it('should set visit to true and keep source unchanged for enter checkpoint with non-empty source', () => { | ||
const bundle = { | ||
events: [ | ||
{ checkpoint: 'enter', source: 'some_source' }, | ||
], | ||
}; | ||
const result = addCalculatedProps(bundle); | ||
assert.strictEqual(result.visit, true); | ||
assert.strictEqual(result.events[0].source, 'some_source'); | ||
}); | ||
it('should not set visit or change source for non-enter checkpoint', () => { | ||
const bundle = { | ||
events: [ | ||
{ checkpoint: 'other', source: '' }, | ||
], | ||
}; | ||
const result = addCalculatedProps(bundle); | ||
assert.strictEqual(result.visit, undefined); | ||
assert.strictEqual(result.events[0].source, ''); | ||
}); | ||
}); | ||
describe('scoreCWV', () => { | ||
it('should return "good" if lcp is good', () => { | ||
const lcp = 2000; | ||
const result = scoreCWV(lcp, 'lcp'); | ||
assert.strictEqual(result, 'good'); | ||
}); | ||
it('should return "poor" if lcp is poor', () => { | ||
const lcp = 5000; | ||
const result = scoreCWV(lcp, 'lcp'); | ||
assert.strictEqual(result, 'poor'); | ||
}); | ||
it('should return "good" if cls is good', () => { | ||
const cls = 0.05; | ||
const result = scoreCWV(cls, 'cls'); | ||
assert.strictEqual(result, 'good'); | ||
}); | ||
it('should return "ni" if cls is ni', () => { | ||
const cls = 0.15; | ||
const result = scoreCWV(cls, 'cls'); | ||
assert.strictEqual(result, 'ni'); | ||
}); | ||
it('should return "ni" if inp is ni', () => { | ||
const inp = 300; | ||
const result = scoreCWV(inp, 'inp'); | ||
assert.strictEqual(result, 'ni'); | ||
}); | ||
it('should return "poor" if inp is poor', () => { | ||
const inp = 600; | ||
const result = scoreCWV(inp, 'inp'); | ||
assert.strictEqual(result, 'poor'); | ||
}); | ||
it('should return "good" if ttfb is good', () => { | ||
const ttfb = 700; | ||
const result = scoreCWV(ttfb, 'ttfb'); | ||
assert.strictEqual(result, 'good'); | ||
}); | ||
it('should return "ni" if ttfb is ni', () => { | ||
const ttfb = 1000; | ||
const result = scoreCWV(ttfb, 'ttfb'); | ||
assert.strictEqual(result, 'ni'); | ||
}); | ||
it('should return "poor" if ttfb is poor', () => { | ||
const ttfb = 2000; | ||
const result = scoreCWV(ttfb, 'ttfb'); | ||
assert.strictEqual(result, 'poor'); | ||
}); | ||
it('should return null if value is undefined', () => { | ||
const result = scoreCWV(undefined, 'ttfb'); | ||
assert.strictEqual(result, null); | ||
}); | ||
it('should return null if value is null', () => { | ||
const result = scoreCWV(null, 'ttfb'); | ||
assert.strictEqual(result, null); | ||
}); | ||
}); |
38
utils.js
@@ -147,15 +147,2 @@ /* | ||
export function toISOStringWithTimezone(date) { | ||
// Pad a number to 2 digits | ||
const pad = (n) => `${Math.floor(Math.abs(n))}`.padStart(2, '0'); | ||
// Get timezone offset in ISO format (+hh:mm or -hh:mm) | ||
const getTimezoneOffset = () => { | ||
const tzOffset = -date.getTimezoneOffset(); | ||
const diff = tzOffset >= 0 ? '+' : '-'; | ||
return `${diff}${pad(tzOffset / 60)}:${pad(tzOffset % 60)}`; | ||
}; | ||
return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}T${pad(date.getHours())}:${pad(date.getMinutes())}:${pad(date.getSeconds())}${getTimezoneOffset()}`; | ||
} | ||
export function scoreBundle(bundle) { | ||
@@ -222,27 +209,2 @@ // a bundle is good if all CWV that have a value are good | ||
export function reclassifyEnter(acc, event, i, allEvents) { | ||
const has = (cp) => allEvents.find((evt) => evt.checkpoint === cp); | ||
if (event.checkpoint === 'enter') acc.referrer = event.source; | ||
if (event.checkpoint === 'acquisition') acc.acquisition = event.source; | ||
if ( | ||
// we need to reclassify when we have seen both enter and acquisition | ||
(event.checkpoint === 'enter' || event.checkpoint === 'acquisition') | ||
// but if there is no acquisition, we reclassify the enter event | ||
&& ((acc.acquisition && acc.referrer) || (!has('acquisition')))) { | ||
const [aGroup, aCategory, aVendor] = (acc.acquisition || '').split(':'); | ||
const [, rCategory, rVendor] = (classifyAcquisition(acc.referrer) || '').split(':'); | ||
const group = aGroup || 'earned'; | ||
const category = rCategory || aCategory; | ||
const vndr = rVendor || aVendor; | ||
const newsrc = `${group}:${category}:${vndr}`.replace(/:undefined/g, ''); | ||
// console.log('reclassifyEnter', acc.referrer, acc.acquisition, newsrc); | ||
acc.push({ checkpoint: 'acquisition', source: newsrc }); | ||
} | ||
if (event.checkpoint !== 'acquisition') { | ||
acc.push(event); | ||
} | ||
return acc; | ||
} | ||
/** | ||
@@ -249,0 +211,0 @@ * Calculates properties on the bundle, so that bundle-level filtering can be performed |
2898944
30
90818