@phantomstudios/ft-lib 0.4.1-rc1 → 0.5.0-rc2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. package/.github/CODEOWNERS +1 -1
  2. package/.github/dependabot.yml +11 -0
  3. package/.github/workflows/pr.yml +4 -1
  4. package/.github/workflows/release.yml +9 -2
  5. package/CODE_OF_CONDUCT.md +1 -1
  6. package/CONTRIBUTING.md +3 -5
  7. package/SUPPORT.md +3 -3
  8. package/eslint.config.mjs +91 -0
  9. package/jest.config.js +1 -6
  10. package/lib/FTTracking/index.js +1 -1
  11. package/lib/FTTracking/index.js.map +1 -1
  12. package/lib/cmp/loadFtCmp.d.ts +3 -0
  13. package/lib/cmp/loadFtCmp.d.ts.map +1 -0
  14. package/lib/cmp/loadFtCmp.js +31 -0
  15. package/lib/cmp/loadFtCmp.js.map +1 -0
  16. package/lib/consentMonitor/index.d.ts +12 -14
  17. package/lib/consentMonitor/index.d.ts.map +1 -1
  18. package/lib/consentMonitor/index.js +86 -73
  19. package/lib/consentMonitor/index.js.map +1 -1
  20. package/lib/gaTracker/index.js +1 -2
  21. package/lib/gaTracker/index.js.map +1 -1
  22. package/lib/index.d.ts +7 -3
  23. package/lib/index.d.ts.map +1 -1
  24. package/lib/index.js +7 -4
  25. package/lib/index.js.map +1 -1
  26. package/lib/oTracker/index.js.map +1 -1
  27. package/lib/permutiveVideoUtils/index.d.ts.map +1 -1
  28. package/lib/permutiveVideoUtils/index.js +1 -1
  29. package/lib/permutiveVideoUtils/index.js.map +1 -1
  30. package/lib/reactPlayerTracking/index.d.ts.map +1 -1
  31. package/lib/reactPlayerTracking/index.js +1 -1
  32. package/lib/reactPlayerTracking/index.js.map +1 -1
  33. package/lib/utils/attention.js.map +1 -1
  34. package/lib/utils/cookies.js +1 -2
  35. package/lib/utils/cookies.js.map +1 -1
  36. package/lib/utils/getTrace.js.map +1 -1
  37. package/lib/utils/intersectionObserverPolyfill.d.ts +1 -0
  38. package/lib/utils/intersectionObserverPolyfill.js +1 -0
  39. package/lib/utils/intersectionObserverPolyfill.js.map +1 -1
  40. package/lib/utils/scroll.js.map +1 -1
  41. package/lib/utils/yupValidator.d.ts.map +1 -1
  42. package/lib/utils/yupValidator.js +2 -2
  43. package/lib/utils/yupValidator.js.map +1 -1
  44. package/lib/ytIframeTracking/index.d.ts +0 -1
  45. package/lib/ytIframeTracking/index.d.ts.map +1 -1
  46. package/lib/ytIframeTracking/index.js.map +1 -1
  47. package/package.json +25 -17
  48. package/src/FTTracking/index.ts +2 -2
  49. package/src/cmp/loadFtCmp.ts +28 -0
  50. package/src/consentMonitor/index.ts +82 -90
  51. package/src/gaTracker/index.ts +4 -4
  52. package/src/index.ts +10 -3
  53. package/src/oTracker/index.ts +5 -5
  54. package/src/permutiveVideoUtils/index.ts +2 -2
  55. package/src/reactPlayerTracking/index.ts +4 -4
  56. package/src/types.d.ts +13 -5
  57. package/src/utils/attention.js +9 -9
  58. package/src/utils/getTrace.js +2 -2
  59. package/src/utils/scroll.ts +2 -2
  60. package/src/utils/yupValidator.ts +6 -6
  61. package/src/ytIframeTracking/index.ts +9 -9
  62. package/test/index.test.tsx +13 -12
  63. package/tsconfig.json +3 -1
  64. package/.eslintrc +0 -58
  65. package/.nvmrc +0 -1
