blob: 31177cc1ffed618ba0ac3c91059bdc9c21008eda [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 Williams169d7bc2024-01-05 11:33:25 -06009import os
Patrick Williamsda295312023-12-05 16:48:56 -060010import random
11import string
Patrick Williams169d7bc2024-01-05 11:33:25 -060012from unittest import skip
Patrick Williamsac13d5f2023-11-24 18:59:46 -060013import pytest
14from django.urls import reverse
Patrick Williamsda295312023-12-05 16:48:56 -060015from django.utils import timezone
16from selenium.webdriver.common.keys import Keys
Patrick Williamsac13d5f2023-11-24 18:59:46 -060017from selenium.webdriver.support.select import Select
Patrick Williams169d7bc2024-01-05 11:33:25 -060018from selenium.common.exceptions import TimeoutException
Patrick Williamsac13d5f2023-11-24 18:59:46 -060019from tests.functional.functional_helpers import SeleniumFunctionalTestCase
Patrick Williamsda295312023-12-05 16:48:56 -060020from orm.models import Build, Project, Target
Patrick Williamsac13d5f2023-11-24 18:59:46 -060021from selenium.webdriver.common.by import By
22
Patrick Williams169d7bc2024-01-05 11:33:25 -060023from .utils import get_projectId_from_url, wait_until_build, wait_until_build_cancelled
24
Patrick Williamsac13d5f2023-11-24 18:59:46 -060025
26@pytest.mark.django_db
Patrick Williams169d7bc2024-01-05 11:33:25 -060027@pytest.mark.order("last")
Patrick Williamsac13d5f2023-11-24 18:59:46 -060028class TestProjectPage(SeleniumFunctionalTestCase):
Patrick Williams169d7bc2024-01-05 11:33:25 -060029 project_id = None
30 PROJECT_NAME = 'TestProjectPage'
Patrick Williamsac13d5f2023-11-24 18:59:46 -060031
Patrick Williams169d7bc2024-01-05 11:33:25 -060032 def _create_project(self, project_name):
Patrick Williamsac13d5f2023-11-24 18:59:46 -060033 """ 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'))
Patrick Williams169d7bc2024-01-05 11:33:25 -060039 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')
Patrick Williamsac13d5f2023-11-24 18:59:46 -060043
44 # check merge toaster settings
45 checkbox = self.find('.checkbox-mergeattr')
Patrick Williams169d7bc2024-01-05 11:33:25 -060046 if not checkbox.is_selected():
47 checkbox.click()
Patrick Williamsac13d5f2023-11-24 18:59:46 -060048
Patrick Williams169d7bc2024-01-05 11:33:25 -060049 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')
Patrick Williamsac13d5f2023-11-24 18:59:46 -060078
Patrick Williamsda295312023-12-05 16:48:56 -060079 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()
Patrick Williamsda295312023-12-05 16:48:56 -060083 self.project1_build_success = {
Patrick Williams169d7bc2024-01-05 11:33:25 -060084 'project': Project.objects.get(id=TestProjectPage.project_id),
Patrick Williamsda295312023-12-05 16:48:56 -060085 'started_on': now,
86 'completed_on': now,
87 'outcome': Build.SUCCEEDED
88 }
89
90 self.project1_build_failure = {
Patrick Williams169d7bc2024-01-05 11:33:25 -060091 'project': Project.objects.get(id=TestProjectPage.project_id),
Patrick Williamsda295312023-12-05 16:48:56 -060092 '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
Patrick Williams169d7bc2024-01-05 11:33:25 -0600184 self._navigate_to_project_page()
Patrick Williamsda295312023-12-05 16:48:56 -0600185 # 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=2)
196 self.assertTrue(
197 len(self.find_all(f'#{table_selector} tbody tr')) == row_to_show
198 )
199 self.wait_until_present(f'#{table_selector} tbody tr')
200 show_rows = self.driver.find_elements(
201 By.XPATH,
202 f'//select[@class="form-control pagesize-{table_selector}"]'
203 )
204 rows_to_show = [10, 25, 50, 100, 150]
205 to_skip = kwargs.get('to_skip', [])
206 # Check show rows
207 for show_row_link in show_rows:
208 show_row_link = Select(show_row_link)
209 for row_to_show in rows_to_show:
210 if row_to_show not in to_skip:
211 test_show_rows(row_to_show, show_row_link)
212
Patrick Williamsda295312023-12-05 16:48:56 -0600213 def _mixin_test_table_search_input(self, **kwargs):
214 input_selector, input_text, searchBtn_selector, table_selector, *_ = kwargs.values()
215 # Test search input
216 self.wait_until_visible(f'#{input_selector}')
217 recipe_input = self.find(f'#{input_selector}')
218 recipe_input.send_keys(input_text)
219 self.find(f'#{searchBtn_selector}').click()
220 self.wait_until_visible(f'#{table_selector} tbody tr')
221 rows = self.find_all(f'#{table_selector} tbody tr')
222 self.assertTrue(len(rows) > 0)
223
Patrick Williams169d7bc2024-01-05 11:33:25 -0600224 def test_create_project(self):
225 """ Create/Test new project using:
226 - Project Name: Any string
227 - Release: Any string
228 - Merge Toaster settings: True or False
229 """
230 self._create_project(project_name=self.PROJECT_NAME)
231
Patrick Williamsda295312023-12-05 16:48:56 -0600232 def test_image_recipe_editColumn(self):
233 """ Test the edit column feature in image recipe table on project page """
234 self._get_create_builds(success=10, failure=10)
235
Patrick Williams169d7bc2024-01-05 11:33:25 -0600236 url = reverse('projectimagerecipes', args=(TestProjectPage.project_id,))
Patrick Williamsda295312023-12-05 16:48:56 -0600237 self.get(url)
238 self.wait_until_present('#imagerecipestable tbody tr')
239
240 column_list = [
241 'get_description_or_summary', 'layer_version__get_vcs_reference',
242 'layer_version__layer__name', 'license', 'recipe-file', 'section',
243 'version'
244 ]
245
246 # Check that we can hide the edit column
247 self._mixin_test_table_edit_column(
248 'imagerecipestable',
249 'edit-columns-button',
250 [f'checkbox-{column}' for column in column_list]
251 )
252
Patrick Williamsac13d5f2023-11-24 18:59:46 -0600253 def test_page_header_on_project_page(self):
254 """ Check page header in project page:
255 - AT LEFT -> Logo of Yocto project, displayed, clickable
256 - "Toaster"+" Information icon", displayed, clickable
257 - "Server Icon" + "All builds", displayed, clickable
258 - "Directory Icon" + "All projects", displayed, clickable
259 - "Book Icon" + "Documentation", displayed, clickable
260 - AT RIGHT -> button "New project", displayed, clickable
261 """
262 # navigate to the project page
Patrick Williams169d7bc2024-01-05 11:33:25 -0600263 self._navigate_to_project_page()
Patrick Williamsac13d5f2023-11-24 18:59:46 -0600264
265 # check page header
266 # AT LEFT -> Logo of Yocto project
267 logo = self.driver.find_element(
268 By.XPATH,
269 "//div[@class='toaster-navbar-brand']",
270 )
271 logo_img = logo.find_element(By.TAG_NAME, 'img')
272 self.assertTrue(logo_img.is_displayed(),
273 'Logo of Yocto project not found')
274 self.assertTrue(
275 '/static/img/logo.png' in str(logo_img.get_attribute('src')),
276 'Logo of Yocto project not found'
277 )
278 # "Toaster"+" Information icon", clickable
279 toaster = self.driver.find_element(
280 By.XPATH,
281 "//div[@class='toaster-navbar-brand']//a[@class='brand']",
282 )
283 self.assertTrue(toaster.is_displayed(), 'Toaster not found')
284 self.assertTrue(toaster.text == 'Toaster')
285 info_sign = self.find('.glyphicon-info-sign')
286 self.assertTrue(info_sign.is_displayed())
287
288 # "Server Icon" + "All builds"
289 all_builds = self.find('#navbar-all-builds')
290 all_builds_link = all_builds.find_element(By.TAG_NAME, 'a')
291 self.assertTrue("All builds" in all_builds_link.text)
292 self.assertTrue(
293 '/toastergui/builds/' in str(all_builds_link.get_attribute('href'))
294 )
295 server_icon = all_builds.find_element(By.TAG_NAME, 'i')
296 self.assertTrue(
297 server_icon.get_attribute('class') == 'glyphicon glyphicon-tasks'
298 )
299 self.assertTrue(server_icon.is_displayed())
300
301 # "Directory Icon" + "All projects"
302 all_projects = self.find('#navbar-all-projects')
303 all_projects_link = all_projects.find_element(By.TAG_NAME, 'a')
304 self.assertTrue("All projects" in all_projects_link.text)
305 self.assertTrue(
306 '/toastergui/projects/' in str(all_projects_link.get_attribute(
307 'href'))
308 )
309 dir_icon = all_projects.find_element(By.TAG_NAME, 'i')
310 self.assertTrue(
311 dir_icon.get_attribute('class') == 'icon-folder-open'
312 )
313 self.assertTrue(dir_icon.is_displayed())
314
315 # "Book Icon" + "Documentation"
316 toaster_docs_link = self.find('#navbar-docs')
317 toaster_docs_link_link = toaster_docs_link.find_element(By.TAG_NAME,
318 'a')
319 self.assertTrue("Documentation" in toaster_docs_link_link.text)
320 self.assertTrue(
321 toaster_docs_link_link.get_attribute('href') == 'http://docs.yoctoproject.org/toaster-manual/index.html#toaster-user-manual'
322 )
323 book_icon = toaster_docs_link.find_element(By.TAG_NAME, 'i')
324 self.assertTrue(
325 book_icon.get_attribute('class') == 'glyphicon glyphicon-book'
326 )
327 self.assertTrue(book_icon.is_displayed())
328
329 # AT RIGHT -> button "New project"
330 new_project_button = self.find('#new-project-button')
331 self.assertTrue(new_project_button.is_displayed())
332 self.assertTrue(new_project_button.text == 'New project')
333 new_project_button.click()
334 self.assertTrue(
335 '/toastergui/newproject/' in str(self.driver.current_url)
336 )
337
338 def test_edit_project_name(self):
339 """ Test edit project name:
340 - Click on "Edit" icon button
341 - Change project name
342 - Click on "Save" button
343 - Check project name is changed
344 """
345 # navigate to the project page
Patrick Williams169d7bc2024-01-05 11:33:25 -0600346 self._navigate_to_project_page()
Patrick Williamsac13d5f2023-11-24 18:59:46 -0600347
348 # click on "Edit" icon button
349 self.wait_until_visible('#project-name-container')
350 edit_button = self.find('#project-change-form-toggle')
351 edit_button.click()
352 project_name_input = self.find('#project-name-change-input')
353 self.assertTrue(project_name_input.is_displayed())
354 project_name_input.clear()
355 project_name_input.send_keys('New Name')
356 self.find('#project-name-change-btn').click()
357
358 # check project name is changed
359 self.wait_until_visible('#project-name-container')
360 self.assertTrue(
361 'New Name' in str(self.find('#project-name-container').text)
362 )
363
364 def test_project_page_tabs(self):
365 """ Test project tabs:
366 - "configuration" tab
367 - "Builds" tab
368 - "Import layers" tab
369 - "New custom image" tab
370 Check search box used to build recipes
371 """
372 # navigate to the project page
Patrick Williams169d7bc2024-01-05 11:33:25 -0600373 self._navigate_to_project_page()
Patrick Williamsac13d5f2023-11-24 18:59:46 -0600374
375 # check "configuration" tab
376 self.wait_until_visible('#topbar-configuration-tab')
377 config_tab = self.find('#topbar-configuration-tab')
378 self.assertTrue(config_tab.get_attribute('class') == 'active')
Patrick Williamsda295312023-12-05 16:48:56 -0600379 self.assertTrue('Configuration' in str(config_tab.text))
Patrick Williamsac13d5f2023-11-24 18:59:46 -0600380 self.assertTrue(
Patrick Williams169d7bc2024-01-05 11:33:25 -0600381 f"/toastergui/project/{TestProjectPage.project_id}" in str(self.driver.current_url)
Patrick Williamsac13d5f2023-11-24 18:59:46 -0600382 )
383
384 def get_tabs():
385 # tabs links list
386 return self.driver.find_elements(
387 By.XPATH,
388 '//div[@id="project-topbar"]//li'
389 )
390
391 def check_tab_link(tab_index, tab_name, url):
392 tab = get_tabs()[tab_index]
393 tab_link = tab.find_element(By.TAG_NAME, 'a')
394 self.assertTrue(url in tab_link.get_attribute('href'))
395 self.assertTrue(tab_name in tab_link.text)
396 self.assertTrue(tab.get_attribute('class') == 'active')
397
398 # check "Builds" tab
399 builds_tab = get_tabs()[1]
400 builds_tab.find_element(By.TAG_NAME, 'a').click()
401 check_tab_link(
402 1,
403 'Builds',
Patrick Williams169d7bc2024-01-05 11:33:25 -0600404 f"/toastergui/project/{TestProjectPage.project_id}/builds"
Patrick Williamsac13d5f2023-11-24 18:59:46 -0600405 )
406
407 # check "Import layers" tab
408 import_layers_tab = get_tabs()[2]
409 import_layers_tab.find_element(By.TAG_NAME, 'a').click()
410 check_tab_link(
411 2,
412 'Import layer',
Patrick Williams169d7bc2024-01-05 11:33:25 -0600413 f"/toastergui/project/{TestProjectPage.project_id}/importlayer"
Patrick Williamsac13d5f2023-11-24 18:59:46 -0600414 )
415
416 # check "New custom image" tab
417 new_custom_image_tab = get_tabs()[3]
418 new_custom_image_tab.find_element(By.TAG_NAME, 'a').click()
419 check_tab_link(
420 3,
421 'New custom image',
Patrick Williams169d7bc2024-01-05 11:33:25 -0600422 f"/toastergui/project/{TestProjectPage.project_id}/newcustomimage"
Patrick Williamsac13d5f2023-11-24 18:59:46 -0600423 )
424
425 # check search box can be use to build recipes
426 search_box = self.find('#build-input')
427 search_box.send_keys('core-image-minimal')
428 self.find('#build-button').click()
429 self.wait_until_visible('#latest-builds')
430 lastest_builds = self.driver.find_elements(
431 By.XPATH,
432 '//div[@id="latest-builds"]',
433 )
434 last_build = lastest_builds[0]
435 self.assertTrue(
436 'core-image-minimal' in str(last_build.text)
437 )
Patrick Williamsda295312023-12-05 16:48:56 -0600438
439 def test_softwareRecipe_page(self):
440 """ Test software recipe page
441 - Check title "Compatible software recipes" is displayed
442 - Check search input
443 - Check "build recipe" button works
444 - Check software recipe table feature(show/hide column, pagination)
445 """
446 self._navigate_to_config_nav('softwarerecipestable', 4)
447 # check title "Compatible software recipes" is displayed
448 self.assertTrue("Compatible software recipes" in self.get_page_source())
449 # Test search input
450 self._mixin_test_table_search_input(
451 input_selector='search-input-softwarerecipestable',
452 input_text='busybox',
453 searchBtn_selector='search-submit-softwarerecipestable',
454 table_selector='softwarerecipestable'
455 )
456 # check "build recipe" button works
457 rows = self.find_all('#softwarerecipestable tbody tr')
458 image_to_build = rows[0]
459 build_btn = image_to_build.find_element(
460 By.XPATH,
461 '//td[@class="add-del-layers"]//a[1]'
462 )
463 build_btn.click()
Patrick Williams169d7bc2024-01-05 11:33:25 -0600464 build_state = wait_until_build(self, 'queued cloning starting parsing failed')
Patrick Williamsda295312023-12-05 16:48:56 -0600465 lastest_builds = self.driver.find_elements(
466 By.XPATH,
467 '//div[@id="latest-builds"]/div'
468 )
469 self.assertTrue(len(lastest_builds) > 0)
Patrick Williams169d7bc2024-01-05 11:33:25 -0600470 last_build = lastest_builds[0]
471 cancel_button = last_build.find_element(
472 By.XPATH,
473 '//span[@class="cancel-build-btn pull-right alert-link"]',
474 )
475 cancel_button.click()
476 if 'starting' not in build_state: # change build state when cancelled in starting state
477 wait_until_build_cancelled(self)
Patrick Williamsda295312023-12-05 16:48:56 -0600478
479 # check software recipe table feature(show/hide column, pagination)
480 self._navigate_to_config_nav('softwarerecipestable', 4)
481 column_list = [
482 'get_description_or_summary',
483 'layer_version__get_vcs_reference',
484 'layer_version__layer__name',
485 'license',
486 'recipe-file',
487 'section',
488 'version',
489 ]
490 self._mixin_test_table_edit_column(
491 'softwarerecipestable',
492 'edit-columns-button',
493 [f'checkbox-{column}' for column in column_list]
494 )
495 self._navigate_to_config_nav('softwarerecipestable', 4)
496 # check show rows(pagination)
Patrick Williams169d7bc2024-01-05 11:33:25 -0600497 self._mixin_test_table_show_rows(
498 table_selector='softwarerecipestable',
499 to_skip=[150],
500 )
Patrick Williamsda295312023-12-05 16:48:56 -0600501
502 def test_machines_page(self):
503 """ Test Machine page
504 - Check if title "Compatible machines" is displayed
505 - Check search input
506 - Check "Select machine" button works
507 - Check "Add layer" button works
508 - Check Machine table feature(show/hide column, pagination)
509 """
510 self._navigate_to_config_nav('machinestable', 5)
511 # check title "Compatible software recipes" is displayed
512 self.assertTrue("Compatible machines" in self.get_page_source())
513 # Test search input
514 self._mixin_test_table_search_input(
515 input_selector='search-input-machinestable',
516 input_text='qemux86-64',
517 searchBtn_selector='search-submit-machinestable',
518 table_selector='machinestable'
519 )
520 # check "Select machine" button works
521 rows = self.find_all('#machinestable tbody tr')
522 machine_to_select = rows[0]
523 select_btn = machine_to_select.find_element(
524 By.XPATH,
525 '//td[@class="add-del-layers"]//a[1]'
526 )
527 select_btn.send_keys(Keys.RETURN)
528 self.wait_until_visible('#config-nav')
529 project_machine_name = self.find('#project-machine-name')
530 self.assertTrue(
531 'qemux86-64' in project_machine_name.text
532 )
533 # check "Add layer" button works
534 self._navigate_to_config_nav('machinestable', 5)
535 # Search for a machine whit layer not in project
536 self._mixin_test_table_search_input(
537 input_selector='search-input-machinestable',
538 input_text='qemux86-64-tpm2',
539 searchBtn_selector='search-submit-machinestable',
540 table_selector='machinestable'
541 )
Patrick Williams169d7bc2024-01-05 11:33:25 -0600542 self.wait_until_visible('#machinestable tbody tr', poll=3)
Patrick Williamsda295312023-12-05 16:48:56 -0600543 rows = self.find_all('#machinestable tbody tr')
544 machine_to_add = rows[0]
545 add_btn = machine_to_add.find_element(By.XPATH, '//td[@class="add-del-layers"]')
546 add_btn.click()
547 self.wait_until_visible('#change-notification')
548 change_notification = self.find('#change-notification')
549 self.assertTrue(
550 f'You have added 1 layer to your project' in str(change_notification.text)
551 )
552 # check Machine table feature(show/hide column, pagination)
553 self._navigate_to_config_nav('machinestable', 5)
554 column_list = [
555 'description',
556 'layer_version__get_vcs_reference',
557 'layer_version__layer__name',
558 'machinefile',
559 ]
560 self._mixin_test_table_edit_column(
561 'machinestable',
562 'edit-columns-button',
563 [f'checkbox-{column}' for column in column_list]
564 )
565 self._navigate_to_config_nav('machinestable', 5)
566 # check show rows(pagination)
Patrick Williams169d7bc2024-01-05 11:33:25 -0600567 self._mixin_test_table_show_rows(
568 table_selector='machinestable',
569 to_skip=[150],
570 )
Patrick Williamsda295312023-12-05 16:48:56 -0600571
572 def test_layers_page(self):
573 """ Test layers page
574 - Check if title "Compatible layerss" is displayed
575 - Check search input
576 - Check "Add layer" button works
577 - Check "Remove layer" button works
578 - Check layers table feature(show/hide column, pagination)
579 """
580 self._navigate_to_config_nav('layerstable', 6)
581 # check title "Compatible layers" is displayed
582 self.assertTrue("Compatible layers" in self.get_page_source())
583 # Test search input
584 input_text='meta-tanowrt'
585 self._mixin_test_table_search_input(
586 input_selector='search-input-layerstable',
587 input_text=input_text,
588 searchBtn_selector='search-submit-layerstable',
589 table_selector='layerstable'
590 )
591 # check "Add layer" button works
Patrick Williams169d7bc2024-01-05 11:33:25 -0600592 self.wait_until_visible('#layerstable tbody tr', poll=3)
Patrick Williamsda295312023-12-05 16:48:56 -0600593 rows = self.find_all('#layerstable tbody tr')
594 layer_to_add = rows[0]
595 add_btn = layer_to_add.find_element(
596 By.XPATH,
597 '//td[@class="add-del-layers"]'
598 )
599 add_btn.click()
600 # check modal is displayed
Patrick Williams169d7bc2024-01-05 11:33:25 -0600601 self.wait_until_visible('#dependencies-modal', poll=3)
Patrick Williamsda295312023-12-05 16:48:56 -0600602 list_dependencies = self.find_all('#dependencies-list li')
603 # click on add-layers button
604 add_layers_btn = self.driver.find_element(
605 By.XPATH,
606 '//form[@id="dependencies-modal-form"]//button[@class="btn btn-primary"]'
607 )
608 add_layers_btn.click()
609 self.wait_until_visible('#change-notification')
610 change_notification = self.find('#change-notification')
611 self.assertTrue(
612 f'You have added {len(list_dependencies)+1} layers to your project: {input_text} and its dependencies' in str(change_notification.text)
613 )
614 # check "Remove layer" button works
Patrick Williams169d7bc2024-01-05 11:33:25 -0600615 self.wait_until_visible('#layerstable tbody tr', poll=3)
Patrick Williamsda295312023-12-05 16:48:56 -0600616 rows = self.find_all('#layerstable tbody tr')
617 layer_to_remove = rows[0]
618 remove_btn = layer_to_remove.find_element(
619 By.XPATH,
620 '//td[@class="add-del-layers"]'
621 )
622 remove_btn.click()
623 self.wait_until_visible('#change-notification', poll=2)
624 change_notification = self.find('#change-notification')
625 self.assertTrue(
626 f'You have removed 1 layer from your project: {input_text}' in str(change_notification.text)
627 )
628 # check layers table feature(show/hide column, pagination)
629 self._navigate_to_config_nav('layerstable', 6)
630 column_list = [
631 'dependencies',
632 'revision',
633 'layer__vcs_url',
634 'git_subdir',
635 'layer__summary',
636 ]
637 self._mixin_test_table_edit_column(
638 'layerstable',
639 'edit-columns-button',
640 [f'checkbox-{column}' for column in column_list]
641 )
642 self._navigate_to_config_nav('layerstable', 6)
643 # check show rows(pagination)
Patrick Williams169d7bc2024-01-05 11:33:25 -0600644 self._mixin_test_table_show_rows(
645 table_selector='layerstable',
646 to_skip=[150],
647 )
Patrick Williamsda295312023-12-05 16:48:56 -0600648
649 def test_distro_page(self):
650 """ Test distros page
651 - Check if title "Compatible distros" is displayed
652 - Check search input
653 - Check "Add layer" button works
654 - Check distro table feature(show/hide column, pagination)
655 """
656 self._navigate_to_config_nav('distrostable', 7)
657 # check title "Compatible distros" is displayed
658 self.assertTrue("Compatible Distros" in self.get_page_source())
659 # Test search input
660 input_text='poky-altcfg'
661 self._mixin_test_table_search_input(
662 input_selector='search-input-distrostable',
663 input_text=input_text,
664 searchBtn_selector='search-submit-distrostable',
665 table_selector='distrostable'
666 )
667 # check "Add distro" button works
668 rows = self.find_all('#distrostable tbody tr')
669 distro_to_add = rows[0]
670 add_btn = distro_to_add.find_element(
671 By.XPATH,
672 '//td[@class="add-del-layers"]//a[1]'
673 )
674 add_btn.click()
675 self.wait_until_visible('#change-notification', poll=2)
676 change_notification = self.find('#change-notification')
677 self.assertTrue(
678 f'You have changed the distro to: {input_text}' in str(change_notification.text)
679 )
680 # check distro table feature(show/hide column, pagination)
681 self._navigate_to_config_nav('distrostable', 7)
682 column_list = [
683 'description',
684 'templatefile',
685 'layer_version__get_vcs_reference',
686 'layer_version__layer__name',
687 ]
688 self._mixin_test_table_edit_column(
689 'distrostable',
690 'edit-columns-button',
691 [f'checkbox-{column}' for column in column_list]
692 )
693 self._navigate_to_config_nav('distrostable', 7)
694 # check show rows(pagination)
695 self._mixin_test_table_show_rows(
696 table_selector='distrostable',
Patrick Williams169d7bc2024-01-05 11:33:25 -0600697 to_skip=[150],
Patrick Williamsda295312023-12-05 16:48:56 -0600698 )
699
700 def test_single_layer_page(self):
701 """ Test layer page
702 - Check if title is displayed
703 - Check add/remove layer button works
704 - Check tabs(layers, recipes, machines) are displayed
705 - Check left section is displayed
706 - Check layer name
707 - Check layer summary
708 - Check layer description
709 """
Patrick Williams169d7bc2024-01-05 11:33:25 -0600710 url = reverse("layerdetails", args=(TestProjectPage.project_id, 8))
Patrick Williamsda295312023-12-05 16:48:56 -0600711 self.get(url)
712 self.wait_until_visible('.page-header')
713 # check title is displayed
714 self.assertTrue(self.find('.page-header h1').is_displayed())
715
716 # check add layer button works
717 remove_layer_btn = self.find('#add-remove-layer-btn')
718 remove_layer_btn.click()
719 self.wait_until_visible('#change-notification', poll=2)
720 change_notification = self.find('#change-notification')
721 self.assertTrue(
722 f'You have removed 1 layer from your project' in str(change_notification.text)
723 )
724 # check add layer button works, 18 is the random layer id
725 add_layer_btn = self.find('#add-remove-layer-btn')
726 add_layer_btn.click()
727 self.wait_until_visible('#change-notification')
728 change_notification = self.find('#change-notification')
729 self.assertTrue(
730 f'You have added 1 layer to your project' in str(change_notification.text)
731 )
732 # check tabs(layers, recipes, machines) are displayed
733 tabs = self.find_all('.nav-tabs li')
734 self.assertEqual(len(tabs), 3)
735 # Check first tab
736 tabs[0].click()
737 self.assertTrue(
738 'active' in str(self.find('#information').get_attribute('class'))
739 )
740 # Check second tab
741 tabs[1].click()
742 self.assertTrue(
743 'active' in str(self.find('#recipes').get_attribute('class'))
744 )
745 # Check third tab
746 tabs[2].click()
747 self.assertTrue(
748 'active' in str(self.find('#machines').get_attribute('class'))
749 )
750 # Check left section is displayed
751 section = self.find('.well')
752 # Check layer name
753 self.assertTrue(
754 section.find_element(By.XPATH, '//h2[1]').is_displayed()
755 )
756 # Check layer summary
757 self.assertTrue("Summary" in section.text)
758 # Check layer description
759 self.assertTrue("Description" in section.text)
760
761 def test_single_recipe_page(self):
762 """ Test recipe page
763 - Check if title is displayed
764 - Check add recipe layer displayed
765 - Check left section is displayed
766 - Check recipe: name, summary, description, Version, Section,
767 License, Approx. packages included, Approx. size, Recipe file
768 """
Patrick Williams169d7bc2024-01-05 11:33:25 -0600769 url = reverse("recipedetails", args=(TestProjectPage.project_id, 53428))
Patrick Williamsda295312023-12-05 16:48:56 -0600770 self.get(url)
771 self.wait_until_visible('.page-header')
772 # check title is displayed
773 self.assertTrue(self.find('.page-header h1').is_displayed())
774 # check add recipe layer displayed
775 add_recipe_layer_btn = self.find('#add-layer-btn')
776 self.assertTrue(add_recipe_layer_btn.is_displayed())
777 # check left section is displayed
778 section = self.find('.well')
779 # Check recipe name
780 self.assertTrue(
781 section.find_element(By.XPATH, '//h2[1]').is_displayed()
782 )
783 # Check recipe sections details info are displayed
784 self.assertTrue("Summary" in section.text)
785 self.assertTrue("Description" in section.text)
786 self.assertTrue("Version" in section.text)
787 self.assertTrue("Section" in section.text)
788 self.assertTrue("License" in section.text)
789 self.assertTrue("Approx. packages included" in section.text)
790 self.assertTrue("Approx. package size" in section.text)
791 self.assertTrue("Recipe file" in section.text)