From 4166bab8ab73347bc70d44f9ed1f8ea1d8a0a8c8 Mon Sep 17 00:00:00 2001 From: dstrukt Date: Fri, 31 Jul 2026 11:25:09 -0600 Subject: [PATCH] app: add tests for the Home page connect UI Cover the merged Home page: the connect buttons and session list rendering, creating a session through the add session form, copying a pairing phrase and revoking a session. Also assert that the routes for Home, the custom session form and the /connect redirect resolve to the right page. --- app/src/__tests__/AppRoutes.spec.tsx | 31 ++++ .../components/home/HomePage.spec.tsx | 145 ++++++++++++++++++ 2 files changed, 176 insertions(+) create mode 100644 app/src/__tests__/components/home/HomePage.spec.tsx diff --git a/app/src/__tests__/AppRoutes.spec.tsx b/app/src/__tests__/AppRoutes.spec.tsx index 0b1d54b3..7f4c9c77 100644 --- a/app/src/__tests__/AppRoutes.spec.tsx +++ b/app/src/__tests__/AppRoutes.spec.tsx @@ -8,6 +8,8 @@ describe('Routes Component', () => { let store: Store; beforeEach(async () => { + // jsdom does not implement scrollTo, which some pages call when mounted + window.scrollTo = jest.fn(); store = createStore(); await store.init(); }); @@ -28,6 +30,35 @@ describe('Routes Component', () => { await store.authStore.login('pw'); }); + it('should display the Home page with the connect UI', async () => { + const { findByText, store } = render(); + act(() => { + store.appView.goToHome(); + }); + expect(await findByText('Connect to Terminal')).toBeInTheDocument(); + expect(await findByText('Connect with QR')).toBeInTheDocument(); + expect(await findByText('Create a new session')).toBeInTheDocument(); + expect(store.router.location.pathname).toBe('/home'); + }); + + it('should redirect the old Connect route to the Home page', async () => { + const { findByText, store } = render(); + act(() => { + store.router.push('/connect'); + }); + expect(await findByText('Create a new session')).toBeInTheDocument(); + expect(store.router.location.pathname).toBe('/home'); + }); + + it('should display the Custom Session page', async () => { + const { findByText, store } = render(); + act(() => { + store.appView.goToConnectCustom(); + }); + expect(await findByText('Custom Permissions')).toBeInTheDocument(); + expect(store.router.location.pathname).toBe('/connect/custom'); + }); + it('should display the Loop page', async () => { const { findByText, store } = render(); act(() => { diff --git a/app/src/__tests__/components/home/HomePage.spec.tsx b/app/src/__tests__/components/home/HomePage.spec.tsx new file mode 100644 index 00000000..ddc9e914 --- /dev/null +++ b/app/src/__tests__/components/home/HomePage.spec.tsx @@ -0,0 +1,145 @@ +import React from 'react'; +import { fireEvent, waitFor } from '@testing-library/react'; +import copyToClipboard from 'copy-to-clipboard'; +import { renderWithProviders } from 'util/tests'; +import { createStore, Store } from 'store'; +import HomePage from 'components/home/HomePage'; + +jest.mock('copy-to-clipboard'); + +describe('HomePage', () => { + let store: Store; + + beforeEach(async () => { + store = createStore(); + await store.fetchAllData(); + }); + + const render = () => { + return renderWithProviders(, store); + }; + + it('should display the page title', () => { + const { getByText } = render(); + expect(getByText('Home')).toBeInTheDocument(); + }); + + it('should display the description', () => { + const { getByText } = render(); + expect( + getByText( + 'Securely and privately connect your node to Terminal on the web via the links below. For mobile, generate a QR code to connect.', + ), + ).toBeInTheDocument(); + }); + + it('should display the three connect buttons on one line', () => { + const { getByText } = render(); + const terminal = getByText('Connect to Terminal'); + const qr = getByText('Connect with QR'); + const create = getByText('Create a new session'); + expect(terminal).toBeInTheDocument(); + expect(qr).toBeInTheDocument(); + expect(create).toBeInTheDocument(); + // all three buttons are siblings in the same row + expect(qr.parentElement).toBe(terminal.parentElement); + expect(create.parentElement).toBe(terminal.parentElement); + }); + + it('should display the session list', async () => { + const { getByText } = render(); + await waitFor(() => { + expect(store.sessionStore.sortedSessions).not.toHaveLength(0); + }); + expect(getByText('Label')).toBeInTheDocument(); + expect(getByText('Type')).toBeInTheDocument(); + expect(getByText('State')).toBeInTheDocument(); + expect(getByText('Expiry')).toBeInTheDocument(); + }); + + it('should display the add session form', () => { + const { getByText, getByPlaceholderText } = render(); + fireEvent.click(getByText('Create a new session')); + expect(getByPlaceholderText('My First Session')).toBeInTheDocument(); + expect(getByText('Permissions')).toBeInTheDocument(); + }); + + it('should create a new session and add it to the list', async () => { + const { getByText, getByPlaceholderText, findByText, store } = render(); + const count = store.sessionStore.sortedSessions.length; + + fireEvent.click(getByText('Create a new session')); + fireEvent.change(getByPlaceholderText('My First Session'), { + target: { value: 'Home Page Session' }, + }); + fireEvent.click(getByText('Submit')); + + expect(await findByText('Session Created')).toBeInTheDocument(); + // the new session is added to the list. the rows themselves are virtualized, so + // they are not rendered in the test environment where the list has no width + await waitFor(() => { + expect(store.sessionStore.sortedSessions).toHaveLength(count + 1); + }); + const session = store.sessionStore.sortedSessions.find( + s => s.label === 'Home Page Session', + ); + expect(session).toBeDefined(); + expect(session?.pairingSecretMnemonic).not.toBe(''); + // its pairing phrase is copied to the clipboard + expect(copyToClipboard).toBeCalledWith(session?.pairingSecretMnemonic); + // the form is closed once the session is created + expect(getByText('Create a new session')).toBeInTheDocument(); + }); + + it('should display the QR code modal', async () => { + const { getByText, findByText } = render(); + fireEvent.click(getByText('Connect with QR')); + expect(await findByText('LNC QR')).toBeInTheDocument(); + expect( + getByText('Scan to connect to Terminal from your mobile phone.'), + ).toBeInTheDocument(); + }); + + it('should open a new tab to connect to the web terminal', async () => { + const location = { replace: jest.fn() }; + const open = jest + .spyOn(window, 'open') + .mockImplementation(() => ({ location } as any)); + + const { getByText } = render(); + fireEvent.click(getByText('Connect to Terminal')); + await waitFor(() => { + expect(location.replace).toBeCalled(); + }); + + open.mockRestore(); + }); + + it('should display the learn more button', () => { + const { getByText, queryByText } = render(); + expect(getByText('Learn More')).toBeInTheDocument(); + expect( + queryByText( + 'The connection to your node occurs through the Lightning Node Connect protocol.', + ), + ).not.toBeInTheDocument(); + }); + + it('should not display the removed sections', () => { + const { queryByText } = render(); + expect(queryByText(`What's different?`)).not.toBeInTheDocument(); + expect(queryByText('Improved Lightning Loop UX')).not.toBeInTheDocument(); + expect(queryByText('Lightning Terminal Dashboard')).not.toBeInTheDocument(); + expect( + queryByText( + 'Lightning Node Connect enables you to connect to this node from the web.', + ), + ).not.toBeInTheDocument(); + }); + + it('should display the youtube modal', () => { + const { getByText } = render(); + fireEvent.click(getByText('Learn More')); + expect(getByText('Get Connected')).toBeInTheDocument(); + }); +});