Files
orca/tests/e2e/ssh-config-host-import.spec.ts

154 lines
5.9 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// Cover Add all import versus Settings re-adoption (P5/P6/P7/P9); picker host selection lives in ssh-config-host-picker.spec.ts.
import type { ElectronApplication, Page } from '@stablyai/playwright-test'
import { expect, test } from './helpers/orca-app'
import { waitForSessionReady } from './helpers/store'
import {
buildSshConfigBody,
configHostRow,
expectSshHostAbsentFromSettings,
expectSshHostListedInSettings,
makeSshConfigHostPrefix,
openSshConfigHostPicker,
openSshHostSettings,
removeSshTargetByAlias,
removeSshTargetsByPrefix,
returnToAppShell,
seedIsolatedSshConfig,
type SeededSshConfigHost
} from './helpers/ssh-config-host-picker'
test.use({ seedTestRepo: false })
// Use unique cleanup prefixes so workers loaded in the same millisecond cannot delete each other’s targets.
const HOST_PREFIX = makeSshConfigHostPrefix()
function pairHosts(prefix: string): { alpha: SeededSshConfigHost; bravo: SeededSshConfigHost } {
return {
alpha: {
alias: `${prefix}-alpha`,
hostname: `${prefix}-alpha.example.test`,
user: 'deploy',
port: 22
},
bravo: {
alias: `${prefix}-bravo`,
hostname: `${prefix}-bravo.example.test`,
user: 'ops',
port: 2222
}
}
}
async function seedPairConfig(
electronApp: ElectronApplication,
prefix: string
): Promise<{ alpha: SeededSshConfigHost; bravo: SeededSshConfigHost }> {
const hosts = pairHosts(prefix)
await seedIsolatedSshConfig(electronApp, buildSshConfigBody([hosts.alpha, hosts.bravo]))
return hosts
}
/** Import both config hosts via picker, then suppress `alias` (removeTarget tombstone). */
async function importPairThenDeleteAlias(
page: Page,
electronApp: ElectronApplication,
prefix: string,
aliasToDelete: string
): Promise<{ alpha: SeededSshConfigHost; bravo: SeededSshConfigHost }> {
const hosts = await seedPairConfig(electronApp, prefix)
const picker = await openSshConfigHostPicker(page)
await expect(picker.getByRole('button', { name: 'Add all 2 to Orca' })).toBeEnabled()
await picker.getByRole('button', { name: 'Add all 2 to Orca' }).click()
await expect(page.getByText('Added 2 hosts to Orca.')).toBeVisible({ timeout: 15_000 })
await expect(page.getByRole('dialog', { name: 'Choose from ~/.ssh/config' })).toBeHidden({
timeout: 10_000
})
await expect(page.getByRole('dialog', { name: 'Add SSH host' })).toBeHidden({
timeout: 10_000
})
await removeSshTargetByAlias(page, aliasToDelete)
return hosts
}
test.describe('SSH config host import (bulk + settings re-adopt)', () => {
test.beforeEach(async ({ orcaPage }) => {
await waitForSessionReady(orcaPage)
})
test.afterEach(async ({ orcaPage }) => {
await removeSshTargetsByPrefix(orcaPage, HOST_PREFIX).catch(() => undefined)
})
test('P6: Add all N to Orca imports new hosts; re-open shows all in Orca', async ({
electronApp,
orcaPage
}) => {
const hosts = await seedPairConfig(electronApp, HOST_PREFIX)
const picker = await openSshConfigHostPicker(orcaPage)
await expect(configHostRow(picker, hosts.alpha)).toBeVisible()
await expect(configHostRow(picker, hosts.bravo)).toBeVisible()
await expect(picker.getByRole('button', { name: 'Add all 2 to Orca' })).toBeEnabled()
await picker.getByRole('button', { name: 'Add all 2 to Orca' }).click()
await expect(orcaPage.getByText('Added 2 hosts to Orca.')).toBeVisible({ timeout: 15_000 })
await expect(orcaPage.getByRole('dialog', { name: 'Choose from ~/.ssh/config' })).toBeHidden({
timeout: 10_000
})
await expect(orcaPage.getByRole('dialog', { name: 'Add SSH host' })).toBeHidden({
timeout: 10_000
})
const sshSection = await openSshHostSettings(orcaPage)
await expectSshHostListedInSettings(sshSection, hosts.alpha)
await expectSshHostListedInSettings(sshSection, hosts.bravo)
await returnToAppShell(orcaPage)
const reopened = await openSshConfigHostPicker(orcaPage)
await expect(configHostRow(reopened, hosts.alpha)).toBeDisabled()
await expect(
configHostRow(reopened, hosts.alpha).getByText('In Orca', { exact: true })
).toBeVisible()
await expect(configHostRow(reopened, hosts.bravo)).toBeDisabled()
await expect(
configHostRow(reopened, hosts.bravo).getByText('In Orca', { exact: true })
).toBeVisible()
await expect(reopened.getByRole('button', { name: 'No new hosts to add' })).toBeDisabled()
})
test('P9: Sync keeps deleted config hosts; Restore removed hosts re-adopts them', async ({
electronApp,
orcaPage
}) => {
const hosts = await importPairThenDeleteAlias(
orcaPage,
electronApp,
HOST_PREFIX,
`${HOST_PREFIX}-alpha`
)
const sshSection = await openSshHostSettings(orcaPage)
await expectSshHostListedInSettings(sshSection, hosts.bravo)
await expectSshHostAbsentFromSettings(sshSection, hosts.alpha)
// Sync never lifts a removal, so bulk removal sticks.
await sshSection.getByRole('button', { name: 'Sync ~/.ssh/config' }).click()
await expect(orcaPage.getByText(/already in sync|Synced \d+ servers?/i).first()).toBeVisible({
timeout: 15_000
})
await expectSshHostAbsentFromSettings(sshSection, hosts.alpha)
await sshSection.getByRole('button', { name: 'More SSH host actions' }).click()
// The label carries the removed count once ~/.ssh/config is read; click waits for it to enable.
await orcaPage.getByRole('menuitem', { name: /^Restore removed hosts \(\d+\)…$/ }).click()
const restoreDialog = orcaPage.getByRole('dialog', { name: 'Restore removed hosts?' })
await expect(restoreDialog.getByText(hosts.alpha.alias)).toBeVisible({ timeout: 15_000 })
await restoreDialog.getByRole('button', { name: 'Restore' }).click()
await expect(orcaPage.getByText(/Synced \d+ servers?/i)).toBeVisible({ timeout: 15_000 })
await expectSshHostListedInSettings(sshSection, hosts.alpha)
await expectSshHostListedInSettings(sshSection, hosts.bravo)
})
})