Skip to content

Commit 72b6d43

Browse files
Update silo creation form to require a TLS certificate (#2579)
* Update silo creation form to require a TLS certificate * Add test verifying TLS cert requirement in form * Move error message to field, out of msw handler * Update error message copy * Move addTlsCert to e2e utils function * use CertificateCreate type directly * use validate instead of required for TlsCertsField requiredness --------- Co-authored-by: David Crespo <[email protected]>
1 parent 15e5504 commit 72b6d43

File tree

4 files changed

+44
-4
lines changed

4 files changed

+44
-4
lines changed

app/components/form/fields/TlsCertsField.tsx

+17-4
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import * as MiniTable from '~/ui/lib/MiniTable'
1818
import { Modal } from '~/ui/lib/Modal'
1919

2020
import { DescriptionField } from './DescriptionField'
21+
import { ErrorMessage } from './ErrorMessage'
2122
import { FileField } from './FileField'
2223
import { validateName } from './NameField'
2324
import { TextField } from './TextField'
@@ -26,8 +27,18 @@ export function TlsCertsField({ control }: { control: Control<SiloCreateFormValu
2627
const [showAddCert, setShowAddCert] = useState(false)
2728

2829
const {
29-
field: { value: items, onChange },
30-
} = useController({ control, name: 'tlsCertificates' })
30+
field: { value: items, onChange, ref },
31+
fieldState: { error },
32+
} = useController({
33+
control,
34+
name: 'tlsCertificates',
35+
rules: {
36+
// docs recommend validate over required for array-valued field
37+
// https://react-hook-form.com/docs/useform/register
38+
validate: (certs) =>
39+
certs.length < 1 ? 'At least one certificate is required' : undefined,
40+
},
41+
})
3142

3243
return (
3344
<>
@@ -61,16 +72,18 @@ export function TlsCertsField({ control }: { control: Control<SiloCreateFormValu
6172
</MiniTable.Table>
6273
)}
6374

64-
<Button size="sm" onClick={() => setShowAddCert(true)}>
75+
{/* ref on button element allows scrollTo to work when the form has a "missing TLS cert" error */}
76+
<Button size="sm" onClick={() => setShowAddCert(true)} ref={ref}>
6577
Add TLS certificate
6678
</Button>
79+
<ErrorMessage error={error} label="TLS certificate" />
6780
</div>
6881

6982
{showAddCert && (
7083
<AddCertModal
7184
onDismiss={() => setShowAddCert(false)}
7285
onSubmit={async (values) => {
73-
const certCreate: (typeof items)[number] = {
86+
const certCreate: CertificateCreate = {
7487
...values,
7588
// cert and key are required fields. they will always be present if we get here
7689
cert: await values.cert!.text(),

test/e2e/silos.e2e.ts

+11
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import { expect, test } from '@playwright/test'
99

1010
import {
11+
addTlsCert,
1112
chooseFile,
1213
clickRowAction,
1314
closeToast,
@@ -31,6 +32,9 @@ test('Create silo', async ({ page }) => {
3132

3233
await page.click('role=link[name="New silo"]')
3334

35+
const modal = page.getByRole('dialog', { name: 'Create silo' })
36+
await expect(modal).toBeVisible()
37+
3438
// fill out form
3539
await page.getByRole('textbox', { name: 'Name', exact: true }).fill('other-silo')
3640
await page.getByRole('textbox', { name: 'Description' }).fill('definitely a silo')
@@ -68,6 +72,11 @@ test('Create silo', async ({ page }) => {
6872
await page.getByRole('textbox', { name: 'Memory quota' }).fill('58')
6973
await page.getByRole('textbox', { name: 'Storage quota' }).fill('735')
7074

75+
await page.getByRole('button', { name: 'Create silo' }).click()
76+
77+
// expect error because no TLS cert
78+
await expect(modal.getByText('At least one certificate is required')).toBeVisible()
79+
7180
////////////////////////////
7281
// TLS CERT
7382
////////////////////////////
@@ -348,6 +357,8 @@ test('form scrolls to name field on already exists error', async ({ page }) => {
348357
.evaluate((el: HTMLElement, to) => el.scrollTo(0, to), 800)
349358
await expect(nameField).not.toBeInViewport()
350359

360+
await addTlsCert(page)
361+
351362
await page.getByRole('button', { name: 'Create silo' }).click()
352363

353364
await expect(nameField).toBeInViewport()

test/e2e/utilization.e2e.ts

+5
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
* Copyright Oxide Computer Company
77
*/
88
import {
9+
addTlsCert,
910
clickRowAction,
1011
clipboardText,
1112
closeToast,
@@ -70,6 +71,10 @@ test.describe('System utilization', () => {
7071
await page.goto('/system/silos-new')
7172
await page.getByRole('textbox', { name: 'Name', exact: true }).fill('all-zeros')
7273
// don't need to set silo values, they're zero by default
74+
75+
// but do need to add a tls cert
76+
await addTlsCert(page)
77+
7378
await page.getByRole('button', { name: 'Create silo' }).click()
7479

7580
await closeToast(page)

test/e2e/utils.ts

+11
Original file line numberDiff line numberDiff line change
@@ -243,3 +243,14 @@ export async function scrollTo(page: Page, to: number) {
243243
const container = page.getByTestId('scroll-container')
244244
await container.evaluate((el: HTMLElement, to) => el.scrollTo(0, to), to)
245245
}
246+
247+
export async function addTlsCert(page: Page) {
248+
page.getByRole('button', { name: 'Add TLS certificate' }).click()
249+
await page
250+
.getByRole('dialog', { name: 'Add TLS certificate' })
251+
.getByRole('textbox', { name: 'Name' })
252+
.fill('test-cert')
253+
await chooseFile(page, page.getByLabel('Cert', { exact: true }), 'small')
254+
await chooseFile(page, page.getByLabel('Key'), 'small')
255+
await page.getByRole('button', { name: 'Add Certificate' }).click()
256+
}

0 commit comments

Comments
 (0)