fix: stabilize release terminal e2e (#5869)
Co-authored-by: Orca <help@stably.ai>
This commit is contained in:
parent
230297b430
commit
3478efca08
|
|
@ -1,6 +1,5 @@
|
|||
import { useEffect, useId, useRef, useState } from 'react'
|
||||
import { useEffect, useId, useMemo, useRef, useState } from 'react'
|
||||
import { RefreshCw } from 'lucide-react'
|
||||
import { useShallow } from 'zustand/react/shallow'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { useAppStore } from '../store'
|
||||
import { translate } from '@/i18n/i18n'
|
||||
|
|
@ -73,20 +72,21 @@ export default function CodexRestartChip({
|
|||
isVisible?: boolean
|
||||
worktreeId: string
|
||||
}): React.JSX.Element | null {
|
||||
const { staleWorktreePtyIds, restartNotice } = useAppStore(
|
||||
useShallow((s) => {
|
||||
const stalePtyIds = collectStalePtyIdsForTabs({
|
||||
tabs: s.tabsByWorktree[worktreeId] ?? EMPTY_TABS,
|
||||
ptyIdsByTabId: s.ptyIdsByTabId,
|
||||
codexRestartNoticeByPtyId: s.codexRestartNoticeByPtyId
|
||||
})
|
||||
const firstStalePtyId = stalePtyIds[0]
|
||||
return {
|
||||
staleWorktreePtyIds: stalePtyIds,
|
||||
restartNotice: firstStalePtyId ? s.codexRestartNoticeByPtyId[firstStalePtyId] : undefined
|
||||
}
|
||||
})
|
||||
const tabs = useAppStore((s) => s.tabsByWorktree[worktreeId] ?? EMPTY_TABS)
|
||||
const ptyIdsByTabId = useAppStore((s) => s.ptyIdsByTabId)
|
||||
const codexRestartNoticeByPtyId = useAppStore((s) => s.codexRestartNoticeByPtyId)
|
||||
const staleWorktreePtyIds = useMemo(
|
||||
() =>
|
||||
collectStalePtyIdsForTabs({
|
||||
tabs,
|
||||
ptyIdsByTabId,
|
||||
codexRestartNoticeByPtyId
|
||||
}),
|
||||
[codexRestartNoticeByPtyId, ptyIdsByTabId, tabs]
|
||||
)
|
||||
const restartNotice = staleWorktreePtyIds[0]
|
||||
? codexRestartNoticeByPtyId[staleWorktreePtyIds[0]]
|
||||
: undefined
|
||||
const queueCodexPaneRestarts = useAppStore((s) => s.queueCodexPaneRestarts)
|
||||
const clearCodexRestartNotice = useAppStore((s) => s.clearCodexRestartNotice)
|
||||
|
||||
|
|
@ -96,10 +96,11 @@ export default function CodexRestartChip({
|
|||
)
|
||||
|
||||
const currentCollapseState = getCodexRestartOverlayCollapseState(collapseState, noticeKey)
|
||||
// Why: a new account switch must reopen loud mode even if the prior notice was collapsed.
|
||||
if (currentCollapseState !== collapseState) {
|
||||
setCollapseState(currentCollapseState)
|
||||
}
|
||||
useEffect(() => {
|
||||
// Why: a new account switch must reopen loud mode without updating state
|
||||
// during render, which can trip React's external-store snapshot guard.
|
||||
setCollapseState((state) => getCodexRestartOverlayCollapseState(state, noticeKey))
|
||||
}, [noticeKey])
|
||||
|
||||
if (staleWorktreePtyIds.length === 0 || !restartNotice) {
|
||||
return null
|
||||
|
|
|
|||
|
|
@ -1,10 +1,33 @@
|
|||
import { describe, expect, it, vi } from 'vitest'
|
||||
import {
|
||||
// @vitest-environment happy-dom
|
||||
|
||||
import React, { act } from 'react'
|
||||
import { createRoot, type Root } from 'react-dom/client'
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import { useAppStore } from '../store'
|
||||
import CodexRestartChip, {
|
||||
collectStalePtyIdsForTabs,
|
||||
collectStaleWorktreePtyIds,
|
||||
dismissStaleWorktreePtyIds
|
||||
} from './CodexRestartChip'
|
||||
|
||||
let container: HTMLDivElement
|
||||
let root: Root
|
||||
|
||||
beforeEach(() => {
|
||||
useAppStore.setState(useAppStore.getInitialState(), true)
|
||||
container = document.createElement('div')
|
||||
document.body.appendChild(container)
|
||||
root = createRoot(container)
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
act(() => {
|
||||
root.unmount()
|
||||
})
|
||||
container.remove()
|
||||
useAppStore.setState(useAppStore.getInitialState(), true)
|
||||
})
|
||||
|
||||
describe('CodexRestartChip helpers', () => {
|
||||
it('collects all stale PTY ids for tabs in a worktree', () => {
|
||||
expect(
|
||||
|
|
@ -68,4 +91,38 @@ describe('CodexRestartChip helpers', () => {
|
|||
expect(clearCodexRestartNotice).toHaveBeenNthCalledWith(2, 'pty-3')
|
||||
expect(clearCodexRestartNotice).toHaveBeenCalledTimes(2)
|
||||
})
|
||||
|
||||
it('renders a stale Codex restart notice without an external-store update loop', async () => {
|
||||
useAppStore.setState({
|
||||
tabsByWorktree: {
|
||||
'worktree-1': [
|
||||
{
|
||||
id: 'tab-1',
|
||||
worktreeId: 'worktree-1',
|
||||
title: 'Terminal',
|
||||
customTitle: null,
|
||||
color: null,
|
||||
sortOrder: 0,
|
||||
createdAt: 1,
|
||||
ptyId: null
|
||||
}
|
||||
]
|
||||
},
|
||||
ptyIdsByTabId: {
|
||||
'tab-1': ['pty-1']
|
||||
},
|
||||
codexRestartNoticeByPtyId: {
|
||||
'pty-1': {
|
||||
previousAccountLabel: 'old@example.com',
|
||||
nextAccountLabel: 'new@example.com'
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
await act(async () => {
|
||||
root.render(React.createElement(CodexRestartChip, { worktreeId: 'worktree-1' }))
|
||||
})
|
||||
|
||||
expect(container.textContent).toContain('Codex is still signed in as old@example.com')
|
||||
})
|
||||
})
|
||||
|
|
|
|||
|
|
@ -25,7 +25,16 @@ export type ViewportSize = {
|
|||
}
|
||||
|
||||
function isCursorProbePixel(red: number, green: number, blue: number, alpha: number): boolean {
|
||||
return alpha > 240 && Math.abs(red - 35) <= 2 && green >= 253 && Math.abs(blue - 69) <= 2
|
||||
if (alpha <= 240) {
|
||||
return false
|
||||
}
|
||||
|
||||
const isRawProbeColor = Math.abs(red - 35) <= 2 && green >= 253 && Math.abs(blue - 69) <= 2
|
||||
// Why: WebGL can composite the forced cursor color over the terminal
|
||||
// background before Playwright captures pixels, especially on macOS.
|
||||
const isCompositedProbeColor =
|
||||
Math.abs(red - 121) <= 3 && green >= 249 && Math.abs(blue - 100) <= 3
|
||||
return isRawProbeColor || isCompositedProbeColor
|
||||
}
|
||||
|
||||
export function analyzeRasterCursorCells(
|
||||
|
|
|
|||
Loading…
Reference in New Issue