codemie-test-harness 0.1.206__py3-none-any.whl → 0.1.208__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.

@@ -0,0 +1,256 @@
1
+ from hamcrest import assert_that, contains_string
2
+ from playwright.sync_api import Page, expect
3
+ from reportportal_client import step
4
+
5
+
6
+ class AssistantViewPage:
7
+ def __init__(self, page: Page):
8
+ self.page = page
9
+
10
+ # ----------------
11
+ # --- LOCATORS ---
12
+ # ----------------
13
+
14
+ @property
15
+ def assistant_name(self):
16
+ return self.page.locator("h4.name-target")
17
+
18
+ @property
19
+ def assistant_author(self):
20
+ return self.page.locator(".text-xs.text-text-secondary > p")
21
+
22
+ @property
23
+ def about_heading(self):
24
+ return self.page.locator("h5.font-bold.text-sm:text('About Assistant:')")
25
+
26
+ @property
27
+ def about_content(self):
28
+ # Paragraph under "About Assistant:"
29
+ return self.about_heading.locator("xpath=../p")
30
+
31
+ @property
32
+ def system_instructions_heading(self):
33
+ return self.page.locator(
34
+ "div.flex.bg-new-panel.border.rounded-lg [class*='text-xs']:text('System Instructions')"
35
+ )
36
+
37
+ @property
38
+ def system_instructions_content(self):
39
+ return self.page.locator("div.flex.bg-new-panel.border.rounded-lg > p.text-sm")
40
+
41
+ # --- Overview Block ---
42
+ @property
43
+ def overview_block(self):
44
+ return self.page.locator("p:text-is('OVERVIEW')").locator("xpath=..")
45
+
46
+ @property
47
+ def overview_project(self):
48
+ return self.overview_block.locator(
49
+ "div.flex:has(p.text-text-tertiary:text('Project:')) > p:not(.text-text-tertiary)"
50
+ )
51
+
52
+ @property
53
+ def overview_shared_status(self):
54
+ return self.overview_block.locator(
55
+ "div.flex:has(p.text-text-tertiary:text('Shared status:')) > p:not(.text-text-tertiary)"
56
+ )
57
+
58
+ @property
59
+ def overview_assistant_id(self):
60
+ return self.overview_block.locator(
61
+ "div.flex.flex-col.gap-2.mt-2.font-semibold input[readonly]"
62
+ )
63
+
64
+ # --- Links Block ---
65
+ @property
66
+ def access_links_block(self):
67
+ return self.page.locator(
68
+ "p.text-xs.text-text-main.font-semibold:text('ACCESS LINKS')"
69
+ ).locator("xpath=..")
70
+
71
+ @property
72
+ def details_link_input(self):
73
+ return self.access_links_block.locator(
74
+ "div.flex.flex-col.gap-2:has(p:text('Link to assistant details:')) input[readonly]"
75
+ )
76
+
77
+ @property
78
+ def chat_link_input(self):
79
+ return self.access_links_block.locator(
80
+ "div.flex.flex-col.gap-2:has(p:text('Link to start a chat')) input[readonly]"
81
+ )
82
+
83
+ # --- Configuration ---
84
+ @property
85
+ def configuration_block(self):
86
+ return self.page.locator(
87
+ "p.text-xs.text-text-main.font-semibold:text('CONFIGURATION')"
88
+ ).locator("xpath=..")
89
+
90
+ @property
91
+ def config_llm_model(self):
92
+ return self.configuration_block.locator("p:text('LLM model:')").locator(
93
+ "xpath=../div"
94
+ )
95
+
96
+ @property
97
+ def config_temperature(self):
98
+ return self.configuration_block.locator("p:text('Temperature:')").locator(
99
+ "xpath=../div"
100
+ )
101
+
102
+ @property
103
+ def config_top_p(self):
104
+ return self.configuration_block.locator("p:text('Top P:')").locator(
105
+ "xpath=../div"
106
+ )
107
+
108
+ @property
109
+ def config_additional_datasource_context(self):
110
+ return self.configuration_block.locator(
111
+ "div:has(p:text('Additional datasource context')) div.bg-new-panel.py-1\\.5.px-2"
112
+ )
113
+
114
+ # --- Tools & Capabilities ---
115
+
116
+ @property
117
+ def tools_block(self):
118
+ """Block containing all toolkits under 'TOOLS & CAPABILITIES'."""
119
+ return self.page.locator(
120
+ "p.text-xs.text-text-main.font-semibold:text('TOOLS & CAPABILITIES')"
121
+ ).locator("xpath=..")
122
+
123
+ def toolkit_block(self, toolkit_name: str):
124
+ """Returns the block for the toolkit (e.g., 'Git', 'VCS', etc.)."""
125
+ return self.tools_block.locator(
126
+ f"div.text-xs.flex.flex-col.gap-2:has(p.text-sm:has-text('{toolkit_name}'))"
127
+ )
128
+
129
+ def toolkit_tool_labels(self, toolkit_name: str):
130
+ """Returns elements for all tool labels under a given toolkit block."""
131
+ return self.toolkit_block(toolkit_name).locator(
132
+ "div.flex.flex-wrap.gap-2 > div"
133
+ )
134
+
135
+ def toolkit_tool_label(self, toolkit_name: str, tool_label: str):
136
+ """Returns the div for a specific tool label (exact match, trimmed)."""
137
+ return self.toolkit_block(toolkit_name).locator(
138
+ f"div.flex.flex-wrap.gap-2 > div:text-is('{tool_label}')"
139
+ )
140
+
141
+ # --------------------------
142
+ # --- EXPECT/VERIFY ---
143
+ # --------------------------
144
+
145
+ @step
146
+ def should_have_all_form_fields_visible(
147
+ self, name: str, author: str, description: str
148
+ ):
149
+ expect(self.assistant_name).to_be_visible()
150
+ expect(self.assistant_name).to_have_text(name)
151
+ expect(self.assistant_author).to_be_visible()
152
+ expect(self.assistant_author).to_have_text(f"by {author}")
153
+ expect(self.about_content).to_be_visible()
154
+ expect(self.about_content).to_have_text(description)
155
+ expect(self.system_instructions_content).to_be_visible()
156
+ return self
157
+
158
+ @step
159
+ def should_have_overview_form_fields_visible(
160
+ self, project: str, status: str, assistant_id: str
161
+ ):
162
+ expect(self.overview_project).to_be_visible()
163
+ expect(self.overview_project).to_have_text(project)
164
+ expect(self.overview_shared_status).to_be_visible()
165
+ expect(self.overview_shared_status).to_have_text(status)
166
+ expect(self.overview_assistant_id).to_be_visible()
167
+ expect(self.overview_assistant_id).to_have_value(assistant_id)
168
+ return self
169
+
170
+ @step
171
+ def should_have_access_links_form_fields_visible(
172
+ self, assistant_id: str, assistant_name: str
173
+ ):
174
+ expect(self.details_link_input).to_be_visible()
175
+ assert_that(
176
+ self.details_link_input.input_value(), contains_string(assistant_id)
177
+ )
178
+ expect(self.chat_link_input).to_be_visible()
179
+ assert_that(self.chat_link_input.input_value(), contains_string(assistant_name))
180
+ return self
181
+
182
+ @step
183
+ def should_have_configuration_form_fields_visible(
184
+ self, temperature: str, top_p: str
185
+ ):
186
+ expect(self.config_temperature).to_be_visible()
187
+ expect(self.config_temperature).to_have_text(temperature)
188
+ expect(self.config_top_p).to_be_visible()
189
+ expect(self.config_top_p).to_have_text(top_p)
190
+ return self
191
+
192
+ @step
193
+ def should_see_assistant_name(self, expected):
194
+ expect(self.assistant_name).to_have_text(expected)
195
+
196
+ @step
197
+ def should_see_assistant_author(self, expected):
198
+ expect(self.assistant_author).to_have_text(expected)
199
+
200
+ @step
201
+ def should_see_about_content(self, expected):
202
+ expect(self.about_content).to_have_text(expected)
203
+
204
+ @step
205
+ def should_see_system_instructions(self, expected):
206
+ expect(self.system_instructions_content).to_have_text(expected)
207
+
208
+ @step
209
+ def should_see_overview_project(self, expected):
210
+ expect(self.overview_project()).to_have_text(expected)
211
+
212
+ @step
213
+ def should_see_overview_shared_status(self, expected):
214
+ expect(self.overview_shared_status()).to_have_text(expected)
215
+
216
+ @step
217
+ def should_see_assistant_id(self, expected):
218
+ expect(self.assistant_id_value).to_have_value(expected)
219
+
220
+ @step
221
+ def should_see_links(self, details_link, chat_link):
222
+ expect(self.details_link_input()).to_have_value(details_link)
223
+ expect(self.chat_link_input()).to_have_value(chat_link)
224
+
225
+ @step
226
+ def should_see_config_llm_model(self, expected):
227
+ expect(self.config_llm_model()).to_have_text(expected)
228
+
229
+ @step
230
+ def should_see_config_temperature(self, expected):
231
+ expect(self.config_temperature()).to_have_text(expected)
232
+
233
+ @step
234
+ def should_see_config_top_p(self, expected):
235
+ expect(self.config_top_p()).to_have_text(expected)
236
+
237
+ @step
238
+ def should_see_toolkit_visible(self, toolkit_name: str):
239
+ """Assert that the toolkit with the given name is visible."""
240
+ expect(self.toolkit_block(toolkit_name)).to_be_visible()
241
+
242
+ @step
243
+ def should_see_toolkit_contains(self, toolkit_name: str, tool_label: str):
244
+ """Assert a toolkit contains a tool label (visible)."""
245
+ expect(self.toolkit_tool_label(toolkit_name, tool_label)).to_be_visible()
246
+
247
+ @step
248
+ def should_see_tool_not_present(self, toolkit_name: str, tool_label: str):
249
+ """Assert a toolkit does NOT contain a tool label."""
250
+ expect(self.toolkit_tool_label(toolkit_name, tool_label)).not_to_be_visible()
251
+
252
+ @step
253
+ def should_see_toolkit_tools(self, toolkit_name: str, expected_tools: list):
254
+ """Assert all expected tool labels are present in a toolkit."""
255
+ for tool in expected_tools:
256
+ self.expect_toolkit_contains(toolkit_name, tool)
@@ -78,6 +78,41 @@ class AssistantsPage(BasePage):
78
78
  """Clear all filters button."""
