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