Skip to content

Commit

Permalink
fix(app): dont block odd protocol setup for mag block (#12737)
Browse files Browse the repository at this point in the history
Exclude mag block from odd connection checks
  • Loading branch information
b-cooper authored May 18, 2023
1 parent f945447 commit 2d1e349
Show file tree
Hide file tree
Showing 10 changed files with 163 additions and 69 deletions.
1 change: 1 addition & 0 deletions app/src/assets/localization/en/protocol_setup.json
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@
"multiple_modules_missing": "Multiple modules missing",
"multiple_modules_modal": "Setting up modules of the same type",
"multiple_modules": "Multiple modules of the same type",
"n_a": "N/A",
"no_data": "no data",
"no_labware_offset_data": "No Labware Offset Data yet",
"no_usb_port_yet": "No USB Port Yet",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,10 @@ import {
useRobot,
} from '.'
import {
MAGNETIC_BLOCK_TYPE,
ModuleType,
NON_CONNECTING_MODULE_TYPES,
getModuleType,
} from '@opentrons/shared-data'

const NON_CONNECTING_MODULE_TYPES: ModuleType[] = [MAGNETIC_BLOCK_TYPE]

interface UnmatchedModuleResults {
missingModuleIds: string[]
remainingAttachedModules: AttachedModule[]
Expand Down
23 changes: 18 additions & 5 deletions app/src/organisms/GripperWizardFlows/MountGripper.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
SPACING,
PrimaryButton,
} from '@opentrons/components'
import { useInstrumentsQuery } from '@opentrons/react-api-client'
import { css } from 'styled-components'
import * as React from 'react'
import { useTranslation } from 'react-i18next'
Expand All @@ -18,7 +19,6 @@ import { SimpleWizardBody } from '../../molecules/SimpleWizardBody'
import mountGripper from '../../assets/videos/gripper-wizards/MOUNT_GRIPPER.webm'

import type { GripperWizardStepProps } from './types'
import { useInstrumentsQuery } from '@opentrons/react-api-client'

const CAPITALIZE_FIRST_LETTER_STYLE = css`
&:first-letter {
Expand All @@ -28,14 +28,27 @@ const CAPITALIZE_FIRST_LETTER_STYLE = css`
export const MountGripper = (
props: GripperWizardStepProps
): JSX.Element | null => {
const { proceed, attachedGripper, isRobotMoving, goBack } = props
const { proceed, isRobotMoving, goBack } = props
const { t } = useTranslation(['gripper_wizard_flows', 'shared'])
const [showUnableToDetect, setShowUnableToDetect] = React.useState(false)

// TODO(bc, 2023-03-23): remove this temporary local poll in favor of the single top level poll in InstrumentsAndModules
const { data: instrumentsQueryData, refetch } = useInstrumentsQuery({
refetchInterval: 3000,
})
const isGripperAttached = (instrumentsQueryData?.data ?? []).some(
i => i.mount === 'extension'
)

const handleOnClick = (): void => {
attachedGripper == null ? setShowUnableToDetect(true) : proceed()
refetch()
.then(() => {
isGripperAttached ? proceed() : setShowUnableToDetect(true)
})
.catch(() => {
setShowUnableToDetect(true)
})
}
// TODO(bc, 2023-03-23): remove this temporary local poll in favor of the single top level poll in InstrumentsAndModules
useInstrumentsQuery({ refetchInterval: 3000 })

if (isRobotMoving)
return (
Expand Down
48 changes: 27 additions & 21 deletions app/src/organisms/GripperWizardFlows/UnmountGripper.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,32 +21,40 @@ import { useInstrumentsQuery } from '@opentrons/react-api-client'
export const UnmountGripper = (
props: GripperWizardStepProps
): JSX.Element | null => {
const {
proceed,
attachedGripper,
isRobotMoving,
goBack,
chainRunCommands,
} = props
const { proceed, isRobotMoving, goBack, chainRunCommands } = props
const { t } = useTranslation(['gripper_wizard_flows', 'shared'])

// TODO(bc, 2023-03-23): remove this temporary local poll in favor of the single top level poll in InstrumentsAndModules
const { data: instrumentsQueryData, refetch } = useInstrumentsQuery({
refetchInterval: 3000,
})
const isGripperStillAttached = (instrumentsQueryData?.data ?? []).some(
i => i.mount === 'extension'
)

const [
showGripperStillDetected,
setShowGripperStillDetected,
] = React.useState(false)
const handleContinue = (): void => {
if (attachedGripper == null) {
chainRunCommands(
[{ commandType: 'home' as const, params: {} }],
true
).then(() => {
proceed()
refetch()
.then(() => {
if (!isGripperStillAttached) {
chainRunCommands([{ commandType: 'home' as const, params: {} }], true)
.then(() => {
proceed()
})
.catch(() => {
// TODO(BC, 2023-05-18): set fatal error here if home fails
})
} else {
setShowGripperStillDetected(true)
}
})
.catch(() => {
setShowGripperStillDetected(true)
})
} else {
setShowGripperStillDetected(true)
}
}
// TODO(bc, 2023-03-23): remove this temporary local poll in favor of the single top level poll in InstrumentsAndModules
useInstrumentsQuery({ refetchInterval: 3000 })

if (isRobotMoving)
return (
Expand All @@ -71,9 +79,7 @@ export const UnmountGripper = (
</Link>
<PrimaryButton
textTransform={TYPOGRAPHY.textTransformCapitalize}
onClick={() => {
handleContinue()
}}
onClick={handleContinue}
>
{t('shared:try_again')}
</PrimaryButton>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,22 +1,34 @@
import * as React from 'react'
import { renderWithProviders } from '@opentrons/components'
import { useInstrumentsQuery } from '@opentrons/react-api-client'
import { instrumentsResponseFixture } from '@opentrons/api-client'
import { i18n } from '../../../i18n'

import { MountGripper } from '../MountGripper'
import { GRIPPER_FLOW_TYPES } from '../constants'
import { instrumentsResponseFixture } from '@opentrons/api-client'

jest.mock('@opentrons/react-api-client')

const mockUseInstrumentsQuery = useInstrumentsQuery as jest.MockedFunction<
typeof useInstrumentsQuery
>

const mockRunId = 'fakeRunId'

describe('MountGripper', () => {
let render: (
props?: Partial<React.ComponentProps<typeof MountGripper>>
) => ReturnType<typeof renderWithProviders>

const mockGoBack = jest.fn()
const mockProceed = jest.fn()
const mockChainRunCommands = jest.fn()
const mockRunId = 'fakeRunId'
let mockRefetch: jest.Mock
let mockGoBack: jest.Mock
let mockProceed: jest.Mock
let mockChainRunCommands: jest.Mock

beforeEach(() => {
mockGoBack = jest.fn()
mockProceed = jest.fn()
mockChainRunCommands = jest.fn()
mockRefetch = jest.fn(() => Promise.resolve())
render = (props = {}) => {
return renderWithProviders(
<MountGripper
Expand All @@ -38,36 +50,50 @@ describe('MountGripper', () => {
jest.resetAllMocks()
})

it('clicking confirm calls proceed if attached gripper', () => {
const { getByRole } = render({
attachedGripper: instrumentsResponseFixture.data[0],
})[0]
getByRole('button', { name: 'continue' }).click()
it('clicking confirm calls proceed if attached gripper', async () => {
mockUseInstrumentsQuery.mockReturnValue({
refetch: mockRefetch,
data: instrumentsResponseFixture,
} as any)
const { getByRole } = render()[0]
await getByRole('button', { name: 'continue' }).click()
expect(mockProceed).toHaveBeenCalled()
})

it('clicking confirm shows unable to detect if no gripper attached', () => {
const { getByRole, getByText } = render({ attachedGripper: null })[0]
getByRole('button', { name: 'continue' }).click()
it('clicking confirm shows unable to detect if no gripper attached', async () => {
mockUseInstrumentsQuery.mockReturnValue({
refetch: mockRefetch,
data: null,
} as any)
const { getByRole, getByText } = render()[0]
await getByRole('button', { name: 'continue' }).click()
expect(mockProceed).not.toHaveBeenCalled()
getByText('Unable to detect Gripper')
await getByText('Unable to detect Gripper')
const tryAgainButton = getByRole('button', { name: 'try again' })
tryAgainButton.click()
expect(mockProceed).not.toHaveBeenCalled()
getByRole('button', { name: 'continue' }).click()
await getByRole('button', { name: 'continue' }).click()
expect(mockProceed).not.toHaveBeenCalled()
const goBackButton = getByRole('button', { name: 'Go back' })
const goBackButton = await getByRole('button', { name: 'Go back' })
goBackButton.click()
expect(mockGoBack).toHaveBeenCalled()
})

it('clicking go back calls back', () => {
mockUseInstrumentsQuery.mockReturnValue({
refetch: mockRefetch,
data: null,
} as any)
const { getByLabelText } = render()[0]
getByLabelText('back').click()
expect(mockGoBack).toHaveBeenCalled()
})

it('renders correct text', () => {
mockUseInstrumentsQuery.mockReturnValue({
refetch: mockRefetch,
data: null,
} as any)
const { getByText } = render()[0]
getByText('Connect and Screw In Gripper')
getByText(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,21 +1,34 @@
import * as React from 'react'
import { useInstrumentsQuery } from '@opentrons/react-api-client'
import { renderWithProviders } from '@opentrons/components'
import { instrumentsResponseFixture } from '@opentrons/api-client'
import { i18n } from '../../../i18n'

import { UnmountGripper } from '../UnmountGripper'
import { GRIPPER_FLOW_TYPES } from '../constants'

jest.mock('@opentrons/react-api-client')

const mockUseInstrumentsQuery = useInstrumentsQuery as jest.MockedFunction<
typeof useInstrumentsQuery
>

const mockRunId = 'fakeRunId'
describe('UnmountGripper', () => {
let render: (
props?: Partial<React.ComponentProps<typeof UnmountGripper>>
) => ReturnType<typeof renderWithProviders>

const mockGoBack = jest.fn()
const mockProceed = jest.fn()
const mockChainRunCommands = jest.fn(() => Promise.resolve())
const mockRunId = 'fakeRunId'
let mockRefetch: jest.Mock
let mockGoBack: jest.Mock
let mockProceed: jest.Mock
let mockChainRunCommands: jest.Mock

beforeEach(() => {
mockGoBack = jest.fn()
mockProceed = jest.fn()
mockChainRunCommands = jest.fn(() => Promise.resolve())
mockRefetch = jest.fn(() => Promise.resolve())
render = props => {
return renderWithProviders(
<UnmountGripper
Expand All @@ -37,23 +50,35 @@ describe('UnmountGripper', () => {
jest.resetAllMocks()
})

it('clicking confirm proceed calls home and proceed if gripper attached', async () => {
it('clicking confirm proceed calls home and proceed if gripper detached', async () => {
mockUseInstrumentsQuery.mockReturnValue({
refetch: mockRefetch,
data: null,
} as any)
const { getByRole } = render({ attachedGripper: null })[0]
await getByRole('button', { name: 'continue' }).click()
expect(mockChainRunCommands).toHaveBeenCalledWith(
await expect(mockChainRunCommands).toHaveBeenCalledWith(
[{ commandType: 'home', params: {} }],
true
)
expect(mockProceed).toHaveBeenCalled()
})

it('clicking go back calls back', () => {
mockUseInstrumentsQuery.mockReturnValue({
refetch: mockRefetch,
data: instrumentsResponseFixture,
} as any)
const { getByLabelText } = render()[0]
getByLabelText('back').click()
expect(mockGoBack).toHaveBeenCalled()
})

it('renders correct text', () => {
mockUseInstrumentsQuery.mockReturnValue({
refetch: mockRefetch,
data: instrumentsResponseFixture,
} as any)
const { getByText } = render()[0]
getByText('Loosen Screws and Detach')
getByText(
Expand Down
40 changes: 26 additions & 14 deletions app/src/organisms/ProtocolSetupModules/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import {
getModuleDisplayName,
getModuleType,
inferModuleOrientationFromXCoordinate,
NON_CONNECTING_MODULE_TYPES,
TC_MODULE_LOCATION_OT3,
THERMOCYCLER_MODULE_TYPE,
} from '@opentrons/shared-data'
Expand Down Expand Up @@ -66,15 +67,15 @@ function RowModule({
setShowMultipleModulesModal,
}: RowModuleProps): JSX.Element {
const { t } = useTranslation('protocol_setup')
const attachedModuleMatch = !!module.attachedModuleMatch
const isNonConnectingModule = NON_CONNECTING_MODULE_TYPES.includes(
module.moduleDef.moduleType
)
const isModuleReady =
isNonConnectingModule || module.attachedModuleMatch != null
return (
<Flex
alignItems={ALIGN_CENTER}
backgroundColor={
attachedModuleMatch
? `${COLORS.successEnabled}${COLORS.opacity20HexCode}`
: COLORS.warningBackgroundMed
}
backgroundColor={isModuleReady ? COLORS.green3 : COLORS.yellow3}
borderRadius={BORDERS.size3}
cursor={isDuplicateModuleModel ? 'pointer' : 'inherit'}
gridGap={SPACING.spacing24}
Expand Down Expand Up @@ -109,14 +110,25 @@ function RowModule({
/>
) : null}
</Flex>
<Chip
text={
attachedModuleMatch ? t('module_connected') : t('module_disconnected')
}
type={attachedModuleMatch ? 'success' : 'warning'}
background={false}
iconName="connection-status"
/>
{isNonConnectingModule ? (
<Flex
alignItems={ALIGN_CENTER}
padding={`${SPACING.spacing8} ${SPACING.spacing16}`}
>
<StyledText as="p" fontWeight={TYPOGRAPHY.fontWeightSemiBold}>
{t('n_a')}
</StyledText>
</Flex>
) : (
<Chip
text={
isModuleReady ? t('module_connected') : t('module_disconnected')
}
type={isModuleReady ? 'success' : 'warning'}
background={false}
iconName="connection-status"
/>
)}
</Flex>
)
}
Expand Down
Loading

0 comments on commit 2d1e349

Please sign in to comment.