|
1 | 1 | """Tests covering the Mix step workflow in Protocol Designer.""" |
2 | 2 |
|
3 | 3 | import pytest |
4 | | -from playwright.sync_api import Page, expect |
| 4 | +from playwright.sync_api import Page |
5 | 5 |
|
6 | | -from automation.pd_pages import LandingPage, MixStepForm, ProtocolEditorPage |
| 6 | +from automation.pd_pages import MixStepForm, ProtocolEditorPage |
| 7 | +from eyes import Eyes |
| 8 | +from utility import import_protocol_and_open_editor |
7 | 9 |
|
8 | 10 | PROTOCOL_PATH = "fixtures/protocol/8/doItAllV8.json" |
9 | 11 | LABWARE_OPTION = "B4 Opentrons Tough 96 Well Plate 200 µL PCR Full Skirt" |
|
12 | 14 |
|
13 | 15 |
|
14 | 16 | @pytest.mark.pdE2E |
15 | | -def test_import_protocol_and_enter_edit_mode(page: Page, pd_base_url: str) -> None: |
16 | | - """Verify we can import a protocol and reach the editor.""" |
17 | | - |
18 | | - _import_protocol_and_open_editor(page) |
19 | | - expect(page.get_by_role("button", name="Add Step")).to_be_visible() |
20 | | - |
21 | | - |
22 | | -@pytest.mark.pdE2E |
23 | | -def test_mix_step_configuration_workflow(page: Page, pd_base_url: str) -> None: |
| 17 | +def test_mix_step_configuration_workflow(page: Page, eyes: Eyes | None) -> None: |
24 | 18 | """Replicate the complete mixSettings Cypress test using Playwright.""" |
25 | 19 |
|
26 | | - editor = _import_protocol_and_open_editor(page) |
27 | | - |
28 | | - # Step menu parity checks |
29 | | - editor.open_add_step_menu() |
30 | | - editor.verify_add_step_menu_options() |
31 | | - editor.select_step_type("Mix") |
| 20 | + import_protocol_and_open_editor(page, PROTOCOL_PATH, migration=True) |
| 21 | + protocol_editor = ProtocolEditorPage(page) |
32 | 22 |
|
33 | 23 | mix_form = MixStepForm(page) |
| 24 | + protocol_editor.add_step("Mix") |
34 | 25 | # currently there is a divergence in the number of parts in the mix step form |
35 | 26 | # between 8.6.2 prod (1/3) and what's in edge (1/4) |
36 | | - # mix_form.expect_part_header("Part 1 / 3") |
37 | | - for label in [ |
38 | | - "Mix", |
39 | | - "Pipette", |
40 | | - "Tiprack", |
41 | | - "Labware", |
42 | | - ]: |
43 | | - print(f"Expecting text: {label}") |
44 | | - mix_form.expect_text(label) |
45 | 27 |
|
46 | 28 | mix_form.select_pipette(PIPETTE_OPTION) |
47 | 29 | mix_form.select_tiprack(TIPRACK_OPTION) |
48 | 30 | mix_form.select_labware(LABWARE_OPTION) |
49 | 31 |
|
50 | | - for label in [ |
51 | | - "Pipette nozzles and wells", |
52 | | - "Mix volume", |
53 | | - "Mix repetitions", |
54 | | - ]: |
55 | | - mix_form.expect_text(label) |
56 | | - |
57 | 32 | mix_form.open_nozzle_and_well_selector() |
| 33 | + if eyes is not None: |
| 34 | + eyes.check( |
| 35 | + checkpoint_name="Nozzle Selector Layout", |
| 36 | + target=eyes.Target.window().fully(), |
| 37 | + ) |
| 38 | + |
58 | 39 | mix_form.select_nozzles() |
59 | 40 | mix_form.expect_well_modal() |
| 41 | + if eyes is not None: |
| 42 | + eyes.check( |
| 43 | + checkpoint_name="Well Modal Layout", |
| 44 | + target=eyes.Target.window().fully(), |
| 45 | + ) |
| 46 | + |
60 | 47 | mix_form.select_wells(["A1", "A2"]) |
61 | 48 | mix_form.enter_volume("100") |
62 | 49 | mix_form.enter_mix_repetitions("5") |
| 50 | + if eyes is not None: |
| 51 | + eyes.check( |
| 52 | + checkpoint_name="Mix Step Settings Form - Part 1", |
| 53 | + target=eyes.Target.window().fully(), |
| 54 | + ) |
63 | 55 | mix_form.click_continue() |
64 | 56 |
|
65 | 57 | # Part 2 / 4 – liquid class settings |
66 | | - mix_form.expect_part_header(("Part 2 / 4", "Part 2 / 3")) |
67 | | - mix_form.expect_text("Apply liquid class settings for this mix") |
68 | 58 | mix_form.click_continue() |
69 | | - mix_form.expect_part_header(("Part 3 / 4", "Part 2 / 3")) |
| 59 | + if eyes is not None: |
| 60 | + eyes.check( |
| 61 | + checkpoint_name="Mix Settings Form Liquid Class Modal - Part 2", |
| 62 | + target=eyes.Target.window().fully(), |
| 63 | + ) |
70 | 64 | mix_form.open_mix_tip_modal() |
71 | | - mix_form.expect_text("Side view") |
| 65 | + if eyes is not None: |
| 66 | + eyes.check( |
| 67 | + checkpoint_name="Mix Tip Position Modal", |
| 68 | + target=eyes.Target.window().fully(), |
| 69 | + ) |
72 | 70 | page.get_by_role("button", name="Swap view").click() |
73 | | - mix_form.expect_text("Top view") |
74 | 71 | mix_form.set_mix_tip_position("2", "2", "4") |
| 72 | + if eyes is not None: |
| 73 | + eyes.check( |
| 74 | + checkpoint_name="Mix Tip Position Modal with Values", |
| 75 | + target=eyes.Target.window().fully(), |
| 76 | + ) |
75 | 77 | mix_form.reset_settings() |
76 | 78 | mix_form.set_mix_tip_position("2", "2", "5") |
77 | 79 | mix_form.save_modal() |
78 | 80 |
|
79 | 81 | mix_form.toggle_checkbox() |
80 | 82 | mix_form.fill_delay_seconds("5") |
| 83 | + if eyes is not None: |
| 84 | + eyes.check( |
| 85 | + checkpoint_name="Mix Step Form Aspirate Settings - Part 3", |
| 86 | + target=eyes.Target.window().fully(), |
| 87 | + ) |
81 | 88 |
|
82 | 89 | # Part 3 / 4 – dispense configuration |
83 | 90 | mix_form.click_dispense_tab() |
84 | 91 | mix_form.set_flow_rate("dispense_flowRate", "300") |
85 | 92 | mix_form.toggle_checkbox() |
86 | 93 | mix_form.fill_delay_seconds("5") |
87 | | - |
88 | 94 | mix_form.toggle_checkbox() |
89 | 95 | mix_form.set_push_out_volume("5") |
90 | | - |
91 | 96 | mix_form.toggle_checkbox() |
92 | 97 | mix_form.open_blowout_location_dropdown() |
93 | | - mix_form.expect_text("Destination well") |
94 | 98 | page.get_by_text("Destination well").click() |
95 | 99 | mix_form.set_flow_rate("blowout_flowRate", "300") |
96 | 100 | mix_form.open_blowout_position_modal() |
97 | 101 | mix_form.set_blowout_position("4") |
| 102 | + if eyes is not None: |
| 103 | + eyes.check( |
| 104 | + checkpoint_name="Mix Step Form Blowout Settings - Part 3", |
| 105 | + target=eyes.Target.window().fully(), |
| 106 | + ) |
98 | 107 | mix_form.reset_settings() |
99 | 108 | mix_form.set_blowout_position("-3") |
100 | 109 | mix_form.save_modal() |
101 | | - mix_form.expect_text("Blowout position from top") |
| 110 | + if eyes is not None: |
| 111 | + eyes.check( |
| 112 | + checkpoint_name="Mix Step Form Dispense Settings - Part 3 cont..", |
| 113 | + target=eyes.Target.window().fully(), |
| 114 | + ) |
102 | 115 |
|
103 | 116 | mix_form.click_continue() |
104 | 117 |
|
105 | 118 | # Part 4 / 4 – tip handling and rename |
106 | | - mix_form.expect_part_header(("Part 4 / 4", "Part 3 / 3")) |
107 | | - mix_form.expect_text("Tip management") |
108 | 119 | mix_form.expect_tip_handling_options(["Always", "Once", "Per source", "Per destination", "Never"]) |
109 | 120 | mix_form.select_tip_handling_option("Once") |
110 | | - |
111 | 121 | mix_form.rename_step("Cypress Mix Test", "This is testing cypress automation in PD") |
112 | 122 | mix_form.save_step() |
113 | | - |
114 | | - expect(page.get_by_text("Cypress Mix Test").first).to_be_visible() |
115 | | - |
116 | | - |
117 | | -def _import_protocol_and_open_editor(page: Page) -> ProtocolEditorPage: |
118 | | - """Shared setup helper used by both tests.""" |
119 | | - |
120 | | - landing = LandingPage(page) |
121 | | - landing.wait_for_page_load() |
122 | | - landing.confirm_welcome_modal() |
123 | | - landing.click_import_existing_protocol() |
124 | | - landing.upload_protocol_file(PROTOCOL_PATH) |
125 | | - |
126 | | - expect(page.get_by_text("Protocol Metadata")).to_be_visible(timeout=10000) |
127 | | - _dismiss_migration_modal(page) |
128 | | - |
129 | | - page.get_by_role("button", name="Edit protocol").click() |
130 | | - expect(page.get_by_role("button", name="Add Step")).to_be_visible(timeout=5000) |
131 | | - return ProtocolEditorPage(page) |
132 | | - |
133 | | - |
134 | | -def _dismiss_migration_modal(page: Page) -> None: |
135 | | - """Dismiss the migration modal if it appears during import.""" |
136 | | - |
137 | | - overlay = page.locator('[aria-label="BackgroundOverlay_ModalShell"]') |
138 | | - if overlay.is_visible(): |
139 | | - page.get_by_role("button", name="Import", exact=True).click() |
140 | | - expect(overlay).not_to_be_visible() |
| 123 | + if eyes is not None: |
| 124 | + eyes.check( |
| 125 | + checkpoint_name="Mix Step Form Tip Handling Settings - Part 4", |
| 126 | + target=eyes.Target.window().fully(), |
| 127 | + ) |
0 commit comments