From c6f0f7974d1d77a448d841f0941080698e9ad765 Mon Sep 17 00:00:00 2001 From: alexjoel42 Date: Mon, 27 Jan 2025 10:32:30 -0500 Subject: [PATCH] Rqa 3878 tempdeck pd cypress and refactoring of helper functions (#17345) # Overview This is a refactoring where we are going to execute protocol run steps through StepExecution (oh and btw it also adds a new module testing e2e test!) Each distinct test component (modules, transfer, mix, etc.) will have its own helper function in support like support/SetupSteps, support/SupportModules ## Test Plan and Hands on Testing These are Cypress tests but we might need to collaborate with dev to handle tests failing because of copy changes . ## Changelog Updated testing environment for PD so that way we now can have helper files for each section of our PD code and don't have to copy and paste code across different files. ## Review requests ## Risk assessment Low. --- .../e2e/ProtocolDesignerModulesTest.cy.ts | 99 +++++ protocol-designer/cypress/e2e/createNew.cy.ts | 42 +- .../cypress/e2e/createNewFlex.cy.ts | 107 ++--- .../cypress/e2e/transferSettings.cy.ts | 112 +++--- .../support/{createNew.ts => SetupSteps.ts} | 375 +++++++++--------- .../cypress/support/StepExecution.ts | 51 +++ .../cypress/support/SupportModules.ts | 98 +++++ 7 files changed, 574 insertions(+), 310 deletions(-) create mode 100644 protocol-designer/cypress/e2e/ProtocolDesignerModulesTest.cy.ts rename protocol-designer/cypress/support/{createNew.ts => SetupSteps.ts} (51%) create mode 100644 protocol-designer/cypress/support/StepExecution.ts create mode 100644 protocol-designer/cypress/support/SupportModules.ts diff --git a/protocol-designer/cypress/e2e/ProtocolDesignerModulesTest.cy.ts b/protocol-designer/cypress/e2e/ProtocolDesignerModulesTest.cy.ts new file mode 100644 index 00000000000..a6f31ab4223 --- /dev/null +++ b/protocol-designer/cypress/e2e/ProtocolDesignerModulesTest.cy.ts @@ -0,0 +1,99 @@ +import { SetupActions, SetupVerifications } from '../support/SetupSteps' +import { UniversalActions } from '../support/universalActions' +import { ModActions, ModVerifications } from '../support/SupportModules' +import { runSteps } from '../support/StepExecution' +import type { StepsList } from '../support/StepExecution' + +describe('The Redesigned Create Protocol Landing Page', () => { + beforeEach(() => { + cy.visit('/') + cy.verifyHomePage() + cy.closeAnalyticsModal() + }) + + it('content and step 1 flow works', () => { + cy.clickCreateNew() + cy.verifyCreateNewHeader() + const steps: StepsList = [ + SetupVerifications.OnStep1, + SetupVerifications.FlexSelected, + UniversalActions.Snapshot, + SetupActions.SelectOT2, + SetupVerifications.OT2Selected, + UniversalActions.Snapshot, + SetupActions.SelectFlex, + SetupVerifications.FlexSelected, + UniversalActions.Snapshot, + SetupActions.Confirm, + SetupVerifications.OnStep2, + SetupActions.SingleChannelPipette50, + SetupVerifications.StepTwo50uL, + UniversalActions.Snapshot, + SetupActions.Confirm, + SetupVerifications.StepTwoPart3, + UniversalActions.Snapshot, + SetupActions.Confirm, + SetupVerifications.OnStep3, + SetupActions.YesGripper, + SetupActions.Confirm, + SetupVerifications.Step4Verification, + SetupActions.AddThermocycler, + SetupVerifications.ThermocyclerImg, + SetupActions.AddHeaterShaker, + SetupVerifications.HeaterShakerImg, + SetupActions.AddMagBlock, + SetupVerifications.MagBlockImg, + SetupActions.AddTempdeck2, + SetupVerifications.Tempdeck2Img, + SetupActions.Confirm, + SetupActions.Confirm, + SetupActions.Confirm, + SetupActions.EditProtocolA, + SetupActions.ChoseDeckSlotC2, + SetupActions.AddHardwareLabware, + SetupActions.ClickLabwareHeader, + SetupActions.ClickWellPlatesSection, + SetupActions.SelectArmadillo96WellPlate, + SetupActions.ChoseDeckSlotC2Labware, + SetupActions.AddLiquid, + SetupActions.ClickLiquidButton, + SetupActions.DefineLiquid, + SetupActions.LiquidSaveWIP, + SetupActions.WellSelector, + SetupActions.LiquidDropdown, + SetupVerifications.LiquidPage, + UniversalActions.Snapshot, + SetupActions.SelectLiquidWells, + SetupActions.SetVolumeAndSaveforWells, + SetupActions.ChoseDeckSlotC1, + SetupActions.EditHardwareLabwareOnDeck, + SetupActions.ClickLabwareHeader, + SetupActions.AddAdapters, + SetupActions.DeepWellTempModAdapter, + SetupActions.AddNest96DeepWellPlate, + SetupActions.Done, + SetupActions.ProtocolStepsH, + SetupActions.AddStep, + ModActions.AddTemperatureStep, + ModVerifications.TempeDeckInitialForm, + UniversalActions.Snapshot, + ModActions.ActivateTempdeck, + ModActions.InputTempDeck4, + ModActions.SaveButtonTempdeck, + ModActions.PauseAfterSettingTempdeck, + // ModVerifications.Temp4CPauseTextVerification, + UniversalActions.Snapshot, + SetupActions.AddStep, + ModActions.AddTemperatureStep, + ModActions.ActivateTempdeck, + ModActions.InputTempDeck95, + ModActions.SaveButtonTempdeck, + ModActions.PauseAfterSettingTempdeck, + SetupActions.AddStep, + ModActions.AddTemperatureStep, + ModActions.ActivateTempdeck, + ModActions.InputTempDeck100, + ] + runSteps(steps) + }) +}) diff --git a/protocol-designer/cypress/e2e/createNew.cy.ts b/protocol-designer/cypress/e2e/createNew.cy.ts index 230721febad..f675036b8be 100644 --- a/protocol-designer/cypress/e2e/createNew.cy.ts +++ b/protocol-designer/cypress/e2e/createNew.cy.ts @@ -1,6 +1,12 @@ -import { Actions, Verifications, runCreateTest } from '../support/createNew' +import { SetupActions, SetupVerifications } from '../support/SetupSteps' import { UniversalActions } from '../support/universalActions' import '../support/commands' +// Every test is goign to use StepsList +// Now every test will be a list of some combination of support +// typescript file list of actions for specific PD stuff and include StepsList for steps + +import { runSteps } from '../support/StepExecution' +import type { StepsList } from '../support/StepExecution' describe('The Redesigned Create Protocol Landing Page', () => { beforeEach(() => { @@ -11,29 +17,29 @@ describe('The Redesigned Create Protocol Landing Page', () => { it('content and step 1 flow works', () => { cy.verifyCreateNewHeader() cy.clickCreateNew() - const steps: Array = [ - Verifications.OnStep1, - Verifications.FlexSelected, + const steps: StepsList = [ + SetupVerifications.OnStep1, + SetupVerifications.FlexSelected, UniversalActions.Snapshot, - Actions.SelectOT2, - Verifications.OT2Selected, + SetupActions.SelectOT2, + SetupVerifications.OT2Selected, UniversalActions.Snapshot, - Actions.SelectFlex, - Verifications.FlexSelected, + SetupActions.SelectFlex, + SetupVerifications.FlexSelected, UniversalActions.Snapshot, - Actions.Confirm, - Verifications.OnStep2, - Verifications.NinetySixChannel, + SetupActions.Confirm, + SetupVerifications.OnStep2, + SetupVerifications.NinetySixChannel, UniversalActions.Snapshot, - Actions.GoBack, - Verifications.OnStep1, - Actions.SelectOT2, - Actions.Confirm, - Verifications.OnStep2, - Verifications.NotNinetySixChannel, + SetupActions.GoBack, + SetupVerifications.OnStep1, + SetupActions.SelectOT2, + SetupActions.Confirm, + SetupVerifications.OnStep2, + SetupVerifications.NotNinetySixChannel, UniversalActions.Snapshot, ] - runCreateTest(steps) + runSteps(steps) }) }) diff --git a/protocol-designer/cypress/e2e/createNewFlex.cy.ts b/protocol-designer/cypress/e2e/createNewFlex.cy.ts index 8f9017e63ae..143d9ce0862 100644 --- a/protocol-designer/cypress/e2e/createNewFlex.cy.ts +++ b/protocol-designer/cypress/e2e/createNewFlex.cy.ts @@ -1,5 +1,8 @@ -import { Actions, Verifications, runCreateTest } from '../support/createNew' +import { SetupActions, SetupVerifications } from '../support/SetupSteps' import { UniversalActions } from '../support/universalActions' +import '../support/commands' +import { runSteps } from '../support/StepExecution' +import type { StepsList } from '../support/StepExecution' describe('The Redesigned Create Protocol Landing Page', () => { beforeEach(() => { @@ -10,63 +13,63 @@ describe('The Redesigned Create Protocol Landing Page', () => { it('content and step 1 flow works', () => { cy.clickCreateNew() cy.verifyCreateNewHeader() - const steps: Array = [ - Verifications.OnStep1, - Verifications.FlexSelected, + const steps: StepsList = [ + SetupVerifications.OnStep1, + SetupVerifications.FlexSelected, UniversalActions.Snapshot, - Actions.SelectOT2, - Verifications.OT2Selected, + SetupActions.SelectOT2, + SetupVerifications.OT2Selected, UniversalActions.Snapshot, - Actions.SelectFlex, - Verifications.FlexSelected, + SetupActions.SelectFlex, + SetupVerifications.FlexSelected, UniversalActions.Snapshot, - Actions.Confirm, - Verifications.OnStep2, - Actions.SingleChannelPipette50, - Verifications.StepTwo50uL, + SetupActions.Confirm, + SetupVerifications.OnStep2, + SetupActions.SingleChannelPipette50, + SetupVerifications.StepTwo50uL, UniversalActions.Snapshot, - Actions.Confirm, - Verifications.StepTwoPart3, + SetupActions.Confirm, + SetupVerifications.StepTwoPart3, UniversalActions.Snapshot, - Actions.Confirm, - Verifications.OnStep3, - Actions.YesGripper, - Actions.Confirm, - Verifications.Step4Verification, - Actions.AddThermocycler, - Verifications.ThermocyclerImg, - Actions.AddHeaterShaker, - Verifications.HeaterShakerImg, - Actions.AddMagBlock, - Verifications.MagBlockImg, - Actions.AddTempdeck2, - Verifications.Tempdeck2Img, - Actions.Confirm, - Actions.Confirm, - Actions.Confirm, - Actions.EditProtocolA, - Actions.ChoseDeckSlotC2, - Actions.AddHardwareLabware, - Actions.ClickLabwareHeader, - Actions.ClickWellPlatesSection, - Actions.SelectArmadillo96WellPlate, - Actions.ChoseDeckSlotC2Labware, - Actions.AddLiquid, - Actions.ClickLiquidButton, - Actions.DefineLiquid, - Actions.LiquidSaveWIP, - Actions.WellSelector, - Actions.LiquidDropdown, - Verifications.LiquidPage, + SetupActions.Confirm, + SetupVerifications.OnStep3, + SetupActions.YesGripper, + SetupActions.Confirm, + SetupVerifications.Step4Verification, + SetupActions.AddThermocycler, + SetupVerifications.ThermocyclerImg, + SetupActions.AddHeaterShaker, + SetupVerifications.HeaterShakerImg, + SetupActions.AddMagBlock, + SetupVerifications.MagBlockImg, + SetupActions.AddTempdeck2, + SetupVerifications.Tempdeck2Img, + SetupActions.Confirm, + SetupActions.Confirm, + SetupActions.Confirm, + SetupActions.EditProtocolA, + SetupActions.ChoseDeckSlotC2, + SetupActions.AddHardwareLabware, + SetupActions.ClickLabwareHeader, + SetupActions.ClickWellPlatesSection, + SetupActions.SelectArmadillo96WellPlate, + SetupActions.ChoseDeckSlotC2Labware, + SetupActions.AddLiquid, + SetupActions.ClickLiquidButton, + SetupActions.DefineLiquid, + SetupActions.LiquidSaveWIP, + SetupActions.WellSelector, + SetupActions.LiquidDropdown, + SetupVerifications.LiquidPage, UniversalActions.Snapshot, - Actions.SelectLiquidWells, - Actions.SetVolumeAndSaveforWells, - Actions.ChoseDeckSlotC3, - Actions.AddHardwareLabware, - Actions.ClickLabwareHeader, - Actions.ClickWellPlatesSection, - Actions.SelectBioRad96WellPlate, + SetupActions.SelectLiquidWells, + SetupActions.SetVolumeAndSaveforWells, + SetupActions.ChoseDeckSlotC3, + SetupActions.AddHardwareLabware, + SetupActions.ClickLabwareHeader, + SetupActions.ClickWellPlatesSection, + SetupActions.SelectBioRad96WellPlate, ] - runCreateTest(steps) + runSteps(steps) }) }) diff --git a/protocol-designer/cypress/e2e/transferSettings.cy.ts b/protocol-designer/cypress/e2e/transferSettings.cy.ts index a3371e0feff..998ad9bb6c8 100644 --- a/protocol-designer/cypress/e2e/transferSettings.cy.ts +++ b/protocol-designer/cypress/e2e/transferSettings.cy.ts @@ -1,5 +1,7 @@ -import { Actions, Verifications, runCreateTest } from '../support/createNew' +import { SetupActions, SetupVerifications } from '../support/SetupSteps' import { UniversalActions } from '../support/universalActions' +import { runSteps } from '../support/StepExecution' +import type { StepsList } from '../support/StepExecution' describe('The Redesigned Create Protocol Landing Page', () => { beforeEach(() => { @@ -11,67 +13,67 @@ describe('The Redesigned Create Protocol Landing Page', () => { it('content and step 1 flow works', () => { cy.clickCreateNew() cy.verifyCreateNewHeader() - const steps: Array = [ - Verifications.OnStep1, - Verifications.FlexSelected, + const steps: StepsList = [ + SetupVerifications.OnStep1, + SetupVerifications.FlexSelected, UniversalActions.Snapshot, - Actions.SelectOT2, - Verifications.OT2Selected, + SetupActions.SelectOT2, + SetupVerifications.OT2Selected, UniversalActions.Snapshot, - Actions.SelectFlex, - Verifications.FlexSelected, + SetupActions.SelectFlex, + SetupVerifications.FlexSelected, UniversalActions.Snapshot, - Actions.Confirm, - Verifications.OnStep2, - Actions.SingleChannelPipette50, - Verifications.StepTwo50uL, + SetupActions.Confirm, + SetupVerifications.OnStep2, + SetupActions.SingleChannelPipette50, + SetupVerifications.StepTwo50uL, UniversalActions.Snapshot, - Actions.Confirm, - Verifications.StepTwoPart3, + SetupActions.Confirm, + SetupVerifications.StepTwoPart3, UniversalActions.Snapshot, - Actions.Confirm, - Verifications.OnStep3, - Actions.YesGripper, - Actions.Confirm, - Verifications.Step4Verification, - Actions.AddThermocycler, - Verifications.ThermocyclerImg, - Actions.AddHeaterShaker, - Verifications.HeaterShakerImg, - Actions.AddMagBlock, - Verifications.MagBlockImg, - Actions.AddTempdeck2, - Verifications.Tempdeck2Img, - Actions.Confirm, - Actions.Confirm, - Actions.Confirm, - Actions.EditProtocolA, - Actions.ChoseDeckSlotC2, - Actions.AddHardwareLabware, - Actions.ClickLabwareHeader, - Actions.ClickWellPlatesSection, - Actions.SelectArmadillo96WellPlate, - Actions.ChoseDeckSlotC2Labware, - Actions.AddLiquid, - Actions.ClickLiquidButton, - Actions.DefineLiquid, - Actions.LiquidSaveWIP, - Actions.WellSelector, - Actions.LiquidDropdown, - Verifications.LiquidPage, + SetupActions.Confirm, + SetupVerifications.OnStep3, + SetupActions.YesGripper, + SetupActions.Confirm, + SetupVerifications.Step4Verification, + SetupActions.AddThermocycler, + SetupVerifications.ThermocyclerImg, + SetupActions.AddHeaterShaker, + SetupVerifications.HeaterShakerImg, + SetupActions.AddMagBlock, + SetupVerifications.MagBlockImg, + SetupActions.AddTempdeck2, + SetupVerifications.Tempdeck2Img, + SetupActions.Confirm, + SetupActions.Confirm, + SetupActions.Confirm, + SetupActions.EditProtocolA, + SetupActions.ChoseDeckSlotC2, + SetupActions.AddHardwareLabware, + SetupActions.ClickLabwareHeader, + SetupActions.ClickWellPlatesSection, + SetupActions.SelectArmadillo96WellPlate, + SetupActions.ChoseDeckSlotC2Labware, + SetupActions.AddLiquid, + SetupActions.ClickLiquidButton, + SetupActions.DefineLiquid, + SetupActions.LiquidSaveWIP, + SetupActions.WellSelector, + SetupActions.LiquidDropdown, + SetupVerifications.LiquidPage, UniversalActions.Snapshot, - Actions.SelectLiquidWells, - Actions.SetVolumeAndSaveforWells, - Actions.ChoseDeckSlotC3, - Actions.AddHardwareLabware, - Actions.ClickLabwareHeader, - Actions.ClickWellPlatesSection, - Actions.SelectBioRad96WellPlate, - Actions.ProtocolStepsH, - Actions.AddStep, - Verifications.TransferPopOut, + SetupActions.SelectLiquidWells, + SetupActions.SetVolumeAndSaveforWells, + SetupActions.ChoseDeckSlotC3, + SetupActions.AddHardwareLabware, + SetupActions.ClickLabwareHeader, + SetupActions.ClickWellPlatesSection, + SetupActions.SelectBioRad96WellPlate, + SetupActions.ProtocolStepsH, + SetupActions.AddStep, + SetupVerifications.TransferPopOut, UniversalActions.Snapshot, ] - runCreateTest(steps) + runSteps(steps) }) }) diff --git a/protocol-designer/cypress/support/createNew.ts b/protocol-designer/cypress/support/SetupSteps.ts similarity index 51% rename from protocol-designer/cypress/support/createNew.ts rename to protocol-designer/cypress/support/SetupSteps.ts index 129edc636c8..13dfa4bb998 100644 --- a/protocol-designer/cypress/support/createNew.ts +++ b/protocol-designer/cypress/support/SetupSteps.ts @@ -1,13 +1,3 @@ -import { executeUniversalAction, UniversalActions } from './universalActions' -import { isEnumValue } from './utils' -import '../support/commands' -// ToDo Future planning should have Step 5, Step 6, and 7 verification -// Todo ProtocolOverview page. This might change from deck map revamp, -// so let's hold off until then. -// This PR unblocks Sara and I to work on this separately, so I want -// To prioritize its getting pulled into the repo -// Some day we should make a way to input variables into actions - import 'cypress-file-upload' declare global { // eslint-disable-next-line @typescript-eslint/no-namespace @@ -17,7 +7,7 @@ declare global { } } } -export enum Actions { +export enum SetupActions { SelectFlex = 'Select Opentrons Flex', SelectOT2 = 'Select Opentrons OT-2', Confirm = 'Confirm', @@ -45,7 +35,9 @@ export enum Actions { ChoseDeckSlotD2 = 'Choose deck slot D2', ChoseDeckSlotD3 = 'Choose deck slot D3', AddHardwareLabware = 'Adds labware to deck slot by chose deck slot', + EditHardwareLabwareOnDeck = 'Edits existing labware/harddware on deck slot', ClickLabwareHeader = 'Click Labware', + AddAdapters = 'Add an adapter to a module after selecting labware header', ClickWellPlatesSection = 'Click Well plates', SelectArmadillo96WellPlate = 'Select Armadillo 96 Well Plate', SelectBioRad96WellPlate = 'Select Bio-Rad 96 Well Plate', @@ -59,9 +51,12 @@ export enum Actions { SetVolumeAndSaveforWells = 'Set volume and save for wells', ProtocolStepsH = 'Select Protocol Steps Header', AddStep = 'Use after making sure you are on ProtocolStepsH or have already made a step', + DeepWellTempModAdapter = 'Select Opentrons 96 Deep Well Temperature Module Adapter', + AddNest96DeepWellPlate = 'Adds Nest 96 Deep Well Plate', + Done = 'Select Done on a step form', } -export enum Verifications { +export enum SetupVerifications { OnStep1 = 'On Step 1 page.', OnStep2 = 'On Step 2 page.', OnStep3 = 'on Step 3 page', @@ -76,10 +71,12 @@ export enum Verifications { HeaterShakerImg = 'Heater-Shaker Module GEN1', Tempdeck2Img = 'Temperature Module GEN2', MagBlockImg = 'Magnetic Block GEN1', - LiquidPage = 'Liquid page content is visible', + LiquidPage = 'Liquid page Content is visible', TransferPopOut = 'Verify Step 1 of the transfer function is present', + TempeDeckInitialForm = 'Verify that the tempdseteck stepform opens correctly', + Temp4CPauseTextVerification = 'Verify that the pause step has the right information in step preview', } -export enum Content { +export enum SetupContent { Step1Title = 'Step 1', Step2Title = 'Step 2', Step3Title = 'Step3', @@ -114,6 +111,7 @@ export enum Content { EditProtocol = 'Edit protocol', EditSlot = 'Edit slot', AddLabwareToDeck = 'Add hardware/labware', + EditHardwareLabwareOnDeck = 'Edit hardware/labware', LabwareH = 'Labware', WellPlatesCat = 'Well plates', Armadillo96WellPlate200uL = 'Armadillo 96 Well Plate 200 µL PCR Full Skirt', @@ -124,9 +122,11 @@ export enum Content { SampleLiquidName = 'My liquid!', ProtocolSteps = 'Protocol steps', AddStep = 'Add Step', + NestDeepWell = 'NEST 96 Deep Well Plate 2mL', + Save = 'Save', } -export enum Locators { +export enum SetupLocators { Confirm = 'button:contains("Confirm")', GoBack = 'button:contains("Go back")', Step1Indicator = 'p:contains("Step 1")', @@ -143,6 +143,10 @@ export enum Locators { SaveButton = 'button[type="submit"]', LiquidsDropdown = 'div[tabindex="0"].sc-bqWxrE', // Add new locator for the dropdown LabwareSelectionLocation = '[data-testid="Toolbox_confirmButton"]', + DoneButtonLabwareSelection = '[data-testid="Toolbox_confirmButton"]', + Div = 'div', + Button = 'button', + TempdeckTempInput = 'input[name="targetTemperature"]', } const chooseDeckSlot = ( @@ -163,18 +167,29 @@ const chooseDeckSlot = ( | 'D3', () => Cypress.Chainable> > = { - A1: () => cy.contains('foreignObject[x="0"][y="321"]', Content.EditSlot), - A2: () => cy.contains('foreignObject[x="164"][y="321"]', Content.EditSlot), - A3: () => cy.contains('foreignObject[x="328"][y="321"]', Content.EditSlot), - B1: () => cy.contains('foreignObject[x="0"][y="214"]', Content.EditSlot), - B2: () => cy.contains('foreignObject[x="164"][y="214"]', Content.EditSlot), - B3: () => cy.contains('foreignObject[x="328"][y="214"]', Content.EditSlot), - C1: () => cy.contains('foreignObject[x="0"][y="107"]', Content.EditSlot), - C2: () => cy.contains('foreignObject[x="164"][y="107"]', Content.EditSlot), - C3: () => cy.contains('foreignObject[x="328"][y="107"]', Content.EditSlot), - D1: () => cy.contains('foreignObject[x="0"][y="0"]', Content.EditSlot), - D2: () => cy.contains('foreignObject[x="164"][y="0"]', Content.EditSlot), - D3: () => cy.contains('foreignObject[x="328"][y="0"]', Content.EditSlot), + A1: () => + cy.contains('foreignObject[x="0"][y="321"]', SetupContent.EditSlot), + A2: () => + cy.contains('foreignObject[x="164"][y="321"]', SetupContent.EditSlot), + A3: () => + cy.contains('foreignObject[x="328"][y="321"]', SetupContent.EditSlot), + B1: () => + cy.contains('foreignObject[x="0"][y="214"]', SetupContent.EditSlot), + B2: () => + cy.contains('foreignObject[x="164"][y="214"]', SetupContent.EditSlot), + B3: () => + cy.contains('foreignObject[x="328"][y="214"]', SetupContent.EditSlot), + C1: () => + cy.contains('foreignObject[x="0"][y="107"]', SetupContent.EditSlot), + C2: () => + cy.contains('foreignObject[x="164"][y="107"]', SetupContent.EditSlot), + C3: () => + cy.contains('foreignObject[x="328"][y="107"]', SetupContent.EditSlot), + D1: () => cy.contains('foreignObject[x="0"][y="0"]', SetupContent.EditSlot), + D2: () => + cy.contains('foreignObject[x="164"][y="0"]', SetupContent.EditSlot), + D3: () => + cy.contains('foreignObject[x="328"][y="0"]', SetupContent.EditSlot), } const slotAction = deckSlots[slot as keyof typeof deckSlots] @@ -219,250 +234,265 @@ const selectWells = (wells: string[]): void => { // Example usage // selectWells(['A1', 'B3', 'H12']) -const executeAction = (action: Actions | UniversalActions): void => { - if (isEnumValue([UniversalActions], [action])) { - executeUniversalAction(action as UniversalActions) - return - } - +export const executeSetupSteps = (action: SetupActions): void => { switch (action) { - case Actions.SelectFlex: - cy.contains(Content.OpentronsFlex).should('be.visible').click() + case SetupActions.SelectFlex: + cy.contains(SetupContent.OpentronsFlex).should('be.visible').click() break - case Actions.SelectOT2: - cy.contains(Content.OpentronsOT2).should('be.visible').click() + case SetupActions.SelectOT2: + cy.contains(SetupContent.OpentronsOT2).should('be.visible').click() break - case Actions.Confirm: - cy.contains(Content.Confirm).should('be.visible').click() + case SetupActions.Confirm: + cy.contains(SetupContent.Confirm).should('be.visible').click() break - case Actions.GoBack: - cy.contains(Content.GoBack).should('be.visible').click() + case SetupActions.GoBack: + cy.contains(SetupContent.GoBack).should('be.visible').click() break - case Actions.SingleChannelPipette50: - cy.contains('label', Content.SingleChannel) + case SetupActions.SingleChannelPipette50: + cy.contains('label', SetupContent.SingleChannel) .should('exist') .and('be.visible') .click() - cy.contains(Content.Volume50).click() - cy.contains(Content.Tiprack50).click() + cy.contains(SetupContent.Volume50).click() + cy.contains(SetupContent.Tiprack50).click() // ToDo after PR, why does this click Tiprack50 again // instead of clicking the filter tiprack? - // cy.contains(Content.FilterTiprack50).click() + // cy.contains(SetupContent.FilterTiprack50).click() break - case Actions.AddThermocycler: - cy.contains(Content.Thermocycler).click() + case SetupActions.AddThermocycler: + cy.contains(SetupContent.Thermocycler).click() break - case Actions.AddHeaterShaker: - cy.contains(Content.HeaterShaker).click() + case SetupActions.AddHeaterShaker: + cy.contains(SetupContent.HeaterShaker).click() break - case Actions.AddTempdeck2: - cy.contains(Content.Tempdeck2).click() + case SetupActions.AddTempdeck2: + cy.contains(SetupContent.Tempdeck2).click() break - case Actions.AddMagBlock: - cy.contains(Content.MagBlock).click() + case SetupActions.AddMagBlock: + cy.contains(SetupContent.MagBlock).click() break - case Actions.YesGripper: - cy.contains(Content.Yes).click() + case SetupActions.YesGripper: + cy.contains(SetupContent.Yes).click() break - case Actions.NoGripper: - cy.contains(Content.No).click() + case SetupActions.NoGripper: + cy.contains(SetupContent.No).click() break - case Actions.EditProtocolA: - cy.contains(Content.EditProtocol).click() + case SetupActions.EditProtocolA: + cy.contains(SetupContent.EditProtocol).click() break - case Actions.ChoseDeckSlotA1: + case SetupActions.ChoseDeckSlotA1: chooseDeckSlot('A1').click() break - case Actions.ChoseDeckSlotA2: + case SetupActions.ChoseDeckSlotA2: chooseDeckSlot('A2').click() break - case Actions.ChoseDeckSlotA3: + case SetupActions.ChoseDeckSlotA3: chooseDeckSlot('A3').click() break - case Actions.ChoseDeckSlotB1: + case SetupActions.ChoseDeckSlotB1: chooseDeckSlot('B1').click() break - case Actions.ChoseDeckSlotB2: + case SetupActions.ChoseDeckSlotB2: chooseDeckSlot('B2').click() break - case Actions.ChoseDeckSlotB3: + case SetupActions.ChoseDeckSlotB3: chooseDeckSlot('B3').click() break - case Actions.ChoseDeckSlotC1: + case SetupActions.ChoseDeckSlotC1: chooseDeckSlot('C1').click() break - case Actions.ChoseDeckSlotC2: + case SetupActions.ChoseDeckSlotC2: chooseDeckSlot('C2').click() break - case Actions.ChoseDeckSlotC3: + case SetupActions.ChoseDeckSlotC3: chooseDeckSlot('C3').click() break - case Actions.ChoseDeckSlotD1: + case SetupActions.ChoseDeckSlotD1: chooseDeckSlot('D1').click() break - case Actions.ChoseDeckSlotD2: + case SetupActions.ChoseDeckSlotD2: chooseDeckSlot('D2').click() break - case Actions.ChoseDeckSlotD3: + case SetupActions.ChoseDeckSlotD3: chooseDeckSlot('D3').click() break - case Actions.AddHardwareLabware: // New case - cy.contains(Content.AddLabwareToDeck).click() + case SetupActions.AddHardwareLabware: + cy.contains(SetupContent.AddLabwareToDeck).click() break - case Actions.ClickLabwareHeader: // New case - cy.contains(Content.LabwareH).click() + case SetupActions.EditHardwareLabwareOnDeck: + cy.contains(SetupContent.EditHardwareLabwareOnDeck).click() break - case Actions.ClickWellPlatesSection: // New case - cy.contains(Content.WellPlatesCat).click() + case SetupActions.ClickLabwareHeader: + cy.contains(SetupContent.LabwareH).click() break - case Actions.ChoseDeckSlotC2Labware: + case SetupActions.ClickWellPlatesSection: + cy.contains(SetupContent.WellPlatesCat).click() + break + case SetupActions.ChoseDeckSlotC2Labware: // Todo Investigate making a dictionary of slot editing. // Maybe next PR chooseDeckSlot('C2') .find('.Box-sc-8ozbhb-0.kIDovv') .find('a[role="button"]') - .contains(Content.EditSlot) + .contains(SetupContent.EditSlot) .click({ force: true }) break - case Actions.SelectArmadillo96WellPlate: // New case for selecting Armadillo plate - cy.contains(Content.Armadillo96WellPlate200uL).click({ force: true }) - cy.get(Locators.LabwareSelectionLocation).click({ force: true }) + case SetupActions.SelectArmadillo96WellPlate: // New case for selecting Armadillo plate + cy.contains(SetupContent.Armadillo96WellPlate200uL).click({ force: true }) + cy.get(SetupLocators.LabwareSelectionLocation).click({ force: true }) break - case Actions.SelectBioRad96WellPlate: // New case for selecting Armadillo plate - cy.contains(Content.Biorad96WellPlate200uL).click({ force: true }) - cy.get(Locators.LabwareSelectionLocation).click({ force: true }) + case SetupActions.SelectBioRad96WellPlate: // New case for selecting Armadillo plate + cy.contains(SetupContent.Biorad96WellPlate200uL).click({ force: true }) + cy.get(SetupLocators.LabwareSelectionLocation).click({ force: true }) break - case Actions.AddLiquid: // New case for "Add liquid" - cy.contains('button', Content.AddLiquid).click() + case SetupActions.AddLiquid: // New case for "Add liquid" + cy.contains('button', SetupContent.AddLiquid).click() break - case Actions.ClickLiquidButton: // New case for "Liquid button" - cy.contains('button', Content.LiquidButton).click() + case SetupActions.ClickLiquidButton: // New case for "Liquid button" + cy.contains('button', SetupContent.LiquidButton).click() break - case Actions.DefineLiquid: // New case for "Define a liquid" - cy.contains('button', Content.DefineALiquid).click() + case SetupActions.DefineLiquid: // New case for "Define a liquid" + cy.contains('button', SetupContent.DefineALiquid).click() break - case Actions.LiquidSaveWIP: - cy.get(Locators.LiquidNameInput) // Locate the input with name="name" - .type(Content.SampleLiquidName) + case SetupActions.LiquidSaveWIP: + cy.get(SetupLocators.LiquidNameInput) // Locate the input with name="name" + .type(SetupContent.SampleLiquidName) - cy.get(Locators.ModalShellArea) + cy.get(SetupLocators.ModalShellArea) .find('form') // Target the form inside the modal .invoke('submit', (e: SubmitEvent) => { e.preventDefault() // Prevent default form submission }) - cy.get(Locators.ModalShellArea) - .find(Locators.SaveButton) // Locate the Save button - .contains('Save') + cy.get(SetupLocators.ModalShellArea) + .find(SetupLocators.SaveButton) // Locate the Save button + .contains(SetupContent.Save) .click({ force: true }) // Trigger the Save button break - case Actions.WellSelector: + case SetupActions.WellSelector: selectWells(['A1', 'A2']) break - case Actions.LiquidDropdown: // New case for dropdown - cy.get(Locators.LiquidsDropdown) + case SetupActions.LiquidDropdown: // New case for dropdown + cy.get(SetupLocators.LiquidsDropdown) .should('be.visible') // Ensure the dropdown is visible .click() // Click the dropdown break - case Actions.SelectLiquidWells: + case SetupActions.SelectLiquidWells: cy.contains('My liquid!').click() // Action for clicking 'My liquid!' break - case Actions.SetVolumeAndSaveforWells: - cy.get('input[name="volume"]').type(`150`) // Set volume - cy.contains('button', 'Save').click() // Click Save button + case SetupActions.SetVolumeAndSaveforWells: + cy.get('input[name="volume"]').type(`150`, { force: true }) // Set volume + cy.contains('button', SetupContent.Save).click() // Click Save button cy.contains('button', 'Done').click({ force: true }) // Click Done button, forcing click if necessary break - case Actions.ProtocolStepsH: - cy.contains('button', Content.ProtocolSteps).click() + case SetupActions.ProtocolStepsH: + cy.contains('button', SetupContent.ProtocolSteps).click() + break + case SetupActions.AddStep: + cy.contains('button', SetupContent.AddStep).click({ force: true }) + break + case SetupActions.AddAdapters: + cy.contains('Adapters').click() break - case Actions.AddStep: - cy.contains('button', Content.AddStep).click() + case SetupActions.DeepWellTempModAdapter: + cy.contains('Opentrons 96 Deep Well Temperature Module Adapter').click() break + case SetupActions.AddNest96DeepWellPlate: + cy.contains(SetupContent.NestDeepWell).click() + break + case SetupActions.Done: + cy.get(SetupLocators.DoneButtonLabwareSelection) + .contains('Done') + .click({ force: true }) + break + default: throw new Error(`Unrecognized action: ${action as string}`) } } -const verifyStep = (verification: Verifications): void => { +export const executeVerificationStep = ( + verification: SetupVerifications +): void => { switch (verification) { - case Verifications.OnStep1: - cy.contains(Content.Step1Title).should('be.visible') + case SetupVerifications.OnStep1: + cy.contains(SetupContent.Step1Title).should('be.visible') break - case Verifications.OnStep2: - cy.contains(Content.Step2Title).should('be.visible') - cy.contains(Content.AddPipette).should('be.visible') + case SetupVerifications.OnStep2: + cy.contains(SetupContent.Step2Title).should('be.visible') + cy.contains(SetupContent.AddPipette).should('be.visible') break - case Verifications.FlexSelected: - cy.contains(Content.OpentronsFlex).should( + case SetupVerifications.FlexSelected: + cy.contains(SetupContent.OpentronsFlex).should( 'have.css', 'background-color', 'rgb(0, 108, 250)' ) break - case Verifications.OT2Selected: - cy.contains(Content.OpentronsOT2).should( + case SetupVerifications.OT2Selected: + cy.contains(SetupContent.OpentronsOT2).should( 'have.css', 'background-color', 'rgb(0, 108, 250)' ) break - case Verifications.NinetySixChannel: - cy.contains(Content.NinetySixChannel).should('be.visible') + case SetupVerifications.NinetySixChannel: + cy.contains(SetupContent.NinetySixChannel).should('be.visible') break - case Verifications.NotNinetySixChannel: - cy.contains(Content.NinetySixChannel).should('not.exist') + case SetupVerifications.NotNinetySixChannel: + cy.contains(SetupContent.NinetySixChannel).should('not.exist') break - case Verifications.StepTwo50uL: + case SetupVerifications.StepTwo50uL: // This function should get used after you select 50uL fully - cy.contains(Content.PipetteVolume) - cy.contains(Content.Volume50).should('be.visible') - cy.contains(Content.Volume1000).should('be.visible') - cy.contains(Content.Tiprack50).should('be.visible') - cy.contains(Content.FilterTiprack50).should('be.visible') + cy.contains(SetupContent.PipetteVolume) + cy.contains(SetupContent.Volume50).should('be.visible') + cy.contains(SetupContent.Volume1000).should('be.visible') + cy.contains(SetupContent.Tiprack50).should('be.visible') + cy.contains(SetupContent.FilterTiprack50).should('be.visible') break - case Verifications.StepTwoPart3: + case SetupVerifications.StepTwoPart3: // This function should get used after you select 50uL fully - cy.contains(Content.FullP50SingleName).should('be.visible') - cy.contains(Content.FullP50TiprackName).should('be.visible') + cy.contains(SetupContent.FullP50SingleName).should('be.visible') + cy.contains(SetupContent.FullP50TiprackName).should('be.visible') cy.contains('Left Mount').should('be.visible') - cy.contains(Content.Step2Title) + cy.contains(SetupContent.Step2Title) cy.contains('Robot pipettes') - cy.contains(Content.AddPipette) + cy.contains(SetupContent.AddPipette) break - case Verifications.OnStep3: + case SetupVerifications.OnStep3: cy.contains('Add a gripper').should('be.visible') cy.contains( 'Do you want to move labware automatically with the gripper?' ).should('be.visible') - cy.contains(Content.Yes).should('be.visible') - cy.contains(Content.No).should('be.visible') + cy.contains(SetupContent.Yes).should('be.visible') + cy.contains(SetupContent.No).should('be.visible') break - case Verifications.Step4Verification: - cy.contains(Content.ModulePageH).should('be.visible') - cy.contains(Content.ModulePageB).should('be.visible') - cy.contains(Content.Thermocycler).should('be.visible') - cy.contains(Content.HeaterShaker).should('be.visible') - cy.contains(Content.MagBlock).should('be.visible') - cy.contains(Content.Tempdeck2).should('be.visible') + case SetupVerifications.Step4Verification: + cy.contains(SetupContent.ModulePageH).should('be.visible') + cy.contains(SetupContent.ModulePageB).should('be.visible') + cy.contains(SetupContent.Thermocycler).should('be.visible') + cy.contains(SetupContent.HeaterShaker).should('be.visible') + cy.contains(SetupContent.MagBlock).should('be.visible') + cy.contains(SetupContent.Tempdeck2).should('be.visible') break - case Verifications.ThermocyclerImg: - cy.get(Locators.TemperatureModuleImage).should('be.visible') + case SetupVerifications.ThermocyclerImg: + cy.get(SetupLocators.TemperatureModuleImage).should('be.visible') break - case Verifications.HeaterShakerImg: - cy.get(Locators.HeaterShakerImage).should('be.visible') + case SetupVerifications.HeaterShakerImg: + cy.get(SetupLocators.HeaterShakerImage).should('be.visible') break - case Verifications.Tempdeck2Img: - cy.contains(Content.Tempdeck2).should('be.visible') + case SetupVerifications.Tempdeck2Img: + cy.contains(SetupContent.Tempdeck2).should('be.visible') break - case Verifications.LiquidPage: + case SetupVerifications.LiquidPage: cy.contains('Liquid').should('be.visible') cy.contains('Add liquid').should('be.visible') cy.contains('Liquid volume by well').should('be.visible') cy.contains('Cancel').should('be.visible') break - case Verifications.TransferPopOut: + case SetupVerifications.TransferPopOut: cy.contains('button', 'Transfer').should('be.visible').click() cy.contains('Source labware') cy.contains('Select source wells') @@ -472,40 +502,15 @@ const verifyStep = (verification: Verifications): void => { cy.contains('Tip handling') cy.contains('Tip drop location') break - - default: - throw new Error( - `Unrecognized verification: ${verification as Verifications}` - ) } } -export const runCreateTest = ( - steps: Array -): void => { - const enumsToCheck = [Actions, Verifications, UniversalActions] - - if (!isEnumValue(enumsToCheck, steps)) { - throw new Error('One or more steps are unrecognized.') - } - - steps.forEach(step => { - if (isEnumValue([Actions], step)) { - executeAction(step as Actions) - } else if (isEnumValue([Verifications], step)) { - verifyStep(step as Verifications) - } else if (isEnumValue([UniversalActions], step)) { - executeAction(step as UniversalActions) - } - }) -} - export const verifyCreateProtocolPage = (): void => { - // Verify step 1 and page content - cy.contains(Content.Step1Title).should('exist').should('be.visible') - cy.contains(Content.LetsGetStarted).should('exist').should('be.visible') - cy.contains(Content.WhatKindOfRobot).should('exist').should('be.visible') - cy.contains(Content.OpentronsFlex).should('exist').should('be.visible') - cy.contains(Content.OpentronsOT2).should('exist').should('be.visible') - cy.contains(Content.Confirm).should('exist').should('be.visible') + // Verify step 1 and page SetupContent + cy.contains(SetupContent.Step1Title).should('exist').should('be.visible') + cy.contains(SetupContent.LetsGetStarted).should('exist').should('be.visible') + cy.contains(SetupContent.WhatKindOfRobot).should('exist').should('be.visible') + cy.contains(SetupContent.OpentronsFlex).should('exist').should('be.visible') + cy.contains(SetupContent.OpentronsOT2).should('exist').should('be.visible') + cy.contains(SetupContent.Confirm).should('exist').should('be.visible') } diff --git a/protocol-designer/cypress/support/StepExecution.ts b/protocol-designer/cypress/support/StepExecution.ts new file mode 100644 index 00000000000..8806fd6f0b7 --- /dev/null +++ b/protocol-designer/cypress/support/StepExecution.ts @@ -0,0 +1,51 @@ +import { executeUniversalAction, UniversalActions } from './universalActions' +import { isEnumValue } from './utils' +import '../support/commands' +import { + SetupActions, + SetupVerifications, + executeVerificationStep, + executeSetupSteps, +} from './SetupSteps' +import { + ModActions, + ModVerifications, + executeModSteps, + executeVerifyModStep, +} from './SupportModules' + +export type StepsList = Array< + | SetupActions + | SetupVerifications + | UniversalActions + | ModActions + | ModVerifications +> + +export const runSteps = (steps: StepsList): void => { + const enumsToCheck = [ + SetupActions, + ModActions, + ModVerifications, + SetupVerifications, + UniversalActions, + ] + + if (!isEnumValue(enumsToCheck, steps)) { + throw new Error('One or more steps are unrecognized.') + } + + steps.forEach(step => { + if (isEnumValue([SetupActions], step)) { + executeSetupSteps(step as SetupActions) + } else if (isEnumValue([SetupVerifications], step)) { + executeVerificationStep(step as SetupVerifications) + } else if (isEnumValue([UniversalActions], step)) { + executeUniversalAction(step as UniversalActions) + } else if (isEnumValue([ModActions], step)) { + executeModSteps(step as ModActions) + } else if (isEnumValue([ModVerifications], step)) { + executeVerifyModStep(step as ModVerifications) + } + }) +} diff --git a/protocol-designer/cypress/support/SupportModules.ts b/protocol-designer/cypress/support/SupportModules.ts new file mode 100644 index 00000000000..427b608d9ab --- /dev/null +++ b/protocol-designer/cypress/support/SupportModules.ts @@ -0,0 +1,98 @@ +import 'cypress-file-upload' +declare global { + // eslint-disable-next-line @typescript-eslint/no-namespace + namespace Cypress { + interface Chainable { + chooseDeckSlot: (slot: string) => Cypress.Chainable + } + } +} +export enum ModActions { + Done = 'Select Done on a step form', + AddTemperatureStep = 'Selects Temperature Module step', + ActivateTempdeck = 'Activates Temperature Module when you first use it', + InputTempDeck4 = 'Inputs 4C into tempdeck', + InputTempDeck95 = 'Inputs 96C into tempdeck', + InputTempDeck100 = 'Inputs 100C into tempdeck. Expect an error then exit', + ExitTempdeckCommand = 'Exits a tempdeck command', + PauseAfterSettingTempdeck = 'Allows you to puase protocol until reached', + SaveButtonTempdeck = 'Saves a temperature set', +} + +export enum ModVerifications { + TempeDeckInitialForm = 'Verify that the tempdeck stepform opens correctly', + Temp4CPauseTextVerification = 'Verify that the pause step has the right information in step preview', +} +export enum ModContent { + ModState = 'Module state', + DecativeTempDeck = 'Deactivate', + Temperature = 'Temperature', + Save = 'Save', + Temp4CVerification = `Build a pause step to wait until Temperature Module GEN2 reaches 4˚C`, +} + +export enum ModLocators { + DoneButtonLabwareSelection = '[data-testid="Toolbox_confirmButton"]', + Div = 'div', + Button = 'button', + TempdeckTempInput = 'input[name="targetTemperature"]', +} + +export const executeModSteps = (action: ModActions): void => { + switch (action) { + case ModActions.Done: + cy.get(ModLocators.DoneButtonLabwareSelection) + .contains('Done') + .click({ force: true }) + break + case ModActions.AddTemperatureStep: + cy.contains('button', 'Temperature').click() + break + case ModActions.ActivateTempdeck: + cy.contains(ModContent.DecativeTempDeck) + .closest(ModLocators.Div) + .find(ModLocators.Button) + .click() + break + case ModActions.InputTempDeck4: + cy.get(ModLocators.TempdeckTempInput).type('4') + break + case ModActions.InputTempDeck95: + cy.get(ModLocators.TempdeckTempInput).type('95') + break + case ModActions.InputTempDeck100: + cy.get(ModLocators.TempdeckTempInput).type('100') + break + case ModActions.ExitTempdeckCommand: + break + case ModActions.PauseAfterSettingTempdeck: + cy.contains(ModLocators.Button, 'Pause protocol') + .should('exist') + .and('be.visible') + .click() + break + case ModActions.SaveButtonTempdeck: + cy.contains(ModContent.Save).click() + break + default: + throw new Error(`Unrecognized action: ${action as string}`) + } +} + +export const executeVerifyModStep = (verification: ModVerifications): void => { + switch (verification) { + case ModVerifications.TempeDeckInitialForm: + cy.contains(ModContent.ModState) + cy.contains(ModContent.DecativeTempDeck) + cy.contains(ModContent.Temperature) + break + case ModVerifications.Temp4CPauseTextVerification: + // This takes place + cy.contains('div', 'Pausing until') + .should('contain', 'Temperature Module GEN2') + .and('contain', 'reaches') + .find('[data-testid="Tag_default"]') + .should('contain', '4°C') + break + } +}