Browse Source

fix(host): keep path entry available when the home listing fails

With no listed level (an unreadable or missing home directory), the
path-edit zone previously disabled forever, stranding the operator on the
alert with only Cancel; it now opens with an empty draft so an absolute
path remains the way forward. Covered by a recovery test.
creatixchu 2 months ago
parent
commit
d2b16380d1

+ 5 - 3
packages/host/directory-picker-browse/src/client/DirectoryBrowser.tsx

@@ -282,9 +282,11 @@ export function DirectoryBrowser({ open, listDirectory, createDirectory, onOpen,
                   type="button"
                   className={css.crumbEditZone}
                   aria-label={t('browser.editPath')}
-                  disabled={parent === null || parentInert}
-                  /* v8 ignore next -- narrowing guard: the zone disables while the level is null. */
-                  onClick={() => { if (parent !== null) setPathDraft(selected?.path ?? parent.path) }}
+                  // Stays available with no listed level: when the home
+                  // listing itself fails, typing an absolute path is the one
+                  // remaining way forward.
+                  disabled={parentInert}
+                  onClick={() => { setPathDraft(selected?.path ?? parent?.path ?? '') }}
                 />
               </>
             )

+ 15 - 0
packages/host/directory-picker-browse/tests/directory-browser.spec.tsx

@@ -269,6 +269,21 @@ describe('DirectoryBrowser', () => {
     expect(screen.getByRole<HTMLButtonElement>('button', { name: 'browser.newFolder' }).disabled).toBe(false)
   })
 
+  it('keeps path entry available when the home listing fails', async () => {
+    const listDirectory = vi.fn(async (): Promise<DirectoryListing> => {
+      throw new DirectoryBrowseError({ code: 'directory-unreadable', message: 'home unreadable', details: { path: HOME } })
+    })
+    mount({ listDirectory })
+    await waitFor(() => { expect(screen.getByRole('alert').textContent).toBe('home unreadable') })
+    // With no listed level, typing an absolute path is the one way forward.
+    fireEvent.click(screen.getByRole('button', { name: 'browser.editPath' }))
+    const input = screen.getByLabelText('browser.editPath')
+    fireEvent.change(input, { target: { value: DOCS } })
+    listDirectory.mockImplementation(async (path?: string) => listingFor(path))
+    fireEvent.keyDown(input, { key: 'Enter' })
+    await waitFor(() => { expect(screen.getByText('harness')).toBeTruthy() })
+  })
+
   it('ignores dismissal while adoption is busy', async () => {
     const b = mount({ busy: true })
     await waitFor(() => { expect(screen.getByRole('dialog')).toBeTruthy() })