79
79
  return self.page.locator('button:has-text("Clear all")')
80
80
 
81
+ @property
82
+ def action_view_details(self):
83
+ """Dropdown 'View Details' button."""
84
+ return self.page.locator("button").filter(has_text="View Details")
85
+
86
+ @property
87
+ def action_copy_link(self):
88
+ """Dropdown 'Copy Link' button."""
89
+ return self.page.locator("button").filter(has_text="Copy Link")
90
+
91
+ @property
92
+ def action_edit(self):
93
+ """Dropdown 'Edit' button."""
94
+ return self.page.locator("button").filter(has_text="Edit")
95
+
96
+ @property
97
+ def action_clone(self):
98
+ """Dropdown 'Clone' button."""
99
+ return self.page.locator("button").filter(has_text="Clone")
100
+
101
+ @property
102
+ def action_delete(self):
103
+ """Dropdown 'Delete' button."""
104
+ return self.page.locator("button").filter(has_text="Delete")
105
+
106
+ @property
107
+ def action_publish_to_marketplace(self):
108
+ """Dropdown 'Publish to Marketplace' button."""
109
+ return self.page.locator("button").filter(has_text="Publish to Marketplace")
110
+
111
+ @property
112
+ def updating_succesful_popup(self):
113
+ """Updating succesful popup."""
114
+ return self.page.locator(".codemie-toast .codemie-toast-header")
115
+
81
116
  # Navigation methods
