| #! /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 |
| change_dl_dir_btn = None |
| |
| if change_dl_dir_btn: |
| 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: |
| btn_chg_sstate_dir = 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 |
| btn_chg_sstate_dir = None |
| |
| if btn_chg_sstate_dir: # Skip continuation if sstate_dir is not displayed |
| # 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 |
| ) |