Skip to content

Commit

Permalink
Handle empty IP Address field in Network Interface create flow (#1854)
Browse files Browse the repository at this point in the history
* Add onBlur to better handle empty IP Address field

* Add test for network-interface-create flow

* Use transform rather than onChange

* More succinct prop description

* small tweaks

---------

Co-authored-by: David Crespo <[email protected]>
  • Loading branch information
charliepark and david-crespo authored Jan 24, 2024
1 parent b9013a3 commit 7c6f53d
Show file tree
Hide file tree
Showing 3 changed files with 91 additions and 1 deletion.
7 changes: 7 additions & 0 deletions app/components/form/fields/TextField.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ export interface TextFieldProps<
units?: string
validate?: Validate<FieldPathValue<TFieldValues, TName>, TFieldValues>
control: Control<TFieldValues>
/** Alters the value of the input during the field's onChange event. */
transform?: (value: string) => FieldPathValue<TFieldValues, TName>
}

export function TextField<
Expand Down Expand Up @@ -119,6 +121,7 @@ export const TextFieldInner = <
tooltipText,
required,
id: idProp,
transform,
...props
}: TextFieldProps<TFieldValues, TName> & UITextAreaProps) => {
const generatedId = useId()
Expand All @@ -144,6 +147,10 @@ export const TextFieldInner = <
// for the calling code despite the actual input value necessarily
// being a string.
onChange={(e) => {
if (transform) {
onChange(transform(e.target.value))
return
}
if (type === 'number') {
if (e.target.value.trim() === '') {
onChange(0)
Expand Down
7 changes: 6 additions & 1 deletion app/forms/network-interface-create.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,12 @@ export default function CreateNetworkInterfaceForm({
required
control={form.control}
/>
<TextField name="ip" label="IP Address" control={form.control} />
<TextField
name="ip"
label="IP Address"
control={form.control}
transform={(ip) => (ip.trim() === '' ? undefined : ip)}
/>
</SideModalForm>
)
}
78 changes: 78 additions & 0 deletions app/test/e2e/network-interface-create.e2e.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, you can obtain one at https://mozilla.org/MPL/2.0/.
*
* Copyright Oxide Computer Company
*/
import { test } from '@playwright/test'

import { expect, expectRowVisible } from './utils'

test('can create a NIC with a specified IP address', async ({ page }) => {
// go to an instance’s Network Interfaces page
await page.goto('/projects/mock-project/instances/db1/network-interfaces')

// stop the instance
await page.getByRole('button', { name: 'Instance actions' }).click()
await page.getByRole('menuitem', { name: 'Stop' }).click()

// open the add network interface side modal
await page.getByRole('button', { name: 'Add network interface' }).click()

// fill out the form
await page.getByLabel('Name').fill('nic-1')
await page.getByRole('button', { name: 'VPC' }).click()
await page.getByRole('option', { name: 'mock-vpc' }).click()
await page.getByRole('button', { name: 'Subnet' }).click()
await page.getByRole('option', { name: 'mock-subnet' }).click()
await page.getByLabel('IP Address').fill('1.2.3.4')

const sidebar = page.getByRole('dialog', { name: 'Add network interface' })

// test that the form can be submitted and a new network interface is created
await sidebar.getByRole('button', { name: 'Add network interface' }).click()
await expect(sidebar).toBeHidden()

await expectRowVisible(page.getByRole('table'), { name: 'nic-1', ip: '1.2.3.4' })
})

test('can create a NIC with a blank IP address', async ({ page }) => {
// go to an instance’s Network Interfaces page
await page.goto('/projects/mock-project/instances/db1/network-interfaces')

// stop the instance
await page.getByRole('button', { name: 'Instance actions' }).click()
await page.getByRole('menuitem', { name: 'Stop' }).click()

// open the add network interface side modal
await page.getByRole('button', { name: 'Add network interface' }).click()

// fill out the form
await page.getByLabel('Name').fill('nic-2')
await page.getByRole('button', { name: 'VPC' }).click()
await page.getByRole('option', { name: 'mock-vpc' }).click()
await page.getByRole('button', { name: 'Subnet' }).click()
await page.getByRole('option', { name: 'mock-subnet' }).click()

// make sure the IP address field has a non-conforming bit of text in it
await page.getByLabel('IP Address').fill('x')

// try to submit it
const sidebar = page.getByRole('dialog', { name: 'Add network interface' })
await sidebar.getByRole('button', { name: 'Add network interface' }).click()

// it should error out
// todo: improve error message from API
await expect(sidebar.getByText('Unknown server error')).toBeVisible()

// make sure the IP address field has spaces in it
await page.getByLabel('IP Address').fill(' ')

// test that the form can be submitted and a new network interface is created
await sidebar.getByRole('button', { name: 'Add network interface' }).click()
await expect(sidebar).toBeHidden()

// ip address is auto-assigned
await expectRowVisible(page.getByRole('table'), { name: 'nic-2', ip: '123.45.68.8' })
})

0 comments on commit 7c6f53d

Please sign in to comment.