test: add coverage for new features
This commit is contained in:
parent
25ad967ccb
commit
70b6c20b29
10 changed files with 81 additions and 12 deletions
2
.github/workflows/e2e.yml
vendored
2
.github/workflows/e2e.yml
vendored
|
@ -104,7 +104,7 @@ jobs:
|
||||||
cd runtipi
|
cd runtipi
|
||||||
echo 'Checking out branch ${{ github.head_ref }}'
|
echo 'Checking out branch ${{ github.head_ref }}'
|
||||||
git checkout ${{ github.head_ref }}
|
git checkout ${{ github.head_ref }}
|
||||||
sudo ./scripts/start-e2e.sh latest
|
sudo ./scripts/start-e2e.sh e2e
|
||||||
echo 'App deployed'
|
echo 'App deployed'
|
||||||
host: ${{ steps.get-droplet-ip.outputs.droplet_ip }}
|
host: ${{ steps.get-droplet-ip.outputs.droplet_ip }}
|
||||||
user: root
|
user: root
|
||||||
|
|
|
@ -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('Running')).toBeVisible({ timeout: 60000 });
|
||||||
await expect(page.getByText('App installed successfully')).toBeVisible();
|
await expect(page.getByText('App installed successfully')).toBeVisible();
|
||||||
|
|
||||||
await page.getByTestId('app-details').getByRole('button', { name: 'Open' }).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: 'localhost:8000' }).click()]);
|
const [newPage] = await Promise.all([context.waitForEvent('page'), await page.getByRole('menuitem', { name: `${process.env.SERVER_IP}:8000` }).click()]);
|
||||||
|
|
||||||
await newPage.waitForLoadState();
|
await newPage.waitForLoadState();
|
||||||
await expect(newPage.getByText('Hello World')).toBeVisible();
|
await expect(newPage.getByText('Hello World')).toBeVisible();
|
||||||
|
|
|
@ -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
|
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"
|
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
|
fi
|
||||||
# Create a file to indicate that the certificate has been generated for this domain
|
|
||||||
touch "traefik/tls/$domain.txt"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -103,7 +103,7 @@ env_variables_json=$(cat <<EOF
|
||||||
"local_domain": "tipi.lan",
|
"local_domain": "tipi.lan",
|
||||||
"repo_id": "$("${ROOT_FOLDER}"/scripts/git.sh get_hash "${apps_repository}")",
|
"repo_id": "$("${ROOT_FOLDER}"/scripts/git.sh get_hash "${apps_repository}")",
|
||||||
"apps_repository": "${apps_repository}",
|
"apps_repository": "${apps_repository}",
|
||||||
"domain": "tipi.lan",
|
"domain": "example.com",
|
||||||
"storage_path": "${ROOT_FOLDER}",
|
"storage_path": "${ROOT_FOLDER}",
|
||||||
"demo_mode": false,
|
"demo_mode": false,
|
||||||
}
|
}
|
||||||
|
|
|
@ -253,7 +253,8 @@
|
||||||
"submit": "Save",
|
"submit": "Save",
|
||||||
"user-settings-title": "User settings",
|
"user-settings-title": "User settings",
|
||||||
"language": "Language",
|
"language": "Language",
|
||||||
"help-translate": "Help translate Tipi"
|
"help-translate": "Help translate Tipi",
|
||||||
|
"download-certificate": "Download certificate"
|
||||||
},
|
},
|
||||||
"security": {
|
"security": {
|
||||||
"tab-title": "Security",
|
"tab-title": "Security",
|
||||||
|
|
|
@ -29,7 +29,7 @@ export const handlers = [
|
||||||
getTRPCMock({
|
getTRPCMock({
|
||||||
path: ['system', 'getSettings'],
|
path: ['system', 'getSettings'],
|
||||||
type: 'query',
|
type: 'query',
|
||||||
response: { internalIp: 'localhost', dnsIp: '1.1.1.1', appsRepoUrl: 'https://test.com/test', domain: 'tipi.localhost' },
|
response: { internalIp: 'localhost', dnsIp: '1.1.1.1', appsRepoUrl: 'https://test.com/test', domain: 'tipi.localhost', localDomain: 'tipi.lan' },
|
||||||
}),
|
}),
|
||||||
getTRPCMock({
|
getTRPCMock({
|
||||||
path: ['system', 'updateSettings'],
|
path: ['system', 'updateSettings'],
|
||||||
|
|
|
@ -124,6 +124,54 @@ describe('Test: AppDetailsContainer', () => {
|
||||||
});
|
});
|
||||||
spy.mockRestore();
|
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(<AppDetailsContainer app={app} />);
|
||||||
|
|
||||||
|
// 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(<AppDetailsContainer app={app} />);
|
||||||
|
|
||||||
|
// 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', () => {
|
describe('Test: Install app', () => {
|
||||||
|
|
|
@ -39,6 +39,7 @@ describe('Test: SettingsForm', () => {
|
||||||
internalIp: 'invalid internal ip',
|
internalIp: 'invalid internal ip',
|
||||||
appsRepoUrl: 'invalid url',
|
appsRepoUrl: 'invalid url',
|
||||||
storagePath: 'invalid path',
|
storagePath: 'invalid path',
|
||||||
|
localDomain: 'invalid local domain',
|
||||||
};
|
};
|
||||||
render(<SettingsForm onSubmit={jest.fn()} submitErrors={submitErrors} />);
|
render(<SettingsForm onSubmit={jest.fn()} submitErrors={submitErrors} />);
|
||||||
|
|
||||||
|
@ -50,6 +51,7 @@ describe('Test: SettingsForm', () => {
|
||||||
expect(screen.getByText(submitErrors.internalIp)).toBeInTheDocument();
|
expect(screen.getByText(submitErrors.internalIp)).toBeInTheDocument();
|
||||||
expect(screen.getByText(submitErrors.appsRepoUrl)).toBeInTheDocument();
|
expect(screen.getByText(submitErrors.appsRepoUrl)).toBeInTheDocument();
|
||||||
expect(screen.getByText(submitErrors.storagePath)).toBeInTheDocument();
|
expect(screen.getByText(submitErrors.storagePath)).toBeInTheDocument();
|
||||||
|
expect(screen.getByText(submitErrors.localDomain)).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should correctly validate the form', async () => {
|
it('should correctly validate the form', async () => {
|
||||||
|
@ -60,19 +62,21 @@ describe('Test: SettingsForm', () => {
|
||||||
const domainInput = screen.getByRole('textbox', { name: 'domain' });
|
const domainInput = screen.getByRole('textbox', { name: 'domain' });
|
||||||
const internalIpInput = screen.getByRole('textbox', { name: 'internalIp' });
|
const internalIpInput = screen.getByRole('textbox', { name: 'internalIp' });
|
||||||
const appsRepoUrlInput = screen.getByRole('textbox', { name: 'appsRepoUrl' });
|
const appsRepoUrlInput = screen.getByRole('textbox', { name: 'appsRepoUrl' });
|
||||||
|
const localDomainInput = screen.getByRole('textbox', { name: 'localDomain' });
|
||||||
|
|
||||||
// act
|
// act
|
||||||
fireEvent.change(dnsIpInput, { target: { value: 'invalid ip' } });
|
fireEvent.change(dnsIpInput, { target: { value: 'invalid ip' } });
|
||||||
fireEvent.change(domainInput, { target: { value: 'invalid domain' } });
|
fireEvent.change(domainInput, { target: { value: 'invalid domain' } });
|
||||||
fireEvent.change(internalIpInput, { target: { value: 'invalid internal ip' } });
|
fireEvent.change(internalIpInput, { target: { value: 'invalid internal ip' } });
|
||||||
fireEvent.change(appsRepoUrlInput, { target: { value: 'invalid url' } });
|
fireEvent.change(appsRepoUrlInput, { target: { value: 'invalid url' } });
|
||||||
|
fireEvent.change(localDomainInput, { target: { value: 'invalid local domain' } });
|
||||||
fireEvent.click(submitButton);
|
fireEvent.click(submitButton);
|
||||||
|
|
||||||
// assert
|
// assert
|
||||||
await waitFor(() => {
|
await waitFor(() => {
|
||||||
expect(screen.getAllByText('Invalid IP address')).toHaveLength(2);
|
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();
|
expect(screen.getByText('Invalid URL')).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -90,4 +94,19 @@ describe('Test: SettingsForm', () => {
|
||||||
expect(onSubmit).toHaveBeenCalledTimes(1);
|
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(<SettingsForm onSubmit={jest.fn} />);
|
||||||
|
const downloadButton = screen.getByRole('button', { name: 'Download certificate' });
|
||||||
|
|
||||||
|
// act
|
||||||
|
fireEvent.click(downloadButton);
|
||||||
|
|
||||||
|
// assert
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(spy).toHaveBeenCalledTimes(1);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -183,9 +183,8 @@ export const SettingsForm = (props: IProps) => {
|
||||||
error={errors.localDomain?.message}
|
error={errors.localDomain?.message}
|
||||||
placeholder="tipi.lan"
|
placeholder="tipi.lan"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Button className="mt-2" onClick={downloadCertificate}>
|
<Button className="mt-2" onClick={downloadCertificate}>
|
||||||
Download certificate
|
{t('download-certificate')}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
<Button loading={loading} type="submit" className="btn-success">
|
<Button loading={loading} type="submit" className="btn-success">
|
||||||
|
|
|
@ -65,7 +65,7 @@ nextApp.prepare().then(async () => {
|
||||||
EventDispatcher.clear();
|
EventDispatcher.clear();
|
||||||
|
|
||||||
// Run database migrations
|
// Run database migrations
|
||||||
if (getConfig().NODE_ENV === 'development') {
|
if (getConfig().NODE_ENV !== 'development') {
|
||||||
await runPostgresMigrations();
|
await runPostgresMigrations();
|
||||||
}
|
}
|
||||||
setConfig('status', 'RUNNING');
|
setConfig('status', 'RUNNING');
|
||||||
|
|
Loading…
Reference in a new issue