From 2e5f3d8c2e427cd9a3885cb616ac2d31c835ed30 Mon Sep 17 00:00:00 2001 From: Ido Shamun <1993245+idoshamun@users.noreply.github.com> Date: Tue, 14 Apr 2026 13:00:18 +0300 Subject: [PATCH] fix: update hijacking strip for onboarding state --- .../src/newtab/HijackingLoginStrip.spec.tsx | 131 ++++++++++++++++++ .../src/newtab/HijackingLoginStrip.tsx | 69 ++++++--- 2 files changed, 178 insertions(+), 22 deletions(-) create mode 100644 packages/extension/src/newtab/HijackingLoginStrip.spec.tsx diff --git a/packages/extension/src/newtab/HijackingLoginStrip.spec.tsx b/packages/extension/src/newtab/HijackingLoginStrip.spec.tsx new file mode 100644 index 0000000000..087c8a528b --- /dev/null +++ b/packages/extension/src/newtab/HijackingLoginStrip.spec.tsx @@ -0,0 +1,131 @@ +import React from 'react'; +import { fireEvent, render, screen } from '@testing-library/react'; +import type { AuthContextData } from '@dailydotdev/shared/src/contexts/AuthContext'; +import { useAuthContext } from '@dailydotdev/shared/src/contexts/AuthContext'; +import { getLogContextStatic } from '@dailydotdev/shared/src/contexts/LogContext'; +import { AuthTriggers } from '@dailydotdev/shared/src/lib/auth'; +import { onboardingUrl } from '@dailydotdev/shared/src/lib/constants'; +import { LogEvent, TargetType } from '@dailydotdev/shared/src/lib/log'; +import loggedUser from '@dailydotdev/shared/__tests__/fixture/loggedUser'; +import HijackingLoginStrip from './HijackingLoginStrip'; + +jest.mock('@dailydotdev/shared/src/contexts/AuthContext', () => ({ + ...jest.requireActual('@dailydotdev/shared/src/contexts/AuthContext'), + useAuthContext: jest.fn(), +})); + +const LogContext = getLogContextStatic(); +const mockUseAuthContext = useAuthContext as jest.MockedFunction< + typeof useAuthContext +>; +const logEvent = jest.fn(); +const showLogin = jest.fn(); + +const defaultAuthContext = { + user: undefined, + isLoggedIn: false, + referral: undefined, + referralOrigin: undefined, + trackingId: undefined, + shouldShowLogin: false, + showLogin, + closeLogin: jest.fn(), + loginState: undefined, + logout: jest.fn(), + updateUser: jest.fn(), + loadingUser: false, + isFetched: true, + tokenRefreshed: false, + loadedUserFromCache: false, + getRedirectUri: jest.fn(), + anonymous: undefined, + visit: undefined, + firstVisit: undefined, + deleteAccount: jest.fn(), + refetchBoot: jest.fn(), + accessToken: undefined, + squads: [], + isAuthReady: true, + geo: undefined, + isAndroidApp: false, + isGdprCovered: false, + isValidRegion: true, + isFunnel: false, +} satisfies AuthContextData; + +const renderComponent = ( + authContext: Partial = {}, +): ReturnType => { + mockUseAuthContext.mockReturnValue({ + ...defaultAuthContext, + ...authContext, + }); + + return render( + + + , + ); +}; + +beforeEach(() => { + jest.clearAllMocks(); +}); + +describe('HijackingLoginStrip', () => { + it('shows a login CTA for logged out users', () => { + renderComponent(); + + expect( + screen.getByText('Unlock the full daily.dev experience'), + ).toBeVisible(); + expect( + screen.getByText('Log in to pick up where you left off.'), + ).toBeVisible(); + + const cta = screen.getByRole('button', { name: 'Log in to continue' }); + fireEvent.click(cta); + + expect(logEvent).toHaveBeenCalledWith({ + event_name: LogEvent.Click, + target_type: TargetType.LoginButton, + target_id: 'hijacking', + }); + expect(showLogin).toHaveBeenCalledWith({ + trigger: AuthTriggers.Onboarding, + options: { isLogin: true }, + }); + }); + + it('shows an onboarding CTA for logged in users who still need onboarding', () => { + renderComponent({ user: loggedUser, isLoggedIn: true }); + + expect( + screen.getByText( + 'You still have a few onboarding steps left. Finish them to unlock the full experience.', + ), + ).toBeVisible(); + + const cta = screen.getByRole('link', { name: 'Continue onboarding' }); + const expectedUrl = new URL(onboardingUrl); + expectedUrl.searchParams.append('r', 'extension'); + + expect(cta).toHaveAttribute('href', expectedUrl.toString()); + + fireEvent.click(cta); + + expect(logEvent).toHaveBeenCalledWith({ + event_name: LogEvent.Click, + target_type: TargetType.LoginButton, + target_id: 'hijacking', + }); + expect(showLogin).not.toHaveBeenCalled(); + }); +}); diff --git a/packages/extension/src/newtab/HijackingLoginStrip.tsx b/packages/extension/src/newtab/HijackingLoginStrip.tsx index 00d9db29c1..7185dbc58a 100644 --- a/packages/extension/src/newtab/HijackingLoginStrip.tsx +++ b/packages/extension/src/newtab/HijackingLoginStrip.tsx @@ -8,13 +8,29 @@ import { import { useAuthContext } from '@dailydotdev/shared/src/contexts/AuthContext'; import { useLogContext } from '@dailydotdev/shared/src/contexts/LogContext'; import { AuthTriggers } from '@dailydotdev/shared/src/lib/auth'; +import { onboardingUrl } from '@dailydotdev/shared/src/lib/constants'; import { LogEvent, TargetType } from '@dailydotdev/shared/src/lib/log'; import feedStyles from '@dailydotdev/shared/src/components/Feed.module.css'; import { cloudinaryReadingReminderCat } from '@dailydotdev/shared/src/lib/image'; export default function HijackingLoginStrip(): ReactElement { - const { showLogin } = useAuthContext(); + const { showLogin, user } = useAuthContext(); const { logEvent } = useLogContext(); + const isLoggedOut = !user; + const onboardingHref = (() => { + const base = new URL(onboardingUrl); + base.searchParams.append('r', 'extension'); + + return base.toString(); + })(); + + const logHijackingClick = (): void => { + logEvent({ + event_name: LogEvent.Click, + target_type: TargetType.LoginButton, + target_id: 'hijacking', + }); + }; return (
@@ -28,31 +44,40 @@ export default function HijackingLoginStrip(): ReactElement {

- Log in to unlock the full daily.dev experience + Unlock the full daily.dev experience

- If you were in the middle of onboarding, you can pick up right - where you left off. + {isLoggedOut + ? 'Log in to pick up where you left off.' + : 'You still have a few onboarding steps left. Finish them to unlock the full experience.'}

- + showLogin({ + trigger: AuthTriggers.Onboarding, + options: { isLogin: true }, + }); + }} + > + Log in to continue + + ) : ( + + )}