Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .agents/knowledge/entry-points.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,8 @@
- `packages/analytics-js/src/components/core/Analytics.ts` — v3 lifecycle engine, service composition, and initialization sequence (`packages/analytics-js/src/components/core/Analytics.ts::Analytics`).
- `packages/analytics-v1.1/src/core/analytics.js` — legacy line core implementation (class-centric runtime still shipped as `rudder-sdk-js`) (`packages/analytics-v1.1/src/core/analytics.js::Analytics`).
- `packages/loading-scripts/src/index.ts` — snippet loader that buffers calls and chooses modern vs legacy runtime script dynamically (`packages/loading-scripts/src/index.ts:1`).

## SDK-5014 — Amplitude V2 Autocapture Entry Points

- Amplitude device-mode integration work lives under `packages/analytics-js-integrations/src/integrations/Amplitude/`; for v2 autocapture changes, start with `browser.js` for initialization, `utils.js` for helper getters, and `constants.js` for integration constants.
- Existing Jest coverage for Amplitude Browser SDK v2 initialization payload shape is in `packages/analytics-js-integrations/__tests__/integrations/Amplitude/browser.test.js`; helper behavior is covered in `packages/analytics-js-integrations/__tests__/integrations/Amplitude/util.test.js`.
5 changes: 5 additions & 0 deletions .agents/knowledge/patterns.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,8 @@
- Plugin calls prefer extension-point invocation (`invokeSingle`/`invokeMultiple`) to keep queue and destination behaviors swappable (`packages/analytics-js/src/services/PluginEngine/PluginEngine.ts::PluginEngine.invokeSingle`).
- Load-time buffering is a consistent behavior: preload snippet arrays and in-memory queues are drained after initialization to preserve call order (`packages/loading-scripts/src/index.ts:12`, `packages/analytics-js/src/components/core/Analytics.ts::Analytics.processDataInPreloadBuffer`).
- Legacy v1.1 still follows class-centric mutable state and explicit polling for integration readiness (`allModulesInitialized` Promise loop), unlike v3 reactive effects (`packages/analytics-v1.1/src/core/analytics.js::Analytics.allModulesInitialized`).

## SDK-5014 — Amplitude V2 Autocapture Config Boundaries

- Amplitude Browser SDK v2 autocapture config is assembled in `packages/analytics-js-integrations/src/integrations/Amplitude/browser.js:init()` using helper getters from `packages/analytics-js-integrations/src/integrations/Amplitude/utils.js`.
- The dedicated `pageViews` config key maps only to the Amplitude SDK `autocapture.pageViews` option; it intentionally does not affect Rudder `page()` translation gates such as `trackAllPages`, `trackCategorizedPages`, or `trackNamedPages`.
33 changes: 25 additions & 8 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,19 @@
destinationId: 'sample-destination-id',
};

const defaultV2Autocapture = {
attribution: true,
pageViews: false,
sessions: false,
formInteractions: false,
fileDownloads: false,
elementInteractions: false,
frustrationInteractions: false,
networkTracking: false,
webVitals: false,
pageUrlEnrichment: false,
};

