Skip to content

Commit

Permalink
fix(app): Fix persistent cancelled state (#14228)
Browse files Browse the repository at this point in the history
* fix(app): add dimsissing current run back

* fix(app): fix robot stuck in cancelled state

* test fixes
  • Loading branch information
mjhuff authored and ncdiehl11 committed Dec 20, 2023
1 parent 59c9e8e commit 4a12a9d
Show file tree
Hide file tree
Showing 5 changed files with 60 additions and 12 deletions.
16 changes: 13 additions & 3 deletions app/src/organisms/Devices/ProtocolRun/ProtocolRunHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ import { RunProgressMeter } from '../../RunProgressMeter'
import { getIsFixtureMismatch } from '../../../resources/deck_configuration/utils'
import { useDeckConfigurationCompatibility } from '../../../resources/deck_configuration/hooks'
import { useMostRecentCompletedAnalysis } from '../../LabwarePositionCheck/useMostRecentCompletedAnalysis'
import { useMostRecentRunId } from '../../ProtocolUpload/hooks/useMostRecentRunId'

import type { Run, RunError } from '@opentrons/api-client'
import type { State } from '../../../redux/types'
Expand Down Expand Up @@ -160,6 +161,7 @@ export function ProtocolRunHeader({
const { analysisErrors } = useProtocolAnalysisErrors(runId)
const { data: attachedInstruments } = useInstrumentsQuery()
const isRunCurrent = Boolean(useRunQuery(runId)?.data?.data?.current)
const mostRecentRunId = useMostRecentRunId()
const { closeCurrentRun, isClosingCurrentRun } = useCloseCurrentRun()
const { startedAt, stoppedAt, completedAt } = useRunTimestamps(runId)
const [showRunFailedModal, setShowRunFailedModal] = React.useState(false)
Expand Down Expand Up @@ -248,8 +250,9 @@ export function ProtocolRunHeader({
...robotAnalyticsData,
},
})
closeCurrentRun()
}
}, [runStatus, isRunCurrent, runId])
}, [runStatus, isRunCurrent, runId, closeCurrentRun])