@@ -1,120 +1,112 @@
1
1
  import Debug from "debug";
2
- const debug = Debug("@phntms/ft-lib");
2
+
3
+ import { enqueueCmpCallback, loadFtCmpScript } from "../cmp/loadFtCmp";
4
+
5
+ const debug = Debug("@phantomstudios/ft-lib/consentMonitor");
3
6
 
4
7
  const DEFAULT_DEV_HOSTS = ["localhost", "phq", ".app", "preview"];
5
8
 
6
- export class consentMonitor {
7
- protected _consent = false;
8
- protected _devHosts: string[];
9
- protected _isDevEnvironment = false;
10
- protected _hostname: string;
11
- protected _isInitialized = false;
9
+ interface ConsentReadyInfo {
10
+ consentedToAll: boolean;
11
+ }
12
+ type ConsentReadyHandler = (
13
+ legislation: string,
14
+ uuid: string,
15
+ tcData: unknown,
16
+ info: ConsentReadyInfo,
17
+ ) => void;
18
+ type MessageChoiceHandler = (
19
+ legislation: string,
20
+ choiceId: number,
21
+ choiceTypeId: number,
22
+ ) => void;
12
23
 
13
- constructor(hostname?: string, devHosts?: string[] | string) {
14
- if (Array.isArray(devHosts)) {
15
- this._devHosts = devHosts.concat(DEFAULT_DEV_HOSTS);
16
- } else if (devHosts === undefined) {
17
- this._devHosts = DEFAULT_DEV_HOSTS;
18
- } else {
19
- this._devHosts = DEFAULT_DEV_HOSTS;
20
- this._devHosts.push(devHosts);
21
- }
24
+ const CMP_CHOICE_ACCEPT_ALL = 11;
25
+ const CMP_CHOICE_REJECT_ALL = 13;
22
26
 
23
- this._hostname = hostname || window.location.hostname;
24
- this.init();
25
- }
27
+ export class ConsentMonitor {
28
+ private _consent = false;
29
+ private _devHosts: string[];
30
+ private _isDevEnvironment = false;
31
+ private _isInitialized = false;
26
32
 
27
- get consent(): boolean {
33
+ public get consent(): boolean {
28
34
  return this._consent;
29
35
  }
30
-
31
- get devHosts(): string[] | string {
36
+ public get devHosts(): string[] {
32
37
  return this._devHosts;
33
38
  }
34
-
35
- get isDevEnvironment(): boolean {
39
+ public get isDevEnvironment(): boolean {
36
40
  return this._isDevEnvironment;
37
41
  }
38
-
39
- get isInitialized(): boolean {
42
+ public get isInitialized(): boolean {
40
43
  return this._isInitialized;
41
44
  }
42
45
 
43
- getCookieValue = (name: string) =>
44
- document.cookie.match("(^|;)\\s*" + name + "\\s*=\\s*([^;]+)")?.pop() || "";
46
+ public get userHasConsented(): boolean {
47
+ return this._consent;
48
+ }
45
49
 
46
- init = () => {
47
- this.cookieConsentTest();
48
- setInterval(this.cookieConsentTest, 3000);
50
+ constructor(hostname?: string, devHosts?: string[] | string) {
51
+ if (Array.isArray(devHosts)) {
52
+ this._devHosts = [...devHosts, ...DEFAULT_DEV_HOSTS];
53
+ } else if (devHosts === undefined) {
54
+ this._devHosts = [...DEFAULT_DEV_HOSTS];
55
+ } else {
56
+ this._devHosts = [...DEFAULT_DEV_HOSTS, devHosts];
57
+ }
49
58
 
50
- //Simulate cookie consent behaviour in non-prod environments
51
- this._devHosts.map(
52
- (devHost) =>
53
- this._hostname.includes(devHost) && this.setDevCookieHandler()
59
+ const hostToCheck = hostname ?? window.location.hostname;
60
+ this._isDevEnvironment = this._devHosts.some((h) =>
61
+ hostToCheck.includes(h),
54
62
  );
55
- };
56
-
57
- cookieConsentTest = () => {
58
- if (window.permutive) {
59
- if (!this._isInitialized) {
60
- if (
61
- this.getCookieValue("FTConsent").includes("behaviouraladsOnsite%3Aon")
62
- ) {
63
- this.permutiveConsentOn();
63
+
64
+ loadFtCmpScript()
65
+ .then(() => {
66
+ this.attachCmpListeners();
67
+ this._isInitialized = true;
68
+ })
69
+ .catch((err) => console.error(err));
70
+ }
71
+
72
+ private attachCmpListeners(): void {
73
+ enqueueCmpCallback(() => {
74
+ const onReady: ConsentReadyHandler = (_l, _u, _t, info) => {
75
+ debug("onConsentReady:", info);
76
+ if (info.consentedToAll) {
77
+ this.enablePermutive();
64
78
  } else {
65
- this.permutiveConsentOff();
79
+ this.disablePermutive();
66
80
  }
67
- this._isInitialized = true;
68
- } else if (
69
- this.getCookieValue("FTConsent").includes(
70
- "behaviouraladsOnsite%3Aon"
71
- ) &&
72
- !this.consent
73
- ) {
74
- debug("setting permutive tracking consent: on");
75
- this.permutiveConsentOn();
76
- } else if (
77
- !this.getCookieValue("FTConsent").includes(
78
- "behaviouraladsOnsite%3Aon"
79
- ) &&
80
- this.consent
81
- ) {
82
- debug("setting permutive tracking consent: off");
83
- this.permutiveConsentOff();
84
- }
85
- }
86
- };
87
-
88
- setDevCookieHandler = () => {
89
- this._isDevEnvironment = true;
90
- debug("setting development environment from host match");
91
- const oCookieMessage =
92
- document.getElementsByClassName("o-cookie-message")[0];
93
- if (oCookieMessage) {
94
- const onCookieMessageAct = () => {
95
- debug("setting development FT consent cookies");
96
- document.cookie = "FTConsent=behaviouraladsOnsite%3Aon";
97
- document.cookie = "FTCookieConsentGDPR=true";
98
- oCookieMessage.removeEventListener(
99
- "oCookieMessage.act",
100
- onCookieMessageAct,
101
- false
102
- );
103
81
  };
104
- oCookieMessage.addEventListener("oCookieMessage.act", onCookieMessageAct);
105
- }
106
- };
107
82
 
108
- permutiveConsentOn = () => {
109
- window.permutive.consent({
83
+ const onChoice: MessageChoiceHandler = (_l, _c, typeId) => {
84
+ debug("onMessageChoiceSelect:", typeId);
85
+ if (typeId === CMP_CHOICE_ACCEPT_ALL) this.enablePermutive();
86
+ else if (typeId === CMP_CHOICE_REJECT_ALL) this.disablePermutive();
87
+ };
88
+
89
+ window._sp_.addEventListener?.("onConsentReady", onReady);
90
+ window._sp_.addEventListener?.("onMessageChoiceSelect", onChoice);
91
+ });
92
+ }
93
+
94
+ private enablePermutive(): void {
95
+ if (this._consent) return;
96
+ debug("Permutive consent: ON");
97
+ window.permutive?.consent({
110
98
  opt_in: true,
111
99
  token: "behaviouraladsOnsite:on",
112
100
  });
113
101
  this._consent = true;
114
- };
102
+ }
115
103
 
116
- permutiveConsentOff = () => {
117
- window.permutive.consent({ opt_in: false });
104
+ private disablePermutive(): void {
105
+ if (!this._consent) return;
106
+ debug("Permutive consent: OFF");
107
+ window.permutive?.consent({ opt_in: false });
118
108
  this._consent = false;
119
- };
109
+ }
120
110
  }
111
+
112
+ export { ConsentMonitor as consentMonitor };
@@ -55,13 +55,13 @@ export class gaTracker {
55
55
  this.GTMEventDispatcher(
56
56
  category,
57
57
  action,
58
- label || window.location.pathname //use pathname if label section has not been defined
58
+ label || window.location.pathname, //use pathname if label section has not been defined
59
59
  );
60
60
  } else {
61
61
  this.GtagEventDispatcher(
62
62
  category,
63
63
  action,
64
- label || window.location.pathname
64
+ label || window.location.pathname,
65
65
  );
66
66
  }
67
67
  }
@@ -70,7 +70,7 @@ export class gaTracker {
70
70
 
71
71
  setUpFTNavigationLinksTracking() {
72
72
  const FTNavigationLinks = Array.from(
73
- document.querySelectorAll(".o-header__drawer-menu-link")
73
+ document.querySelectorAll(".o-header__drawer-menu-link"),
74
74
  );
75
75
  if (FTNavigationLinks) {
76
76
  FTNavigationLinks.map((navLink) => {
@@ -119,7 +119,7 @@ export class gaTracker {
119
119
 
120
120
  setupFooterLinksTracking() {
121
121
  const FTFooterLinks = Array.from(
122
- document.querySelectorAll(".o-footer__legal-links li a")
122
+ document.querySelectorAll(".o-footer__legal-links li a"),
123
123
  );
124
124
  if (FTFooterLinks) {
125
125
  FTFooterLinks.forEach((footerLink) => {
package/src/index.ts CHANGED
@@ -1,8 +1,15 @@
1
- export { consentMonitor } from "./consentMonitor";
1
+ import { FTTracking, TrackingOptions } from "./FTTracking";
2
+ import { ConfigType, OrigamiEventType } from "./utils/yupValidator";
3
+
4
+ export { FTTracking };
5
+ export type { TrackingOptions };
6
+
7
+ export { loadFtCmpScript, enqueueCmpCallback } from "./cmp/loadFtCmp";
8
+
9
+ export { ConsentMonitor as consentMonitor } from "./consentMonitor";
2
10
  export { permutiveVideoUtils } from "./permutiveVideoUtils";
3
11
  export { reactPlayerTracking } from "./reactPlayerTracking";
4
12
  export { gaTracker } from "./gaTracker";
5
13
  export { oTracker } from "./oTracker";
6
14
  export { ytIframeTracking } from "./ytIframeTracking";
7
- export { FTTracking, TrackingOptions } from "./FTTracking";
8
- export { ConfigType, OrigamiEventType } from "./utils/yupValidator";
15
+ export type { ConfigType, OrigamiEventType };
@@ -21,12 +21,12 @@ export class oTracker {
21
21
  constructor(config: ConfigType, options: TrackingOptions) {
22
22
  this._config = parseConfig(
23
23
  config,
24
- options.disableAppFormatTransform || false
24
+ options.disableAppFormatTransform || false,
25
25
  );
26
26
  validateConfig(
27
27
  this._config,
28
28
  options.disableAppFormatTransform || false,
29
- options.logValidationErrors || false
29
+ options.logValidationErrors || false,
30
30
  );
31
31
  this._config.source_id = oTracking.getRootID();
32
32
  this.options = options;
@@ -68,7 +68,7 @@ export class oTracker {
68
68
  detail.app = this.config.app;
69
69
  detail.product = this.config.product;
70
70
  document.body.dispatchEvent(
71
- new CustomEvent("oTracking.event", { detail, bubbles: true })
71
+ new CustomEvent("oTracking.event", { detail, bubbles: true }),
72
72
  );
73
73
  } else {
74
74
  throw "Invalid event type";
@@ -92,7 +92,7 @@ export class oTracker {
92
92
  category: "brandedContent",
93
93
  },
94
94
  bubbles: true,
95
- })
95
+ }),
96
96
  );
97
97
  }
98
98
 
@@ -104,7 +104,7 @@ export class oTracker {
104
104
  (elm) =>
105
105
  elm instanceof HTMLElement &&
106
106
  elm.hasAttribute &&
107
- elm.hasAttribute("data-o-event")
107
+ elm.hasAttribute("data-o-event"),
108
108
  );
109
109
  if (trackableElement && trackableElement instanceof HTMLElement) {
110
110
  const data = trackableElement.getAttribute("data-o-event");
@@ -1,5 +1,5 @@
1
1
  import Debug from "debug";
2
- const debug = Debug("@phntms/ft-lib");
2
+ const debug = Debug("@phantomstudios/ft-lib");
3
3
  export class permutiveVideoUtils {
4
4
  protected progressPermutiveMilestones: number[] = [0, 0.25, 0.5, 0.75, 1];
5
5
  protected _campaign: string;
@@ -26,7 +26,7 @@ export class permutiveVideoUtils {
26
26
  emitPermutiveProgressEvent = (
27
27
  duration: number,
28
28
  currentTime: number,
29
- interval: any = undefined
29
+ interval: any = undefined,
30
30
  ) => {
31
31
  const progress = currentTime / (Math.floor(duration) - 1);
32
32
 
@@ -3,7 +3,7 @@ import Debug from "debug";
3
3
  import { FTTracking } from "../FTTracking";
4
4
  import { permutiveVideoUtils } from "../permutiveVideoUtils";
5
5
  import { OrigamiEventType } from "../utils/yupValidator";
6
- const debug = Debug("@phntms/ft-lib");
6
+ const debug = Debug("@phantomstudios/ft-lib");
7
7
  interface PlayerProgressEvent {
8
8
  played: number;
9
9
  loaded: number;
@@ -31,7 +31,7 @@ export class reactPlayerTracking {
31
31
  this.permutiveTracker = new permutiveVideoUtils(
32
32
  this.FTTracker.config.campaign,
33
33
  this.videoTitle,
34
- this.videoUrl
34
+ this.videoUrl,
35
35
  );
36
36
  }
37
37
 
@@ -79,7 +79,7 @@ export class reactPlayerTracking {
79
79
  this.playedSeconds = reactPlayerProgress.playedSeconds;
80
80
  if (this.duration > 0) {
81
81
  this.playedPercent = Math.floor(
82
- (Math.ceil(reactPlayerProgress.playedSeconds) / this.duration) * 100
82
+ (Math.ceil(reactPlayerProgress.playedSeconds) / this.duration) * 100,
83
83
  );
84
84
  }
85
85
 
@@ -99,7 +99,7 @@ export class reactPlayerTracking {
99
99
  debug("edit permutive progress event- " + this.playedSeconds);
100
100
  this.permutiveTracker?.emitPermutiveProgressEvent(
101
101
  this.duration,
102
- this.playedSeconds
102
+ this.playedSeconds,
103
103
  );
104
104
  };
105
105
  }
package/src/types.d.ts CHANGED
@@ -1,7 +1,20 @@
1
1
  declare module "@financial-times/o-tracking";
2
2
  declare module "@financial-times/o-viewport";
3
3
 
4
+ interface ConsentOptions {
5
+ opt_in?: boolean;
6
+ token?: string;
7
+ }
8
+ interface SourcepointCmpAPI {
9
+ queue?: Array<() => void>;
10
+ addEventListener?: (
11
+ eventName: string,
12
+
13
+ callback: (...args: any[]) => void,
14
+ ) => void;
15
+ }
4
16
  interface Window {
17
+ _sp_: SourcepointCmpAPI;
5
18
  dataLayer: any;
6
19
  gtag: any;
7
20
  permutive: {
@@ -13,11 +26,6 @@ interface Window {
13
26
  isOvideoPlayer?: boolean; //channels only
14
27
  }
15
28
 
16
- interface ConsentOptions {
17
- opt_in?: boolean;
18
- token?: string;
19
- }
20
-
21
29
  interface CallableConsent {
22
30
  ({}: ConsentOptions): void;
23
31
  }
@@ -31,13 +31,13 @@ export class Attention {
31
31
  //Add events for all the other Attention events
32
32
  for (let i = 0; i < ATTENTION_EVENTS.length; i++) {
33
33
  window.addEventListener(ATTENTION_EVENTS[i], (ev) =>
34
- this.startAttention(ev)
34
+ this.startAttention(ev),
35
35
  );
36
36
  }
37
37
 
38
38
  for (let i = 0; i < UNATTENTION_EVENTS.length; i++) {
39
39
  window.addEventListener(UNATTENTION_EVENTS[i], (ev) =>
40
- this.endAttention(ev)
40
+ this.endAttention(ev),
41
41
  );
42
42
  }
43
43
 
@@ -46,7 +46,7 @@ export class Attention {
46
46
  document.body.addEventListener(
47
47
  "oViewport.visibility",
48
48
  (ev) => this.handleVisibilityChange(ev),
49
- false
49
+ false,
50
50
  );
51
51
 
52
52
  this.addVideoEvents();
@@ -79,14 +79,14 @@ export class Attention {
79
79
  }
80
80
  this.attentionTimeout = setTimeout(
81
81
  () => this.endAttention(),
82
- ATTENTION_INTERVAL
82
+ ATTENTION_INTERVAL,
83
83
  );
84
84
  }
85
85
 
86
86
  startConstantAttention() {
87
87
  this.constantAttentionInterval = setInterval(
88
88
  () => this.startAttention(),
89
- ATTENTION_INTERVAL
89
+ ATTENTION_INTERVAL,
90
90
  );
91
91
  }
92
92
 
@@ -99,7 +99,7 @@ export class Attention {
99
99
  if (this.startAttentionTime) {
100
100
  this.endAttentionTime = new Date().getTime();
101
101
  this.totalAttentionTime += Math.round(
102
- (this.endAttentionTime - this.startAttentionTime) / 1000
102
+ (this.endAttentionTime - this.startAttentionTime) / 1000,
103
103
  );
104
104
  clearTimeout(this.attentionTimeout);
105
105
  this.startAttentionTime = null;
@@ -117,13 +117,13 @@ export class Attention {
117
117
  this.videoPlayers = document.getElementsByTagName("video");
118
118
  for (let i = 0; i < this.videoPlayers.length; i++) {
119
119
  this.videoPlayers[i].addEventListener("playing", (ev) =>
120
- this.startConstantAttention(ev)
120
+ this.startConstantAttention(ev),
121
121
  );
122
122
  this.videoPlayers[i].addEventListener("pause", (ev) =>
123
- this.endConstantAttention(ev)
123
+ this.endConstantAttention(ev),
124
124
  );
125
125
  this.videoPlayers[i].addEventListener("ended", (ev) =>
126
- this.endConstantAttention(ev)
126
+ this.endConstantAttention(ev),
127
127
  );
128
128
  }
129
129
  }
@@ -53,7 +53,7 @@ const getDomPathProps = (attrs, props) => {
53
53
  // Collect any attribute that matches given strings.
54
54
  attrs
55
55
  .filter((attribute) =>
56
- attribute.name.match(/^data-trackable|^data-o-|^aria-/i)
56
+ attribute.name.match(/^data-trackable|^data-o-|^aria-/i),
57
57
  )
58
58
  .forEach((attribute) => {
59
59
  props[attribute.name] = attribute.value;
@@ -104,7 +104,7 @@ function getTrace(el) {
104
104
  if (domPathProps["data-trackable"]) {
105
105
  domPathProps = Object.assign(
106
106
  domPathProps,
107
- getSiblingsAndPosition(el, originalEl, selector)
107
+ getSiblingsAndPosition(el, originalEl, selector),
108
108
  );
109
109
  }
110
110
 
@@ -56,7 +56,7 @@ export class ScrollTracker {
56
56
 
57
57
  intersectionCallback(
58
58
  observer: IntersectionObserver,
59
- changes: IntersectionObserverEntry[]
59
+ changes: IntersectionObserverEntry[],
60
60
  ) {
61
61
  changes.forEach((change) => {
62
62
  if (change.isIntersecting || change.intersectionRatio > 0) {
@@ -81,7 +81,7 @@ export class ScrollTracker {
81
81
  this.gaTracker.GtagEventDispatcher(
82
82
  "Scroll",
83
83
  `scroll - ${percentagesViewed}%`,
84
- window.location.pathname
84
+ window.location.pathname,
85
85
  );
86
86
  }
87
87
 
@@ -54,7 +54,7 @@ const configSchema = object({
54
54
  .required()
55
55
  .defined()
56
56
  .transform((value) =>
57
- unifyValuesTransform(value, disableAppFormatTransformValue)
57
+ unifyValuesTransform(value, disableAppFormatTransformValue),
58
58
  )
59
59
  .oneOf([
60
60
  "Stream",
@@ -112,14 +112,14 @@ const origamiEventSchema = object({
112
112
  .nullable()
113
113
  .notRequired()
114
114
  .when("category", {
115
- is: "video" || "audio",
115
+ is: (value: string) => value === "video" || "audio",
116
116
  then: () => number().required(),
117
117
  }),
118
118
  progress: number()
119
119
  .nullable()
120
120
  .notRequired()
121
121
  .when("category", {
122
- is: "video" || "audio",
122
+ is: (value: string) => value === "video" || "audio",
123
123
  then: () => number().required(),
124
124
  }),
125
125
  });
@@ -129,7 +129,7 @@ export type OrigamiEventType = InferType<typeof origamiEventSchema>;
129
129
 
130
130
  export const parseConfig = (
131
131
  config: ConfigType,
132
- disableAppFormatTransform: boolean
132
+ disableAppFormatTransform: boolean,
133
133
  ): ConfigType => {
134
134
  disableAppFormatTransformValue = disableAppFormatTransform;
135
135
  try {
@@ -155,7 +155,7 @@ export const parseConfig = (
155
155
  export const validateConfig = (
156
156
  config: ConfigType,
157
157
  disableAppFormatTransform: boolean,
158
- logValidationErrors: boolean
158
+ logValidationErrors: boolean,
159
159
  ): ValidationError[] | undefined => {
160
160
  disableAppFormatTransformValue = disableAppFormatTransform;
161
161
  try {
@@ -174,7 +174,7 @@ export const validateConfig = (
174
174
  };
175
175
 
176
176
  export const validateOrigamiEvent = (
177
- config: OrigamiEventType
177
+ config: OrigamiEventType,
178
178
  ): ValidationError[] | undefined => {
179
179
  try {
180
180
  const parsedConfig = origamiEventSchema.cast(config);
@@ -17,7 +17,7 @@ export class ytIframeTracking {
17
17
  this.videoUrl = videoUrl;
18
18
  this.permutiveUtils = new permutiveVideoUtils(
19
19
  this.FTTracker.config.campaign,
20
- this.videoTitle
20
+ this.videoTitle,
21
21
  );
22
22
  }
23
23
 
@@ -27,13 +27,13 @@ export class ytIframeTracking {
27
27
  emitProgressEvents = (
28
28
  progress: number,
29
29
  duration: number,
30
- isYoutube: boolean
30
+ isYoutube: boolean,
31
31
  ) => {
32
32
  while (progress >= this.progressMilestones[0]) {
33
33
  this.FTTracker.gaEvent(
34
34
  `Video${window.isOvideoPlayer ? ":fallback" : ""}`,
35
35
  `${this.progressMilestones[0]}% watched`,
36
- this.videoTitle
36
+ this.videoTitle,
37
37
  );
38
38
  if (isYoutube) {
39
39
  this.FTTracker.oEvent({
@@ -103,7 +103,7 @@ export class ytIframeTracking {
103
103
  this.permutiveUtils.emitPermutiveProgressEvent(
104
104
  duration - 1,
105
105
  currentTime,
106
- this.videoProgressInterval
106
+ this.videoProgressInterval,
107
107
  );
108
108
  const progress = this.progressPercentage(duration, currentTime);
109
109
  this.emitProgressEvents(progress, duration, true);
@@ -115,7 +115,7 @@ export class ytIframeTracking {
115
115
  this.FTTracker.gaEvent(
116
116
  `Video${window.isOvideoPlayer ? ":fallback" : ""}`,
117
117
  `playing`,
118
- this.videoTitle
118
+ this.videoTitle,
119
119
  );
120
120
  this.FTTracker.oEvent({
121
121
  category: "video",
@@ -131,7 +131,7 @@ export class ytIframeTracking {
131
131
  this.FTTracker.gaEvent(
132
132
  `Video${window.isOvideoPlayer ? ":fallback" : ""}`,
133
133
  `pause`,
134
- `${this.videoTitle}`
134
+ `${this.videoTitle}`,
135
135
  );
136
136
  this.FTTracker.oEvent({
137
137
  category: "video",
@@ -146,7 +146,7 @@ export class ytIframeTracking {
146
146
  this.FTTracker.gaEvent(
147
147
  `Video${window.isOvideoPlayer ? ":fallback" : ""}`,
148
148
  `100% watched`,
149
- `${this.videoTitle}`
149
+ `${this.videoTitle}`,
150
150
  );
151
151
  this.FTTracker.oEvent({
152
152
  category: "video",
@@ -159,7 +159,7 @@ export class ytIframeTracking {
159
159
  this.permutiveUtils.emitPermutiveProgressEvent(
160
160
  currentTime,
161
161
  currentTime,
162
- this.videoProgressInterval
162
+ this.videoProgressInterval,
163
163
  );
164
164
  }
165
165
  }
@@ -172,7 +172,7 @@ export class ytIframeTracking {
172
172
  this.permutiveUtils.emitPermutiveProgressEvent(
173
173
  duration - 1,
174
174
  currentTime,
175
- this.videoProgressInterval
175
+ this.videoProgressInterval,
176
176
  );
177
177
  }, 1000);
178
178
  const progress = this.progressPercentage(duration, currentTime);
@@ -1,8 +1,15 @@
1
- import { consentMonitor } from "../src/consentMonitor";
1
+ /**
2
+ * @jest-environment jsdom
3
+ */
4
+
5
+ import * as cmpLoader from "../src/cmp/loadFtCmp";
6
+ import { ConsentMonitor, consentMonitor } from "../src/consentMonitor";
2
7
  import { permutiveVideoUtils } from "../src/permutiveVideoUtils";
3
8
 
4
9
  let windowSpy: jest.SpyInstance;
5
10
 
11
+ jest.spyOn(cmpLoader, "loadFtCmpScript").mockResolvedValue(undefined);
12
+
6
13
  beforeEach(() => {
7
14
  windowSpy = jest.spyOn(window, "window", "get");
8
15
  });
@@ -30,23 +37,17 @@ describe("consentMonitor", () => {
30
37
  expect(obj.isDevEnvironment).toBe(true);
31
38
  });
32
39
 
33
- it("Initializes correctly", async () => {
34
- jest.useFakeTimers();
35
- windowSpy.mockImplementation(() => ({
36
- permutive: {
37
- consent: ({}) => {
38
- return {};
39
- },
40
- },
41
- }));
42
- const obj = new consentMonitor("https://FT-staging.devhost.com", [
40
+ it("Initializes correctly and sets the isInitialized flag", async () => {
41
+ const obj = new ConsentMonitor("https://FT-staging.devhost.com", [
43
42
  "localhost",
44
- "vercel.app",
45
43
  "phq",
46
44
  "devhost",
47
45
  ]);
46
+
47
+ await new Promise(process.nextTick);
48
48
  expect(obj.isInitialized).toBe(true);
49
49
  });
50
+
50
51
  //TODO - mock document.cookie get/set and test consent value
51
52
  describe("Permutive video progress utils", () => {
52
53
  it("can be initiated", () => {
package/tsconfig.json CHANGED
@@ -1,7 +1,9 @@
1
1
  {
2
2
  "compilerOptions": {
3
3
  "target": "es5",
4
- "module": "commonjs",
4
+ "module": "node16",
5
+ "moduleResolution": "node16",
6
+ "isolatedModules": true,
5
7
  "types": ["youtube", "jest"],
6
8
  "lib": ["dom", "es2020"],
7
9
  "jsx": "react",