blob: 03f64f8feff81adf9d82d79ebab812102819faa9 [file] [log] [blame]
Patrick Williamsac13d5f2023-11-24 18:59:46 -06001#! /usr/bin/env python3 #
2# BitBake Toaster UI tests implementation
3#
4# Copyright (C) 2023 Savoir-faire Linux
5#
6# SPDX-License-Identifier: GPL-2.0-only
7#
8
Patrick Williamsda295312023-12-05 16:48:56 -06009import random
10import string
Patrick Williamsac13d5f2023-11-24 18:59:46 -060011import pytest
Patrick Williamsda295312023-12-05 16:48:56 -060012from time import sleep
Patrick Williamsac13d5f2023-11-24 18:59:46 -060013from django.urls import reverse
Patrick Williamsda295312023-12-05 16:48:56 -060014from django.utils import timezone
15from selenium.webdriver.common.keys import Keys
Patrick Williamsac13d5f2023-11-24 18:59:46 -060016from selenium.webdriver.support.select import Select
Patrick Williamsda295312023-12-05 16:48:56 -060017from selenium.common.exceptions import NoSuchElementException, TimeoutException
Patrick Williamsac13d5f2023-11-24 18:59:46 -060018from tests.functional.functional_helpers import SeleniumFunctionalTestCase
Patrick Williamsda295312023-12-05 16:48:56 -060019from orm.models import Build, Project, Target
Patrick Williamsac13d5f2023-11-24 18:59:46 -060020from selenium.webdriver.common.by import By
21
22
23@pytest.mark.django_db
24class TestProjectPage(SeleniumFunctionalTestCase):
25
26 def setUp(self):
27 super().setUp()
28 release = '3'
Patrick Williamsda295312023-12-05 16:48:56 -060029 project_name = 'project_' + self.generate_random_string()
Patrick Williamsac13d5f2023-11-24 18:59:46 -060030 self._create_test_new_project(
31 project_name,
32 release,
33 False,
34 )
35
Patrick Williamsda295312023-12-05 16:48:56 -060036 def generate_random_string(self, length=10):
37 characters = string.ascii_letters + string.digits # alphabetic and numerical characters
38 random_string = ''.join(random.choice(characters) for _ in range(length))
39 return random_string
40
Patrick Williamsac13d5f2023-11-24 18:59:46 -060041 def _create_test_new_project(
42 self,
43 project_name,
44 release,
45 merge_toaster_settings,
46 ):
47 """ Create/Test new project using:
48 - Project Name: Any string
49 - Release: Any string
50 - Merge Toaster settings: True or False
51 """
52 self.get(reverse('newproject'))
53 self.driver.find_element(By.ID,
54 "new-project-name").send_keys(project_name)
55
56 select = Select(self.find('#projectversion'))
57 select.select_by_value(release)
58
59 # check merge toaster settings
60 checkbox = self.find('.checkbox-mergeattr')
61 if merge_toaster_settings:
62 if not checkbox.is_selected():
63 checkbox.click()
64 else:
65 if checkbox.is_selected():
66 checkbox.click()
67
68 self.driver.find_element(By.ID, "create-project-button").click()
69
Patrick Williamsda295312023-12-05 16:48:56 -060070 def _get_create_builds(self, **kwargs):
71 """ Create a build and return the build object """
72 # parameters for builds to associate with the projects
73 now = timezone.now()
74 release = '3'
75 project_name = 'projectmaster'
76 self._create_test_new_project(
77 project_name+"2",
78 release,
79 False,
80 )
81
82 self.project1_build_success = {
83 'project': Project.objects.get(id=1),
84 'started_on': now,
85 'completed_on': now,
86 'outcome': Build.SUCCEEDED
87 }
88
89 self.project1_build_failure = {
90 'project': Project.objects.get(id=1),
91 'started_on': now,
92 'completed_on': now,
93 'outcome': Build.FAILED
94 }
95 build1 = Build.objects.create(**self.project1_build_success)
96 build2 = Build.objects.create(**self.project1_build_failure)
97
98 # add some targets to these builds so they have recipe links
99 # (and so we can find the row in the ToasterTable corresponding to
100 # a particular build)
101 Target.objects.create(build=build1, target='foo')
102 Target.objects.create(build=build2, target='bar')
103
104 if kwargs:
105 # Create kwargs.get('success') builds with success status with target
106 # and kwargs.get('failure') builds with failure status with target
107 for i in range(kwargs.get('success', 0)):
108 now = timezone.now()
109 self.project1_build_success['started_on'] = now
110 self.project1_build_success[
111 'completed_on'] = now - timezone.timedelta(days=i)
112 build = Build.objects.create(**self.project1_build_success)
113 Target.objects.create(build=build,
114 target=f'{i}_success_recipe',
115 task=f'{i}_success_task')
116
117 for i in range(kwargs.get('failure', 0)):
118 now = timezone.now()
119 self.project1_build_failure['started_on'] = now
120 self.project1_build_failure[
121 'completed_on'] = now - timezone.timedelta(days=i)
122 build = Build.objects.create(**self.project1_build_failure)
123 Target.objects.create(build=build,
124 target=f'{i}_fail_recipe',
125 task=f'{i}_fail_task')
126 return build1, build2
127
128 def _mixin_test_table_edit_column(
129 self,
130 table_id,
131 edit_btn_id,
132 list_check_box_id: list
133 ):
134 # Check edit column
135 edit_column = self.find(f'#{edit_btn_id}')
136 self.assertTrue(edit_column.is_displayed())
137 edit_column.click()
138 # Check dropdown is visible
139 self.wait_until_visible('ul.dropdown-menu.editcol')
140 for check_box_id in list_check_box_id:
141 # Check that we can hide/show table column
142 check_box = self.find(f'#{check_box_id}')
143 th_class = str(check_box_id).replace('checkbox-', '')
144 if check_box.is_selected():
145 # check if column is visible in table
146 self.assertTrue(
147 self.find(
148 f'#{table_id} thead th.{th_class}'
149 ).is_displayed(),
150 f"The {th_class} column is checked in EditColumn dropdown, but it's not visible in table"
151 )
152 check_box.click()
153 # check if column is hidden in table
154 self.assertFalse(
155 self.find(
156 f'#{table_id} thead th.{th_class}'
157 ).is_displayed(),
158 f"The {th_class} column is unchecked in EditColumn dropdown, but it's visible in table"
159 )
160 else:
161 # check if column is hidden in table
162 self.assertFalse(
163 self.find(
164 f'#{table_id} thead th.{th_class}'
165 ).is_displayed(),
166 f"The {th_class} column is unchecked in EditColumn dropdown, but it's visible in table"
167 )
168 check_box.click()
169 # check if column is visible in table
170 self.assertTrue(
171 self.find(
172 f'#{table_id} thead th.{th_class}'
173 ).is_displayed(),
174 f"The {th_class} column is checked in EditColumn dropdown, but it's not visible in table"
175 )
176
177 def _get_config_nav_item(self, index):
178 config_nav = self.find('#config-nav')
179 return config_nav.find_elements(By.TAG_NAME, 'li')[index]
180
181 def _navigate_to_config_nav(self, nav_id, nav_index):
182 # navigate to the project page
183 url = reverse("project", args=(1,))
184 self.get(url)
185 self.wait_until_visible('#config-nav')
186 # click on "Software recipe" tab
187 soft_recipe = self._get_config_nav_item(nav_index)
188 soft_recipe.click()
189 self.wait_until_visible(f'#{nav_id}')
190
191 def _mixin_test_table_show_rows(self, table_selector, **kwargs):
192 """ Test the show rows feature in the builds table on the all builds page """
193 def test_show_rows(row_to_show, show_row_link):
194 # Check that we can show rows == row_to_show
195 show_row_link.select_by_value(str(row_to_show))
196 self.wait_until_visible(f'#{table_selector} tbody tr', poll=2)
197 self.assertTrue(
198 len(self.find_all(f'#{table_selector} tbody tr')) == row_to_show
199 )
200 self.wait_until_present(f'#{table_selector} tbody tr')
201 show_rows = self.driver.find_elements(
202 By.XPATH,
203 f'//select[@class="form-control pagesize-{table_selector}"]'
204 )
205 rows_to_show = [10, 25, 50, 100, 150]
206 to_skip = kwargs.get('to_skip', [])
207 # Check show rows
208 for show_row_link in show_rows:
209 show_row_link = Select(show_row_link)
210 for row_to_show in rows_to_show:
211 if row_to_show not in to_skip:
212 test_show_rows(row_to_show, show_row_link)
213
214 def _wait_until_build(self, state):
215 timeout = 10
216 start_time = 0
217 while True:
218 if start_time > timeout:
219 raise TimeoutException(
220 f'Build did not reach {state} state within {timeout} seconds'
221 )
222 try:
223 last_build_state = self.driver.find_element(
224 By.XPATH,
225 '//*[@id="latest-builds"]/div[1]//div[@class="build-state"]',
226 )
227 build_state = last_build_state.get_attribute(
228 'data-build-state')
229 state_text = state.lower().split()
230 if any(x in str(build_state).lower() for x in state_text):
231 break
232 except NoSuchElementException:
233 continue
234 start_time += 1
235 sleep(1) # take a breath and try again
236
237 def _mixin_test_table_search_input(self, **kwargs):
238 input_selector, input_text, searchBtn_selector, table_selector, *_ = kwargs.values()
239 # Test search input
240 self.wait_until_visible(f'#{input_selector}')
241 recipe_input = self.find(f'#{input_selector}')
242 recipe_input.send_keys(input_text)
243 self.find(f'#{searchBtn_selector}').click()
244 self.wait_until_visible(f'#{table_selector} tbody tr')
245 rows = self.find_all(f'#{table_selector} tbody tr')
246 self.assertTrue(len(rows) > 0)
247
248 def test_image_recipe_editColumn(self):
249 """ Test the edit column feature in image recipe table on project page """
250 self._get_create_builds(success=10, failure=10)
251
252 url = reverse('projectimagerecipes', args=(1,))
253 self.get(url)
254 self.wait_until_present('#imagerecipestable tbody tr')
255
256 column_list = [
257 'get_description_or_summary', 'layer_version__get_vcs_reference',
258 'layer_version__layer__name', 'license', 'recipe-file', 'section',
259 'version'
260 ]
261
262 # Check that we can hide the edit column
263 self._mixin_test_table_edit_column(
264 'imagerecipestable',
265 'edit-columns-button',
266 [f'checkbox-{column}' for column in column_list]
267 )
268
Patrick Williamsac13d5f2023-11-24 18:59:46 -0600269 def test_page_header_on_project_page(self):
270 """ Check page header in project page:
271 - AT LEFT -> Logo of Yocto project, displayed, clickable
272 - "Toaster"+" Information icon", displayed, clickable
273 - "Server Icon" + "All builds", displayed, clickable
274 - "Directory Icon" + "All projects", displayed, clickable
275 - "Book Icon" + "Documentation", displayed, clickable
276 - AT RIGHT -> button "New project", displayed, clickable
277 """
278 # navigate to the project page
279 url = reverse("project", args=(1,))
280 self.get(url)
281
282 # check page header
283 # AT LEFT -> Logo of Yocto project
284 logo = self.driver.find_element(
285 By.XPATH,
286 "//div[@class='toaster-navbar-brand']",
287 )
288 logo_img = logo.find_element(By.TAG_NAME, 'img')
289 self.assertTrue(logo_img.is_displayed(),
290 'Logo of Yocto project not found')
291 self.assertTrue(
292 '/static/img/logo.png' in str(logo_img.get_attribute('src')),
293 'Logo of Yocto project not found'
294 )
295 # "Toaster"+" Information icon", clickable
296 toaster = self.driver.find_element(
297 By.XPATH,
298 "//div[@class='toaster-navbar-brand']//a[@class='brand']",
299 )
300 self.assertTrue(toaster.is_displayed(), 'Toaster not found')
301 self.assertTrue(toaster.text == 'Toaster')
302 info_sign = self.find('.glyphicon-info-sign')
303 self.assertTrue(info_sign.is_displayed())
304
305 # "Server Icon" + "All builds"
306 all_builds = self.find('#navbar-all-builds')
307 all_builds_link = all_builds.find_element(By.TAG_NAME, 'a')
308 self.assertTrue("All builds" in all_builds_link.text)
309 self.assertTrue(
310 '/toastergui/builds/' in str(all_builds_link.get_attribute('href'))
311 )
312 server_icon = all_builds.find_element(By.TAG_NAME, 'i')
313 self.assertTrue(
314 server_icon.get_attribute('class') == 'glyphicon glyphicon-tasks'
315 )
316 self.assertTrue(server_icon.is_displayed())
317
318 # "Directory Icon" + "All projects"
319 all_projects = self.find('#navbar-all-projects')
320 all_projects_link = all_projects.find_element(By.TAG_NAME, 'a')
321 self.assertTrue("All projects" in all_projects_link.text)
322 self.assertTrue(
323 '/toastergui/projects/' in str(all_projects_link.get_attribute(
324 'href'))
325 )
326 dir_icon = all_projects.find_element(By.TAG_NAME, 'i')
327 self.assertTrue(
328 dir_icon.get_attribute('class') == 'icon-folder-open'
329 )
330 self.assertTrue(dir_icon.is_displayed())
331
332 # "Book Icon" + "Documentation"
333 toaster_docs_link = self.find('#navbar-docs')
334 toaster_docs_link_link = toaster_docs_link.find_element(By.TAG_NAME,
335 'a')
336 self.assertTrue("Documentation" in toaster_docs_link_link.text)
337 self.assertTrue(
338 toaster_docs_link_link.get_attribute('href') == 'http://docs.yoctoproject.org/toaster-manual/index.html#toaster-user-manual'
339 )
340 book_icon = toaster_docs_link.find_element(By.TAG_NAME, 'i')
341 self.assertTrue(
342 book_icon.get_attribute('class') == 'glyphicon glyphicon-book'
343 )
344 self.assertTrue(book_icon.is_displayed())
345
346 # AT RIGHT -> button "New project"
347 new_project_button = self.find('#new-project-button')
348 self.assertTrue(new_project_button.is_displayed())
349 self.assertTrue(new_project_button.text == 'New project')
350 new_project_button.click()
351 self.assertTrue(
352 '/toastergui/newproject/' in str(self.driver.current_url)
353 )
354
355 def test_edit_project_name(self):
356 """ Test edit project name:
357 - Click on "Edit" icon button
358 - Change project name
359 - Click on "Save" button
360 - Check project name is changed
361 """
362 # navigate to the project page
363 url = reverse("project", args=(1,))
364 self.get(url)
365
366 # click on "Edit" icon button
367 self.wait_until_visible('#project-name-container')
368 edit_button = self.find('#project-change-form-toggle')
369 edit_button.click()
370 project_name_input = self.find('#project-name-change-input')
371 self.assertTrue(project_name_input.is_displayed())
372 project_name_input.clear()
373 project_name_input.send_keys('New Name')
374 self.find('#project-name-change-btn').click()
375
376 # check project name is changed
377 self.wait_until_visible('#project-name-container')
378 self.assertTrue(
379 'New Name' in str(self.find('#project-name-container').text)
380 )
381
382 def test_project_page_tabs(self):
383 """ Test project tabs:
384 - "configuration" tab
385 - "Builds" tab
386 - "Import layers" tab
387 - "New custom image" tab
388 Check search box used to build recipes
389 """
390 # navigate to the project page
391 url = reverse("project", args=(1,))
392 self.get(url)
393
394 # check "configuration" tab
395 self.wait_until_visible('#topbar-configuration-tab')
396 config_tab = self.find('#topbar-configuration-tab')
397 self.assertTrue(config_tab.get_attribute('class') == 'active')
Patrick Williamsda295312023-12-05 16:48:56 -0600398 self.assertTrue('Configuration' in str(config_tab.text))
Patrick Williamsac13d5f2023-11-24 18:59:46 -0600399 self.assertTrue(
Patrick Williamsda295312023-12-05 16:48:56 -0600400 f"/toastergui/project/1" in str(self.driver.current_url)
Patrick Williamsac13d5f2023-11-24 18:59:46 -0600401 )
402
403 def get_tabs():
404 # tabs links list
405 return self.driver.find_elements(
406 By.XPATH,
407 '//div[@id="project-topbar"]//li'
408 )
409
410 def check_tab_link(tab_index, tab_name, url):
411 tab = get_tabs()[tab_index]
412 tab_link = tab.find_element(By.TAG_NAME, 'a')
413 self.assertTrue(url in tab_link.get_attribute('href'))
414 self.assertTrue(tab_name in tab_link.text)
415 self.assertTrue(tab.get_attribute('class') == 'active')
416
417 # check "Builds" tab
418 builds_tab = get_tabs()[1]
419 builds_tab.find_element(By.TAG_NAME, 'a').click()
420 check_tab_link(
421 1,
422 'Builds',
423 f"/toastergui/project/1/builds"
424 )
425
426 # check "Import layers" tab
427 import_layers_tab = get_tabs()[2]
428 import_layers_tab.find_element(By.TAG_NAME, 'a').click()
429 check_tab_link(
430 2,
431 'Import layer',
432 f"/toastergui/project/1/importlayer"
433 )
434
435 # check "New custom image" tab
436 new_custom_image_tab = get_tabs()[3]
437 new_custom_image_tab.find_element(By.TAG_NAME, 'a').click()
438 check_tab_link(
439 3,
440 'New custom image',
441 f"/toastergui/project/1/newcustomimage"
442 )
443
444 # check search box can be use to build recipes
445 search_box = self.find('#build-input')
446 search_box.send_keys('core-image-minimal')
447 self.find('#build-button').click()
448 self.wait_until_visible('#latest-builds')
449 lastest_builds = self.driver.find_elements(
450 By.XPATH,
451 '//div[@id="latest-builds"]',
452 )
453 last_build = lastest_builds[0]
454 self.assertTrue(
455 'core-image-minimal' in str(last_build.text)
456 )
Patrick Williamsda295312023-12-05 16:48:56 -0600457
458 def test_softwareRecipe_page(self):
459 """ Test software recipe page
460 - Check title "Compatible software recipes" is displayed
461 - Check search input
462 - Check "build recipe" button works
463 - Check software recipe table feature(show/hide column, pagination)
464 """
465 self._navigate_to_config_nav('softwarerecipestable', 4)
466 # check title "Compatible software recipes" is displayed
467 self.assertTrue("Compatible software recipes" in self.get_page_source())
468 # Test search input
469 self._mixin_test_table_search_input(
470 input_selector='search-input-softwarerecipestable',
471 input_text='busybox',
472 searchBtn_selector='search-submit-softwarerecipestable',
473 table_selector='softwarerecipestable'
474 )
475 # check "build recipe" button works
476 rows = self.find_all('#softwarerecipestable tbody tr')
477 image_to_build = rows[0]
478 build_btn = image_to_build.find_element(
479 By.XPATH,
480 '//td[@class="add-del-layers"]//a[1]'
481 )
482 build_btn.click()
483 self._wait_until_build('parsing starting cloning queued')
484 lastest_builds = self.driver.find_elements(
485 By.XPATH,
486 '//div[@id="latest-builds"]/div'
487 )
488 self.assertTrue(len(lastest_builds) > 0)
489
490 # check software recipe table feature(show/hide column, pagination)
491 self._navigate_to_config_nav('softwarerecipestable', 4)
492 column_list = [
493 'get_description_or_summary',
494 'layer_version__get_vcs_reference',
495 'layer_version__layer__name',
496 'license',
497 'recipe-file',
498 'section',
499 'version',
500 ]
501 self._mixin_test_table_edit_column(
502 'softwarerecipestable',
503 'edit-columns-button',
504 [f'checkbox-{column}' for column in column_list]
505 )
506 self._navigate_to_config_nav('softwarerecipestable', 4)
507 # check show rows(pagination)
508 self._mixin_test_table_show_rows(table_selector='softwarerecipestable')
509
510 def test_machines_page(self):
511 """ Test Machine page
512 - Check if title "Compatible machines" is displayed
513 - Check search input
514 - Check "Select machine" button works
515 - Check "Add layer" button works
516 - Check Machine table feature(show/hide column, pagination)
517 """
518 self._navigate_to_config_nav('machinestable', 5)
519 # check title "Compatible software recipes" is displayed
520 self.assertTrue("Compatible machines" in self.get_page_source())
521 # Test search input
522 self._mixin_test_table_search_input(
523 input_selector='search-input-machinestable',
524 input_text='qemux86-64',
525 searchBtn_selector='search-submit-machinestable',
526 table_selector='machinestable'
527 )
528 # check "Select machine" button works
529 rows = self.find_all('#machinestable tbody tr')
530 machine_to_select = rows[0]
531 select_btn = machine_to_select.find_element(
532 By.XPATH,
533 '//td[@class="add-del-layers"]//a[1]'
534 )
535 select_btn.send_keys(Keys.RETURN)
536 self.wait_until_visible('#config-nav')
537 project_machine_name = self.find('#project-machine-name')
538 self.assertTrue(
539 'qemux86-64' in project_machine_name.text
540 )
541 # check "Add layer" button works
542 self._navigate_to_config_nav('machinestable', 5)
543 # Search for a machine whit layer not in project
544 self._mixin_test_table_search_input(
545 input_selector='search-input-machinestable',
546 input_text='qemux86-64-tpm2',
547 searchBtn_selector='search-submit-machinestable',
548 table_selector='machinestable'
549 )
550 rows = self.find_all('#machinestable tbody tr')
551 machine_to_add = rows[0]
552 add_btn = machine_to_add.find_element(By.XPATH, '//td[@class="add-del-layers"]')
553 add_btn.click()
554 self.wait_until_visible('#change-notification')
555 change_notification = self.find('#change-notification')
556 self.assertTrue(
557 f'You have added 1 layer to your project' in str(change_notification.text)
558 )
559 # check Machine table feature(show/hide column, pagination)
560 self._navigate_to_config_nav('machinestable', 5)
561 column_list = [
562 'description',
563 'layer_version__get_vcs_reference',
564 'layer_version__layer__name',
565 'machinefile',
566 ]
567 self._mixin_test_table_edit_column(
568 'machinestable',
569 'edit-columns-button',
570 [f'checkbox-{column}' for column in column_list]
571 )
572 self._navigate_to_config_nav('machinestable', 5)
573 # check show rows(pagination)
574 self._mixin_test_table_show_rows(table_selector='machinestable')
575
576 def test_layers_page(self):
577 """ Test layers page
578 - Check if title "Compatible layerss" is displayed
579 - Check search input
580 - Check "Add layer" button works
581 - Check "Remove layer" button works
582 - Check layers table feature(show/hide column, pagination)
583 """
584 self._navigate_to_config_nav('layerstable', 6)
585 # check title "Compatible layers" is displayed
586 self.assertTrue("Compatible layers" in self.get_page_source())
587 # Test search input
588 input_text='meta-tanowrt'
589 self._mixin_test_table_search_input(
590 input_selector='search-input-layerstable',
591 input_text=input_text,
592 searchBtn_selector='search-submit-layerstable',
593 table_selector='layerstable'
594 )
595 # check "Add layer" button works
596 rows = self.find_all('#layerstable tbody tr')
597 layer_to_add = rows[0]
598 add_btn = layer_to_add.find_element(
599 By.XPATH,
600 '//td[@class="add-del-layers"]'
601 )
602 add_btn.click()
603 # check modal is displayed
604 self.wait_until_visible('#dependencies-modal', poll=2)
605 list_dependencies = self.find_all('#dependencies-list li')
606 # click on add-layers button
607 add_layers_btn = self.driver.find_element(
608 By.XPATH,
609 '//form[@id="dependencies-modal-form"]//button[@class="btn btn-primary"]'
610 )
611 add_layers_btn.click()
612 self.wait_until_visible('#change-notification')
613 change_notification = self.find('#change-notification')
614 self.assertTrue(
615 f'You have added {len(list_dependencies)+1} layers to your project: {input_text} and its dependencies' in str(change_notification.text)
616 )
617 # check "Remove layer" button works
618 rows = self.find_all('#layerstable tbody tr')
619 layer_to_remove = rows[0]
620 remove_btn = layer_to_remove.find_element(
621 By.XPATH,
622 '//td[@class="add-del-layers"]'
623 )
624 remove_btn.click()
625 self.wait_until_visible('#change-notification', poll=2)
626 change_notification = self.find('#change-notification')
627 self.assertTrue(
628 f'You have removed 1 layer from your project: {input_text}' in str(change_notification.text)
629 )
630 # check layers table feature(show/hide column, pagination)
631 self._navigate_to_config_nav('layerstable', 6)
632 column_list = [
633 'dependencies',
634 'revision',
635 'layer__vcs_url',
636 'git_subdir',
637 'layer__summary',
638 ]
639 self._mixin_test_table_edit_column(
640 'layerstable',
641 'edit-columns-button',
642 [f'checkbox-{column}' for column in column_list]
643 )
644 self._navigate_to_config_nav('layerstable', 6)
645 # check show rows(pagination)
646 self._mixin_test_table_show_rows(table_selector='layerstable')
647
648 def test_distro_page(self):
649 """ Test distros page
650 - Check if title "Compatible distros" is displayed
651 - Check search input
652 - Check "Add layer" button works
653 - Check distro table feature(show/hide column, pagination)
654 """
655 self._navigate_to_config_nav('distrostable', 7)
656 # check title "Compatible distros" is displayed
657 self.assertTrue("Compatible Distros" in self.get_page_source())
658 # Test search input
659 input_text='poky-altcfg'
660 self._mixin_test_table_search_input(
661 input_selector='search-input-distrostable',
662 input_text=input_text,
663 searchBtn_selector='search-submit-distrostable',
664 table_selector='distrostable'
665 )
666 # check "Add distro" button works
667 rows = self.find_all('#distrostable tbody tr')
668 distro_to_add = rows[0]
669 add_btn = distro_to_add.find_element(
670 By.XPATH,
671 '//td[@class="add-del-layers"]//a[1]'
672 )
673 add_btn.click()
674 self.wait_until_visible('#change-notification', poll=2)
675 change_notification = self.find('#change-notification')
676 self.assertTrue(
677 f'You have changed the distro to: {input_text}' in str(change_notification.text)
678 )
679 # check distro table feature(show/hide column, pagination)
680 self._navigate_to_config_nav('distrostable', 7)
681 column_list = [
682 'description',
683 'templatefile',
684 'layer_version__get_vcs_reference',
685 'layer_version__layer__name',
686 ]
687 self._mixin_test_table_edit_column(
688 'distrostable',
689 'edit-columns-button',
690 [f'checkbox-{column}' for column in column_list]
691 )
692 self._navigate_to_config_nav('distrostable', 7)
693 # check show rows(pagination)
694 self._mixin_test_table_show_rows(
695 table_selector='distrostable',
696 to_skip=[150]
697 )
698
699 def test_single_layer_page(self):
700 """ Test layer page
701 - Check if title is displayed
702 - Check add/remove layer button works
703 - Check tabs(layers, recipes, machines) are displayed
704 - Check left section is displayed
705 - Check layer name
706 - Check layer summary
707 - Check layer description
708 """
709 url = reverse("layerdetails", args=(1, 8))
710 self.get(url)
711 self.wait_until_visible('.page-header')
712 # check title is displayed
713 self.assertTrue(self.find('.page-header h1').is_displayed())
714
715 # check add layer button works
716 remove_layer_btn = self.find('#add-remove-layer-btn')
717 remove_layer_btn.click()
718 self.wait_until_visible('#change-notification', poll=2)
719 change_notification = self.find('#change-notification')
720 self.assertTrue(
721 f'You have removed 1 layer from your project' in str(change_notification.text)
722 )
723 # check add layer button works, 18 is the random layer id
724 add_layer_btn = self.find('#add-remove-layer-btn')
725 add_layer_btn.click()
726 self.wait_until_visible('#change-notification')
727 change_notification = self.find('#change-notification')
728 self.assertTrue(
729 f'You have added 1 layer to your project' in str(change_notification.text)
730 )
731 # check tabs(layers, recipes, machines) are displayed
732 tabs = self.find_all('.nav-tabs li')
733 self.assertEqual(len(tabs), 3)
734 # Check first tab
735 tabs[0].click()
736 self.assertTrue(
737 'active' in str(self.find('#information').get_attribute('class'))
738 )
739 # Check second tab
740 tabs[1].click()
741 self.assertTrue(
742 'active' in str(self.find('#recipes').get_attribute('class'))
743 )
744 # Check third tab
745 tabs[2].click()
746 self.assertTrue(
747 'active' in str(self.find('#machines').get_attribute('class'))
748 )
749 # Check left section is displayed
750 section = self.find('.well')
751 # Check layer name
752 self.assertTrue(
753 section.find_element(By.XPATH, '//h2[1]').is_displayed()
754 )
755 # Check layer summary
756 self.assertTrue("Summary" in section.text)
757 # Check layer description
758 self.assertTrue("Description" in section.text)
759
760 def test_single_recipe_page(self):
761 """ Test recipe page
762 - Check if title is displayed
763 - Check add recipe layer displayed
764 - Check left section is displayed
765 - Check recipe: name, summary, description, Version, Section,
766 License, Approx. packages included, Approx. size, Recipe file
767 """
768 url = reverse("recipedetails", args=(1, 53428))
769 self.get(url)
770 self.wait_until_visible('.page-header')
771 # check title is displayed
772 self.assertTrue(self.find('.page-header h1').is_displayed())
773 # check add recipe layer displayed
774 add_recipe_layer_btn = self.find('#add-layer-btn')
775 self.assertTrue(add_recipe_layer_btn.is_displayed())
776 # check left section is displayed
777 section = self.find('.well')
778 # Check recipe name
779 self.assertTrue(
780 section.find_element(By.XPATH, '//h2[1]').is_displayed()
781 )
782 # Check recipe sections details info are displayed
783 self.assertTrue("Summary" in section.text)
784 self.assertTrue("Description" in section.text)
785 self.assertTrue("Version" in section.text)
786 self.assertTrue("Section" in section.text)
787 self.assertTrue("License" in section.text)
788 self.assertTrue("Approx. packages included" in section.text)
789 self.assertTrue("Approx. package size" in section.text)
790 self.assertTrue("Recipe file" in section.text)