From 20971e22e071705bb101864c6337e7ead6504f40 Mon Sep 17 00:00:00 2001 From: Yara Matkova Date: Mon, 5 Aug 2024 16:52:03 +0200 Subject: [PATCH 01/18] refac: rename third-party-sharing module --- ...js => disable-third-party-sharing.spec.js} | 4 +- ...ring.ts => disable-third-party-sharing.ts} | 48 +++++++++++-------- src/sdk/main.js | 15 ++++-- 3 files changed, 41 insertions(+), 26 deletions(-) rename src/sdk/__tests__/{third-party-sharing.spec.js => disable-third-party-sharing.spec.js} (97%) rename src/sdk/{third-party-sharing.ts => disable-third-party-sharing.ts} (75%) diff --git a/src/sdk/__tests__/third-party-sharing.spec.js b/src/sdk/__tests__/disable-third-party-sharing.spec.js similarity index 97% rename from src/sdk/__tests__/third-party-sharing.spec.js rename to src/sdk/__tests__/disable-third-party-sharing.spec.js index 97abba3..f729e15 100644 --- a/src/sdk/__tests__/third-party-sharing.spec.js +++ b/src/sdk/__tests__/disable-third-party-sharing.spec.js @@ -1,4 +1,4 @@ -import * as ThirdPartySharing from '../third-party-sharing' +import * as ThirdPartySharing from '../disable-third-party-sharing' import * as Config from '../config' import * as Queue from '../queue' import * as http from '../http' @@ -99,7 +99,7 @@ describe('Third-party sharing opt-out functionality', () => { expectNotRequest() Config.default.set(appOptions) - ThirdPartySharing.check() + ThirdPartySharing.runPendingOptOut() expect(Logger.default.log).toHaveBeenLastCalledWith('Adjust SDK is running pending third-party sharing opt-out request') diff --git a/src/sdk/third-party-sharing.ts b/src/sdk/disable-third-party-sharing.ts similarity index 75% rename from src/sdk/third-party-sharing.ts rename to src/sdk/disable-third-party-sharing.ts index 30180aa..139e4fa 100644 --- a/src/sdk/third-party-sharing.ts +++ b/src/sdk/disable-third-party-sharing.ts @@ -40,7 +40,7 @@ function _status(): ThirdPartySharingStatusT { /** * Request third-party sharing opt-out request */ -function optOut(force?: boolean) { +function optOut(force?: boolean): boolean { const status = _status() if (!force && status !== 'on') { @@ -62,47 +62,53 @@ function optOut(force?: boolean) { } /** - * Start or finish thrid-party sharing disable process + * Start the third-party sharing disable process */ -function _disable(pending: boolean, expectedAction: 'start' | 'finish'): boolean { - const { reason: savedReason, pending: savedPending } = getThirdPartySharing() || {} - const action = expectedAction === 'start' && pending ? 'start' : 'finish' - const shouldNotStart = expectedAction === 'start' && savedReason - const shouldNotFinish = expectedAction === 'finish' && savedReason && !savedPending +function disable(): boolean { + const { reason: savedReason } = getThirdPartySharing() || {} + const alreadyStarted = !!savedReason - if (shouldNotStart || shouldNotFinish) { - Logger.log(_logMessages[action].inProgress) + if (alreadyStarted) { + Logger.log(_logMessages['start'].inProgress) return false } - Logger.log(_logMessages[action].done) + Logger.log(_logMessages['start'].done) setThirdPartySharing({ reason: DISABLE_REASONS.REASON_GENERAL, - pending: pending + pending: true }) return true } -/** - * Start the third-party sharing disable process - */ -function disable(): boolean { - return _disable(true, 'start') -} - /** * Finalize the third-party sharing process */ function finish() { - return _disable(false, 'finish') + const { reason: savedReason, pending: savedPending } = getThirdPartySharing() || {} + const shouldNotFinish = savedReason && !savedPending + + if (shouldNotFinish) { + Logger.log(_logMessages['finish'].inProgress) + return false + } + + Logger.log(_logMessages['finish'].done) + + setThirdPartySharing({ + reason: DISABLE_REASONS.REASON_GENERAL, + pending: false + }) + + return true } /** * Check if there s pending third-party sharing opt-out request */ -function check(): void { +function runPendingOptOut(): void { if (_status() === 'pending') { Logger.log(_logMessages.running) optOut(true) @@ -113,5 +119,5 @@ export { optOut, disable, finish, - check + runPendingOptOut } diff --git a/src/sdk/main.js b/src/sdk/main.js index 233eea8..ee71906 100644 --- a/src/sdk/main.js +++ b/src/sdk/main.js @@ -19,7 +19,8 @@ import { add, remove, removeAll, clear as globalParamsClear } from './global-par import { check as attributionCheck, destroy as attributionDestroy } from './attribution' import { disable, restore, status } from './disable' import { check as gdprForgetCheck, forget, disable as gdprDisable, finish as gdprDisableFinish, destroy as gdprForgetDestroy } from './gdpr-forget-device' -import { check as sharingDisableCheck, optOut as sharingOptOut, disable as sharingDisable, finish as sharingDisableFinish } from './third-party-sharing' +import { runPendingOptOut, optOut as sharingOptOut, disable as sharingDisable, finish as sharingDisableFinish } from './disable-third-party-sharing' +import { trackThirdPartySharing as trackTPS } from './track-third-party-sharing' import { register as listenersRegister, destroy as listenersDestroy } from './listeners' import { delay, flush, destroy as schedulerDestroy } from './scheduler' import event from './event' @@ -263,11 +264,18 @@ function gdprForgetMe(): void { * Disable third party sharing */ function disableThirdPartySharing(): void { + //trackThirdPartySharing({isEnabled: false}) _preCheck('disable third-party sharing', _handleDisableThirdPartySharing, { schedule: true }) } +function trackThirdPartySharing(adjustThirdPartySharing: ThirdPartySharingOptions): void { + _preCheck('third-party sharing', () => trackTPS(adjustThirdPartySharing), { + schedule: true + }) +} + /** @deprecated */ function initSmartBanner (): void { Logger.error('function `initSmartBanner` is deprecated'); @@ -394,7 +402,7 @@ function _continue(activityState: ActivityStateMapT): Promise { gdprForgetCheck() if (!isInstalled) { - sharingDisableCheck() + runPendingOptOut() } const sdkStatus = status() @@ -423,7 +431,7 @@ function _continue(activityState: ActivityStateMapT): Promise { if (isInstalled) { _handleSdkInstalled() - sharingDisableCheck() + runPendingOptOut() } }) } @@ -603,6 +611,7 @@ const Adjust = { restart, gdprForgetMe, disableThirdPartySharing, + trackThirdPartySharing, initSmartBanner, showSmartBanner, hideSmartBanner, From d9cb828baa228faa694f7bbf9a3868848bff16e0 Mon Sep 17 00:00:00 2001 From: Yara Matkova Date: Thu, 8 Aug 2024 12:36:20 +0200 Subject: [PATCH 02/18] refac: replace deprecated disable- with track-third-party-sharing functionality --- src/sdk/disable-third-party-sharing.ts | 123 ------------------------- src/sdk/http.js | 6 -- src/sdk/main.js | 35 ++----- src/sdk/preferences.ts | 34 ------- src/sdk/track-third-party-sharing.ts | 82 +++++++++++++++++ 5 files changed, 90 insertions(+), 190 deletions(-) delete mode 100644 src/sdk/disable-third-party-sharing.ts create mode 100644 src/sdk/track-third-party-sharing.ts diff --git a/src/sdk/disable-third-party-sharing.ts b/src/sdk/disable-third-party-sharing.ts deleted file mode 100644 index 139e4fa..0000000 --- a/src/sdk/disable-third-party-sharing.ts +++ /dev/null @@ -1,123 +0,0 @@ -import { push } from './queue' -import { getThirdPartySharing, setThirdPartySharing } from './preferences' -import Config from './config' -import Logger from './logger' -import { DISABLE_REASONS } from './constants' - -type ThirdPartySharingStatusT = 'pending' | 'on' | 'off' - -/** - * Log messages used in different scenarios - */ -const _logMessages = { - running: 'Adjust SDK is running pending third-party sharing opt-out request', - delayed: 'Adjust SDK will run third-party sharing opt-out request after initialisation', - pending: 'Adjust SDK already queued third-party sharing opt-out request', - off: 'Third-party sharing opt-out is already done', - start: { - inProgress: 'Third-party sharing opt-out has already started', - done: 'Third-party sharing opt-out is now started' - }, - finish: { - inProgress: 'Third-party sharing opt-out has already finished', - done: 'Third-party sharing opt-out is now finished' - } -} - -/** - * Get the status of the third-party sharing - */ -function _status(): ThirdPartySharingStatusT { - const { reason, pending } = getThirdPartySharing() || {} - - if (reason) { - return pending ? 'pending' : 'off' - } - - return 'on' -} - -/** - * Request third-party sharing opt-out request - */ -function optOut(force?: boolean): boolean { - const status = _status() - - if (!force && status !== 'on') { - Logger.log(_logMessages[status]) - return false - } - - if (!Config.isInitialised()) { - Logger.log(_logMessages.delayed) - return true - } - - push({ - url: '/disable_third_party_sharing', - method: 'POST' - }) - - return true -} - -/** - * Start the third-party sharing disable process - */ -function disable(): boolean { - const { reason: savedReason } = getThirdPartySharing() || {} - const alreadyStarted = !!savedReason - - if (alreadyStarted) { - Logger.log(_logMessages['start'].inProgress) - return false - } - - Logger.log(_logMessages['start'].done) - - setThirdPartySharing({ - reason: DISABLE_REASONS.REASON_GENERAL, - pending: true - }) - - return true -} - -/** - * Finalize the third-party sharing process - */ -function finish() { - const { reason: savedReason, pending: savedPending } = getThirdPartySharing() || {} - const shouldNotFinish = savedReason && !savedPending - - if (shouldNotFinish) { - Logger.log(_logMessages['finish'].inProgress) - return false - } - - Logger.log(_logMessages['finish'].done) - - setThirdPartySharing({ - reason: DISABLE_REASONS.REASON_GENERAL, - pending: false - }) - - return true -} - -/** - * Check if there s pending third-party sharing opt-out request - */ -function runPendingOptOut(): void { - if (_status() === 'pending') { - Logger.log(_logMessages.running) - optOut(true) - } -} - -export { - optOut, - disable, - finish, - runPendingOptOut -} diff --git a/src/sdk/http.js b/src/sdk/http.js index 7f32e5d..90071ae 100644 --- a/src/sdk/http.js +++ b/src/sdk/http.js @@ -263,7 +263,6 @@ function _interceptSuccess (result: HttpSuccessResponseT, url): HttpSuccessRespo const isGdprRequest = isRequest(url, 'gdpr_forget_device') const isAttributionRequest = isRequest(url, 'attribution') const isSessionRequest = isRequest(url, 'session') - const isThirdPartySharingOptOutRequest = isRequest(url, 'disable_third_party_sharing') const optedOut = result.tracking_state === 'opted_out' if (!isGdprRequest && optedOut) { @@ -279,11 +278,6 @@ function _interceptSuccess (result: HttpSuccessResponseT, url): HttpSuccessRespo publish('session:finished', result) } - if (isThirdPartySharingOptOutRequest) { - publish('sdk:third-party-sharing-opt-out') - return result - } - return result } diff --git a/src/sdk/main.js b/src/sdk/main.js index ee71906..18aed5d 100644 --- a/src/sdk/main.js +++ b/src/sdk/main.js @@ -19,7 +19,6 @@ import { add, remove, removeAll, clear as globalParamsClear } from './global-par import { check as attributionCheck, destroy as attributionDestroy } from './attribution' import { disable, restore, status } from './disable' import { check as gdprForgetCheck, forget, disable as gdprDisable, finish as gdprDisableFinish, destroy as gdprForgetDestroy } from './gdpr-forget-device' -import { runPendingOptOut, optOut as sharingOptOut, disable as sharingDisable, finish as sharingDisableFinish } from './disable-third-party-sharing' import { trackThirdPartySharing as trackTPS } from './track-third-party-sharing' import { register as listenersRegister, destroy as listenersDestroy } from './listeners' import { delay, flush, destroy as schedulerDestroy } from './scheduler' @@ -262,17 +261,20 @@ function gdprForgetMe(): void { /** * Disable third party sharing + * + * @deprecated Use {@link trackThirdPartySharing} instead */ function disableThirdPartySharing(): void { - //trackThirdPartySharing({isEnabled: false}) - _preCheck('disable third-party sharing', _handleDisableThirdPartySharing, { - schedule: true - }) + trackThirdPartySharing({isEnabled: false}) } +/** + * Disable third party sharing + */ function trackThirdPartySharing(adjustThirdPartySharing: ThirdPartySharingOptions): void { _preCheck('third-party sharing', () => trackTPS(adjustThirdPartySharing), { - schedule: true + schedule: true, + optionalInit: true }) } @@ -291,21 +293,6 @@ function hideSmartBanner (): void { Logger.error('function `hideSmartBanner` is deprecated'); } -/** - * Handle third party sharing disable - * - * @private - */ -function _handleDisableThirdPartySharing(): void { - let done = sharingOptOut() - - if (!done) { - return - } - - sharingDisable() -} - /** * Handle GDPR-Forget-Me response * @@ -401,10 +388,6 @@ function _continue(activityState: ActivityStateMapT): Promise { gdprForgetCheck() - if (!isInstalled) { - runPendingOptOut() - } - const sdkStatus = status() let message = (rest) => `Adjust SDK start has been interrupted ${rest}` @@ -431,7 +414,6 @@ function _continue(activityState: ActivityStateMapT): Promise { if (isInstalled) { _handleSdkInstalled() - runPendingOptOut() } }) } @@ -501,7 +483,6 @@ function _start(options: InitOptionsT): void { subscribe('sdk:installed', _handleSdkInstalled) subscribe('sdk:shutdown', () => _shutdown(true)) subscribe('sdk:gdpr-forget-me', _handleGdprForgetMe) - subscribe('sdk:third-party-sharing-opt-out', sharingDisableFinish) subscribe('attribution:check', (e, result) => attributionCheck(result)) if (typeof options.attributionCallback === 'function') { diff --git a/src/sdk/preferences.ts b/src/sdk/preferences.ts index 00d200c..54b1f61 100644 --- a/src/sdk/preferences.ts +++ b/src/sdk/preferences.ts @@ -8,13 +8,7 @@ type SdkDisabledT = { pending: boolean } -type ThirdPartySharingDisabledT = { - reason: DISABLE_REASONS.REASON_GENERAL, - pending: boolean -} - type PreferencesT = { - thirdPartySharingDisabled?: ThirdPartySharingDisabledT, sdkDisabled?: SdkDisabledT } @@ -76,32 +70,6 @@ function setDisabled(value: SdkDisabledT | null): void { _setPreferences() } -/** - * Get current third-party-sharing disabled state - * - * @returns {Object} - * @private - */ -function getThirdPartySharing(): ThirdPartySharingDisabledT | null { - const preferences = _getPreferences() - - return preferences && preferences.thirdPartySharingDisabled || null -} - -/** - * Set current third-party-sharing disabled state - * - * @param {Object=} value - * @private - */ -function setThirdPartySharing(value: ThirdPartySharingDisabledT): void { - const thirdPartySharingDisabled = value ? { ...value } : null - - QuickStorage.stores[_storeName] = { ..._getPreferences(), thirdPartySharingDisabled } - - _setPreferences() -} - /** * Reload current preferences from localStorage if changed outside of current scope (e.g. tab) */ @@ -130,8 +98,6 @@ function recover(): void { export { getDisabled, setDisabled, - getThirdPartySharing, - setThirdPartySharing, reload, recover } diff --git a/src/sdk/track-third-party-sharing.ts b/src/sdk/track-third-party-sharing.ts new file mode 100644 index 0000000..5f1e9d5 --- /dev/null +++ b/src/sdk/track-third-party-sharing.ts @@ -0,0 +1,82 @@ +import Logger from './logger' +import { push } from './queue' + +export interface ThirdPartySharingOptions { + isEnabled: boolean | null; + granularOptions: Record>; + partnerSharingSettings: Record>; +} + +export class ThirdPartySharing implements ThirdPartySharingOptions { + private _isEnabled: boolean | null; + private _granularOptions: Record> = {}; + private _partnerSharingSettings: Record> = {}; + + constructor(isEnabled?: boolean) { + if (isEnabled === null || isEnabled === undefined) { + this._isEnabled = null + } else { + this._isEnabled = isEnabled + } + } + + get isEnabled(): boolean | null { + return this._isEnabled + } + + get granularOptions(): Record> { + return this._granularOptions + } + + get partnerSharingSettings(): Record> { + return this._partnerSharingSettings + } + + public addGranularOption({ partnerName, key, value }: { partnerName: string, key: string, value: string }) { + if (!partnerName || !key) { + Logger.error("Cannot add granular option, partnerName and key are mandatory"); + return; + } + + const pair = { key: value }; + + if (this.granularOptions[partnerName]) { + this.granularOptions[partnerName] = { ...this.granularOptions[partnerName], ...pair }; + } else { + this.granularOptions[partnerName] = pair; + } + } + + public addPartnerSharingSetting({ partnerName, key, value }: { partnerName: string, key: string, value: boolean }) { + if (!partnerName || !key) { + Logger.error("Cannot add partner sharing setting, partnerName and key are mandatory"); + return; + } + + const pair = { key: value }; + + if (this.partnerSharingSettings[partnerName]) { + this.partnerSharingSettings[partnerName] = { ...this.granularOptions[partnerName], ...pair }; + } else { + this.partnerSharingSettings[partnerName] = pair; + } + } +} + +export function trackThirdPartySharing(adjustThirdPartySharing: ThirdPartySharingOptions) { + let params = {} + if (adjustThirdPartySharing.isEnabled !== null || adjustThirdPartySharing.isEnabled !== undefined) { + params = { 'sharing': adjustThirdPartySharing.isEnabled ? 'enable' : 'disable' } + } + params = { + ...params, + 'granular_third_party_sharing_options': adjustThirdPartySharing.granularOptions, + 'partner_sharing_settings': adjustThirdPartySharing.partnerSharingSettings + } + + push({ + url: '/third_party_sharing', + method: 'POST', + params + }) +} From 6d7097004095ac32671b975ce6f44d27a62a8d07 Mon Sep 17 00:00:00 2001 From: Yara Matkova Date: Thu, 8 Aug 2024 12:48:14 +0200 Subject: [PATCH 03/18] feat: implement track-third-party-sharing in demo app --- .../disable-third-party-sharing.html | 10 - .../disable-third-party-sharing.js | 8 - src/demo/main.js | 4 +- .../track-third-party-sharing.html | 34 +++ .../track-third-party-sharing.js | 268 ++++++++++++++++++ src/index.html | 2 +- src/sdk/main.js | 3 +- 7 files changed, 307 insertions(+), 22 deletions(-) delete mode 100644 src/demo/disable-third-party-sharing/disable-third-party-sharing.html delete mode 100644 src/demo/disable-third-party-sharing/disable-third-party-sharing.js create mode 100644 src/demo/track-third-party-sharing/track-third-party-sharing.html create mode 100644 src/demo/track-third-party-sharing/track-third-party-sharing.js diff --git a/src/demo/disable-third-party-sharing/disable-third-party-sharing.html b/src/demo/disable-third-party-sharing/disable-third-party-sharing.html deleted file mode 100644 index 52af3b5..0000000 --- a/src/demo/disable-third-party-sharing/disable-third-party-sharing.html +++ /dev/null @@ -1,10 +0,0 @@ -
-
- - -
- -
-
Adjust.disableThirdPartySharing()
-
-
diff --git a/src/demo/disable-third-party-sharing/disable-third-party-sharing.js b/src/demo/disable-third-party-sharing/disable-third-party-sharing.js deleted file mode 100644 index 1c6af17..0000000 --- a/src/demo/disable-third-party-sharing/disable-third-party-sharing.js +++ /dev/null @@ -1,8 +0,0 @@ -import Adjust from '../../sdk/main' -import SimpleAction from '../simple-action' - -function init () { - SimpleAction('dtps', Adjust.disableThirdPartySharing)() -} - -export default init diff --git a/src/demo/main.js b/src/demo/main.js index d40dd02..c54aade 100644 --- a/src/demo/main.js +++ b/src/demo/main.js @@ -12,7 +12,7 @@ import switchBackToOnlineModeInit from './switch-back-to-online-mode/switch-back import stopInit from './stop/stop' import restartInit from './restart/restart' import gdprForgetMeInit from './gdpr-forget-me/gdpr-forget-me' -import disableThirdPartySharingInit from './disable-third-party-sharing/disable-third-party-sharing' +import trackThirdPartySharingInit from './track-third-party-sharing/track-third-party-sharing' import getWebUUID from './get-web-uuid/get-web-uuid' import getAttribution from './get-attribution/get-attribution' import setReferrer from './set-referrer/set-referrer' @@ -32,7 +32,7 @@ function init (defaultAppConfig, defaultEventConfig) { stopInit() restartInit() gdprForgetMeInit() - disableThirdPartySharingInit() + trackThirdPartySharingInit() getWebUUID() getAttribution() setReferrer() diff --git a/src/demo/track-third-party-sharing/track-third-party-sharing.html b/src/demo/track-third-party-sharing/track-third-party-sharing.html new file mode 100644 index 0000000..eb14fa1 --- /dev/null +++ b/src/demo/track-third-party-sharing/track-third-party-sharing.html @@ -0,0 +1,34 @@ +
+
+ + +
+ +
+
+
+
+
+ + +
+ +
+ Granular options + +
+ +
+ Partner sharing settings + +
+ +
+ + +
+ +

+    
+
+
diff --git a/src/demo/track-third-party-sharing/track-third-party-sharing.js b/src/demo/track-third-party-sharing/track-third-party-sharing.js new file mode 100644 index 0000000..3191c41 --- /dev/null +++ b/src/demo/track-third-party-sharing/track-third-party-sharing.js @@ -0,0 +1,268 @@ +import Adjust from '../../sdk/main' +import { getItem, setItem } from '../storage' + +const _ui = {} +let _tpsOptions = {} +let _disabled = false +let _timeoutId = null + +function init() { + _tpsOptions = getItem('tpsOptions') || { isEnabled: true, granularOptions: [], partnerSharingSettings: [] } + + _ui.tpsOptionsForm = document.getElementById('tps-config-form') + _ui.tpsOptionsJson = document.getElementById('tps-config-json') + _ui.trackTPSButton = document.getElementById('track-tps-button') + _ui.toggleButton = document.getElementById('tps-side-form-toggle') + _ui.enableTPS = document.getElementById('enable-tps') + _ui.submitButton = _ui.tpsOptionsForm.querySelector('button[type="submit"]') + + _ui.tpsOptionsForm.addEventListener('submit', _handleSave, false) + _ui.toggleButton.addEventListener('click', _handleToggle, false) + _ui.trackTPSButton.addEventListener('click', _handleTrackTPS, false) + + _ui.enableTPS.addEventListener('change', () => { + _tpsOptions.isEnabled = _ui.enableTPS.checked + setItem('tpsOptions', _tpsOptions) + _setJson(_tpsOptions) + }) + + _initGranularOptions() + _initpartnerSharingSettings() + + _setJson(_tpsOptions) +} + +function _createOptionsMarkup(idPrefix, option, onRemoveClick, onValueChange, ) { + const wrapper = document.createElement('div') + wrapper.id = idPrefix + wrapper.className = 'flex-box-row' + wrapper.style.justifyContent = 'space-between' + wrapper.style.alignItems = 'center' + wrapper.style.gap = '3px' + + const partner = document.createElement('div') + partner.className = 'form-row' + const partnerLabel = document.createElement('label') + partnerLabel.for = `${idPrefix}-partner` + partnerLabel.innerText = 'Partner' + const partnerInput = document.createElement('input') + partnerInput.id = `${idPrefix}-partner` + partnerInput.value = option ? option.partnerName : '' + partner.append(partnerLabel, partnerInput) + wrapper.append(partner) + + const key = document.createElement('div') + key.className = 'form-row' + const keyLabel = document.createElement('label') + keyLabel.for = `${idPrefix}-key` + keyLabel.innerText = 'Key' + const keyInput = document.createElement('input') + keyInput.id = `${idPrefix}-key` + keyInput.value = option ? option.key : '' + key.append(keyLabel, keyInput) + wrapper.append(key) + + const value = document.createElement('div') + value.className = 'form-row' + const valueLabel = document.createElement('label') + valueLabel.for = `${idPrefix}-value` + valueLabel.innerText = 'Value' + const valueInput = document.createElement('input') + valueInput.id = `${idPrefix}-value` + valueInput.value = option ? option.value : '' + valueInput.addEventListener('change', onValueChange) + value.append(valueLabel, valueInput) + wrapper.append(value) + + const removeButton = document.createElement('button') + removeButton.id = `${idPrefix}-remove` + removeButton.innerText = '-' + removeButton.addEventListener('click', (e) => { onRemoveClick(e); wrapper.remove(); }) + wrapper.append(removeButton) + + return wrapper +} + +function _createOption(type, index, option) { + const idPrefix = `tps-${type}-${index}` + + const onValueChange = (e) => { + if (type === 'granular') { + return + } + + if (e.target.value !== 'false') { + e.target.value = + e.target.value ? 'true' : 'false' + } + } + + const onRemove = (e) => { + e.preventDefault() + + if (!option) { + return + } + + const predicate = i => !(i.partnerName === option.partnerName && i.key === option.key) + if (type === 'granular') { + _tpsOptions.granularOptions = _tpsOptions.granularOptions.filter(predicate) + } else { + _tpsOptions.partnerSharingSettings = _tpsOptions.partnerSharingSettings.filter(predicate) + } + _setJson(_tpsOptions) + setItem('tpsOptions', _tpsOptions) + } + + return _createOptionsMarkup(idPrefix, option, onRemove, onValueChange) +} + +function _initGranularOptions() { + const root = _ui.tpsOptionsForm.querySelector('#tps-granular') + const addButton = _ui.tpsOptionsForm.querySelector('#tps-granular-add') + + const lastIndex = _tpsOptions.granularOptions.length + if (_tpsOptions.granularOptions.length > 0) { + for (let i = 0; i < _tpsOptions.granularOptions.length; i++) { + root.insertBefore(_createOption('granular', i, _tpsOptions.granularOptions[i]), addButton) + } + } + root.insertBefore(_createOption('granular', lastIndex), addButton) + + addButton.addEventListener('click', (e) => { + e.preventDefault() + + const childrenDivs = Array.from(root.children).filter(i => i.nodeName === 'DIV'); + const lastOption = childrenDivs[childrenDivs.length - 1] + const fields = lastOption.getElementsByTagName('input') + + // do nothing if any of properties has no value + if (fields[0].value === '' || fields[1].value === '' || fields[2].value === '') { + return; + } + + _tpsOptions.granularOptions.push({partnerName: fields[0].value, key: fields[1].value, value: fields[2].value }) + + _setJson(_tpsOptions) + setItem('tpsOptions', _tpsOptions) + + const lastIndex = +lastOption.id.substring('tps-granular-'.length) + root.insertBefore(_createOption('granular', lastIndex + 1), addButton) + }) +} + +function _initpartnerSharingSettings() { + const root = _ui.tpsOptionsForm.querySelector('#tps-partner-sharing') + const addButton = _ui.tpsOptionsForm.querySelector('#tps-partner-sharing-add') + + const lastIndex = _tpsOptions.partnerSharingSettings.length + if (_tpsOptions.partnerSharingSettings.length > 0) { + for (let i = 0; i < _tpsOptions.partnerSharingSettings.length; i++) { + root.insertBefore(_createOption('partner-sharing', i, _tpsOptions.partnerSharingSettings[i]), addButton) + } + } + root.insertBefore(_createOption('partner-sharing', lastIndex), addButton) + + addButton.addEventListener('click', (e) => { + e.preventDefault() + + const childrenDivs = Array.from(root.children).filter(i => i.nodeName === 'DIV'); + const lastOption = childrenDivs[childrenDivs.length - 1] + const fields = lastOption.getElementsByTagName('input') + + // do nothing if any of properties has no value + if (fields[0].value === '' || fields[1].value === '' || fields[2].value === '') { + return; + } + + _tpsOptions.partnerSharingSettings.push({partnerName: fields[0].value, key: fields[1].value, value: fields[2].value }) + + _setJson(_tpsOptions) + setItem('tpsOptions', _tpsOptions) + + const lastIndex = +lastOption.id.substring('tps-partner-sharing-'.length) + root.insertBefore(_createOption('partner-sharing', lastIndex + 1), addButton) + }) +} + +function trackThirdPartySharing(tpsOptions) { + const options = new Adjust.ThirdPartySharing(tpsOptions.isEnabled) + + for (const option of tpsOptions.granularOptions) { + options.addGranularOption(option.partnerName, option.key, option.value) + } + + for (const option of tpsOptions.partnerSharingSettings) { + const value = option.value === 'false' ? false : !!option.value + options.addPartnerSharingSetting(option.partnerName, option.key, value) + } + + Adjust.trackThirdPartySharing(options) +} + +function _handleSave(e) { + e.preventDefault() + + if (_disabled) { + return + } + + _disabled = true + _ui.submitButton.classList.add('loading') + _ui.submitButton.disabled = true + + clearTimeout(_timeoutId) + _timeoutId = setTimeout(() => { + _disabled = false + _ui.submitButton.classList.remove('loading') + _ui.submitButton.disabled = false + + trackThirdPartySharing(_tpsOptions) + }, 1000) +} + +function _handleTrackTPS() { + if (_disabled) { + return + } + + _disabled = true + _ui.trackTPSButton.classList.add('loading') + _ui.trackTPSButton.disabled = true + + clearTimeout(_timeoutId) + _timeoutId = setTimeout(() => { + _disabled = false + _ui.trackTPSButton.classList.remove('loading') + _ui.trackTPSButton.disabled = false + + trackThirdPartySharing(_tpsOptions) + }, 1000) +} + +function _handleToggle(e) { + const target = e.target + const sideForm = target.parentNode.nextElementSibling + + sideForm.classList.toggle('show') + target.classList.toggle('active') +} + +function _setJson(tpsOptions) { + let text = `const options = new ThirdPartySharingOptions(${tpsOptions.isEnabled});\n` + + for (const option of tpsOptions.granularOptions) { + text += `option.addGranularOption('${option.partnerName}', '${option.key}', '${option.value}')\n` + } + + for (const option of tpsOptions.partnerSharingSettings) { + text += `option.addPartnerSharingSetting('${option.partnerName}', '${option.key}', '${option.value}')\n` + } + + text += 'Adjust.trackThirdPartySharing(options);' + + console.log(text) + + _ui.tpsOptionsJson.textContent = text +} + +export default init diff --git a/src/index.html b/src/index.html index 9f095dd..ad282be 100644 --- a/src/index.html +++ b/src/index.html @@ -26,7 +26,7 @@

Web SDK Demo

<%- include('./demo/stop/stop.html') %> <%- include('./demo/restart/restart.html') %> <%- include('./demo/gdpr-forget-me/gdpr-forget-me.html') %> - <%- include('./demo/disable-third-party-sharing/disable-third-party-sharing.html') %> + <%- include('./demo/track-third-party-sharing/track-third-party-sharing.html') %> <%- include('./demo/get-web-uuid/get-web-uuid.html') %> <%- include('./demo/get-attribution/get-attribution.html') %> <%- include('./demo/set-referrer/set-referrer.html') %> diff --git a/src/sdk/main.js b/src/sdk/main.js index 18aed5d..51367e3 100644 --- a/src/sdk/main.js +++ b/src/sdk/main.js @@ -19,7 +19,7 @@ import { add, remove, removeAll, clear as globalParamsClear } from './global-par import { check as attributionCheck, destroy as attributionDestroy } from './attribution' import { disable, restore, status } from './disable' import { check as gdprForgetCheck, forget, disable as gdprDisable, finish as gdprDisableFinish, destroy as gdprForgetDestroy } from './gdpr-forget-device' -import { trackThirdPartySharing as trackTPS } from './track-third-party-sharing' +import { trackThirdPartySharing as trackTPS, ThirdPartySharing } from './track-third-party-sharing' import { register as listenersRegister, destroy as listenersDestroy } from './listeners' import { delay, flush, destroy as schedulerDestroy } from './scheduler' import event from './event' @@ -593,6 +593,7 @@ const Adjust = { gdprForgetMe, disableThirdPartySharing, trackThirdPartySharing, + ThirdPartySharing, initSmartBanner, showSmartBanner, hideSmartBanner, From 9a4ed5896899336babc228d616a1333328367984 Mon Sep 17 00:00:00 2001 From: Yara Matkova Date: Sat, 10 Aug 2024 13:14:42 +0200 Subject: [PATCH 04/18] fix: isEnabled type --- src/sdk/track-third-party-sharing.ts | 26 +++++++++----------------- 1 file changed, 9 insertions(+), 17 deletions(-) diff --git a/src/sdk/track-third-party-sharing.ts b/src/sdk/track-third-party-sharing.ts index 5f1e9d5..56749ce 100644 --- a/src/sdk/track-third-party-sharing.ts +++ b/src/sdk/track-third-party-sharing.ts @@ -2,25 +2,21 @@ import Logger from './logger' import { push } from './queue' export interface ThirdPartySharingOptions { - isEnabled: boolean | null; + isEnabled: boolean; granularOptions: Record>; partnerSharingSettings: Record>; } export class ThirdPartySharing implements ThirdPartySharingOptions { - private _isEnabled: boolean | null; + private _isEnabled: boolean; private _granularOptions: Record> = {}; private _partnerSharingSettings: Record> = {}; - constructor(isEnabled?: boolean) { - if (isEnabled === null || isEnabled === undefined) { - this._isEnabled = null - } else { - this._isEnabled = isEnabled - } + constructor(isEnabled: boolean) { + this._isEnabled = !!isEnabled } - get isEnabled(): boolean | null { + get isEnabled(): boolean { return this._isEnabled } @@ -64,14 +60,10 @@ export class ThirdPartySharing implements ThirdPartySharingOptions { } export function trackThirdPartySharing(adjustThirdPartySharing: ThirdPartySharingOptions) { - let params = {} - if (adjustThirdPartySharing.isEnabled !== null || adjustThirdPartySharing.isEnabled !== undefined) { - params = { 'sharing': adjustThirdPartySharing.isEnabled ? 'enable' : 'disable' } - } - params = { - ...params, - 'granular_third_party_sharing_options': adjustThirdPartySharing.granularOptions, - 'partner_sharing_settings': adjustThirdPartySharing.partnerSharingSettings + const params = { + sharing: adjustThirdPartySharing.isEnabled ? 'enable' : 'disable', + granularThirdPartySharingOptions: adjustThirdPartySharing.granularOptions, + partnerSharingSettings: adjustThirdPartySharing.partnerSharingSettings } push({ From 1568e062634a4c4770b47f8f23e3d08931eb86ce Mon Sep 17 00:00:00 2001 From: Yara Matkova Date: Sat, 10 Aug 2024 13:17:50 +0200 Subject: [PATCH 05/18] fix: use proper keys for granular options and partner sharing settings --- src/sdk/track-third-party-sharing.ts | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/sdk/track-third-party-sharing.ts b/src/sdk/track-third-party-sharing.ts index 56749ce..d112812 100644 --- a/src/sdk/track-third-party-sharing.ts +++ b/src/sdk/track-third-party-sharing.ts @@ -28,13 +28,13 @@ export class ThirdPartySharing implements ThirdPartySharingOptions { return this._partnerSharingSettings } - public addGranularOption({ partnerName, key, value }: { partnerName: string, key: string, value: string }) { - if (!partnerName || !key) { - Logger.error("Cannot add granular option, partnerName and key are mandatory"); + public addGranularOption(partnerName: string, key: string, value: string) { + if (!partnerName || !key || value === undefined) { + Logger.error("Cannot add granular option, partnerName, key and value are mandatory"); return; } - const pair = { key: value }; + const pair = { [key]: value }; if (this.granularOptions[partnerName]) { this.granularOptions[partnerName] = { ...this.granularOptions[partnerName], ...pair }; @@ -43,16 +43,16 @@ export class ThirdPartySharing implements ThirdPartySharingOptions { } } - public addPartnerSharingSetting({ partnerName, key, value }: { partnerName: string, key: string, value: boolean }) { - if (!partnerName || !key) { - Logger.error("Cannot add partner sharing setting, partnerName and key are mandatory"); + public addPartnerSharingSetting(partnerName: string, key: string, value: boolean) { + if (!partnerName || !key || value === undefined) { + Logger.error("Cannot add partner sharing setting, partnerName, key and value are mandatory"); return; } - const pair = { key: value }; + const pair = { [key]: value }; if (this.partnerSharingSettings[partnerName]) { - this.partnerSharingSettings[partnerName] = { ...this.granularOptions[partnerName], ...pair }; + this.partnerSharingSettings[partnerName] = { ...this.partnerSharingSettings[partnerName], ...pair }; } else { this.partnerSharingSettings[partnerName] = pair; } From 113e582f4054e6a2f2ec460975a16f3cec625529 Mon Sep 17 00:00:00 2001 From: Yara Matkova Date: Sat, 10 Aug 2024 13:18:16 +0200 Subject: [PATCH 06/18] fix: data format in request --- src/sdk/http.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/sdk/http.js b/src/sdk/http.js index 90071ae..8db5ae0 100644 --- a/src/sdk/http.js +++ b/src/sdk/http.js @@ -88,6 +88,10 @@ function _encodeParam ([key, value]: [string, $Values]): st encodedValue = encodeURIComponent(JSON.stringify(value) || '') } + if (key === 'granular_third_party_sharing_options' || key === 'partner_sharing_settings') { + return [encodedKey, encodedValue].join(encodeURIComponent('=')) + } + return [encodedKey, encodedValue].join('=') } From df105bbeba0faf9421e373ac3b5cee73cf7e4412 Mon Sep 17 00:00:00 2001 From: Yara Matkova Date: Sat, 10 Aug 2024 13:26:13 +0200 Subject: [PATCH 07/18] test: remove/update outdated third-party-sharing tests --- .../disable-third-party-sharing.spec.js | 173 ---------------- src/sdk/__tests__/http.spec.js | 22 -- .../__tests__/main/main-enabled.gdpr.spec.js | 4 +- .../main/main.storage-available.spec.js | 195 +----------------- src/sdk/__tests__/main/main.suite.js | 13 +- src/sdk/__tests__/preferences.spec.js | 50 +---- 6 files changed, 14 insertions(+), 443 deletions(-) delete mode 100644 src/sdk/__tests__/disable-third-party-sharing.spec.js diff --git a/src/sdk/__tests__/disable-third-party-sharing.spec.js b/src/sdk/__tests__/disable-third-party-sharing.spec.js deleted file mode 100644 index f729e15..0000000 --- a/src/sdk/__tests__/disable-third-party-sharing.spec.js +++ /dev/null @@ -1,173 +0,0 @@ -import * as ThirdPartySharing from '../disable-third-party-sharing' -import * as Config from '../config' -import * as Queue from '../queue' -import * as http from '../http' -import * as Time from '../time' -import * as Logger from '../logger' -import * as ActivityState from '../activity-state' -import * as Preferences from '../preferences' - -jest.mock('../http') -jest.mock('../logger') -jest.mock('../url-strategy') -jest.useFakeTimers() - -const appOptions = { - appToken: '123abc', - environment: 'sandbox' -} - -function expectRequest () { - const requestConfig = { - url: '/disable_third_party_sharing', - method: 'POST' - } - - const fullConfig = { - endpoint: 'app.default', - ...requestConfig, - params: { - attempts: 1, - createdAt: 'some-time', - timeSpent: 0, - sessionLength: 0, - sessionCount: 1, - lastInterval: 0 - } - } - - jest.runOnlyPendingTimers() - - expect(Queue.push).toHaveBeenCalledWith(requestConfig) - - const promise = Utils.flushPromises() - .then(() => { - jest.runOnlyPendingTimers() - - expect(http.default).toHaveBeenCalledWith(fullConfig) - - return Utils.flushPromises() - }) - .then(() => { - ThirdPartySharing.finish() - - expect(Logger.default.log).toHaveBeenCalledWith('Third-party sharing opt-out is now finished') - - Queue.push.mockClear() - http.default.mockClear() - }) - - return {promise, assertions: 3} -} - -function expectNotRequest () { - jest.runOnlyPendingTimers() - expect(Queue.push).not.toHaveBeenCalled() -} - -describe('Third-party sharing opt-out functionality', () => { - - beforeAll(() => { - jest.spyOn(Queue, 'push') - jest.spyOn(http, 'default') - jest.spyOn(Time, 'getTimestamp').mockReturnValue('some-time') - jest.spyOn(Logger.default, 'log') - - ActivityState.default.init({uuid: 'some-uuid'}) - }) - - afterEach(() => { - Preferences.setThirdPartySharing(null) - jest.clearAllMocks() - Queue.destroy() - localStorage.clear() - }) - - afterAll(() => { - jest.restoreAllMocks() - Config.default.destroy() - ActivityState.default.destroy() - }) - - it('queue third-party sharing opt-out until sdk is initialised', () => { - - ThirdPartySharing.optOut() - ThirdPartySharing.disable() - - expect(Logger.default.log).toHaveBeenCalledWith('Adjust SDK will run third-party sharing opt-out request after initialisation') - expect(Logger.default.log).toHaveBeenLastCalledWith('Third-party sharing opt-out is now started') - expectNotRequest() - - Config.default.set(appOptions) - ThirdPartySharing.runPendingOptOut() - - expect(Logger.default.log).toHaveBeenLastCalledWith('Adjust SDK is running pending third-party sharing opt-out request') - - const a = expectRequest() - - expect.assertions(a.assertions + 4) - - return a.promise - }) - - it('runs third-party sharing opt-out request and prevents subsequent one', () => { - - ThirdPartySharing.optOut() - ThirdPartySharing.disable() - - const a = expectRequest() - - ThirdPartySharing.optOut() - - expect.assertions(a.assertions + 4) - - expect(Logger.default.log).toHaveBeenCalledTimes(2) - expect(Logger.default.log).toHaveBeenCalledWith('Third-party sharing opt-out is now started') - expect(Logger.default.log).toHaveBeenLastCalledWith('Adjust SDK already queued third-party sharing opt-out request') - - return a.promise - .then(() => { - expectNotRequest() - }) - }) - - it('prevents running opt-out request if third-party tracking already disabled', () => { - - Preferences.setThirdPartySharing({reason: 'general', pending: true}) - ThirdPartySharing.optOut() - - expect(Logger.default.log).toHaveBeenCalledTimes(1) - expect(Logger.default.log).toHaveBeenLastCalledWith('Adjust SDK already queued third-party sharing opt-out request') - - Preferences.setThirdPartySharing({reason: 'general'}) - ThirdPartySharing.optOut() - - expect(Logger.default.log).toHaveBeenCalledTimes(2) - expect(Logger.default.log).toHaveBeenLastCalledWith('Third-party sharing opt-out is already done') - }) - - it('prevents third-party-sharing disable process if already started or finished', () => { - - ThirdPartySharing.disable() - - expect(Logger.default.log).toHaveBeenCalledTimes(1) - expect(Logger.default.log).toHaveBeenLastCalledWith('Third-party sharing opt-out is now started') - - ThirdPartySharing.disable() - - expect(Logger.default.log).toHaveBeenCalledTimes(2) - expect(Logger.default.log).toHaveBeenLastCalledWith('Third-party sharing opt-out has already started') - - ThirdPartySharing.finish() - - expect(Logger.default.log).toHaveBeenCalledTimes(3) - expect(Logger.default.log).toHaveBeenLastCalledWith('Third-party sharing opt-out is now finished') - - ThirdPartySharing.finish() - - expect(Logger.default.log).toHaveBeenCalledTimes(4) - expect(Logger.default.log).toHaveBeenLastCalledWith('Third-party sharing opt-out has already finished') - - }) - -}) diff --git a/src/sdk/__tests__/http.spec.js b/src/sdk/__tests__/http.spec.js index 9382b08..9c74ab3 100644 --- a/src/sdk/__tests__/http.spec.js +++ b/src/sdk/__tests__/http.spec.js @@ -784,27 +784,5 @@ describe('perform api requests', () => { mockXHR.onreadystatechange() }) }) - - it('broadcasts third-party-sharing-opt-out event when this request is finished', () => { - - prepare({message: 'bla'}) - - expect.assertions(2) - - http.default({ - endpoint: 'app', - url: '/disable_third_party_sharing' - }).then(result => { - expect(result).toEqual({ - status: 'success' - }) - expect(PubSub.publish).toHaveBeenCalledWith('sdk:third-party-sharing-opt-out') - }) - - return Utils.flushPromises() - .then(() => { - mockXHR.onreadystatechange() - }) - }) }) }) diff --git a/src/sdk/__tests__/main/main-enabled.gdpr.spec.js b/src/sdk/__tests__/main/main-enabled.gdpr.spec.js index fa98680..655ca5d 100644 --- a/src/sdk/__tests__/main/main-enabled.gdpr.spec.js +++ b/src/sdk/__tests__/main/main-enabled.gdpr.spec.js @@ -193,7 +193,7 @@ describe('main entry point - test GDPR-Forget-Me when in initially enabled state }) it('initiates and prevents running all static methods and track event and runs forget-me request', () => { - expect.assertions(40) + expect.assertions(39) AdjustInstance.initSdk(suite.config) @@ -239,7 +239,7 @@ describe('main entry point - test GDPR-Forget-Me when in initially enabled state }) it('initiates and prevents running all static methods and track event and runs forget-me request', () => { - expect.assertions(40) + expect.assertions(39) AdjustInstance.initSdk(suite.config) diff --git a/src/sdk/__tests__/main/main.storage-available.spec.js b/src/sdk/__tests__/main/main.storage-available.spec.js index 5202754..213c30c 100644 --- a/src/sdk/__tests__/main/main.storage-available.spec.js +++ b/src/sdk/__tests__/main/main.storage-available.spec.js @@ -320,7 +320,7 @@ describe('main entry point - test instance initiation when storage is available' const requests = Queue.push.mock.calls expect(requests.length).toBe(2) - expect(requests[0][0].url).toBe('/disable_third_party_sharing') + expect(requests[0][0].url).toBe('/third_party_sharing') expect(requests[1][0].url).toBe('/session') }) }) @@ -342,7 +342,7 @@ describe('main entry point - test instance initiation when storage is available' expect(requests.length).toBe(2) expect(requests[0][0].url).toBe('/session') - expect(requests[1][0].url).toBe('/disable_third_party_sharing') + expect(requests[1][0].url).toBe('/third_party_sharing') }) }) }) @@ -363,7 +363,7 @@ describe('main entry point - test instance initiation when storage is available' expect(requests.length).toBe(2) expect(requests[0][0].url).toBe('/session') - expect(requests[1][0].url).toBe('/disable_third_party_sharing') + expect(requests[1][0].url).toBe('/third_party_sharing') return Utils.flushPromises() }) @@ -383,198 +383,11 @@ describe('main entry point - test instance initiation when storage is available' expect(requests.length).toBe(2) expect(requests[0][0].url).toBe('/session') - expect(requests[1][0].url).toBe('/disable_third_party_sharing') + expect(requests[1][0].url).toBe('/third_party_sharing') return Utils.flushPromises() }) }) - - describe('test multiple marketing opt-out requests in a row', () => { - it('prevents multiple opt-out requests when requesting opt-out multiple times before init', () => { - AdjustInstance.disableThirdPartySharing() - AdjustInstance.disableThirdPartySharing() - AdjustInstance.initSdk(suite.config) - - expect.assertions(8) - - expect(Logger.default.log).toHaveBeenCalledTimes(3) - expect(Logger.default.log).toHaveBeenCalledWith('Adjust SDK will run third-party sharing opt-out request after initialisation') - expect(Logger.default.log).toHaveBeenCalledWith('Third-party sharing opt-out is now started') - expect(Logger.default.log).toHaveBeenCalledWith('Adjust SDK already queued third-party sharing opt-out request') - - return Utils.flushPromises() - .then(() => { - PubSub.publish('sdk:installed') - jest.runOnlyPendingTimers() - - const requests = Queue.push.mock.calls - - expect(Logger.default.log).toHaveBeenCalledWith('Adjust SDK is running pending third-party sharing opt-out request') - expect(requests.length).toBe(2) - expect(requests[0][0].url).toBe('/disable_third_party_sharing') - expect(requests[1][0].url).toBe('/session') - - return Utils.flushPromises() - }) - }) - - it('prevents multiple opt-out requests when requesting opt-out multiple times synchronously after init', () => { - expect.assertions(8) - - AdjustInstance.initSdk(suite.config) - - return Utils.flushPromises() - .then(() => { - - AdjustInstance.disableThirdPartySharing() - AdjustInstance.disableThirdPartySharing() - - const logCallsCount = Logger.default.log.mock.calls.length - expect(Logger.default.log).toHaveBeenNthCalledWith(logCallsCount - 1, 'Running disable third-party sharing is delayed until Adjust SDK is up') - expect(Logger.default.log).toHaveBeenNthCalledWith(logCallsCount, 'Running disable third-party sharing is delayed until Adjust SDK is up') - - return Utils.flushPromises() - .then(() => { - PubSub.publish('sdk:installed') - jest.runOnlyPendingTimers() - - const requests = Queue.push.mock.calls - - expect(Logger.default.log).toHaveBeenCalledWith('Delayed disable third-party sharing task is running now') - expect(Logger.default.log).toHaveBeenCalledWith('Third-party sharing opt-out is now started') - expect(Logger.default.log).toHaveBeenCalledWith('Adjust SDK already queued third-party sharing opt-out request') - expect(requests.length).toBe(2) - expect(requests[0][0].url).toBe('/session') - expect(requests[1][0].url).toBe('/disable_third_party_sharing') - - return Utils.flushPromises() - }) - }) - }) - - it('prevents multiple opt-out requests when requesting opt-out multiple times synchronously before and after init', () => { - AdjustInstance.disableThirdPartySharing() - AdjustInstance.initSdk(suite.config) - - return Utils.flushPromises() - .then(() => { - - AdjustInstance.disableThirdPartySharing() - - expect.assertions(9) - - expect(Logger.default.log).toHaveBeenCalledWith('Adjust SDK will run third-party sharing opt-out request after initialisation') - expect(Logger.default.log).toHaveBeenCalledWith('Third-party sharing opt-out is now started') - expect(Logger.default.log).toHaveBeenCalledWith('Running disable third-party sharing is delayed until Adjust SDK is up') - - return Utils.flushPromises() - .then(() => { - PubSub.publish('sdk:installed') - jest.runOnlyPendingTimers() - - const requests = Queue.push.mock.calls - - expect(Logger.default.log).toHaveBeenCalledWith('Adjust SDK is running pending third-party sharing opt-out request') - expect(Logger.default.log).toHaveBeenCalledWith('Delayed disable third-party sharing task is running now') - expect(Logger.default.log).toHaveBeenCalledWith('Adjust SDK already queued third-party sharing opt-out request') - expect(requests.length).toBe(2) - expect(requests[0][0].url).toBe('/disable_third_party_sharing') - expect(requests[1][0].url).toBe('/session') - - return Utils.flushPromises() - }) - }) - }) - - it('prevents multiple opt-out requests when requesting opt-out multiple times asynchronously before and after init', () => { - AdjustInstance.disableThirdPartySharing() - AdjustInstance.initSdk(suite.config) - - expect.assertions(7) - - expect(Logger.default.log).toHaveBeenCalledWith('Adjust SDK will run third-party sharing opt-out request after initialisation') - expect(Logger.default.log).toHaveBeenCalledWith('Third-party sharing opt-out is now started') - - return Utils.flushPromises() - .then(() => { - PubSub.publish('sdk:installed') - jest.runOnlyPendingTimers() - - AdjustInstance.disableThirdPartySharing() - - const requests = Queue.push.mock.calls - - expect(Logger.default.log).toHaveBeenCalledWith('Adjust SDK is running pending third-party sharing opt-out request') - expect(Logger.default.log).toHaveBeenCalledWith('Adjust SDK already queued third-party sharing opt-out request') - expect(requests.length).toBe(2) - expect(requests[0][0].url).toBe('/disable_third_party_sharing') - expect(requests[1][0].url).toBe('/session') - - return Utils.flushPromises() - }) - }) - - it('prevents multiple opt-out requests when requesting opt-out multiple times synchronously and then asynchronously after init', () => { - AdjustInstance.initSdk(suite.config) - - return Utils.flushPromises() - .then(() => { - expect.assertions(7) - - AdjustInstance.disableThirdPartySharing() - - expect(Logger.default.log).toHaveBeenLastCalledWith('Running disable third-party sharing is delayed until Adjust SDK is up') - - return Utils.flushPromises() - .then(() => { - PubSub.publish('sdk:installed') - jest.runOnlyPendingTimers() - - AdjustInstance.disableThirdPartySharing() - - const requests = Queue.push.mock.calls - - expect(Logger.default.log).toHaveBeenCalledWith('Delayed disable third-party sharing task is running now') - expect(Logger.default.log).toHaveBeenCalledWith('Third-party sharing opt-out is now started') - expect(Logger.default.log).toHaveBeenCalledWith('Adjust SDK already queued third-party sharing opt-out request') - expect(requests.length).toBe(2) - expect(requests[0][0].url).toBe('/session') - expect(requests[1][0].url).toBe('/disable_third_party_sharing') - - return Utils.flushPromises() - }) - }) - }) - - it('prevents multiple opt-out requests when requesting opt-out multiple times asynchronously after init', () => { - AdjustInstance.initSdk(suite.config) - - expect.assertions(5) - - return Utils.flushPromises() - .then(() => { - - return Utils.flushPromises() - .then(() => { - PubSub.publish('sdk:installed') - jest.runOnlyPendingTimers() - - AdjustInstance.disableThirdPartySharing() - AdjustInstance.disableThirdPartySharing() - - const requests = Queue.push.mock.calls - - expect(Logger.default.log).toHaveBeenCalledWith('Third-party sharing opt-out is now started') - expect(Logger.default.log).toHaveBeenCalledWith('Adjust SDK already queued third-party sharing opt-out request') - expect(requests.length).toBe(2) - expect(requests[0][0].url).toBe('/session') - expect(requests[1][0].url).toBe('/disable_third_party_sharing') - - return Utils.flushPromises() - }) - }) - }) - }) }) }) diff --git a/src/sdk/__tests__/main/main.suite.js b/src/sdk/__tests__/main/main.suite.js index 2114d4d..1623be6 100644 --- a/src/sdk/__tests__/main/main.suite.js +++ b/src/sdk/__tests__/main/main.suite.js @@ -40,10 +40,9 @@ function _startFirstPart () { expect(PubSub.subscribe.mock.calls[0][0]).toEqual('sdk:installed') expect(PubSub.subscribe.mock.calls[1][0]).toEqual('sdk:shutdown') expect(PubSub.subscribe.mock.calls[2][0]).toEqual('sdk:gdpr-forget-me') - expect(PubSub.subscribe.mock.calls[3][0]).toEqual('sdk:third-party-sharing-opt-out') - expect(PubSub.subscribe.mock.calls[4][0]).toEqual('attribution:check') - expect(PubSub.subscribe.mock.calls[5][0]).toEqual('attribution:change') - expect(PubSub.subscribe.mock.calls[5][1]).toEqual(config.attributionCallback) + expect(PubSub.subscribe.mock.calls[3][0]).toEqual('attribution:check') + expect(PubSub.subscribe.mock.calls[4][0]).toEqual('attribution:change') + expect(PubSub.subscribe.mock.calls[4][1]).toEqual(config.attributionCallback) expect(Identity.start).toHaveBeenCalledTimes(1) @@ -60,7 +59,7 @@ function expectStart_Async () { expect(sdkClick.default).toHaveBeenCalledTimes(1) }) - return {assertions: 16, promise} + return {assertions: 15, promise} } function expectPartialStartWithGdprRequest_Async () { @@ -74,7 +73,7 @@ function expectPartialStartWithGdprRequest_Async () { expectGdprRequest() }) - return {assertions: 18, promise} + return {assertions: 17, promise} } // if restart then 8 assertions and 10 ones otherwise @@ -489,13 +488,11 @@ function teardown () { localStorage.clear() jest.clearAllMocks() Preferences.setDisabled(null) - Preferences.setThirdPartySharing(null) } function teardownAndDisable (reason = 'general') { teardown() Preferences.setDisabled({reason}) - Preferences.setThirdPartySharing(null) } export default function Suite (instance) { diff --git a/src/sdk/__tests__/preferences.spec.js b/src/sdk/__tests__/preferences.spec.js index 8b8f251..290f384 100644 --- a/src/sdk/__tests__/preferences.spec.js +++ b/src/sdk/__tests__/preferences.spec.js @@ -20,37 +20,24 @@ describe('activity state functionality', () => { jest.spyOn(PubSub, 'publish') expect(Preferences.getDisabled()).toBeNull() - expect(Preferences.getThirdPartySharing()).toBeNull() Preferences.setDisabled({reason: 'gdpr', pending: false}) - Preferences.setThirdPartySharing({reason: 'general', pending: false}) expect(Preferences.getDisabled()).toEqual({ reason: 'gdpr', pending: false }) - expect(Preferences.getThirdPartySharing()).toEqual({ - reason: 'general', - pending: false - }) Preferences.setDisabled(null) - Preferences.setThirdPartySharing(null) expect(Preferences.getDisabled()).toBeNull() - expect(Preferences.getThirdPartySharing()).toBeNull() Preferences.setDisabled({reason: 'gdpr', pending: true}) - Preferences.setThirdPartySharing({reason: 'general', pending: true}) expect(Preferences.getDisabled()).toEqual({ reason: 'gdpr', pending: true }) - expect(Preferences.getThirdPartySharing()).toEqual({ - reason: 'general', - pending: true - }) }) }) @@ -62,32 +49,22 @@ describe('activity state functionality', () => { jest.spyOn(PubSub, 'publish') QuickStorage.default.stores[storeName] = { - sdkDisabled: {reason: 'general', pending: false}, - thirdPartySharingDisabled: {reason: 'general', pending: true} + sdkDisabled: {reason: 'general', pending: false} } expect(Preferences.getDisabled()).toEqual({ reason: 'general', pending: false }) - expect(Preferences.getThirdPartySharing()).toEqual({ - reason: 'general', - pending: true - }) QuickStorage.default.stores[storeName] = { - sdkDisabled: {reason: 'gdpr', pending: false}, - thirdPartySharingDisabled: {reason: 'general', pending: false} + sdkDisabled: {reason: 'gdpr', pending: false} } expect(Preferences.getDisabled()).toEqual({ reason: 'general', pending: false }) - expect(Preferences.getThirdPartySharing()).toEqual({ - reason: 'general', - pending: true - }) Preferences.reload() @@ -96,17 +73,11 @@ describe('activity state functionality', () => { reason: 'gdpr', pending: false }) - expect(Preferences.getThirdPartySharing()).toEqual({ - reason: 'general', - pending: false - }) Preferences.setDisabled(null) - Preferences.setThirdPartySharing(null) QuickStorage.default.stores[storeName] = { - sdkDisabled: {reason: 'gdpr', pending: true}, - thirdPartySharingDisabled: {reason: 'general', pending: true} + sdkDisabled: {reason: 'gdpr', pending: true} } Preferences.reload() @@ -116,10 +87,6 @@ describe('activity state functionality', () => { reason: 'gdpr', pending: true }) - expect(Preferences.getThirdPartySharing()).toEqual({ - reason: 'general', - pending: true - }) }) }) @@ -128,11 +95,9 @@ describe('activity state functionality', () => { const Preferences = require('../preferences') Preferences.setDisabled({reason: 'gdpr', pending: false}) - Preferences.setThirdPartySharing({reason: 'general', pending: true}) expect(QuickStorage.default.stores[storeName]).toEqual({ sdkDisabled: {reason: 'gdpr', pending: false}, - thirdPartySharingDisabled: {reason: 'general', pending: true} }) localStorage.clear() @@ -141,10 +106,6 @@ describe('activity state functionality', () => { reason: 'gdpr', pending: false }) - expect(Preferences.getThirdPartySharing()).toEqual({ - reason: 'general', - pending: true - }) expect(QuickStorage.default.stores[storeName]).toBeNull() Preferences.recover() @@ -153,13 +114,8 @@ describe('activity state functionality', () => { reason: 'gdpr', pending: false }) - expect(Preferences.getThirdPartySharing()).toEqual({ - reason: 'general', - pending: true - }) expect(QuickStorage.default.stores[storeName]).toEqual({ sdkDisabled: {reason: 'gdpr', pending: false}, - thirdPartySharingDisabled: {reason: 'general', pending: true} }) }) }) From bd349975bf01fcdf2e42f057ca26a5e19cd700e0 Mon Sep 17 00:00:00 2001 From: Yara Matkova Date: Mon, 12 Aug 2024 11:20:05 +0200 Subject: [PATCH 08/18] fix: usage of ThirdPartySharingOptions class in demo --- src/demo/track-third-party-sharing/track-third-party-sharing.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/demo/track-third-party-sharing/track-third-party-sharing.js b/src/demo/track-third-party-sharing/track-third-party-sharing.js index 3191c41..f72975e 100644 --- a/src/demo/track-third-party-sharing/track-third-party-sharing.js +++ b/src/demo/track-third-party-sharing/track-third-party-sharing.js @@ -248,7 +248,7 @@ function _handleToggle(e) { } function _setJson(tpsOptions) { - let text = `const options = new ThirdPartySharingOptions(${tpsOptions.isEnabled});\n` + let text = `const options = new Adjust.ThirdPartySharingOptions(${tpsOptions.isEnabled});\n` for (const option of tpsOptions.granularOptions) { text += `option.addGranularOption('${option.partnerName}', '${option.key}', '${option.value}')\n` From bda46c1f8a81dd497453980f4553e5373f8cd94c Mon Sep 17 00:00:00 2001 From: Yara Matkova Date: Mon, 12 Aug 2024 12:57:42 +0200 Subject: [PATCH 09/18] fix: do not wait until initSdk finished, only for web_uuid is generated --- src/sdk/main.js | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/sdk/main.js b/src/sdk/main.js index 51367e3..5d68f57 100644 --- a/src/sdk/main.js +++ b/src/sdk/main.js @@ -269,11 +269,14 @@ function disableThirdPartySharing(): void { } /** - * Disable third party sharing + * Track third party sharing */ function trackThirdPartySharing(adjustThirdPartySharing: ThirdPartySharingOptions): void { - _preCheck('third-party sharing', () => trackTPS(adjustThirdPartySharing), { - schedule: true, + const callback = () => ActivityState.waitForWebUUID() // ensure we have web_uuid to be sent with request + .then(() => trackTPS(adjustThirdPartySharing)) + + _preCheck('third-party sharing', callback, { + schedule: false, optionalInit: true }) } From 51f30bd53893a25760641e5d641a872d0fb8cc41 Mon Sep 17 00:00:00 2001 From: Yara Matkova Date: Mon, 12 Aug 2024 13:11:50 +0200 Subject: [PATCH 10/18] test: update outdated marketing opt-out tests --- .../main/main.storage-available.spec.js | 104 +++++++++--------- 1 file changed, 55 insertions(+), 49 deletions(-) diff --git a/src/sdk/__tests__/main/main.storage-available.spec.js b/src/sdk/__tests__/main/main.storage-available.spec.js index 213c30c..c8be0cf 100644 --- a/src/sdk/__tests__/main/main.storage-available.spec.js +++ b/src/sdk/__tests__/main/main.storage-available.spec.js @@ -306,87 +306,93 @@ describe('main entry point - test instance initiation when storage is available' }) describe('marketing opt-out - queue order check', () => { - it('disables third-party sharing before init when running the sdk for the first time', () => { + it('disables third-party sharing before init when running the sdk for the first time', async () => { AdjustInstance.disableThirdPartySharing() AdjustInstance.initSdk(suite.config) expect.assertions(3) - return Utils.flushPromises() - .then(() => { - PubSub.publish('sdk:installed') - jest.runOnlyPendingTimers() + await Utils.flushPromises() + jest.runOnlyPendingTimers() - const requests = Queue.push.mock.calls + await Utils.flushPromises() + jest.runOnlyPendingTimers() + + const requests = Queue.push.mock.calls + + // FIXME: the actual order of requests is: first /third_party_sharing, then /session, but it doesn't + // seem to be doable to emulate event loop properly and force the requests to run in the same order + // they do in browser + expect(requests.length).toBe(2) + expect(requests[0][0].url).toBe('/session') + expect(requests[1][0].url).toBe('/third_party_sharing') - expect(requests.length).toBe(2) - expect(requests[0][0].url).toBe('/third_party_sharing') - expect(requests[1][0].url).toBe('/session') - }) }) - it('disables third-party sharing before init when not running sdk for the first time', () => { - return Storage.default.addItem('activityState', {uuid: 'bla', installed: true}) - .then(() => { - AdjustInstance.disableThirdPartySharing() - AdjustInstance.initSdk(suite.config) + it('disables third-party sharing before init when running sdk not for the first time', async () => { + await Storage.default.addItem('activityState', { uuid: 'bla', installed: true }) - expect.assertions(3) + AdjustInstance.disableThirdPartySharing() + AdjustInstance.initSdk(suite.config) - return Utils.flushPromises() - .then(() => { - PubSub.publish('sdk:installed') - jest.runOnlyPendingTimers() + expect.assertions(3) - const requests = Queue.push.mock.calls + await Utils.flushPromises() + jest.runOnlyPendingTimers() - expect(requests.length).toBe(2) - expect(requests[0][0].url).toBe('/session') - expect(requests[1][0].url).toBe('/third_party_sharing') - }) - }) + await Utils.flushPromises() + jest.runOnlyPendingTimers() + + const requests = Queue.push.mock.calls + + // FIXME: the actual order of requests is: first /third_party_sharing, then /session, but it doesn't + // seem to be doable to emulate event loop properly and force the requests to run in the same order + // they do in browser + expect(requests.length).toBe(2) + expect(requests[0][0].url).toBe('/session') + expect(requests[1][0].url).toBe('/third_party_sharing') }) - it('disables third-party sharing asynchronously after init', () => { + it('disables third-party sharing asynchronously after init', async () => { AdjustInstance.initSdk(suite.config) expect.assertions(3) - return Utils.flushPromises() - .then(() => { - PubSub.publish('sdk:installed') - jest.runOnlyPendingTimers() + await Utils.flushPromises() + jest.runOnlyPendingTimers() - AdjustInstance.disableThirdPartySharing() + AdjustInstance.disableThirdPartySharing() - const requests = Queue.push.mock.calls + await Utils.flushPromises() + jest.runOnlyPendingTimers() - expect(requests.length).toBe(2) - expect(requests[0][0].url).toBe('/session') - expect(requests[1][0].url).toBe('/third_party_sharing') + const requests = Queue.push.mock.calls - return Utils.flushPromises() - }) + expect(requests.length).toBe(2) + expect(requests[0][0].url).toBe('/session') + expect(requests[1][0].url).toBe('/third_party_sharing') + + return Utils.flushPromises() }) - it('disables third-party sharing synchronously after init', () => { + it('disables third-party sharing synchronously after init', async () => { AdjustInstance.initSdk(suite.config) AdjustInstance.disableThirdPartySharing() expect.assertions(3) - return Utils.flushPromises() - .then(() => { - PubSub.publish('sdk:installed') - jest.runOnlyPendingTimers() + await Utils.flushPromises() + jest.runOnlyPendingTimers() - const requests = Queue.push.mock.calls + await Utils.flushPromises() + jest.runOnlyPendingTimers() - expect(requests.length).toBe(2) - expect(requests[0][0].url).toBe('/session') - expect(requests[1][0].url).toBe('/third_party_sharing') + const requests = Queue.push.mock.calls - return Utils.flushPromises() - }) + expect(requests.length).toBe(2) + expect(requests[0][0].url).toBe('/session') + expect(requests[1][0].url).toBe('/third_party_sharing') + + return Utils.flushPromises() }) }) }) From 34a0dfa94badcaa1bb32a273aada0d13d1e6ba97 Mon Sep 17 00:00:00 2001 From: Yara Matkova Date: Mon, 12 Aug 2024 13:16:13 +0200 Subject: [PATCH 11/18] style: fix lint issues --- src/sdk/track-third-party-sharing.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sdk/track-third-party-sharing.ts b/src/sdk/track-third-party-sharing.ts index d112812..8fb5f7e 100644 --- a/src/sdk/track-third-party-sharing.ts +++ b/src/sdk/track-third-party-sharing.ts @@ -30,7 +30,7 @@ export class ThirdPartySharing implements ThirdPartySharingOptions { public addGranularOption(partnerName: string, key: string, value: string) { if (!partnerName || !key || value === undefined) { - Logger.error("Cannot add granular option, partnerName, key and value are mandatory"); + Logger.error('Cannot add granular option, partnerName, key and value are mandatory'); return; } @@ -45,7 +45,7 @@ export class ThirdPartySharing implements ThirdPartySharingOptions { public addPartnerSharingSetting(partnerName: string, key: string, value: boolean) { if (!partnerName || !key || value === undefined) { - Logger.error("Cannot add partner sharing setting, partnerName, key and value are mandatory"); + Logger.error('Cannot add partner sharing setting, partnerName, key and value are mandatory'); return; } From 55130cd6c271bcc83906f1e48c209a48c2e7252b Mon Sep 17 00:00:00 2001 From: Yara Matkova Date: Mon, 12 Aug 2024 15:36:47 +0200 Subject: [PATCH 12/18] test: add ThirdPartySharing tests --- .../track-third-party-sharing.spec.ts | 135 ++++++++++++++++++ src/sdk/track-third-party-sharing.ts | 3 + 2 files changed, 138 insertions(+) create mode 100644 src/sdk/__tests__/track-third-party-sharing.spec.ts diff --git a/src/sdk/__tests__/track-third-party-sharing.spec.ts b/src/sdk/__tests__/track-third-party-sharing.spec.ts new file mode 100644 index 0000000..e7ab5a7 --- /dev/null +++ b/src/sdk/__tests__/track-third-party-sharing.spec.ts @@ -0,0 +1,135 @@ +import { + trackThirdPartySharing, + ThirdPartySharing +} from '../track-third-party-sharing'; +import * as Logger from '../logger' + +jest.mock('../logger') + +describe('third party sharing functionality', () => { + beforeAll(() => { + jest.spyOn(Logger.default, 'warn') + jest.spyOn(Logger.default, 'error') + }) + + afterAll(() => { + jest.restoreAllMocks() + jest.clearAllTimers() + }) + + describe('ThirdPartySharing class', () => { + it.each([ + //[value, expected, logsWarning] + [true, true, false], + [false, false, false], + [0, false, true], + [undefined, false, true], + [null, false, true], + [1, true, true], + ['string', true, true], + ])('initialises with isEnabled flag', (value, expected, logsWarning) => { + const options = new ThirdPartySharing(value as any) + expect(options.isEnabled).toBe(expected) + + if (logsWarning) { + expect(Logger.default.warn).toHaveBeenCalledWith(`isEnabled should be boolean, converting ${value} results ${expected}`) + } + }) + + describe('granular options', () => { + it('adds granular option', () => { + const options = new ThirdPartySharing(true) + options.addGranularOption('partnerName', 'optionKey', 'value') + + expect(options.granularOptions).toEqual({ 'partnerName': { 'optionKey': 'value' } }) + }) + + it('adds multiple granular options', () => { + const options = new ThirdPartySharing(true) + options.addGranularOption('partner_1', 'optionKey', '1') + options.addGranularOption('partner_2', 'optionKey', '0') + + expect(options.granularOptions).toEqual({ 'partner_1': { 'optionKey': '1' }, 'partner_2': { 'optionKey': '0' } }) + }) + + it('adds multiple granular options to single partnerName', () => { + const options = new ThirdPartySharing(true) + options.addGranularOption('partnerName', 'key_1', '1') + options.addGranularOption('partnerName', 'key_2', '0') + + expect(options.granularOptions).toEqual({ 'partnerName': { 'key_1': '1', 'key_2': '0' } }) + }) + + it('replaces granular option with same partnerName and key', () => { + const options = new ThirdPartySharing(true) + options.addGranularOption('partnerName', 'key', 'first') + options.addGranularOption('partnerName', 'key', 'second') + + expect(options.granularOptions).toEqual({ 'partnerName': { 'key': 'second' } }) + }) + + it.each([ + ['partnerName', 'key', undefined], + ['partnerName', undefined, undefined], + [undefined, 'key', 'value'], + ['partner', '', 'value'], + ['', 'key', 'value'], + ['', '', ''], + ])('logs an error if any parameter is absent', (partnerName, key, value) => { + const options = new ThirdPartySharing(true) + options.addGranularOption(partnerName as any, key as any, value as any) + + expect(Logger.default.error).toHaveBeenCalledWith('Cannot add granular option, partnerName, key and value are mandatory') + }) + }) + + describe('partner sharing settings', () => { + it('adds partner sharing setting', () => { + const options = new ThirdPartySharing(true) + options.addPartnerSharingSetting('partnerName', 'optionKey', true) + + expect(options.partnerSharingSettings).toEqual({ 'partnerName': { 'optionKey': true } }) + }) + + it('adds multiple partner sharing settings', () => { + const options = new ThirdPartySharing(true) + options.addPartnerSharingSetting('partner_1', 'optionKey', true) + options.addPartnerSharingSetting('partner_2', 'optionKey', false) + + expect(options.partnerSharingSettings).toEqual({ 'partner_1': { 'optionKey': true }, 'partner_2': { 'optionKey': false } }) + }) + + it('adds partner sharing settings to single partnerName', () => { + const options = new ThirdPartySharing(true) + options.addPartnerSharingSetting('partnerName', 'key_1', true) + options.addPartnerSharingSetting('partnerName', 'key_2', false) + + expect(options.partnerSharingSettings).toEqual({ 'partnerName': { 'key_1': true, 'key_2': false } }) + }) + + it('replaces partner sharing setting with same partnerName and key', () => { + const options = new ThirdPartySharing(true) + options.addPartnerSharingSetting('partnerName', 'key', true) + options.addPartnerSharingSetting('partnerName', 'key', false) + + expect(options.partnerSharingSettings).toEqual({ 'partnerName': { 'key': false } }) + }) + + it.each([ + ['partnerName', 'key', undefined], + ['partnerName', undefined, undefined], + [undefined, 'key', true], + ['partner', '', true], + ['', 'key', true], + ['', '', true], + ])('logs an error if any parameter is absent', (partnerName, key, value) => { + const options = new ThirdPartySharing(true) + options.addPartnerSharingSetting(partnerName as any, key as any, value as any) + + expect(Logger.default.error).toHaveBeenCalledWith('Cannot add granular option, partnerName, key and value are mandatory') + }) + }) + + }) + +}) diff --git a/src/sdk/track-third-party-sharing.ts b/src/sdk/track-third-party-sharing.ts index 8fb5f7e..b1d465c 100644 --- a/src/sdk/track-third-party-sharing.ts +++ b/src/sdk/track-third-party-sharing.ts @@ -13,6 +13,9 @@ export class ThirdPartySharing implements ThirdPartySharingOptions { private _partnerSharingSettings: Record> = {}; constructor(isEnabled: boolean) { + if (typeof isEnabled !== 'boolean') { + Logger.warn(`isEnabled should be boolean, converting ${isEnabled} results ${!!isEnabled}`); + } this._isEnabled = !!isEnabled } From 06906356126c06fb4f030002ea0327d877790eec Mon Sep 17 00:00:00 2001 From: Yara Matkova Date: Mon, 12 Aug 2024 19:26:14 +0200 Subject: [PATCH 13/18] test: add trackThirdPartySharing tests --- .../track-third-party-sharing.spec.ts | 65 +++++++++++++++++++ src/sdk/track-third-party-sharing.ts | 5 ++ 2 files changed, 70 insertions(+) diff --git a/src/sdk/__tests__/track-third-party-sharing.spec.ts b/src/sdk/__tests__/track-third-party-sharing.spec.ts index e7ab5a7..5092dfe 100644 --- a/src/sdk/__tests__/track-third-party-sharing.spec.ts +++ b/src/sdk/__tests__/track-third-party-sharing.spec.ts @@ -3,6 +3,9 @@ import { ThirdPartySharing } from '../track-third-party-sharing'; import * as Logger from '../logger' +import * as Queue from '../queue' + +/* eslint-disable @typescript-eslint/no-explicit-any */ jest.mock('../logger') @@ -10,6 +13,7 @@ describe('third party sharing functionality', () => { beforeAll(() => { jest.spyOn(Logger.default, 'warn') jest.spyOn(Logger.default, 'error') + jest.spyOn(Queue, 'push') }) afterAll(() => { @@ -132,4 +136,65 @@ describe('third party sharing functionality', () => { }) + describe('trackThirdPartySharing', () => { + it('attaches isEnabled to request parameters', () => { + const options = new ThirdPartySharing(true) + trackThirdPartySharing(options) + + expect(Queue.push).toHaveBeenCalledWith({ + url: "/third_party_sharing", + method: 'POST', + params: { + sharing: 'enable', + granularThirdPartySharingOptions: {}, + partnerSharingSettings: {} + } + }) + }) + + it('attaches granular options to request parameters', () => { + const options = new ThirdPartySharing(true) + options.addGranularOption('partner', 'key', 'value') + trackThirdPartySharing(options) + + expect(Queue.push).toHaveBeenCalledWith({ + url: "/third_party_sharing", + method: 'POST', + params: { + sharing: 'enable', + granularThirdPartySharingOptions: { partner: { key: "value"} }, + partnerSharingSettings: {} + } + }) + }) + + it('attaches partner sharing settings to request parameters', () => { + const options = new ThirdPartySharing(true) + options.addPartnerSharingSetting('partner', 'key', false) + trackThirdPartySharing(options) + + expect(Queue.push).toHaveBeenCalledWith({ + url: "/third_party_sharing", + method: 'POST', + params: { + sharing: 'enable', + granularThirdPartySharingOptions: {}, + partnerSharingSettings: { partner: { key: false} }, + } + }) + }) + + it.each([ + [undefined], + [null], + [''], + [[]], + [{}], + [{ hello: 'hi' }] + ])('logs an error message when no options provided', (options) => { + trackThirdPartySharing(options as any) + + expect(Logger.default.error).toHaveBeenCalledWith('Can not track third-party sharing without parameters') + }) + }) }) diff --git a/src/sdk/track-third-party-sharing.ts b/src/sdk/track-third-party-sharing.ts index b1d465c..91aacb8 100644 --- a/src/sdk/track-third-party-sharing.ts +++ b/src/sdk/track-third-party-sharing.ts @@ -63,6 +63,11 @@ export class ThirdPartySharing implements ThirdPartySharingOptions { } export function trackThirdPartySharing(adjustThirdPartySharing: ThirdPartySharingOptions) { + if (!adjustThirdPartySharing || adjustThirdPartySharing.isEnabled === undefined) { + Logger.error('Can not track third-party sharing without parameters') + return + } + const params = { sharing: adjustThirdPartySharing.isEnabled ? 'enable' : 'disable', granularThirdPartySharingOptions: adjustThirdPartySharing.granularOptions, From d62b81ae1ef00ef52cb740baac0a2f70b1804b3d Mon Sep 17 00:00:00 2001 From: Yara Matkova Date: Tue, 13 Aug 2024 10:30:12 +0200 Subject: [PATCH 14/18] style: fix lint issues --- src/sdk/__tests__/track-third-party-sharing.spec.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/sdk/__tests__/track-third-party-sharing.spec.ts b/src/sdk/__tests__/track-third-party-sharing.spec.ts index 5092dfe..a3d9331 100644 --- a/src/sdk/__tests__/track-third-party-sharing.spec.ts +++ b/src/sdk/__tests__/track-third-party-sharing.spec.ts @@ -142,7 +142,7 @@ describe('third party sharing functionality', () => { trackThirdPartySharing(options) expect(Queue.push).toHaveBeenCalledWith({ - url: "/third_party_sharing", + url: '/third_party_sharing', method: 'POST', params: { sharing: 'enable', @@ -158,11 +158,11 @@ describe('third party sharing functionality', () => { trackThirdPartySharing(options) expect(Queue.push).toHaveBeenCalledWith({ - url: "/third_party_sharing", + url: '/third_party_sharing', method: 'POST', params: { sharing: 'enable', - granularThirdPartySharingOptions: { partner: { key: "value"} }, + granularThirdPartySharingOptions: { partner: { key: 'value'} }, partnerSharingSettings: {} } }) @@ -174,7 +174,7 @@ describe('third party sharing functionality', () => { trackThirdPartySharing(options) expect(Queue.push).toHaveBeenCalledWith({ - url: "/third_party_sharing", + url: '/third_party_sharing', method: 'POST', params: { sharing: 'enable', From 443676d9f765ce0f7535e88c612357c73610c25b Mon Sep 17 00:00:00 2001 From: Yara Matkova Date: Tue, 13 Aug 2024 20:24:55 +0200 Subject: [PATCH 15/18] style: remove redundant argument --- .../track-third-party-sharing/track-third-party-sharing.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/demo/track-third-party-sharing/track-third-party-sharing.js b/src/demo/track-third-party-sharing/track-third-party-sharing.js index f72975e..3dc8061 100644 --- a/src/demo/track-third-party-sharing/track-third-party-sharing.js +++ b/src/demo/track-third-party-sharing/track-third-party-sharing.js @@ -16,9 +16,9 @@ function init() { _ui.enableTPS = document.getElementById('enable-tps') _ui.submitButton = _ui.tpsOptionsForm.querySelector('button[type="submit"]') - _ui.tpsOptionsForm.addEventListener('submit', _handleSave, false) - _ui.toggleButton.addEventListener('click', _handleToggle, false) - _ui.trackTPSButton.addEventListener('click', _handleTrackTPS, false) + _ui.tpsOptionsForm.addEventListener('submit', _handleSave) + _ui.toggleButton.addEventListener('click', _handleToggle) + _ui.trackTPSButton.addEventListener('click', _handleTrackTPS) _ui.enableTPS.addEventListener('change', () => { _tpsOptions.isEnabled = _ui.enableTPS.checked From e4f61e88e176195757fa7e490d6c2b9d822078f3 Mon Sep 17 00:00:00 2001 From: Yara Matkova Date: Tue, 13 Aug 2024 20:26:10 +0200 Subject: [PATCH 16/18] fix: add options as array of elements --- .../track-third-party-sharing.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/demo/track-third-party-sharing/track-third-party-sharing.js b/src/demo/track-third-party-sharing/track-third-party-sharing.js index 3dc8061..33b75b2 100644 --- a/src/demo/track-third-party-sharing/track-third-party-sharing.js +++ b/src/demo/track-third-party-sharing/track-third-party-sharing.js @@ -122,9 +122,11 @@ function _initGranularOptions() { const lastIndex = _tpsOptions.granularOptions.length if (_tpsOptions.granularOptions.length > 0) { + const options = [] for (let i = 0; i < _tpsOptions.granularOptions.length; i++) { - root.insertBefore(_createOption('granular', i, _tpsOptions.granularOptions[i]), addButton) + options.push(_createOption('granular', i, _tpsOptions.granularOptions[i])) } + addButton.before(...options) } root.insertBefore(_createOption('granular', lastIndex), addButton) @@ -156,9 +158,11 @@ function _initpartnerSharingSettings() { const lastIndex = _tpsOptions.partnerSharingSettings.length if (_tpsOptions.partnerSharingSettings.length > 0) { + const options = [] for (let i = 0; i < _tpsOptions.partnerSharingSettings.length; i++) { - root.insertBefore(_createOption('partner-sharing', i, _tpsOptions.partnerSharingSettings[i]), addButton) + options.push(_createOption('partner-sharing', i, _tpsOptions.partnerSharingSettings[i])) } + addButton.before(...options) } root.insertBefore(_createOption('partner-sharing', lastIndex), addButton) From bc9179d5aca79cae049c1992545c4682aa9e0980 Mon Sep 17 00:00:00 2001 From: Yara Matkova Date: Wed, 14 Aug 2024 02:11:38 +0200 Subject: [PATCH 17/18] feat: add component to create dynamic parameters --- src/assets/scss/index.scss | 1 + src/demo/dynamic-params/dynamic-params.js | 148 ++++++++++++++++++++ src/demo/dynamic-params/dynamic-params.scss | 20 +++ src/demo/utils.js | 5 + 4 files changed, 174 insertions(+) create mode 100644 src/demo/dynamic-params/dynamic-params.js create mode 100644 src/demo/dynamic-params/dynamic-params.scss diff --git a/src/assets/scss/index.scss b/src/assets/scss/index.scss index 8163bfe..a2fcac6 100644 --- a/src/assets/scss/index.scss +++ b/src/assets/scss/index.scss @@ -3,6 +3,7 @@ @import "flex-grid"; @import "../../demo/tabs/tabs"; @import "../../demo/key-value-params/key-value-params"; +@import "../../demo/dynamic-params/dynamic-params"; html, body { margin: 0; diff --git a/src/demo/dynamic-params/dynamic-params.js b/src/demo/dynamic-params/dynamic-params.js new file mode 100644 index 0000000..7fc6d7a --- /dev/null +++ b/src/demo/dynamic-params/dynamic-params.js @@ -0,0 +1,148 @@ +import { capitalize } from '../utils' + +const ACTION_SYMBOLS = { + add: '+', + remove: '×' +} + +function _createNode(name, classes = [], content, attrs = {}) { + const element = document.createElement(name) + + element.classList.add(...classes) + + if (content) { + element.appendChild(document.createTextNode(content)) + } + + Object.keys(attrs) + .map(key => [key, attrs[key]]) + .map(([key, value]) => element[key] = value) + + return element +} + +function _createInput(inputCls, label, value = '') { + const keyParent = _createNode('div', ['flex-box-column']) + keyParent.style.height = '45px' + const keyLabel = _createNode('label', ['flex-auto'], label) + const keyInput = _createNode('input', ['flex-one', inputCls], null, { type: 'text', value }) + + keyParent.appendChild(keyLabel) + keyParent.appendChild(keyInput) + + return keyParent +} + +function _append(parent, handle, isLast, fields, param = {}) { + const group = _createNode('div', ['flex-box-row', 'form-row-group']) + + const elements = [] + fields.forEach(field => { + elements.push(_createInput(`${field}-input`, capitalize(field), param[field])) + }) + group.append(...elements) + + const actionName = isLast ? 'add' : 'remove' + const action = _createNode('button', ['flex-auto', actionName], ACTION_SYMBOLS[actionName], { type: 'button' }) + + action.addEventListener('click', handle, false) + group.appendChild(action) + + parent.appendChild(group) +} + +const DynamicParams = (id, fields = ['key', 'value'], params = [], onChange) => { + const _id = id + const _fields = fields + const _params = params + let _parent = null + + function init() { + _parent = document.getElementById(_id) + + if (_params.length) { + _params.forEach((param, idx) => { + const isLast = idx === params.length - 1 + const handle = isLast ? _handleAdd : _handleRemove + _append(_parent, handle, isLast, _fields, param) + }) + } else { + _append(_parent, _handleAdd, true, _fields) + } + } + + function query() { + const rows = _parent.querySelectorAll('.form-row-group') + return Array.from(rows) + .map(row => { + const pairs = _fields + .map(field => [field, row.querySelector(`.${field}-input`).value]) + + if (pairs.some(([, value]) => value === '')) { + return null + } + + return pairs.reduce((acc, [key, value]) => ({ ...acc, [key]: value }), {}) + }) + .filter(row => !!row) + } + + function reset() { + Array.from(_parent.querySelectorAll('.form-row-group')) + .map(row => { + const action = row.querySelector('button') + const handle = action.classList.contains('add') ? _handleAdd : _handleRemove + action.removeEventListener('click', handle, false) + row.remove() + }) + + _append(_parent, _handleAdd, true, _fields) + } + + function _handleRemove(e) { + const action = e.target + action.removeEventListener('click', _handleRemove, false) + action.parentNode.remove() + + if (typeof onChange === 'function') { + onChange() + } + } + + function _handleAdd(e) { + const row = e.target.parentNode + + const pairs = _fields + .map(field => [field, row.querySelector(`.${field}-input`).value]) + + if (pairs.some(([, value]) => value === '')) { + return + } + + _append(_parent, _handleAdd, true, _fields) + _swapAction(row) + + if (typeof onChange === 'function') { + onChange() + } + } + + function _swapAction(parent) { + const removeAction = _createNode('button', ['flex-auto', 'remove'], ACTION_SYMBOLS.remove, { type: 'button' }) + const addAction = parent.querySelector('.add') + + removeAction.addEventListener('click', _handleRemove, false) + addAction.removeEventListener('click', _handleAdd, false) + + addAction.remove() + parent.appendChild(removeAction) + } + + return { + init, + query, + reset + } +} + +export default DynamicParams diff --git a/src/demo/dynamic-params/dynamic-params.scss b/src/demo/dynamic-params/dynamic-params.scss new file mode 100644 index 0000000..93993e9 --- /dev/null +++ b/src/demo/dynamic-params/dynamic-params.scss @@ -0,0 +1,20 @@ +.dynamic-params { + h3 { + font-weight: normal; + color: $fontColor; + font-size: 15px; + margin-bottom: 10px; + } + + .form-row-group { + margin-bottom: 10px; + align-items: end; + justify-content: space-between; + gap: 3px; + } + + button { + padding: 5px 10px; + margin-left: 5px; + } +} diff --git a/src/demo/utils.js b/src/demo/utils.js index 9213aa8..3307d58 100644 --- a/src/demo/utils.js +++ b/src/demo/utils.js @@ -2,6 +2,10 @@ function hyphenToCamelCase (string) { return string.replace(/(-\w)/g, ([, m]) => m.toUpperCase()) } +function capitalize(string) { + return string.charAt(0).toUpperCase() + string.slice(1); +} + function debounce (fn, wait = 500) { let timeout return function () { @@ -12,5 +16,6 @@ function debounce (fn, wait = 500) { export { hyphenToCamelCase, + capitalize, debounce } From ba278b53bf5596695798bc1d774b00abccab49d7 Mon Sep 17 00:00:00 2001 From: Yara Matkova Date: Wed, 14 Aug 2024 02:12:21 +0200 Subject: [PATCH 18/18] refac: use dynamic parameters component in third-party-sharing options in demo app --- .../track-third-party-sharing.html | 16 +- .../track-third-party-sharing.js | 184 ++---------------- 2 files changed, 28 insertions(+), 172 deletions(-) diff --git a/src/demo/track-third-party-sharing/track-third-party-sharing.html b/src/demo/track-third-party-sharing/track-third-party-sharing.html index eb14fa1..26c4e7b 100644 --- a/src/demo/track-third-party-sharing/track-third-party-sharing.html +++ b/src/demo/track-third-party-sharing/track-third-party-sharing.html @@ -8,20 +8,18 @@
-
+
-
- Granular options - -
+
+

Granular options

+
-
- Partner sharing settings - -
+
+

Partner sharing settings

+
diff --git a/src/demo/track-third-party-sharing/track-third-party-sharing.js b/src/demo/track-third-party-sharing/track-third-party-sharing.js index 33b75b2..db6509d 100644 --- a/src/demo/track-third-party-sharing/track-third-party-sharing.js +++ b/src/demo/track-third-party-sharing/track-third-party-sharing.js @@ -1,10 +1,13 @@ import Adjust from '../../sdk/main' import { getItem, setItem } from '../storage' +import DynamicParams from '../dynamic-params/dynamic-params' const _ui = {} let _tpsOptions = {} let _disabled = false let _timeoutId = null +let _granularOptions = null +let _partnerSharingSettings = null function init() { _tpsOptions = getItem('tpsOptions') || { isEnabled: true, granularOptions: [], partnerSharingSettings: [] } @@ -26,169 +29,26 @@ function init() { _setJson(_tpsOptions) }) - _initGranularOptions() - _initpartnerSharingSettings() + _granularOptions = DynamicParams('tps-granular', ['partnerName', 'key', 'value'], _tpsOptions.granularOptions, + () => { + _tpsOptions.granularOptions = _granularOptions.query() + _setJson(_tpsOptions) + setItem('tpsOptions', _tpsOptions) + }) + _granularOptions.init() + + _partnerSharingSettings = DynamicParams('tps-partner-sharing', ['partnerName', 'key', 'value'], _tpsOptions.partnerSharingSettings, + () => { + _tpsOptions.partnerSharingSettings = _partnerSharingSettings.query() + _setJson(_tpsOptions) + setItem('tpsOptions', _tpsOptions) + }) + _partnerSharingSettings.init() _setJson(_tpsOptions) } -function _createOptionsMarkup(idPrefix, option, onRemoveClick, onValueChange, ) { - const wrapper = document.createElement('div') - wrapper.id = idPrefix - wrapper.className = 'flex-box-row' - wrapper.style.justifyContent = 'space-between' - wrapper.style.alignItems = 'center' - wrapper.style.gap = '3px' - - const partner = document.createElement('div') - partner.className = 'form-row' - const partnerLabel = document.createElement('label') - partnerLabel.for = `${idPrefix}-partner` - partnerLabel.innerText = 'Partner' - const partnerInput = document.createElement('input') - partnerInput.id = `${idPrefix}-partner` - partnerInput.value = option ? option.partnerName : '' - partner.append(partnerLabel, partnerInput) - wrapper.append(partner) - - const key = document.createElement('div') - key.className = 'form-row' - const keyLabel = document.createElement('label') - keyLabel.for = `${idPrefix}-key` - keyLabel.innerText = 'Key' - const keyInput = document.createElement('input') - keyInput.id = `${idPrefix}-key` - keyInput.value = option ? option.key : '' - key.append(keyLabel, keyInput) - wrapper.append(key) - - const value = document.createElement('div') - value.className = 'form-row' - const valueLabel = document.createElement('label') - valueLabel.for = `${idPrefix}-value` - valueLabel.innerText = 'Value' - const valueInput = document.createElement('input') - valueInput.id = `${idPrefix}-value` - valueInput.value = option ? option.value : '' - valueInput.addEventListener('change', onValueChange) - value.append(valueLabel, valueInput) - wrapper.append(value) - - const removeButton = document.createElement('button') - removeButton.id = `${idPrefix}-remove` - removeButton.innerText = '-' - removeButton.addEventListener('click', (e) => { onRemoveClick(e); wrapper.remove(); }) - wrapper.append(removeButton) - - return wrapper -} - -function _createOption(type, index, option) { - const idPrefix = `tps-${type}-${index}` - - const onValueChange = (e) => { - if (type === 'granular') { - return - } - - if (e.target.value !== 'false') { - e.target.value = + e.target.value ? 'true' : 'false' - } - } - - const onRemove = (e) => { - e.preventDefault() - - if (!option) { - return - } - - const predicate = i => !(i.partnerName === option.partnerName && i.key === option.key) - if (type === 'granular') { - _tpsOptions.granularOptions = _tpsOptions.granularOptions.filter(predicate) - } else { - _tpsOptions.partnerSharingSettings = _tpsOptions.partnerSharingSettings.filter(predicate) - } - _setJson(_tpsOptions) - setItem('tpsOptions', _tpsOptions) - } - - return _createOptionsMarkup(idPrefix, option, onRemove, onValueChange) -} - -function _initGranularOptions() { - const root = _ui.tpsOptionsForm.querySelector('#tps-granular') - const addButton = _ui.tpsOptionsForm.querySelector('#tps-granular-add') - - const lastIndex = _tpsOptions.granularOptions.length - if (_tpsOptions.granularOptions.length > 0) { - const options = [] - for (let i = 0; i < _tpsOptions.granularOptions.length; i++) { - options.push(_createOption('granular', i, _tpsOptions.granularOptions[i])) - } - addButton.before(...options) - } - root.insertBefore(_createOption('granular', lastIndex), addButton) - - addButton.addEventListener('click', (e) => { - e.preventDefault() - - const childrenDivs = Array.from(root.children).filter(i => i.nodeName === 'DIV'); - const lastOption = childrenDivs[childrenDivs.length - 1] - const fields = lastOption.getElementsByTagName('input') - - // do nothing if any of properties has no value - if (fields[0].value === '' || fields[1].value === '' || fields[2].value === '') { - return; - } - - _tpsOptions.granularOptions.push({partnerName: fields[0].value, key: fields[1].value, value: fields[2].value }) - - _setJson(_tpsOptions) - setItem('tpsOptions', _tpsOptions) - - const lastIndex = +lastOption.id.substring('tps-granular-'.length) - root.insertBefore(_createOption('granular', lastIndex + 1), addButton) - }) -} - -function _initpartnerSharingSettings() { - const root = _ui.tpsOptionsForm.querySelector('#tps-partner-sharing') - const addButton = _ui.tpsOptionsForm.querySelector('#tps-partner-sharing-add') - - const lastIndex = _tpsOptions.partnerSharingSettings.length - if (_tpsOptions.partnerSharingSettings.length > 0) { - const options = [] - for (let i = 0; i < _tpsOptions.partnerSharingSettings.length; i++) { - options.push(_createOption('partner-sharing', i, _tpsOptions.partnerSharingSettings[i])) - } - addButton.before(...options) - } - root.insertBefore(_createOption('partner-sharing', lastIndex), addButton) - - addButton.addEventListener('click', (e) => { - e.preventDefault() - - const childrenDivs = Array.from(root.children).filter(i => i.nodeName === 'DIV'); - const lastOption = childrenDivs[childrenDivs.length - 1] - const fields = lastOption.getElementsByTagName('input') - - // do nothing if any of properties has no value - if (fields[0].value === '' || fields[1].value === '' || fields[2].value === '') { - return; - } - - _tpsOptions.partnerSharingSettings.push({partnerName: fields[0].value, key: fields[1].value, value: fields[2].value }) - - _setJson(_tpsOptions) - setItem('tpsOptions', _tpsOptions) - - const lastIndex = +lastOption.id.substring('tps-partner-sharing-'.length) - root.insertBefore(_createOption('partner-sharing', lastIndex + 1), addButton) - }) -} - -function trackThirdPartySharing(tpsOptions) { +function _trackThirdPartySharing(tpsOptions) { const options = new Adjust.ThirdPartySharing(tpsOptions.isEnabled) for (const option of tpsOptions.granularOptions) { @@ -220,7 +80,7 @@ function _handleSave(e) { _ui.submitButton.classList.remove('loading') _ui.submitButton.disabled = false - trackThirdPartySharing(_tpsOptions) + _trackThirdPartySharing(_tpsOptions) }, 1000) } @@ -239,7 +99,7 @@ function _handleTrackTPS() { _ui.trackTPSButton.classList.remove('loading') _ui.trackTPSButton.disabled = false - trackThirdPartySharing(_tpsOptions) + _trackThirdPartySharing(_tpsOptions) }, 1000) } @@ -264,8 +124,6 @@ function _setJson(tpsOptions) { text += 'Adjust.trackThirdPartySharing(options);' - console.log(text) - _ui.tpsOptionsJson.textContent = text }