82
117
  @step
83
118
  def navigate_to(self):
@@ -139,6 +174,27 @@ class AssistantsPage(BasePage):
139
174
  self.get_assistant_card_by_name(name).click()
140
175
  return self
141
176
 
177
+ @step
178
+ def action_dropdown_panel(self, name: str):
179
+ """Three dot menu."""
180
+ return self.get_assistant_card_by_name(name).locator(
181
+ "div.flex.items-center.relative"
182
+ )
183
+
184
+ @step
185
+ def click_assistant_view(self, name: str):
186
+ """Click on an assistant view by name."""
187
+ self.action_dropdown_panel(name).click()
188
+ self.action_view_details.click()
189
+ return self
190
+
191
+ @step
192
+ def click_assistant_edit(self, name: str):
193
+ """Click on an assistant edit by name."""
194
+ self.action_dropdown_panel(name).click()
195
+ self.action_edit.click()
196
+ return self
197
+
142
198
  # Verification methods
143
199
  @step
144
200
  def should_be_on_assistants_page(self):
@@ -195,3 +251,10 @@ class AssistantsPage(BasePage):
195
251
  """Verify that an assistant with specific name is not visible."""
196
252
  expect(self.get_assistant_card_by_name(name)).to_be_hidden()
197
253
  return self
254
+
255
+ @step
256
+ def should_see_updating_popup(self, text: str):
257
+ """Verify that an update popup is visible."""
258
+ expect(self.updating_succesful_popup).to_be_visible()
259
+ expect(self.updating_succesful_popup).to_have_text(text)
260
+ return self