diff options
Diffstat (limited to 'bitbake/lib/toaster/tests/functional/test_project_page.py')
-rw-r--r-- | bitbake/lib/toaster/tests/functional/test_project_page.py | 415 |
1 files changed, 199 insertions, 216 deletions
diff --git a/bitbake/lib/toaster/tests/functional/test_project_page.py b/bitbake/lib/toaster/tests/functional/test_project_page.py index adbe3587e4..429d86feba 100644 --- a/bitbake/lib/toaster/tests/functional/test_project_page.py +++ b/bitbake/lib/toaster/tests/functional/test_project_page.py | |||
@@ -7,8 +7,8 @@ | |||
7 | # | 7 | # |
8 | 8 | ||
9 | import os | 9 | import os |
10 | import random | ||
11 | import string | 10 | import string |
11 | import time | ||
12 | from unittest import skip | 12 | from unittest import skip |
13 | import pytest | 13 | import pytest |
14 | from django.urls import reverse | 14 | from django.urls import reverse |
@@ -22,58 +22,17 @@ from selenium.webdriver.common.by import By | |||
22 | 22 | ||
23 | from .utils import get_projectId_from_url, wait_until_build, wait_until_build_cancelled | 23 | from .utils import get_projectId_from_url, wait_until_build, wait_until_build_cancelled |
24 | 24 | ||
25 | 25 | class TestProjectPageBase(SeleniumFunctionalTestCase): | |
26 | @pytest.mark.django_db | ||
27 | @pytest.mark.order("last") | ||
28 | class TestProjectPage(SeleniumFunctionalTestCase): | ||
29 | project_id = None | 26 | project_id = None |
30 | PROJECT_NAME = 'TestProjectPage' | 27 | PROJECT_NAME = 'TestProjectPage' |
31 | 28 | ||
32 | def _create_project(self, project_name): | ||
33 | """ Create/Test new project using: | ||
34 | - Project Name: Any string | ||
35 | - Release: Any string | ||
36 | - Merge Toaster settings: True or False | ||
37 | """ | ||
38 | self.get(reverse('newproject')) | ||
39 | self.wait_until_visible('#new-project-name') | ||
40 | self.find("#new-project-name").send_keys(project_name) | ||
41 | select = Select(self.find("#projectversion")) | ||
42 | select.select_by_value('3') | ||
43 | |||
44 | # check merge toaster settings | ||
45 | checkbox = self.find('.checkbox-mergeattr') | ||
46 | if not checkbox.is_selected(): | ||
47 | checkbox.click() | ||
48 | |||
49 | if self.PROJECT_NAME != 'TestProjectPage': | ||
50 | # Reset project name if it's not the default one | ||
51 | self.PROJECT_NAME = 'TestProjectPage' | ||
52 | |||
53 | self.find("#create-project-button").click() | ||
54 | |||
55 | try: | ||
56 | self.wait_until_visible('#hint-error-project-name') | ||
57 | url = reverse('project', args=(TestProjectPage.project_id, )) | ||
58 | self.get(url) | ||
59 | self.wait_until_visible('#config-nav', poll=3) | ||
60 | except TimeoutException: | ||
61 | self.wait_until_visible('#config-nav', poll=3) | ||
62 | |||
63 | def _random_string(self, length): | ||
64 | return ''.join( | ||
65 | random.choice(string.ascii_letters) for _ in range(length) | ||
66 | ) | ||
67 | |||
68 | def _navigate_to_project_page(self): | 29 | def _navigate_to_project_page(self): |
69 | # Navigate to project page | 30 | # Navigate to project page |
70 | if TestProjectPage.project_id is None: | 31 | if TestProjectPageBase.project_id is None: |
71 | self._create_project(project_name=self._random_string(10)) | 32 | TestProjectPageBase.project_id = self.create_new_project(self.PROJECT_NAME, '3', None, True) |
72 | current_url = self.driver.current_url | 33 | |
73 | TestProjectPage.project_id = get_projectId_from_url(current_url) | 34 | url = reverse('project', args=(TestProjectPageBase.project_id,)) |
74 | else: | 35 | self.get(url) |
75 | url = reverse('project', args=(TestProjectPage.project_id,)) | ||
76 | self.get(url) | ||
77 | self.wait_until_visible('#config-nav') | 36 | self.wait_until_visible('#config-nav') |
78 | 37 | ||
79 | def _get_create_builds(self, **kwargs): | 38 | def _get_create_builds(self, **kwargs): |
@@ -81,14 +40,14 @@ class TestProjectPage(SeleniumFunctionalTestCase): | |||
81 | # parameters for builds to associate with the projects | 40 | # parameters for builds to associate with the projects |
82 | now = timezone.now() | 41 | now = timezone.now() |
83 | self.project1_build_success = { | 42 | self.project1_build_success = { |
84 | 'project': Project.objects.get(id=TestProjectPage.project_id), | 43 | 'project': Project.objects.get(id=TestProjectPageBase.project_id), |
85 | 'started_on': now, | 44 | 'started_on': now, |
86 | 'completed_on': now, | 45 | 'completed_on': now, |
87 | 'outcome': Build.SUCCEEDED | 46 | 'outcome': Build.SUCCEEDED |
88 | } | 47 | } |
89 | 48 | ||
90 | self.project1_build_failure = { | 49 | self.project1_build_failure = { |
91 | 'project': Project.objects.get(id=TestProjectPage.project_id), | 50 | 'project': Project.objects.get(id=TestProjectPageBase.project_id), |
92 | 'started_on': now, | 51 | 'started_on': now, |
93 | 'completed_on': now, | 52 | 'completed_on': now, |
94 | 'outcome': Build.FAILED | 53 | 'outcome': Build.FAILED |
@@ -133,7 +92,8 @@ class TestProjectPage(SeleniumFunctionalTestCase): | |||
133 | list_check_box_id: list | 92 | list_check_box_id: list |
134 | ): | 93 | ): |
135 | # Check edit column | 94 | # Check edit column |
136 | edit_column = self.find(f'#{edit_btn_id}') | 95 | finder = lambda driver: self.find(f'#{edit_btn_id}') |
96 | edit_column = self.wait_until_element_clickable(finder) | ||
137 | self.assertTrue(edit_column.is_displayed()) | 97 | self.assertTrue(edit_column.is_displayed()) |
138 | edit_column.click() | 98 | edit_column.click() |
139 | # Check dropdown is visible | 99 | # Check dropdown is visible |
@@ -192,7 +152,7 @@ class TestProjectPage(SeleniumFunctionalTestCase): | |||
192 | def test_show_rows(row_to_show, show_row_link): | 152 | def test_show_rows(row_to_show, show_row_link): |
193 | # Check that we can show rows == row_to_show | 153 | # Check that we can show rows == row_to_show |
194 | show_row_link.select_by_value(str(row_to_show)) | 154 | show_row_link.select_by_value(str(row_to_show)) |
195 | self.wait_until_visible(f'#{table_selector} tbody tr', poll=3) | 155 | self.wait_until_visible(f'#{table_selector} tbody tr') |
196 | # check at least some rows are visible | 156 | # check at least some rows are visible |
197 | self.assertTrue( | 157 | self.assertTrue( |
198 | len(self.find_all(f'#{table_selector} tbody tr')) > 0 | 158 | len(self.find_all(f'#{table_selector} tbody tr')) > 0 |
@@ -222,34 +182,7 @@ class TestProjectPage(SeleniumFunctionalTestCase): | |||
222 | rows = self.find_all(f'#{table_selector} tbody tr') | 182 | rows = self.find_all(f'#{table_selector} tbody tr') |
223 | self.assertTrue(len(rows) > 0) | 183 | self.assertTrue(len(rows) > 0) |
224 | 184 | ||
225 | def test_create_project(self): | 185 | class TestProjectPage(TestProjectPageBase): |
226 | """ Create/Test new project using: | ||
227 | - Project Name: Any string | ||
228 | - Release: Any string | ||
229 | - Merge Toaster settings: True or False | ||
230 | """ | ||
231 | self._create_project(project_name=self.PROJECT_NAME) | ||
232 | |||
233 | def test_image_recipe_editColumn(self): | ||
234 | """ Test the edit column feature in image recipe table on project page """ | ||
235 | self._get_create_builds(success=10, failure=10) | ||
236 | |||
237 | url = reverse('projectimagerecipes', args=(TestProjectPage.project_id,)) | ||
238 | self.get(url) | ||
239 | self.wait_until_present('#imagerecipestable tbody tr') | ||
240 | |||
241 | column_list = [ | ||
242 | 'get_description_or_summary', 'layer_version__get_vcs_reference', | ||
243 | 'layer_version__layer__name', 'license', 'recipe-file', 'section', | ||
244 | 'version' | ||
245 | ] | ||
246 | |||
247 | # Check that we can hide the edit column | ||
248 | self._mixin_test_table_edit_column( | ||
249 | 'imagerecipestable', | ||
250 | 'edit-columns-button', | ||
251 | [f'checkbox-{column}' for column in column_list] | ||
252 | ) | ||
253 | 186 | ||
254 | def test_page_header_on_project_page(self): | 187 | def test_page_header_on_project_page(self): |
255 | """ Check page header in project page: | 188 | """ Check page header in project page: |
@@ -272,8 +205,8 @@ class TestProjectPage(SeleniumFunctionalTestCase): | |||
272 | logo_img = logo.find_element(By.TAG_NAME, 'img') | 205 | logo_img = logo.find_element(By.TAG_NAME, 'img') |
273 | self.assertTrue(logo_img.is_displayed(), | 206 | self.assertTrue(logo_img.is_displayed(), |
274 | 'Logo of Yocto project not found') | 207 | 'Logo of Yocto project not found') |
275 | self.assertTrue( | 208 | self.assertIn( |
276 | '/static/img/logo.png' in str(logo_img.get_attribute('src')), | 209 | '/static/img/logo.png', str(logo_img.get_attribute('src')), |
277 | 'Logo of Yocto project not found' | 210 | 'Logo of Yocto project not found' |
278 | ) | 211 | ) |
279 | # "Toaster"+" Information icon", clickable | 212 | # "Toaster"+" Information icon", clickable |
@@ -282,34 +215,34 @@ class TestProjectPage(SeleniumFunctionalTestCase): | |||
282 | "//div[@class='toaster-navbar-brand']//a[@class='brand']", | 215 | "//div[@class='toaster-navbar-brand']//a[@class='brand']", |
283 | ) | 216 | ) |
284 | self.assertTrue(toaster.is_displayed(), 'Toaster not found') | 217 | self.assertTrue(toaster.is_displayed(), 'Toaster not found') |
285 | self.assertTrue(toaster.text == 'Toaster') | 218 | self.assertEqual(toaster.text, 'Toaster') |
286 | info_sign = self.find('.glyphicon-info-sign') | 219 | info_sign = self.find('.glyphicon-info-sign') |
287 | self.assertTrue(info_sign.is_displayed()) | 220 | self.assertTrue(info_sign.is_displayed()) |
288 | 221 | ||
289 | # "Server Icon" + "All builds" | 222 | # "Server Icon" + "All builds" |
290 | all_builds = self.find('#navbar-all-builds') | 223 | all_builds = self.find('#navbar-all-builds') |
291 | all_builds_link = all_builds.find_element(By.TAG_NAME, 'a') | 224 | all_builds_link = all_builds.find_element(By.TAG_NAME, 'a') |
292 | self.assertTrue("All builds" in all_builds_link.text) | 225 | self.assertIn("All builds", all_builds_link.text) |
293 | self.assertTrue( | 226 | self.assertIn( |
294 | '/toastergui/builds/' in str(all_builds_link.get_attribute('href')) | 227 | '/toastergui/builds/', str(all_builds_link.get_attribute('href')) |
295 | ) | 228 | ) |
296 | server_icon = all_builds.find_element(By.TAG_NAME, 'i') | 229 | server_icon = all_builds.find_element(By.TAG_NAME, 'i') |
297 | self.assertTrue( | 230 | self.assertEqual( |
298 | server_icon.get_attribute('class') == 'glyphicon glyphicon-tasks' | 231 | server_icon.get_attribute('class'), 'glyphicon glyphicon-tasks' |
299 | ) | 232 | ) |
300 | self.assertTrue(server_icon.is_displayed()) | 233 | self.assertTrue(server_icon.is_displayed()) |
301 | 234 | ||
302 | # "Directory Icon" + "All projects" | 235 | # "Directory Icon" + "All projects" |
303 | all_projects = self.find('#navbar-all-projects') | 236 | all_projects = self.find('#navbar-all-projects') |
304 | all_projects_link = all_projects.find_element(By.TAG_NAME, 'a') | 237 | all_projects_link = all_projects.find_element(By.TAG_NAME, 'a') |
305 | self.assertTrue("All projects" in all_projects_link.text) | 238 | self.assertIn("All projects", all_projects_link.text) |
306 | self.assertTrue( | 239 | self.assertIn( |
307 | '/toastergui/projects/' in str(all_projects_link.get_attribute( | 240 | '/toastergui/projects/', str(all_projects_link.get_attribute( |
308 | 'href')) | 241 | 'href')) |
309 | ) | 242 | ) |
310 | dir_icon = all_projects.find_element(By.TAG_NAME, 'i') | 243 | dir_icon = all_projects.find_element(By.TAG_NAME, 'i') |
311 | self.assertTrue( | 244 | self.assertEqual( |
312 | dir_icon.get_attribute('class') == 'icon-folder-open' | 245 | dir_icon.get_attribute('class'), 'icon-folder-open' |
313 | ) | 246 | ) |
314 | self.assertTrue(dir_icon.is_displayed()) | 247 | self.assertTrue(dir_icon.is_displayed()) |
315 | 248 | ||
@@ -317,23 +250,23 @@ class TestProjectPage(SeleniumFunctionalTestCase): | |||
317 | toaster_docs_link = self.find('#navbar-docs') | 250 | toaster_docs_link = self.find('#navbar-docs') |
318 | toaster_docs_link_link = toaster_docs_link.find_element(By.TAG_NAME, | 251 | toaster_docs_link_link = toaster_docs_link.find_element(By.TAG_NAME, |
319 | 'a') | 252 | 'a') |
320 | self.assertTrue("Documentation" in toaster_docs_link_link.text) | 253 | self.assertIn("Documentation", toaster_docs_link_link.text) |
321 | self.assertTrue( | 254 | self.assertEqual( |
322 | toaster_docs_link_link.get_attribute('href') == 'http://docs.yoctoproject.org/toaster-manual/index.html#toaster-user-manual' | 255 | toaster_docs_link_link.get_attribute('href'), 'http://docs.yoctoproject.org/toaster-manual/index.html#toaster-user-manual' |
323 | ) | 256 | ) |
324 | book_icon = toaster_docs_link.find_element(By.TAG_NAME, 'i') | 257 | book_icon = toaster_docs_link.find_element(By.TAG_NAME, 'i') |
325 | self.assertTrue( | 258 | self.assertEqual( |
326 | book_icon.get_attribute('class') == 'glyphicon glyphicon-book' | 259 | book_icon.get_attribute('class'), 'glyphicon glyphicon-book' |
327 | ) | 260 | ) |
328 | self.assertTrue(book_icon.is_displayed()) | 261 | self.assertTrue(book_icon.is_displayed()) |
329 | 262 | ||
330 | # AT RIGHT -> button "New project" | 263 | # AT RIGHT -> button "New project" |
331 | new_project_button = self.find('#new-project-button') | 264 | new_project_button = self.find('#new-project-button') |
332 | self.assertTrue(new_project_button.is_displayed()) | 265 | self.assertTrue(new_project_button.is_displayed()) |
333 | self.assertTrue(new_project_button.text == 'New project') | 266 | self.assertEqual(new_project_button.text, 'New project') |
334 | new_project_button.click() | 267 | new_project_button.click() |
335 | self.assertTrue( | 268 | self.assertIn( |
336 | '/toastergui/newproject/' in str(self.driver.current_url) | 269 | '/toastergui/newproject/', str(self.driver.current_url) |
337 | ) | 270 | ) |
338 | 271 | ||
339 | def test_edit_project_name(self): | 272 | def test_edit_project_name(self): |
@@ -348,7 +281,8 @@ class TestProjectPage(SeleniumFunctionalTestCase): | |||
348 | 281 | ||
349 | # click on "Edit" icon button | 282 | # click on "Edit" icon button |
350 | self.wait_until_visible('#project-name-container') | 283 | self.wait_until_visible('#project-name-container') |
351 | edit_button = self.find('#project-change-form-toggle') | 284 | finder = lambda driver: self.find('#project-change-form-toggle') |
285 | edit_button = self.wait_until_element_clickable(finder) | ||
352 | edit_button.click() | 286 | edit_button.click() |
353 | project_name_input = self.find('#project-name-change-input') | 287 | project_name_input = self.find('#project-name-change-input') |
354 | self.assertTrue(project_name_input.is_displayed()) | 288 | self.assertTrue(project_name_input.is_displayed()) |
@@ -358,8 +292,8 @@ class TestProjectPage(SeleniumFunctionalTestCase): | |||
358 | 292 | ||
359 | # check project name is changed | 293 | # check project name is changed |
360 | self.wait_until_visible('#project-name-container') | 294 | self.wait_until_visible('#project-name-container') |
361 | self.assertTrue( | 295 | self.assertIn( |
362 | 'New Name' in str(self.find('#project-name-container').text) | 296 | 'New Name', str(self.find('#project-name-container').text) |
363 | ) | 297 | ) |
364 | 298 | ||
365 | def test_project_page_tabs(self): | 299 | def test_project_page_tabs(self): |
@@ -376,10 +310,10 @@ class TestProjectPage(SeleniumFunctionalTestCase): | |||
376 | # check "configuration" tab | 310 | # check "configuration" tab |
377 | self.wait_until_visible('#topbar-configuration-tab') | 311 | self.wait_until_visible('#topbar-configuration-tab') |
378 | config_tab = self.find('#topbar-configuration-tab') | 312 | config_tab = self.find('#topbar-configuration-tab') |
379 | self.assertTrue(config_tab.get_attribute('class') == 'active') | 313 | self.assertEqual(config_tab.get_attribute('class'), 'active') |
380 | self.assertTrue('Configuration' in str(config_tab.text)) | 314 | self.assertIn('Configuration', str(config_tab.text)) |
381 | self.assertTrue( | 315 | self.assertIn( |
382 | f"/toastergui/project/{TestProjectPage.project_id}" in str(self.driver.current_url) | 316 | f"/toastergui/project/{TestProjectPageBase.project_id}", str(self.driver.current_url) |
383 | ) | 317 | ) |
384 | 318 | ||
385 | def get_tabs(): | 319 | def get_tabs(): |
@@ -392,9 +326,9 @@ class TestProjectPage(SeleniumFunctionalTestCase): | |||
392 | def check_tab_link(tab_index, tab_name, url): | 326 | def check_tab_link(tab_index, tab_name, url): |
393 | tab = get_tabs()[tab_index] | 327 | tab = get_tabs()[tab_index] |
394 | tab_link = tab.find_element(By.TAG_NAME, 'a') | 328 | tab_link = tab.find_element(By.TAG_NAME, 'a') |
395 | self.assertTrue(url in tab_link.get_attribute('href')) | 329 | self.assertIn(url, tab_link.get_attribute('href')) |
396 | self.assertTrue(tab_name in tab_link.text) | 330 | self.assertIn(tab_name, tab_link.text) |
397 | self.assertTrue(tab.get_attribute('class') == 'active') | 331 | self.assertEqual(tab.get_attribute('class'), 'active') |
398 | 332 | ||
399 | # check "Builds" tab | 333 | # check "Builds" tab |
400 | builds_tab = get_tabs()[1] | 334 | builds_tab = get_tabs()[1] |
@@ -402,7 +336,7 @@ class TestProjectPage(SeleniumFunctionalTestCase): | |||
402 | check_tab_link( | 336 | check_tab_link( |
403 | 1, | 337 | 1, |
404 | 'Builds', | 338 | 'Builds', |
405 | f"/toastergui/project/{TestProjectPage.project_id}/builds" | 339 | f"/toastergui/project/{TestProjectPageBase.project_id}/builds" |
406 | ) | 340 | ) |
407 | 341 | ||
408 | # check "Import layers" tab | 342 | # check "Import layers" tab |
@@ -411,7 +345,7 @@ class TestProjectPage(SeleniumFunctionalTestCase): | |||
411 | check_tab_link( | 345 | check_tab_link( |
412 | 2, | 346 | 2, |
413 | 'Import layer', | 347 | 'Import layer', |
414 | f"/toastergui/project/{TestProjectPage.project_id}/importlayer" | 348 | f"/toastergui/project/{TestProjectPageBase.project_id}/importlayer" |
415 | ) | 349 | ) |
416 | 350 | ||
417 | # check "New custom image" tab | 351 | # check "New custom image" tab |
@@ -420,7 +354,7 @@ class TestProjectPage(SeleniumFunctionalTestCase): | |||
420 | check_tab_link( | 354 | check_tab_link( |
421 | 3, | 355 | 3, |
422 | 'New custom image', | 356 | 'New custom image', |
423 | f"/toastergui/project/{TestProjectPage.project_id}/newcustomimage" | 357 | f"/toastergui/project/{TestProjectPageBase.project_id}/newcustomimage" |
424 | ) | 358 | ) |
425 | 359 | ||
426 | # check search box can be use to build recipes | 360 | # check search box can be use to build recipes |
@@ -428,13 +362,17 @@ class TestProjectPage(SeleniumFunctionalTestCase): | |||
428 | search_box.send_keys('core-image-minimal') | 362 | search_box.send_keys('core-image-minimal') |
429 | self.find('#build-button').click() | 363 | self.find('#build-button').click() |
430 | self.wait_until_visible('#latest-builds') | 364 | self.wait_until_visible('#latest-builds') |
431 | lastest_builds = self.driver.find_elements( | 365 | buildtext = "Loading" |
432 | By.XPATH, | 366 | while "Loading" in buildtext: |
433 | '//div[@id="latest-builds"]', | 367 | time.sleep(1) |
434 | ) | 368 | lastest_builds = self.driver.find_elements( |
435 | last_build = lastest_builds[0] | 369 | By.XPATH, |
436 | self.assertTrue( | 370 | '//div[@id="latest-builds"]', |
437 | 'core-image-minimal' in str(last_build.text) | 371 | ) |
372 | last_build = lastest_builds[0] | ||
373 | buildtext = last_build.text | ||
374 | self.assertIn( | ||
375 | 'core-image-minimal', str(last_build.text) | ||
438 | ) | 376 | ) |
439 | 377 | ||
440 | def test_softwareRecipe_page(self): | 378 | def test_softwareRecipe_page(self): |
@@ -446,7 +384,7 @@ class TestProjectPage(SeleniumFunctionalTestCase): | |||
446 | """ | 384 | """ |
447 | self._navigate_to_config_nav('softwarerecipestable', 4) | 385 | self._navigate_to_config_nav('softwarerecipestable', 4) |
448 | # check title "Compatible software recipes" is displayed | 386 | # check title "Compatible software recipes" is displayed |
449 | self.assertTrue("Compatible software recipes" in self.get_page_source()) | 387 | self.assertIn("Compatible software recipes", self.get_page_source()) |
450 | # Test search input | 388 | # Test search input |
451 | self._mixin_test_table_search_input( | 389 | self._mixin_test_table_search_input( |
452 | input_selector='search-input-softwarerecipestable', | 390 | input_selector='search-input-softwarerecipestable', |
@@ -455,12 +393,8 @@ class TestProjectPage(SeleniumFunctionalTestCase): | |||
455 | table_selector='softwarerecipestable' | 393 | table_selector='softwarerecipestable' |
456 | ) | 394 | ) |
457 | # check "build recipe" button works | 395 | # check "build recipe" button works |
458 | rows = self.find_all('#softwarerecipestable tbody tr') | 396 | finder = lambda driver: self.find_all('#softwarerecipestable tbody tr')[0].find_element(By.XPATH, '//td[@class="add-del-layers"]/a') |
459 | image_to_build = rows[0] | 397 | build_btn = self.wait_until_element_clickable(finder) |
460 | build_btn = image_to_build.find_element( | ||
461 | By.XPATH, | ||
462 | '//td[@class="add-del-layers"]//a[1]' | ||
463 | ) | ||
464 | build_btn.click() | 398 | build_btn.click() |
465 | build_state = wait_until_build(self, 'queued cloning starting parsing failed') | 399 | build_state = wait_until_build(self, 'queued cloning starting parsing failed') |
466 | lastest_builds = self.driver.find_elements( | 400 | lastest_builds = self.driver.find_elements( |
@@ -468,11 +402,10 @@ class TestProjectPage(SeleniumFunctionalTestCase): | |||
468 | '//div[@id="latest-builds"]/div' | 402 | '//div[@id="latest-builds"]/div' |
469 | ) | 403 | ) |
470 | self.assertTrue(len(lastest_builds) > 0) | 404 | self.assertTrue(len(lastest_builds) > 0) |
471 | last_build = lastest_builds[0] | 405 | # Find the latest builds, the last build and then the cancel button |
472 | cancel_button = last_build.find_element( | 406 | |
473 | By.XPATH, | 407 | finder = lambda driver: driver.find_elements(By.XPATH, '//div[@id="latest-builds"]/div')[0].find_element(By.XPATH, '//span[@class="cancel-build-btn pull-right alert-link"]') |
474 | '//span[@class="cancel-build-btn pull-right alert-link"]', | 408 | cancel_button = self.wait_until_element_clickable(finder) |
475 | ) | ||
476 | cancel_button.click() | 409 | cancel_button.click() |
477 | if 'starting' not in build_state: # change build state when cancelled in starting state | 410 | if 'starting' not in build_state: # change build state when cancelled in starting state |
478 | wait_until_build_cancelled(self) | 411 | wait_until_build_cancelled(self) |
@@ -510,7 +443,7 @@ class TestProjectPage(SeleniumFunctionalTestCase): | |||
510 | """ | 443 | """ |
511 | self._navigate_to_config_nav('machinestable', 5) | 444 | self._navigate_to_config_nav('machinestable', 5) |
512 | # check title "Compatible software recipes" is displayed | 445 | # check title "Compatible software recipes" is displayed |
513 | self.assertTrue("Compatible machines" in self.get_page_source()) | 446 | self.assertIn("Compatible machines", self.get_page_source()) |
514 | # Test search input | 447 | # Test search input |
515 | self._mixin_test_table_search_input( | 448 | self._mixin_test_table_search_input( |
516 | input_selector='search-input-machinestable', | 449 | input_selector='search-input-machinestable', |
@@ -519,17 +452,13 @@ class TestProjectPage(SeleniumFunctionalTestCase): | |||
519 | table_selector='machinestable' | 452 | table_selector='machinestable' |
520 | ) | 453 | ) |
521 | # check "Select machine" button works | 454 | # check "Select machine" button works |
522 | rows = self.find_all('#machinestable tbody tr') | 455 | finder = lambda driver: self.find_all('#machinestable tbody tr')[0].find_element(By.XPATH, '//td[@class="add-del-layers"]') |
523 | machine_to_select = rows[0] | 456 | select_btn = self.wait_until_element_clickable(finder) |
524 | select_btn = machine_to_select.find_element( | 457 | select_btn.click() |
525 | By.XPATH, | 458 | self.wait_until_visible('#project-machine-name') |
526 | '//td[@class="add-del-layers"]//a[1]' | ||
527 | ) | ||
528 | select_btn.send_keys(Keys.RETURN) | ||
529 | self.wait_until_visible('#config-nav') | ||
530 | project_machine_name = self.find('#project-machine-name') | 459 | project_machine_name = self.find('#project-machine-name') |
531 | self.assertTrue( | 460 | self.assertIn( |
532 | 'qemux86-64' in project_machine_name.text | 461 | 'qemux86-64', project_machine_name.text |
533 | ) | 462 | ) |
534 | # check "Add layer" button works | 463 | # check "Add layer" button works |
535 | self._navigate_to_config_nav('machinestable', 5) | 464 | self._navigate_to_config_nav('machinestable', 5) |
@@ -540,16 +469,23 @@ class TestProjectPage(SeleniumFunctionalTestCase): | |||
540 | searchBtn_selector='search-submit-machinestable', | 469 | searchBtn_selector='search-submit-machinestable', |
541 | table_selector='machinestable' | 470 | table_selector='machinestable' |
542 | ) | 471 | ) |
543 | self.wait_until_visible('#machinestable tbody tr', poll=3) | 472 | |
544 | rows = self.find_all('#machinestable tbody tr') | 473 | self.wait_until_visible('#machinestable tbody tr') |
545 | machine_to_add = rows[0] | 474 | # Locate a machine to add button |
546 | add_btn = machine_to_add.find_element(By.XPATH, '//td[@class="add-del-layers"]') | 475 | finder = lambda driver: self.find_all('#machinestable tbody tr')[0].find_element(By.XPATH, '//td[@class="add-del-layers"]') |
476 | add_btn = self.wait_until_element_clickable(finder) | ||
547 | add_btn.click() | 477 | add_btn.click() |
548 | self.wait_until_visible('#change-notification') | 478 | self.wait_until_visible('#change-notification') |
549 | change_notification = self.find('#change-notification') | 479 | change_notification = self.find('#change-notification') |
550 | self.assertTrue( | 480 | self.assertIn( |
551 | f'You have added 1 layer to your project' in str(change_notification.text) | 481 | f'You have added 1 layer to your project', str(change_notification.text) |
552 | ) | 482 | ) |
483 | |||
484 | finder = lambda driver: self.find('#hide-alert') | ||
485 | hide_button = self.wait_until_element_clickable(finder) | ||
486 | hide_button.click() | ||
487 | self.wait_until_not_visible('#change-notification') | ||
488 | |||
553 | # check Machine table feature(show/hide column, pagination) | 489 | # check Machine table feature(show/hide column, pagination) |
554 | self._navigate_to_config_nav('machinestable', 5) | 490 | self._navigate_to_config_nav('machinestable', 5) |
555 | column_list = [ | 491 | column_list = [ |
@@ -580,7 +516,7 @@ class TestProjectPage(SeleniumFunctionalTestCase): | |||
580 | """ | 516 | """ |
581 | self._navigate_to_config_nav('layerstable', 6) | 517 | self._navigate_to_config_nav('layerstable', 6) |
582 | # check title "Compatible layers" is displayed | 518 | # check title "Compatible layers" is displayed |
583 | self.assertTrue("Compatible layers" in self.get_page_source()) | 519 | self.assertIn("Compatible layers", self.get_page_source()) |
584 | # Test search input | 520 | # Test search input |
585 | input_text='meta-tanowrt' | 521 | input_text='meta-tanowrt' |
586 | self._mixin_test_table_search_input( | 522 | self._mixin_test_table_search_input( |
@@ -590,42 +526,44 @@ class TestProjectPage(SeleniumFunctionalTestCase): | |||
590 | table_selector='layerstable' | 526 | table_selector='layerstable' |
591 | ) | 527 | ) |
592 | # check "Add layer" button works | 528 | # check "Add layer" button works |
593 | self.wait_until_visible('#layerstable tbody tr', poll=3) | 529 | self.wait_until_visible('#layerstable tbody tr') |
594 | rows = self.find_all('#layerstable tbody tr') | 530 | finder = lambda driver: self.find_all('#layerstable tbody tr')[0].find_element(By.XPATH, '//td[@class="add-del-layers"]/a[@data-directive="add"]') |
595 | layer_to_add = rows[0] | 531 | add_btn = self.wait_until_element_clickable(finder) |
596 | add_btn = layer_to_add.find_element( | ||
597 | By.XPATH, | ||
598 | '//td[@class="add-del-layers"]' | ||
599 | ) | ||
600 | add_btn.click() | 532 | add_btn.click() |
601 | # check modal is displayed | 533 | # check modal is displayed |
602 | self.wait_until_visible('#dependencies-modal', poll=3) | 534 | self.wait_until_visible('#dependencies-modal') |
603 | list_dependencies = self.find_all('#dependencies-list li') | 535 | list_dependencies = self.find_all('#dependencies-list li') |
604 | # click on add-layers button | 536 | # click on add-layers button |
605 | add_layers_btn = self.driver.find_element( | 537 | finder = lambda driver: self.driver.find_element(By.XPATH, '//form[@id="dependencies-modal-form"]//button[@class="btn btn-primary"]') |
606 | By.XPATH, | 538 | add_layers_btn = self.wait_until_element_clickable(finder) |
607 | '//form[@id="dependencies-modal-form"]//button[@class="btn btn-primary"]' | ||
608 | ) | ||
609 | add_layers_btn.click() | 539 | add_layers_btn.click() |
610 | self.wait_until_visible('#change-notification') | 540 | self.wait_until_visible('#change-notification') |
611 | change_notification = self.find('#change-notification') | 541 | change_notification = self.find('#change-notification') |
612 | self.assertTrue( | 542 | self.assertIn( |
613 | f'You have added {len(list_dependencies)+1} layers to your project: {input_text} and its dependencies' in str(change_notification.text) | 543 | f'You have added {len(list_dependencies)+1} layers to your project: {input_text} and its dependencies', str(change_notification.text) |
614 | ) | 544 | ) |
545 | |||
546 | finder = lambda driver: self.find('#hide-alert') | ||
547 | hide_button = self.wait_until_element_clickable(finder) | ||
548 | hide_button.click() | ||
549 | self.wait_until_not_visible('#change-notification') | ||
550 | |||
615 | # check "Remove layer" button works | 551 | # check "Remove layer" button works |
616 | self.wait_until_visible('#layerstable tbody tr', poll=3) | 552 | self.wait_until_visible('#layerstable tbody tr') |
617 | rows = self.find_all('#layerstable tbody tr') | 553 | finder = lambda driver: self.find_all('#layerstable tbody tr')[0].find_element(By.XPATH, '//td[@class="add-del-layers"]/a[@data-directive="remove"]') |
618 | layer_to_remove = rows[0] | 554 | remove_btn = self.wait_until_element_clickable(finder) |
619 | remove_btn = layer_to_remove.find_element( | ||
620 | By.XPATH, | ||
621 | '//td[@class="add-del-layers"]' | ||
622 | ) | ||
623 | remove_btn.click() | 555 | remove_btn.click() |
624 | self.wait_until_visible('#change-notification', poll=2) | 556 | self.wait_until_visible('#change-notification') |
625 | change_notification = self.find('#change-notification') | 557 | change_notification = self.find('#change-notification') |
626 | self.assertTrue( | 558 | self.assertIn( |
627 | f'You have removed 1 layer from your project: {input_text}' in str(change_notification.text) | 559 | f'You have removed 1 layer from your project: {input_text}', str(change_notification.text) |
628 | ) | 560 | ) |
561 | |||
562 | finder = lambda driver: self.find('#hide-alert') | ||
563 | hide_button = self.wait_until_element_clickable(finder) | ||
564 | hide_button.click() | ||
565 | self.wait_until_not_visible('#change-notification') | ||
566 | |||
629 | # check layers table feature(show/hide column, pagination) | 567 | # check layers table feature(show/hide column, pagination) |
630 | self._navigate_to_config_nav('layerstable', 6) | 568 | self._navigate_to_config_nav('layerstable', 6) |
631 | column_list = [ | 569 | column_list = [ |
@@ -656,7 +594,7 @@ class TestProjectPage(SeleniumFunctionalTestCase): | |||
656 | """ | 594 | """ |
657 | self._navigate_to_config_nav('distrostable', 7) | 595 | self._navigate_to_config_nav('distrostable', 7) |
658 | # check title "Compatible distros" is displayed | 596 | # check title "Compatible distros" is displayed |
659 | self.assertTrue("Compatible Distros" in self.get_page_source()) | 597 | self.assertIn("Compatible Distros", self.get_page_source()) |
660 | # Test search input | 598 | # Test search input |
661 | input_text='poky-altcfg' | 599 | input_text='poky-altcfg' |
662 | self._mixin_test_table_search_input( | 600 | self._mixin_test_table_search_input( |
@@ -666,17 +604,14 @@ class TestProjectPage(SeleniumFunctionalTestCase): | |||
666 | table_selector='distrostable' | 604 | table_selector='distrostable' |
667 | ) | 605 | ) |
668 | # check "Add distro" button works | 606 | # check "Add distro" button works |
669 | rows = self.find_all('#distrostable tbody tr') | 607 | self.wait_until_visible(".add-del-layers") |
670 | distro_to_add = rows[0] | 608 | finder = lambda driver: self.find_all('#distrostable tbody tr')[0].find_element(By.XPATH, '//td[@class="add-del-layers"]') |
671 | add_btn = distro_to_add.find_element( | 609 | add_btn = self.wait_until_element_clickable(finder) |
672 | By.XPATH, | ||
673 | '//td[@class="add-del-layers"]//a[1]' | ||
674 | ) | ||
675 | add_btn.click() | 610 | add_btn.click() |
676 | self.wait_until_visible('#change-notification', poll=2) | 611 | self.wait_until_visible('#change-notification') |
677 | change_notification = self.find('#change-notification') | 612 | change_notification = self.find('#change-notification') |
678 | self.assertTrue( | 613 | self.assertIn( |
679 | f'You have changed the distro to: {input_text}' in str(change_notification.text) | 614 | f'You have changed the distro to: {input_text}', str(change_notification.text) |
680 | ) | 615 | ) |
681 | # check distro table feature(show/hide column, pagination) | 616 | # check distro table feature(show/hide column, pagination) |
682 | self._navigate_to_config_nav('distrostable', 7) | 617 | self._navigate_to_config_nav('distrostable', 7) |
@@ -699,7 +634,7 @@ class TestProjectPage(SeleniumFunctionalTestCase): | |||
699 | ) | 634 | ) |
700 | 635 | ||
701 | def test_single_layer_page(self): | 636 | def test_single_layer_page(self): |
702 | """ Test layer page | 637 | """ Test layer details page using meta-poky as an example (assumes is added to start with) |
703 | - Check if title is displayed | 638 | - Check if title is displayed |
704 | - Check add/remove layer button works | 639 | - Check add/remove layer button works |
705 | - Check tabs(layers, recipes, machines) are displayed | 640 | - Check tabs(layers, recipes, machines) are displayed |
@@ -708,45 +643,62 @@ class TestProjectPage(SeleniumFunctionalTestCase): | |||
708 | - Check layer summary | 643 | - Check layer summary |
709 | - Check layer description | 644 | - Check layer description |
710 | """ | 645 | """ |
711 | url = reverse("layerdetails", args=(TestProjectPage.project_id, 8)) | 646 | self._navigate_to_config_nav('layerstable', 6) |
712 | self.get(url) | 647 | layer_link = self.driver.find_element(By.XPATH, '//tr/td[@class="layer__name"]/a[contains(text(),"meta-poky")]') |
648 | layer_link.click() | ||
713 | self.wait_until_visible('.page-header') | 649 | self.wait_until_visible('.page-header') |
714 | # check title is displayed | 650 | # check title is displayed |
715 | self.assertTrue(self.find('.page-header h1').is_displayed()) | 651 | self.assertTrue(self.find('.page-header h1').is_displayed()) |
716 | 652 | ||
717 | # check add layer button works | 653 | # check remove layer button works |
718 | remove_layer_btn = self.find('#add-remove-layer-btn') | 654 | finder = lambda driver: self.find('#add-remove-layer-btn') |
655 | remove_layer_btn = self.wait_until_element_clickable(finder) | ||
719 | remove_layer_btn.click() | 656 | remove_layer_btn.click() |
720 | self.wait_until_visible('#change-notification', poll=2) | 657 | self.wait_until_visible('#change-notification') |
721 | change_notification = self.find('#change-notification') | 658 | change_notification = self.find('#change-notification') |
722 | self.assertTrue( | 659 | self.assertIn( |
723 | f'You have removed 1 layer from your project' in str(change_notification.text) | 660 | f'You have removed 1 layer from your project', str(change_notification.text) |
724 | ) | 661 | ) |
725 | # check add layer button works, 18 is the random layer id | 662 | finder = lambda driver: self.find('#hide-alert') |
726 | add_layer_btn = self.find('#add-remove-layer-btn') | 663 | hide_button = self.wait_until_element_clickable(finder) |
664 | hide_button.click() | ||
665 | # check add layer button works | ||
666 | self.wait_until_not_visible('#change-notification') | ||
667 | finder = lambda driver: self.find('#add-remove-layer-btn') | ||
668 | add_layer_btn = self.wait_until_element_clickable(finder) | ||
727 | add_layer_btn.click() | 669 | add_layer_btn.click() |
728 | self.wait_until_visible('#change-notification') | 670 | self.wait_until_visible('#change-notification') |
729 | change_notification = self.find('#change-notification') | 671 | change_notification = self.find('#change-notification') |
730 | self.assertTrue( | 672 | self.assertIn( |
731 | f'You have added 1 layer to your project' in str(change_notification.text) | 673 | f'You have added 1 layer to your project', str(change_notification.text) |
732 | ) | 674 | ) |
675 | finder = lambda driver: self.find('#hide-alert') | ||
676 | hide_button = self.wait_until_element_clickable(finder) | ||
677 | hide_button.click() | ||
678 | self.wait_until_not_visible('#change-notification') | ||
733 | # check tabs(layers, recipes, machines) are displayed | 679 | # check tabs(layers, recipes, machines) are displayed |
734 | tabs = self.find_all('.nav-tabs li') | 680 | tabs = self.find_all('.nav-tabs li') |
735 | self.assertEqual(len(tabs), 3) | 681 | self.assertEqual(len(tabs), 3) |
736 | # Check first tab | 682 | # Check first tab |
737 | tabs[0].click() | 683 | tabs[0].click() |
738 | self.assertTrue( | 684 | self.assertIn( |
739 | 'active' in str(self.find('#information').get_attribute('class')) | 685 | 'active', str(self.find('#information').get_attribute('class')) |
740 | ) | 686 | ) |
741 | # Check second tab | 687 | # Check second tab (recipes) |
688 | self.wait_until_visible('.nav-tabs') | ||
689 | # Ensure page is scrolled to the top | ||
690 | self.driver.execute_script('window.scrollTo({behavior: "instant", top: 0, left: 0})') | ||
742 | tabs[1].click() | 691 | tabs[1].click() |
743 | self.assertTrue( | 692 | self.assertIn( |
744 | 'active' in str(self.find('#recipes').get_attribute('class')) | 693 | 'active', str(self.find('#recipes').get_attribute('class')) |
745 | ) | 694 | ) |
746 | # Check third tab | 695 | # Check third tab (machines) |
696 | self.wait_until_visible('.nav-tabs') | ||
697 | # Ensure page is scrolled to the top | ||
698 | self.driver.execute_script('window.scrollTo({behavior: "instant", top: 0, left: 0})') | ||
747 | tabs[2].click() | 699 | tabs[2].click() |
748 | self.assertTrue( | 700 | self.assertIn( |
749 | 'active' in str(self.find('#machines').get_attribute('class')) | 701 | 'active', str(self.find('#machines').get_attribute('class')) |
750 | ) | 702 | ) |
751 | # Check left section is displayed | 703 | # Check left section is displayed |
752 | section = self.find('.well') | 704 | section = self.find('.well') |
@@ -755,9 +707,13 @@ class TestProjectPage(SeleniumFunctionalTestCase): | |||
755 | section.find_element(By.XPATH, '//h2[1]').is_displayed() | 707 | section.find_element(By.XPATH, '//h2[1]').is_displayed() |
756 | ) | 708 | ) |
757 | # Check layer summary | 709 | # Check layer summary |
758 | self.assertTrue("Summary" in section.text) | 710 | self.assertIn("Summary", section.text) |
759 | # Check layer description | 711 | # Check layer description |
760 | self.assertTrue("Description" in section.text) | 712 | self.assertIn("Description", section.text) |
713 | |||
714 | @pytest.mark.django_db | ||
715 | @pytest.mark.order("last") | ||
716 | class TestProjectPageRecipes(TestProjectPageBase): | ||
761 | 717 | ||
762 | def test_single_recipe_page(self): | 718 | def test_single_recipe_page(self): |
763 | """ Test recipe page | 719 | """ Test recipe page |
@@ -767,7 +723,12 @@ class TestProjectPage(SeleniumFunctionalTestCase): | |||
767 | - Check recipe: name, summary, description, Version, Section, | 723 | - Check recipe: name, summary, description, Version, Section, |
768 | License, Approx. packages included, Approx. size, Recipe file | 724 | License, Approx. packages included, Approx. size, Recipe file |
769 | """ | 725 | """ |
770 | url = reverse("recipedetails", args=(TestProjectPage.project_id, 53428)) | 726 | # Use a recipe which is likely to exist in the layer index but not enabled |
727 | # in poky out the box - xen-image-minimal from meta-virtualization | ||
728 | self._navigate_to_project_page() | ||
729 | prj = Project.objects.get(pk=TestProjectPageBase.project_id) | ||
730 | recipe_id = prj.get_all_compatible_recipes().get(name="xen-image-minimal").pk | ||
731 | url = reverse("recipedetails", args=(TestProjectPageBase.project_id, recipe_id)) | ||
771 | self.get(url) | 732 | self.get(url) |
772 | self.wait_until_visible('.page-header') | 733 | self.wait_until_visible('.page-header') |
773 | # check title is displayed | 734 | # check title is displayed |
@@ -782,11 +743,33 @@ class TestProjectPage(SeleniumFunctionalTestCase): | |||
782 | section.find_element(By.XPATH, '//h2[1]').is_displayed() | 743 | section.find_element(By.XPATH, '//h2[1]').is_displayed() |
783 | ) | 744 | ) |
784 | # Check recipe sections details info are displayed | 745 | # Check recipe sections details info are displayed |
785 | self.assertTrue("Summary" in section.text) | 746 | self.assertIn("Summary", section.text) |
786 | self.assertTrue("Description" in section.text) | 747 | self.assertIn("Description", section.text) |
787 | self.assertTrue("Version" in section.text) | 748 | self.assertIn("Version", section.text) |
788 | self.assertTrue("Section" in section.text) | 749 | self.assertIn("Section", section.text) |
789 | self.assertTrue("License" in section.text) | 750 | self.assertIn("License", section.text) |
790 | self.assertTrue("Approx. packages included" in section.text) | 751 | self.assertIn("Approx. packages included", section.text) |
791 | self.assertTrue("Approx. package size" in section.text) | 752 | self.assertIn("Approx. package size", section.text) |
792 | self.assertTrue("Recipe file" in section.text) | 753 | self.assertIn("Recipe file", section.text) |
754 | |||
755 | def test_image_recipe_editColumn(self): | ||
756 | """ Test the edit column feature in image recipe table on project page """ | ||
757 | self._get_create_builds(success=10, failure=10) | ||
758 | |||
759 | url = reverse('projectimagerecipes', args=(TestProjectPageBase.project_id,)) | ||
760 | self.get(url) | ||
761 | self.wait_until_present('#imagerecipestable tbody tr') | ||
762 | |||
763 | column_list = [ | ||
764 | 'get_description_or_summary', 'layer_version__get_vcs_reference', | ||
765 | 'layer_version__layer__name', 'license', 'recipe-file', 'section', | ||
766 | 'version' | ||
767 | ] | ||
768 | |||
769 | # Check that we can hide the edit column | ||
770 | self._mixin_test_table_edit_column( | ||
771 | 'imagerecipestable', | ||
772 | 'edit-columns-button', | ||
773 | [f'checkbox-{column}' for column in column_list] | ||
774 | ) | ||
775 | |||