diff --git a/live-connect-js-6.0.3-alpha-8f0b28c.0.tgz b/live-connect-js-6.0.3-alpha-8f0b28c.0.tgz
deleted file mode 100644
index 136fbb99..00000000
Binary files a/live-connect-js-6.0.3-alpha-8f0b28c.0.tgz and /dev/null differ
diff --git a/src/cache.ts b/src/cache.ts
index eaf245b5..11350f4f 100644
--- a/src/cache.ts
+++ b/src/cache.ts
@@ -1,6 +1,6 @@
import { strEqualsIgnoreCase, expiresInHours } from 'live-connect-common'
import { WrappedStorageHandler } from './handlers/storage-handler'
-import { StorageStrategies, StorageStrategy } from './model/storage-strategy'
+import { StorageStrategies } from './model/storage-strategy'
export type CacheRecord = {
data: string
@@ -19,20 +19,6 @@ export type StorageHandlerBackedCacheOpts = {
defaultExpirationHours?: number
}
-// export type MakeCacheOpts = StorageHandlerBackedCacheOpts & {
-// strategy: StorageStrategy,
-// }
-
-// export function makeCache(opts: MakeCacheOpts): DurableCache {
-// if (!strEqualsIgnoreCase(opts.strategy, StorageStrategies.cookie) && strEqualsIgnoreCase(opts.strategy, StorageStrategies.none)) {
-// return NoOpCache
-// } else {
-// // TODO: Remove once we validate config properly
-// const strategyWithDefault = opts.strategy ?? StorageStrategies.cookie
-// return new StorageHandlerBackedCache({ ...opts, strategy: strategyWithDefault })
-// }
-// }
-
export class StorageHandlerBackedCache implements DurableCache {
private handler
private storageStrategy
diff --git a/src/enrichers/cache.ts b/src/enrichers/cache.ts
index a0612046..23e25514 100644
--- a/src/enrichers/cache.ts
+++ b/src/enrichers/cache.ts
@@ -1,8 +1,8 @@
-import { strEqualsIgnoreCase } from "live-connect-common";
-import { DurableCache, NoOpCache, StorageHandlerBackedCache } from "../cache";
-import { WrappedStorageHandler } from "../handlers/storage-handler";
-import { StorageStrategies, StorageStrategy } from "../model/storage-strategy";
-import { Enricher } from "../types";
+import { strEqualsIgnoreCase } from 'live-connect-common'
+import { DurableCache, NoOpCache, StorageHandlerBackedCache } from '../cache'
+import { WrappedStorageHandler } from '../handlers/storage-handler'
+import { StorageStrategies, StorageStrategy } from '../model/storage-strategy'
+import { Enricher } from '../types'
type Input = { domain: string, storageHandler: WrappedStorageHandler, storageStrategy: StorageStrategy }
type Output = { cache: DurableCache }
@@ -16,13 +16,13 @@ export const enrichCache: Enricher = state => {
cache = new StorageHandlerBackedCache({
strategy: 'ls',
storageHandler: state.storageHandler,
- domain: state.domain,
+ domain: state.domain
})
} else {
cache = new StorageHandlerBackedCache({
strategy: 'cookie',
storageHandler: state.storageHandler,
- domain: state.domain,
+ domain: state.domain
})
}
return { ...state, cache }
diff --git a/src/enrichers/call-handler.ts b/src/enrichers/call-handler.ts
index 14ccf0a5..d5de5a98 100644
--- a/src/enrichers/call-handler.ts
+++ b/src/enrichers/call-handler.ts
@@ -1,6 +1,6 @@
-import { CallHandler, EventBus } from "live-connect-common"
-import { WrappedCallHandler } from "../handlers/call-handler"
-import { Enricher } from "../types"
+import { CallHandler, EventBus } from 'live-connect-common'
+import { WrappedCallHandler } from '../handlers/call-handler'
+import { Enricher } from '../types'
type Input = { callHandler: CallHandler, eventBus: EventBus }
type Output = { callHandler: WrappedCallHandler }
diff --git a/src/enrichers/decisions.ts b/src/enrichers/decisions.ts
index 4a6c3eb2..dd7ba1e8 100644
--- a/src/enrichers/decisions.ts
+++ b/src/enrichers/decisions.ts
@@ -1,70 +1,11 @@
-import { getQueryParameter, ParsedParam } from '../utils/url'
-import { trim, isUUID, expiresInDays } from 'live-connect-common'
-import { Enricher, EventBus, State } from '../types'
+import { Enricher, EventBus } from '../types'
import { WrappedStorageHandler } from '../handlers/storage-handler'
-
-const DEFAULT_DECISION_ID_COOKIE_EXPIRES = expiresInDays(30)
-const DECISION_ID_QUERY_PARAM_NAME = 'li_did'
-const DECISION_ID_COOKIE_NAMESPACE = 'lidids.'
-
-const _onlyUnique = (value: string, index: number, self: string[]) => self.indexOf(value) === index
-const _nonEmpty = (value: string) => value && trim(value).length > 0
+import { resolve } from '../manager/decisions'
type Input = { storageHandler: WrappedStorageHandler, eventBus: EventBus, pageUrl?: string, domain: string }
type Output = { decisionIds: string[] }
export const enrichDecisionIds: Enricher = state => {
- const { storageHandler, eventBus, pageUrl, domain } = state
- function _addDecisionId(key: string, cookieDomain?: string) {
- if (key) {
- storageHandler.setCookie(
- `${DECISION_ID_COOKIE_NAMESPACE}${key}`,
- key,
- DEFAULT_DECISION_ID_COOKIE_EXPIRES,
- 'Lax',
- cookieDomain)
- }
- }
-
- function _orElseEmtpy(errorDescription: string, f: () => A[]): A[] {
- try {
- return f()
- } catch (e) {
- eventBus.emitErrorWithMessage('DecisionsResolve', errorDescription, e)
- return []
- }
- }
-
- const freshDecisions = _orElseEmtpy(
- 'Error while extracting new decision ids',
- () => {
- const extractedFreshDecisions = ([] as ParsedParam[]).concat((pageUrl && getQueryParameter(pageUrl, DECISION_ID_QUERY_PARAM_NAME)) || [])
- return extractedFreshDecisions
- .map(trim)
- .filter(_nonEmpty)
- .filter(isUUID)
- .filter(_onlyUnique)
- }
- )
-
- const storedDecisions = _orElseEmtpy(
- 'Error while retrieving stored decision ids',
- () => {
- const extractedStoredDecisions = storageHandler.findSimilarCookies(DECISION_ID_COOKIE_NAMESPACE)
- return extractedStoredDecisions.map(trim)
- .filter(_nonEmpty)
- .filter(isUUID)
- .filter(_onlyUnique)
- }
- )
-
- freshDecisions.forEach(decision => {
- try {
- _addDecisionId(decision, domain)
- } catch (e) {
- eventBus.emitErrorWithMessage('DecisionsResolve', 'Error while storing new decision id', e)
- }
- })
-
- return { ...state, decisionIds: freshDecisions.concat(storedDecisions).filter(_onlyUnique) }
+ const { storageHandler, eventBus } = state
+ return { ...state, ...resolve(state, storageHandler, eventBus) }
}
diff --git a/src/enrichers/domain.ts b/src/enrichers/domain.ts
index 1fbe3875..6430092d 100644
--- a/src/enrichers/domain.ts
+++ b/src/enrichers/domain.ts
@@ -1,6 +1,6 @@
-import { WrappedStorageHandler } from "../handlers/storage-handler"
-import { Enricher } from "../types"
-import { determineHighestAccessibleDomain } from "../utils/domain"
+import { WrappedStorageHandler } from '../handlers/storage-handler'
+import { Enricher } from '../types'
+import { determineHighestAccessibleDomain } from '../utils/domain'
type Input = { storageHandler: WrappedStorageHandler }
type Output = { domain: string }
diff --git a/src/enrichers/error-pixel.ts b/src/enrichers/error-pixel.ts
deleted file mode 100644
index b3bb6d41..00000000
--- a/src/enrichers/error-pixel.ts
+++ /dev/null
@@ -1,10 +0,0 @@
-import { EventBus } from "live-connect-common";
-import { Enricher } from "../types";
-import { register } from "../events/error-pixel";
-import { WrappedCallHandler } from "../handlers/call-handler";
-
-type Input = { eventBus: EventBus, domain: string, callHandler: WrappedCallHandler }
-
-export const enrichErrorPixel: Enricher = ({ eventBus, domain, callHandler }) => {
- register(eventBus, domain, callHandler)
-}
diff --git a/src/enrichers/identifiers-nohash.ts b/src/enrichers/identifiers-nohash.ts
index df5b44ff..34b32879 100644
--- a/src/enrichers/identifiers-nohash.ts
+++ b/src/enrichers/identifiers-nohash.ts
@@ -1,10 +1,10 @@
import { containsEmailField, isEmail } from '../utils/email'
import { safeToString, isArray, trim } from 'live-connect-common'
-import { Enricher, EventBus, RetrievedIdentifier, State } from '../types'
+import { Enricher, EventBus, RetrievedIdentifier } from '../types'
import { WrappedReadOnlyStorageHandler } from '../handlers/storage-handler'
-type Input = {identifiersToResolve: string | string[], storageHandler: WrappedReadOnlyStorageHandler, eventBus: EventBus}
-type Output = {retrievedIdentifiers: RetrievedIdentifier[]}
+type Input = { identifiersToResolve: string | string[], storageHandler: WrappedReadOnlyStorageHandler, eventBus: EventBus }
+type Output = { retrievedIdentifiers: RetrievedIdentifier[] }
export const enrichIdentifiers: Enricher = state => {
try {
diff --git a/src/enrichers/identifiers.ts b/src/enrichers/identifiers.ts
index 637e308c..d785e98c 100644
--- a/src/enrichers/identifiers.ts
+++ b/src/enrichers/identifiers.ts
@@ -1,6 +1,6 @@
import { replaceEmailsWithHashes } from '../utils/email'
import { safeToString, isString, isArray } from 'live-connect-common'
-import { EventBus, HashedEmail, State, RetrievedIdentifier, Enricher } from '../types'
+import { EventBus, HashedEmail, RetrievedIdentifier, Enricher } from '../types'
import { WrappedReadOnlyStorageHandler } from '../handlers/storage-handler'
type Input = { identifiersToResolve: string | string[], storageHandler: WrappedReadOnlyStorageHandler, eventBus: EventBus }
diff --git a/src/enrichers/live-connect-id.ts b/src/enrichers/live-connect-id.ts
index d90df1f5..ae96916e 100644
--- a/src/enrichers/live-connect-id.ts
+++ b/src/enrichers/live-connect-id.ts
@@ -2,7 +2,7 @@ import { ulid } from '../utils/ulid'
import { domainHash } from '../utils/hash'
import { expiresInDays } from 'live-connect-common'
import { PEOPLE_VERIFIED_LS_ENTRY } from '../utils/consts'
-import { Enricher, EventBus } from '../types'
+import { Enricher } from '../types'
import { WrappedStorageHandler } from '../handlers/storage-handler'
import { DurableCache } from '../cache'
@@ -13,7 +13,7 @@ type Input = { expirationDays?: number, domain: string, cache: DurableCache, sto
type Output = { liveConnectId?: string, peopleVerifiedId?: string }
export const enrichLiveConnectId: Enricher = state => {
- const {expirationDays, domain, storageHandler, cache } = state
+ const { expirationDays, domain, storageHandler, cache } = state
const expiry = expirationDays || DEFAULT_EXPIRATION_DAYS
const oldValue = cache.get(NEXT_GEN_FP_NAME)?.data
@@ -21,8 +21,7 @@ export const enrichLiveConnectId: Enricher = state => {
if (oldValue) {
cache.set(NEXT_GEN_FP_NAME, oldValue, expiresInDays(expiry))
} else {
- const newValue = `${domainHash(domain)}--${ulid()}`
-
+ const newValue = `${domainHash(domain)}--${ulid()}`.toLocaleLowerCase()
cache.set(NEXT_GEN_FP_NAME, newValue, expiresInDays(expiry))
}
diff --git a/src/enrichers/page.ts b/src/enrichers/page.ts
index aaf6aae7..c5b39e24 100644
--- a/src/enrichers/page.ts
+++ b/src/enrichers/page.ts
@@ -12,4 +12,3 @@ export const enrichPage: Enricher = state => {
contextElements: getContextElements(state.privacyMode, state.contextSelectors, state.contextElementsLength)
}
}
-
diff --git a/src/enrichers/people-verified-id.ts b/src/enrichers/people-verified-id.ts
index 556916c6..199edeb7 100644
--- a/src/enrichers/people-verified-id.ts
+++ b/src/enrichers/people-verified-id.ts
@@ -16,4 +16,3 @@ export const enrichPeopleVerifiedId: Enricher = state => {
return state
}
}
-
diff --git a/src/enrichers/storage-handler.ts b/src/enrichers/storage-handler.ts
index 01142bd9..00e1a0a6 100644
--- a/src/enrichers/storage-handler.ts
+++ b/src/enrichers/storage-handler.ts
@@ -1,9 +1,9 @@
-import { EventBus, ReadOnlyStorageHandler, StorageHandler } from "live-connect-common";
-import { StorageStrategy } from "../model/storage-strategy";
-import { Enricher } from "../types";
-import { WrappedReadOnlyStorageHandler, WrappedStorageHandler } from "../handlers/storage-handler";
+import { EventBus, ReadOnlyStorageHandler, StorageHandler } from 'live-connect-common'
+import { StorageStrategy } from '../model/storage-strategy'
+import { Enricher } from '../types'
+import { WrappedReadOnlyStorageHandler, WrappedStorageHandler } from '../handlers/storage-handler'
-type InputReadOnly = { storageStrategy: StorageStrategy, storageHandler: ReadOnlyStorageHandler, eventBus: EventBus }
+type InputReadOnly = { storageStrategy: StorageStrategy, storageHandler: ReadOnlyStorageHandler, eventBus: EventBus }
type OutputReadOnly = { storageHandler: WrappedReadOnlyStorageHandler }
export const enrichReadOnlyStorageHandler: Enricher = state => {
@@ -11,7 +11,7 @@ export const enrichReadOnlyStorageHandler: Enricher = state => {
diff --git a/src/enrichers/storage-strategy.ts b/src/enrichers/storage-strategy.ts
index eba6863a..e15fc569 100644
--- a/src/enrichers/storage-strategy.ts
+++ b/src/enrichers/storage-strategy.ts
@@ -1,5 +1,5 @@
-import { StorageStrategies, StorageStrategy } from "../model/storage-strategy";
-import { Enricher } from "../types";
+import { StorageStrategies, StorageStrategy } from '../model/storage-strategy'
+import { Enricher } from '../types'
type Input = { privacyMode: boolean, storageStrategy?: StorageStrategy }
type Output = { storageStrategy: StorageStrategy }
diff --git a/src/events/error-pixel.ts b/src/events/error-pixel.ts
index 67ec486b..f0aa2264 100644
--- a/src/events/error-pixel.ts
+++ b/src/events/error-pixel.ts
@@ -1,9 +1,7 @@
import { PixelSender } from '../pixel/sender'
import { StateWrapper } from '../pixel/state'
-import * as page from '../enrichers/page'
import { ERRORS_CHANNEL, isRecord, isString } from 'live-connect-common'
import { EventBus, State } from '../types'
-import { WrappedCallHandler } from '../handlers/call-handler'
const MAX_ERROR_FIELD_LENGTH = 120
@@ -51,12 +49,10 @@ export function asErrorDetails(e: unknown): State {
}
}
-export function register(state: {}, callHandler: WrappedCallHandler, eventBus: EventBus): void {
+export function register(state: State, sender: PixelSender, eventBus: EventBus): void {
try {
- const pixelSender = new PixelSender(state, callHandler, eventBus)
-
eventBus.on(ERRORS_CHANNEL, (error) => {
- pixelSender.sendPixel(new StateWrapper(asErrorDetails(error), eventBus).combineWith(state || {}))
+ sender.sendPixel(new StateWrapper(asErrorDetails(error), eventBus).combineWith(state || {}))
})
} catch (e) {
console.error('handlers.error.register', e)
diff --git a/src/idex.ts b/src/idex.ts
index ec6c71bc..8adfc4a8 100644
--- a/src/idex.ts
+++ b/src/idex.ts
@@ -4,66 +4,27 @@ import { expiresInHours, isFunction, isObject } from 'live-connect-common'
import { asParamOrEmpty, asStringParamWhen, asStringParam, mapAsParams } from './utils/params'
import { DEFAULT_IDEX_AJAX_TIMEOUT, DEFAULT_IDEX_EXPIRATION_HOURS, DEFAULT_IDEX_URL, DEFAULT_REQUESTED_ATTRIBUTES } from './utils/consts'
import { IdentityResolutionConfig, State, ResolutionParams, EventBus, RetrievedIdentifier } from './types'
-import { WrappedStorageHandler } from './handlers/storage-handler'
import { WrappedCallHandler } from './handlers/call-handler'
+import { DurableCache, NoOpCache } from './cache'
-export type CacheRecord = {
- data: unknown
- expiresAt?: Date
-}
-
-interface Cache {
- get: (key: unknown) => CacheRecord | null // null will be used to signal missing value
- set: (key: unknown, value: unknown, expiration?: Date) => void
-}
-
-function storageHandlerBackedCache(defaultExpirationHours: number, domain: string | undefined, storageHandler: WrappedStorageHandler, eventBus: EventBus): Cache {
- const IDEX_STORAGE_KEY = '__li_idex_cache'
-
- function _cacheKey(rawKey: unknown) {
- if (rawKey) {
- const suffix = base64UrlEncode(JSON.stringify(rawKey))
- return `${IDEX_STORAGE_KEY}_${suffix}`
- } else {
- return IDEX_STORAGE_KEY
- }
- }
+const IDEX_STORAGE_KEY = '__li_idex_cache'
- return {
- get: (key) => {
- const cachedValue = storageHandler.get(_cacheKey(key))
- if (cachedValue) {
- return { data: JSON.parse(cachedValue.data), expiresAt: cachedValue.expiresAt }
- } else {
- return null
- }
- },
- set: (key, value, expiresAt) => {
- try {
- storageHandler.set(
- _cacheKey(key),
- JSON.stringify(value),
- expiresAt || expiresInHours(defaultExpirationHours),
- domain
- )
- } catch (ex) {
- eventBus.emitError('IdentityResolverStorage', ex)
- }
- }
+function _cacheKey(rawKey: unknown) {
+ if (rawKey) {
+ const suffix = base64UrlEncode(JSON.stringify(rawKey))
+ return `${IDEX_STORAGE_KEY}_${suffix}`
+ } else {
+ return IDEX_STORAGE_KEY
}
}
-const noopCache: Cache = {
- get: () => null,
- set: () => undefined
-}
-
export class IdentityResolver {
eventBus: EventBus
calls: WrappedCallHandler
- cache: Cache
+ cache: DurableCache
idexConfig: IdentityResolutionConfig
externalIds: RetrievedIdentifier[]
+ defaultExpirationHours: number
source: string
publisherId: number | string
url: string
@@ -71,12 +32,18 @@ export class IdentityResolver {
requestedAttributes: string[]
tuples: [string, string][]
- private constructor (config: State, calls: WrappedCallHandler, cache: Cache, eventBus: EventBus) {
+ private constructor (
+ config: State,
+ calls: WrappedCallHandler,
+ cache: DurableCache,
+ eventBus: EventBus
+ ) {
this.eventBus = eventBus
this.calls = calls
this.cache = cache
this.idexConfig = config.identityResolutionConfig || {}
this.externalIds = config.retrievedIdentifiers || []
+ this.defaultExpirationHours = this.idexConfig.expirationHours || DEFAULT_IDEX_EXPIRATION_HOURS
this.source = this.idexConfig.source || 'unknown'
this.publisherId = this.idexConfig.publisherId || 'any'
this.url = this.idexConfig.url || DEFAULT_IDEX_URL
@@ -108,18 +75,31 @@ export class IdentityResolver {
})
}
- static make(config: State, storageHandler: WrappedStorageHandler, calls: WrappedCallHandler, eventBus: EventBus): IdentityResolver {
- const nonNullConfig = config || {}
- const idexConfig = nonNullConfig.identityResolutionConfig || {}
- const expirationHours = idexConfig.expirationHours || DEFAULT_IDEX_EXPIRATION_HOURS
- const domain = nonNullConfig.domain
-
- const cache = storageHandlerBackedCache(expirationHours, domain, storageHandler, eventBus)
+ static make(
+ config: State,
+ cache: DurableCache,
+ calls: WrappedCallHandler,
+ eventBus: EventBus
+ ): IdentityResolver {
+ const nonNullConfig = config || { identityResolutionConfig: {} }
return new IdentityResolver(nonNullConfig, calls, cache, eventBus)
}
static makeNoCache(config: State, calls: WrappedCallHandler, eventBus: EventBus): IdentityResolver {
- return new IdentityResolver(config || {}, calls, noopCache, eventBus)
+ return IdentityResolver.make(config || {}, NoOpCache, calls, eventBus)
+ }
+
+ private getCached(key: unknown) {
+ const cachedValue = this.cache.get(_cacheKey(key))
+ if (cachedValue) {
+ return { data: JSON.parse(cachedValue.data), expiresAt: cachedValue.expiresAt }
+ } else {
+ return null
+ }
+ }
+
+ private setCached(key: unknown, value: unknown, expiresAt?: Date) {
+ this.cache.set(_cacheKey(key), JSON.stringify(value), expiresAt || expiresInHours(this.defaultExpirationHours))
}
private responseReceived(
@@ -139,13 +119,13 @@ export class IdentityResolver {
const expiresAt = responseExpires(response)
- this.cache.set(additionalParams, responseObj, expiresAt)
+ this.setCached(additionalParams, responseObj, expiresAt)
successCallback(responseObj, expiresAt)
}
}
unsafeResolve(successCallback: (result: unknown, expiresAt?: Date) => void, errorCallback: () => void, additionalParams: ResolutionParams): void {
- const cachedValue = this.cache.get(additionalParams)
+ const cachedValue = this.getCached(additionalParams)
if (cachedValue) {
successCallback(cachedValue.data, cachedValue.expiresAt)
} else {
@@ -164,12 +144,14 @@ export class IdentityResolver {
return `${this.url}/${this.source}/${this.publisherId}${params}`
}
- resolve(successCallback: (result: unknown, expiresAt?: Date) => void, errorCallback: () => void, additionalParams?: ResolutionParams): void {
+ resolve(successCallback: (result: unknown, expiresAt?: Date) => void, errorCallback?: () => void, additionalParams?: ResolutionParams): void {
try {
- this.unsafeResolve(successCallback, errorCallback, additionalParams || {})
+ this.unsafeResolve(successCallback, errorCallback || (() => {}), additionalParams || {})
} catch (e) {
console.error('IdentityResolve', e)
- errorCallback()
+ if (errorCallback && isFunction(errorCallback)) {
+ errorCallback()
+ }
if (this.eventBus) {
this.eventBus.emitError('IdentityResolve', e)
}
diff --git a/src/manager/decisions.ts b/src/manager/decisions.ts
new file mode 100644
index 00000000..a37cad6a
--- /dev/null
+++ b/src/manager/decisions.ts
@@ -0,0 +1,66 @@
+import { getQueryParameter, ParsedParam } from '../utils/url'
+import { trim, isUUID, expiresInDays } from 'live-connect-common'
+import { EventBus } from '../types'
+import { WrappedStorageHandler } from '../handlers/storage-handler'
+
+const DEFAULT_DECISION_ID_COOKIE_EXPIRES = expiresInDays(30)
+const DECISION_ID_QUERY_PARAM_NAME = 'li_did'
+const DECISION_ID_COOKIE_NAMESPACE = 'lidids.'
+
+const _onlyUnique = (value: string, index: number, self: string[]) => self.indexOf(value) === index
+const _nonEmpty = (value: string) => value && trim(value).length > 0
+
+export function resolve(state: { pageUrl?: string, domain?: string }, storageHandler: WrappedStorageHandler, eventBus: EventBus): { decisionIds: string[] } {
+ function _addDecisionId(key: string, cookieDomain?: string) {
+ if (key) {
+ storageHandler.setCookie(
+ `${DECISION_ID_COOKIE_NAMESPACE}${key}`,
+ key,
+ DEFAULT_DECISION_ID_COOKIE_EXPIRES,
+ 'Lax',
+ cookieDomain)
+ }
+ }
+
+ function _orElseEmtpy(errorDescription: string, f: () => A[]): A[] {
+ try {
+ return f()
+ } catch (e) {
+ eventBus.emitErrorWithMessage('DecisionsResolve', errorDescription, e)
+ return []
+ }
+ }
+
+ const freshDecisions = _orElseEmtpy(
+ 'Error while extracting new decision ids',
+ () => {
+ const extractedFreshDecisions = ([] as ParsedParam[]).concat((state.pageUrl && getQueryParameter(state.pageUrl, DECISION_ID_QUERY_PARAM_NAME)) || [])
+ return extractedFreshDecisions
+ .map(trim)
+ .filter(_nonEmpty)
+ .filter(isUUID)
+ .filter(_onlyUnique)
+ }
+ )
+
+ const storedDecisions = _orElseEmtpy(
+ 'Error while retrieving stored decision ids',
+ () => {
+ const extractedStoredDecisions = storageHandler.findSimilarCookies(DECISION_ID_COOKIE_NAMESPACE)
+ return extractedStoredDecisions.map(trim)
+ .filter(_nonEmpty)
+ .filter(isUUID)
+ .filter(_onlyUnique)
+ }
+ )
+
+ freshDecisions.forEach(decision => {
+ try {
+ _addDecisionId(decision, state.domain)
+ } catch (e) {
+ eventBus.emitErrorWithMessage('DecisionsResolve', 'Error while storing new decision id', e)
+ }
+ })
+
+ return { decisionIds: freshDecisions.concat(storedDecisions).filter(_onlyUnique) }
+}
diff --git a/src/pixel/fiddler.ts b/src/pixel/fiddler.ts
index 4b28b930..b1f2d5c3 100644
--- a/src/pixel/fiddler.ts
+++ b/src/pixel/fiddler.ts
@@ -79,11 +79,11 @@ export function mergeObjects(obj1: A, obj2:
export class EnrichmentContext {
data: A
- constructor(state: A) {
+ constructor (state: A) {
this.data = state
}
via(enricher: Enricher): EnrichmentContext {
- return new EnrichmentContext({...this.data, ...enricher(this.data)})
+ return new EnrichmentContext({ ...this.data, ...enricher(this.data) })
}
}
diff --git a/src/pixel/sender.ts b/src/pixel/sender.ts
index 0c711b9e..e317582d 100644
--- a/src/pixel/sender.ts
+++ b/src/pixel/sender.ts
@@ -1,6 +1,6 @@
import { isArray, isFunction } from 'live-connect-common'
import { asStringParam } from '../utils/params'
-import { LiveConnectConfig, EventBus, Enricher } from '../types'
+import { LiveConnectConfig, EventBus } from '../types'
import { StateWrapper } from './state'
import { WrappedCallHandler } from '../handlers/call-handler'
diff --git a/src/pixel/state.ts b/src/pixel/state.ts
index 6b3b7672..0b6ee90e 100644
--- a/src/pixel/state.ts
+++ b/src/pixel/state.ts
@@ -4,7 +4,7 @@ import { fiddle, mergeObjects } from './fiddler'
import { isObject, trim, isArray, nonNull } from 'live-connect-common'
import { asStringParam, asParamOrEmpty, asStringParamWhen, asStringParamTransform } from '../utils/params'
import { toParams } from '../utils/url'
-import { Enricher, EventBus, State } from '../types'
+import { EventBus, State } from '../types'
import { collectUrl } from './url-collector'
const noOpEvents = ['setemail', 'setemailhash', 'sethashedemail']
diff --git a/src/standard-live-connect.ts b/src/standard-live-connect.ts
index 6dc25f2d..cd579585 100644
--- a/src/standard-live-connect.ts
+++ b/src/standard-live-connect.ts
@@ -19,6 +19,7 @@ import { enrichDecisionIds } from './enrichers/decisions'
import { enrichLiveConnectId } from './enrichers/live-connect-id'
import { enrichCache } from './enrichers/cache'
import { enrichCallHandler } from './enrichers/call-handler'
+import { register as registerErrorPixel } from './events/error-pixel'
const hemStore: State = {}
function _pushSingleEvent (event: any, pixelClient: PixelSender, enrichedState: StateWrapper, eventBus: EventBus) {
@@ -114,6 +115,8 @@ function _standardInitialization (liveConnectConfig: LiveConnectConfig, external
const onPixelPreload = () => eventBus.emit(C.PRELOAD_PIXEL, '0')
const pixelClient = new PixelSender(enrichedState, enrichedState.callHandler, eventBus, onPixelLoad, onPixelPreload)
+ registerErrorPixel(enrichedState, pixelClient, eventBus)
+
const resolver = IdentityResolver.make(enrichedState, enrichedState.storageHandler, enrichedState.callHandler, eventBus)
const _push = (...args: any[]) => _processArgs(args, pixelClient, new StateWrapper(enrichedState, enrichedState.eventBus), eventBus)
diff --git a/src/utils/domain.ts b/src/utils/domain.ts
index 0e9b5cdd..07f389dc 100644
--- a/src/utils/domain.ts
+++ b/src/utils/domain.ts
@@ -1,5 +1,5 @@
-import { WrappedStorageHandler } from "../handlers/storage-handler"
-import { loadedDomain } from "./page"
+import { WrappedStorageHandler } from '../handlers/storage-handler'
+import { loadedDomain } from './page'
const TLD_CACHE_KEY = '_li_dcdm_c'
diff --git a/src/utils/wrapping.ts b/src/utils/wrapping.ts
index d8015cf5..6bf8cbd0 100644
--- a/src/utils/wrapping.ts
+++ b/src/utils/wrapping.ts
@@ -22,14 +22,14 @@ export class WrappingContext {
if (isObject(this.obj)) {
const member = this.obj[functionName]
if (isFunction(member)) {
- return ((...args) => {
+ return (...args) => {
try {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
return (member as any).call(this.obj, ...args)
} catch (e) {
this.eventBus.emitErrorWithMessage(this.name, `Failed calling ${functionName}`, e)
}
- })
+ }
}
}
this.errors.push(functionName)
diff --git a/test/unit/cache.spec.ts b/test/unit/cache.spec.ts
new file mode 100644
index 00000000..78892a3d
--- /dev/null
+++ b/test/unit/cache.spec.ts
@@ -0,0 +1,186 @@
+import jsdom from 'global-jsdom'
+import { expect, use } from 'chai'
+import { WrappedStorageHandler } from '../../src/handlers/storage-handler'
+import { DefaultStorageHandler } from 'live-connect-handlers'
+import sinon, { SinonStub } from 'sinon'
+import { EventBus, expiresInDays } from 'live-connect-common'
+import dirtyChai from 'dirty-chai'
+import { LocalEventBus } from '../../src/events/event-bus'
+import { StorageHandlerBackedCache } from '../../src/cache'
+import { StorageStrategy } from '../../src/model/storage-strategy'
+
+use(dirtyChai)
+
+type RecordedError = {
+ name: string
+ message: string
+ exception?: unknown
+}
+
+describe('StorageHandler', () => {
+ let errors: RecordedError[] = []
+ let eventBusStub: SinonStub<[string, string, unknown?], EventBus>
+ const eventBus = LocalEventBus()
+ const storage = new DefaultStorageHandler(eventBus)
+
+ const sandbox = sinon.createSandbox()
+ beforeEach(() => {
+ errors = []
+ jsdom('', {
+ url: 'http://www.something.example.com'
+ })
+
+ eventBusStub = sandbox.stub(eventBus, 'emitErrorWithMessage').callsFake((name, message, e) => {
+ errors.push({
+ name,
+ message,
+ exception: e
+ })
+ return eventBus
+ })
+ })
+
+ afterEach(() => {
+ eventBusStub.restore()
+ })
+
+ it('should use local storage', () => {
+ const storageHandler = WrappedStorageHandler.make('ls', storage, eventBus)
+ const cache = new StorageHandlerBackedCache({
+ strategy: 'ls',
+ storageHandler,
+ domain: 'example.com'
+ })
+
+ cache.set('key', 'value', expiresInDays(1))
+ expect(cache.get('key')?.data).to.be.eq('value')
+ expect(storageHandler.getDataFromLocalStorage('key')).to.be.eq('value')
+ expect(cache.get('unknownKey')).to.be.null()
+ })
+
+ it('should use cookies', () => {
+ const storageHandler = WrappedStorageHandler.make('cookie', storage, eventBus)
+ const cache = new StorageHandlerBackedCache({
+ strategy: 'cookie',
+ storageHandler,
+ domain: 'example.com'
+ })
+
+ cache.set('key', 'value', expiresInDays(1))
+ expect(cache.get('key')?.data).to.be.eq('value')
+ expect(storage.getCookie('key')).to.be.eq('value')
+ expect(cache.get('unknownKey')).to.be.null()
+ })
+
+ it('should use cookies when the strategy is not defined', () => {
+ const storageHandler = WrappedStorageHandler.make(null as unknown as StorageStrategy, storage, eventBus)
+ const cache = new StorageHandlerBackedCache({
+ strategy: null as unknown as 'cookie',
+ storageHandler,
+ domain: 'example.com'
+ })
+
+ cache.set('key', 'value', expiresInDays(1))
+ expect(cache.get('key')?.data).to.be.eq('value')
+ expect(storage.getCookie('key')).to.be.eq('value')
+ expect(cache.get('unknownKey')).to.be.null()
+ })
+
+ it('should return nothing when the underlying handler\'s strategy is none', () => {
+ const storageHandler = WrappedStorageHandler.make('none', storage, eventBus)
+ const cache = new StorageHandlerBackedCache({
+ strategy: 'cookie',
+ storageHandler,
+ domain: 'example.com'
+ })
+
+ cache.set('key', 'value', expiresInDays(1))
+ expect(cache.get('key')).to.be.null()
+ expect(storage.getCookie('key')).to.be.null()
+ expect(storage.getDataFromLocalStorage('key')).to.be.null()
+ expect(errors.length).to.be.eq(0)
+ })
+
+ it('should return nothing when the underlying handler\'s the strategy is disabled', () => {
+ const storageHandler = WrappedStorageHandler.make('disabled', storage, eventBus)
+ const cache = new StorageHandlerBackedCache({
+ strategy: 'cookie',
+ storageHandler,
+ domain: 'example.com'
+ })
+
+ cache.set('key_any', 'value_any', expiresInDays(1))
+ storageHandler.setDataInLocalStorage('key_ls', 'value_any')
+ storageHandler.setCookie('key_cookie', 'value_cookie', expiresInDays(1), 'Lax', 'example.com')
+
+ storageHandler.removeDataFromLocalStorage('key_ls')
+
+ expect(cache.get('key_any')).to.be.null()
+ expect(storageHandler.getDataFromLocalStorage('key_ls')).to.be.null()
+ expect(storageHandler.getCookie('key_cookie')).to.be.null()
+
+ expect(storageHandler.findSimilarCookies('key_cookie')).to.be.empty()
+ expect(storageHandler.localStorageIsEnabled()).to.be.false()
+ expect(errors.length).to.be.eq(0)
+ })
+
+ it('should return nothing when the strategy is ls and the time is in the past', () => {
+ const storageHandler = WrappedStorageHandler.make('ls', storage, eventBus)
+ const cache = new StorageHandlerBackedCache({
+ strategy: 'ls',
+ storageHandler,
+ domain: 'example.com'
+ })
+
+ cache.set('key', 'value', expiresInDays(-1))
+ expect(cache.get('key')).to.be.null()
+ expect(storage.getDataFromLocalStorage('key')).to.be.null()
+ })
+
+ it('should return nothing when the strategy is cookie and the time is in the past', () => {
+ const storageHandler = WrappedStorageHandler.make('cookie', storage, eventBus)
+ const cache = new StorageHandlerBackedCache({
+ strategy: 'cookie',
+ storageHandler,
+ domain: 'example.com'
+ })
+
+ cache.set('key', 'value', expiresInDays(-1))
+ expect(cache.get('key')).to.be.null()
+ expect(storage.getCookie('key')).to.be.null()
+ })
+
+ it('should update expiration when overwriting localstorage with expiration with one without', () => {
+ const storageHandler = WrappedStorageHandler.make('ls', storage, eventBus)
+ const cache = new StorageHandlerBackedCache({
+ strategy: 'ls',
+ storageHandler,
+ domain: 'example.com'
+ })
+
+ cache.set('key', 'value', expiresInDays(5))
+ cache.set('key', 'value', undefined)
+
+ const result = cache.get('key')
+
+ expect(result?.data).to.be.eq('value')
+ expect(result?.expiresAt).to.be.undefined()
+ })
+
+ it('should update expiration when overwriting a cookie with expiration with one without', () => {
+ const storageHandler = WrappedStorageHandler.make('cookie', storage, eventBus)
+ const cache = new StorageHandlerBackedCache({
+ strategy: 'cookie',
+ storageHandler,
+ domain: 'example.com'
+ })
+
+ cache.set('key', 'value', expiresInDays(5))
+ cache.set('key', 'value', undefined)
+
+ const result = cache.get('key')
+
+ expect(result?.data).to.be.eq('value')
+ expect(result?.expiresAt).to.be.undefined()
+ })
+})
diff --git a/test/unit/enricher/domain.spec.ts b/test/unit/enricher/domain.spec.ts
new file mode 100644
index 00000000..eeb7b763
--- /dev/null
+++ b/test/unit/enricher/domain.spec.ts
@@ -0,0 +1,42 @@
+import { expect, use } from 'chai'
+import { enrichDomain } from '../../../src/enrichers/domain'
+import { DefaultStorageHandler } from 'live-connect-handlers'
+import jsdom from 'global-jsdom'
+import dirtyChai from 'dirty-chai'
+import { WrappedStorageHandler } from '../../../src/handlers/storage-handler'
+import { LocalEventBus } from '../../../src/events/event-bus'
+
+use(dirtyChai)
+
+const eventBus = LocalEventBus()
+const storage = new DefaultStorageHandler(eventBus)
+const storageHandler = WrappedStorageHandler.make('cookie', storage, eventBus)
+const input = { storageHandler }
+
+describe('TLD checker', () => {
+ beforeEach(() => jsdom('', {
+ url: 'http://subdomain.tests.example.com'
+ }))
+
+ it('should determine correct tld', () => {
+ const resolved = enrichDomain(input)
+ expect(resolved.domain).to.eq('.example.com')
+ })
+
+ it('should reuse the cached correct tld', () => {
+ storageHandler.setCookie('_li_dcdm_c', '.example.com')
+ const resolved = enrichDomain(input)
+ expect(resolved.domain).to.eq('.example.com')
+ })
+})
+
+describe('TLD on sub-domain', () => {
+ beforeEach(() => jsdom('', {
+ url: 'http://example.co.uk'
+ }))
+
+ it('should use the full domain', () => {
+ const resolved = enrichDomain(input)
+ expect(resolved.domain).to.eq('.example.co.uk')
+ })
+})
diff --git a/test/unit/enricher/identifiers-nohash.spec.ts b/test/unit/enricher/identifiers-nohash.spec.ts
index e9dfb5be..25f81d8f 100644
--- a/test/unit/enricher/identifiers-nohash.spec.ts
+++ b/test/unit/enricher/identifiers-nohash.spec.ts
@@ -1,15 +1,16 @@
import { expect, use } from 'chai'
-import * as identifiersEnricher from '../../../src/enrichers/identifiers-nohash'
+import { enrichIdentifiers } from '../../../src/enrichers/identifiers-nohash'
import jsdom from 'global-jsdom'
import { DefaultStorageHandler } from 'live-connect-handlers'
import sinon from 'sinon'
import dirtyChai from 'dirty-chai'
import { LocalEventBus } from '../../../src/events/event-bus'
+import { WrappedStorageHandler } from '../../../src/handlers/storage-handler'
use(dirtyChai)
const eventBus = LocalEventBus()
-const storage = new DefaultStorageHandler(eventBus)
+const storageHandler = WrappedStorageHandler.make('cookie', new DefaultStorageHandler(eventBus), eventBus)
const COOKIE_NAME = 'sample_cookie'
const SIMPLE_COOKIE1 = 'sample_value1'
const SIMPLE_COOKIE2 = 'sample_value2'
@@ -23,133 +24,106 @@ describe('IdentifiersNoHashEnricher', () => {
}))
afterEach(() => {
- storage.setCookie(COOKIE_NAME, '')
- storage.removeDataFromLocalStorage(COOKIE_NAME)
+ storageHandler.setCookie(COOKIE_NAME, '')
+ storageHandler.removeDataFromLocalStorage(COOKIE_NAME)
})
it('should return an empty result when the collecting identifiers config is not set', () => {
- const state = {}
- // @ts-expect-error
- const result = identifiersEnricher.enrich(state, storage)
- expect(result).to.eql({
- retrievedIdentifiers: []
- })
+ const result = enrichIdentifiers({ identifiersToResolve: [], storageHandler, eventBus })
+ expect(result.retrievedIdentifiers).to.eql([])
})
it('should return an empty result when the collecting identifiers config is set but there are no cookies', () => {
- const state = { identifiersToResolve: [COOKIE_NAME] }
- // @ts-expect-error
- const result = identifiersEnricher.enrich(state, storage)
- expect(result).to.eql({
- retrievedIdentifiers: []
- })
+ const state = { identifiersToResolve: [COOKIE_NAME], storageHandler, eventBus }
+ const result = enrichIdentifiers(state)
+ expect(result.retrievedIdentifiers).to.eql([])
})
it('should return the collected cookies when the identifiers config is a string', () => {
- storage.setCookie(COOKIE_NAME, SIMPLE_COOKIE1)
- const state = { identifiersToResolve: `random_name, ${COOKIE_NAME} ` }
-
- // @ts-expect-error
- const result = identifiersEnricher.enrich(state, storage)
-
- expect(result).to.eql({
- retrievedIdentifiers: [{
- name: COOKIE_NAME,
- value: SIMPLE_COOKIE1
- }]
- })
+ storageHandler.setCookie(COOKIE_NAME, SIMPLE_COOKIE1)
+ const state = { identifiersToResolve: `random_name, ${COOKIE_NAME} `, storageHandler, eventBus }
+
+ const result = enrichIdentifiers(state)
+
+ expect(result.retrievedIdentifiers).to.eql([{
+ name: COOKIE_NAME,
+ value: SIMPLE_COOKIE1
+ }])
})
it('should return the collected cookies', () => {
- storage.setCookie(COOKIE_NAME, SIMPLE_COOKIE1)
- const state = { identifiersToResolve: [COOKIE_NAME] }
-
- // @ts-expect-error
- const result = identifiersEnricher.enrich(state, storage)
-
- expect(result).to.eql({
- retrievedIdentifiers: [{
- name: COOKIE_NAME,
- value: SIMPLE_COOKIE1
- }]
- })
+ storageHandler.setCookie(COOKIE_NAME, SIMPLE_COOKIE1)
+ const state = { identifiersToResolve: [COOKIE_NAME], storageHandler, eventBus }
+
+ const result = enrichIdentifiers(state)
+
+ expect(result.retrievedIdentifiers).to.eql([{
+ name: COOKIE_NAME,
+ value: SIMPLE_COOKIE1
+ }])
})
it('should return the collected identifiers from local storage ', () => {
- storage.setDataInLocalStorage(COOKIE_NAME, SIMPLE_COOKIE2)
- const state = { identifiersToResolve: [COOKIE_NAME] }
-
- // @ts-expect-error
- const result = identifiersEnricher.enrich(state, storage)
-
- expect(result).to.eql({
- retrievedIdentifiers: [{
- name: COOKIE_NAME,
- value: SIMPLE_COOKIE2
- }]
- })
+ storageHandler.setDataInLocalStorage(COOKIE_NAME, SIMPLE_COOKIE2)
+ const state = { identifiersToResolve: [COOKIE_NAME], storageHandler, eventBus }
+
+ const result = enrichIdentifiers(state)
+
+ expect(result.retrievedIdentifiers).to.eql([{
+ name: COOKIE_NAME,
+ value: SIMPLE_COOKIE2
+ }])
})
it('should prefer the cookie storage to the local storage', () => {
- storage.setCookie(COOKIE_NAME, SIMPLE_COOKIE1)
- storage.setDataInLocalStorage(COOKIE_NAME, SIMPLE_COOKIE2)
- const state = { identifiersToResolve: [COOKIE_NAME] }
-
- // @ts-expect-error
- const result = identifiersEnricher.enrich(state, storage)
-
- expect(result).to.eql({
- retrievedIdentifiers: [{
- name: COOKIE_NAME,
- value: SIMPLE_COOKIE1
- }]
- })
+ storageHandler.setCookie(COOKIE_NAME, SIMPLE_COOKIE1)
+ storageHandler.setDataInLocalStorage(COOKIE_NAME, SIMPLE_COOKIE2)
+ const state = { identifiersToResolve: [COOKIE_NAME], storageHandler, eventBus }
+
+ const result = enrichIdentifiers(state)
+
+ expect(result.retrievedIdentifiers).to.eql([{
+ name: COOKIE_NAME,
+ value: SIMPLE_COOKIE1
+ }])
})
it('should not return hashes when the cookie is an email', () => {
- storage.setCookie(COOKIE_NAME, EMAIL)
- const state = { identifiersToResolve: [COOKIE_NAME] }
+ storageHandler.setCookie(COOKIE_NAME, EMAIL)
+ const state = { identifiersToResolve: [COOKIE_NAME], storageHandler, eventBus }
- // @ts-expect-error
- const result = identifiersEnricher.enrich(state, storage)
+ const result = enrichIdentifiers(state)
- expect(result).to.eql({
- retrievedIdentifiers: []
- })
+ expect(result.retrievedIdentifiers).to.eql([])
})
it('should not return hashes when the cookie is a json with an email', () => {
- storage.setCookie(COOKIE_NAME, `"username":"${EMAIL}"`)
- const state = { identifiersToResolve: [COOKIE_NAME] }
+ storageHandler.setCookie(COOKIE_NAME, `"username":"${EMAIL}"`)
+ const state = { identifiersToResolve: [COOKIE_NAME], storageHandler, eventBus }
- // @ts-expect-error
- const result = identifiersEnricher.enrich(state, storage)
+ const result = enrichIdentifiers(state)
- expect(result).to.eql({
- retrievedIdentifiers: []
- })
+ expect(result.retrievedIdentifiers).to.eql([])
})
it('should not return multiple hashes when the cookie is a json with an email', () => {
- storage.setCookie(COOKIE_NAME, `"username":"${EMAIL}","username2":"${EMAIL2}"`)
- const state = { identifiersToResolve: [COOKIE_NAME] }
+ storageHandler.setCookie(COOKIE_NAME, `"username":"${EMAIL}","username2":"${EMAIL2}"`)
+ const state = { identifiersToResolve: [COOKIE_NAME], storageHandler, eventBus }
- // @ts-expect-error
- const result = identifiersEnricher.enrich(state, storage)
+ const result = enrichIdentifiers(state)
- expect(result).to.eql({
- retrievedIdentifiers: []
- })
+ expect(result.retrievedIdentifiers).to.eql([])
})
it('should emit an error and emit an empty result if cookies enrichment fails', () => {
- const getCookieStub = sandbox.stub(storage, 'getCookie').throws()
- storage.setCookie(COOKIE_NAME, SIMPLE_COOKIE1)
- const state = { identifiersToResolve: [COOKIE_NAME] }
- // @ts-expect-error
- const resolutionResult = identifiersEnricher.enrich(state, storage, eventBus)
+ const getCookieStub = sandbox.stub(storageHandler, 'getCookie').throws()
+ storageHandler.setCookie(COOKIE_NAME, SIMPLE_COOKIE1)
+ const state = { identifiersToResolve: [COOKIE_NAME], storageHandler, eventBus }
+
+ const resolutionResult = enrichIdentifiers(state)
+
+ expect(resolutionResult.retrievedIdentifiers).to.eql([])
- expect(resolutionResult).to.eql({})
getCookieStub.restore()
})
})
diff --git a/test/unit/enricher/identifiers.spec.ts b/test/unit/enricher/identifiers.spec.ts
index 601ab928..c139d280 100644
--- a/test/unit/enricher/identifiers.spec.ts
+++ b/test/unit/enricher/identifiers.spec.ts
@@ -1,5 +1,5 @@
import { expect, use } from 'chai'
-import * as identifiersEnricher from '../../../src/enrichers/identifiers'
+import { enrichIdentifiers } from '../../../src/enrichers/identifiers'
import jsdom from 'global-jsdom'
import { DefaultStorageHandler } from 'live-connect-handlers'
import sinon from 'sinon'
@@ -10,7 +10,7 @@ import { LocalEventBus } from '../../../src/events/event-bus'
use(dirtyChai)
const eventBus = LocalEventBus()
-const storage = WrappedStorageHandler.make('cookie', new DefaultStorageHandler(eventBus), eventBus)
+const storageHandler = WrappedStorageHandler.make('cookie', new DefaultStorageHandler(eventBus), eventBus)
const COOKIE_NAME = 'sample_cookie'
const SIMPLE_COOKIE1 = 'sample_value1'
@@ -35,174 +35,156 @@ describe('IdentifiersEnricher', () => {
}))
afterEach(() => {
- storage.setCookie(COOKIE_NAME, '')
- storage.removeDataFromLocalStorage(COOKIE_NAME)
+ storageHandler.setCookie(COOKIE_NAME, '')
+ storageHandler.removeDataFromLocalStorage(COOKIE_NAME)
})
it('should return an empty result when the collecting identifiers config is not set', () => {
- const state = {}
- // @ts-expect-error
- const result = identifiersEnricher.enrich(state, storage)
- expect(result).to.eql({
- retrievedIdentifiers: [],
- hashesFromIdentifiers: []
- })
+ const result = enrichIdentifiers({ identifiersToResolve: [], storageHandler, eventBus })
+
+ expect(result.retrievedIdentifiers).to.eql([])
+ expect(result.hashesFromIdentifiers).to.eql([])
})
it('should return an empty result when the collecting identifiers config is set but there are no cookies', () => {
- const state = { identifiersToResolve: [COOKIE_NAME] }
- // @ts-expect-error
- const result = identifiersEnricher.enrich(state, storage)
- expect(result).to.eql({
- retrievedIdentifiers: [],
- hashesFromIdentifiers: []
- })
+ const state = { identifiersToResolve: [COOKIE_NAME], storageHandler, eventBus }
+ const result = enrichIdentifiers(state)
+
+ expect(result.retrievedIdentifiers).to.eql([])
+ expect(result.hashesFromIdentifiers).to.eql([])
})
it('should return the collected cookies when the identifiers config is a string', () => {
- storage.setCookie(COOKIE_NAME, SIMPLE_COOKIE1)
- const state = { identifiersToResolve: `random_name, ${COOKIE_NAME} ` }
+ storageHandler.setCookie(COOKIE_NAME, SIMPLE_COOKIE1)
+ const state = { identifiersToResolve: `random_name, ${COOKIE_NAME} `, storageHandler, eventBus }
- // @ts-expect-error
- const result = identifiersEnricher.enrich(state, storage)
+ const result = enrichIdentifiers(state)
- expect(result).to.eql({
- retrievedIdentifiers: [{
- name: COOKIE_NAME,
- value: SIMPLE_COOKIE1
- }],
- hashesFromIdentifiers: []
- })
+ expect(result.retrievedIdentifiers).to.eql([{
+ name: COOKIE_NAME,
+ value: SIMPLE_COOKIE1
+ }])
+
+ expect(result.hashesFromIdentifiers).to.eql([])
})
it('should return the collected cookies', () => {
- storage.setCookie(COOKIE_NAME, SIMPLE_COOKIE1)
- const state = { identifiersToResolve: [COOKIE_NAME] }
+ storageHandler.setCookie(COOKIE_NAME, SIMPLE_COOKIE1)
+ const state = { identifiersToResolve: [COOKIE_NAME], storageHandler, eventBus }
- // @ts-expect-error
- const result = identifiersEnricher.enrich(state, storage)
+ const result = enrichIdentifiers(state)
- expect(result).to.eql({
- retrievedIdentifiers: [{
- name: COOKIE_NAME,
- value: SIMPLE_COOKIE1
- }],
- hashesFromIdentifiers: []
- })
+ expect(result.retrievedIdentifiers).to.eql([{
+ name: COOKIE_NAME,
+ value: SIMPLE_COOKIE1
+ }])
+
+ expect(result.hashesFromIdentifiers).to.eql([])
})
it('should return the collected identifiers from local storage ', () => {
- storage.setDataInLocalStorage(COOKIE_NAME, SIMPLE_COOKIE2)
- const state = { identifiersToResolve: [COOKIE_NAME] }
+ storageHandler.setDataInLocalStorage(COOKIE_NAME, SIMPLE_COOKIE2)
+ const state = { identifiersToResolve: [COOKIE_NAME], storageHandler, eventBus }
- // @ts-expect-error
- const result = identifiersEnricher.enrich(state, storage)
+ const result = enrichIdentifiers(state)
- expect(result).to.eql({
- retrievedIdentifiers: [{
- name: COOKIE_NAME,
- value: SIMPLE_COOKIE2
- }],
- hashesFromIdentifiers: []
- })
+ expect(result.retrievedIdentifiers).to.eql([{
+ name: COOKIE_NAME,
+ value: SIMPLE_COOKIE2
+ }])
+
+ expect(result.hashesFromIdentifiers).to.eql([])
})
it('should prefer the cookie storage to the local storage', () => {
- storage.setCookie(COOKIE_NAME, SIMPLE_COOKIE1)
- storage.setDataInLocalStorage(COOKIE_NAME, SIMPLE_COOKIE2)
- const state = { identifiersToResolve: [COOKIE_NAME] }
+ storageHandler.setCookie(COOKIE_NAME, SIMPLE_COOKIE1)
+ storageHandler.setDataInLocalStorage(COOKIE_NAME, SIMPLE_COOKIE2)
+ const state = { identifiersToResolve: [COOKIE_NAME], storageHandler, eventBus }
- // @ts-expect-error
- const result = identifiersEnricher.enrich(state, storage)
+ const result = enrichIdentifiers(state)
- expect(result).to.eql({
- retrievedIdentifiers: [{
- name: COOKIE_NAME,
- value: SIMPLE_COOKIE1
- }],
- hashesFromIdentifiers: []
- })
+ expect(result.retrievedIdentifiers).to.eql([{
+ name: COOKIE_NAME,
+ value: SIMPLE_COOKIE1
+ }])
+
+ expect(result.hashesFromIdentifiers).to.eql([])
})
it('should return hashes when the cookie is an email', () => {
- storage.setCookie(COOKIE_NAME, EMAIL)
- const state = { identifiersToResolve: [COOKIE_NAME] }
+ storageHandler.setCookie(COOKIE_NAME, EMAIL)
+ const state = { identifiersToResolve: [COOKIE_NAME], storageHandler, eventBus }
- // @ts-expect-error
- const result = identifiersEnricher.enrich(state, storage)
+ const result = enrichIdentifiers(state)
- expect(result).to.eql({
- retrievedIdentifiers: [{
- name: COOKIE_NAME,
- value: EMAIL_HASHES.md5
- }],
- hashesFromIdentifiers: [EMAIL_HASHES]
- })
+ expect(result.retrievedIdentifiers).to.eql([{
+ name: COOKIE_NAME,
+ value: EMAIL_HASHES.md5
+ }])
+
+ expect(result.hashesFromIdentifiers).to.eql([EMAIL_HASHES])
})
it('should return hashes when the cookie is a json with an email', () => {
- storage.setCookie(COOKIE_NAME, `"username":"${EMAIL}"`)
- const state = { identifiersToResolve: [COOKIE_NAME] }
+ storageHandler.setCookie(COOKIE_NAME, `"username":"${EMAIL}"`)
+ const state = { identifiersToResolve: [COOKIE_NAME], storageHandler, eventBus }
- // @ts-expect-error
- const result = identifiersEnricher.enrich(state, storage)
+ const result = enrichIdentifiers(state)
- expect(result).to.eql({
- retrievedIdentifiers: [{
- name: COOKIE_NAME,
- value: `"username":"${EMAIL_HASHES.md5}"`
- }],
- hashesFromIdentifiers: [EMAIL_HASHES]
- })
+ expect(result.retrievedIdentifiers).to.eql([{
+ name: COOKIE_NAME,
+ value: `"username":"${EMAIL_HASHES.md5}"`
+ }])
+
+ expect(result.hashesFromIdentifiers).to.eql([EMAIL_HASHES])
})
it('should return multiple hashes when the cookie is a json with an email', () => {
- storage.setCookie(COOKIE_NAME, `"username":"${EMAIL}","username2":"${EMAIL2}"`)
- const state = { identifiersToResolve: [COOKIE_NAME] }
+ storageHandler.setCookie(COOKIE_NAME, `"username":"${EMAIL}","username2":"${EMAIL2}"`)
+ const state = { identifiersToResolve: [COOKIE_NAME], storageHandler, eventBus }
- // @ts-expect-error
- const result = identifiersEnricher.enrich(state, storage)
+ const result = enrichIdentifiers(state)
- expect(result).to.eql({
- retrievedIdentifiers: [{
- name: COOKIE_NAME,
- value: `"username":"${EMAIL_HASHES.md5}","username2":"${EMAIL2_HASHES.md5}"`
- }],
- hashesFromIdentifiers: [EMAIL_HASHES, EMAIL2_HASHES]
- })
+ expect(result.retrievedIdentifiers).to.eql([{
+ name: COOKIE_NAME,
+ value: `"username":"${EMAIL_HASHES.md5}","username2":"${EMAIL2_HASHES.md5}"`
+ }])
+
+ expect(result.hashesFromIdentifiers).to.eql([EMAIL_HASHES, EMAIL2_HASHES])
})
it('should return cookies and deduplicated hashes', () => {
const COOKIE2_NAME = `${COOKIE_NAME}2`
- storage.setCookie(COOKIE_NAME, `"username":"${EMAIL}"`)
- storage.setDataInLocalStorage(COOKIE2_NAME, EMAIL)
- const state = { identifiersToResolve: [COOKIE_NAME, COOKIE2_NAME] }
-
- // @ts-expect-error
- const result = identifiersEnricher.enrich(state, storage)
-
- expect(result).to.eql({
- retrievedIdentifiers: [
- {
- name: COOKIE_NAME,
- value: `"username":"${EMAIL_HASHES.md5}"`
- },
- {
- name: COOKIE2_NAME,
- value: EMAIL_HASHES.md5
- }
- ],
- hashesFromIdentifiers: [EMAIL_HASHES]
- })
+ storageHandler.setCookie(COOKIE_NAME, `"username":"${EMAIL}"`)
+ storageHandler.setDataInLocalStorage(COOKIE2_NAME, EMAIL)
+
+ const state = { identifiersToResolve: [COOKIE_NAME, COOKIE2_NAME], storageHandler, eventBus }
+
+ const result = enrichIdentifiers(state)
+
+ expect(result.retrievedIdentifiers).to.eql([
+ {
+ name: COOKIE_NAME,
+ value: `"username":"${EMAIL_HASHES.md5}"`
+ },
+ {
+ name: COOKIE2_NAME,
+ value: EMAIL_HASHES.md5
+ }
+ ])
+
+ expect(result.hashesFromIdentifiers).to.eql([EMAIL_HASHES])
})
it('should emit an error and emit an empty result if cookies enrichment fails', () => {
- const getCookieStub = sandbox.stub(storage, 'getCookie').throws()
- storage.setCookie(COOKIE_NAME, SIMPLE_COOKIE1)
- const state = { identifiersToResolve: [COOKIE_NAME] }
- const resolutionResult = identifiersEnricher.enrich(state, storage, eventBus)
+ const getCookieStub = sandbox.stub(storageHandler, 'getCookie').throws()
+ storageHandler.setCookie(COOKIE_NAME, SIMPLE_COOKIE1)
+ const state = { identifiersToResolve: [COOKIE_NAME], storageHandler, eventBus }
+
+ const result = enrichIdentifiers(state)
- expect(resolutionResult).to.eql({})
+ expect(result.retrievedIdentifiers).to.eql([])
+ expect(result.hashesFromIdentifiers).to.eql([])
getCookieStub.restore()
})
})
diff --git a/test/unit/enricher/live-connect-id.spec.ts b/test/unit/enricher/live-connect-id.spec.ts
new file mode 100644
index 00000000..000f68d2
--- /dev/null
+++ b/test/unit/enricher/live-connect-id.spec.ts
@@ -0,0 +1,113 @@
+import { expect, use } from 'chai'
+import { enrichLiveConnectId } from '../../../src/enrichers/live-connect-id'
+import { DefaultStorageHandler } from 'live-connect-handlers'
+import sinon from 'sinon'
+import jsdom from 'global-jsdom'
+import dirtyChai from 'dirty-chai'
+import { WrappedStorageHandler } from '../../../src/handlers/storage-handler'
+import { LocalEventBus } from '../../../src/events/event-bus'
+import { NoOpCache, StorageHandlerBackedCache } from '../../../src/cache'
+import { StorageStrategies, StorageStrategy } from '../../../src/model/storage-strategy'
+import { expiresInHours } from 'live-connect-common'
+
+use(dirtyChai)
+
+function makeDeps(strategy: StorageStrategy = StorageStrategies.cookie) {
+ const domain = '.example.com'
+ const eventBus = LocalEventBus()
+ const storage = new DefaultStorageHandler(eventBus)
+ const storageHandler = WrappedStorageHandler.make(strategy, storage, eventBus)
+
+ let cache
+ if (strategy === 'cookie' || strategy === 'ls') {
+ cache = new StorageHandlerBackedCache({ strategy, storageHandler, domain })
+ } else {
+ cache = NoOpCache
+ }
+ return { storageHandler, cache, eventBus, domain }
+}
+
+describe('LiveConnectIdEnricher', () => {
+ sinon.createSandbox()
+
+ beforeEach(() => {
+ jsdom('', {
+ url: 'http://www.example.com'
+ })
+ })
+
+ it('should create a first party cookie if it doesn\'t exist', () => {
+ const deps = makeDeps('cookie')
+ const { storageHandler } = deps
+
+ expect(storageHandler.getCookie('_lc2_fpi')).to.eql(null)
+
+ const resolutionResult = enrichLiveConnectId(deps)
+
+ expect(storageHandler.getCookie('_lc2_fpi')).to.eql(resolutionResult.liveConnectId)
+ expect(storageHandler.getDataFromLocalStorage('_li_duid')).to.eql(resolutionResult.liveConnectId)
+ })
+
+ it('should create a first party cookie if it doesn\'t exist, and storage strategy is cookie', () => {
+ const deps = makeDeps('cookie')
+ const { storageHandler } = deps
+
+ expect(storageHandler.getCookie('_lc2_fpi')).to.eql(null)
+ const resolutionResult = enrichLiveConnectId(deps)
+ expect(storageHandler.getCookie('_lc2_fpi')).to.eql(resolutionResult.liveConnectId)
+ expect(storageHandler.getDataFromLocalStorage('_li_duid')).to.eql(resolutionResult.liveConnectId)
+ })
+
+ it('should create a first party identifier in local storage if it doesn\'t exist, and storage strategy is ls', () => {
+ const deps = makeDeps('ls')
+ const { storageHandler } = deps
+
+ expect(storageHandler.getDataFromLocalStorage('_lc2_fpi')).to.eql(null)
+
+ const resolutionResult = enrichLiveConnectId(deps)
+
+ expect(storageHandler.getDataFromLocalStorage('_lc2_fpi')).to.eql(resolutionResult.liveConnectId)
+ expect(storageHandler.getDataFromLocalStorage('_lc2_fpi_exp')).to.be.not.null()
+ expect(storageHandler.getDataFromLocalStorage('_li_duid')).to.eql(resolutionResult.liveConnectId)
+ })
+
+ it('should not create or return a first party identifier if the StorageStrategy is set to "none"', () => {
+ const deps = makeDeps('none')
+
+ const resolutionResult = enrichLiveConnectId(deps)
+
+ expect(resolutionResult.liveConnectId).to.be.undefined()
+ expect(resolutionResult.peopleVerifiedId).to.be.undefined()
+ })
+
+ it('should re-use a first party cookie if it exist', () => {
+ const deps = makeDeps('cookie')
+ const { storageHandler } = deps
+
+ const id = 'xxxxx'
+ storageHandler.setCookie('_lc2_fpi', id, expiresInHours(4), undefined, '.example.com')
+
+ const resolutionResult = enrichLiveConnectId(deps)
+ expect(storageHandler.getCookie('_lc2_fpi')).to.eql(id)
+ expect(resolutionResult.liveConnectId).to.eql(id)
+ })
+
+ it('should create a first party cookie that starts with apex domain hash', () => {
+ const deps = makeDeps('cookie')
+ const { storageHandler } = deps
+
+ enrichLiveConnectId(deps)
+
+ // apexOfExampleCom = '0caaf24ab1a0'
+ expect(storageHandler.getCookie('_lc2_fpi')).to.match(/0caaf24ab1a0--.*/)
+ })
+
+ it('should create a first party cookie that is lowercased', () => {
+ const deps = makeDeps('cookie')
+ const { storageHandler } = deps
+
+ enrichLiveConnectId(deps)
+
+ expect(storageHandler.getCookie('_lc2_fpi')).to.satisfy((cookie: string) => cookie === cookie.toLowerCase())
+ })
+})
diff --git a/test/unit/enricher/page.spec.ts b/test/unit/enricher/page.spec.ts
index 00284bb2..2f0e1f95 100644
--- a/test/unit/enricher/page.spec.ts
+++ b/test/unit/enricher/page.spec.ts
@@ -1,6 +1,6 @@
import jsdom from 'global-jsdom'
import { expect, use } from 'chai'
-import * as pageEnricher from '../../../src/enrichers/page'
+import { enrichPage } from '../../../src/enrichers/page'
import dirtyChai from 'dirty-chai'
use(dirtyChai)
@@ -26,15 +26,15 @@ describe('PageEnricher', () => {
document.body.appendChild(newP)
const state = {
+ privacyMode: false,
contextSelectors: 'h1,p',
contextElementsLength: 1000
}
const encodedContextElements = 'PGgxPlNvbWUgaGVhZGVyPC9oMT48cD5tYWlsdG86NTYzNGZmMTNmOTUzZWJjYjM3NGFjOGMzNDliY2ZjZmUsIGFsc28gZm91bmQ6IGYxMzdlM2QwOTg5ODc3ZWIzZjU3NWRjOWNkNmRmZDBkICE8L3A-'
- const result = pageEnricher.enrich(state)
- expect(result).to.eql({
- pageUrl: url,
- referrer,
- contextElements: encodedContextElements
- })
+ const result = enrichPage(state)
+
+ expect(result.pageUrl).to.eql(url)
+ expect(result.referrer).to.eql(referrer)
+ expect(result.contextElements).to.eql(encodedContextElements)
})
})
diff --git a/test/unit/enricher/privacy-config.spec.ts b/test/unit/enricher/privacy-config.spec.ts
index c09a2122..0fd633c8 100644
--- a/test/unit/enricher/privacy-config.spec.ts
+++ b/test/unit/enricher/privacy-config.spec.ts
@@ -1,7 +1,6 @@
import { expect, use } from 'chai'
-import * as pageEnricher from '../../../src/enrichers/privacy-config'
+import { enrichPrivacyMode } from '../../../src/enrichers/privacy-config'
import dirtyChai from 'dirty-chai'
-import { State } from '../../../src/types'
use(dirtyChai)
@@ -10,42 +9,35 @@ describe('PrivacyConfigEnricher', () => {
const state = {
gdprApplies: true
}
- const result = pageEnricher.enrich(state)
+ const result = enrichPrivacyMode(state)
- expect(result).to.eql({
- privacyMode: true
- })
+ expect(result.privacyMode).to.be.true()
})
it('should return empty enrichment when gdprApplies is undefined', () => {
- const state = {
- someTestField: 'a'
- } as State
- const result = pageEnricher.enrich(state)
+ const state = { someTestField: 'a', gdprApplies: undefined }
+
+ const result = enrichPrivacyMode(state)
- expect(result).to.eql({})
+ expect(result.privacyMode).to.be.false()
})
it('should return privacyMode set to true when gdprApplies is defined but is not a boolean value', () => {
const state = {
- gdprApplies: 'a'
+ gdprApplies: 'a' as unknown as boolean
}
- // @ts-expect-error
- const result = pageEnricher.enrich(state)
- expect(result).to.eql({
- privacyMode: true
- })
+ const result = enrichPrivacyMode(state)
+
+ expect(result.privacyMode).to.be.true()
})
it('should return privacyMode set to false when gdprApplies is false', () => {
const state = {
gdprApplies: false
}
- const result = pageEnricher.enrich(state)
+ const result = enrichPrivacyMode(state)
- expect(result).to.eql({
- privacyMode: false
- })
+ expect(result.privacyMode).to.be.false()
})
})
diff --git a/test/unit/events/error-pixel.spec.ts b/test/unit/events/error-pixel.spec.ts
index 094f2d4e..7bc4a27d 100644
--- a/test/unit/events/error-pixel.spec.ts
+++ b/test/unit/events/error-pixel.spec.ts
@@ -6,7 +6,6 @@ import { PixelSender } from '../../../src/pixel/sender'
import { LocalEventBus } from '../../../src/events/event-bus'
import { ERRORS_CHANNEL, EventBus } from 'live-connect-common'
import dirtyChai from 'dirty-chai'
-import { WrappedCallHandler } from '../../../src/handlers/call-handler'
import { StateWrapper } from '../../../src/pixel/state'
use(dirtyChai)
@@ -29,7 +28,9 @@ describe('ErrorPixel', () => {
})
it('should register itself on the global bus', () => {
- errorPixel.register({ collectorUrl: 'http://localhost' }, {} as WrappedCallHandler, eventBus)
+ const pixelSender = {} as PixelSender
+
+ errorPixel.register({ collectorUrl: 'http://localhost' }, pixelSender, eventBus)
// @ts-expect-error
const errorHandler = eventBus.data.h
expect(errorHandler).to.have.key(ERRORS_CHANNEL)
@@ -37,9 +38,14 @@ describe('ErrorPixel', () => {
})
it('should call the pixel once registered', () => {
- sandbox.stub(PixelSender.prototype, 'sendPixel').callsFake((data: StateWrapper) => errors.push(data))
+ const pixelSender = {
+ sendPixel: (data: StateWrapper) => errors.push(data)
+ } as unknown as PixelSender
- errorPixel.register({ collectorUrl: 'http://localhost' }, {} as WrappedCallHandler, eventBus)
+ errorPixel.register({
+ collectorUrl: 'http://localhost',
+ pageUrl: 'https://www.example.com/?sad=0&dsad=iou'
+ }, pixelSender, eventBus)
eventBus.emitErrorWithMessage('Error', 'some other message')
expect(errors.length).to.eql(1)
const errorDetails = errors[0].data.errorDetails
diff --git a/test/unit/handlers/storage-handler.spec.ts b/test/unit/handlers/storage-handler.spec.ts
index ee7d7efe..7d3295d1 100644
--- a/test/unit/handlers/storage-handler.spec.ts
+++ b/test/unit/handlers/storage-handler.spec.ts
@@ -1,9 +1,8 @@
import jsdom from 'global-jsdom'
import { expect, use } from 'chai'
import { WrappedStorageHandler } from '../../../src/handlers/storage-handler'
-import { DefaultStorageHandler } from 'live-connect-handlers'
import sinon, { SinonStub } from 'sinon'
-import { EventBus, expiresInDays } from 'live-connect-common'
+import { EventBus } from 'live-connect-common'
import dirtyChai from 'dirty-chai'
import { LocalEventBus } from '../../../src/events/event-bus'
@@ -19,7 +18,6 @@ describe('StorageHandler', () => {
let errors: RecordedError[] = []
let eventBusStub: SinonStub<[string, string, unknown?], EventBus>
const eventBus = LocalEventBus()
- const storage = new DefaultStorageHandler(eventBus)
const sandbox = sinon.createSandbox()
beforeEach(() => {
@@ -62,100 +60,4 @@ describe('StorageHandler', () => {
WrappedStorageHandler.make('disabled', {}, eventBus)
expect(errors.length).to.be.eq(0)
})
-
- it('should use local storage', () => {
- const storageHandler = WrappedStorageHandler.make('ls', storage, eventBus)
- storageHandler.set('key', 'value', expiresInDays(1), 'example.com')
- expect(storageHandler.get('key')?.data).to.be.eq('value')
- expect(storage.getDataFromLocalStorage('key')).to.be.eq('value')
- expect(storageHandler.get('unknownKey')).to.be.null()
- })
-
- it('should use cookies', () => {
- const storageHandler = WrappedStorageHandler.make('cookie', storage, eventBus)
- storageHandler.set('key', 'value', expiresInDays(1), 'example.com')
- expect(storageHandler.get('key')?.data).to.be.eq('value')
- expect(storage.getCookie('key')).to.be.eq('value')
- expect(storageHandler.get('unknownKey')).to.be.null()
- })
-
- it('should use cookies when the strategy is not defined', () => {
- // @ts-expect-error
- const storageHandler = WrappedStorageHandler.make(null, storage, eventBus)
- storageHandler.set('key', 'value', expiresInDays(1), 'example.com')
- expect(storageHandler.get('key')?.data).to.be.eq('value')
- expect(storage.getCookie('key')).to.be.eq('value')
- expect(storageHandler.get('unknownKey')).to.be.null()
- })
-
- it('should return nothing when the strategy is none', () => {
- const storageHandler = WrappedStorageHandler.make('none', storage, eventBus)
- storageHandler.set('key', 'value', expiresInDays(1), 'example.com')
- expect(storageHandler.get('key')).to.be.null()
- expect(storage.getCookie('key')).to.be.null()
- expect(storage.getDataFromLocalStorage('key')).to.be.null()
- expect(errors.length).to.be.eq(0)
- })
-
- it('should return nothing when the strategy is disabled', () => {
- const storageHandler = WrappedStorageHandler.make('disabled', storage, eventBus)
-
- storageHandler.set('key_any', 'value_any', expiresInDays(1), 'example.com')
- storageHandler.setDataInLocalStorage('key_ls', 'value_any')
- storageHandler.setCookie('key_cookie', 'value_cookie', expiresInDays(1), 'Lax', 'example.com')
-
- storageHandler.removeDataFromLocalStorage('key_ls')
-
- expect(storageHandler.get('key_any')).to.be.null()
- expect(storageHandler.getDataFromLocalStorage('key_ls')).to.be.null()
- expect(storageHandler.getCookie('key_cookie')).to.be.null()
-
- expect(storageHandler.findSimilarCookies('key_cookie')).to.be.empty()
- expect(storageHandler.localStorageIsEnabled()).to.be.false()
- expect(errors.length).to.be.eq(0)
- })
-
- it('should return nothing when the strategy is ls and the time is in the past', () => {
- const storageHandler = WrappedStorageHandler.make('ls', storage, eventBus)
- storageHandler.set('key', 'value', expiresInDays(-1), 'example.com')
- expect(storageHandler.get('key')).to.be.null()
- expect(storage.getDataFromLocalStorage('key')).to.be.null()
- })
-
- it('should return nothing when the strategy is cookie and the time is in the past', () => {
- const storageHandler = WrappedStorageHandler.make('cookie', storage, eventBus)
- storageHandler.set('key', 'value', expiresInDays(-1), 'example.com')
- expect(storageHandler.get('key')).to.be.null()
- expect(storage.getCookie('key')).to.be.null()
- })
-
- it('should return nothing when the strategy is undefined and the time is in the past', () => {
- // @ts-expect-error
- const storageHandler = WrappedStorageHandler.make(null, storage, eventBus)
- storageHandler.set('key', 'value', expiresInDays(-1), 'example.com')
- expect(storageHandler.get('key')).to.be.null()
- expect(storage.getCookie('key')).to.be.null()
- })
-
- it('should update expiration when overwriting localstorage with expiration with one without', () => {
- const storageHandler = WrappedStorageHandler.make('ls', storage, eventBus)
- storageHandler.set('key', 'value', expiresInDays(5), 'example.com')
- storageHandler.set('key', 'value', undefined, 'example.com')
-
- const result = storageHandler.get('key')
-
- expect(result?.data).to.be.eq('value')
- expect(result?.expiresAt).to.be.undefined()
- })
-
- it('should update expiration when overwriting a cookie with expiration with one without', () => {
- const storageHandler = WrappedStorageHandler.make('cookie', storage, eventBus)
- storageHandler.set('key', 'value', expiresInDays(5), 'example.com')
- storageHandler.set('key', 'value', undefined, 'example.com')
-
- const result = storageHandler.get('key')
-
- expect(result?.data).to.be.eq('value')
- expect(result?.expiresAt).to.be.undefined()
- })
})
diff --git a/test/unit/idex/identity-resolver.spec.ts b/test/unit/idex/identity-resolver.spec.ts
index 15d10449..771491b2 100644
--- a/test/unit/idex/identity-resolver.spec.ts
+++ b/test/unit/idex/identity-resolver.spec.ts
@@ -1,5 +1,4 @@
// @ts-nocheck
-
import jsdom from 'global-jsdom'
import sinon from 'sinon'
import { expect, use } from 'chai'
@@ -9,6 +8,7 @@ import { LocalEventBus } from '../../../src/events/event-bus'
import dirtyChai from 'dirty-chai'
import { WrappedStorageHandler } from '../../../src/handlers/storage-handler'
import { WrappedCallHandler } from '../../../src/handlers/call-handler'
+import { StorageHandlerBackedCache } from '../../../src/cache'
use(dirtyChai)
@@ -20,6 +20,11 @@ describe('IdentityResolver', () => {
let errors = []
let callCount = 0
const storageHandler = WrappedStorageHandler.make('cookie', new DefaultStorageHandler(eventBus), eventBus)
+ const cache = new StorageHandlerBackedCache({
+ strategy: 'cookie',
+ storageHandler,
+ domain: 'example.com'
+ })
beforeEach(() => {
jsdom('', {
@@ -39,7 +44,7 @@ describe('IdentityResolver', () => {
it('should invoke callback on success, store the result in a cookie', (done) => {
const response = { id: 112233 }
- const identityResolver = IdentityResolver.make({}, storageHandler, calls, eventBus)
+ const identityResolver = IdentityResolver.make({}, cache, calls, eventBus)
const successCallback = (responseAsJson) => {
expect(callCount).to.be.eql(1)
expect(errors).to.be.empty()
@@ -57,7 +62,12 @@ describe('IdentityResolver', () => {
it('should invoke callback on success, if storing the result in a cookie fails', () => {
const setCookieStub = sinon.createSandbox().stub(storage, 'setCookie').throws()
const failedStorage = WrappedStorageHandler.make('cookie', storage, eventBus)
- const identityResolver = IdentityResolver.make({}, failedStorage, calls, eventBus)
+ const cache = new StorageHandlerBackedCache({
+ strategy: 'cookie',
+ storageHandler: failedStorage,
+ domain: 'example.com'
+ })
+ const identityResolver = IdentityResolver.make({}, cache, calls, eventBus)
let jsonResponse = null
const successCallback = (responseAsJson) => {
jsonResponse = responseAsJson
@@ -77,7 +87,7 @@ describe('IdentityResolver', () => {
it('should attach the duid', (done) => {
const response = { id: 112233 }
- const identityResolver = IdentityResolver.make({ peopleVerifiedId: '987' }, storageHandler, calls, eventBus)
+ const identityResolver = IdentityResolver.make({ peopleVerifiedId: '987' }, cache, calls, eventBus)
const successCallback = (responseAsJson) => {
expect(requestToComplete.url).to.eq('https://idx.liadm.com/idex/unknown/any?duid=987')
expect(errors).to.be.empty()
@@ -90,7 +100,7 @@ describe('IdentityResolver', () => {
it('should attach additional params', (done) => {
const response = { id: 112233 }
- const identityResolver = IdentityResolver.make({ peopleVerifiedId: '987' }, storageHandler, calls, eventBus)
+ const identityResolver = IdentityResolver.make({ peopleVerifiedId: '987' }, cache, calls, eventBus)
const successCallback = (responseAsJson) => {
expect(requestToComplete.url).to.eq('https://idx.liadm.com/idex/unknown/any?duid=987&key=value')
expect(errors).to.be.empty()
@@ -103,7 +113,7 @@ describe('IdentityResolver', () => {
it('should attach additional params with an array that should be serialized as repeated query', (done) => {
const response = { id: 112233 }
- const identityResolver = IdentityResolver.make({ peopleVerifiedId: '987' }, storageHandler, calls, eventBus)
+ const identityResolver = IdentityResolver.make({ peopleVerifiedId: '987' }, cache, calls, eventBus)
const successCallback = (responseAsJson) => {
expect(requestToComplete.url).to.eq('https://idx.liadm.com/idex/unknown/any?duid=987&qf=0.1&resolve=age&resolve=gender')
expect(errors).to.be.empty()
@@ -116,7 +126,7 @@ describe('IdentityResolver', () => {
it('should attach publisher id', (done) => {
const response = { id: 112233 }
- const identityResolver = IdentityResolver.make({ peopleVerifiedId: '987', identityResolutionConfig: { publisherId: 123 } }, storageHandler, calls, eventBus)
+ const identityResolver = IdentityResolver.make({ peopleVerifiedId: '987', identityResolutionConfig: { publisherId: 123 } }, cache, calls, eventBus)
const successCallback = (responseAsJson) => {
expect(requestToComplete.url).to.eq('https://idx.liadm.com/idex/unknown/123?duid=987&key=value')
expect(errors).to.be.empty()
@@ -129,7 +139,7 @@ describe('IdentityResolver', () => {
it('should attach the did', (done) => {
const response = { id: 112233 }
- const identityResolver = IdentityResolver.make({ distributorId: 'did-01er' }, storageHandler, calls, eventBus)
+ const identityResolver = IdentityResolver.make({ distributorId: 'did-01er' }, cache, calls, eventBus)
const successCallback = (responseAsJson) => {
expect(requestToComplete.url).to.eq('https://idx.liadm.com/idex/unknown/any?did=did-01er')
expect(errors).to.be.empty()
@@ -141,7 +151,7 @@ describe('IdentityResolver', () => {
})
it('should not attach an empty tuple', (done) => {
- const identityResolver = IdentityResolver.make({ peopleVerifiedId: null }, storageHandler, calls, eventBus)
+ const identityResolver = IdentityResolver.make({ peopleVerifiedId: null }, cache, calls, eventBus)
const successCallback = (responseAsJson) => {
expect(requestToComplete.url).to.eq('https://idx.liadm.com/idex/unknown/any')
expect(errors).to.be.empty()
@@ -166,7 +176,7 @@ describe('IdentityResolver', () => {
value: 'AnotherId'
}
]
- }, storageHandler, calls, eventBus)
+ }, cache, calls, eventBus)
const successCallback = (responseAsJson) => {
expect(requestToComplete.url).to.eq('https://idx.liadm.com/idex/unknown/any?duid=987&pubcid=exexex&some-id=AnotherId')
expect(errors).to.be.empty()
@@ -184,7 +194,7 @@ describe('IdentityResolver', () => {
privacyMode: false,
gdprConsent: 'gdprConsent',
usPrivacyString: 'usPrivacyString'
- }, storageHandler, calls, eventBus)
+ }, cache, calls, eventBus)
const successCallback = (responseAsJson) => {
expect(requestToComplete.url).to.eq('https://idx.liadm.com/idex/unknown/any?us_privacy=usPrivacyString&gdpr=0&gdpr_consent=gdprConsent')
expect(errors).to.be.empty()
@@ -202,7 +212,7 @@ describe('IdentityResolver', () => {
privacyMode: true,
gdprConsent: 'gdprConsent',
usPrivacyString: 'usPrivacyString'
- }, storageHandler, calls, eventBus)
+ }, cache, calls, eventBus)
const successCallback = (responseAsJson) => {
expect(requestToComplete.url).to.eq('https://idx.liadm.com/idex/unknown/any?us_privacy=usPrivacyString&gdpr=1&n3pc=1&gdpr_consent=gdprConsent')
expect(errors).to.be.empty()
@@ -214,7 +224,7 @@ describe('IdentityResolver', () => {
})
it('should return the default empty response and emit error if response is 500', (done) => {
- const identityResolver = IdentityResolver.make({}, storageHandler, calls, eventBus)
+ const identityResolver = IdentityResolver.make({}, cache, calls, eventBus)
const errorCallback = (error) => {
expect(error.message).to.match(/^Error during XHR call: 500, url/)
done()
@@ -227,7 +237,7 @@ describe('IdentityResolver', () => {
const responseMd5 = { id: 123 }
const responseSha1 = { id: 125 }
- const identityResolver = IdentityResolver.make({}, storageHandler, calls, eventBus)
+ const identityResolver = IdentityResolver.make({}, cache, calls, eventBus)
let jsonResponse = null
const successCallback = (responseAsJson) => {
jsonResponse = responseAsJson
@@ -261,7 +271,7 @@ describe('IdentityResolver', () => {
requestedAttributes: ['uid2', 'md5']
}
},
- storageHandler,
+ cache,
calls,
eventBus
)
@@ -287,7 +297,7 @@ describe('IdentityResolver', () => {
},
privacyMode: true
},
- storageHandler,
+ cache,
calls,
eventBus
)
@@ -308,13 +318,17 @@ describe('IdentityResolver', () => {
const response = { id: 112233 }
let recordedExpiresAt: Date
- const customStorage = WrappedStorageHandler.make('cookie', storage, eventBus)
- customStorage.set = (key, value, expiresAt, sameSite, domain) => {
+ const customCache = new StorageHandlerBackedCache({
+ strategy: 'cookie',
+ storageHandler,
+ domain: 'example.com'
+ })
+ customCache.set = (key, value, expiresAt) => {
recordedExpiresAt = expiresAt
- storageHandler.set(key, value, expiresAt, sameSite, domain)
+ cache.set(key, value, expiresAt)
}
- const identityResolver = IdentityResolver.make({}, customStorage, calls, eventBus)
+ const identityResolver = IdentityResolver.make({}, customCache, calls, eventBus)
const expiresAt = new Date()
expiresAt.setHours(expiresAt.getHours() + 12)
diff --git a/test/unit/manager/decisions.spec.ts b/test/unit/manager/decisions.spec.ts
index f8f3088d..4efd9bf5 100644
--- a/test/unit/manager/decisions.spec.ts
+++ b/test/unit/manager/decisions.spec.ts
@@ -49,7 +49,6 @@ describe('DecisionsManager for stored decisions', () => {
it('should not return empty values', () => {
storage.setCookie('lidids.', '')
const resolutionResult = decisions.resolve({}, storage, eventBus)
- // @ts-expect-error
expect(resolutionResult.decisionIds.length).to.eql(0)
})
diff --git a/test/unit/manager/identifiers.spec.ts b/test/unit/manager/identifiers.spec.ts
deleted file mode 100644
index 041c0195..00000000
--- a/test/unit/manager/identifiers.spec.ts
+++ /dev/null
@@ -1,115 +0,0 @@
-import { expect, use } from 'chai'
-import * as identifiers from '../../../src/manager/identifiers'
-import { DefaultStorageHandler } from 'live-connect-handlers'
-import sinon from 'sinon'
-import jsdom from 'global-jsdom'
-import dirtyChai from 'dirty-chai'
-import { WrappedStorageHandler } from '../../../src/handlers/storage-handler'
-import { LocalEventBus } from '../../../src/events/event-bus'
-
-use(dirtyChai)
-
-const eventBus = LocalEventBus()
-const storage = new DefaultStorageHandler(eventBus)
-const storageHandler = WrappedStorageHandler.make('cookie', storage, eventBus)
-
-describe('IdentifiersManager', () => {
- const sandbox = sinon.createSandbox()
-
- beforeEach(() => {
- jsdom('', {
- url: 'http://www.example.com'
- })
- })
-
- it('should create a first party cookie if it doesn\'t exist', () => {
- expect(storageHandler.getCookie('_lc2_fpi')).to.eql(null)
- const resolutionResult = identifiers.resolve({}, storageHandler, eventBus)
- expect(storageHandler.getCookie('_lc2_fpi')).to.eql(resolutionResult.liveConnectId)
- expect(storageHandler.getDataFromLocalStorage('_li_duid')).to.eql(resolutionResult.liveConnectId)
- })
-
- it('should create a first party cookie if it doesn\'t exist, and storage strategy is cookie', () => {
- expect(storageHandler.getCookie('_lc2_fpi')).to.eql(null)
- const resolutionResult = identifiers.resolve({}, storageHandler, eventBus)
- expect(storageHandler.getCookie('_lc2_fpi')).to.eql(resolutionResult.liveConnectId)
- expect(storageHandler.getDataFromLocalStorage('_li_duid')).to.eql(resolutionResult.liveConnectId)
- })
-
- it('should create a first party identifier in local storage if it doesn\'t exist, and storage strategy is ls', () => {
- expect(storageHandler.getDataFromLocalStorage('_lc2_fpi')).to.eql(null)
- const localStorage = WrappedStorageHandler.make('ls', storage, eventBus)
- const resolutionResult = identifiers.resolve({}, localStorage, eventBus)
- expect(storageHandler.getDataFromLocalStorage('_lc2_fpi')).to.eql(resolutionResult.liveConnectId)
- expect(storageHandler.getDataFromLocalStorage('_lc2_fpi_exp')).to.be.not.null()
- expect(storageHandler.getDataFromLocalStorage('_li_duid')).to.eql(resolutionResult.liveConnectId)
- })
-
- it('should not create or return a first party identifier if the StorageStrategy is set to "none"', () => {
- const storageNone = WrappedStorageHandler.make('none', storage, eventBus)
- const resolutionResult = identifiers.resolve({}, storageNone, eventBus)
- expect(resolutionResult).to.include({ domain: '.www.example.com' })
- })
-
- it('should return the domain', () => {
- const resolutionResult = identifiers.resolve({}, storageHandler, eventBus)
- expect(resolutionResult.domain).to.eql('.example.com')
- })
-
- it('should re-use a first party cookie if it exist', () => {
- const id = 'xxxxx'
- // @ts-expect-error
- storageHandler.setCookie('_lc2_fpi', id, 400, undefined, '.example.com')
- const resolutionResult = identifiers.resolve({}, storageHandler, eventBus)
- expect(storageHandler.getCookie('_lc2_fpi')).to.eql(id)
- expect(resolutionResult.liveConnectId).to.eql(id)
- })
-
- it('should emit an error if identifiers.resolve fails for some reason, return an empty object', () => {
- const stub = sandbox.stub(storage, 'getCookie').throws()
- const failedStorage = WrappedStorageHandler.make('cookie', storage, eventBus)
- const resolutionResult = identifiers.resolve({}, failedStorage, eventBus)
- expect(resolutionResult).to.eql({})
- stub.restore()
- })
-
- it('should create a first party cookie that starts with apex domain hash', () => {
- identifiers.resolve({}, storageHandler, eventBus)
- // apexOfExampleCom = '0caaf24ab1a0'
- expect(storageHandler.getCookie('_lc2_fpi')).to.match(/0caaf24ab1a0--.*/)
- })
-
- it('should create a first party cookie that is lowercased', () => {
- identifiers.resolve({}, storageHandler, eventBus)
- // @ts-expect-error
- expect(storageHandler.getCookie('_lc2_fpi')).to.satisfy(cookie => cookie === cookie.toLowerCase())
- })
-})
-
-describe('TLD checker', () => {
- beforeEach(() => jsdom('', {
- url: 'http://subdomain.tests.example.com'
- }))
-
- it('should determine correct tld', () => {
- const resolved = identifiers.resolve({}, storageHandler, eventBus)
- expect(resolved.domain).to.eq('.example.com')
- })
-
- it('should reuse the cached correct tld', () => {
- storageHandler.setCookie('_li_dcdm_c', '.example.com')
- const resolved = identifiers.resolve({}, storageHandler, eventBus)
- expect(resolved.domain).to.eq('.example.com')
- })
-})
-
-describe('TLD on sub-domain', () => {
- beforeEach(() => jsdom('', {
- url: 'http://example.co.uk'
- }))
-
- it('should use the full domain', () => {
- const resolved = identifiers.resolve({}, storageHandler, eventBus)
- expect(resolved.domain).to.eq('.example.co.uk')
- })
-})
diff --git a/test/unit/pixel/state.spec.ts b/test/unit/pixel/state.spec.ts
index 3d7143c7..d7a4875f 100644
--- a/test/unit/pixel/state.spec.ts
+++ b/test/unit/pixel/state.spec.ts
@@ -1,7 +1,7 @@
// @ts-nocheck
import { assert, expect, use } from 'chai'
import { hashEmail } from '../../../src/utils/hash'
-import { enrich as privacyConfig } from '../../../src/enrichers/privacy-config'
+import { enrichPrivacyMode } from '../../../src/enrichers/privacy-config'
import { StateWrapper } from '../../../src/pixel/state'
import { mergeObjects } from '../../../src/pixel/fiddler'
import dirtyChai from 'dirty-chai'
@@ -62,7 +62,7 @@ describe('EventComposition', () => {
gdprConsent: 'test-consent-string',
referrer: 'https://some.test.referrer.com'
}
- const event = new StateWrapper(mergeObjects(pixelData, privacyConfig(pixelData)))
+ const event = new StateWrapper(mergeObjects(pixelData, enrichPrivacyMode(pixelData)))
const expectedPairs = [
'aid=9898', // appId
@@ -114,7 +114,7 @@ describe('EventComposition', () => {
gdprConsent: 'test-consent-string',
referrer: 'https://some.test.referrer.com'
}
- const event = new StateWrapper(mergeObjects(pixelData, privacyConfig(pixelData)))
+ const event = new StateWrapper(mergeObjects(pixelData, enrichPrivacyMode(pixelData)))
const expectedPairs = [
'aid=9898', // appId
@@ -176,7 +176,7 @@ describe('EventComposition', () => {
gdprApplies: true,
gdprConsent: 'some-string'
}
- const event = new StateWrapper(mergeObjects(pixelData, privacyConfig(pixelData)))
+ const event = new StateWrapper(mergeObjects(pixelData, enrichPrivacyMode(pixelData)))
const b64EncodedEventSource = 'eyJldmVudE5hbWUiOiJ2aWV3Q29udGVudCJ9'
expect(event.asQuery().toQueryString()).to.eql(`?se=${b64EncodedEventSource}&gdpr=1&n3pc=1&n3pct=1&nb=1&gdpr_consent=some-string`)
assert.includeDeepMembers(event.asTuples(), [['se', b64EncodedEventSource], ['gdpr', '1'], ['n3pc', '1'], ['n3pct', '1'], ['nb', '1'], ['gdpr_consent', 'some-string']])
@@ -188,7 +188,7 @@ describe('EventComposition', () => {
gdprApplies: false,
gdprConsent: 'some-string'
}
- const event = new StateWrapper(mergeObjects(pixelData, privacyConfig(pixelData)))
+ const event = new StateWrapper(mergeObjects(pixelData, enrichPrivacyMode(pixelData)))
const b64EncodedEventSource = 'eyJldmVudE5hbWUiOiJ2aWV3Q29udGVudCJ9'
expect(event.asQuery().toQueryString()).to.eql(`?se=${b64EncodedEventSource}&gdpr=0&gdpr_consent=some-string`)
assert.includeDeepMembers(event.asTuples(), [['se', b64EncodedEventSource], ['gdpr', '0'], ['gdpr_consent', 'some-string']])
@@ -211,7 +211,7 @@ describe('EventComposition', () => {
gdprConsent: undefined,
wrapperName: undefined
}
- const event = new StateWrapper(mergeObjects(pixelData, privacyConfig(pixelData)))
+ const event = new StateWrapper(mergeObjects(pixelData, enrichPrivacyMode(pixelData)))
expect(event.asQuery().toQueryString()).to.eql(`?tna=${trackerName}`)
assert.includeDeepMembers(event.asTuples(), [['tna', trackerName]])
})