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
|
@@ -7,11 +7,15 @@ from codemie_test_harness.tests.ui.pageobject.assistants.generate_with_ai_modal
|
|
|
7
7
|
AIAssistantGeneratorPage,
|
|
8
8
|
)
|
|
9
9
|
from codemie_test_harness.tests.ui.pageobject.base_page import BasePage
|
|
10
|
+
from tests.ui.pageobject.assistants.assistant_mcp_server import (
|
|
11
|
+
AssistantMCPIntegrationModal,
|
|
12
|
+
)
|
|
13
|
+
from tests.ui.pageobject.assistants.assistant_sidebar import AssistantSidebar
|
|
10
14
|
|
|
11
15
|
|
|
12
|
-
class
|
|
16
|
+
class CreateEditAssistantPage(BasePage):
|
|
13
17
|
"""
|
|
14
|
-
Create Assistant page object following Page Object Model (POM) best practices.
|
|
18
|
+
Create/Edit Assistant page object following Page Object Model (POM) best practices.
|
|
15
19
|
|
|
16
20
|
This class encapsulates all interactions with the Create Assistant page,
|
|
17
21
|
providing a clean interface for test automation while hiding implementation details.
|
|
@@ -23,6 +27,8 @@ class CreateAssistantPage(BasePage):
|
|
|
23
27
|
def __init__(self, page):
|
|
24
28
|
"""Initialize the Create Assistant page object."""
|
|
25
29
|
super().__init__(page)
|
|
30
|
+
self.sidebar = AssistantSidebar(page)
|
|
31
|
+
self.mcp = AssistantMCPIntegrationModal(page)
|
|
26
32
|
self.ai_generator_modal = AIAssistantGeneratorPage(
|
|
27
33
|
page
|
|
28
34
|
) # AI Assistant Generator modal
|
|
@@ -39,34 +45,37 @@ class CreateAssistantPage(BasePage):
|
|
|
39
45
|
@property
|
|
40
46
|
def page_title(self) -> Locator:
|
|
41
47
|
"""Page title 'Create Assistant' element"""
|
|
42
|
-
return (
|
|
43
|
-
|
|
44
|
-
or self.page.locator(
|
|
45
|
-
'div.text-h3.text-white.font-semibold:has-text("Create Assistant")'
|
|
46
|
-
)
|
|
47
|
-
or self.page.locator('h1:has-text("Create Assistant")')
|
|
48
|
+
return self.page.locator(
|
|
49
|
+
'h1.text-h3.text-text-main.font-semibold:has-text("Create Assistant")'
|
|
48
50
|
)
|
|
49
51
|
|
|
50
52
|
@property
|
|
51
53
|
def generate_with_ai_button(self) -> Locator:
|
|
52
54
|
"""Generate with AI button in header with magical styling"""
|
|
53
|
-
return (
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
)
|
|
57
|
-
or self.page.locator('button:has-text("Generate with AI")')
|
|
58
|
-
or self.page.locator("button:has(svg + text)").filter(
|
|
59
|
-
has_text="Generate with AI"
|
|
60
|
-
)
|
|
61
|
-
)
|
|
55
|
+
return self.page.locator(
|
|
56
|
+
'button.bg-magical-button:has-text("Generate with AI")'
|
|
57
|
+
).first
|
|
62
58
|
|
|
63
59
|
@property
|
|
64
60
|
def create_button(self) -> Locator:
|
|
65
61
|
"""Create button with plus icon (primary button)"""
|
|
66
|
-
return (
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
62
|
+
return self.page.locator('button.bg-button-primary-bg:has-text("Create")')
|
|
63
|
+
|
|
64
|
+
@property
|
|
65
|
+
def save_button(self) -> Locator:
|
|
66
|
+
"""Save button"""
|
|
67
|
+
return self.page.locator('button.bg-button-primary-bg:has-text("Save")')
|
|
68
|
+
|
|
69
|
+
@property
|
|
70
|
+
def cancel_button(self) -> Locator:
|
|
71
|
+
"""Cancel button"""
|
|
72
|
+
return self.page.locator('button.bg-button-secondary-bg:has-text("Cancel")')
|
|
73
|
+
|
|
74
|
+
@property
|
|
75
|
+
def back_button(self) -> Locator:
|
|
76
|
+
"""Back button"""
|
|
77
|
+
return self.page.locator(
|
|
78
|
+
'button.bg-button-secondary-bg:has(svg[viewBox="0 0 18 18"])'
|
|
70
79
|
)
|
|
71
80
|
|
|
72
81
|
# =============================================================================
|
|
@@ -79,82 +88,103 @@ class CreateAssistantPage(BasePage):
|
|
|
79
88
|
return self.page.locator('h4:has-text("Assistant Setup")')
|
|
80
89
|
|
|
81
90
|
@property
|
|
82
|
-
def
|
|
83
|
-
"""
|
|
84
|
-
return (
|
|
85
|
-
self.page.locator("div.p-multiselect#project")
|
|
86
|
-
or self.page.locator('[name="project"].p-multiselect')
|
|
87
|
-
or self.page.locator(".p-multiselect-label-container")
|
|
88
|
-
)
|
|
91
|
+
def shared_toggle(self) -> Locator:
|
|
92
|
+
"""'Shared with project' toggle switch - returns the label which is clickable"""
|
|
93
|
+
return self.page.locator('label:has-text("Shared with project")')
|
|
89
94
|
|
|
90
95
|
@property
|
|
91
|
-
def
|
|
92
|
-
"""'Shared with project'
|
|
93
|
-
return self.page.locator(
|
|
96
|
+
def shared_toggle_checkbox(self) -> Locator:
|
|
97
|
+
"""'Shared with project' checkbox input (hidden) - use for checking state only"""
|
|
98
|
+
return self.page.locator(
|
|
99
|
+
'label:has-text("Shared with project") input[type="checkbox"]'
|
|
100
|
+
)
|
|
94
101
|
|
|
95
102
|
@property
|
|
96
103
|
def name_input(self) -> Locator:
|
|
97
104
|
"""Assistant name input field with data-testid validation"""
|
|
98
|
-
return (
|
|
99
|
-
self.page.locator('input#name[data-testid="validation"]')
|
|
100
|
-
or self.page.locator('input[placeholder="Name*"]')
|
|
101
|
-
or self.page.locator('input[name="name"]')
|
|
102
|
-
)
|
|
105
|
+
return self.page.locator('input[placeholder="Name*"]')
|
|
103
106
|
|
|
104
107
|
@property
|
|
105
108
|
def slug_input(self) -> Locator:
|
|
106
109
|
"""Assistant slug input field"""
|
|
107
|
-
return (
|
|
108
|
-
self.page.locator('input#slug[data-testid="validation"]')
|
|
109
|
-
or self.page.locator(
|
|
110
|
-
'input[placeholder="Unique human-readable identifier"]'
|
|
111
|
-
)
|
|
112
|
-
or self.page.locator('input[name="slug"]')
|
|
113
|
-
)
|
|
110
|
+
return self.page.locator('input[name="slug"]')
|
|
114
111
|
|
|
115
112
|
@property
|
|
116
113
|
def icon_url_input(self) -> Locator:
|
|
117
114
|
"""Assistant icon URL input field"""
|
|
118
|
-
return (
|
|
119
|
-
self.page.locator('input#icon_url[data-testid="validation"]')
|
|
120
|
-
or self.page.locator('input[placeholder="URL to the assistant\'s icon"]')
|
|
121
|
-
or self.page.locator('input[name="icon_url"]')
|
|
122
|
-
)
|
|
115
|
+
return self.page.locator('input[name="icon_url"]')
|
|
123
116
|
|
|
124
117
|
@property
|
|
125
118
|
def description_textarea(self) -> Locator:
|
|
126
119
|
"""Assistant description textarea with placeholder"""
|
|
127
|
-
return (
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
120
|
+
return self.page.locator(
|
|
121
|
+
'textarea[name="description"][placeholder="Description*"]'
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
@property
|
|
125
|
+
def categories_dropdown(self) -> Locator:
|
|
126
|
+
"""Categories selection multiselect dropdown"""
|
|
127
|
+
return self.page.locator(
|
|
128
|
+
'div.p-multiselect:has(div.p-multiselect-label:has-text("Select categories"))'
|
|
131
129
|
)
|
|
132
130
|
|
|
133
131
|
@property
|
|
134
132
|
def conversation_starters_input(self) -> Locator:
|
|
135
133
|
"""Conversation starters input field with InputGroup"""
|
|
136
|
-
return (
|
|
137
|
-
self.page.locator("input.p-inputtext#conversationStarters-0")
|
|
138
|
-
or self.page.locator('input[name="conversationStarters"]')
|
|
139
|
-
or self.page.locator(".p-inputgroup input")
|
|
140
|
-
)
|
|
134
|
+
return self.page.locator('input[name="conversation_starters"]')
|
|
141
135
|
|
|
142
136
|
@property
|
|
143
137
|
def add_conversation_starter_button(self) -> Locator:
|
|
144
138
|
"""Add conversation starter button with plus icon"""
|
|
145
|
-
return (
|
|
146
|
-
|
|
147
|
-
or self.page.locator('button:has-text("Add"):has(svg)').first
|
|
148
|
-
or self.page.locator('.flex.justify-between button:has-text("Add")')
|
|
139
|
+
return self.page.locator(
|
|
140
|
+
'label:has-text("Conversation starters") + button:has-text("Add")'
|
|
149
141
|
)
|
|
150
142
|
|
|
151
143
|
@property
|
|
152
144
|
def delete_conversation_starter_button(self) -> Locator:
|
|
153
145
|
"""Delete conversation starter button (trash icon in InputGroup)"""
|
|
154
|
-
return (
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
146
|
+
return self.page.locator(".p-inputgroup button.bg-button-secondary-bg:has(svg)")
|
|
147
|
+
|
|
148
|
+
@property
|
|
149
|
+
def name_error_message(self):
|
|
150
|
+
"""Name field error message."""
|
|
151
|
+
return self.page.locator(
|
|
152
|
+
'label:has(input[name="name"]) div.text-sm.text-error-main.input-error-message'
|
|
153
|
+
)
|
|
154
|
+
|
|
155
|
+
@property
|
|
156
|
+
def icon_error_message(self):
|
|
157
|
+
"""Icon field error message."""
|
|
158
|
+
return self.page.locator(
|
|
159
|
+
'label:has(input[name="icon_url"]) div.text-sm.text-error-main.input-error-message'
|
|
160
|
+
)
|
|
161
|
+
|
|
162
|
+
@property
|
|
163
|
+
def description_error_message(self):
|
|
164
|
+
"""Description field error message."""
|
|
165
|
+
return self.page.locator(
|
|
166
|
+
'div:has(textarea[name="description"][placeholder="Description*"]) div.text-fire-50.text-sm'
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
@property
|
|
170
|
+
def system_prompt_error_message(self):
|
|
171
|
+
"""System prompt field error message."""
|
|
172
|
+
return self.page.locator(
|
|
173
|
+
'div:has(textarea[placeholder="System Instructions*"]) div.text-fire-50.text-sm'
|
|
174
|
+
)
|
|
175
|
+
|
|
176
|
+
@property
|
|
177
|
+
def temperature_error_message(self):
|
|
178
|
+
"""Temperature field error message."""
|
|
179
|
+
return self.page.locator(
|
|
180
|
+
'label:has(input[name="temperature"]) div.text-sm.text-error-main.input-error-message'
|
|
181
|
+
)
|
|
182
|
+
|
|
183
|
+
@property
|
|
184
|
+
def top_p_error_message(self):
|
|
185
|
+
"""Top P field error message."""
|
|
186
|
+
return self.page.locator(
|
|
187
|
+
'label:has(input[name="top_p"]) div.text-sm.text-error-main.input-error-message'
|
|
158
188
|
)
|
|
159
189
|
|
|
160
190
|
# =============================================================================
|
|
@@ -170,56 +200,201 @@ class CreateAssistantPage(BasePage):
|
|
|
170
200
|
def system_instructions_label(self) -> Locator:
|
|
171
201
|
"""System Instructions label"""
|
|
172
202
|
return self.page.locator(
|
|
173
|
-
'.text-sm.font-semibold:has-text("System Instructions")'
|
|
203
|
+
'p.text-sm.font-semibold:has-text("System Instructions")'
|
|
174
204
|
)
|
|
175
205
|
|
|
176
206
|
@property
|
|
177
207
|
def system_prompt_textarea(self) -> Locator:
|
|
178
208
|
"""System instructions textarea with full height"""
|
|
179
|
-
return (
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
209
|
+
return self.page.locator('textarea[placeholder="System Instructions*"]')
|
|
210
|
+
|
|
211
|
+
@property
|
|
212
|
+
def generate_with_ai_system_instructions_button(self) -> Locator:
|
|
213
|
+
"""Generate with AI button for system instructions"""
|
|
214
|
+
return self.page.locator(
|
|
215
|
+
'button.bg-magical-button:has-text("Generate with AI")'
|
|
216
|
+
).nth(1)
|
|
184
217
|
|
|
185
218
|
@property
|
|
186
219
|
def expand_system_prompt_button(self) -> Locator:
|
|
187
220
|
"""Expand system prompt button"""
|
|
188
|
-
return (
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
221
|
+
return self.page.locator('button.bg-button-secondary-bg:has-text("Expand")')
|
|
222
|
+
|
|
223
|
+
@property
|
|
224
|
+
def current_user_prompt_var_button(self) -> Locator:
|
|
225
|
+
"""Current User prompt variable button"""
|
|
226
|
+
return self.page.locator('button:has-text("Current User")')
|
|
227
|
+
|
|
228
|
+
@property
|
|
229
|
+
def date_prompt_var_button(self) -> Locator:
|
|
230
|
+
"""Date prompt variable button"""
|
|
231
|
+
return self.page.locator('button:has-text("Date")')
|
|
232
|
+
|
|
233
|
+
@property
|
|
234
|
+
def manage_prompt_vars_button(self) -> Locator:
|
|
235
|
+
"""Manage Prompt Vars button"""
|
|
236
|
+
return self.page.locator(
|
|
237
|
+
'button.bg-button-primary-bg:has-text("Manage Prompt Vars")'
|
|
192
238
|
)
|
|
193
239
|
|
|
194
240
|
@property
|
|
195
|
-
def
|
|
196
|
-
"""LLM model
|
|
197
|
-
return (
|
|
198
|
-
|
|
199
|
-
or self.page.locator('[name="model_type"].p-multiselect')
|
|
200
|
-
or self.page.locator(
|
|
201
|
-
'.p-multiselect:has(.p-multiselect-label:has-text("Default LLM Model"))'
|
|
202
|
-
)
|
|
241
|
+
def llm_model_dropdown(self) -> Locator:
|
|
242
|
+
"""LLM model selection dropdown"""
|
|
243
|
+
return self.page.locator(
|
|
244
|
+
'div.p-multiselect:has(div.p-multiselect-label:has-text("Default: GPT-4.1"))'
|
|
203
245
|
)
|
|
204
246
|
|
|
205
247
|
@property
|
|
206
248
|
def temperature_input(self) -> Locator:
|
|
207
249
|
"""Temperature input field (0-2 range)"""
|
|
208
|
-
return (
|
|
209
|
-
self.page.locator('input#temperature[data-testid="validation"]')
|
|
210
|
-
or self.page.locator('input[placeholder="0-2"]')
|
|
211
|
-
or self.page.locator('input[name="temperature"]')
|
|
212
|
-
)
|
|
250
|
+
return self.page.locator('input[name="temperature"][placeholder="0-2"]')
|
|
213
251
|
|
|
214
252
|
@property
|
|
215
253
|
def top_p_input(self) -> Locator:
|
|
216
254
|
"""Top P input field (0-1 range)"""
|
|
255
|
+
return self.page.locator('input[name="top_p"][placeholder="0-1"]')
|
|
256
|
+
|
|
257
|
+
# ==================== DataSource Context ====================
|
|
258
|
+
@property
|
|
259
|
+
def datasource_context_label(self):
|
|
260
|
+
"""Label 'Datasource Context' above the dropdown."""
|
|
261
|
+
return self.page.locator(
|
|
262
|
+
'div.text-xs.text-text-secondary:has-text("Datasource Context")'
|
|
263
|
+
)
|
|
264
|
+
|
|
265
|
+
@property
|
|
266
|
+
def datasource_context_add_button(self):
|
|
267
|
+
"""'Add' button next to the Datasource Context label."""
|
|
268
|
+
return self.page.locator(
|
|
269
|
+
'div:has-text("Datasource Context") + button:has-text("Add")'
|
|
270
|
+
)
|
|
271
|
+
|
|
272
|
+
@property
|
|
273
|
+
def datasource_context_dropdown(self):
|
|
274
|
+
"""Datasource Context dropdown (multiselect input)."""
|
|
275
|
+
return self.page.locator("div.p-multiselect#context-selector")
|
|
276
|
+
|
|
277
|
+
@property
|
|
278
|
+
def datasource_context_dropdown_label(self):
|
|
279
|
+
"""The visible label text inside the datasource context dropdown."""
|
|
280
|
+
return self.datasource_context_dropdown.locator("div.p-multiselect-label")
|
|
281
|
+
|
|
282
|
+
# ==================== Sub-Assistant Context ====================
|
|
283
|
+
@property
|
|
284
|
+
def sub_assistants_label(self):
|
|
285
|
+
"""Label 'Sub-Assistants' above the sub assistants dropdown."""
|
|
286
|
+
return self.page.get_by_text("Sub-Assistants", exact=True)
|
|
287
|
+
|
|
288
|
+
@property
|
|
289
|
+
def sub_assistants_dropdown(self):
|
|
290
|
+
"""Sub Assistants dropdown (multiselect input)."""
|
|
291
|
+
return self.page.locator(
|
|
292
|
+
'div.p-multiselect:has(div.p-multiselect-label:has-text("Select Sub-Assistants"))'
|
|
293
|
+
)
|
|
294
|
+
|
|
295
|
+
@property
|
|
296
|
+
def sub_assistants_dropdown_label(self):
|
|
297
|
+
"""The label inside the sub assistants dropdown."""
|
|
298
|
+
return self.sub_assistants_dropdown.locator("div.p-multiselect-label")
|
|
299
|
+
|
|
300
|
+
# ======= Tools Accordion Locators =======
|
|
301
|
+
|
|
302
|
+
@property
|
|
303
|
+
def available_tools_accordion(self) -> Locator:
|
|
304
|
+
"""Available Tools accordion section"""
|
|
305
|
+
return self.page.locator(
|
|
306
|
+
'div.p-accordion-header:has(h1.font-bold:has-text("Available Tools"))'
|
|
307
|
+
)
|
|
308
|
+
|
|
309
|
+
@property
|
|
310
|
+
def external_tools_accordion(self) -> Locator:
|
|
311
|
+
"""External Tools accordion section"""
|
|
312
|
+
return self.page.locator(
|
|
313
|
+
'div.p-accordion-header:has(h1.font-bold:has-text("External Tools"))'
|
|
314
|
+
)
|
|
315
|
+
|
|
316
|
+
@step
|
|
317
|
+
def section(self, name: str) -> Locator:
|
|
318
|
+
"""Get main accordion section (Available Tools or External Tools) by name"""
|
|
319
|
+
return self.page.locator(
|
|
320
|
+
f'div.p-accordion-header:has(h1.font-bold.text-text-quaternary:has-text("{name}"))'
|
|
321
|
+
)
|
|
322
|
+
|
|
323
|
+
@step
|
|
324
|
+
def toolkit(self, section_name: str, toolkit_name: str) -> Locator:
|
|
325
|
+
"""Get toolkit accordion header within a section by name
|
|
326
|
+
Handles both spaced and non-spaced toolkit names (e.g., 'Open API' vs 'OpenAPI')"""
|
|
327
|
+
# Try the exact name first, if not found try without spaces
|
|
328
|
+
toolkit_name_no_space = toolkit_name.replace(" ", "")
|
|
329
|
+
# Look for h2 with the toolkit name, which is inside the accordion header
|
|
330
|
+
return self.page.locator(
|
|
331
|
+
f'div.p-accordion-header:has(h2.font-medium:text-is("{toolkit_name}")),'
|
|
332
|
+
f'div.p-accordion-header:has(h2.font-medium:text-is("{toolkit_name_no_space}"))'
|
|
333
|
+
).first
|
|
334
|
+
|
|
335
|
+
@step
|
|
336
|
+
def tool_rows_under_toolkit(self, toolkit_name: str) -> Locator:
|
|
337
|
+
"""Returns all tool rows (each with a label and a checkbox) under an expanded toolkit panel."""
|
|
338
|
+
panel = self.page.locator(
|
|
339
|
+
"div.p-accordion-tab.p-accordion-tab-active div.p-accordion-content"
|
|
340
|
+
).filter(has_text=toolkit_name)
|
|
341
|
+
return panel.locator(
|
|
342
|
+
"div.grid.grid-cols-[auto,1fr]"
|
|
343
|
+
) # Adapt selector to match tool row
|
|
344
|
+
|
|
345
|
+
@step
|
|
346
|
+
def tool_label_spans_under_toolkit(self, tool: str) -> Locator:
|
|
347
|
+
tool_stripped = tool.strip()
|
|
348
|
+
if "mcp" in tool_stripped.lower():
|
|
349
|
+
return (
|
|
350
|
+
self.page.locator("div.p-accordion-tab-active div.p-accordion-content")
|
|
351
|
+
.locator('button, [id*="mcp"]')
|
|
352
|
+
.filter(has_text=tool_stripped)
|
|
353
|
+
.first
|
|
354
|
+
)
|
|
355
|
+
return self.page.locator(
|
|
356
|
+
"div.p-accordion-tab-active div.p-accordion-content label"
|
|
357
|
+
).get_by_text(tool_stripped, exact=True)
|
|
358
|
+
|
|
359
|
+
@step
|
|
360
|
+
def tool_checkbox(self, tool_name: str) -> Locator:
|
|
217
361
|
return (
|
|
218
|
-
self.page.locator(
|
|
219
|
-
|
|
220
|
-
|
|
362
|
+
self.page.locator("span")
|
|
363
|
+
.filter(has_text=tool_name)
|
|
364
|
+
.locator("xpath=..")
|
|
365
|
+
.locator("input[type=checkbox]")
|
|
221
366
|
)
|
|
222
367
|
|
|
368
|
+
@step
|
|
369
|
+
def tool_row(self, tool_name: str) -> Locator:
|
|
370
|
+
return (
|
|
371
|
+
self.page.locator("span")
|
|
372
|
+
.filter(has_text=tool_name)
|
|
373
|
+
.locator("xpath=ancestor::div[contains(@class,'grid')]")
|
|
374
|
+
)
|
|
375
|
+
|
|
376
|
+
@step
|
|
377
|
+
def select_section(self, section_name: str):
|
|
378
|
+
self.section(section_name).click()
|
|
379
|
+
return self
|
|
380
|
+
|
|
381
|
+
@step
|
|
382
|
+
def select_toolkit(self, section: str, toolkit: str):
|
|
383
|
+
self.toolkit(section, toolkit).click()
|
|
384
|
+
return self
|
|
385
|
+
|
|
386
|
+
@step
|
|
387
|
+
def should_be_visible_tool(self, tool: str):
|
|
388
|
+
expect(self.tool_label_spans_under_toolkit(tool)).to_be_visible()
|
|
389
|
+
return self
|
|
390
|
+
|
|
391
|
+
@step
|
|
392
|
+
def select_tool(self, tool: str):
|
|
393
|
+
self.tool_label_spans_under_toolkit(tool).click()
|
|
394
|
+
if self.mcp.is_pop_visible():
|
|
395
|
+
self.mcp.fill_mcp_server_base_form()
|
|
396
|
+
return self
|
|
397
|
+
|
|
223
398
|
# ==================== NAVIGATION METHODS ====================
|
|
224
399
|
|
|
225
400
|
@step
|
|
@@ -444,8 +619,8 @@ class CreateAssistantPage(BasePage):
|
|
|
444
619
|
Returns:
|
|
445
620
|
self: Returns the page object for method chaining
|
|
446
621
|
"""
|
|
447
|
-
# Check current state and toggle if needed
|
|
448
|
-
is_currently_checked = self.
|
|
622
|
+
# Check current state using the hidden checkbox and toggle if needed by clicking the label
|
|
623
|
+
is_currently_checked = self.shared_toggle_checkbox.is_checked()
|
|
449
624
|
if (shared and not is_currently_checked) or (
|
|
450
625
|
not shared and is_currently_checked
|
|
451
626
|
):
|
|
@@ -495,6 +670,17 @@ class CreateAssistantPage(BasePage):
|
|
|
495
670
|
self.create_button.click()
|
|
496
671
|
return self
|
|
497
672
|
|
|
673
|
+
@step
|
|
674
|
+
def click_save(self):
|
|
675
|
+
"""
|
|
676
|
+
Click the Create button to create the assistant.
|
|
677
|
+
|
|
678
|
+
Returns:
|
|
679
|
+
self: Returns the page object for method chaining
|
|
680
|
+
"""
|
|
681
|
+
self.save_button.click()
|
|
682
|
+
return self
|
|
683
|
+
|
|
498
684
|
@step
|
|
499
685
|
def click_cancel(self):
|
|
500
686
|
"""
|
|
@@ -528,6 +714,27 @@ class CreateAssistantPage(BasePage):
|
|
|
528
714
|
self.generate_with_ai_button.click()
|
|
529
715
|
return self
|
|
530
716
|
|
|
717
|
+
def select_tool_option(self, toolkit_name: str, label: str, checked: bool = True):
|
|
718
|
+
self.expand_toolkit(toolkit_name)
|
|
719
|
+
checkbox = self.toolkit_checkbox_by_label(toolkit_name, label)
|
|
720
|
+
if checked != checkbox.is_checked():
|
|
721
|
+
checkbox.click()
|
|
722
|
+
return self
|
|
723
|
+
|
|
724
|
+
def get_all_tools_in_toolkit(self, toolkit_name: str):
|
|
725
|
+
"""
|
|
726
|
+
Returns a list of tuples (label, checked) for all tool features inside a toolkit accordion.
|
|
727
|
+
"""
|
|
728
|
+
panel = self.toolkit_accordion_panel(toolkit_name)
|
|
729
|
+
labels = panel.locator(".checkbox-label span.text-sm").all_text_contents()
|
|
730
|
+
checkboxes = panel.locator('input[type="checkbox"]')
|
|
731
|
+
return [
|
|
732
|
+
(lbl, checkboxes.nth(idx).is_checked()) for idx, lbl in enumerate(labels)
|
|
733
|
+
]
|
|
734
|
+
|
|
735
|
+
def toolkit_select_all_checkbox(self, toolkit_name: str):
|
|
736
|
+
return self.toolkit_checkbox_by_label(toolkit_name, "Select all")
|
|
737
|
+
|
|
531
738
|
# ==================== COMPREHENSIVE ASSISTANT CREATION METHOD ====================
|
|
532
739
|
|
|
533
740
|
@step
|
|
@@ -594,6 +801,7 @@ class CreateAssistantPage(BasePage):
|
|
|
594
801
|
def should_have_all_form_fields_visible(self):
|
|
595
802
|
"""Verify that all essential form fields are visible."""
|
|
596
803
|
expect(self.name_input).to_be_visible()
|
|
804
|
+
expect(self.slug_input).to_be_visible()
|
|
597
805
|
expect(self.description_textarea).to_be_visible()
|
|
598
806
|
expect(self.system_prompt_textarea).to_be_visible()
|
|
599
807
|
return self
|
|
@@ -623,6 +831,12 @@ class CreateAssistantPage(BasePage):
|
|
|
623
831
|
expect(self.system_prompt_textarea).to_have_value(expected_prompt)
|
|
624
832
|
return self
|
|
625
833
|
|
|
834
|
+
@step
|
|
835
|
+
def should_have_categories_visible(self):
|
|
836
|
+
"""Verify categories dropdown is visible.."""
|
|
837
|
+
expect(self.categories_dropdown).to_be_visible()
|
|
838
|
+
return self
|
|
839
|
+
|
|
626
840
|
@step
|
|
627
841
|
def should_have_icon_url_value(self, expected_url: str):
|
|
628
842
|
"""Verify icon URL field has expected value."""
|
|
@@ -632,13 +846,13 @@ class CreateAssistantPage(BasePage):
|
|
|
632
846
|
@step
|
|
633
847
|
def should_have_shared_checked(self):
|
|
634
848
|
"""Verify shared toggle is checked."""
|
|
635
|
-
expect(self.
|
|
849
|
+
expect(self.shared_toggle_checkbox).to_be_checked()
|
|
636
850
|
return self
|
|
637
851
|
|
|
638
852
|
@step
|
|
639
853
|
def should_have_shared_unchecked(self):
|
|
640
854
|
"""Verify shared toggle is unchecked."""
|
|
641
|
-
expect(self.
|
|
855
|
+
expect(self.shared_toggle_checkbox).not_to_be_checked()
|
|
642
856
|
return self
|
|
643
857
|
|
|
644
858
|
@step
|
|
@@ -667,3 +881,73 @@ class CreateAssistantPage(BasePage):
|
|
|
667
881
|
expect(self.system_prompt_textarea).to_have_value("")
|
|
668
882
|
expect(self.icon_url_input).to_have_value("")
|
|
669
883
|
return self
|
|
884
|
+
|
|
885
|
+
@step
|
|
886
|
+
def should_have_top_p_and_temperature(self):
|
|
887
|
+
"""Verify top p and temperature fields are visible."""
|
|
888
|
+
expect(self.temperature_input).to_be_visible()
|
|
889
|
+
expect(self.top_p_input).to_be_visible()
|
|
890
|
+
return self
|
|
891
|
+
|
|
892
|
+
@step
|
|
893
|
+
def should_have_top_p_and_temperature_value(self, temperature: str, top_p: str):
|
|
894
|
+
"""Verify top p and temperature field values is visible."""
|
|
895
|
+
expect(self.temperature_input).to_have_value(temperature)
|
|
896
|
+
expect(self.top_p_input).to_have_value(top_p)
|
|
897
|
+
|
|
898
|
+
@step
|
|
899
|
+
def should_have_datasource_context(self):
|
|
900
|
+
"""Verify datasource context fields are visible."""
|
|
901
|
+
expect(self.datasource_context_label).to_be_visible()
|
|
902
|
+
expect(self.datasource_context_add_button).to_be_visible()
|
|
903
|
+
expect(self.datasource_context_dropdown).to_be_visible()
|
|
904
|
+
return self
|
|
905
|
+
|
|
906
|
+
@step
|
|
907
|
+
def should_have_sub_assistants_context(self):
|
|
908
|
+
"""Verify sub assistants context fields are visible."""
|
|
909
|
+
expect(self.sub_assistants_label).to_be_visible()
|
|
910
|
+
expect(self.sub_assistants_dropdown).to_be_visible()
|
|
911
|
+
return self
|
|
912
|
+
|
|
913
|
+
@step
|
|
914
|
+
def should_have_name_error_textarea(self, error_message: str):
|
|
915
|
+
"""Verify name error text field is visible."""
|
|
916
|
+
expect(self.name_error_message).to_be_visible()
|
|
917
|
+
expect(self.name_error_message).to_have_text(error_message)
|
|
918
|
+
return self
|
|
919
|
+
|
|
920
|
+
@step
|
|
921
|
+
def should_have_description_error_textarea(self, error_message: str):
|
|
922
|
+
"""Verify description error text field is visible."""
|
|
923
|
+
expect(self.description_error_message).to_be_visible()
|
|
924
|
+
expect(self.description_error_message).to_have_text(error_message)
|
|
925
|
+
return self
|
|
926
|
+
|
|
927
|
+
@step
|
|
928
|
+
def should_have_system_prompt_error_textarea(self, error_message: str):
|
|
929
|
+
"""Verify system prompt error text field is visible."""
|
|
930
|
+
expect(self.system_prompt_error_message).to_be_visible()
|
|
931
|
+
expect(self.system_prompt_error_message).to_have_text(error_message)
|
|
932
|
+
return self
|
|
933
|
+
|
|
934
|
+
@step
|
|
935
|
+
def should_have_icon_error_textarea(self, error_message: str):
|
|
936
|
+
"""Verify icon error text field is visible."""
|
|
937
|
+
expect(self.icon_error_message).to_be_visible()
|
|
938
|
+
expect(self.icon_error_message).to_have_text(error_message)
|
|
939
|
+
return self
|
|
940
|
+
|
|
941
|
+
@step
|
|
942
|
+
def should_have_temperature_error_textarea(self, error_message: str):
|
|
943
|
+
"""Verify temperature error text field is visible."""
|
|
944
|
+
expect(self.temperature_error_message).to_be_visible()
|
|
945
|
+
expect(self.temperature_error_message).to_have_text(error_message)
|
|
946
|
+
return self
|
|
947
|
+
|
|
948
|
+
@step
|
|
949
|
+
def should_have_top_p_error_textarea(self, error_message: str):
|
|
950
|
+
"""Verify top p error text field is visible."""
|
|
951
|
+
expect(self.top_p_error_message).to_be_visible()
|
|
952
|
+
expect(self.top_p_error_message).to_have_text(error_message)
|
|
953
|
+
return self
|