codemie-test-harness 0.1.172__py3-none-any.whl → 0.1.174__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.
- codemie_test_harness/tests/ui/_test_data/__init__.py +0 -0
- codemie_test_harness/tests/ui/_test_data/integration_test_data.py +121 -0
- codemie_test_harness/tests/ui/assistants/test_create_assistant.py +1 -1
- codemie_test_harness/tests/ui/conftest.py +25 -0
- codemie_test_harness/tests/ui/integrations/__init__.py +0 -0
- codemie_test_harness/tests/ui/integrations/test_create_integration.py +320 -0
- codemie_test_harness/tests/ui/pageobject/assistants/create_assistant_page.py +0 -20
- codemie_test_harness/tests/ui/pageobject/base_page.py +19 -6
- codemie_test_harness/tests/ui/pageobject/components/integration_row.py +299 -0
- codemie_test_harness/tests/ui/pageobject/integrations/create_integration_page.py +772 -0
- codemie_test_harness/tests/ui/pageobject/integrations/integrations_page.py +434 -0
- codemie_test_harness-0.1.174.dist-info/METADATA +567 -0
- {codemie_test_harness-0.1.172.dist-info → codemie_test_harness-0.1.174.dist-info}/RECORD +16 -9
- codemie_test_harness-0.1.172.dist-info/METADATA +0 -306
- /codemie_test_harness/tests/{test_data → ui/_test_data}/assistant_test_data.py +0 -0
- {codemie_test_harness-0.1.172.dist-info → codemie_test_harness-0.1.174.dist-info}/WHEEL +0 -0
- {codemie_test_harness-0.1.172.dist-info → codemie_test_harness-0.1.174.dist-info}/entry_points.txt +0 -0
|
@@ -0,0 +1,434 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Integrations page object for managing integrations.
|
|
3
|
+
Contains methods for navigating to integrations, viewing integration lists, and interacting with integrations.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from playwright.sync_api import expect, Locator
|
|
7
|
+
from reportportal_client import step
|
|
8
|
+
|
|
9
|
+
from codemie_test_harness.tests.ui.pageobject.base_page import BasePage
|
|
10
|
+
from codemie_test_harness.tests.ui.pageobject.components.integration_row import (
|
|
11
|
+
IntegrationRow,
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
page_url = "/#/integrations?tab=integrations"
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
# noinspection PyArgumentList
|
|
18
|
+
class IntegrationsPage(BasePage):
|
|
19
|
+
"""Integrations page with property-based element locators and comprehensive functionality."""
|
|
20
|
+
|
|
21
|
+
def __init__(self, page):
|
|
22
|
+
super().__init__(page)
|
|
23
|
+
|
|
24
|
+
# Page-specific properties
|
|
25
|
+
@property
|
|
26
|
+
def page_title(self):
|
|
27
|
+
"""Page title element."""
|
|
28
|
+
return self.page.locator("h2.text-text-main")
|
|
29
|
+
|
|
30
|
+
@property
|
|
31
|
+
def create_button(self):
|
|
32
|
+
"""Create Integration button."""
|
|
33
|
+
return self.page.locator("button").filter(has_text="Create")
|
|
34
|
+
|
|
35
|
+
@property
|
|
36
|
+
def user_type_button(self):
|
|
37
|
+
"""Integration type User button."""
|
|
38
|
+
return self.page.locator(".p-button").filter(has_text="User")
|
|
39
|
+
|
|
40
|
+
@property
|
|
41
|
+
def project_type_button(self):
|
|
42
|
+
"""Integration type Project button."""
|
|
43
|
+
return self.page.locator(".p-button").filter(has_text="Project")
|
|
44
|
+
|
|
45
|
+
@property
|
|
46
|
+
def create_user_integration_menu_item(self):
|
|
47
|
+
"""Create User Integration menu item in the tiered menu."""
|
|
48
|
+
return self.page.locator(
|
|
49
|
+
'#null_overlay_list li[aria-label="Create User Integration"] .p-menuitem-link,'
|
|
50
|
+
'li[role="menuitem"]:has(.p-menuitem-text:text("Create User Integration")) .p-menuitem-link'
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
@property
|
|
54
|
+
def create_project_integration_menu_item(self):
|
|
55
|
+
"""Create Project Integration menu item in the tiered menu."""
|
|
56
|
+
return self.page.locator(
|
|
57
|
+
'#null_overlay_list li[aria-label="Create Project Integration"] .p-menuitem-link,'
|
|
58
|
+
'li[role="menuitem"]:has(.p-menuitem-text:text("Create Project Integration")) .p-menuitem-link'
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
@property
|
|
62
|
+
def tiered_menu_overlay(self):
|
|
63
|
+
"""Tiered menu overlay containing integration creation options."""
|
|
64
|
+
return self.page.locator("#null_overlay_list, .p-tieredmenu-root-list")
|
|
65
|
+
|
|
66
|
+
@property
|
|
67
|
+
def search_input(self):
|
|
68
|
+
"""Search input field for filtering integrations."""
|
|
69
|
+
return self.page.locator(
|
|
70
|
+
'input[placeholder*="Search"], input[data-testid="search-input"]'
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
@property
|
|
74
|
+
def filter_dropdown(self):
|
|
75
|
+
"""Filter dropdown for integration types."""
|
|
76
|
+
return self.page.locator(
|
|
77
|
+
'.filter-dropdown, select[data-testid="filter-select"]'
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
@property
|
|
81
|
+
def integrations_table(self):
|
|
82
|
+
"""All integration rows on the page."""
|
|
83
|
+
return self.page.locator("table.w-full")
|
|
84
|
+
|
|
85
|
+
@property
|
|
86
|
+
def no_integrations_message(self):
|
|
87
|
+
"""Message displayed when no integrations are found."""
|
|
88
|
+
return self.page.locator(
|
|
89
|
+
'.no-results, .empty-state, [data-testid="no-integrations"]'
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
@property
|
|
93
|
+
def toast_message(self) -> Locator:
|
|
94
|
+
"""Toast message after operation."""
|
|
95
|
+
return self.page.locator(".codemie-toast-info")
|
|
96
|
+
|
|
97
|
+
@property
|
|
98
|
+
def pagination_container(self):
|
|
99
|
+
"""Pagination controls container."""
|
|
100
|
+
return self.page.locator('.pagination, [data-testid="pagination"]')
|
|
101
|
+
|
|
102
|
+
@property
|
|
103
|
+
def items_per_page_selector(self):
|
|
104
|
+
"""Items per page selector."""
|
|
105
|
+
return self.page.locator(
|
|
106
|
+
'.items-per-page, select[data-testid="items-per-page"]'
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
# Navigation and initialization methods
|
|
110
|
+
@step
|
|
111
|
+
def navigate_to(self):
|
|
112
|
+
"""Navigate to the integrations page."""
|
|
113
|
+
self.page.goto(page_url)
|
|
114
|
+
self.wait_for_page_load()
|
|
115
|
+
return self
|
|
116
|
+
|
|
117
|
+
@step
|
|
118
|
+
def navigate_to_via_menu(self):
|
|
119
|
+
"""Navigate to integrations page using header navigation."""
|
|
120
|
+
self.go_to_integrations_page()
|
|
121
|
+
self.wait_for_page_load()
|
|
122
|
+
return self
|
|
123
|
+
|
|
124
|
+
# Integration management actions
|
|
125
|
+
@step
|
|
126
|
+
def click_create_integration(self):
|
|
127
|
+
"""Click the Create Integration button to start creating a new integration."""
|
|
128
|
+
self.create_button.click()
|
|
129
|
+
return self
|
|
130
|
+
|
|
131
|
+
@step
|
|
132
|
+
def navigate_to_user_integration_creation(self):
|
|
133
|
+
"""
|
|
134
|
+
Navigate to the User Integration creation page.
|
|
135
|
+
|
|
136
|
+
This method clicks the Create button to open the tiered menu,
|
|
137
|
+
then selects "Create User Integration" from the dropdown menu.
|
|
138
|
+
|
|
139
|
+
Returns:
|
|
140
|
+
self: For method chaining
|
|
141
|
+
"""
|
|
142
|
+
# Click the Create button to open the tiered menu
|
|
143
|
+
self.create_button.click()
|
|
144
|
+
|
|
145
|
+
# Wait for the tiered menu to appear
|
|
146
|
+
self.tiered_menu_overlay.wait_for(state="visible", timeout=5000)
|
|
147
|
+
|
|
148
|
+
# Click on "Create User Integration" menu item
|
|
149
|
+
self.create_user_integration_menu_item.click()
|
|
150
|
+
|
|
151
|
+
# Wait for navigation to complete
|
|
152
|
+
self.wait_for_page_load()
|
|
153
|
+
|
|
154
|
+
return self
|
|
155
|
+
|
|
156
|
+
@step
|
|
157
|
+
def navigate_to_project_integration_creation(self):
|
|
158
|
+
"""
|
|
159
|
+
Navigate to the Project Integration creation page.
|
|
160
|
+
|
|
161
|
+
This method clicks the Create button to open the tiered menu,
|
|
162
|
+
then selects "Create Project Integration" from the dropdown menu.
|
|
163
|
+
|
|
164
|
+
Returns:
|
|
165
|
+
self: For method chaining
|
|
166
|
+
"""
|
|
167
|
+
# Click the Create button to open the tiered menu
|
|
168
|
+
self.create_button.click()
|
|
169
|
+
|
|
170
|
+
# Wait for the tiered menu to appear
|
|
171
|
+
self.tiered_menu_overlay.wait_for(state="visible", timeout=5000)
|
|
172
|
+
|
|
173
|
+
# Click on "Create Project Integration" menu item
|
|
174
|
+
self.create_project_integration_menu_item.click()
|
|
175
|
+
|
|
176
|
+
# Wait for navigation to complete
|
|
177
|
+
self.wait_for_page_load()
|
|
178
|
+
|
|
179
|
+
return self
|
|
180
|
+
|
|
181
|
+
@step
|
|
182
|
+
def search_integrations(self, search_term: str):
|
|
183
|
+
"""
|
|
184
|
+
Search for integrations using the search input.
|
|
185
|
+
|
|
186
|
+
Args:
|
|
187
|
+
search_term (str): The term to search for
|
|
188
|
+
"""
|
|
189
|
+
self.search_input.clear()
|
|
190
|
+
self.search_input.fill(search_term)
|
|
191
|
+
self.search_input.press("Enter")
|
|
192
|
+
self.wait_for_page_load()
|
|
193
|
+
return self
|
|
194
|
+
|
|
195
|
+
@step
|
|
196
|
+
def clear_search(self):
|
|
197
|
+
"""Clear the search input."""
|
|
198
|
+
self.search_input.clear()
|
|
199
|
+
self.search_input.press("Enter")
|
|
200
|
+
self.wait_for_page_load()
|
|
201
|
+
return self
|
|
202
|
+
|
|
203
|
+
@step
|
|
204
|
+
def select_filter(self, filter_type: str):
|
|
205
|
+
"""
|
|
206
|
+
Select a filter type from the filter dropdown.
|
|
207
|
+
|
|
208
|
+
Args:
|
|
209
|
+
filter_type (str): The integration type to filter by (e.g., "AWS", "JIRA", "GIT")
|
|
210
|
+
"""
|
|
211
|
+
self.filter_dropdown.click()
|
|
212
|
+
self.page.locator(
|
|
213
|
+
f'option[value="{filter_type}"], .filter-option:has-text("{filter_type}")'
|
|
214
|
+
).click()
|
|
215
|
+
self.wait_for_page_load()
|
|
216
|
+
return self
|
|
217
|
+
|
|
218
|
+
@step
|
|
219
|
+
def clear_filters(self):
|
|
220
|
+
"""Clear all applied filters."""
|
|
221
|
+
clear_filters_btn = self.page.locator(
|
|
222
|
+
'button:has-text("Clear Filters"), [data-testid="clear-filters"]'
|
|
223
|
+
)
|
|
224
|
+
if clear_filters_btn.is_visible():
|
|
225
|
+
clear_filters_btn.click()
|
|
226
|
+
self.wait_for_page_load()
|
|
227
|
+
return self
|
|
228
|
+
|
|
229
|
+
# Integration card interactions
|
|
230
|
+
@step
|
|
231
|
+
def get_integration_card_by_name(self, integration_name: str) -> IntegrationRow:
|
|
232
|
+
"""
|
|
233
|
+
Get an integration card by its name.
|
|
234
|
+
|
|
235
|
+
Args:
|
|
236
|
+
integration_name (str): The name of the integration
|
|
237
|
+
|
|
238
|
+
Returns:
|
|
239
|
+
IntegrationRow: The integration card component
|
|
240
|
+
"""
|
|
241
|
+
card_locator = self.integrations_table.filter(has_text=integration_name).first
|
|
242
|
+
return IntegrationRow(self.page, card_locator)
|
|
243
|
+
|
|
244
|
+
@step
|
|
245
|
+
def get_all_integration_cards(self) -> list[IntegrationRow]:
|
|
246
|
+
"""
|
|
247
|
+
Get all visible integration cards.
|
|
248
|
+
|
|
249
|
+
Returns:
|
|
250
|
+
list[IntegrationRow]: List of all integration card components
|
|
251
|
+
"""
|
|
252
|
+
cards = []
|
|
253
|
+
count = self.integrations_table.count()
|
|
254
|
+
for i in range(count):
|
|
255
|
+
card_locator = self.integrations_table.nth(i)
|
|
256
|
+
cards.append(IntegrationRow(self.page, card_locator))
|
|
257
|
+
return cards
|
|
258
|
+
|
|
259
|
+
# Pagination methods
|
|
260
|
+
@step
|
|
261
|
+
def navigate_to_page(self, page_number: int):
|
|
262
|
+
"""
|
|
263
|
+
Navigate to a specific page in pagination.
|
|
264
|
+
|
|
265
|
+
Args:
|
|
266
|
+
page_number (int): The page number to navigate to
|
|
267
|
+
"""
|
|
268
|
+
page_btn = self.pagination_container.locator(
|
|
269
|
+
f'button:has-text("{page_number}"), a:has-text("{page_number}")'
|
|
270
|
+
)
|
|
271
|
+
page_btn.click()
|
|
272
|
+
self.wait_for_page_load()
|
|
273
|
+
return self
|
|
274
|
+
|
|
275
|
+
@step
|
|
276
|
+
def go_to_next_page(self):
|
|
277
|
+
"""Navigate to the next page."""
|
|
278
|
+
next_btn = self.pagination_container.locator(
|
|
279
|
+
'button:has-text("Next"), [data-testid="next-page"]'
|
|
280
|
+
)
|
|
281
|
+
if next_btn.is_enabled():
|
|
282
|
+
next_btn.click()
|
|
283
|
+
self.wait_for_page_load()
|
|
284
|
+
return self
|
|
285
|
+
|
|
286
|
+
@step
|
|
287
|
+
def go_to_previous_page(self):
|
|
288
|
+
"""Navigate to the previous page."""
|
|
289
|
+
prev_btn = self.pagination_container.locator(
|
|
290
|
+
'button:has-text("Previous"), [data-testid="prev-page"]'
|
|
291
|
+
)
|
|
292
|
+
if prev_btn.is_enabled():
|
|
293
|
+
prev_btn.click()
|
|
294
|
+
self.wait_for_page_load()
|
|
295
|
+
return self
|
|
296
|
+
|
|
297
|
+
@step
|
|
298
|
+
def change_items_per_page(self, items_count: int):
|
|
299
|
+
"""
|
|
300
|
+
Change the number of items displayed per page.
|
|
301
|
+
|
|
302
|
+
Args:
|
|
303
|
+
items_count (int): Number of items to display per page
|
|
304
|
+
"""
|
|
305
|
+
self.items_per_page_selector.select_option(value=str(items_count))
|
|
306
|
+
self.wait_for_page_load()
|
|
307
|
+
return self
|
|
308
|
+
|
|
309
|
+
# Utility methods
|
|
310
|
+
@step
|
|
311
|
+
def get_integrations_count(self) -> int:
|
|
312
|
+
"""
|
|
313
|
+
Get the total number of visible integration cards.
|
|
314
|
+
|
|
315
|
+
Returns:
|
|
316
|
+
int: Number of integration cards
|
|
317
|
+
"""
|
|
318
|
+
return self.integrations_table.count()
|
|
319
|
+
|
|
320
|
+
@step
|
|
321
|
+
def get_current_page_number(self) -> int:
|
|
322
|
+
"""
|
|
323
|
+
Get the current page number from pagination.
|
|
324
|
+
|
|
325
|
+
Returns:
|
|
326
|
+
int: Current page number
|
|
327
|
+
"""
|
|
328
|
+
current_page = self.pagination_container.locator(
|
|
329
|
+
'.active, .current-page, [aria-current="page"]'
|
|
330
|
+
)
|
|
331
|
+
if current_page.is_visible():
|
|
332
|
+
return int(current_page.text_content())
|
|
333
|
+
return 1
|
|
334
|
+
|
|
335
|
+
@step
|
|
336
|
+
def integration_exists(self, integration_name: str) -> bool:
|
|
337
|
+
"""
|
|
338
|
+
Check if an integration with the given name exists.
|
|
339
|
+
|
|
340
|
+
Args:
|
|
341
|
+
integration_name (str): The name of the integration to check
|
|
342
|
+
|
|
343
|
+
Returns:
|
|
344
|
+
bool: True if integration exists, False otherwise
|
|
345
|
+
"""
|
|
346
|
+
return self.integrations_table.filter(has_text=integration_name).count() > 0
|
|
347
|
+
|
|
348
|
+
# Verification methods
|
|
349
|
+
@step
|
|
350
|
+
def should_be_on_integrations_page(self):
|
|
351
|
+
"""Verify that the user is on the integrations page."""
|
|
352
|
+
expect(self.page).to_have_url(page_url)
|
|
353
|
+
return self
|
|
354
|
+
|
|
355
|
+
@step
|
|
356
|
+
def should_have_page_title(self, expected_title: str = "Integrations"):
|
|
357
|
+
"""Verify the page title."""
|
|
358
|
+
expect(self.page_title).to_contain_text(expected_title)
|
|
359
|
+
return self
|
|
360
|
+
|
|
361
|
+
@step
|
|
362
|
+
def should_see_create_integration_button(self):
|
|
363
|
+
"""Verify that the Create Integration button is visible."""
|
|
364
|
+
expect(self.create_button).to_be_visible()
|
|
365
|
+
return self
|
|
366
|
+
|
|
367
|
+
@step
|
|
368
|
+
def should_see_integration_type_switcher(self):
|
|
369
|
+
"""Verify that the Integration type selectors are visible."""
|
|
370
|
+
expect(self.user_type_button).to_be_visible()
|
|
371
|
+
expect(self.project_type_button).to_be_visible()
|
|
372
|
+
return self
|
|
373
|
+
|
|
374
|
+
@step
|
|
375
|
+
def should_see_search_input(self):
|
|
376
|
+
"""Verify that the search input is visible."""
|
|
377
|
+
expect(self.search_input).to_be_visible()
|
|
378
|
+
return self
|
|
379
|
+
|
|
380
|
+
@step
|
|
381
|
+
def should_see_integrations_table(self):
|
|
382
|
+
"""Verify that integration table is visible."""
|
|
383
|
+
expect(self.integrations_table).to_be_visible()
|
|
384
|
+
return self
|
|
385
|
+
|
|
386
|
+
@step
|
|
387
|
+
def should_see_specific_integration(self, integration_name: str):
|
|
388
|
+
"""
|
|
389
|
+
Verify that a specific integration is visible.
|
|
390
|
+
|
|
391
|
+
Args:
|
|
392
|
+
integration_name (str): The name of the integration to verify
|
|
393
|
+
"""
|
|
394
|
+
integration_card = self.get_integration_card_by_name(integration_name)
|
|
395
|
+
integration_card.should_be_visible()
|
|
396
|
+
return self
|
|
397
|
+
|
|
398
|
+
@step
|
|
399
|
+
def should_not_see_integration(self, integration_name: str):
|
|
400
|
+
"""
|
|
401
|
+
Verify that a specific integration is not visible.
|
|
402
|
+
|
|
403
|
+
Args:
|
|
404
|
+
integration_name (str): The name of the integration that should not be visible
|
|
405
|
+
"""
|
|
406
|
+
expect(self.integrations_table.filter(has_text=integration_name)).to_have_count(
|
|
407
|
+
0
|
|
408
|
+
)
|
|
409
|
+
return self
|
|
410
|
+
|
|
411
|
+
@step
|
|
412
|
+
def should_see_no_integrations_message(self):
|
|
413
|
+
"""Verify that the no integrations message is displayed."""
|
|
414
|
+
expect(self.no_integrations_message).to_be_visible()
|
|
415
|
+
return self
|
|
416
|
+
|
|
417
|
+
@step
|
|
418
|
+
def should_have_pagination(self):
|
|
419
|
+
"""Verify that pagination controls are visible."""
|
|
420
|
+
expect(self.pagination_container).to_be_visible()
|
|
421
|
+
return self
|
|
422
|
+
|
|
423
|
+
@step
|
|
424
|
+
def should_see_message(self, message: str):
|
|
425
|
+
"""
|
|
426
|
+
Verify that a message is displayed.
|
|
427
|
+
|
|
428
|
+
Args:
|
|
429
|
+
message (str, optional): Specific message to verify
|
|
430
|
+
"""
|
|
431
|
+
expect(self.toast_message).to_be_visible()
|
|
432
|
+
if message:
|
|
433
|
+
expect(self.toast_message).to_contain_text(message)
|
|
434
|
+
return self
|