1
0
mirror of https://git.yoctoproject.org/poky synced 2026-06-02 01:19:52 +00:00

bitbake: toaster/tests: Bug fixes, functional tests dependent on each other

refactor test_create_project and test_project_page to remove their dependencies

(Bitbake rev: 54f7c0bb6ff435c4936c3422532aa071bd5b66e8)

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:19 +01:00
committed by Richard Purdie
parent d5a6e3b546
commit 23d3e2c718
2 changed files with 79 additions and 86 deletions
@@ -16,6 +16,7 @@ from selenium.webdriver.common.by import By
@pytest.mark.django_db @pytest.mark.django_db
@pytest.mark.order("last")
class TestCreateNewProject(SeleniumFunctionalTestCase): class TestCreateNewProject(SeleniumFunctionalTestCase):
def _create_test_new_project( def _create_test_new_project(
@@ -6,88 +6,89 @@
# SPDX-License-Identifier: GPL-2.0-only # SPDX-License-Identifier: GPL-2.0-only
# #
import os
import random import random
import string import string
from unittest import skip
import pytest import pytest
from time import sleep
from django.urls import reverse from django.urls import reverse
from django.utils import timezone from django.utils import timezone
from selenium.webdriver.common.keys import Keys from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support.select import Select from selenium.webdriver.support.select import Select
from selenium.common.exceptions import NoSuchElementException, TimeoutException from selenium.common.exceptions import TimeoutException
from tests.functional.functional_helpers import SeleniumFunctionalTestCase from tests.functional.functional_helpers import SeleniumFunctionalTestCase
from orm.models import Build, Project, Target from orm.models import Build, Project, Target
from selenium.webdriver.common.by import By 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.django_db
@pytest.mark.order("last")
class TestProjectPage(SeleniumFunctionalTestCase): class TestProjectPage(SeleniumFunctionalTestCase):
project_id = None
PROJECT_NAME = 'TestProjectPage'
def setUp(self): def _create_project(self, project_name):
super().setUp()
release = '3'
project_name = 'project_' + self.generate_random_string()
self._create_test_new_project(
project_name,
release,
False,
)
def generate_random_string(self, length=10):
characters = string.ascii_letters + string.digits # alphabetic and numerical characters
random_string = ''.join(random.choice(characters) for _ in range(length))
return random_string
def _create_test_new_project(
self,
project_name,
release,
merge_toaster_settings,
):
""" Create/Test new project using: """ Create/Test new project using:
- Project Name: Any string - Project Name: Any string
- Release: Any string - Release: Any string
- Merge Toaster settings: True or False - Merge Toaster settings: True or False
""" """
self.get(reverse('newproject')) self.get(reverse('newproject'))
self.driver.find_element(By.ID, self.wait_until_visible('#new-project-name')
"new-project-name").send_keys(project_name) self.find("#new-project-name").send_keys(project_name)
select = Select(self.find("#projectversion"))
select = Select(self.find('#projectversion')) select.select_by_value('3')
select.select_by_value(release)
# check merge toaster settings # check merge toaster settings
checkbox = self.find('.checkbox-mergeattr') checkbox = self.find('.checkbox-mergeattr')
if merge_toaster_settings: if not checkbox.is_selected():
if not checkbox.is_selected(): checkbox.click()
checkbox.click()
else:
if checkbox.is_selected():
checkbox.click()
self.driver.find_element(By.ID, "create-project-button").click() if self.PROJECT_NAME != 'TestProjectPage':
# Reset project name if it's not the default one
self.PROJECT_NAME = 'TestProjectPage'
self.find("#create-project-button").click()
try:
self.wait_until_visible('#hint-error-project-name')
url = reverse('project', args=(TestProjectPage.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 TestProjectPage.project_id is None:
self._create_project(project_name=self._random_string(10))
current_url = self.driver.current_url
TestProjectPage.project_id = get_projectId_from_url(current_url)
else:
url = reverse('project', args=(TestProjectPage.project_id,))
self.get(url)
self.wait_until_visible('#config-nav')
def _get_create_builds(self, **kwargs): def _get_create_builds(self, **kwargs):
""" Create a build and return the build object """ """ Create a build and return the build object """
# parameters for builds to associate with the projects # parameters for builds to associate with the projects
now = timezone.now() now = timezone.now()
release = '3'
project_name = 'projectmaster'
self._create_test_new_project(
project_name+"2",
release,
False,
)
self.project1_build_success = { self.project1_build_success = {
'project': Project.objects.get(id=1), 'project': Project.objects.get(id=TestProjectPage.project_id),
'started_on': now, 'started_on': now,
'completed_on': now, 'completed_on': now,
'outcome': Build.SUCCEEDED 'outcome': Build.SUCCEEDED
} }
self.project1_build_failure = { self.project1_build_failure = {
'project': Project.objects.get(id=1), 'project': Project.objects.get(id=TestProjectPage.project_id),
'started_on': now, 'started_on': now,
'completed_on': now, 'completed_on': now,
'outcome': Build.FAILED 'outcome': Build.FAILED
@@ -180,9 +181,7 @@ class TestProjectPage(SeleniumFunctionalTestCase):
def _navigate_to_config_nav(self, nav_id, nav_index): def _navigate_to_config_nav(self, nav_id, nav_index):
# navigate to the project page # navigate to the project page
url = reverse("project", args=(1,)) self._navigate_to_project_page()
self.get(url)
self.wait_until_visible('#config-nav')
# click on "Software recipe" tab # click on "Software recipe" tab
soft_recipe = self._get_config_nav_item(nav_index) soft_recipe = self._get_config_nav_item(nav_index)
soft_recipe.click() soft_recipe.click()
@@ -211,29 +210,6 @@ class TestProjectPage(SeleniumFunctionalTestCase):
if row_to_show not in to_skip: if row_to_show not in to_skip:
test_show_rows(row_to_show, show_row_link) test_show_rows(row_to_show, show_row_link)
def _wait_until_build(self, state):
timeout = 10
start_time = 0
while True:
if start_time > timeout:
raise TimeoutException(
f'Build did not reach {state} state within {timeout} seconds'
)
try:
last_build_state = self.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
start_time += 1
sleep(1) # take a breath and try again
def _mixin_test_table_search_input(self, **kwargs): def _mixin_test_table_search_input(self, **kwargs):
input_selector, input_text, searchBtn_selector, table_selector, *_ = kwargs.values() input_selector, input_text, searchBtn_selector, table_selector, *_ = kwargs.values()
# Test search input # Test search input
@@ -245,11 +221,19 @@ class TestProjectPage(SeleniumFunctionalTestCase):
rows = self.find_all(f'#{table_selector} tbody tr') rows = self.find_all(f'#{table_selector} tbody tr')
self.assertTrue(len(rows) > 0) self.assertTrue(len(rows) > 0)
def test_create_project(self):
""" Create/Test new project using:
- Project Name: Any string
- Release: Any string
- Merge Toaster settings: True or False
"""
self._create_project(project_name=self.PROJECT_NAME)
def test_image_recipe_editColumn(self): def test_image_recipe_editColumn(self):
""" Test the edit column feature in image recipe table on project page """ """ Test the edit column feature in image recipe table on project page """
self._get_create_builds(success=10, failure=10) self._get_create_builds(success=10, failure=10)
url = reverse('projectimagerecipes', args=(1,)) url = reverse('projectimagerecipes', args=(TestProjectPage.project_id,))
self.get(url) self.get(url)
self.wait_until_present('#imagerecipestable tbody tr') self.wait_until_present('#imagerecipestable tbody tr')
@@ -276,8 +260,7 @@ class TestProjectPage(SeleniumFunctionalTestCase):
- AT RIGHT -> button "New project", displayed, clickable - AT RIGHT -> button "New project", displayed, clickable
""" """
# navigate to the project page # navigate to the project page
url = reverse("project", args=(1,)) self._navigate_to_project_page()
self.get(url)
# check page header # check page header
# AT LEFT -> Logo of Yocto project # AT LEFT -> Logo of Yocto project
@@ -360,8 +343,7 @@ class TestProjectPage(SeleniumFunctionalTestCase):
- Check project name is changed - Check project name is changed
""" """
# navigate to the project page # navigate to the project page
url = reverse("project", args=(1,)) self._navigate_to_project_page()
self.get(url)
# click on "Edit" icon button # click on "Edit" icon button
self.wait_until_visible('#project-name-container') self.wait_until_visible('#project-name-container')
@@ -388,8 +370,7 @@ class TestProjectPage(SeleniumFunctionalTestCase):
Check search box used to build recipes Check search box used to build recipes
""" """
# navigate to the project page # navigate to the project page
url = reverse("project", args=(1,)) self._navigate_to_project_page()
self.get(url)
# check "configuration" tab # check "configuration" tab
self.wait_until_visible('#topbar-configuration-tab') self.wait_until_visible('#topbar-configuration-tab')
@@ -397,7 +378,7 @@ class TestProjectPage(SeleniumFunctionalTestCase):
self.assertTrue(config_tab.get_attribute('class') == 'active') self.assertTrue(config_tab.get_attribute('class') == 'active')
self.assertTrue('Configuration' in str(config_tab.text)) self.assertTrue('Configuration' in str(config_tab.text))
self.assertTrue( self.assertTrue(
f"/toastergui/project/1" in str(self.driver.current_url) f"/toastergui/project/{TestProjectPage.project_id}" in str(self.driver.current_url)
) )
def get_tabs(): def get_tabs():
@@ -420,7 +401,7 @@ class TestProjectPage(SeleniumFunctionalTestCase):
check_tab_link( check_tab_link(
1, 1,
'Builds', 'Builds',
f"/toastergui/project/1/builds" f"/toastergui/project/{TestProjectPage.project_id}/builds"
) )
# check "Import layers" tab # check "Import layers" tab
@@ -429,7 +410,7 @@ class TestProjectPage(SeleniumFunctionalTestCase):
check_tab_link( check_tab_link(
2, 2,
'Import layer', 'Import layer',
f"/toastergui/project/1/importlayer" f"/toastergui/project/{TestProjectPage.project_id}/importlayer"
) )
# check "New custom image" tab # check "New custom image" tab
@@ -438,7 +419,7 @@ class TestProjectPage(SeleniumFunctionalTestCase):
check_tab_link( check_tab_link(
3, 3,
'New custom image', 'New custom image',
f"/toastergui/project/1/newcustomimage" f"/toastergui/project/{TestProjectPage.project_id}/newcustomimage"
) )
# check search box can be use to build recipes # check search box can be use to build recipes
@@ -480,12 +461,20 @@ class TestProjectPage(SeleniumFunctionalTestCase):
'//td[@class="add-del-layers"]//a[1]' '//td[@class="add-del-layers"]//a[1]'
) )
build_btn.click() build_btn.click()
self._wait_until_build('parsing starting cloning queued') build_state = wait_until_build(self, 'parsing starting cloning queued')
lastest_builds = self.driver.find_elements( lastest_builds = self.driver.find_elements(
By.XPATH, By.XPATH,
'//div[@id="latest-builds"]/div' '//div[@id="latest-builds"]/div'
) )
self.assertTrue(len(lastest_builds) > 0) self.assertTrue(len(lastest_builds) > 0)
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()
if 'starting' not in build_state: # change build state when cancelled in starting state
wait_until_build_cancelled(self)
# check software recipe table feature(show/hide column, pagination) # check software recipe table feature(show/hide column, pagination)
self._navigate_to_config_nav('softwarerecipestable', 4) self._navigate_to_config_nav('softwarerecipestable', 4)
@@ -547,6 +536,7 @@ class TestProjectPage(SeleniumFunctionalTestCase):
searchBtn_selector='search-submit-machinestable', searchBtn_selector='search-submit-machinestable',
table_selector='machinestable' table_selector='machinestable'
) )
self.wait_until_visible('#machinestable tbody tr', poll=3)
rows = self.find_all('#machinestable tbody tr') rows = self.find_all('#machinestable tbody tr')
machine_to_add = rows[0] machine_to_add = rows[0]
add_btn = machine_to_add.find_element(By.XPATH, '//td[@class="add-del-layers"]') add_btn = machine_to_add.find_element(By.XPATH, '//td[@class="add-del-layers"]')
@@ -593,6 +583,7 @@ class TestProjectPage(SeleniumFunctionalTestCase):
table_selector='layerstable' table_selector='layerstable'
) )
# check "Add layer" button works # check "Add layer" button works
self.wait_until_visible('#layerstable tbody tr', poll=3)
rows = self.find_all('#layerstable tbody tr') rows = self.find_all('#layerstable tbody tr')
layer_to_add = rows[0] layer_to_add = rows[0]
add_btn = layer_to_add.find_element( add_btn = layer_to_add.find_element(
@@ -601,7 +592,7 @@ class TestProjectPage(SeleniumFunctionalTestCase):
) )
add_btn.click() add_btn.click()
# check modal is displayed # check modal is displayed
self.wait_until_visible('#dependencies-modal', poll=2) self.wait_until_visible('#dependencies-modal', poll=3)
list_dependencies = self.find_all('#dependencies-list li') list_dependencies = self.find_all('#dependencies-list li')
# click on add-layers button # click on add-layers button
add_layers_btn = self.driver.find_element( add_layers_btn = self.driver.find_element(
@@ -615,6 +606,7 @@ class TestProjectPage(SeleniumFunctionalTestCase):
f'You have added {len(list_dependencies)+1} layers to your project: {input_text} and its dependencies' in str(change_notification.text) f'You have added {len(list_dependencies)+1} layers to your project: {input_text} and its dependencies' in str(change_notification.text)
) )
# check "Remove layer" button works # check "Remove layer" button works
self.wait_until_visible('#layerstable tbody tr', poll=3)
rows = self.find_all('#layerstable tbody tr') rows = self.find_all('#layerstable tbody tr')
layer_to_remove = rows[0] layer_to_remove = rows[0]
remove_btn = layer_to_remove.find_element( remove_btn = layer_to_remove.find_element(
@@ -706,7 +698,7 @@ class TestProjectPage(SeleniumFunctionalTestCase):
- Check layer summary - Check layer summary
- Check layer description - Check layer description
""" """
url = reverse("layerdetails", args=(1, 8)) url = reverse("layerdetails", args=(TestProjectPage.project_id, 8))
self.get(url) self.get(url)
self.wait_until_visible('.page-header') self.wait_until_visible('.page-header')
# check title is displayed # check title is displayed
@@ -765,7 +757,7 @@ class TestProjectPage(SeleniumFunctionalTestCase):
- Check recipe: name, summary, description, Version, Section, - Check recipe: name, summary, description, Version, Section,
License, Approx. packages included, Approx. size, Recipe file License, Approx. packages included, Approx. size, Recipe file
""" """
url = reverse("recipedetails", args=(1, 53428)) url = reverse("recipedetails", args=(TestProjectPage.project_id, 53428))
self.get(url) self.get(url)
self.wait_until_visible('.page-header') self.wait_until_visible('.page-header')
# check title is displayed # check title is displayed