summaryrefslogtreecommitdiffstats
path: root/bitbake/lib/toaster/tests/functional/test_project_page.py
diff options
context:
space:
mode:
Diffstat (limited to 'bitbake/lib/toaster/tests/functional/test_project_page.py')
-rw-r--r--bitbake/lib/toaster/tests/functional/test_project_page.py415
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
9import os 9import os
10import random
11import string 10import string
11import time
12from unittest import skip 12from unittest import skip
13import pytest 13import pytest
14from django.urls import reverse 14from django.urls import reverse
@@ -22,58 +22,17 @@ from selenium.webdriver.common.by import By
22 22
23from .utils import get_projectId_from_url, wait_until_build, wait_until_build_cancelled 23from .utils import get_projectId_from_url, wait_until_build, wait_until_build_cancelled
24 24
25 25class TestProjectPageBase(SeleniumFunctionalTestCase):
26@pytest.mark.django_db
27@pytest.mark.order("last")
28class 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): 185class 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")
716class 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