1
0
mirror of https://git.yoctoproject.org/poky synced 2026-05-08 17:19:20 +00:00

bitbake: toaster/tests: Refactorize tests/functional

- Split testcases from test_project_page_tab_config into tow files
- Added new testcases in test_project_config
    - Test changing distro variable
    - Test setting IMAGE_INSTALL:append variable
    - Test setting PACKAGE_CLASSES variable
    - Test creating new bitbake variable

(Bitbake rev: 649218c648b79a89b0e91aa80d8c9bf8fa2de645)

Signed-off-by: Alassane Yattara <alassane.yattara@savoirfairelinux.com>
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
This commit is contained in:
Alassane Yattara
2023-12-08 02:53:18 +01:00
committed by Richard Purdie
parent 49128ef8ba
commit d5a6e3b546
2 changed files with 554 additions and 276 deletions
@@ -0,0 +1,335 @@
#! /usr/bin/env python3 #
# BitBake Toaster UI tests implementation
#
# Copyright (C) 2023 Savoir-faire Linux
#
# SPDX-License-Identifier: GPL-2.0-only
#
import string
import random
import pytest
from django.urls import reverse
from selenium.webdriver import Keys
from selenium.webdriver.support.select import Select
from selenium.common.exceptions import TimeoutException
from tests.functional.functional_helpers import SeleniumFunctionalTestCase
from selenium.webdriver.common.by import By
from .utils import get_projectId_from_url
@pytest.mark.django_db
@pytest.mark.order("last")
class TestProjectConfig(SeleniumFunctionalTestCase):
project_id = None
PROJECT_NAME = 'TestProjectConfig'
INVALID_PATH_START_TEXT = 'The directory path should either start with a /'
INVALID_PATH_CHAR_TEXT = 'The directory path cannot include spaces or ' \
'any of these characters'
def _create_project(self, project_name):
""" Create/Test new project using:
- Project Name: Any string
- Release: Any string
- Merge Toaster settings: True or False
"""
self.get(reverse('newproject'))
self.wait_until_visible('#new-project-name', poll=2)
self.find("#new-project-name").send_keys(project_name)
select = Select(self.find("#projectversion"))
select.select_by_value('3')
# check merge toaster settings
checkbox = self.find('.checkbox-mergeattr')
if not checkbox.is_selected():
checkbox.click()
if self.PROJECT_NAME != 'TestProjectConfig':
# Reset project name if it's not the default one
self.PROJECT_NAME = 'TestProjectConfig'
self.find("#create-project-button").click()
try:
self.wait_until_visible('#hint-error-project-name', poll=2)
url = reverse('project', args=(TestProjectConfig.project_id, ))
self.get(url)
self.wait_until_visible('#config-nav', poll=3)
except TimeoutException:
self.wait_until_visible('#config-nav', poll=3)
def _random_string(self, length):
return ''.join(
random.choice(string.ascii_letters) for _ in range(length)
)
def _get_config_nav_item(self, index):
config_nav = self.find('#config-nav')
return config_nav.find_elements(By.TAG_NAME, 'li')[index]
def _navigate_bbv_page(self):
""" Navigate to project BitBake variables page """
# check if the menu is displayed
if TestProjectConfig.project_id is None:
self._create_project(project_name=self._random_string(10))
current_url = self.driver.current_url
TestProjectConfig.project_id = get_projectId_from_url(current_url)
else:
url = reverse('projectconf', args=(TestProjectConfig.project_id,))
self.get(url)
self.wait_until_visible('#config-nav', poll=3)
bbv_page_link = self._get_config_nav_item(9)
bbv_page_link.click()
self.wait_until_visible('#config-nav', poll=3)
def test_no_underscore_iamgefs_type(self):
"""
Should not accept IMAGEFS_TYPE with an underscore
"""
self._navigate_bbv_page()
imagefs_type = "foo_bar"
self.wait_until_visible('#change-image_fstypes-icon', poll=2)
self.click('#change-image_fstypes-icon')
self.enter_text('#new-imagefs_types', imagefs_type)
element = self.wait_until_visible('#hintError-image-fs_type', poll=2)
self.assertTrue(("A valid image type cannot include underscores" in element.text),
"Did not find underscore error message")
def test_checkbox_verification(self):
"""
Should automatically check the checkbox if user enters value
text box, if value is there in the checkbox.
"""
self._navigate_bbv_page()
imagefs_type = "btrfs"
self.wait_until_visible('#change-image_fstypes-icon', poll=2)
self.click('#change-image_fstypes-icon')
self.enter_text('#new-imagefs_types', imagefs_type)
checkboxes = self.driver.find_elements(By.XPATH, "//input[@class='fs-checkbox-fstypes']")
for checkbox in checkboxes:
if checkbox.get_attribute("value") == "btrfs":
self.assertEqual(checkbox.is_selected(), True)
def test_textbox_with_checkbox_verification(self):
"""
Should automatically add or remove value in textbox, if user checks
or unchecks checkboxes.
"""
self._navigate_bbv_page()
self.wait_until_visible('#change-image_fstypes-icon', poll=2)
self.click('#change-image_fstypes-icon')
checkboxes_selector = '.fs-checkbox-fstypes'
self.wait_until_visible(checkboxes_selector, poll=2)
checkboxes = self.find_all(checkboxes_selector)
for checkbox in checkboxes:
if checkbox.get_attribute("value") == "cpio":
checkbox.click()
element = self.driver.find_element(By.ID, 'new-imagefs_types')
self.wait_until_visible('#new-imagefs_types', poll=2)
self.assertTrue(("cpio" in element.get_attribute('value'),
"Imagefs not added into the textbox"))
checkbox.click()
self.assertTrue(("cpio" not in element.text),
"Image still present in the textbox")
def test_set_download_dir(self):
"""
Validate the allowed and disallowed types in the directory field for
DL_DIR
"""
self._navigate_bbv_page()
# activate the input to edit download dir
try:
change_dl_dir_btn = self.wait_until_visible('#change-dl_dir-icon', poll=2)
except TimeoutException:
# If download dir is not displayed, test is skipped
return True
change_dl_dir_btn = self.wait_until_visible('#change-dl_dir-icon', poll=2)
change_dl_dir_btn.click()
# downloads dir path doesn't start with / or ${...}
input_field = self.wait_until_visible('#new-dl_dir', poll=2)
input_field.clear()
self.enter_text('#new-dl_dir', 'home/foo')
element = self.wait_until_visible('#hintError-initialChar-dl_dir', poll=2)
msg = 'downloads directory path starts with invalid character but ' \
'treated as valid'
self.assertTrue((self.INVALID_PATH_START_TEXT in element.text), msg)
# downloads dir path has a space
self.driver.find_element(By.ID, 'new-dl_dir').clear()
self.enter_text('#new-dl_dir', '/foo/bar a')
element = self.wait_until_visible('#hintError-dl_dir', poll=2)
msg = 'downloads directory path characters invalid but treated as valid'
self.assertTrue((self.INVALID_PATH_CHAR_TEXT in element.text), msg)
# downloads dir path starts with ${...} but has a space
self.driver.find_element(By.ID,'new-dl_dir').clear()
self.enter_text('#new-dl_dir', '${TOPDIR}/down foo')
element = self.wait_until_visible('#hintError-dl_dir', poll=2)
msg = 'downloads directory path characters invalid but treated as valid'
self.assertTrue((self.INVALID_PATH_CHAR_TEXT in element.text), msg)
# downloads dir path starts with /
self.driver.find_element(By.ID,'new-dl_dir').clear()
self.enter_text('#new-dl_dir', '/bar/foo')
hidden_element = self.driver.find_element(By.ID,'hintError-dl_dir')
self.assertEqual(hidden_element.is_displayed(), False,
'downloads directory path valid but treated as invalid')
# downloads dir path starts with ${...}
self.driver.find_element(By.ID,'new-dl_dir').clear()
self.enter_text('#new-dl_dir', '${TOPDIR}/down')
hidden_element = self.driver.find_element(By.ID,'hintError-dl_dir')
self.assertEqual(hidden_element.is_displayed(), False,
'downloads directory path valid but treated as invalid')
def test_set_sstate_dir(self):
"""
Validate the allowed and disallowed types in the directory field for
SSTATE_DIR
"""
self._navigate_bbv_page()
try:
self.wait_until_visible('#change-sstate_dir-icon', poll=2)
self.click('#change-sstate_dir-icon')
except TimeoutException:
# If sstate_dir is not displayed, test is skipped
return True
# path doesn't start with / or ${...}
input_field = self.wait_until_visible('#new-sstate_dir', poll=2)
input_field.clear()
self.enter_text('#new-sstate_dir', 'home/foo')
element = self.wait_until_visible('#hintError-initialChar-sstate_dir', poll=2)
msg = 'sstate directory path starts with invalid character but ' \
'treated as valid'
self.assertTrue((self.INVALID_PATH_START_TEXT in element.text), msg)
# path has a space
self.driver.find_element(By.ID, 'new-sstate_dir').clear()
self.enter_text('#new-sstate_dir', '/foo/bar a')
element = self.wait_until_visible('#hintError-sstate_dir', poll=2)
msg = 'sstate directory path characters invalid but treated as valid'
self.assertTrue((self.INVALID_PATH_CHAR_TEXT in element.text), msg)
# path starts with ${...} but has a space
self.driver.find_element(By.ID,'new-sstate_dir').clear()
self.enter_text('#new-sstate_dir', '${TOPDIR}/down foo')
element = self.wait_until_visible('#hintError-sstate_dir', poll=2)
msg = 'sstate directory path characters invalid but treated as valid'
self.assertTrue((self.INVALID_PATH_CHAR_TEXT in element.text), msg)
# path starts with /
self.driver.find_element(By.ID,'new-sstate_dir').clear()
self.enter_text('#new-sstate_dir', '/bar/foo')
hidden_element = self.driver.find_element(By.ID, 'hintError-sstate_dir')
self.assertEqual(hidden_element.is_displayed(), False,
'sstate directory path valid but treated as invalid')
# paths starts with ${...}
self.driver.find_element(By.ID, 'new-sstate_dir').clear()
self.enter_text('#new-sstate_dir', '${TOPDIR}/down')
hidden_element = self.driver.find_element(By.ID, 'hintError-sstate_dir')
self.assertEqual(hidden_element.is_displayed(), False,
'sstate directory path valid but treated as invalid')
def _change_bbv_value(self, **kwargs):
var_name, field, btn_id, input_id, value, save_btn, *_ = kwargs.values()
""" Change bitbake variable value """
self._navigate_bbv_page()
self.wait_until_visible(f'#{btn_id}', poll=2)
if kwargs.get('new_variable'):
self.find(f"#{btn_id}").clear()
self.enter_text(f"#{btn_id}", f"{var_name}")
else:
self.click(f'#{btn_id}')
self.wait_until_visible(f'#{input_id}', poll=2)
if kwargs.get('is_select'):
select = Select(self.find(f'#{input_id}'))
select.select_by_visible_text(value)
else:
self.find(f"#{input_id}").clear()
self.enter_text(f'#{input_id}', f'{value}')
self.click(f'#{save_btn}')
value_displayed = str(self.wait_until_visible(f'#{field}').text).lower()
msg = f'{var_name} variable not changed'
self.assertTrue(str(value).lower() in value_displayed, msg)
def test_change_distro_var(self):
""" Test changing distro variable """
self._change_bbv_value(
var_name='DISTRO',
field='distro',
btn_id='change-distro-icon',
input_id='new-distro',
value='poky-changed',
save_btn="apply-change-distro",
)
def test_set_image_install_append_var(self):
""" Test setting IMAGE_INSTALL:append variable """
self._change_bbv_value(
var_name='IMAGE_INSTALL:append',
field='image_install',
btn_id='change-image_install-icon',
input_id='new-image_install',
value='bash, apt, busybox',
save_btn="apply-change-image_install",
)
def test_set_package_classes_var(self):
""" Test setting PACKAGE_CLASSES variable """
self._change_bbv_value(
var_name='PACKAGE_CLASSES',
field='package_classes',
btn_id='change-package_classes-icon',
input_id='package_classes-select',
value='package_deb',
save_btn="apply-change-package_classes",
is_select=True,
)
def test_create_new_bbv(self):
""" Test creating new bitbake variable """
self._change_bbv_value(
var_name='New_Custom_Variable',
field='configvar-list',
btn_id='variable',
input_id='value',
value='new variable value',
save_btn="add-configvar-button",
new_variable=True
)
@@ -6,87 +6,81 @@
# SPDX-License-Identifier: GPL-2.0-only
#
from time import sleep
import string
import random
import pytest
from django.utils import timezone
from django.urls import reverse
from selenium.webdriver import Keys
from selenium.webdriver.support.select import Select
from selenium.common.exceptions import NoSuchElementException
from orm.models import Build, Project, Target
from selenium.common.exceptions import TimeoutException
from orm.models import Project
from tests.functional.functional_helpers import SeleniumFunctionalTestCase
from selenium.webdriver.common.by import By
from .utils import get_projectId_from_url, wait_until_build, wait_until_build_cancelled
@pytest.mark.django_db
@pytest.mark.order("last")
class TestProjectConfigTab(SeleniumFunctionalTestCase):
PROJECT_NAME = 'TestProjectConfigTab'
project_id = None
def setUp(self):
self.recipe = None
super().setUp()
release = '3'
project_name = 'projectmaster'
self._create_test_new_project(
project_name,
release,
False,
)
def _create_test_new_project(
self,
project_name,
release,
merge_toaster_settings,
):
def _create_project(self, project_name):
""" Create/Test new project using:
- Project Name: Any string
- Release: Any string
- Merge Toaster settings: True or False
"""
self.get(reverse('newproject'))
self.driver.find_element(By.ID,
"new-project-name").send_keys(project_name)
select = Select(self.find('#projectversion'))
select.select_by_value(release)
self.wait_until_visible('#new-project-name')
self.find("#new-project-name").send_keys(project_name)
select = Select(self.find("#projectversion"))
select.select_by_value('3')
# check merge toaster settings
checkbox = self.find('.checkbox-mergeattr')
if merge_toaster_settings:
if not checkbox.is_selected():
checkbox.click()
if not checkbox.is_selected():
checkbox.click()
if self.PROJECT_NAME != 'TestProjectConfigTab':
# Reset project name if it's not the default one
self.PROJECT_NAME = 'TestProjectConfigTab'
self.find("#create-project-button").click()
try:
self.wait_until_visible('#hint-error-project-name')
url = reverse('project', args=(TestProjectConfigTab.project_id, ))
self.get(url)
self.wait_until_visible('#config-nav', poll=3)
except TimeoutException:
self.wait_until_visible('#config-nav', poll=3)
def _random_string(self, length):
return ''.join(
random.choice(string.ascii_letters) for _ in range(length)
)
def _navigate_to_project_page(self):
# Navigate to project page
if TestProjectConfigTab.project_id is None:
self._create_project(project_name=self._random_string(10))
current_url = self.driver.current_url
TestProjectConfigTab.project_id = get_projectId_from_url(current_url)
else:
if checkbox.is_selected():
checkbox.click()
self.driver.find_element(By.ID, "create-project-button").click()
@classmethod
def _wait_until_build(cls, state):
while True:
try:
last_build_state = cls.driver.find_element(
By.XPATH,
'//*[@id="latest-builds"]/div[1]//div[@class="build-state"]',
)
build_state = last_build_state.get_attribute(
'data-build-state')
state_text = state.lower().split()
if any(x in str(build_state).lower() for x in state_text):
break
except NoSuchElementException:
continue
sleep(1)
url = reverse('project', args=(TestProjectConfigTab.project_id,))
self.get(url)
self.wait_until_visible('#config-nav')
def _create_builds(self):
# check search box can be use to build recipes
search_box = self.find('#build-input')
search_box.send_keys('core-image-minimal')
self.find('#build-button').click()
sleep(1)
self.wait_until_visible('#latest-builds')
# loop until reach the parsing state
self._wait_until_build('parsing starting cloning')
build_state = wait_until_build(self, 'parsing starting cloning')
lastest_builds = self.driver.find_elements(
By.XPATH,
'//div[@id="latest-builds"]/div',
@@ -100,8 +94,9 @@ class TestProjectConfigTab(SeleniumFunctionalTestCase):
'//span[@class="cancel-build-btn pull-right alert-link"]',
)
cancel_button.click()
sleep(1)
self._wait_until_build('cancelled')
if 'starting' not in build_state: # change build state when cancelled in starting state
wait_until_build_cancelled(self)
return build_state
def _get_tabs(self):
# tabs links list
@@ -114,64 +109,6 @@ class TestProjectConfigTab(SeleniumFunctionalTestCase):
config_nav = self.find('#config-nav')
return config_nav.find_elements(By.TAG_NAME, 'li')[index]
def _get_create_builds(self, **kwargs):
""" Create a build and return the build object """
# parameters for builds to associate with the projects
now = timezone.now()
release = '3'
project_name = 'projectmaster'
self._create_test_new_project(
project_name+"2",
release,
False,
)
self.project1_build_success = {
'project': Project.objects.get(id=1),
'started_on': now,
'completed_on': now,
'outcome': Build.SUCCEEDED
}
self.project1_build_failure = {
'project': Project.objects.get(id=1),
'started_on': now,
'completed_on': now,
'outcome': Build.FAILED
}
build1 = Build.objects.create(**self.project1_build_success)
build2 = Build.objects.create(**self.project1_build_failure)
# add some targets to these builds so they have recipe links
# (and so we can find the row in the ToasterTable corresponding to
# a particular build)
Target.objects.create(build=build1, target='foo')
Target.objects.create(build=build2, target='bar')
if kwargs:
# Create kwargs.get('success') builds with success status with target
# and kwargs.get('failure') builds with failure status with target
for i in range(kwargs.get('success', 0)):
now = timezone.now()
self.project1_build_success['started_on'] = now
self.project1_build_success[
'completed_on'] = now - timezone.timedelta(days=i)
build = Build.objects.create(**self.project1_build_success)
Target.objects.create(build=build,
target=f'{i}_success_recipe',
task=f'{i}_success_task')
for i in range(kwargs.get('failure', 0)):
now = timezone.now()
self.project1_build_failure['started_on'] = now
self.project1_build_failure[
'completed_on'] = now - timezone.timedelta(days=i)
build = Build.objects.create(**self.project1_build_failure)
Target.objects.create(build=build,
target=f'{i}_fail_recipe',
task=f'{i}_fail_task')
return build1, build2
def test_project_config_nav(self):
""" Test project config tab navigation:
- Check if the menu is displayed and contains the right elements:
@@ -188,13 +125,7 @@ class TestProjectConfigTab(SeleniumFunctionalTestCase):
- Actions
- Delete project
"""
# navigate to the project page
url = reverse("project", args=(1,))
self.get(url)
# check if the menu is displayed
self.wait_until_visible('#config-nav')
self._navigate_to_project_page()
def _get_config_nav_item(index):
config_nav = self.find('#config-nav')
return config_nav.find_elements(By.TAG_NAME, 'li')[index]
@@ -221,14 +152,14 @@ class TestProjectConfigTab(SeleniumFunctionalTestCase):
self.assertTrue("actions" in str(actions.text).lower())
conf_nav_list = [
[0, 'Configuration', f"/toastergui/project/1"], # config
[2, 'Custom images', f"/toastergui/project/1/customimages"], # custom images
[3, 'Image recipes', f"/toastergui/project/1/images"], # image recipes
[4, 'Software recipes', f"/toastergui/project/1/softwarerecipes"], # software recipes
[5, 'Machines', f"/toastergui/project/1/machines"], # machines
[6, 'Layers', f"/toastergui/project/1/layers"], # layers
[7, 'Distro', f"/toastergui/project/1/distro"], # distro
[9, 'BitBake variables', f"/toastergui/project/1/configuration"], # bitbake variables
[0, 'Configuration', f"/toastergui/project/{TestProjectConfigTab.project_id}"], # config
[2, 'Custom images', f"/toastergui/project/{TestProjectConfigTab.project_id}/customimages"], # custom images
[3, 'Image recipes', f"/toastergui/project/{TestProjectConfigTab.project_id}/images"], # image recipes
[4, 'Software recipes', f"/toastergui/project/{TestProjectConfigTab.project_id}/softwarerecipes"], # software recipes
[5, 'Machines', f"/toastergui/project/{TestProjectConfigTab.project_id}/machines"], # machines
[6, 'Layers', f"/toastergui/project/{TestProjectConfigTab.project_id}/layers"], # layers
[7, 'Distros', f"/toastergui/project/{TestProjectConfigTab.project_id}/distros"], # distro
# [9, 'BitBake variables', f"/toastergui/project/{TestProjectConfigTab.project_id}/configuration"], # bitbake variables
]
for index, item_name, url in conf_nav_list:
item = _get_config_nav_item(index)
@@ -236,6 +167,96 @@ class TestProjectConfigTab(SeleniumFunctionalTestCase):
item.click()
check_config_nav_item(index, item_name, url)
def test_image_recipe_editColumn(self):
""" Test the edit column feature in image recipe table on project page """
def test_edit_column(check_box_id):
# Check that we can hide/show table column
check_box = self.find(f'#{check_box_id}')
th_class = str(check_box_id).replace('checkbox-', '')
if check_box.is_selected():
# check if column is visible in table
self.assertTrue(
self.find(
f'#imagerecipestable thead th.{th_class}'
).is_displayed(),
f"The {th_class} column is checked in EditColumn dropdown, but it's not visible in table"
)
check_box.click()
# check if column is hidden in table
self.assertFalse(
self.find(
f'#imagerecipestable thead th.{th_class}'
).is_displayed(),
f"The {th_class} column is unchecked in EditColumn dropdown, but it's visible in table"
)
else:
# check if column is hidden in table
self.assertFalse(
self.find(
f'#imagerecipestable thead th.{th_class}'
).is_displayed(),
f"The {th_class} column is unchecked in EditColumn dropdown, but it's visible in table"
)
check_box.click()
# check if column is visible in table
self.assertTrue(
self.find(
f'#imagerecipestable thead th.{th_class}'
).is_displayed(),
f"The {th_class} column is checked in EditColumn dropdown, but it's not visible in table"
)
self._navigate_to_project_page()
# navigate to project image recipe page
recipe_image_page_link = self._get_config_nav_item(3)
recipe_image_page_link.click()
self.wait_until_present('#imagerecipestable tbody tr')
# Check edit column
edit_column = self.find('#edit-columns-button')
self.assertTrue(edit_column.is_displayed())
edit_column.click()
# Check dropdown is visible
self.wait_until_visible('ul.dropdown-menu.editcol')
# Check that we can hide the edit column
test_edit_column('checkbox-get_description_or_summary')
test_edit_column('checkbox-layer_version__get_vcs_reference')
test_edit_column('checkbox-layer_version__layer__name')
test_edit_column('checkbox-license')
test_edit_column('checkbox-recipe-file')
test_edit_column('checkbox-section')
test_edit_column('checkbox-version')
def test_image_recipe_show_rows(self):
""" Test the show rows feature in image recipe table on project page """
def test_show_rows(row_to_show, show_row_link):
# Check that we can show rows == row_to_show
show_row_link.select_by_value(str(row_to_show))
self.wait_until_visible('#imagerecipestable tbody tr')
self.assertTrue(
len(self.find_all('#imagerecipestable tbody tr')) == row_to_show
)
self._navigate_to_project_page()
# navigate to project image recipe page
recipe_image_page_link = self._get_config_nav_item(3)
recipe_image_page_link.click()
self.wait_until_present('#imagerecipestable tbody tr')
show_rows = self.driver.find_elements(
By.XPATH,
'//select[@class="form-control pagesize-imagerecipestable"]'
)
# Check show rows
for show_row_link in show_rows:
show_row_link = Select(show_row_link)
test_show_rows(10, show_row_link)
test_show_rows(25, show_row_link)
test_show_rows(50, show_row_link)
test_show_rows(100, show_row_link)
test_show_rows(150, show_row_link)
def test_project_config_tab_right_section(self):
""" Test project config tab right section contains five blocks:
- Machine:
@@ -257,35 +278,36 @@ class TestProjectConfigTab(SeleniumFunctionalTestCase):
- meta-poky
- meta-yocto-bsp
"""
# navigate to the project page
url = reverse("project", args=(1,))
self.get(url)
# Create a new project for this test
project_name = self._random_string(10)
self._create_project(project_name=project_name)
current_url = self.driver.current_url
TestProjectConfigTab.project_id = get_projectId_from_url(current_url)
url = current_url.split('?')[0]
# check if the menu is displayed
self.wait_until_visible('#project-page')
block_l = self.driver.find_element(
By.XPATH, '//*[@id="project-page"]/div[2]')
machine = self.find('#machine-section')
distro = self.find('#distro-section')
most_built_recipes = self.driver.find_element(
By.XPATH, '//*[@id="project-page"]/div[1]/div[3]')
project_release = self.driver.find_element(
By.XPATH, '//*[@id="project-page"]/div[1]/div[4]')
layers = block_l.find_element(By.ID, 'layer-container')
def check_machine_distro(self, item_name, new_item_name, block):
def check_machine_distro(self, item_name, new_item_name, block_id):
block = self.find(f'#{block_id}')
title = block.find_element(By.TAG_NAME, 'h3')
self.assertTrue(item_name.capitalize() in title.text)
edit_btn = block.find_element(By.ID, f'change-{item_name}-toggle')
edit_btn = self.find(f'#change-{item_name}-toggle')
edit_btn.click()
sleep(1)
name_input = block.find_element(By.ID, f'{item_name}-change-input')
self.wait_until_visible(f'#{item_name}-change-input')
name_input = self.find(f'#{item_name}-change-input')
name_input.clear()
name_input.send_keys(new_item_name)
change_btn = block.find_element(By.ID, f'{item_name}-change-btn')
change_btn = self.find(f'#{item_name}-change-btn')
change_btn.click()
sleep(1)
project_name = block.find_element(By.ID, f'project-{item_name}-name')
self.wait_until_visible(f'#project-{item_name}-name')
project_name = self.find(f'#project-{item_name}-name')
self.assertTrue(new_item_name in project_name.text)
# check change notificaiton is displayed
change_notification = self.find('#change-notification')
@@ -293,10 +315,30 @@ class TestProjectConfigTab(SeleniumFunctionalTestCase):
f'You have changed the {item_name} to: {new_item_name}' in change_notification.text
)
def rebuild_from_most_build_recipes(recipe_list_items):
checkbox = recipe_list_items[0].find_element(By.TAG_NAME, 'input')
checkbox.click()
build_btn = self.find('#freq-build-btn')
build_btn.click()
self.wait_until_visible('#latest-builds')
build_state = wait_until_build(self, 'parsing starting cloning queued')
lastest_builds = self.driver.find_elements(
By.XPATH,
'//div[@id="latest-builds"]/div'
)
last_build = lastest_builds[0]
self.assertTrue(len(lastest_builds) >= 2)
cancel_button = last_build.find_element(
By.XPATH,
'//span[@class="cancel-build-btn pull-right alert-link"]',
)
cancel_button.click()
if 'starting' not in build_state: # change build state when cancelled in starting state
wait_until_build_cancelled(self)
# Machine
check_machine_distro(self, 'machine', 'qemux86-64', machine)
check_machine_distro(self, 'machine', 'qemux86-64', 'machine-section')
# Distro
check_machine_distro(self, 'distro', 'poky-altcfg', distro)
check_machine_distro(self, 'distro', 'poky-altcfg', 'distro-section')
# Project release
title = project_release.find_element(By.TAG_NAME, 'h3')
@@ -304,7 +346,6 @@ class TestProjectConfigTab(SeleniumFunctionalTestCase):
self.assertTrue(
"Yocto Project master" in self.find('#project-release-title').text
)
# Layers
title = layers.find_element(By.TAG_NAME, 'h3')
self.assertTrue("Layers" in title.text)
@@ -314,7 +355,9 @@ class TestProjectConfigTab(SeleniumFunctionalTestCase):
# meta-yocto-bsp
layers_list = layers.find_element(By.ID, 'layers-in-project-list')
layers_list_items = layers_list.find_elements(By.TAG_NAME, 'li')
self.assertTrue(len(layers_list_items) == 3)
# remove all layers except the first three layers
for i in range(3, len(layers_list_items)):
layers_list_items[i].find_element(By.TAG_NAME, 'span').click()
# check can add a layer if exists
add_layer_input = layers.find_element(By.ID, 'layer-add-input')
add_layer_input.send_keys('meta-oe')
@@ -326,7 +369,7 @@ class TestProjectConfigTab(SeleniumFunctionalTestCase):
dropdown_item.click()
add_layer_btn = layers.find_element(By.ID, 'add-layer-btn')
add_layer_btn.click()
sleep(1)
self.wait_until_visible('#layers-in-project-list')
# check layer is added
layers_list_items = layers_list.find_elements(By.TAG_NAME, 'li')
self.assertTrue(len(layers_list_items) == 4)
@@ -334,48 +377,33 @@ class TestProjectConfigTab(SeleniumFunctionalTestCase):
# Most built recipes
title = most_built_recipes.find_element(By.TAG_NAME, 'h3')
self.assertTrue("Most built recipes" in title.text)
# Create a new builds 5
self._create_builds()
# Create a new builds
build_state = self._create_builds()
# Refresh the page
self.get(url)
self.driver.get(url)
sleep(1) # wait for page to load
self.wait_until_visible('#project-page')
self.wait_until_visible('#project-page', poll=3)
# check can select a recipe and build it
most_built_recipes = self.driver.find_element(
By.XPATH, '//*[@id="project-page"]/div[1]/div[3]')
recipe_list = most_built_recipes.find_element(By.ID, 'freq-build-list')
recipe_list_items = recipe_list.find_elements(By.TAG_NAME, 'li')
self.assertTrue(
len(recipe_list_items) > 0,
msg="No recipes found in the most built recipes list",
)
checkbox = recipe_list_items[0].find_element(By.TAG_NAME, 'input')
checkbox.click()
build_btn = self.find('#freq-build-btn')
build_btn.click()
sleep(1) # wait for page to load
self.wait_until_visible('#latest-builds')
self._wait_until_build('parsing starting cloning queueing')
lastest_builds = self.driver.find_elements(
By.XPATH,
'//div[@id="latest-builds"]/div'
)
last_build = lastest_builds[0]
cancel_button = last_build.find_element(
By.XPATH,
'//span[@class="cancel-build-btn pull-right alert-link"]',
)
cancel_button.click()
self.assertTrue(len(lastest_builds) == 2)
if 'starting' not in build_state: # Build will not appear in the list if canceled in starting state
self.assertTrue(
len(recipe_list_items) > 0,
msg="No recipes found in the most built recipes list",
)
rebuild_from_most_build_recipes(recipe_list_items)
else:
self.assertTrue(
len(recipe_list_items) == 0,
msg="Recipes found in the most built recipes list",
)
def test_project_page_tab_importlayer(self):
""" Test project page tab import layer """
# navigate to the project page
url = reverse("project", args=(1,))
self.get(url)
self._navigate_to_project_page()
# navigate to "Import layers" tab
import_layers_tab = self._get_tabs()[2]
import_layers_tab.find_element(By.TAG_NAME, 'a').click()
@@ -415,10 +443,10 @@ class TestProjectConfigTab(SeleniumFunctionalTestCase):
def test_project_page_custom_image_no_image(self):
""" Test project page tab "New custom image" when no custom image """
# navigate to the project page
url = reverse("project", args=(1,))
self.get(url)
project_name = self._random_string(10)
self._create_project(project_name=project_name)
current_url = self.driver.current_url
TestProjectConfigTab.project_id = get_projectId_from_url(current_url)
# navigate to "Custom image" tab
custom_image_section = self._get_config_nav_item(2)
custom_image_section.click()
@@ -433,8 +461,10 @@ class TestProjectConfigTab(SeleniumFunctionalTestCase):
div_empty_msg = self.find('#empty-state-customimagestable')
link_create_custom_image = div_empty_msg.find_element(
By.TAG_NAME, 'a')
last_project_id = Project.objects.get(name=project_name).id
self.assertTrue(last_project_id is not None)
self.assertTrue(
f"/toastergui/project/1/newcustomimage" in str(
f"/toastergui/project/{last_project_id}/newcustomimage" in str(
link_create_custom_image.get_attribute('href')
)
)
@@ -451,11 +481,7 @@ class TestProjectConfigTab(SeleniumFunctionalTestCase):
- Check image recipe build button works
- Check image recipe table features(show/hide column, pagination)
"""
# navigate to the project page
url = reverse("project", args=(1,))
self.get(url)
self.wait_until_visible('#config-nav')
self._navigate_to_project_page()
# navigate to "Images section"
images_section = self._get_config_nav_item(3)
images_section.click()
@@ -479,100 +505,17 @@ class TestProjectConfigTab(SeleniumFunctionalTestCase):
'//td[@class="add-del-layers"]'
)
build_btn.click()
self._wait_until_build('parsing starting cloning')
build_state = wait_until_build(self, 'parsing starting cloning queued')
lastest_builds = self.driver.find_elements(
By.XPATH,
'//div[@id="latest-builds"]/div'
)
self.assertTrue(len(lastest_builds) > 0)
def test_image_recipe_editColumn(self):
""" Test the edit column feature in image recipe table on project page """
self._get_create_builds(success=10, failure=10)
def test_edit_column(check_box_id):
# Check that we can hide/show table column
check_box = self.find(f'#{check_box_id}')
th_class = str(check_box_id).replace('checkbox-', '')
if check_box.is_selected():
# check if column is visible in table
self.assertTrue(
self.find(
f'#imagerecipestable thead th.{th_class}'
).is_displayed(),
f"The {th_class} column is checked in EditColumn dropdown, but it's not visible in table"
)
check_box.click()
# check if column is hidden in table
self.assertFalse(
self.find(
f'#imagerecipestable thead th.{th_class}'
).is_displayed(),
f"The {th_class} column is unchecked in EditColumn dropdown, but it's visible in table"
)
else:
# check if column is hidden in table
self.assertFalse(
self.find(
f'#imagerecipestable thead th.{th_class}'
).is_displayed(),
f"The {th_class} column is unchecked in EditColumn dropdown, but it's visible in table"
)
check_box.click()
# check if column is visible in table
self.assertTrue(
self.find(
f'#imagerecipestable thead th.{th_class}'
).is_displayed(),
f"The {th_class} column is checked in EditColumn dropdown, but it's not visible in table"
)
url = reverse('projectimagerecipes', args=(1,))
self.get(url)
self.wait_until_present('#imagerecipestable tbody tr')
# Check edit column
edit_column = self.find('#edit-columns-button')
self.assertTrue(edit_column.is_displayed())
edit_column.click()
# Check dropdown is visible
self.wait_until_visible('ul.dropdown-menu.editcol')
# Check that we can hide the edit column
test_edit_column('checkbox-get_description_or_summary')
test_edit_column('checkbox-layer_version__get_vcs_reference')
test_edit_column('checkbox-layer_version__layer__name')
test_edit_column('checkbox-license')
test_edit_column('checkbox-recipe-file')
test_edit_column('checkbox-section')
test_edit_column('checkbox-version')
def test_image_recipe_show_rows(self):
""" Test the show rows feature in image recipe table on project page """
self._get_create_builds(success=100, failure=100)
def test_show_rows(row_to_show, show_row_link):
# Check that we can show rows == row_to_show
show_row_link.select_by_value(str(row_to_show))
self.wait_until_present('#imagerecipestable tbody tr')
sleep(1)
self.assertTrue(
len(self.find_all('#imagerecipestable tbody tr')) == row_to_show
)
url = reverse('projectimagerecipes', args=(2,))
self.get(url)
self.wait_until_present('#imagerecipestable tbody tr')
show_rows = self.driver.find_elements(
last_build = lastest_builds[0]
cancel_button = last_build.find_element(
By.XPATH,
'//select[@class="form-control pagesize-imagerecipestable"]'
'//span[@class="cancel-build-btn pull-right alert-link"]',
)
# Check show rows
for show_row_link in show_rows:
show_row_link = Select(show_row_link)
test_show_rows(10, show_row_link)
test_show_rows(25, show_row_link)
test_show_rows(50, show_row_link)
test_show_rows(100, show_row_link)
test_show_rows(150, show_row_link)
cancel_button.click()
if 'starting' not in build_state: # change build state when cancelled in starting state
wait_until_build_cancelled(self)