mirror of
https://github.com/stablyai/orca.git
synced 2026-10-22 16:03:47 +00:00
fix(settings): stop truncating the SSH connection error to one line
The host key messages are written to be actionable — a mismatch ends in "Run: ssh-keygen -R <host>", which is the remedy that also unblocks ssh and git. The only place in the renderer that displays an SSH connection error clamped it to a single line with `truncate` and carried no title attribute, so the remedy was unreachable, not even on hover. The careful wording reached a CSS ellipsis. Wraps instead, with [overflow-wrap:anywhere] because a long hostname offers no break opportunity and would overflow the column on its own. The paragraph only renders on failure, so the extra height costs nothing in the normal case. CORRECTION to the four preceding commits: they each claimed 3 pre-existing failures in ssh-connection-sftp-namespace.test.ts. That was my error — I had been running `npx vitest` without `--config config/vitest.config.ts`, so the project's setupFiles and execArgv were absent. Under the real config those 3 pass, and have throughout. The whole SSH suite is green: 1,504 passed, 13 skipped. Still failing on this branch and unrelated to it (different subsystems, no file overlap): 5 in terminal-snapshot-osc8-roundtrip and 2 in browser-cookie-import.
This commit is contained in:
@@ -0,0 +1,84 @@
|
||||
// @vitest-environment happy-dom
|
||||
|
||||
import { act } from 'react'
|
||||
import { createRoot } from 'react-dom/client'
|
||||
import { afterEach, describe, expect, it, vi } from 'vitest'
|
||||
import { SshTargetCard } from './SshTargetCard'
|
||||
import { TooltipProvider } from '../ui/tooltip'
|
||||
import type { SshConnectionState, SshTarget } from '../../../../shared/ssh-types'
|
||||
|
||||
afterEach(() => {
|
||||
document.body.innerHTML = ''
|
||||
})
|
||||
|
||||
const target: SshTarget = {
|
||||
id: 'target-1',
|
||||
label: 'build-01',
|
||||
host: 'build-01.internal',
|
||||
port: 22,
|
||||
username: 'deploy'
|
||||
}
|
||||
|
||||
/** The real thing a host key mismatch produces: the remedy is the last clause. */
|
||||
const HOST_KEY_ERROR =
|
||||
'Host key verification failed for build-01.internal. The key does not match the entry in your known_hosts file. ssh and git will refuse this host too. Run: ssh-keygen -R build-01.internal'
|
||||
|
||||
async function renderCard(state: SshConnectionState | undefined): Promise<HTMLElement> {
|
||||
const container = document.createElement('div')
|
||||
document.body.appendChild(container)
|
||||
await act(async () => {
|
||||
createRoot(container).render(
|
||||
<TooltipProvider>
|
||||
<SshTargetCard
|
||||
target={target}
|
||||
state={state}
|
||||
testing={false}
|
||||
onConnect={vi.fn()}
|
||||
onDisconnect={vi.fn()}
|
||||
onTerminateSessions={vi.fn()}
|
||||
onResetRelay={vi.fn()}
|
||||
onTest={vi.fn()}
|
||||
onEdit={vi.fn()}
|
||||
onRemove={vi.fn()}
|
||||
/>
|
||||
</TooltipProvider>
|
||||
)
|
||||
})
|
||||
return container
|
||||
}
|
||||
|
||||
const errorState = (error: string): SshConnectionState => ({
|
||||
targetId: 'target-1',
|
||||
status: 'error',
|
||||
error,
|
||||
reconnectAttempt: 0,
|
||||
supportsFolderDownload: false
|
||||
})
|
||||
|
||||
describe('the connection error on an SSH target card', () => {
|
||||
it('shows the whole message, remedy included', async () => {
|
||||
const container = await renderCard(errorState(HOST_KEY_ERROR))
|
||||
|
||||
expect(container.textContent).toContain('ssh-keygen -R build-01.internal')
|
||||
})
|
||||
|
||||
// This is the regression that made the careful wording pointless: `truncate` clamps to one line
|
||||
// with an ellipsis and there is no title attribute, so the remedy was unreachable even on hover.
|
||||
it('does not clamp it to a single line', async () => {
|
||||
const container = await renderCard(errorState(HOST_KEY_ERROR))
|
||||
const paragraph = [...container.querySelectorAll('p')].find((node) =>
|
||||
node.textContent?.includes('ssh-keygen')
|
||||
)
|
||||
|
||||
expect(paragraph).toBeDefined()
|
||||
expect(paragraph?.className).not.toContain('truncate')
|
||||
// A long hostname has no break opportunity, so wrapping alone would still overflow the column.
|
||||
expect(paragraph?.className).toContain('[overflow-wrap:anywhere]')
|
||||
})
|
||||
|
||||
it('renders nothing when there is no error', async () => {
|
||||
const container = await renderCard(undefined)
|
||||
|
||||
expect(container.textContent).not.toContain('Host key verification failed')
|
||||
})
|
||||
})
|
||||
@@ -301,8 +301,10 @@ export function SshTargetCard({
|
||||
{target.identityFile ? ` \u2022 ${target.identityFile}` : ''}
|
||||
{` \u2022 ${terminalPersistence}`}
|
||||
</p>
|
||||
{/* Why not truncate: host key failures put the remedy (`ssh-keygen -R <host>`) at the end,
|
||||
and a one-line clamp with no tooltip made it unreachable even on hover. */}
|
||||
{state?.error ? (
|
||||
<p className="mt-0.5 truncate text-xs text-red-400">{state.error}</p>
|
||||
<p className="mt-0.5 text-xs text-red-400 [overflow-wrap:anywhere]">{state.error}</p>
|
||||
) : null}
|
||||
</div>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user