codemie-test-harness 0.1.167__py3-none-any.whl → 0.1.169__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of codemie-test-harness might be problematic. Click here for more details.

Files changed (36) hide show
  1. codemie_test_harness/tests/test_data/assistant_test_data.py +197 -0
  2. codemie_test_harness/tests/ui/assistants/__init__.py +0 -0
  3. codemie_test_harness/tests/ui/assistants/test_create_assistant.py +408 -0
  4. codemie_test_harness/tests/ui/conftest.py +23 -3
  5. codemie_test_harness/tests/ui/pageobject/assistants/assistants_page.py +3 -4
  6. codemie_test_harness/tests/ui/pageobject/assistants/create_assistant_page.py +689 -0
  7. codemie_test_harness/tests/ui/pageobject/assistants/generate_with_ai_modal.py +367 -0
  8. codemie_test_harness/tests/ui/pageobject/base_page.py +24 -24
  9. codemie_test_harness/tests/ui/pageobject/components/__init__.py +2 -2
  10. codemie_test_harness/tests/ui/pageobject/components/execution_history_row.py +3 -3
  11. codemie_test_harness/tests/ui/pageobject/components/{header.py → menu.py} +57 -111
  12. codemie_test_harness/tests/ui/pageobject/components/pop_up.py +2 -2
  13. codemie_test_harness/tests/ui/pageobject/components/workflow_card.py +7 -10
  14. codemie_test_harness/tests/ui/pageobject/components/workflow_execution_history_item.py +9 -9
  15. codemie_test_harness/tests/ui/pageobject/components/workflow_execution_state.py +1 -1
  16. codemie_test_harness/tests/ui/pageobject/components/workflow_sidebar.py +16 -16
  17. codemie_test_harness/tests/ui/pageobject/workflows/base_workflow_form_page.py +32 -65
  18. codemie_test_harness/tests/ui/pageobject/workflows/create_workflow_page.py +4 -4
  19. codemie_test_harness/tests/ui/pageobject/workflows/edit_workflow_page.py +6 -4
  20. codemie_test_harness/tests/ui/pageobject/workflows/workflow_details_page.py +32 -61
  21. codemie_test_harness/tests/ui/pageobject/workflows/workflow_executions_page.py +17 -23
  22. codemie_test_harness/tests/ui/pageobject/workflows/workflow_template_details.py +5 -11
  23. codemie_test_harness/tests/ui/pageobject/workflows/workflow_templates_page.py +5 -2
  24. codemie_test_harness/tests/ui/pageobject/workflows/workflows_page.py +3 -5
  25. codemie_test_harness/tests/ui/pytest.ini +18 -0
  26. codemie_test_harness/tests/ui/workflows/__init__.py +0 -0
  27. codemie_test_harness/tests/ui/{test_create_workflow.py → workflows/test_create_workflow.py} +12 -33
  28. codemie_test_harness/tests/ui/{test_edit_workflow.py → workflows/test_edit_workflow.py} +14 -34
  29. codemie_test_harness/tests/ui/{test_workflow_details.py → workflows/test_workflow_details.py} +11 -11
  30. codemie_test_harness/tests/ui/{test_workflow_executions_page.py → workflows/test_workflow_executions_page.py} +0 -2
  31. codemie_test_harness/tests/ui/{test_workflow_templates.py → workflows/test_workflow_templates.py} +0 -2
  32. codemie_test_harness/tests/ui/{test_workflows.py → workflows/test_workflows.py} +8 -6
  33. {codemie_test_harness-0.1.167.dist-info → codemie_test_harness-0.1.169.dist-info}/METADATA +2 -2
  34. {codemie_test_harness-0.1.167.dist-info → codemie_test_harness-0.1.169.dist-info}/RECORD +36 -29
  35. {codemie_test_harness-0.1.167.dist-info → codemie_test_harness-0.1.169.dist-info}/WHEEL +0 -0
  36. {codemie_test_harness-0.1.167.dist-info → codemie_test_harness-0.1.169.dist-info}/entry_points.txt +0 -0
@@ -1,10 +1,10 @@
1
1
  import platform
2
2
  from playwright.sync_api import Page, Locator, expect
3
3
  from reportportal_client import step
4
- from typing import Optional
5
4
 
6
5
  from codemie_test_harness.tests.ui.pageobject.base_page import BasePage
