mirror of
https://github.com/stablyai/orca.git
synced 2026-10-31 00:57:45 +00:00
Preserve the selected host in explorer watcher ownership (#26616)
This commit is contained in:
@@ -360,7 +360,8 @@ jobs:
|
||||
. != "tests/e2e/ssh-orcad-markdown-live-documents.spec.ts" and
|
||||
. != "tests/e2e/ssh-orcad-open-in-owner.spec.ts" and
|
||||
. != "tests/e2e/ssh-orcad-editor-watch-recovery.spec.ts" and
|
||||
. != "tests/e2e/ssh-orcad-explorer-watch-recovery.spec.ts"
|
||||
. != "tests/e2e/ssh-orcad-explorer-watch-recovery.spec.ts" and
|
||||
. != "tests/e2e/ssh-orcad-explorer-selected-host.spec.ts"
|
||||
)' <<<"$TEST_FILES_JSON" > "$RUNNER_TEMP/general-e2e-specs"
|
||||
fi
|
||||
mapfile -t TEST_FILES < "$RUNNER_TEMP/general-e2e-specs"
|
||||
@@ -594,7 +595,7 @@ jobs:
|
||||
orcad-auto-convert-docker:
|
||||
name: ssh host auto-converts to managed orcad (Docker)
|
||||
needs: [build, prepare-native-cache]
|
||||
if: inputs.test_files == '' || contains(inputs.test_files, 'tests/e2e/ssh-orcad-auto-convert.spec.ts') || contains(inputs.test_files, 'tests/e2e/ssh-orcad-idle-exit.spec.ts') || contains(inputs.test_files, 'tests/e2e/ssh-orcad-browser-capabilities.spec.ts') || contains(inputs.test_files, 'tests/e2e/ssh-orcad-browser-service-status.spec.ts') || contains(inputs.test_files, 'tests/e2e/ssh-orcad-browser-routing.spec.ts') || contains(inputs.test_files, 'tests/e2e/ssh-orcad-editor-ownership.spec.ts') || contains(inputs.test_files, 'tests/e2e/ssh-orcad-markdown-conversion.spec.ts') || contains(inputs.test_files, 'tests/e2e/ssh-orcad-markdown-link-refresh.spec.ts') || contains(inputs.test_files, 'tests/e2e/ssh-orcad-markdown-live-documents.spec.ts') || contains(inputs.test_files, 'tests/e2e/ssh-orcad-open-in-owner.spec.ts') || contains(inputs.test_files, 'tests/e2e/ssh-orcad-editor-watch-recovery.spec.ts') || contains(inputs.test_files, 'tests/e2e/ssh-orcad-explorer-watch-recovery.spec.ts')
|
||||
if: inputs.test_files == '' || contains(inputs.test_files, 'tests/e2e/ssh-orcad-auto-convert.spec.ts') || contains(inputs.test_files, 'tests/e2e/ssh-orcad-idle-exit.spec.ts') || contains(inputs.test_files, 'tests/e2e/ssh-orcad-browser-capabilities.spec.ts') || contains(inputs.test_files, 'tests/e2e/ssh-orcad-browser-service-status.spec.ts') || contains(inputs.test_files, 'tests/e2e/ssh-orcad-browser-routing.spec.ts') || contains(inputs.test_files, 'tests/e2e/ssh-orcad-editor-ownership.spec.ts') || contains(inputs.test_files, 'tests/e2e/ssh-orcad-markdown-conversion.spec.ts') || contains(inputs.test_files, 'tests/e2e/ssh-orcad-markdown-link-refresh.spec.ts') || contains(inputs.test_files, 'tests/e2e/ssh-orcad-markdown-live-documents.spec.ts') || contains(inputs.test_files, 'tests/e2e/ssh-orcad-open-in-owner.spec.ts') || contains(inputs.test_files, 'tests/e2e/ssh-orcad-editor-watch-recovery.spec.ts') || contains(inputs.test_files, 'tests/e2e/ssh-orcad-explorer-watch-recovery.spec.ts') || contains(inputs.test_files, 'tests/e2e/ssh-orcad-explorer-selected-host.spec.ts')
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 45
|
||||
env:
|
||||
@@ -630,7 +631,7 @@ jobs:
|
||||
ORCA_RELAY_PATH: ${{ github.workspace }}/out/relay
|
||||
run: |
|
||||
export ORCA_E2E_ORCAD_CONVERT_TEMPLATE="$RUNNER_TEMP/orcad-convert-template"
|
||||
xvfb-run --auto-servernum bash .github/scripts/e2e-with-window-manager.sh pnpm exec playwright test --config tests/playwright.config.ts tests/e2e/ssh-orcad-auto-convert.spec.ts tests/e2e/ssh-orcad-idle-exit.spec.ts tests/e2e/ssh-orcad-browser-capabilities.spec.ts tests/e2e/ssh-orcad-browser-service-status.spec.ts tests/e2e/ssh-orcad-browser-routing.spec.ts tests/e2e/ssh-orcad-editor-ownership.spec.ts tests/e2e/ssh-orcad-markdown-conversion.spec.ts tests/e2e/ssh-orcad-markdown-link-refresh.spec.ts tests/e2e/ssh-orcad-markdown-live-documents.spec.ts tests/e2e/ssh-orcad-open-in-owner.spec.ts tests/e2e/ssh-orcad-editor-watch-recovery.spec.ts tests/e2e/ssh-orcad-explorer-watch-recovery.spec.ts --project=electron-headless --workers=1
|
||||
xvfb-run --auto-servernum bash .github/scripts/e2e-with-window-manager.sh pnpm exec playwright test --config tests/playwright.config.ts tests/e2e/ssh-orcad-auto-convert.spec.ts tests/e2e/ssh-orcad-idle-exit.spec.ts tests/e2e/ssh-orcad-browser-capabilities.spec.ts tests/e2e/ssh-orcad-browser-service-status.spec.ts tests/e2e/ssh-orcad-browser-routing.spec.ts tests/e2e/ssh-orcad-editor-ownership.spec.ts tests/e2e/ssh-orcad-markdown-conversion.spec.ts tests/e2e/ssh-orcad-markdown-link-refresh.spec.ts tests/e2e/ssh-orcad-markdown-live-documents.spec.ts tests/e2e/ssh-orcad-open-in-owner.spec.ts tests/e2e/ssh-orcad-editor-watch-recovery.spec.ts tests/e2e/ssh-orcad-explorer-watch-recovery.spec.ts tests/e2e/ssh-orcad-explorer-selected-host.spec.ts --project=electron-headless --workers=1
|
||||
- uses: actions/upload-artifact@v7
|
||||
if: failure()
|
||||
with:
|
||||
|
||||
@@ -67,6 +67,8 @@ export const ORCAD_EDITOR_WATCH_RECOVERY_E2E_SPEC =
|
||||
'tests/e2e/ssh-orcad-editor-watch-recovery.spec.ts'
|
||||
export const ORCAD_EXPLORER_WATCH_RECOVERY_E2E_SPEC =
|
||||
'tests/e2e/ssh-orcad-explorer-watch-recovery.spec.ts'
|
||||
export const ORCAD_EXPLORER_SELECTED_HOST_E2E_SPEC =
|
||||
'tests/e2e/ssh-orcad-explorer-selected-host.spec.ts'
|
||||
export const DEDICATED_E2E_SPECS = [
|
||||
...DOCKER_SSH_E2E_SPECS,
|
||||
NODE_NETWORK_E2E_SPEC,
|
||||
@@ -86,7 +88,8 @@ export const DEDICATED_E2E_SPECS = [
|
||||
ORCAD_MARKDOWN_LIVE_DOCUMENTS_E2E_SPEC,
|
||||
ORCAD_OPEN_IN_OWNER_E2E_SPEC,
|
||||
ORCAD_EDITOR_WATCH_RECOVERY_E2E_SPEC,
|
||||
ORCAD_EXPLORER_WATCH_RECOVERY_E2E_SPEC
|
||||
ORCAD_EXPLORER_WATCH_RECOVERY_E2E_SPEC,
|
||||
ORCAD_EXPLORER_SELECTED_HOST_E2E_SPEC
|
||||
]
|
||||
const dedicatedSpecs = new Set(DEDICATED_E2E_SPECS)
|
||||
const dockerSpecs = new Set(DOCKER_SSH_E2E_SPECS)
|
||||
@@ -138,7 +141,8 @@ export function classifyE2eJobs(input, sshSourceChanged = 'false') {
|
||||
spec === ORCAD_MARKDOWN_LIVE_DOCUMENTS_E2E_SPEC ||
|
||||
spec === ORCAD_OPEN_IN_OWNER_E2E_SPEC ||
|
||||
spec === ORCAD_EDITOR_WATCH_RECOVERY_E2E_SPEC ||
|
||||
spec === ORCAD_EXPLORER_WATCH_RECOVERY_E2E_SPEC
|
||||
spec === ORCAD_EXPLORER_WATCH_RECOVERY_E2E_SPEC ||
|
||||
spec === ORCAD_EXPLORER_SELECTED_HOST_E2E_SPEC
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -266,3 +266,17 @@ it.each([
|
||||
spec
|
||||
)
|
||||
})
|
||||
|
||||
it.each([
|
||||
'src/renderer/src/components/right-sidebar/useFileExplorerWatch.ts',
|
||||
'src/renderer/src/components/right-sidebar/file-explorer-operation-owner.ts'
|
||||
])('routes %s to the template-building selected-host explorer lane', (file) => {
|
||||
const spec = 'tests/e2e/ssh-orcad-explorer-selected-host.spec.ts'
|
||||
expect(selectPrE2eSpecs([file])).toContain(spec)
|
||||
expect(classify([spec])).toEqual({ e2e_run_changed: false, e2e_needs_build: true })
|
||||
const job = workflow.jobs['orcad-auto-convert-docker']
|
||||
expect(job.if).toContain(spec)
|
||||
expect(job.steps.find((step) => step.name === 'Convert a relay-era Docker host').run).toContain(
|
||||
spec
|
||||
)
|
||||
})
|
||||
|
||||
@@ -67,6 +67,14 @@ export const PR_E2E_SOURCE_ROUTES = [
|
||||
file
|
||||
)
|
||||
},
|
||||
{
|
||||
id: 'ssh.orcad-explorer-selected-host',
|
||||
specs: ['tests/e2e/ssh-orcad-explorer-selected-host.spec.ts'],
|
||||
matches: (file) =>
|
||||
isProductSource(file) &&
|
||||
(file === 'src/renderer/src/components/right-sidebar/useFileExplorerWatch.ts' ||
|
||||
file === 'src/renderer/src/components/right-sidebar/file-explorer-operation-owner.ts')
|
||||
},
|
||||
{
|
||||
id: 'serve.orcad-mode-switch',
|
||||
specs: ['tests/e2e/orcad-serve-mode-switch.spec.ts'],
|
||||
|
||||
@@ -38,7 +38,8 @@ export type FileExplorerOwnerState = Pick<
|
||||
| 'folderWorkspaces'
|
||||
| 'projectGroups'
|
||||
| 'restoredRuntimeHostIdByWorkspaceSessionKey'
|
||||
>
|
||||
> &
|
||||
Partial<Pick<AppState, 'activeWorktreeId' | 'activeWorkspaceExecutionHostId'>>
|
||||
|
||||
export function getFileExplorerOperationOwnerFromState(
|
||||
state: FileExplorerOwnerState,
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import type { AppState } from '@/store/types'
|
||||
import { makeFolderWorkspace, makeWorktree } from '@/store/slices/worktrees-slice-test-fixtures'
|
||||
import { getDefaultSettings } from '../../../../shared/constants'
|
||||
import { getFileExplorerWatchRuntimeEnvironmentId } from './useFileExplorerWatch'
|
||||
import type { FileExplorerOperationOwner } from './file-explorer-types'
|
||||
|
||||
type WatchState = Parameters<typeof getFileExplorerWatchRuntimeEnvironmentId>[0] &
|
||||
Pick<AppState, 'activeWorktreeId' | 'activeWorkspaceExecutionHostId'>
|
||||
|
||||
const hosts = ['host-a', 'host-b'] as const
|
||||
function makeState(workspace: 'worktree' | 'folder', selected: (typeof hosts)[number]): WatchState {
|
||||
const id = workspace === 'folder' ? 'folder:same-folder' : 'same-worktree'
|
||||
return {
|
||||
settings: getDefaultSettings('/home/me'),
|
||||
activeWorktreeId: id,
|
||||
activeWorkspaceExecutionHostId: `runtime:${selected}`,
|
||||
repos: [],
|
||||
worktreesByRepo:
|
||||
workspace === 'worktree'
|
||||
? {
|
||||
repo: hosts.map((host) =>
|
||||
makeWorktree({ id, repoId: 'repo', path: '/repo', hostId: `runtime:${host}` })
|
||||
)
|
||||
}
|
||||
: {},
|
||||
folderWorkspaces:
|
||||
workspace === 'folder'
|
||||
? hosts.map((host) =>
|
||||
makeFolderWorkspace({
|
||||
id: 'same-folder',
|
||||
folderPath: '/folder',
|
||||
executionHostId: `runtime:${host}`
|
||||
})
|
||||
)
|
||||
: [],
|
||||
projectGroups: []
|
||||
}
|
||||
}
|
||||
function owner(environmentId: string): FileExplorerOperationOwner {
|
||||
return { kind: 'runtime', environmentId, executionHostId: `runtime:${environmentId}` }
|
||||
}
|
||||
|
||||
describe('explorer watcher preserving the selected host', () => {
|
||||
it.each(['worktree', 'folder'] as const)(
|
||||
'resolves the selected %s owner when IDs repeat across hosts',
|
||||
(workspace) => {
|
||||
for (const host of hosts) {
|
||||
const state = makeState(workspace, host)
|
||||
expect(
|
||||
getFileExplorerWatchRuntimeEnvironmentId(state, state.activeWorktreeId, owner(host))
|
||||
).toBe(host)
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
it.each(['worktree', 'folder'] as const)(
|
||||
'rejects the previous %s listing owner after host selection changes',
|
||||
(workspace) => {
|
||||
const state = makeState(workspace, 'host-b')
|
||||
expect(
|
||||
getFileExplorerWatchRuntimeEnvironmentId(state, state.activeWorktreeId, owner('host-a'))
|
||||
).toBeUndefined()
|
||||
expect(
|
||||
getFileExplorerWatchRuntimeEnvironmentId(state, state.activeWorktreeId, owner('host-b'))
|
||||
).toBe('host-b')
|
||||
}
|
||||
)
|
||||
|
||||
it('does not apply the selected host to a different workspace', () => {
|
||||
const state = makeState('worktree', 'host-b')
|
||||
state.activeWorktreeId = 'different-worktree'
|
||||
expect(
|
||||
getFileExplorerWatchRuntimeEnvironmentId(state, 'same-worktree', owner('host-b'))
|
||||
).toBeUndefined()
|
||||
})
|
||||
})
|
||||
@@ -51,6 +51,8 @@ export function getFileExplorerWatchRuntimeEnvironmentId(
|
||||
expectedOwner?: FileExplorerOperationOwner
|
||||
): string | null | undefined {
|
||||
const ownerState: FileExplorerOwnerState = {
|
||||
activeWorktreeId: state.activeWorktreeId,
|
||||
activeWorkspaceExecutionHostId: state.activeWorkspaceExecutionHostId,
|
||||
settings: state.settings,
|
||||
repos: state.repos,
|
||||
worktreesByRepo: state.worktreesByRepo,
|
||||
|
||||
@@ -0,0 +1,207 @@
|
||||
import type { ElectronApplication } from '@stablyai/playwright-test'
|
||||
import { expect, test } from './helpers/orca-app'
|
||||
import { createRestartSession } from './helpers/orca-restart'
|
||||
import { startOrcadConvertHost } from './helpers/orcad-convert-host'
|
||||
import { seedRelayEraProfile, seedRelayEraTarget } from './helpers/orcad-upgrade-profile'
|
||||
import { mutateStoppedProfileState } from './helpers/persisted-profile-state'
|
||||
import { convertAndRetain, reconnect, serverCall } from './helpers/orcad-convert-flow'
|
||||
import { waitForSessionReady, waitForStartupWorktreeRefresh } from './helpers/store'
|
||||
import { dismissTransientAnnouncement } from './helpers/ssh-config-host-picker'
|
||||
import { toRuntimeExecutionHostId } from '../../src/shared/execution-host'
|
||||
import { folderWorkspaceKey } from '../../src/shared/workspace-scope'
|
||||
|
||||
const TEMPLATE = process.env.ORCA_E2E_ORCAD_CONVERT_TEMPLATE
|
||||
test.skip(!TEMPLATE || process.env.ORCA_E2E_SSH_DOCKER !== '1', 'Needs Docker and server template')
|
||||
|
||||
for (const selectedHost of ['A', 'B'] as const) {
|
||||
test(`same-ID folder explorer follows selected host ${selectedHost}`, async (// oxlint-disable-next-line no-empty-pattern -- This exercise owns its app launch.
|
||||
{}, testInfo) => {
|
||||
test.setTimeout(5 * 60_000)
|
||||
const hostA = startOrcadConvertHost('docker', testInfo)
|
||||
const hostB = startOrcadConvertHost('docker', testInfo)
|
||||
const session = createRestartSession(testInfo, { ORCA_ORCAD_TEMPLATE_PATH: TEMPLATE! })
|
||||
const otherSession = createRestartSession(testInfo, { ORCA_ORCAD_TEMPLATE_PATH: TEMPLATE! })
|
||||
let app: ElectronApplication | null = null
|
||||
let otherApp: ElectronApplication | null = null
|
||||
try {
|
||||
if (!hostA.exec || !hostB.exec) {
|
||||
throw new Error('Docker controls unavailable')
|
||||
}
|
||||
const folderPath = '/tmp/orca-same-id-folder'
|
||||
hostA.exec(`mkdir -p '${folderPath}' && printf 'initial A\n' > '${folderPath}/INITIAL_A.txt'`)
|
||||
hostB.exec(`mkdir -p '${folderPath}' && printf 'initial B\n' > '${folderPath}/INITIAL_B.txt'`)
|
||||
const first = await session.launch()
|
||||
app = first.app
|
||||
await waitForSessionReady(first.page)
|
||||
await session.close(app)
|
||||
app = null
|
||||
const seededA = seedRelayEraProfile(session.userDataDir, hostA.input, {
|
||||
repoPath: hostA.remoteRepoPath,
|
||||
folderPath
|
||||
})
|
||||
const initialOther = await otherSession.launch()
|
||||
otherApp = initialOther.app
|
||||
await waitForSessionReady(initialOther.page)
|
||||
await otherSession.close(otherApp)
|
||||
otherApp = null
|
||||
const seededB = seedRelayEraProfile(otherSession.userDataDir, hostB.input, {
|
||||
repoPath: hostB.remoteRepoPath,
|
||||
folderPath
|
||||
})
|
||||
const folderId = mutateStoppedProfileState(session.userDataDir, (state) => {
|
||||
if (!Array.isArray(state.folderWorkspaces)) {
|
||||
throw new Error('No folder fixture')
|
||||
}
|
||||
const folder = state.folderWorkspaces.find(
|
||||
(entry) => entry.connectionId === seededA.targetId
|
||||
)
|
||||
if (!folder || typeof folder.id !== 'string') {
|
||||
throw new Error('Missing folder owner')
|
||||
}
|
||||
folder.name = 'Folder host A'
|
||||
return folder.id
|
||||
})
|
||||
mutateStoppedProfileState(otherSession.userDataDir, (state) => {
|
||||
if (!Array.isArray(state.folderWorkspaces)) {
|
||||
throw new Error('No folder fixture')
|
||||
}
|
||||
const folder = state.folderWorkspaces.find(
|
||||
(entry) => entry.connectionId === seededB.targetId
|
||||
)
|
||||
if (!folder) {
|
||||
throw new Error('Missing other folder owner')
|
||||
}
|
||||
folder.id = folderId
|
||||
folder.name = 'Folder host B'
|
||||
})
|
||||
const launched = await session.launch()
|
||||
app = launched.app
|
||||
await waitForSessionReady(launched.page)
|
||||
await convertAndRetain(launched.page, session.userDataDir, seededA)
|
||||
await session.close(app)
|
||||
app = null
|
||||
const other = await otherSession.launch()
|
||||
otherApp = other.app
|
||||
await waitForSessionReady(other.page)
|
||||
await convertAndRetain(other.page, otherSession.userDataDir, seededB)
|
||||
await otherSession.close(otherApp)
|
||||
otherApp = null
|
||||
const adoptedTargetId = seedRelayEraTarget(session.userDataDir, hostB.input)
|
||||
const restarted = await session.launch()
|
||||
app = restarted.app
|
||||
const page = restarted.page
|
||||
page.on('console', (message) => {
|
||||
if (message.text().startsWith('[file-change-event]') || message.type() === 'error') {
|
||||
console.log('[renderer]', message.text())
|
||||
}
|
||||
})
|
||||
await waitForSessionReady(page)
|
||||
console.log('[adopt-existing-managed-host]', await reconnect(page, adoptedTargetId))
|
||||
const environments = await page.evaluate(() => window.api.runtimeEnvironments.list())
|
||||
const environmentA = environments.find(
|
||||
(entry) => entry.orcadDeployment?.sshTargetId === seededA.targetId
|
||||
)
|
||||
const environmentB = environments.find(
|
||||
(entry) => entry.orcadDeployment?.sshTargetId === adoptedTargetId
|
||||
)
|
||||
if (!environmentA || !environmentB) {
|
||||
throw new Error('Missing converted host')
|
||||
}
|
||||
console.log(
|
||||
'[actual-folder-owners]',
|
||||
await serverCall(page, environmentA.id, 'folderWorkspace.list'),
|
||||
await serverCall(page, environmentB.id, 'folderWorkspace.list')
|
||||
)
|
||||
await waitForStartupWorktreeRefresh(page)
|
||||
await expect
|
||||
.poll(() =>
|
||||
page.evaluate(
|
||||
(id) =>
|
||||
window.__store?.getState().folderWorkspaces.filter((folder) => folder.id === id)
|
||||
.length,
|
||||
folderId
|
||||
)
|
||||
)
|
||||
.toBe(2)
|
||||
await dismissTransientAnnouncement(page)
|
||||
await page.evaluate(() => {
|
||||
window.addEventListener('orca:worktree-file-change', (event) => {
|
||||
if (event instanceof CustomEvent) {
|
||||
console.log('[file-change-event]', JSON.stringify(event.detail))
|
||||
}
|
||||
})
|
||||
})
|
||||
const worktreeId = folderWorkspaceKey(folderId)
|
||||
const [index, environment, host, marker] =
|
||||
selectedHost === 'A'
|
||||
? ([0, environmentA, hostA, 'HOST_A_READY.txt'] as const)
|
||||
: ([1, environmentB, hostB, 'HOST_B_READY.txt'] as const)
|
||||
await page.evaluate(
|
||||
({ id, hostId }) => window.__store?.getState().setActiveFolderWorkspace(id, hostId),
|
||||
{
|
||||
id: folderId,
|
||||
hostId: toRuntimeExecutionHostId(environment.id)
|
||||
}
|
||||
)
|
||||
await expect
|
||||
.poll(() => page.evaluate(() => window.__store?.getState().activeWorkspaceExecutionHostId))
|
||||
.toBe(toRuntimeExecutionHostId(environment.id))
|
||||
await expect(page.getByText(`INITIAL_${selectedHost}.txt`, { exact: true })).toBeVisible()
|
||||
await expect(
|
||||
page.getByText(`INITIAL_${selectedHost === 'A' ? 'B' : 'A'}.txt`, { exact: true })
|
||||
).toHaveCount(0)
|
||||
console.log(
|
||||
'[selected-folder-host]',
|
||||
index,
|
||||
await page.evaluate(() => {
|
||||
const state = window.__store?.getState()
|
||||
return {
|
||||
id: state?.activeWorktreeId,
|
||||
host: state?.activeWorkspaceExecutionHostId,
|
||||
settingsHost: state?.settings?.activeRuntimeEnvironmentId,
|
||||
files: state?.openFiles.map((file) => ({
|
||||
id: file.id,
|
||||
owner: file.runtimeEnvironmentId
|
||||
}))
|
||||
}
|
||||
})
|
||||
)
|
||||
const changed = page.waitForEvent('console', {
|
||||
predicate: (message) =>
|
||||
message.text().startsWith('[file-change-event]') &&
|
||||
message.text().includes(marker) &&
|
||||
message.text().includes(environment.id),
|
||||
timeout: 15_000
|
||||
})
|
||||
host.exec?.(`printf 'host ${index}\n' > '${folderPath}/${marker}'`)
|
||||
console.log(
|
||||
'[positive-host-file]',
|
||||
await serverCall(page, environment.id, 'files.read', {
|
||||
worktree: `id:${worktreeId}`,
|
||||
relativePath: marker
|
||||
})
|
||||
)
|
||||
await changed
|
||||
await page.screenshot({ path: testInfo.outputPath('selected-host-explorer-observed.png') })
|
||||
await expect(page.getByText(marker, { exact: true })).toBeVisible({ timeout: 10_000 })
|
||||
await page.screenshot({ path: testInfo.outputPath('selected-host-explorer-refreshed.png') })
|
||||
const otherHost = selectedHost === 'A' ? hostB : hostA
|
||||
otherHost.exec?.(`printf 'other host\n' > '${folderPath}/OTHER_HOST_ONLY.txt'`)
|
||||
await expect(page.getByText('OTHER_HOST_ONLY.txt', { exact: true })).toHaveCount(0)
|
||||
host.exec?.(`rm '${folderPath}/${marker}'`)
|
||||
await expect(page.getByText(marker, { exact: true })).toHaveCount(0, { timeout: 10_000 })
|
||||
await page.screenshot({ path: testInfo.outputPath(`host-${index}-folder-watch.png`) })
|
||||
} finally {
|
||||
if (app) {
|
||||
await session.close(app)
|
||||
}
|
||||
if (otherApp) {
|
||||
await otherSession.close(otherApp)
|
||||
}
|
||||
await session.dispose()
|
||||
await otherSession.dispose()
|
||||
hostA.cleanup()
|
||||
hostB.cleanup()
|
||||
}
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user