diff --git a/.changeset/poor-garlics-flash.md b/.changeset/poor-garlics-flash.md new file mode 100644 index 000000000..4ee7a7979 --- /dev/null +++ b/.changeset/poor-garlics-flash.md @@ -0,0 +1,5 @@ +--- +"@scaleway/cookie-consent": patch +--- + +update import cookies diff --git a/packages/cookie-consent/src/CookieConsentProvider/CookieConsentProvider.tsx b/packages/cookie-consent/src/CookieConsentProvider/CookieConsentProvider.tsx index f0ffce732..d7860cdf4 100644 --- a/packages/cookie-consent/src/CookieConsentProvider/CookieConsentProvider.tsx +++ b/packages/cookie-consent/src/CookieConsentProvider/CookieConsentProvider.tsx @@ -1,4 +1,4 @@ -import cookie from 'cookie' +import { parse, serialize } from 'cookie' import type { SerializeOptions } from 'cookie' import { createContext, @@ -11,13 +11,12 @@ import { import type { PropsWithChildren } from 'react' import { uniq } from '../helpers/array' import { stringToHash } from '../helpers/misc' -import { isCategoryKind } from './helpers' +import { IS_CLIENT, isCategoryKind } from './helpers' import type { Config, Consent, Integrations } from './types' import { useSegmentIntegrations } from './useSegmentIntegrations' const COOKIE_PREFIX = '_scw_rgpd' const HASH_COOKIE = `${COOKIE_PREFIX}_hash` -const IS_CLIENT = typeof document !== 'undefined' // Appx 13 Months const CONSENT_MAX_AGE = 13 * 30 * 24 * 60 * 60 @@ -72,8 +71,9 @@ export const CookieConsentProvider = ({ cookiesOptions?: SerializeOptions }>) => { const [needConsent, setNeedsConsent] = useState(false) + console.debug('document cookie', document.cookie) const [cookies, setCookies] = useState>( - IS_CLIENT ? cookie.parse(document.cookie) : {}, + IS_CLIENT ? parse(document.cookie) : {}, ) const { @@ -113,6 +113,7 @@ export const CookieConsentProvider = ({ // We set needConsent at false until we have an answer from segment // This is to avoid showing setting needConsent to true only to be set // to false after receiving segment answer and flicker the UI + setNeedsConsent( isConsentRequired && cookies[HASH_COOKIE] !== integrationsHash.toString() && @@ -160,22 +161,18 @@ export const CookieConsentProvider = ({ if (!consentValue) { // If consent is set to false we have to delete the cookie - document.cookie = cookie.serialize(cookieName, '', { + document.cookie = serialize(cookieName, '', { ...cookiesOptions, expires: new Date(0), }) } else { - document.cookie = cookie.serialize( - cookieName, - consentValue.toString(), - { - ...cookiesOptions, - maxAge: - consentCategoryName === 'advertising' - ? consentAdvertisingMaxAge - : consentMaxAge, - }, - ) + document.cookie = serialize(cookieName, consentValue.toString(), { + ...cookiesOptions, + maxAge: + consentCategoryName === 'advertising' + ? consentAdvertisingMaxAge + : consentMaxAge, + }) } setCookies(prevCookies => ({ ...prevCookies, @@ -183,15 +180,11 @@ export const CookieConsentProvider = ({ })) } // We set the hash cookie to the current consented integrations - document.cookie = cookie.serialize( - HASH_COOKIE, - integrationsHash.toString(), - { - ...cookiesOptions, - // Here we use the shortest max age to force to ask again for expired consent - maxAge: consentAdvertisingMaxAge, - }, - ) + document.cookie = serialize(HASH_COOKIE, integrationsHash.toString(), { + ...cookiesOptions, + // Here we use the shortest max age to force to ask again for expired consent + maxAge: consentAdvertisingMaxAge, + }) setCookies(prevCookies => ({ ...prevCookies, [HASH_COOKIE]: integrationsHash.toString(), diff --git a/packages/cookie-consent/src/CookieConsentProvider/SegmentConsentMiddleware.tsx b/packages/cookie-consent/src/CookieConsentProvider/SegmentConsentMiddleware.tsx index 383406b18..282d4c564 100644 --- a/packages/cookie-consent/src/CookieConsentProvider/SegmentConsentMiddleware.tsx +++ b/packages/cookie-consent/src/CookieConsentProvider/SegmentConsentMiddleware.tsx @@ -1,7 +1,8 @@ import { useSegment } from '@scaleway/use-segment' -import cookie from 'cookie' +import { parse } from 'cookie' import type { PropsWithChildren } from 'react' import { useCookieConsent } from './CookieConsentProvider' +import { IS_CLIENT } from './helpers' import { type CategoryKind } from './helpers' export const AMPLITUDE_INTEGRATION_NAME = 'Amplitude (Actions)' @@ -14,7 +15,9 @@ type ConsentObject = { } export const getSessionId = () => { - const sessionId = cookie.parse(document.cookie)[COOKIE_SESSION_ID_NAME] + const sessionId = IS_CLIENT + ? parse(document.cookie)[COOKIE_SESSION_ID_NAME] + : '' if (sessionId) { return Number.parseInt(sessionId, 10) } diff --git a/packages/cookie-consent/src/CookieConsentProvider/__tests__/index.test.tsx b/packages/cookie-consent/src/CookieConsentProvider/__tests__/index.test.tsx index 20c5007eb..cc0f88e80 100644 --- a/packages/cookie-consent/src/CookieConsentProvider/__tests__/index.test.tsx +++ b/packages/cookie-consent/src/CookieConsentProvider/__tests__/index.test.tsx @@ -1,6 +1,6 @@ // useSegmentIntegrations tests have been splitted in multiple files because of https://github.com/facebook/vi/issues/8987 -import { act, renderHook } from '@testing-library/react' -import cookie from 'cookie' +import { act, renderHook, waitFor } from '@testing-library/react' +import * as cookie from 'cookie' import type { ComponentProps, ReactNode } from 'react' import { describe, expect, it, vi } from 'vitest' import { CookieConsentProvider, useCookieConsent } from '..' @@ -193,9 +193,9 @@ describe('CookieConsent - CookieConsentProvider', () => { }) it('should not need consent if hash cookie is set', () => { - vi.spyOn(cookie, 'parse').mockImplementation(() => ({ - _scw_rgpd_hash: '913003917', - })) + // document.cookie = '_scw_rgpd_hash=913003917;' + document.cookie = cookie.serialize('_scw_rgpd_hash', '913003917') + const { result } = renderHook(() => useCookieConsent(), { wrapper: wrapper({ isConsentRequired: true, @@ -215,19 +215,18 @@ describe('CookieConsent - CookieConsentProvider', () => { }) }) - it('should not need consent if hash cookie is set and some categories already approved', () => { - vi.spyOn(cookie, 'parse').mockImplementation(() => ({ - _scw_rgpd_hash: '913003917', - _scw_rgpd_marketing: 'true', - })) + it('should not need consent if hash cookie is set and some categories already approved', async () => { + document.cookie = cookie.serialize('_scw_rgpd_marketing', 'true') + document.cookie = cookie.serialize('_scw_rgpd_hash', '913003917') const { result } = renderHook(() => useCookieConsent(), { wrapper: wrapper({ isConsentRequired: true, }), }) - - expect(result.current.needConsent).toBe(false) + await waitFor(() => { + expect(result.current.needConsent).toBe(false) + }) expect(result.current.isSegmentAllowed).toBe(true) expect(result.current.categoriesConsent).toStrictEqual({ analytics: false, diff --git a/packages/cookie-consent/src/CookieConsentProvider/helpers.ts b/packages/cookie-consent/src/CookieConsentProvider/helpers.ts index d5e7f32b8..df1e3413a 100644 --- a/packages/cookie-consent/src/CookieConsentProvider/helpers.ts +++ b/packages/cookie-consent/src/CookieConsentProvider/helpers.ts @@ -10,3 +10,5 @@ export type CategoryKind = (typeof categories)[number] export const isCategoryKind = (key: string): key is CategoryKind => categories.includes(key as CategoryKind) + +export const IS_CLIENT = typeof document !== 'undefined'