7
6
  from codemie_test_harness.tests.ui.pageobject.components import WorkflowSidebar
7
+ from codemie_test_harness.tests.ui.pageobject.components import Menu
8
8
 
9
9
 
10
10
  class BaseWorkflowFormPage(BasePage):
@@ -13,6 +13,7 @@ class BaseWorkflowFormPage(BasePage):
13
13
  def __init__(self, page: Page):
14
14
  super().__init__(page)
15
15
  self.sidebar = WorkflowSidebar(page)
16
+ self._menu = Menu(page)
16
17
 
17
18
  # ==============================================
18
19
  # PROPERTIES - UI ELEMENTS
@@ -60,56 +61,58 @@ class BaseWorkflowFormPage(BasePage):
60
61
  @property
61
62
  def shared_switch(self) -> Locator:
62
63
  """Shared with Project Team switch."""
63
- return self.page.locator("//input[@id='shared']")
64
+ return self.page.locator("input#shared")
64
65
 
65
66
  @property
66
67
  def shared_switch_label(self) -> Locator:
67
68
  """Shared with Project Team switch label."""
68
- return self.page.locator("//label[@id='shared']")
69
+ return self.page.locator("label#shared")
69
70
 
70
71
  # ------ Name Field Elements ------
71
72
  @property
72
73
  def name_input(self) -> Locator:
73
74
  """Name input field."""
74
- return self.page.locator("#name")
75
+ return self.page.locator("input#name")
75
76
 
76
77
  @property
77
78
  def name_field_label(self) -> Locator:
78
79
  """Name field label."""
79
- return self.page.locator('div.text-sm.font-semibold:has-text("Name")')
80
+ return self.page.locator('.input-label:has-text("Name")')
80
81
 
81
82
  @property
82
83
  def name_error_message(self):
83
84
  """Name field error message."""
84
- return self.page.locator(".input-field-wrapper:has(#name) .input-error-message")
85
+ return self.page.locator(
86
+ "label.input-field-wrapper[for='name'] .input-error-message"
87
+ )
85
88
 
86
89
  # ------ Description Field Elements ------
87
90
  @property
88
91
  def description_textarea(self) -> Locator:
89
92
  """Description textarea field."""
90
- return self.page.locator("#description")
93
+ return self.page.locator("textarea#description")
91
94
 
92
95
  @property
93
96
  def description_field_label(self) -> Locator:
94
97
  """Description field label."""
95
- return self.page.locator('div.text-sm.font-semibold:has-text("Description")')
98
+ return self.page.locator('label[for="description"].textarea-label')
96
99
 
97
100
  # ------ Icon URL Field Elements ------
98
101
  @property
99
102
  def icon_url_input(self) -> Locator:
100
103
  """Icon URL input field."""
101
- return self.page.locator("#icon_url")
104
+ return self.page.locator("input#icon_url")
102
105
 
103
106
  @property
104
107
  def icon_url_field_label(self) -> Locator:
105
108
  """Icon URL field label."""
106
- return self.page.locator('div.text-sm.font-semibold:has-text("Icon URL")')
109
+ return self.page.locator('.input-label:has-text("Icon URL")')
107
110
 
108
111
  @property
109
112
  def icon_url_error_message(self):
110
113
  """Icon URL field error message."""
111
114
  return self.page.locator(
112
- ".input-field-wrapper:has(#icon_url) .input-error-message"
115
+ "label.input-field-wrapper[for='icon_url'] .input-error-message"
113
116
  )
114
117
 
115
118
  # ------ Workflow Mode Elements ------
@@ -118,11 +121,6 @@ class BaseWorkflowFormPage(BasePage):
118
121
  """Workflow Mode dropdown."""
119
122
  return self.page.locator('.p-dropdown[data-pc-name="dropdown"]')
120
123
 
121
- @property
122
- def workflow_mode_label(self) -> Locator:
123
- """Workflow Mode field label."""
124
- return self.page.locator('div.text-sm.font-semibold:has-text("Workflow Mode")')
125
-
126
124
  @property
127
125
  def workflow_mode_selected_value(self) -> Locator:
128
126
  """Currently selected workflow mode value."""
@@ -131,12 +129,12 @@ class BaseWorkflowFormPage(BasePage):
131
129
  @property
132
130
  def workflow_mode_info_icon(self) -> Locator:
133
131
  """Information icon next to workflow mode."""
