Skip to content

Commit

Permalink
Refactoring
Browse files Browse the repository at this point in the history
  • Loading branch information
rupertbates committed Jan 9, 2025
1 parent 2e05899 commit 2ac27fb
Show file tree
Hide file tree
Showing 12 changed files with 161 additions and 245 deletions.
30 changes: 30 additions & 0 deletions dotcom-rendering/src/client/userFeatures/cookies/adFree.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { getCookie, removeCookie, setCookie } from '@guardian/libs';

const AD_FREE_USER_COOKIE = 'GU_AF1';

export const getAdFreeCookie = (): string | null =>
getCookie({ name: AD_FREE_USER_COOKIE });

/*
* Set the ad free cookie
*
* @param daysToLive - number of days the cookie should be valid
*/
export const setAdFreeCookie = (daysToLive = 1): void => {
const expires = new Date();
expires.setMonth(expires.getMonth() + 6);
setCookie({
name: AD_FREE_USER_COOKIE,
value: expires.getTime().toString(),
daysToLive,
});
};

export const adFreeDataIsPresent = (): boolean => {
const cookieVal = getAdFreeCookie();
if (!cookieVal) return false;
return !Number.isNaN(parseInt(cookieVal, 10));
};

export const removeAdFreeCookie = (): void =>
removeCookie({ name: AD_FREE_USER_COOKIE });
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { getCookie, removeCookie, setCookie } from '@guardian/libs';

const HIDE_SUPPORT_MESSAGING_COOKIE = 'gu_hide_support_messaging';

export const getHideSupportMessagingCookie = (): string | null =>
getCookie({ name: HIDE_SUPPORT_MESSAGING_COOKIE });

export const setHideSupportMessagingCookie = (value: boolean): void => {
setCookie({
name: HIDE_SUPPORT_MESSAGING_COOKIE,
value: String(value),
});
};

export const removeHideSupportMessagingCookie = (): void =>
removeCookie({ name: HIDE_SUPPORT_MESSAGING_COOKIE });
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { getCookie, removeCookie, setCookie } from '@guardian/libs';

const USER_FEATURES_EXPIRY_COOKIE = 'gu_user_features_expiry';

export const getUserFeaturesExpiryCookie = (): string | null =>
getCookie({ name: USER_FEATURES_EXPIRY_COOKIE });

export const setUserFeaturesExpiryCookie = (expiryTime: string): void =>
setCookie({ name: USER_FEATURES_EXPIRY_COOKIE, value: expiryTime });

export const removeUserFeaturesExpiryCookie = (): void =>
removeCookie({ name: USER_FEATURES_EXPIRY_COOKIE });

export const featuresDataIsOld = (): boolean => {
const cookie = getUserFeaturesExpiryCookie();
if (!cookie) return true;
const expiryTime = parseInt(cookie, 10);
const timeNow = new Date().getTime();
return timeNow >= expiryTime;
};
16 changes: 16 additions & 0 deletions dotcom-rendering/src/client/userFeatures/fetchJson.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
export const fetchJson = async (
path: string,
init: RequestInit = {},
): Promise<unknown> => {
const resp = await fetch(path, init);
if (resp.ok) {
try {
return resp.json();
} catch (ex) {
throw new Error(
`Fetch error while requesting ${path}: Invalid JSON response`,
);
}
}
throw new Error(`Fetch error while requesting ${path}: ${resp.statusText}`);
};
4 changes: 2 additions & 2 deletions dotcom-rendering/src/client/userFeatures/membersDataApi.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { isBoolean, isObject } from '@guardian/libs';
import type { SignedInWithCookies, SignedInWithOkta } from '../../lib/identity';
import { getOptionsHeadersWithOkta } from '../../lib/identity';
import type { UserBenefits } from './user-features-lib';
import { fetchJson } from './user-features-lib';
import { fetchJson } from './fetchJson';
import type { UserBenefits } from './user-features';

