codemie-test-harness 0.1.206__py3-none-any.whl → 0.1.207__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.
- codemie_test_harness/tests/ui/assistants/test_create_assistant.py +13 -13
- codemie_test_harness/tests/ui/assistants/test_edit_assistant.py +200 -0
- codemie_test_harness/tests/ui/pageobject/assistants/assistant_mcp_server.py +171 -0
- codemie_test_harness/tests/ui/pageobject/assistants/assistant_sidebar.py +140 -0
- codemie_test_harness/tests/ui/pageobject/assistants/assistant_view_page.py +256 -0
- codemie_test_harness/tests/ui/pageobject/assistants/assistants_page.py +63 -0
- codemie_test_harness/tests/ui/pageobject/assistants/{create_assistant_page.py → create_edit_assistant_page.py} +379 -95
- codemie_test_harness/tests/ui/test_data/assistant_test_data.py +347 -18
- {codemie_test_harness-0.1.206.dist-info → codemie_test_harness-0.1.207.dist-info}/METADATA +2 -2
- {codemie_test_harness-0.1.206.dist-info → codemie_test_harness-0.1.207.dist-info}/RECORD +12 -8
- {codemie_test_harness-0.1.206.dist-info → codemie_test_harness-0.1.207.dist-info}/WHEEL +0 -0
- {codemie_test_harness-0.1.206.dist-info → codemie_test_harness-0.1.207.dist-info}/entry_points.txt +0 -0
|
@@ -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
|