Skip to content

Commit

Permalink
fix(DIST-2017): Race condition with hubspot integration script (#554)
Browse files Browse the repository at this point in the history
  • Loading branch information
mathio authored Jan 18, 2023
1 parent 9d62f3c commit f32f631
Show file tree
Hide file tree
Showing 6 changed files with 101 additions and 14 deletions.
12 changes: 8 additions & 4 deletions packages/embed/src/initializers/initialize.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { includeCss } from '../utils'
import { includeCss, waitForHubspotCookie } from '../utils'

import { buildOptionsFromAttributes } from './build-options-from-attributes'

export const initialize = (
export const initialize = async (
embedElementAttribute: string,
cssFilename: string,
forceReload: boolean = false,
Expand All @@ -14,14 +14,18 @@ export const initialize = (
includeCss(cssFilename)
}

Array.from(embedTypeElements).forEach((element, index) => {
for (let index = 0; index < embedTypeElements.length; index += 1) {
const element = embedTypeElements.item(index)
if (forceReload || element.dataset.tfLoaded !== 'true') {
const formId = element.getAttribute(embedElementAttribute)
if (!formId) {
throw new Error(`Invalid ${embedElementAttribute}=${formId} for embed #${index}`)
}
if (element.hasAttribute('data-tf-hubspot')) {
await waitForHubspotCookie()
}
factoryMethod(formId, buildOptionsFromAttributes(element), element)
element.dataset.tfLoaded = 'true'
}
})
}
}
69 changes: 69 additions & 0 deletions packages/embed/src/utils/hubspot.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import * as hubspotUtil from './hubspot'
import * as sleepUtil from './sleep'

const { getHubspotHiddenFields, waitForHubspotCookie } = hubspotUtil

let cookieValue = ''
Object.defineProperty(document, 'cookie', {
get: () => cookieValue,
})

describe('#getHubspotHiddenFields', () => {
const title = 'my page title'
const href = 'http://my-url'
const utk = 'foobar'

Object.defineProperty(window, 'location', { value: { href } })
Object.defineProperty(document, 'title', { value: title })

it('should return correct hidden fields', () => {
cookieValue = `foo=bar; hubspotutk=${utk}; bar=foo;`
expect(getHubspotHiddenFields()).toEqual({
hubspot_page_name: title,
hubspot_page_url: href,
hubspot_utk: utk,
})
})

it('should return undefined utk value', () => {
cookieValue = 'notahubspotutk=bar;'
expect(getHubspotHiddenFields()).toEqual({
hubspot_page_name: title,
hubspot_page_url: href,
hubspot_utk: undefined,
})
})
})

describe('#waitForHubspotCookie', () => {
const sleepSpy = jest.spyOn(sleepUtil, 'sleep')
const cookieSpy = jest.spyOn(hubspotUtil, 'getHubspotCookieValue')

beforeEach(() => {
sleepSpy.mockReset()
cookieSpy.mockReset()
})

it('should wait until the cookie is set', async () => {
sleepSpy.mockResolvedValue()
cookieSpy
.mockReturnValueOnce(undefined)
.mockReturnValueOnce(undefined)
.mockReturnValueOnce(undefined)
.mockReturnValueOnce('utk')

await waitForHubspotCookie()
expect(sleepSpy).toHaveBeenCalledTimes(3)
expect(cookieSpy).toHaveBeenCalledTimes(4)
})

it('should retry maximum of 10 times before continuing without no cookie', async () => {
sleepSpy.mockResolvedValue()
cookieSpy.mockReturnValue(undefined)

await waitForHubspotCookie()

expect(sleepSpy).toHaveBeenCalledTimes(10)
expect(cookieSpy).toHaveBeenCalledTimes(11)
})
})
18 changes: 8 additions & 10 deletions packages/embed/src/utils/hubspot.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { sleep } from './sleep'

type HubspotFieldsType = {
hubspot_page_name: string
hubspot_page_url: string
Expand All @@ -6,23 +8,19 @@ type HubspotFieldsType = {

const HUBSPOT_COOKIE = 'hubspotutk'

/**
*
* @returns {string} cookie value
*/

export const getHubspotCookieValue = () => {
const match = document.cookie.match(new RegExp(`(^| )${HUBSPOT_COOKIE}=([^;]+)`))
return (match && match[2]) || undefined
}

/**
*
* @returns {HubspotFieldsType} object with all hubspot fields
*/

export const getHubspotHiddenFields = (): HubspotFieldsType => ({
hubspot_page_name: document.title,
hubspot_page_url: window.location.href,
hubspot_utk: getHubspotCookieValue(),
})

export const waitForHubspotCookie = async (): Promise<void> => {
for (let maxRetries = 10, retries = 0; !getHubspotCookieValue() && retries < maxRetries; retries++) {
await sleep(250)
}
}
1 change: 1 addition & 0 deletions packages/embed/src/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,4 @@ export * from './lazy-init'
export * from './is-open'
export * from './make-auto-resize'
export * from './change-color-opacity'
export * from './hubspot'
12 changes: 12 additions & 0 deletions packages/embed/src/utils/sleep.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { sleep } from './sleep'

jest.useFakeTimers()
jest.spyOn(global, 'setTimeout')
describe('#sleep', () => {
it('should wait for 5 seconds', () => {
sleep(5000)

expect(setTimeout).toHaveBeenCalledTimes(1)
expect(setTimeout).toHaveBeenLastCalledWith(expect.any(Function), 5000)
})
})
3 changes: 3 additions & 0 deletions packages/embed/src/utils/sleep.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export const sleep = (ms: number): Promise<void> => {
return new Promise((resolve) => setTimeout(resolve, ms))
}

0 comments on commit f32f631

Please sign in to comment.