From 803eb66afb44cf0d59dd3d744e301e617e53b494 Mon Sep 17 00:00:00 2001 From: jaaaaavier Date: Thu, 7 May 2026 16:51:11 +0200 Subject: [PATCH 1/6] feat: add dynamic default fallback for storage plans --- src/app/i18n/locales/de.json | 7 ++++--- src/app/i18n/locales/en.json | 7 ++++--- src/app/i18n/locales/es.json | 7 ++++--- src/app/i18n/locales/fr.json | 7 ++++--- src/app/i18n/locales/it.json | 7 ++++--- src/app/i18n/locales/ru.json | 7 ++++--- src/app/i18n/locales/tw.json | 7 ++++--- src/app/i18n/locales/zh.json | 7 ++++--- .../Checkout/components/CheckoutProductCard.tsx | 17 ++++++++++++----- 9 files changed, 44 insertions(+), 29 deletions(-) diff --git a/src/app/i18n/locales/de.json b/src/app/i18n/locales/de.json index ec4b745f9..e93bb8128 100644 --- a/src/app/i18n/locales/de.json +++ b/src/app/i18n/locales/de.json @@ -2051,9 +2051,10 @@ ], "comingSoonFeatures": ["Mail", "Fotos"] }, - "1GB": { - "title": "Kostenlos", - "features": ["1GB verschlüsselter Cloud-Speicher", "Verschlüsseltes VPN"] + "default": { + "freeTitle": "Kostenlos", + "bytesTitle": "{{bytes}}", + "features": ["{{bytes}} verschlüsselter Cloud-Speicher", "Verschlüsseltes VPN"] } }, "businessPlanFeaturesList": { diff --git a/src/app/i18n/locales/en.json b/src/app/i18n/locales/en.json index 9dab09673..2a6f8047d 100644 --- a/src/app/i18n/locales/en.json +++ b/src/app/i18n/locales/en.json @@ -2135,9 +2135,10 @@ ], "comingSoonFeatures": ["Mail", "Photos"] }, - "1GB": { - "title": "Free", - "features": ["1GB of encrypted storage", "Encrypted VPN"] + "default": { + "freeTitle": "Free", + "bytesTitle": "{{bytes}}", + "features": ["{{bytes}} of encrypted storage", "Encrypted VPN"] } }, "businessPlanFeaturesList": { diff --git a/src/app/i18n/locales/es.json b/src/app/i18n/locales/es.json index d8b0db3d3..8a19a6800 100644 --- a/src/app/i18n/locales/es.json +++ b/src/app/i18n/locales/es.json @@ -2111,9 +2111,10 @@ ], "comingSoonFeatures": ["Mail", "Fotos"] }, - "1GB": { - "title": "Gratis", - "features": ["1GB de almacenamiento en la nube cifrado", "VPN cifrada"] + "default": { + "freeTitle": "Gratis", + "bytesTitle": "{{bytes}}", + "features": ["{{bytes}} de almacenamiento en la nube cifrado", "VPN cifrada"] } }, "businessPlanFeaturesList": { diff --git a/src/app/i18n/locales/fr.json b/src/app/i18n/locales/fr.json index 2e6d8b00a..875a5677b 100644 --- a/src/app/i18n/locales/fr.json +++ b/src/app/i18n/locales/fr.json @@ -2057,9 +2057,10 @@ ], "comingSoonFeatures": ["Mail", "Photos"] }, - "1GB": { - "title": "Gratuit", - "features": ["1Go de stockage cloud chiffré", "VPN Chiffré"] + "default": { + "freeTitle": "Gratuit", + "bytesTitle": "{{bytes}}", + "features": ["{{bytes}} de stockage cloud chiffré", "VPN Chiffré"] } }, "businessPlanFeaturesList": { diff --git a/src/app/i18n/locales/it.json b/src/app/i18n/locales/it.json index 3f07c0a9f..c062a44a4 100644 --- a/src/app/i18n/locales/it.json +++ b/src/app/i18n/locales/it.json @@ -2164,9 +2164,10 @@ ], "comingSoonFeatures": ["Mail", "Foto"] }, - "1GB": { - "title": "Gratuito", - "features": ["1GB di spazio di archiviazione cloud crittografato", "VPN Crittografata"] + "default": { + "freeTitle": "Gratuito", + "bytesTitle": "{{bytes}}", + "features": ["{{bytes}} di spazio di archiviazione cloud crittografato", "VPN Crittografata"] } }, "businessPlanFeaturesList": { diff --git a/src/app/i18n/locales/ru.json b/src/app/i18n/locales/ru.json index 647b13ca8..3d5efbf9a 100644 --- a/src/app/i18n/locales/ru.json +++ b/src/app/i18n/locales/ru.json @@ -2072,9 +2072,10 @@ ], "comingSoonFeatures": ["Mail (Почта)", "Фотографии"] }, - "1GB": { - "title": "Free (Бесплатный)", - "features": ["1 ГБ зашифрованного облачного хранилища", "Зашифрованный VPN"] + "default": { + "freeTitle": "Free (Бесплатный)", + "bytesTitle": "{{bytes}}", + "features": ["{{bytes}} зашифрованного облачного хранилища", "Зашифрованный VPN"] } }, "businessPlanFeaturesList": { diff --git a/src/app/i18n/locales/tw.json b/src/app/i18n/locales/tw.json index e6af4d31b..63a33fc2c 100644 --- a/src/app/i18n/locales/tw.json +++ b/src/app/i18n/locales/tw.json @@ -2062,9 +2062,10 @@ ], "comingSoonFeatures": ["郵件 (Mail)", "照片(Photos)"] }, - "1GB": { - "title": "Free (免費版)", - "features": ["1GB 加密雲端儲存空間", "加密 VPN"] + "default": { + "freeTitle": "Free (免費版)", + "bytesTitle": "{{bytes}}", + "features": ["{{bytes}} 加密雲端儲存空間", "加密 VPN"] } }, "businessPlanFeaturesList": { diff --git a/src/app/i18n/locales/zh.json b/src/app/i18n/locales/zh.json index ecd732711..a0b6a6e35 100644 --- a/src/app/i18n/locales/zh.json +++ b/src/app/i18n/locales/zh.json @@ -2099,9 +2099,10 @@ ], "comingSoonFeatures": ["邮件 (Mail)", "照片 (Photos)"] }, - "1GB": { - "title": "Free (免费版)", - "features": ["1GB 加密云存储空间", "加密 VPN"] + "default": { + "freeTitle": "Free (免费版)", + "bytesTitle": "{{bytes}}", + "features": ["{{bytes}} 加密云存储空间", "加密 VPN"] } }, "businessPlanFeaturesList": { diff --git a/src/views/Checkout/components/CheckoutProductCard.tsx b/src/views/Checkout/components/CheckoutProductCard.tsx index 3b09ab0c6..bfe98a54b 100644 --- a/src/views/Checkout/components/CheckoutProductCard.tsx +++ b/src/views/Checkout/components/CheckoutProductCard.tsx @@ -79,11 +79,18 @@ export const CheckoutProductCard = ({ const planType = isBusiness ? 'businessPlanFeaturesList' : 'planFeaturesList'; - const productLabel = translate(`preferences.account.plans.${planType}.${bytes}.title`) ?? bytes; - const featureKeys = - translateList(`preferences.account.plans.${planType}.${bytes ?? 'freeFeatures'}.features`, { - returnObjects: true, - }) ?? translateList('preferences.account.plans.planFeaturesList.1GB.features'); + const specificTitleKey = `preferences.account.plans.${planType}.${bytes}.title`; + const productLabel = + translate(specificTitleKey) !== specificTitleKey + ? translate(specificTitleKey) + : translate('preferences.account.plans.planFeaturesList.default.bytesTitle', { bytes }); + + const specificFeatures = translateList(`preferences.account.plans.${planType}.${bytes}.features`, { + returnObjects: true, + }); + const featureKeys = Array.isArray(specificFeatures) + ? specificFeatures + : translateList('preferences.account.plans.planFeaturesList.default.features', { bytes, returnObjects: true }); const featuresList = Array.isArray( translateList(`preferences.account.plans.${planType}.${bytes}.comingSoonFeatures`), From 5f1624da11f3171197fe7eda5961085cbbf458b7 Mon Sep 17 00:00:00 2001 From: jaaaaavier Date: Thu, 7 May 2026 17:17:45 +0200 Subject: [PATCH 2/6] Update CheckoutProductCard.tsx --- src/views/Checkout/components/CheckoutProductCard.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/views/Checkout/components/CheckoutProductCard.tsx b/src/views/Checkout/components/CheckoutProductCard.tsx index bfe98a54b..fc62184cf 100644 --- a/src/views/Checkout/components/CheckoutProductCard.tsx +++ b/src/views/Checkout/components/CheckoutProductCard.tsx @@ -81,9 +81,9 @@ export const CheckoutProductCard = ({ const specificTitleKey = `preferences.account.plans.${planType}.${bytes}.title`; const productLabel = - translate(specificTitleKey) !== specificTitleKey - ? translate(specificTitleKey) - : translate('preferences.account.plans.planFeaturesList.default.bytesTitle', { bytes }); + translate(specificTitleKey) === specificTitleKey + ? translate('preferences.account.plans.planFeaturesList.default.bytesTitle', { bytes }) + : translate(specificTitleKey); const specificFeatures = translateList(`preferences.account.plans.${planType}.${bytes}.features`, { returnObjects: true, From a16fb3e9496913819a9a5dcf16feeef33b5f062d Mon Sep 17 00:00:00 2001 From: jaaaaavier Date: Fri, 8 May 2026 10:16:22 +0200 Subject: [PATCH 3/6] add fallback for features --- .../Sections/Account/Plans/components/PlanCard.tsx | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/views/NewSettings/components/Sections/Account/Plans/components/PlanCard.tsx b/src/views/NewSettings/components/Sections/Account/Plans/components/PlanCard.tsx index cdec26209..24ed1830b 100644 --- a/src/views/NewSettings/components/Sections/Account/Plans/components/PlanCard.tsx +++ b/src/views/NewSettings/components/Sections/Account/Plans/components/PlanCard.tsx @@ -88,7 +88,15 @@ const PlanDetailsList = ({ planSpace }: { planSpace: string }) => { const { translateList } = useTranslationContext(); const planType = 'planFeaturesList'; - const featureKeys = translateList(`preferences.account.plans.${planType}.${planSpace ?? 'freeFeatures'}.features`); + const specificFeatures = translateList(`preferences.account.plans.${planType}.${planSpace}.features`, { + returnObjects: true, + }); + const featureKeys = Array.isArray(specificFeatures) + ? specificFeatures + : translateList('preferences.account.plans.planFeaturesList.default.features', { + bytes: planSpace, + returnObjects: true, + }); const comingSoonFeatureKeys = translateList(`preferences.account.plans.${planType}.${planSpace}.comingSoonFeatures`); From c7db97dcd5da18b36226ad0c5f5d75101ac3c216 Mon Sep 17 00:00:00 2001 From: jaaaaavier Date: Mon, 11 May 2026 10:27:55 +0200 Subject: [PATCH 4/6] feat: remove unused texts and improve callings --- src/app/i18n/locales/de.json | 1 - src/app/i18n/locales/en.json | 1 - src/app/i18n/locales/es.json | 1 - src/app/i18n/locales/fr.json | 1 - src/app/i18n/locales/it.json | 1 - src/app/i18n/locales/ru.json | 1 - src/app/i18n/locales/tw.json | 1 - src/app/i18n/locales/zh.json | 1 - src/views/Checkout/components/CheckoutProductCard.tsx | 5 +++-- 9 files changed, 3 insertions(+), 10 deletions(-) diff --git a/src/app/i18n/locales/de.json b/src/app/i18n/locales/de.json index 882b057a0..0b49fa68f 100644 --- a/src/app/i18n/locales/de.json +++ b/src/app/i18n/locales/de.json @@ -2053,7 +2053,6 @@ "comingSoonFeatures": ["Mail", "Fotos"] }, "default": { - "freeTitle": "Kostenlos", "bytesTitle": "{{bytes}}", "features": ["{{bytes}} verschlüsselter Cloud-Speicher", "Verschlüsseltes VPN"] } diff --git a/src/app/i18n/locales/en.json b/src/app/i18n/locales/en.json index 8543c5a75..8a25449e4 100644 --- a/src/app/i18n/locales/en.json +++ b/src/app/i18n/locales/en.json @@ -2137,7 +2137,6 @@ "comingSoonFeatures": ["Mail", "Photos"] }, "default": { - "freeTitle": "Free", "bytesTitle": "{{bytes}}", "features": ["{{bytes}} of encrypted storage", "Encrypted VPN"] } diff --git a/src/app/i18n/locales/es.json b/src/app/i18n/locales/es.json index 2e0ff04ab..c285f4c9b 100644 --- a/src/app/i18n/locales/es.json +++ b/src/app/i18n/locales/es.json @@ -2113,7 +2113,6 @@ "comingSoonFeatures": ["Mail", "Fotos"] }, "default": { - "freeTitle": "Gratis", "bytesTitle": "{{bytes}}", "features": ["{{bytes}} de almacenamiento en la nube cifrado", "VPN cifrada"] } diff --git a/src/app/i18n/locales/fr.json b/src/app/i18n/locales/fr.json index 2032ded42..b350d07fc 100644 --- a/src/app/i18n/locales/fr.json +++ b/src/app/i18n/locales/fr.json @@ -2059,7 +2059,6 @@ "comingSoonFeatures": ["Mail", "Photos"] }, "default": { - "freeTitle": "Gratuit", "bytesTitle": "{{bytes}}", "features": ["{{bytes}} de stockage cloud chiffré", "VPN Chiffré"] } diff --git a/src/app/i18n/locales/it.json b/src/app/i18n/locales/it.json index a291b85ae..a395b4b30 100644 --- a/src/app/i18n/locales/it.json +++ b/src/app/i18n/locales/it.json @@ -2166,7 +2166,6 @@ "comingSoonFeatures": ["Mail", "Foto"] }, "default": { - "freeTitle": "Gratuito", "bytesTitle": "{{bytes}}", "features": ["{{bytes}} di spazio di archiviazione cloud crittografato", "VPN Crittografata"] } diff --git a/src/app/i18n/locales/ru.json b/src/app/i18n/locales/ru.json index e7f60c545..9361576c5 100644 --- a/src/app/i18n/locales/ru.json +++ b/src/app/i18n/locales/ru.json @@ -2074,7 +2074,6 @@ "comingSoonFeatures": ["Mail (Почта)", "Фотографии"] }, "default": { - "freeTitle": "Free (Бесплатный)", "bytesTitle": "{{bytes}}", "features": ["{{bytes}} зашифрованного облачного хранилища", "Зашифрованный VPN"] } diff --git a/src/app/i18n/locales/tw.json b/src/app/i18n/locales/tw.json index fab672636..5dc911590 100644 --- a/src/app/i18n/locales/tw.json +++ b/src/app/i18n/locales/tw.json @@ -2064,7 +2064,6 @@ "comingSoonFeatures": ["郵件 (Mail)", "照片(Photos)"] }, "default": { - "freeTitle": "Free (免費版)", "bytesTitle": "{{bytes}}", "features": ["{{bytes}} 加密雲端儲存空間", "加密 VPN"] } diff --git a/src/app/i18n/locales/zh.json b/src/app/i18n/locales/zh.json index bc0a73bba..57fbde576 100644 --- a/src/app/i18n/locales/zh.json +++ b/src/app/i18n/locales/zh.json @@ -2101,7 +2101,6 @@ "comingSoonFeatures": ["邮件 (Mail)", "照片 (Photos)"] }, "default": { - "freeTitle": "Free (免费版)", "bytesTitle": "{{bytes}}", "features": ["{{bytes}} 加密云存储空间", "加密 VPN"] } diff --git a/src/views/Checkout/components/CheckoutProductCard.tsx b/src/views/Checkout/components/CheckoutProductCard.tsx index 4a82f0753..3e31f24a5 100644 --- a/src/views/Checkout/components/CheckoutProductCard.tsx +++ b/src/views/Checkout/components/CheckoutProductCard.tsx @@ -69,10 +69,11 @@ export const CheckoutProductCard = ({ const planType = 'planFeaturesList'; const specificTitleKey = `preferences.account.plans.${planType}.${bytes}.title`; + const specificTransalatedKey = translate(specificTitleKey); const productLabel = - translate(specificTitleKey) === specificTitleKey + specificTransalatedKey === specificTitleKey ? translate('preferences.account.plans.planFeaturesList.default.bytesTitle', { bytes }) - : translate(specificTitleKey); + : specificTransalatedKey; const specificFeatures = translateList(`preferences.account.plans.${planType}.${bytes}.features`, { returnObjects: true, From 27c657c7ee038a148906b102ea791abb9abbf7d0 Mon Sep 17 00:00:00 2001 From: jaaaaavier Date: Mon, 11 May 2026 10:49:01 +0200 Subject: [PATCH 5/6] Update impact.service.test.ts --- src/app/analytics/impact.service.test.ts | 42 ++++++++++++------------ 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/src/app/analytics/impact.service.test.ts b/src/app/analytics/impact.service.test.ts index bb90e177b..c12d68e08 100644 --- a/src/app/analytics/impact.service.test.ts +++ b/src/app/analytics/impact.service.test.ts @@ -106,7 +106,7 @@ beforeEach(() => { describe('Testing Impact Service', () => { describe('savePaymentDataInLocalStorage', () => { - it('should save the correct amount to localStorage after applying coupon', () => { + it('When coupon is applied, then it should save the correct amount to localStorage', () => { const setToLocalStorageSpy = vi.spyOn(localStorageService, 'set'); savePaymentDataInLocalStorage({ @@ -121,7 +121,7 @@ describe('Testing Impact Service', () => { expect(setToLocalStorageSpy).toHaveBeenCalledWith('amountPaid', expectedAmount); }); - it('should save subscription ID when plan is not lifetime', () => { + it('When plan is not lifetime, then it should save subscription ID', () => { const setToLocalStorageSpy = vi.spyOn(localStorageService, 'set'); savePaymentDataInLocalStorage({ @@ -136,7 +136,7 @@ describe('Testing Impact Service', () => { expect(setToLocalStorageSpy).toHaveBeenCalledWith('subscriptionId', subId); }); - it('should save payment intent ID when plan is lifetime', () => { + it('When plan is lifetime, then it should save payment intent ID', () => { const setToLocalStorageSpy = vi.spyOn(localStorageService, 'set'); const lifetimeProduct = { ...product, @@ -155,7 +155,7 @@ describe('Testing Impact Service', () => { expect(setToLocalStorageSpy).toHaveBeenCalledWith('paymentIntentId', paymentIntentId); }); - it('should save product metadata including name, price ID, and currency', () => { + it('When saving payment data, then it should save product metadata including name, price ID, and currency', () => { const setToLocalStorageSpy = vi.spyOn(localStorageService, 'set'); savePaymentDataInLocalStorage({ @@ -172,7 +172,7 @@ describe('Testing Impact Service', () => { expect(setToLocalStorageSpy).toHaveBeenCalledWith('currency', product.price.currency); }); - it('should save coupon code when provided', () => { + it('When coupon code is provided, then it should save coupon code', () => { const setToLocalStorageSpy = vi.spyOn(localStorageService, 'set'); savePaymentDataInLocalStorage({ @@ -187,7 +187,7 @@ describe('Testing Impact Service', () => { expect(setToLocalStorageSpy).toHaveBeenCalledWith('couponCode', promoCode.codeName); }); - it('should save isFirstPurchase flag to localStorage', () => { + it('When saving payment data, then it should save isFirstPurchase flag to localStorage', () => { const setToLocalStorageSpy = vi.spyOn(localStorageService, 'set'); savePaymentDataInLocalStorage({ @@ -205,7 +205,7 @@ describe('Testing Impact Service', () => { describe('trackSignUp', () => { describe('gtag tracking', () => { - it('should send User Signup event to gtag', async () => { + it('When tracking sign up, then it should send User Signup event to gtag', async () => { const gTagSpy = vi.spyOn(globalThis.window, 'gtag'); await trackSignUp(mockedUserUuid); @@ -213,7 +213,7 @@ describe('Testing Impact Service', () => { expect(gTagSpy).toHaveBeenCalledWith('event', 'User Signup'); }); - it('should report error when gtag fails but continue execution', async () => { + it('When gtag fails, then it should report error but continue execution', async () => { const unknownError = new Error('gtag Error'); const gTagSpy = vi.spyOn(globalThis.window, 'gtag').mockImplementation(() => { throw unknownError; @@ -228,7 +228,7 @@ describe('Testing Impact Service', () => { }); describe('Impact API tracking', () => { - it('should send signup event to Impact API with correct payload', async () => { + it('When tracking sign up, then it should send signup event to Impact API with correct payload', async () => { const axiosSpy = vi.spyOn(axios, 'post').mockResolvedValue({}); await trackSignUp(mockedUserUuid); @@ -246,7 +246,7 @@ describe('Testing Impact Service', () => { ); }); - it('should include message ID in Impact API payload', async () => { + it('When tracking sign up, then it should include message ID in Impact API payload', async () => { const axiosSpy = vi.spyOn(axios, 'post').mockResolvedValue({}); await trackSignUp(mockedUserUuid); @@ -256,7 +256,7 @@ describe('Testing Impact Service', () => { expect(callArgs.messageId).toBe(mockedUserUuid); }); - it('should not send to Impact API when source is direct', async () => { + it('When source is direct, then it should not send to Impact API', async () => { const getCookieMock = await import('./utils'); vi.mocked(getCookieMock.getCookie).mockImplementation((key) => { if (key === 'impactSource') return 'direct'; @@ -274,7 +274,7 @@ describe('Testing Impact Service', () => { describe('trackPaymentConversion', () => { describe('Impact API tracking', () => { - it('should send payment conversion to Impact API with correct data', async () => { + it('When tracking payment conversion, then it should send payment conversion to Impact API with correct data', async () => { const axiosSpy = vi.spyOn(axios, 'post').mockResolvedValue({}); await trackPaymentConversion(); @@ -298,7 +298,7 @@ describe('Testing Impact Service', () => { ); }); - it('should use minimum value of 0.01 when amount is 0 (free purchase)', async () => { + it('When amount is 0 (free purchase), then it should use minimum value of 0.01', async () => { vi.spyOn(localStorageService, 'get').mockImplementation((key) => { if (key === 'amountPaid') return '0'; if (key === 'subscriptionId') return subId; @@ -314,7 +314,7 @@ describe('Testing Impact Service', () => { expect(callArgs.properties.impact_value).toBe(0.01); }); - it('should include coupon code in properties when available', async () => { + it('When coupon code is available, then it should include coupon code in properties', async () => { const axiosSpy = vi.spyOn(axios, 'post').mockResolvedValue({}); await trackPaymentConversion(); @@ -323,7 +323,7 @@ describe('Testing Impact Service', () => { expect(callArgs.properties).toHaveProperty('order_promo_code', promoCode.codeName); }); - it('should report error when Impact API call fails', async () => { + it('When Impact API call fails, then it should report error', async () => { const unknownError = new Error('API Error'); const axiosSpy = vi.spyOn(axios, 'post').mockRejectedValue(unknownError); const errorServiceSpy = vi.spyOn(errorService, 'reportError'); @@ -334,7 +334,7 @@ describe('Testing Impact Service', () => { expect(errorServiceSpy).toHaveBeenCalledWith(unknownError); }); - it('should not send to Impact when source is direct and no coupon code', async () => { + it('When source is direct and no coupon code, then it should not send to Impact', async () => { const getCookieMock = await import('./utils'); vi.mocked(getCookieMock.getCookie).mockImplementation((key) => { if (key === 'impactSource') return 'direct'; @@ -353,7 +353,7 @@ describe('Testing Impact Service', () => { expect(axiosSpy).not.toHaveBeenCalled(); }); - it('should not send to Impact when isFirstPurchase is false', async () => { + it('When isFirstPurchase is false, then it should not send to Impact', async () => { vi.spyOn(localStorageService, 'get').mockImplementation((key) => { if (key === 'isFirstPurchase') return 'false'; if (key === 'amountPaid') return expectedAmount; @@ -369,7 +369,7 @@ describe('Testing Impact Service', () => { }); describe('Error handling', () => { - it('should handle missing user settings gracefully', async () => { + it('When user settings are missing, then it should handle them gracefully', async () => { const consoleWarnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {}); vi.spyOn(localStorageService, 'getUser').mockReturnValue(null); @@ -378,13 +378,13 @@ describe('Testing Impact Service', () => { consoleWarnSpy.mockRestore(); }); - it('should continue execution when gtag is not available', async () => { + it('When gtag is not available, then it should continue execution', async () => { globalThis.window.gtag = undefined as any; await expect(trackPaymentConversion()).resolves.not.toThrow(); }); - it('should handle errors in entire function gracefully', async () => { + it('When an error occurs in the entire function, then it should handle it gracefully', async () => { const consoleErrorSpy = vi.spyOn(console, 'error').mockImplementation(() => {}); vi.spyOn(localStorageService, 'getUser').mockImplementation(() => { throw new Error('Storage Error'); @@ -399,7 +399,7 @@ describe('Testing Impact Service', () => { }); describe('uuid library', () => { - it('v4 generates a valid UUID', async () => { + it('When calling v4, then it should generate a valid UUID', async () => { const { v4 } = await vi.importActual('uuid'); const id = v4(); expect(id).toMatch(/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i); From f127be1be05dd9a9e7fa1787fbe77b6f6647d19d Mon Sep 17 00:00:00 2001 From: jaaaaavier Date: Mon, 11 May 2026 10:53:43 +0200 Subject: [PATCH 6/6] remove update --- src/app/analytics/impact.service.test.ts | 42 ++++++++++++------------ 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/src/app/analytics/impact.service.test.ts b/src/app/analytics/impact.service.test.ts index c12d68e08..bb90e177b 100644 --- a/src/app/analytics/impact.service.test.ts +++ b/src/app/analytics/impact.service.test.ts @@ -106,7 +106,7 @@ beforeEach(() => { describe('Testing Impact Service', () => { describe('savePaymentDataInLocalStorage', () => { - it('When coupon is applied, then it should save the correct amount to localStorage', () => { + it('should save the correct amount to localStorage after applying coupon', () => { const setToLocalStorageSpy = vi.spyOn(localStorageService, 'set'); savePaymentDataInLocalStorage({ @@ -121,7 +121,7 @@ describe('Testing Impact Service', () => { expect(setToLocalStorageSpy).toHaveBeenCalledWith('amountPaid', expectedAmount); }); - it('When plan is not lifetime, then it should save subscription ID', () => { + it('should save subscription ID when plan is not lifetime', () => { const setToLocalStorageSpy = vi.spyOn(localStorageService, 'set'); savePaymentDataInLocalStorage({ @@ -136,7 +136,7 @@ describe('Testing Impact Service', () => { expect(setToLocalStorageSpy).toHaveBeenCalledWith('subscriptionId', subId); }); - it('When plan is lifetime, then it should save payment intent ID', () => { + it('should save payment intent ID when plan is lifetime', () => { const setToLocalStorageSpy = vi.spyOn(localStorageService, 'set'); const lifetimeProduct = { ...product, @@ -155,7 +155,7 @@ describe('Testing Impact Service', () => { expect(setToLocalStorageSpy).toHaveBeenCalledWith('paymentIntentId', paymentIntentId); }); - it('When saving payment data, then it should save product metadata including name, price ID, and currency', () => { + it('should save product metadata including name, price ID, and currency', () => { const setToLocalStorageSpy = vi.spyOn(localStorageService, 'set'); savePaymentDataInLocalStorage({ @@ -172,7 +172,7 @@ describe('Testing Impact Service', () => { expect(setToLocalStorageSpy).toHaveBeenCalledWith('currency', product.price.currency); }); - it('When coupon code is provided, then it should save coupon code', () => { + it('should save coupon code when provided', () => { const setToLocalStorageSpy = vi.spyOn(localStorageService, 'set'); savePaymentDataInLocalStorage({ @@ -187,7 +187,7 @@ describe('Testing Impact Service', () => { expect(setToLocalStorageSpy).toHaveBeenCalledWith('couponCode', promoCode.codeName); }); - it('When saving payment data, then it should save isFirstPurchase flag to localStorage', () => { + it('should save isFirstPurchase flag to localStorage', () => { const setToLocalStorageSpy = vi.spyOn(localStorageService, 'set'); savePaymentDataInLocalStorage({ @@ -205,7 +205,7 @@ describe('Testing Impact Service', () => { describe('trackSignUp', () => { describe('gtag tracking', () => { - it('When tracking sign up, then it should send User Signup event to gtag', async () => { + it('should send User Signup event to gtag', async () => { const gTagSpy = vi.spyOn(globalThis.window, 'gtag'); await trackSignUp(mockedUserUuid); @@ -213,7 +213,7 @@ describe('Testing Impact Service', () => { expect(gTagSpy).toHaveBeenCalledWith('event', 'User Signup'); }); - it('When gtag fails, then it should report error but continue execution', async () => { + it('should report error when gtag fails but continue execution', async () => { const unknownError = new Error('gtag Error'); const gTagSpy = vi.spyOn(globalThis.window, 'gtag').mockImplementation(() => { throw unknownError; @@ -228,7 +228,7 @@ describe('Testing Impact Service', () => { }); describe('Impact API tracking', () => { - it('When tracking sign up, then it should send signup event to Impact API with correct payload', async () => { + it('should send signup event to Impact API with correct payload', async () => { const axiosSpy = vi.spyOn(axios, 'post').mockResolvedValue({}); await trackSignUp(mockedUserUuid); @@ -246,7 +246,7 @@ describe('Testing Impact Service', () => { ); }); - it('When tracking sign up, then it should include message ID in Impact API payload', async () => { + it('should include message ID in Impact API payload', async () => { const axiosSpy = vi.spyOn(axios, 'post').mockResolvedValue({}); await trackSignUp(mockedUserUuid); @@ -256,7 +256,7 @@ describe('Testing Impact Service', () => { expect(callArgs.messageId).toBe(mockedUserUuid); }); - it('When source is direct, then it should not send to Impact API', async () => { + it('should not send to Impact API when source is direct', async () => { const getCookieMock = await import('./utils'); vi.mocked(getCookieMock.getCookie).mockImplementation((key) => { if (key === 'impactSource') return 'direct'; @@ -274,7 +274,7 @@ describe('Testing Impact Service', () => { describe('trackPaymentConversion', () => { describe('Impact API tracking', () => { - it('When tracking payment conversion, then it should send payment conversion to Impact API with correct data', async () => { + it('should send payment conversion to Impact API with correct data', async () => { const axiosSpy = vi.spyOn(axios, 'post').mockResolvedValue({}); await trackPaymentConversion(); @@ -298,7 +298,7 @@ describe('Testing Impact Service', () => { ); }); - it('When amount is 0 (free purchase), then it should use minimum value of 0.01', async () => { + it('should use minimum value of 0.01 when amount is 0 (free purchase)', async () => { vi.spyOn(localStorageService, 'get').mockImplementation((key) => { if (key === 'amountPaid') return '0'; if (key === 'subscriptionId') return subId; @@ -314,7 +314,7 @@ describe('Testing Impact Service', () => { expect(callArgs.properties.impact_value).toBe(0.01); }); - it('When coupon code is available, then it should include coupon code in properties', async () => { + it('should include coupon code in properties when available', async () => { const axiosSpy = vi.spyOn(axios, 'post').mockResolvedValue({}); await trackPaymentConversion(); @@ -323,7 +323,7 @@ describe('Testing Impact Service', () => { expect(callArgs.properties).toHaveProperty('order_promo_code', promoCode.codeName); }); - it('When Impact API call fails, then it should report error', async () => { + it('should report error when Impact API call fails', async () => { const unknownError = new Error('API Error'); const axiosSpy = vi.spyOn(axios, 'post').mockRejectedValue(unknownError); const errorServiceSpy = vi.spyOn(errorService, 'reportError'); @@ -334,7 +334,7 @@ describe('Testing Impact Service', () => { expect(errorServiceSpy).toHaveBeenCalledWith(unknownError); }); - it('When source is direct and no coupon code, then it should not send to Impact', async () => { + it('should not send to Impact when source is direct and no coupon code', async () => { const getCookieMock = await import('./utils'); vi.mocked(getCookieMock.getCookie).mockImplementation((key) => { if (key === 'impactSource') return 'direct'; @@ -353,7 +353,7 @@ describe('Testing Impact Service', () => { expect(axiosSpy).not.toHaveBeenCalled(); }); - it('When isFirstPurchase is false, then it should not send to Impact', async () => { + it('should not send to Impact when isFirstPurchase is false', async () => { vi.spyOn(localStorageService, 'get').mockImplementation((key) => { if (key === 'isFirstPurchase') return 'false'; if (key === 'amountPaid') return expectedAmount; @@ -369,7 +369,7 @@ describe('Testing Impact Service', () => { }); describe('Error handling', () => { - it('When user settings are missing, then it should handle them gracefully', async () => { + it('should handle missing user settings gracefully', async () => { const consoleWarnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {}); vi.spyOn(localStorageService, 'getUser').mockReturnValue(null); @@ -378,13 +378,13 @@ describe('Testing Impact Service', () => { consoleWarnSpy.mockRestore(); }); - it('When gtag is not available, then it should continue execution', async () => { + it('should continue execution when gtag is not available', async () => { globalThis.window.gtag = undefined as any; await expect(trackPaymentConversion()).resolves.not.toThrow(); }); - it('When an error occurs in the entire function, then it should handle it gracefully', async () => { + it('should handle errors in entire function gracefully', async () => { const consoleErrorSpy = vi.spyOn(console, 'error').mockImplementation(() => {}); vi.spyOn(localStorageService, 'getUser').mockImplementation(() => { throw new Error('Storage Error'); @@ -399,7 +399,7 @@ describe('Testing Impact Service', () => { }); describe('uuid library', () => { - it('When calling v4, then it should generate a valid UUID', async () => { + it('v4 generates a valid UUID', async () => { const { v4 } = await vi.importActual('uuid'); const id = v4(); expect(id).toMatch(/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i);