const startedAtTimestamp =
startedAt != null ? formatTimestamp(startedAt) : EMPTY_TIMESTAMP
Expand Down Expand Up @@ -371,7 +374,9 @@ export function ProtocolRunHeader({
}}
/>
) : null}
{isRunCurrent && showDropTipBanner && pipettesWithTip.length !== 0 ? (
{mostRecentRunId === runId &&
showDropTipBanner &&
pipettesWithTip.length !== 0 ? (
<ProtocolDropTipBanner
onLaunchWizardClick={setShowDropTipWizard}
onCloseClick={() => {
Expand Down Expand Up @@ -784,6 +789,11 @@ function TerminalRunBanner(props: TerminalRunProps): JSX.Element | null {
} = props
const { t } = useTranslation('run_details')

const handleClick = (): void => {
handleClearClick()
setShowRunFailedModal(true)
}

if (runStatus === RUN_STATUS_FAILED || runStatus === RUN_STATUS_SUCCEEDED) {
return (
<>
Expand All @@ -808,7 +818,7 @@ function TerminalRunBanner(props: TerminalRunProps): JSX.Element | null {
</StyledText>

<LinkButton
onClick={() => setShowRunFailedModal(true)}
onClick={handleClick}
textDecoration={TYPOGRAPHY.textDecorationUnderline}
>
{t('view_error')}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import {
useRunQuery,
useModulesQuery,
usePipettesQuery,
useDismissCurrentRunMutation,
useEstopQuery,
useDoorQuery,
useInstrumentsQuery,
Expand Down Expand Up @@ -91,6 +92,7 @@ import { getPipettesWithTipAttached } from '../../../DropTipWizard/getPipettesWi
import { getIsFixtureMismatch } from '../../../../resources/deck_configuration/utils'
import { useDeckConfigurationCompatibility } from '../../../../resources/deck_configuration/hooks'
import { useMostRecentCompletedAnalysis } from '../../../LabwarePositionCheck/useMostRecentCompletedAnalysis'
import { useMostRecentRunId } from '../../../ProtocolUpload/hooks/useMostRecentRunId'

import type { UseQueryResult } from 'react-query'
import type { Run } from '@opentrons/api-client'
Expand Down Expand Up @@ -139,6 +141,7 @@ jest.mock('../../../DropTipWizard/getPipettesWithTipAttached')
jest.mock('../../../../resources/deck_configuration/utils')
jest.mock('../../../../resources/deck_configuration/hooks')
jest.mock('../../../LabwarePositionCheck/useMostRecentCompletedAnalysis')
jest.mock('../../../ProtocolUpload/hooks/useMostRecentRunId')

const mockGetIsHeaterShakerAttached = getIsHeaterShakerAttached as jest.MockedFunction<
typeof getIsHeaterShakerAttached
Expand Down Expand Up @@ -189,6 +192,9 @@ const mockUsePipettesQuery = usePipettesQuery as jest.MockedFunction<
const mockConfirmCancelModal = ConfirmCancelModal as jest.MockedFunction<
typeof ConfirmCancelModal
>
const mockUseDismissCurrentRunMutation = useDismissCurrentRunMutation as jest.MockedFunction<
typeof useDismissCurrentRunMutation
>
const mockHeaterShakerIsRunningModal = HeaterShakerIsRunningModal as jest.MockedFunction<
typeof HeaterShakerIsRunningModal
>
Expand Down Expand Up @@ -242,6 +248,9 @@ const mockUseDeckConfigurationCompatibility = useDeckConfigurationCompatibility
const mockUseMostRecentCompletedAnalysis = useMostRecentCompletedAnalysis as jest.MockedFunction<
typeof useMostRecentCompletedAnalysis
>
const mockUseMostRecentRunId = useMostRecentRunId as jest.MockedFunction<
typeof useMostRecentRunId
>

const ROBOT_NAME = 'otie'
const RUN_ID = '95e67900-bc9f-4fbf-92c6-cc4d7226a51b'
Expand Down Expand Up @@ -398,6 +407,11 @@ describe('ProtocolRunHeader', () => {
when(mockUseTrackProtocolRunEvent).calledWith(RUN_ID).mockReturnValue({
trackProtocolRunEvent: mockTrackProtocolRunEvent,
})
when(mockUseDismissCurrentRunMutation)
.calledWith()
.mockReturnValue({
dismissCurrentRun: jest.fn(),
} as any)
when(mockUseUnmatchedModulesForProtocol)
.calledWith(ROBOT_NAME, RUN_ID)
.mockReturnValue({ missingModuleIds: [], remainingAttachedModules: [] })
Expand Down Expand Up @@ -429,6 +443,7 @@ describe('ProtocolRunHeader', () => {
} as any)
mockUseDeckConfigurationCompatibility.mockReturnValue([])
when(mockGetIsFixtureMismatch).mockReturnValue(false)
when(mockUseMostRecentRunId).mockReturnValue(RUN_ID)
})

afterEach(() => {
Expand Down Expand Up @@ -518,7 +533,7 @@ describe('ProtocolRunHeader', () => {
data: { data: { ...mockIdleUnstartedRun, current: true } },
} as UseQueryResult<Run>)
render()
expect(mockCloseCurrentRun).not.toBeCalled()
expect(mockCloseCurrentRun).toBeCalled()
expect(mockTrackProtocolRunEvent).toBeCalled()
expect(mockTrackProtocolRunEvent).toBeCalledWith({
name: ANALYTICS_PROTOCOL_RUN_FINISH,
Expand Down Expand Up @@ -825,9 +840,9 @@ describe('ProtocolRunHeader', () => {
when(mockUseRunStatus).calledWith(RUN_ID).mockReturnValue(RUN_STATUS_FAILED)
render()

fireEvent.click(screen.getByText('View error'))
expect(mockCloseCurrentRun).not.toBeCalled()
screen.getByText('mock RunFailedModal')
getByText('View error').click()
expect(mockCloseCurrentRun).toBeCalled()
getByText('mock RunFailedModal')
})

it('renders a clear protocol banner when run has been canceled', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@ import {
Flex,
SPACING,
} from '@opentrons/components'
import { useStopRunMutation } from '@opentrons/react-api-client'
import {
useStopRunMutation,
useDismissCurrentRunMutation,
} from '@opentrons/react-api-client'

import { StyledText } from '../../../atoms/text'
import { SmallButton } from '../../../atoms/buttons'
Expand All @@ -37,6 +40,10 @@ export function ConfirmCancelRunModal({
}: ConfirmCancelRunModalProps): JSX.Element {
const { t } = useTranslation(['run_details', 'shared'])
const { stopRun } = useStopRunMutation()
const {
dismissCurrentRun,
isLoading: isDismissing,
} = useDismissCurrentRunMutation()
const runStatus = useRunStatus(runId)
const { trackProtocolRunEvent } = useTrackProtocolRunEvent(runId)
const history = useHistory()
Expand All @@ -61,6 +68,7 @@ export function ConfirmCancelRunModal({
React.useEffect(() => {
if (runStatus === RUN_STATUS_STOPPED) {
trackProtocolRunEvent({ name: ANALYTICS_PROTOCOL_RUN_CANCEL })
dismissCurrentRun(runId)
if (!isActiveRun) {
if (protocolId != null) {
history.push(`/protocols/${protocolId}`)
Expand All @@ -71,7 +79,7 @@ export function ConfirmCancelRunModal({
}
}, [runStatus])

return isCanceling ? (
return isCanceling || isDismissing ? (
<CancelingRunModal />
) : (
<Modal
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,15 @@ describe('ConfirmCancelRunModal', () => {
getByText('Cancel run')
})

it('shoudler render the canceling run modal when run is dismissing', () => {
mockUseDismissCurrentRunMutation.mockReturnValue({
dismissCurrentRun: mockDismissCurrentRun,
isLoading: true,
} as any)
const [{ getByText }] = render(props)
getByText('mock CancelingRunModal')
})

it('when tapping go back, the mock function is called', () => {
const [{ getByText }] = render(props)
const button = getByText('Go back')
Expand All @@ -138,7 +147,7 @@ describe('ConfirmCancelRunModal', () => {
.mockReturnValue(RUN_STATUS_STOPPED)
render(props)

expect(mockDismissCurrentRun).not.toHaveBeenCalled()
expect(mockDismissCurrentRun).toHaveBeenCalled()
expect(mockTrackProtocolRunEvent).toHaveBeenCalled()
})

Expand All @@ -152,7 +161,7 @@ describe('ConfirmCancelRunModal', () => {
.mockReturnValue(RUN_STATUS_STOPPED)
render(props)

expect(mockDismissCurrentRun).not.toHaveBeenCalled()
expect(mockDismissCurrentRun).toHaveBeenCalled()
expect(mockTrackProtocolRunEvent).toHaveBeenCalled()
expect(mockPush).toHaveBeenCalledWith('/protocols')
})
Expand Down
8 changes: 7 additions & 1 deletion app/src/pages/OnDeviceDisplay/RunSummary.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ import { formatTimeWithUtcLabel } from '../../resources/runs/utils'
import { handleTipsAttachedModal } from '../../organisms/DropTipWizard/TipsAttachedModal'
import { getPipettesWithTipAttached } from '../../organisms/DropTipWizard/getPipettesWithTipAttached'
import { getPipetteModelSpecs, FLEX_ROBOT_TYPE } from '@opentrons/shared-data'
import { useMostRecentRunId } from '../../organisms/ProtocolUpload/hooks/useMostRecentRunId'

import type { OnDeviceRouteParams } from '../../App/types'
import type { PipetteModelSpecs } from '@opentrons/shared-data'
Expand All @@ -79,6 +80,7 @@ export function RunSummary(): JSX.Element {
const host = useHost()
const { data: runRecord } = useRunQuery(runId, { staleTime: Infinity })
const isRunCurrent = Boolean(runRecord?.data?.current)
const mostRecentRunId = useMostRecentRunId()
const { data: attachedInstruments } = useInstrumentsQuery()
const runStatus = runRecord?.data.status ?? null
const didRunSucceed = runStatus === RUN_STATUS_SUCCEEDED
Expand Down Expand Up @@ -130,7 +132,11 @@ export function RunSummary(): JSX.Element {

const handleReturnToDash = (): void => {
const { mount, specs } = pipettesWithTip[0] || {}
if (isRunCurrent && pipettesWithTip.length !== 0 && specs != null) {
if (
mostRecentRunId === runId &&
pipettesWithTip.length !== 0 &&
specs != null
) {
handleTipsAttachedModal(
mount,
specs,
Expand Down

0 comments on commit 4a12a9d

Please sign in to comment.