diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index 785ec9ee..e035d1c3 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -104,7 +104,7 @@ jobs: cd runtipi echo 'Checking out branch ${{ github.head_ref }}' git checkout ${{ github.head_ref }} - sudo ./scripts/start-e2e.sh latest + sudo ./scripts/start-e2e.sh e2e echo 'App deployed' host: ${{ steps.get-droplet-ip.outputs.droplet_ip }} user: root diff --git a/e2e/0003-apps.spec.ts b/e2e/0003-apps.spec.ts index e44ff3c6..c0696b27 100644 --- a/e2e/0003-apps.spec.ts +++ b/e2e/0003-apps.spec.ts @@ -30,8 +30,8 @@ test('user can install and uninstall app', async ({ page, context }) => { await expect(page.getByText('Running')).toBeVisible({ timeout: 60000 }); await expect(page.getByText('App installed successfully')).toBeVisible(); - await page.getByTestId('app-details').getByRole('button', { name: 'Open' }).click(); - const [newPage] = await Promise.all([context.waitForEvent('page'), await page.getByRole('menuitem', { name: 'localhost:8000' }).click()]); + await page.getByTestId('app-details').getByRole('button', { name: 'Open' }).press('ArrowDown'); + const [newPage] = await Promise.all([context.waitForEvent('page'), await page.getByRole('menuitem', { name: `${process.env.SERVER_IP}:8000` }).click()]); await newPage.waitForLoadState(); await expect(newPage.getByText('Hello World')).toBeVisible(); diff --git a/scripts/common.sh b/scripts/common.sh index 40831983..f5446e4c 100644 --- a/scripts/common.sh +++ b/scripts/common.sh @@ -114,9 +114,11 @@ function generateTLSCert() { if ! openssl req -x509 -newkey rsa:4096 -keyout traefik/tls/key.pem -out traefik/tls/cert.pem -days 365 -subj "/O=runtipi.io/OU=IT/CN=*.${domain}/emailAddress=webmaster@${domain}" -addext "subjectAltName = DNS:*.${domain},DNS:${domain}" -nodes; then echo "Failed to generate TLS certificate" + else + echo "TLS certificate generated" + # Create a file to indicate that the certificate has been generated for this domain + touch "traefik/tls/$domain.txt" fi - # Create a file to indicate that the certificate has been generated for this domain - touch "traefik/tls/$domain.txt" } diff --git a/scripts/start.sh b/scripts/start.sh index 73820d5e..4d093d1b 100755 --- a/scripts/start.sh +++ b/scripts/start.sh @@ -103,7 +103,7 @@ env_variables_json=$(cat < { }); spy.mockRestore(); }); + + it('should open with domain when domain is clicked', async () => { + // Arrange + const app = createAppEntity({ overrides: { domain: 'test.com', exposed: true } }); + const spy = jest.spyOn(window, 'open').mockImplementation(() => null); + render(); + + // Act + const openButton = screen.getByRole('button', { name: 'Open' }); + userEvent.type(openButton, '{arrowdown}'); + + await waitFor(() => { + expect(screen.getByText(/test.com/)).toBeInTheDocument(); + }); + + const openButtonItem = screen.getByText(/test.com/); + userEvent.click(openButtonItem); + + // Assert + await waitFor(() => { + expect(spy).toHaveBeenCalledWith(`https://test.com`, '_blank', 'noreferrer'); + }); + spy.mockRestore(); + }); + + it('should open with local domain when local domain is clicked', async () => { + // Arrange + const app = createAppEntity({}); + const spy = jest.spyOn(window, 'open').mockImplementation(() => null); + render(); + + // Act + const openButton = screen.getByRole('button', { name: 'Open' }); + userEvent.type(openButton, '{arrowdown}'); + + await waitFor(() => { + expect(screen.getByText(/.tipi.lan/)).toBeInTheDocument(); + }); + + const openButtonItem = screen.getByText(/.tipi.lan/); + userEvent.click(openButtonItem); + + // Assert + await waitFor(() => { + expect(spy).toHaveBeenCalledWith(`https://${app.id}.tipi.lan`, '_blank', 'noreferrer'); + }); + spy.mockRestore(); + }); }); describe('Test: Install app', () => { diff --git a/src/client/modules/Settings/components/SettingsForm/SettingsForm.test.tsx b/src/client/modules/Settings/components/SettingsForm/SettingsForm.test.tsx index f3cd0e2b..7d6b931a 100644 --- a/src/client/modules/Settings/components/SettingsForm/SettingsForm.test.tsx +++ b/src/client/modules/Settings/components/SettingsForm/SettingsForm.test.tsx @@ -39,6 +39,7 @@ describe('Test: SettingsForm', () => { internalIp: 'invalid internal ip', appsRepoUrl: 'invalid url', storagePath: 'invalid path', + localDomain: 'invalid local domain', }; render(); @@ -50,6 +51,7 @@ describe('Test: SettingsForm', () => { expect(screen.getByText(submitErrors.internalIp)).toBeInTheDocument(); expect(screen.getByText(submitErrors.appsRepoUrl)).toBeInTheDocument(); expect(screen.getByText(submitErrors.storagePath)).toBeInTheDocument(); + expect(screen.getByText(submitErrors.localDomain)).toBeInTheDocument(); }); it('should correctly validate the form', async () => { @@ -60,19 +62,21 @@ describe('Test: SettingsForm', () => { const domainInput = screen.getByRole('textbox', { name: 'domain' }); const internalIpInput = screen.getByRole('textbox', { name: 'internalIp' }); const appsRepoUrlInput = screen.getByRole('textbox', { name: 'appsRepoUrl' }); + const localDomainInput = screen.getByRole('textbox', { name: 'localDomain' }); // act fireEvent.change(dnsIpInput, { target: { value: 'invalid ip' } }); fireEvent.change(domainInput, { target: { value: 'invalid domain' } }); fireEvent.change(internalIpInput, { target: { value: 'invalid internal ip' } }); fireEvent.change(appsRepoUrlInput, { target: { value: 'invalid url' } }); + fireEvent.change(localDomainInput, { target: { value: 'invalid local domain' } }); fireEvent.click(submitButton); // assert await waitFor(() => { expect(screen.getAllByText('Invalid IP address')).toHaveLength(2); }); - expect(screen.getByText('Invalid domain')).toBeInTheDocument(); + expect(screen.getAllByText('Invalid domain')).toHaveLength(2); expect(screen.getByText('Invalid URL')).toBeInTheDocument(); }); @@ -90,4 +94,19 @@ describe('Test: SettingsForm', () => { expect(onSubmit).toHaveBeenCalledTimes(1); }); }); + + it('should download the certificate when the download button is clicked', async () => { + // arrange + const spy = jest.spyOn(window, 'open').mockImplementation(); + render(); + const downloadButton = screen.getByRole('button', { name: 'Download certificate' }); + + // act + fireEvent.click(downloadButton); + + // assert + await waitFor(() => { + expect(spy).toHaveBeenCalledTimes(1); + }); + }); }); diff --git a/src/client/modules/Settings/components/SettingsForm/SettingsForm.tsx b/src/client/modules/Settings/components/SettingsForm/SettingsForm.tsx index 14c03856..097ae2bc 100644 --- a/src/client/modules/Settings/components/SettingsForm/SettingsForm.tsx +++ b/src/client/modules/Settings/components/SettingsForm/SettingsForm.tsx @@ -183,9 +183,8 @@ export const SettingsForm = (props: IProps) => { error={errors.localDomain?.message} placeholder="tipi.lan" /> -