const dates = {
1: '01',
Expand Down
97 changes: 0 additions & 97 deletions dotcom-rendering/src/client/userFeatures/user-features-lib.ts

This file was deleted.

94 changes: 26 additions & 68 deletions dotcom-rendering/src/client/userFeatures/user-features.test.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
import { getCookie, removeCookie, setCookie } from '@guardian/libs';
import type { AuthStatus } from '../../lib/identity';
import {
getAuthStatus as getAuthStatus_,
isUserLoggedInOktaRefactor as isUserLoggedInOktaRefactor_,
} from '../../lib/identity';
import { refresh } from './user-features';
import { fetchJson } from './user-features-lib';
import { getAdFreeCookie, setAdFreeCookie } from './cookies/adFree';
import { setHideSupportMessagingCookie } from './cookies/hideSupportMessaging';
import {
getUserFeaturesExpiryCookie,
setUserFeaturesExpiryCookie,
} from './cookies/userFeaturesExpiry';
import { fetchJson } from './fetchJson';
import { deleteAllCookies, refresh } from './user-features';

const fakeUserFeatures = {
showSupportMessaging: false,
Expand All @@ -16,11 +21,8 @@ const fakeUserFeatures = {
},
};

jest.mock('./user-features-lib', () => {
// Only mock the fetchJson function, rather than the whole module
const original = jest.requireActual('./user-features-lib');
jest.mock('./fetchJson', () => {
return {
...original,
fetchJson: jest.fn(() => {
return Promise.resolve(fakeUserFeatures);
}),
Expand All @@ -44,40 +46,16 @@ const getAuthStatus = getAuthStatus_ as jest.MockedFunction<
typeof getAuthStatus_
>;

const PERSISTENCE_KEYS = {
USER_FEATURES_EXPIRY_COOKIE: 'gu_user_features_expiry',
AD_FREE_USER_COOKIE: 'GU_AF1',
SUPPORT_ONE_OFF_CONTRIBUTION_COOKIE: 'gu.contributions.contrib-timestamp',
HIDE_SUPPORT_MESSAGING_COOKIE: 'gu_hide_support_messaging',
};

const setAllFeaturesData = (opts: { isExpired: boolean }) => {
const currentTime = new Date().getTime();
const msInOneDay = 24 * 60 * 60 * 1000;
const expiryDate = opts.isExpired
? new Date(currentTime - msInOneDay)
: new Date(currentTime + msInOneDay);
const adFreeExpiryDate = opts.isExpired
? new Date(currentTime - msInOneDay * 2)
: new Date(currentTime + msInOneDay * 2);
setCookie({
name: PERSISTENCE_KEYS.HIDE_SUPPORT_MESSAGING_COOKIE,
value: 'true',
});
setCookie({
name: PERSISTENCE_KEYS.AD_FREE_USER_COOKIE,
value: adFreeExpiryDate.getTime().toString(),
});
setCookie({
name: PERSISTENCE_KEYS.USER_FEATURES_EXPIRY_COOKIE,
value: expiryDate.getTime().toString(),
});
};

const deleteAllFeaturesData = () => {
removeCookie({ name: PERSISTENCE_KEYS.USER_FEATURES_EXPIRY_COOKIE });
removeCookie({ name: PERSISTENCE_KEYS.AD_FREE_USER_COOKIE });
removeCookie({ name: PERSISTENCE_KEYS.HIDE_SUPPORT_MESSAGING_COOKIE });
setHideSupportMessagingCookie(true);
setAdFreeCookie(2);
setUserFeaturesExpiryCookie(expiryDate.getTime().toString());
};

beforeAll(() => {
Expand All @@ -96,7 +74,7 @@ describe('Refreshing the features data', () => {
});

it('Performs an update if the user has missing data', async () => {
deleteAllFeaturesData();
deleteAllCookies();
await refresh();
expect(fetchJsonSpy).toHaveBeenCalledTimes(1);
});
Expand All @@ -110,14 +88,10 @@ describe('Refreshing the features data', () => {
it('Does not delete the data just because it has expired', async () => {
setAllFeaturesData({ isExpired: true });
await refresh();
expect(
getCookie({
name: PERSISTENCE_KEYS.USER_FEATURES_EXPIRY_COOKIE,
}),
).toEqual(expect.stringMatching(/\d{13}/));
expect(
getCookie({ name: PERSISTENCE_KEYS.AD_FREE_USER_COOKIE }),
).toEqual(expect.stringMatching(/\d{13}/));
expect(getUserFeaturesExpiryCookie()).toEqual(
expect.stringMatching(/\d{13}/),
);
expect(getAdFreeCookie()).toEqual(expect.stringMatching(/\d{13}/));
});

it('Does not perform update if user has fresh feature data', async () => {
Expand All @@ -135,22 +109,16 @@ describe('If user signed out', () => {
});

it('Does not perform update, even if feature data missing', async () => {
deleteAllFeaturesData();
deleteAllCookies();
await refresh();
expect(fetchJsonSpy).not.toHaveBeenCalled();
});

it('Deletes leftover feature data', async () => {
setAllFeaturesData({ isExpired: false });
await refresh();
expect(
getCookie({ name: PERSISTENCE_KEYS.AD_FREE_USER_COOKIE }),
).toBeNull();
expect(
getCookie({
name: PERSISTENCE_KEYS.USER_FEATURES_EXPIRY_COOKIE,
}),
).toBeNull();
expect(getAdFreeCookie()).toBeNull();
expect(getUserFeaturesExpiryCookie()).toBeNull();
});
});

Expand All @@ -171,7 +139,7 @@ describe('Storing new feature data', () => {

jest.resetAllMocks();
fetchJsonSpy.mockReturnValue(Promise.resolve(mockResponse));
deleteAllFeaturesData();
deleteAllCookies();
isUserLoggedInOktaRefactor.mockResolvedValue(true);
getAuthStatus.mockResolvedValue({
kind: 'SignedInWithOkta',
Expand All @@ -191,9 +159,7 @@ describe('Storing new feature data', () => {
}),
);
return refresh().then(() => {
expect(
getCookie({ name: PERSISTENCE_KEYS.AD_FREE_USER_COOKIE }),
).toBeNull();
expect(getAdFreeCookie()).toBeNull();
});
});

Expand All @@ -210,16 +176,12 @@ describe('Storing new feature data', () => {
}),
);
return refresh().then(() => {
expect(
getCookie({ name: PERSISTENCE_KEYS.AD_FREE_USER_COOKIE }),
).toBeTruthy();
expect(getAdFreeCookie()).toBeTruthy();
expect(
Number.isNaN(
parseInt(
// @ts-expect-error -- we’re testing it
getCookie({
name: PERSISTENCE_KEYS.AD_FREE_USER_COOKIE,
}),
getAdFreeCookie(),
10,
),
),
Expand All @@ -229,19 +191,15 @@ describe('Storing new feature data', () => {

it('Puts an expiry date in an accompanying cookie', () =>
refresh().then(() => {
const expiryDate = getCookie({
name: PERSISTENCE_KEYS.USER_FEATURES_EXPIRY_COOKIE,
});
const expiryDate = getUserFeaturesExpiryCookie();
expect(expiryDate).toBeTruthy();
// @ts-expect-error -- we’re testing it
expect(Number.isNaN(parseInt(expiryDate, 10))).toBe(false);
}));

it('The expiry date is in the future', () =>
refresh().then(() => {
const expiryDateString = getCookie({
name: PERSISTENCE_KEYS.USER_FEATURES_EXPIRY_COOKIE,
});
const expiryDateString = getUserFeaturesExpiryCookie();
// @ts-expect-error -- we’re testing it
const expiryDateEpoch = parseInt(expiryDateString, 10);
const currentTimeEpoch = new Date().getTime();
Expand Down
Loading

0 comments on commit 2ac27fb

Please sign in to comment.