134
- return self.page.locator(".scale-105.opacity-75 svg")
132
+ return self.page.locator(".flex.w-full.px-2.gap-2 .opacity-75 svg")
135
133
 
136
134
  @property
137
135
  def workflow_mode_description(self) -> Locator:
138
136
  """Workflow mode description text."""
139
- return self.page.locator(".flex.w-full.px-2.gap-2 .text-text-secondary.text-sm")
137
+ return self.page.locator(".flex.w-full.px-2.gap-2 .text-text-secondary.text-xs")
140
138
 
141
139
  # ------ YAML Configuration Elements ------
142
140
  @property
@@ -175,49 +173,48 @@ class BaseWorkflowFormPage(BasePage):
175
173
  @property
176
174
  def visualize_button(self) -> Locator:
177
175
  """Visualize button."""
178
- return self.page.locator('button.button.secondary.large:has-text("Visualize")')
176
+ return self.page.locator('button.button.secondary.medium:has-text("Visualize")')
179
177
 
180
178
  @property
181
179
  def visualization_placeholder(self) -> Locator:
182
180
  """Workflow visualization placeholder area."""
183
181
  return self.page.locator(
184
- "//div[text()=' Workflow visualisation ']/../following-sibling::div"
182
+ ".flex.flex-col.items-center.justify-center.bg-new-panel"
185
183
  )
186
184
 
187
185
  @property
188
186
  def visualization_placeholder_text(self) -> Locator:
189
187
  """Visualization placeholder text."""
190
- return self.page.locator(
191
- "//div[text()=' Workflow visualisation ']/../following-sibling::div/div[text()=' Workflow visualisation']"
192
- )
188
+ return self.visualization_placeholder.locator(":scope > .text-md")
193
189
 
194
190
  @property
195
191
  def workflow_diagram(self) -> Locator:
196
- """Workflow diagram image."""
197
- return self.page.locator(
198
- "//div[text()=' Workflow visualisation ']/../following-sibling::div//img[@alt='Workflow Diagram']"
199
- )
192
+ """Workflow diagram image (if/when present)."""
193
+ return self.visualization_placeholder.locator("img[alt='Workflow Diagram']")
200
194
 
201
195
  @property
202
196
  def reset_zooming(self) -> Locator:
203
197
  """Reset zooming button in visualization."""
