Skip to content

Commit

Permalink
Update silo creation form to require a TLS certificate (#2579)
Browse files Browse the repository at this point in the history
* 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]>
  • Loading branch information
charliepark and david-crespo authored Dec 3, 2024
1 parent 15e5504 commit 72b6d43
Show file tree
Hide file tree
Showing 4 changed files with 44 additions and 4 deletions.
21 changes: 17 additions & 4 deletions app/components/form/fields/TlsCertsField.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import * as MiniTable from '~/ui/lib/MiniTable'
import { Modal } from '~/ui/lib/Modal'

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

const {
field: { value: items, onChange },
} = useController({ control, name: 'tlsCertificates' })
field: { value: items, onChange, ref },
fieldState: { error },
} = useController({
control,
name: 'tlsCertificates',
rules: {
// docs recommend validate over required for array-valued field
// https://react-hook-form.com/docs/useform/register
validate: (certs) =>
certs.length < 1 ? 'At least one certificate is required' : undefined,
},
})

return (
<>
Expand Down Expand Up @@ -61,16 +72,18 @@ export function TlsCertsField({ control }: { control: Control<SiloCreateFormValu
</MiniTable.Table>
)}

<Button size="sm" onClick={() => setShowAddCert(true)}>
{/* ref on button element allows scrollTo to work when the form has a "missing TLS cert" error */}
<Button size="sm" onClick={() => setShowAddCert(true)} ref={ref}>
Add TLS certificate
</Button>
<ErrorMessage error={error} label="TLS certificate" />
</div>

{showAddCert && (
<AddCertModal
onDismiss={() => setShowAddCert(false)}
onSubmit={async (values) => {
const certCreate: (typeof items)[number] = {
const certCreate: CertificateCreate = {
...values,
// cert and key are required fields. they will always be present if we get here
cert: await values.cert!.text(),
Expand Down
11 changes: 11 additions & 0 deletions test/e2e/silos.e2e.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import { expect, test } from '@playwright/test'

import {
addTlsCert,
chooseFile,
clickRowAction,
closeToast,
Expand All @@ -31,6 +32,9 @@ test('Create silo', async ({ page }) => {

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

const modal = page.getByRole('dialog', { name: 'Create silo' })
await expect(modal).toBeVisible()

// fill out form
await page.getByRole('textbox', { name: 'Name', exact: true }).fill('other-silo')
await page.getByRole('textbox', { name: 'Description' }).fill('definitely a silo')
Expand Down Expand Up @@ -68,6 +72,11 @@ test('Create silo', async ({ page }) => {
await page.getByRole('textbox', { name: 'Memory quota' }).fill('58')
await page.getByRole('textbox', { name: 'Storage quota' }).fill('735')

await page.getByRole('button', { name: 'Create silo' }).click()

// expect error because no TLS cert
await expect(modal.getByText('At least one certificate is required')).toBeVisible()

////////////////////////////
// TLS CERT
////////////////////////////
Expand Down Expand Up @@ -348,6 +357,8 @@ test('form scrolls to name field on already exists error', async ({ page }) => {
.evaluate((el: HTMLElement, to) => el.scrollTo(0, to), 800)
await expect(nameField).not.toBeInViewport()

await addTlsCert(page)

await page.getByRole('button', { name: 'Create silo' }).click()

await expect(nameField).toBeInViewport()
Expand Down
5 changes: 5 additions & 0 deletions test/e2e/utilization.e2e.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
* Copyright Oxide Computer Company
*/
import {
addTlsCert,
clickRowAction,
clipboardText,
closeToast,
Expand Down Expand Up @@ -70,6 +71,10 @@ test.describe('System utilization', () => {
await page.goto('/system/silos-new')
await page.getByRole('textbox', { name: 'Name', exact: true }).fill('all-zeros')
// don't need to set silo values, they're zero by default

// but do need to add a tls cert
await addTlsCert(page)

await page.getByRole('button', { name: 'Create silo' }).click()

await closeToast(page)
Expand Down
11 changes: 11 additions & 0 deletions test/e2e/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -243,3 +243,14 @@ export async function scrollTo(page: Page, to: number) {
const container = page.getByTestId('scroll-container')
await container.evaluate((el: HTMLElement, to) => el.scrollTo(0, to), to)
}

export async function addTlsCert(page: Page) {
page.getByRole('button', { name: 'Add TLS certificate' }).click()
await page
.getByRole('dialog', { name: 'Add TLS certificate' })
.getByRole('textbox', { name: 'Name' })
.fill('test-cert')
await chooseFile(page, page.getByLabel('Cert', { exact: true }), 'small')
await chooseFile(page, page.getByLabel('Key'), 'small')
await page.getByRole('button', { name: 'Add Certificate' }).click()
}

0 comments on commit 72b6d43

Please sign in to comment.