describe('Amplitude', () => {
beforeAll(() => {
// Add a dummy script as it is required by the init script
Expand Down Expand Up @@ -277,18 +290,7 @@
expect(window.amplitude._q[0].name).toBe('init');
expect(window.amplitude._q[0].args[1]).toBeUndefined();
expect(window.amplitude._q[0].args[2]).toMatchObject({
autocapture: {
attribution: true,
pageViews: false,
sessions: false,
formInteractions: false,
fileDownloads: false,
elementInteractions: false,
frustrationInteractions: false,
networkTracking: false,
webVitals: false,
pageUrlEnrichment: false,
},
autocapture: defaultV2Autocapture,
serverUrl: 'https://some.proxyserverurl.com',
});
expect(window.amplitude._q[0].args[2].attribution).toBeUndefined();
Expand All @@ -309,22 +311,71 @@
expect(document.querySelector(`script[src="${AMPLITUDE_V2_SDK_URL}"]`)).toBeTruthy();
expect(window.amplitude._q[0].args[2]).toMatchObject({
autocapture: {
...defaultV2Autocapture,
attribution: false,
pageViews: false,
sessions: false,
formInteractions: false,
fileDownloads: false,
elementInteractions: false,
frustrationInteractions: false,
networkTracking: false,
webVitals: false,
pageUrlEnrichment: false,
},
serverUrl: 'https://some.proxyserverurl.com',
});
expect(window.amplitude._q[0].args[2].attribution).toBeUndefined();
});

it.each([
['pageViews', 'pageViews', true],
['pageUrlEnrichment', 'pageUrlEnrichment', true],
['trackSessionEvents', 'sessions', true],
['webVitals', 'webVitals', true],
['fileDownloads', 'fileDownloads', true],
['frustrationInteractions', 'frustrationInteractions', true],
['networkTracking', 'networkTracking', true],
['elementInteractions', 'elementInteractions', true],
['formInteractions', 'formInteractions', true],
])(
'should map %s config to only autocapture.%s for v2',
(configKey, autocaptureKey, configValue) => {
const amplitude = new Amplitude(
{
...destinationConfig,
sdkVersion: 2,
attribution: false,
[configKey]: configValue,
},
analyticsInstance,
destinationInfo,
);
amplitude.init();

expect(window.amplitude._q[0].args[2].autocapture).toStrictEqual({
...defaultV2Autocapture,
[autocaptureKey]: true,
});
},
);

it('should keep auto-capture page views independent from user-instrumented page toggles', () => {
const amplitude = new Amplitude(
{
...destinationConfig,
sdkVersion: 2,
attribution: false,
pageViews: true,
trackAllPages: false,
trackCategorizedPages: false,
trackNamedPages: false,
},
analyticsInstance,
destinationInfo,
);
amplitude.init();

expect(window.amplitude._q[0].args[2].autocapture).toStrictEqual({
...defaultV2Autocapture,
pageViews: true,
});
expect(amplitude.trackAllPages).toBe(false);
expect(amplitude.trackCategorizedPages).toBe(false);
expect(amplitude.trackNamedPages).toBe(false);
});

it('should set the correct SRI integrity on the injected v1 script', () => {
const amplitude = new Amplitude(
{ ...destinationConfig, sdkVersion: 1 },
Expand Down Expand Up @@ -352,7 +403,7 @@

describe('identify', () => {
// Identify user with userId and traits
it('should identify user with userId and traits', () => {

Check failure on line 406 in packages/analytics-js-integrations/__tests__/integrations/Amplitude/browser.test.js

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Add at least one assertion to this test case.

See more on https://sonarcloud.io/project/issues?id=rudderlabs_repo_rudder-sdk-js&issues=AZ8c6WKFas0eDFONNlEU&open=AZ8c6WKFas0eDFONNlEU&pullRequest=3091
const config = {
apiKey: 'AMPLITUDE_API_KEY',
trackAllPages: true,
Expand Down Expand Up @@ -406,7 +457,7 @@
});

// Identify user with only userId
it('should identify user with only userId', () => {

Check failure on line 460 in packages/analytics-js-integrations/__tests__/integrations/Amplitude/browser.test.js

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Add at least one assertion to this test case.

See more on https://sonarcloud.io/project/issues?id=rudderlabs_repo_rudder-sdk-js&issues=AZ8c6WKFas0eDFONNlEV&open=AZ8c6WKFas0eDFONNlEV&pullRequest=3091
const config = {
apiKey: 'YOUR_AMPLITUDE_API_KEY',
trackAllPages: true,
Expand Down Expand Up @@ -455,7 +506,7 @@
// Add assertions here
});
// Identify user with only traits
it('should identify user with only traits', () => {

Check failure on line 509 in packages/analytics-js-integrations/__tests__/integrations/Amplitude/browser.test.js

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Add at least one assertion to this test case.

See more on https://sonarcloud.io/project/issues?id=rudderlabs_repo_rudder-sdk-js&issues=AZ8c6WKFas0eDFONNlEW&open=AZ8c6WKFas0eDFONNlEW&pullRequest=3091
const config = {
apiKey: 'YOUR_AMPLITUDE_API_KEY',
trackAllPages: true,
Expand Down Expand Up @@ -1075,6 +1126,39 @@

expect(spy).toHaveBeenCalledWith('Viewed page NAME', {});
});

it('should not translate page calls when only v2 auto-capture page views is enabled', () => {
const config = {
apiKey: 'YOUR_AMPLITUDE_API_KEY',
sdkVersion: 2,
pageViews: true,
trackAllPages: false,
trackCategorizedPages: false,
trackNamedPages: false,
};

const analytics = {
logLevel: 'debug',
getAnonymousId: () => 'ANONYMOUS_ID',
};

const rudderElement = {
message: {
properties: {},
name: 'NAME',
category: 'CATEGORY',
integrations: {},
},
};

const amplitude = new Amplitude(config, analytics, destinationInfo);
window.amplitude = window.amplitude ?? { track: jest.fn() };
const spy = jest.spyOn(window.amplitude, 'track');

amplitude.page(rudderElement);

expect(spy).not.toHaveBeenCalled();
});
});

describe('group', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,15 @@
getDestinationOptions,
formatUrl,
getAmplitudeSdkVersion,
getAutoCapturePageViews,
getPageUrlEnrichment,
getTrackSessionEvents,
getWebVitals,
getFileDownloads,
getFrustrationInteractions,
getNetworkTracking,
getElementInteractions,
getFormInteractions,
} from '../../../src/integrations/Amplitude/utils';

describe('getTraitsToSetOnce', () => {
Expand Down Expand Up @@ -133,7 +142,7 @@

const result = getDestinationOptions(integrationsOptions);

expect(result).toEqual(undefined);

Check warning on line 145 in packages/analytics-js-integrations/__tests__/integrations/Amplitude/util.test.js

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Prefer "expect(result).toBeUndefined()" over this generic assertion; dedicated matchers read better and report clearer failures.

See more on https://sonarcloud.io/project/issues?id=rudderlabs_repo_rudder-sdk-js&issues=AZ8c6WK6as0eDFONNlEX&open=AZ8c6WK6as0eDFONNlEX&pullRequest=3091
});

// Returns null if integrations options object does not have options for the given destination
Expand Down Expand Up @@ -210,3 +219,35 @@
expect(getAmplitudeSdkVersion({ sdkVersion: '2' })).toBe(2);
});
});

describe('Amplitude v2 autocapture config helpers', () => {
const helperCases = [
{
name: 'getAutoCapturePageViews',
getter: getAutoCapturePageViews,
key: 'pageViews',
},
{ name: 'getPageUrlEnrichment', getter: getPageUrlEnrichment, key: 'pageUrlEnrichment' },
{ name: 'getWebVitals', getter: getWebVitals, key: 'webVitals' },
{ name: 'getFileDownloads', getter: getFileDownloads, key: 'fileDownloads' },
{
name: 'getFrustrationInteractions',
getter: getFrustrationInteractions,
key: 'frustrationInteractions',
},
{ name: 'getNetworkTracking', getter: getNetworkTracking, key: 'networkTracking' },
{ name: 'getElementInteractions', getter: getElementInteractions, key: 'elementInteractions' },
{ name: 'getFormInteractions', getter: getFormInteractions, key: 'formInteractions' },
{ name: 'getTrackSessionEvents', getter: getTrackSessionEvents, key: 'trackSessionEvents' },
];

it.each(helperCases)('$name should default missing config to false', ({ getter }) => {
expect(getter({})).toBe(false);
});

it.each(helperCases)('$name should read true and false config values', ({ getter, key }) => {
expect(getter({ [key]: true })).toBe(true);
expect(getter({ [key]: false })).toBe(false);
});

});
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,15 @@
getFieldsToUnset,
formatUrl,
getAmplitudeSdkVersion,
getAutoCapturePageViews,
getPageUrlEnrichment,
getTrackSessionEvents,
getWebVitals,
getFileDownloads,
getFrustrationInteractions,
getNetworkTracking,
getElementInteractions,
getFormInteractions,
} from './utils';
import { getValueOrDefault } from '../../utils/utils';

Expand Down Expand Up @@ -44,6 +53,15 @@
this.groupTypeTrait = config.groupTypeTrait;
this.groupValueTrait = config.groupValueTrait;
this.sdkVersion = getAmplitudeSdkVersion(config);
this.autoCapturePageViews = getAutoCapturePageViews(config);
this.pageUrlEnrichment = getPageUrlEnrichment(config);
this.trackSessionEvents = getTrackSessionEvents(config);
this.webVitals = getWebVitals(config);
this.fileDownloads = getFileDownloads(config);
this.frustrationInteractions = getFrustrationInteractions(config);
this.networkTracking = getNetworkTracking(config);
this.elementInteractions = getElementInteractions(config);
this.formInteractions = getFormInteractions(config);
({
shouldApplyDeviceModeTransformation: this.shouldApplyDeviceModeTransformation,
propagateEventsUntransformedOnError: this.propagateEventsUntransformedOnError,
Expand All @@ -69,18 +87,17 @@
this.sdkVersion === AMPLITUDE_SDK_V2
? {
...commonInitOptions,
// Enable only attribution in v2; all other autocapture toggles stay off.
autocapture: {
attribution: !this.attribution,
pageViews: false,
sessions: false,
formInteractions: false,
fileDownloads: false,
elementInteractions: false,
frustrationInteractions: false,
networkTracking: false,
webVitals: false,
pageUrlEnrichment: false,
pageViews: this.autoCapturePageViews,
sessions: this.trackSessionEvents,
formInteractions: this.formInteractions,
fileDownloads: this.fileDownloads,
elementInteractions: this.elementInteractions,
frustrationInteractions: this.frustrationInteractions,
networkTracking: this.networkTracking,
webVitals: this.webVitals,
pageUrlEnrichment: this.pageUrlEnrichment,
},
}
: {
Expand All @@ -103,8 +120,8 @@
}

if (
navigator.userAgent.indexOf('MSIE') !== -1 ||

Check warning on line 123 in packages/analytics-js-integrations/src/integrations/Amplitude/browser.js

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Use `.includes()`, rather than `.indexOf()`, when checking for existence.

See more on https://sonarcloud.io/project/issues?id=rudderlabs_repo_rudder-sdk-js&issues=AZ8c6WM3as0eDFONNlEa&open=AZ8c6WM3as0eDFONNlEa&pullRequest=3091
navigator.appVersion.indexOf('Trident/') > -1

Check warning on line 124 in packages/analytics-js-integrations/src/integrations/Amplitude/browser.js

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Use `.includes()`, rather than `.indexOf()`, when checking for existence.

See more on https://sonarcloud.io/project/issues?id=rudderlabs_repo_rudder-sdk-js&issues=AZ8c6WM3as0eDFONNlEb&open=AZ8c6WM3as0eDFONNlEb&pullRequest=3091
) {
initOptions.transport = 'xhr';
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,15 @@

const DIR_NAME = 'Amplitude';
const AMPLITUDE_SDK_VERSION_CONFIG_KEY = 'sdkVersion';
const AMPLITUDE_AUTO_CAPTURE_PAGE_VIEWS_CONFIG_KEY = 'pageViews';
const AMPLITUDE_PAGE_URL_ENRICHMENT_CONFIG_KEY = 'pageUrlEnrichment';
const AMPLITUDE_TRACK_SESSION_EVENTS_CONFIG_KEY = 'trackSessionEvents';
const AMPLITUDE_WEB_VITALS_CONFIG_KEY = 'webVitals';
const AMPLITUDE_FILE_DOWNLOADS_CONFIG_KEY = 'fileDownloads';
const AMPLITUDE_FRUSTRATION_INTERACTIONS_CONFIG_KEY = 'frustrationInteractions';
const AMPLITUDE_NETWORK_TRACKING_CONFIG_KEY = 'networkTracking';
const AMPLITUDE_ELEMENT_INTERACTIONS_CONFIG_KEY = 'elementInteractions';
const AMPLITUDE_FORM_INTERACTIONS_CONFIG_KEY = 'formInteractions';
const AMPLITUDE_SDK_V1 = 1;
const AMPLITUDE_SDK_V2 = 2;

Expand All @@ -15,9 +24,18 @@
export {
NAME,
CNameMapping,
DISPLAY_NAME,

Check warning on line 27 in packages/analytics-js-integrations/src/integrations/Amplitude/constants.js

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Use `export…from` to re-export `DISPLAY_NAME`.

See more on https://sonarcloud.io/project/issues?id=rudderlabs_repo_rudder-sdk-js&issues=AZ8c6WNNas0eDFONNlEs&open=AZ8c6WNNas0eDFONNlEs&pullRequest=3091
DIR_NAME,
AMPLITUDE_SDK_VERSION_CONFIG_KEY,
AMPLITUDE_AUTO_CAPTURE_PAGE_VIEWS_CONFIG_KEY,
AMPLITUDE_PAGE_URL_ENRICHMENT_CONFIG_KEY,
AMPLITUDE_TRACK_SESSION_EVENTS_CONFIG_KEY,
AMPLITUDE_WEB_VITALS_CONFIG_KEY,
AMPLITUDE_FILE_DOWNLOADS_CONFIG_KEY,
AMPLITUDE_FRUSTRATION_INTERACTIONS_CONFIG_KEY,
AMPLITUDE_NETWORK_TRACKING_CONFIG_KEY,
AMPLITUDE_ELEMENT_INTERACTIONS_CONFIG_KEY,
AMPLITUDE_FORM_INTERACTIONS_CONFIG_KEY,
AMPLITUDE_SDK_V1,
AMPLITUDE_SDK_V2,
};
Loading
Loading