204
- return self.page.locator(
205
- "//div[text()=' Workflow visualisation ']/../following-sibling::div/div/div/ul/li[2]"
198
+ return self.visualization_placeholder.locator(
199
+ "div.controll__buttons li.controll__home"
200
+ )
201
+
202
+ @property
203
+ def zoom_buttons(self) -> Locator:
204
+ """Zoom buttons."""
205
+ return self.visualization_placeholder.locator(
206
+ "div.controll__buttons li.controll__zoom-in"
206
207
  )
207
208
 
208
209
  @property
209
210
  def zoom_in_button(self) -> Locator:
210
211
  """Zoom in button in visualization."""
211
- return self.page.locator(
212
- "//div[text()=' Workflow visualisation ']/../following-sibling::div/div/div/ul/li[3]"
213
- )
212
+ return self.zoom_buttons.nth(0)
214
213
 
215
214
  @property
216
215
  def zoom_out_button(self) -> Locator:
217
216
  """Zoom out button in visualization."""
218
- return self.page.locator(
219
- "//div[text()=' Workflow visualisation ']/../following-sibling::div/div/div/ul/li[4]"
220
- )
217
+ return self.zoom_buttons.nth(1)
221
218
 
222
219
  # ==============================================
223
220
  # METHODS - ACTIONS & INTERACTIONS
@@ -275,14 +272,6 @@ class BaseWorkflowFormPage(BasePage):
275
272
  """Fill the icon URL field."""
276
273
  self.icon_url_input.fill(icon_url)
277
274
 
278
- # ------ Workflow Mode Actions ------
279
- @step
280
- def select_workflow_mode(self, mode: Optional[str] = None):
281
- """Select workflow mode from dropdown. If mode is None, just click to open."""
282
- self.workflow_mode_dropdown.click()
283
- if mode:
284
- self.page.locator(f'li:has-text("{mode}")').click()
285
-
286
275
  # ------ YAML Configuration Actions ------
287
276
  @step
288
277
  def fill_yaml_config(self, yaml_content: str):
@@ -366,27 +355,6 @@ class BaseWorkflowFormPage(BasePage):
366
355
  """Verify that the icon URL field is empty."""
367
356
  expect(self.icon_url_input).to_have_value("")
368
357
 
369
- # ------ Workflow Mode Verifications ------
370
- @step
371
- def should_have_workflow_mode_dropdown_visible(self):
372
- """Verify that the workflow mode dropdown is visible."""
373
- expect(self.workflow_mode_dropdown).to_be_visible()
374
-
375
- @step
376
- def should_have_workflow_mode_selected(self, mode: str):
377
- """Verify that the specified workflow mode is selected."""
378
- expect(self.workflow_mode_selected_value).to_have_text(mode)
379
-
380
- @step
381
- def should_show_workflow_mode_info(self):
382
- """Verify that workflow mode information is displayed."""
383
- expect(self.workflow_mode_info_icon).to_be_visible()
384
- expect(self.workflow_mode_description).to_be_visible()
385
- expect(self.workflow_mode_description).to_contain_text(
386
- "Sequential Mode offers full control. You define the workflow, choosing assistants and setting each step. "
387
- "It's perfect for tasks needing specific, orderly execution. Opt for this when detail and sequence matter."
388
- )
389
-
390
358
  # ------ YAML Configuration Verifications ------
391
359
  @step
392
360
  def should_have_yaml_editor_visible(self):
@@ -445,7 +413,6 @@ class BaseWorkflowFormPage(BasePage):
445
413
  expect(self.name_field_label).to_be_visible()
446
414
  expect(self.description_field_label).to_be_visible()
447
415
  expect(self.icon_url_field_label).to_be_visible()
448
- expect(self.workflow_mode_label).to_be_visible()
449
416
  expect(self.yaml_config_header).to_be_visible()
450
417
  expect(self.visualization_header).to_be_visible()
451
418
  return self
@@ -16,7 +16,7 @@ class CreateWorkflowPage(BaseWorkflowFormPage):
16
16
  super().__init__(page)
17
17
 
18
18
  # ==================== PROPERTIES ====================
19
- # Header elements specific to Create page
19
+ # Menu elements specific to Create page
20
20
  @property
21
21
  def page_title(self):
22
22
  """Page title 'Create Workflow'."""
@@ -24,7 +24,7 @@ class CreateWorkflowPage(BaseWorkflowFormPage):
24
24
 
25
25
  @property
26
26
  def create_button(self):
27
- """Create button in the header."""
27
+ """Create button in the menu."""
28
28
  return self.page.locator('button.button.primary.medium:has-text("Create")')
29
29
 
30
30
  # ==================== NAVIGATION METHODS ====================
@@ -91,8 +91,8 @@ class CreateWorkflowPage(BaseWorkflowFormPage):
91
91
  return self
92
92
 
93
93
  @step
94
- def should_have_header_elements_visible(self):
95
- """Verify that all header elements are visible."""
94
+ def should_have_menu_elements_visible(self):
95
+ """Verify that all menu elements are visible."""
96
96
  expect(self.back_button).to_be_visible()
97
97
  expect(self.page_title).to_be_visible()
98
98
  expect(self.cancel_button).to_be_visible()
@@ -27,7 +27,7 @@ class EditWorkflowPage(BaseWorkflowFormPage):
27
27
 
28
28
  @property
29
29
  def update_button(self) -> Locator:
30
- """Update button in the header."""
30
+ """Update button in the menu."""
31
31
  return self.page.locator('button.button.primary.medium:has-text("Update")')
32
32
 
33
33
  @property
@@ -68,7 +68,9 @@ class EditWorkflowPage(BaseWorkflowFormPage):
68
68
  @property
69
69
  def restore_button(self) -> Locator:
70
70
  """Restore button in history tab."""
71
- return self.page.locator('button.button.secondary.large:has-text("Restore")')
71
+ return self.page.locator(
72
+ 'button.primary.medium.self-center:has-text("Restore")'
73
+ )
72
74
 
73
75
  @property
74
76
  def history_yaml_editor(self) -> Locator:
@@ -230,8 +232,8 @@ class EditWorkflowPage(BaseWorkflowFormPage):
230
232
  expect(self.back_button).to_be_visible()
231
233
 
232
234
  @step
233
- def should_have_header_elements_visible(self):
234
- """Verify that all header elements are visible."""
235
+ def should_have_menu_elements_visible(self):
236
+ """Verify that all menu elements are visible."""
235
237
  expect(self.back_button).to_be_visible()
236
238
  expect(self.page_title).to_be_visible()
237
239
  expect(self.cancel_button).to_be_visible()
@@ -28,9 +28,7 @@ class WorkflowDetailsPage(BasePage):
28
28
  @property
29
29
  def page_title(self) -> Locator:
30
30
  """Page title 'Workflow Details'."""
31
- return self.page.locator(
32
- 'div.text-lg.font-semibold:has-text("Workflow Details")'
33
- )
31
+ return self.page.locator('div.text-h3:has-text("Workflow Details")')
34
32
 
35
33
  @property
36
34
  def workflow_avatar(self) -> Locator:
@@ -40,7 +38,7 @@ class WorkflowDetailsPage(BasePage):
40
38
  @property
41
39
  def workflow_name(self) -> Locator:
42
40
  """Workflow name heading."""
43
- return self.page.locator('//main//div[contains(@class,"text-text-main")]')
41
+ return self.page.locator("h4.text-2xl ")
44
42
 
45
43
  @property
46
44
  def workflow_author(self) -> Locator:
@@ -62,9 +60,7 @@ class WorkflowDetailsPage(BasePage):
62
60
  @property
63
61
  def run_workflow_button(self) -> Locator:
64
62
  """Run workflow button."""
65
- return self.page.locator(
66
- 'button.button.primary.medium:has-text("Run workflow")'
67
- )
63
+ return self.page.locator('.button.primary.medium:has-text("Run workflow")')
68
64
 
69
65
  # Tab Elements
70
66
  @property
@@ -170,12 +166,9 @@ class WorkflowDetailsPage(BasePage):
170
166
  @property
171
167
  def about_workflow_section(self) -> Locator:
172
168
  """About workflow section."""
173
- return self.page.locator('div:has-text("About workflow:")').first
174
-
175
- @property
176
- def workflow_description(self) -> Locator:
177
- """Workflow description text."""
178
- return self.page.locator("div.text-sm.text-text-tertiary").first
169
+ return self.page.locator(
170
+ '//div[contains(text(),"About workflow:")]/following::div'
171
+ ).first
179
172
 
180
173
  @property
181
174
  def workflow_graph_schema_section(self) -> Locator:
@@ -190,7 +183,7 @@ class WorkflowDetailsPage(BasePage):
190
183
  @property
191
184
  def config_code_block(self) -> Locator:
192
185
  """Configuration code block container."""
193
- return self.page.locator("div.config div.code-block")
186
+ return self.page.locator("div.code-block > pre.code-content")
194
187
 
195
188
  @property
196
189
  def code_block_header(self) -> Locator:
@@ -205,12 +198,12 @@ class WorkflowDetailsPage(BasePage):
205
198
  @property
206
199
  def copy_code_button(self) -> Locator:
207
200
  """Copy code button."""
208
- return self.page.locator('button:has-text("Copy code")')
201
+ return self.page.locator('//button[normalize-space()="Copy"]')
209
202
 
210
203
  @property
211
204
  def download_code_button(self) -> Locator:
212
205
  """Download button."""
213
- return self.page.locator('button:has-text("Download")')
206
+ return self.page.locator('//button[normalize-space()="Download"]')
214
207
 
215
208
  @property
216
209
  def code_content(self) -> Locator:
@@ -228,11 +221,6 @@ class WorkflowDetailsPage(BasePage):
228
221
  """Overview title in sidebar."""
229
222
  return self.page.locator('span:has-text("OVERVIEW")').first
230
223
 
231
- @property
232
- def mode_info(self) -> Locator:
233
- """Mode information in sidebar."""
234
- return self.page.locator('div:has-text("Mode:")').first
235
-
236
224
  @property
237
225
  def project_info(self) -> Locator:
238
226
  """Project information in sidebar."""
@@ -240,15 +228,13 @@ class WorkflowDetailsPage(BasePage):
240
228
 
241
229
  @property
242
230
  def workflow_id_section(self) -> Locator:
243
- """Workflow ID section in sidebar."""
244
- return self.page.locator(
245
- '//div[text()="Workflow ID"]/following-sibling::label/div'
246
- )
231
+ """Workflow ID tab in Overview."""
232
+ return self.page.locator('//div[.="Workflow ID"]/following-sibling::label')
247
233
 
248
234
  @property
249
235
  def workflow_id_input(self) -> Locator:
250
236
  """Workflow ID input field."""
251
- return self.workflow_id_section.locator("//input[@disabled]")
237
+ return self.workflow_id_section.locator("//input[@readonly]")
252
238
 
253
239
  @property
254
240
  def workflow_id_copy_button(self) -> Locator:
@@ -256,28 +242,21 @@ class WorkflowDetailsPage(BasePage):
256
242
  return self.workflow_id_section.locator("//button")
257
243
 
258
244
  @property
259
- def access_links_section(self) -> Locator:
260
- """Access Links section in sidebar."""
261
- return self.page.locator(
262
- '//div[text()=" Access Links "]/following-sibling::label/div'
263
- )
245
+ def workflow_access_links_section(self) -> Locator:
246
+ """Workflow Access Links tab in Overview."""
247
+ return self.page.locator('//div[.="Access Links"]/following-sibling::label')
264
248
 
265
249
  @property
266
- def workflow_details_link_label(self) -> Locator:
267
- """Link to workflow details label."""
268
- return self.access_links_section.locator(
269
- '../preceding-sibling::div[text()="Link to workflow details:"]'
270
- )
250
+ def workflow_access_links_input(self) -> Locator:
251
+ """Workflow Access Links input field."""
252
+ return self.workflow_access_links_section.locator("//input[@readonly]")
271
253
 
272
254
  @property
273
- def workflow_details_link_input(self) -> Locator:
274
- """Workflow details link input field."""
275
- return self.access_links_section.locator("//input[@disabled]")
276
-
277
- @property
278
- def workflow_details_link_copy_button(self) -> Locator:
279
- """Copy button for workflow details link."""
280
- return self.access_links_section.locator("//button")
255
+ def workflow_access_links_copy_button(self) -> Locator:
256
+ """Copy button for workflow Access Links."""
257
+ return self.workflow_access_links_section.locator(
258
+ "//div[contains(@class, 'input-right-icon')]//button"
259
+ )
281
260
 
282
261
  # ==================== NAVIGATION METHODS ====================
283
262
 
@@ -540,7 +519,7 @@ class WorkflowDetailsPage(BasePage):
540
519
  """Verify that the Export popup is visible with expected content."""
541
520
  expect(self.pop_up.cancel).to_have_text("Cancel")
542
521
  expect(self.pop_up.submit).to_have_text("Export")
543
- expect(self.pop_up.header).to_have_text("Export Workflow Execution")
522
+ expect(self.pop_up.menu).to_have_text("Export Workflow Execution")
544
523
  expect(self.pop_up.close).to_be_visible()
545
524
 
546
525
  @step
@@ -554,7 +533,6 @@ class WorkflowDetailsPage(BasePage):
554
533
  def should_have_configuration_elements_visible(self):
555
534
  """Verify that all configuration tab elements are visible."""
556
535
  expect(self.about_workflow_section).to_be_visible()
557
- expect(self.workflow_description).to_be_visible()
558
536
  expect(self.workflow_graph_schema_section).to_be_visible()
559
537
  expect(self.workflow_graph_image).to_be_visible()
560
538
  expect(self.config_code_block).to_be_visible()
@@ -574,33 +552,26 @@ class WorkflowDetailsPage(BasePage):
574
552
  """Verify that overview sidebar is visible with all elements."""
575
553
  expect(self.overview_sidebar).to_be_visible()
576
554
  expect(self.overview_title).to_be_visible()
577
- expect(self.mode_info).to_be_visible()
578
555
  expect(self.project_info).to_be_visible()
579
556
 
580
557
  @step
581
558
  def should_have_workflow_id_section_visible(self):
582
559
  """Verify that workflow ID section is visible."""
583
560
  expect(self.workflow_id_section).to_be_visible()
561
+ expect(self.workflow_id_copy_button).to_be_visible()
584
562
  expect(self.workflow_id_input).to_be_visible()
585
- expect(self.workflow_id_input).to_be_disabled()
586
563
 
587
564
  @step
588
565
  def should_have_access_links_section_visible(self):
589
566
  """Verify that access links section is visible."""
590
- expect(self.access_links_section).to_be_visible()
591
- expect(self.workflow_details_link_label).to_be_visible()
592
- expect(self.workflow_details_link_input).to_be_visible()
593
- expect(self.workflow_details_link_input).to_be_disabled()
567
+ expect(self.workflow_access_links_section).to_be_visible()
568
+ expect(self.workflow_access_links_copy_button).to_be_visible()
569
+ expect(self.workflow_access_links_input).to_be_visible()
594
570
 
595
571
  @step
596
572
  def should_have_workflow_description(self, expected_description: str):
597
573
  """Verify the workflow description text."""
598
- expect(self.workflow_description).to_have_text(expected_description)
599
-
600
- @step
601
- def should_have_mode_value(self, expected_mode: str):
602
- """Verify the mode value in overview."""
603
- expect(self.mode_info).to_contain_text(expected_mode)
574
+ expect(self.about_workflow_section).to_have_text(expected_description)
604
575
 
605
576
  @step
606
577
  def should_have_project_value(self, expected_project: str):
@@ -615,8 +586,8 @@ class WorkflowDetailsPage(BasePage):
615
586
  @step
616
587
  def should_have_workflow_details_link_value(self, workflow_id: str):
617
588
  """Verify the workflow details link value."""
618
- expect(self.workflow_details_link_input).to_have_value(
619
- f"{os.getenv('UI_URL')}/#/workflows?workflow={workflow_id}"
589
+ expect(self.workflow_access_links_input).to_have_value(
590
+ f"{os.getenv('FRONTEND_URL')}#/workflows?workflow={workflow_id}"
620
591
  )
621
592
 
622
593
  @step
@@ -648,7 +619,7 @@ class WorkflowDetailsPage(BasePage):
648
619
  @step
649
620
  def click_copy_workflow_details_link(self):
650
621
  """Click the copy workflow details link button."""
651
- self.workflow_details_link_copy_button.click()
622
+ self.workflow_access_links_copy_button.click()
652
623
 
653
624
  @step
654
625
  def should_have_graph_image_loaded(self):
@@ -22,13 +22,13 @@ class WorkflowExecutionsPage(BasePage):
22
22
  @property
23
23
  def workflow_title(self) -> Locator:
24
24
  """Workflow title in the header."""
25
- return self.page.locator("div.text-lg.font-semibold.whitespace-nowrap")
25
+ return self.page.locator("div.text-h3")
26
26
 
27
27
  @property
28
28
  def export_button(self) -> Locator:
29
29
  """Export button in the header."""
30
30
  return self.page.locator(
31
- '//button[text()=" Rerun workflow "]/preceding-sibling::button'
31
+ '//button[text()=" Rerun workflow "]/preceding-sibling::button[2]'
32
32
  )
33
33
 
34
34
  @property
@@ -41,7 +41,7 @@ class WorkflowExecutionsPage(BasePage):
41
41
  @property
42
42
  def sidebar(self) -> Locator:
43
43
  """Left sidebar container."""
44
- return self.page.locator("aside.sidebar")
44
+ return self.page.locator("aside.w-workflow-exec-sidebar")
45
45
 
46
46
  @property
47
47
  def execution_history_title(self) -> Locator:
@@ -51,9 +51,7 @@ class WorkflowExecutionsPage(BasePage):
51
51
  @property
52
52
  def time_period_label(self) -> Locator:
53
53
  """Time period label (Last 7 days)."""
54
- return self.page.locator(
55
- 'div.text-xs.text-text-secondary.uppercase:has-text("Last 7 days")'
56
- )
54
+ return self.page.locator("div.text-xs.text-text-secondary.uppercase")
57
55
 
58
56
  @property
59
57
  def execution_history_items(self) -> Locator:
@@ -63,7 +61,9 @@ class WorkflowExecutionsPage(BasePage):
63
61
  @property
64
62
  def active_execution_item(self) -> Locator:
65
63
  """Currently active/selected execution item in sidebar."""
66
- return self.page.locator(".bg-new-stroke")
64
+ return self.page.locator(
65
+ "div[class*='bg-new-stroke/60'][class*='!text-text-main']"
66
+ )
67
67
 
68
68
  # ==================== MAIN CONTENT ELEMENTS ====================
69
69
 
@@ -107,12 +107,12 @@ class WorkflowExecutionsPage(BasePage):
107
107
  @property
108
108
  def prompt_section(self) -> Locator:
109
109
  """Prompt code block section."""
110
- return self.page.locator("div.flex.mt-2")
110
+ return self.page.locator("pre.code-content.language-text")
111
111
 
112
112
  @property
113
113
  def prompt_code_block(self) -> Locator:
114
114
  """Prompt code block container."""
115
- return self.prompt_section.locator("div.code-block.min-w-full")
115
+ return self.page.locator("div.code-block.min-w-full")
116
116
 
117
117
  @property
118
118
  def prompt_header(self) -> Locator:
@@ -122,29 +122,27 @@ class WorkflowExecutionsPage(BasePage):
122
122
  @property
123
123
  def prompt_title(self) -> Locator:
124
124
  """Prompt title in header."""
125
- return self.prompt_header.locator('div:has-text("prompt")')
125
+ return self.prompt_header.locator('div:has-text("Prompt")')
126
126
 
127
127
  @property
128
128
  def prompt_info_button(self) -> Locator:
129
129
  """Prompt info button."""
130
- return self.prompt_header.locator(
131
- 'xpath=.//button[text()=" Copy code "]/preceding-sibling::button'
132
- )
130
+ return self.prompt_header.locator("button").first
133
131
 
134
132
  @property
135
133
  def prompt_copy_button(self) -> Locator:
136
134
  """Prompt copy code button."""
137
- return self.prompt_header.locator('xpath=.//button[text()=" Copy code "]')
135
+ return self.prompt_header.locator("button").nth(1)
138
136
 
139
137
  @property
140
138
  def prompt_download_button(self) -> Locator:
141
139
  """Prompt download button."""
142
- return self.prompt_header.locator('xpath=.//button[text()=" Download "]')
140
+ return self.prompt_header.locator("button").nth(2)
143
141
 
144
142
  @property
145
143
  def prompt_content(self) -> Locator:
146
144
  """Prompt code content."""
147
- return self.prompt_code_block.locator("pre.code-content.language-prompt code")
145
+ return self.prompt_code_block.locator("code")
148
146
 
149
147
  # ==================== STATES SECTION ====================
150
148
 
@@ -176,7 +174,7 @@ class WorkflowExecutionsPage(BasePage):
176
174
  @property
177
175
  def execution_states(self) -> Locator:
178
176
  """All execution state cards."""
179
- return self.page.locator('//div[@class="flex flex-row gap-5"]/..')
177
+ return self.page.locator(".flex.flex-col.justify-between.text-sm")
180
178
 
181
179
  # ==================== PAGINATION SECTION ====================
182
180
 
@@ -448,8 +446,7 @@ class WorkflowExecutionsPage(BasePage):
448
446
  self.should_have_status("Succeeded")
449
447
  expect(self.status_badge).to_contain_class("bg-success-secondary")
450
448
  expect(self.status_badge).to_contain_class("text-success-main")
451
- expect(self.status_badge).to_contain_class("border-success-main")
452
- expect(self.status_dot).to_contain_class("bg-success-main")
449
+ expect(self.status_badge).to_contain_class("border-success-border")
453
450
 
454
451
  @step
455
452
  def should_have_failed_status(self):
@@ -457,8 +454,7 @@ class WorkflowExecutionsPage(BasePage):
457
454
  self.should_have_status("Failed")
458
455
  expect(self.status_badge).to_contain_class("bg-error-secondary")
459
456
  expect(self.status_badge).to_contain_class("text-error-main")
460
- expect(self.status_badge).to_contain_class("border-error-main")
461
- expect(self.status_dot).to_contain_class("bg-error-main")
457
+ expect(self.status_badge).to_contain_class("border-error-border")
462
458
 
463
459
  @step
464
460
  def should_have_triggered_by(self, expected_user: str):
@@ -522,13 +518,11 @@ class WorkflowExecutionsPage(BasePage):
522
518
  @step
523
519
  def should_have_configuration_sidebar_closed(self):
524
520
  """Verify that the configuration sidebar is closed."""
525
- expect(self.configuration_sidebar).not_to_contain_class("w-[360px]")
526
521
  expect(self.configuration_sidebar).not_to_contain_class("opacity-100")
527
522
 
528
523
  @step
529
524
  def should_have_configuration_sidebar_open(self):
530
525
  """Verify that the configuration sidebar is open."""
531
- expect(self.configuration_sidebar).to_contain_class("w-[360px]")
532
526
  expect(self.configuration_sidebar).to_contain_class("opacity-100")
533
527
  expect(self.configure_workflow_title).to_be_visible()
534
528
  expect(self.workflow_info_card).to_be_visible()