| #! /usr/bin/env python3 # |
| # BitBake Toaster UI tests implementation |
| # |
| # Copyright (C) 2023 Savoir-faire Linux |
| # |
| # SPDX-License-Identifier: GPL-2.0-only |
| # |
| |
| import os |
| import random |
| import string |
| from unittest import skip |
| import pytest |
| from django.urls import reverse |
| from django.utils import timezone |
| from selenium.webdriver.common.keys import Keys |
| from selenium.webdriver.support.select import Select |
| from selenium.common.exceptions import TimeoutException |
| from tests.functional.functional_helpers import SeleniumFunctionalTestCase |
| from orm.models import Build, Project, Target |
| 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 TestProjectPage(SeleniumFunctionalTestCase): |
| project_id = None |
| PROJECT_NAME = 'TestProjectPage' |
| |
| 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') |
| 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 != '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): |
| """ Create a build and return the build object """ |
| # parameters for builds to associate with the projects |
| now = timezone.now() |
| self.project1_build_success = { |
| 'project': Project.objects.get(id=TestProjectPage.project_id), |
| 'started_on': now, |
| 'completed_on': now, |
| 'outcome': Build.SUCCEEDED |
| } |
| |
| self.project1_build_failure = { |
| 'project': Project.objects.get(id=TestProjectPage.project_id), |
| '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 _mixin_test_table_edit_column( |
| self, |
| table_id, |
| edit_btn_id, |
| list_check_box_id: list |
| ): |
| # Check edit column |
| edit_column = self.find(f'#{edit_btn_id}') |
| self.assertTrue(edit_column.is_displayed()) |
| edit_column.click() |
| # Check dropdown is visible |
| self.wait_until_visible('ul.dropdown-menu.editcol') |
| for check_box_id in list_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'#{table_id} 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'#{table_id} 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'#{table_id} 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'#{table_id} thead th.{th_class}' |
| ).is_displayed(), |
| f"The {th_class} column is checked in EditColumn dropdown, but it's not visible in table" |
| ) |
| |
| 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_to_config_nav(self, nav_id, nav_index): |
| # navigate to the project page |
| self._navigate_to_project_page() |
| # click on "Software recipe" tab |
| soft_recipe = self._get_config_nav_item(nav_index) |
| soft_recipe.click() |
| self.wait_until_visible(f'#{nav_id}') |
| |
| def _mixin_test_table_show_rows(self, table_selector, **kwargs): |
| """ Test the show rows feature in the builds table on the all builds 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(f'#{table_selector} tbody tr', poll=2) |
| self.assertTrue( |
| len(self.find_all(f'#{table_selector} tbody tr')) == row_to_show |
| ) |
| self.wait_until_present(f'#{table_selector} tbody tr') |
| show_rows = self.driver.find_elements( |
| By.XPATH, |
| f'//select[@class="form-control pagesize-{table_selector}"]' |
| ) |
| rows_to_show = [10, 25, 50, 100, 150] |
| to_skip = kwargs.get('to_skip', []) |
| # Check show rows |
| for show_row_link in show_rows: |
| show_row_link = Select(show_row_link) |
| for row_to_show in rows_to_show: |
| if row_to_show not in to_skip: |
| test_show_rows(row_to_show, show_row_link) |
| |
| def _mixin_test_table_search_input(self, **kwargs): |
| input_selector, input_text, searchBtn_selector, table_selector, *_ = kwargs.values() |
| # Test search input |
| self.wait_until_visible(f'#{input_selector}') |
| recipe_input = self.find(f'#{input_selector}') |
| recipe_input.send_keys(input_text) |
| self.find(f'#{searchBtn_selector}').click() |
| self.wait_until_visible(f'#{table_selector} tbody tr') |
| rows = self.find_all(f'#{table_selector} tbody tr') |
| 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): |
| """ Test the edit column feature in image recipe table on project page """ |
| self._get_create_builds(success=10, failure=10) |
| |
| url = reverse('projectimagerecipes', args=(TestProjectPage.project_id,)) |
| self.get(url) |
| self.wait_until_present('#imagerecipestable tbody tr') |
| |
| column_list = [ |
| 'get_description_or_summary', 'layer_version__get_vcs_reference', |
| 'layer_version__layer__name', 'license', 'recipe-file', 'section', |
| 'version' |
| ] |
| |
| # Check that we can hide the edit column |
| self._mixin_test_table_edit_column( |
| 'imagerecipestable', |
| 'edit-columns-button', |
| [f'checkbox-{column}' for column in column_list] |
| ) |
| |
| def test_page_header_on_project_page(self): |
| """ Check page header in project page: |
| - AT LEFT -> Logo of Yocto project, displayed, clickable |
| - "Toaster"+" Information icon", displayed, clickable |
| - "Server Icon" + "All builds", displayed, clickable |
| - "Directory Icon" + "All projects", displayed, clickable |
| - "Book Icon" + "Documentation", displayed, clickable |
| - AT RIGHT -> button "New project", displayed, clickable |
| """ |
| # navigate to the project page |
| self._navigate_to_project_page() |
| |
| # check page header |
| # AT LEFT -> Logo of Yocto project |
| logo = self.driver.find_element( |
| By.XPATH, |
| "//div[@class='toaster-navbar-brand']", |
| ) |
| logo_img = logo.find_element(By.TAG_NAME, 'img') |
| self.assertTrue(logo_img.is_displayed(), |
| 'Logo of Yocto project not found') |
| self.assertTrue( |
| '/static/img/logo.png' in str(logo_img.get_attribute('src')), |
| 'Logo of Yocto project not found' |
| ) |
| # "Toaster"+" Information icon", clickable |
| toaster = self.driver.find_element( |
| By.XPATH, |
| "//div[@class='toaster-navbar-brand']//a[@class='brand']", |
| ) |
| self.assertTrue(toaster.is_displayed(), 'Toaster not found') |
| self.assertTrue(toaster.text == 'Toaster') |
| info_sign = self.find('.glyphicon-info-sign') |
| self.assertTrue(info_sign.is_displayed()) |
| |
| # "Server Icon" + "All builds" |
| all_builds = self.find('#navbar-all-builds') |
| all_builds_link = all_builds.find_element(By.TAG_NAME, 'a') |
| self.assertTrue("All builds" in all_builds_link.text) |
| self.assertTrue( |
| '/toastergui/builds/' in str(all_builds_link.get_attribute('href')) |
| ) |
| server_icon = all_builds.find_element(By.TAG_NAME, 'i') |
| self.assertTrue( |
| server_icon.get_attribute('class') == 'glyphicon glyphicon-tasks' |
| ) |
| self.assertTrue(server_icon.is_displayed()) |
| |
| # "Directory Icon" + "All projects" |
| all_projects = self.find('#navbar-all-projects') |
| all_projects_link = all_projects.find_element(By.TAG_NAME, 'a') |
| self.assertTrue("All projects" in all_projects_link.text) |
| self.assertTrue( |
| '/toastergui/projects/' in str(all_projects_link.get_attribute( |
| 'href')) |
| ) |
| dir_icon = all_projects.find_element(By.TAG_NAME, 'i') |
| self.assertTrue( |
| dir_icon.get_attribute('class') == 'icon-folder-open' |
| ) |
| self.assertTrue(dir_icon.is_displayed()) |
| |
| # "Book Icon" + "Documentation" |
| toaster_docs_link = self.find('#navbar-docs') |
| toaster_docs_link_link = toaster_docs_link.find_element(By.TAG_NAME, |
| 'a') |
| self.assertTrue("Documentation" in toaster_docs_link_link.text) |
| self.assertTrue( |
| toaster_docs_link_link.get_attribute('href') == 'http://docs.yoctoproject.org/toaster-manual/index.html#toaster-user-manual' |
| ) |
| book_icon = toaster_docs_link.find_element(By.TAG_NAME, 'i') |
| self.assertTrue( |
| book_icon.get_attribute('class') == 'glyphicon glyphicon-book' |
| ) |
| self.assertTrue(book_icon.is_displayed()) |
| |
| # AT RIGHT -> button "New project" |
| new_project_button = self.find('#new-project-button') |
| self.assertTrue(new_project_button.is_displayed()) |
| self.assertTrue(new_project_button.text == 'New project') |
| new_project_button.click() |
| self.assertTrue( |
| '/toastergui/newproject/' in str(self.driver.current_url) |
| ) |
| |
| def test_edit_project_name(self): |
| """ Test edit project name: |
| - Click on "Edit" icon button |
| - Change project name |
| - Click on "Save" button |
| - Check project name is changed |
| """ |
| # navigate to the project page |
| self._navigate_to_project_page() |
| |
| # click on "Edit" icon button |
| self.wait_until_visible('#project-name-container') |
| edit_button = self.find('#project-change-form-toggle') |
| edit_button.click() |
| project_name_input = self.find('#project-name-change-input') |
| self.assertTrue(project_name_input.is_displayed()) |
| project_name_input.clear() |
| project_name_input.send_keys('New Name') |
| self.find('#project-name-change-btn').click() |
| |
| # check project name is changed |
| self.wait_until_visible('#project-name-container') |
| self.assertTrue( |
| 'New Name' in str(self.find('#project-name-container').text) |
| ) |
| |
| def test_project_page_tabs(self): |
| """ Test project tabs: |
| - "configuration" tab |
| - "Builds" tab |
| - "Import layers" tab |
| - "New custom image" tab |
| Check search box used to build recipes |
| """ |
| # navigate to the project page |
| self._navigate_to_project_page() |
| |
| # check "configuration" tab |
| self.wait_until_visible('#topbar-configuration-tab') |
| config_tab = self.find('#topbar-configuration-tab') |
| self.assertTrue(config_tab.get_attribute('class') == 'active') |
| self.assertTrue('Configuration' in str(config_tab.text)) |
| self.assertTrue( |
| f"/toastergui/project/{TestProjectPage.project_id}" in str(self.driver.current_url) |
| ) |
| |
| def get_tabs(): |
| # tabs links list |
| return self.driver.find_elements( |
| By.XPATH, |
| '//div[@id="project-topbar"]//li' |
| ) |
| |
| def check_tab_link(tab_index, tab_name, url): |
| tab = get_tabs()[tab_index] |
| tab_link = tab.find_element(By.TAG_NAME, 'a') |
| self.assertTrue(url in tab_link.get_attribute('href')) |
| self.assertTrue(tab_name in tab_link.text) |
| self.assertTrue(tab.get_attribute('class') == 'active') |
| |
| # check "Builds" tab |
| builds_tab = get_tabs()[1] |
| builds_tab.find_element(By.TAG_NAME, 'a').click() |
| check_tab_link( |
| 1, |
| 'Builds', |
| f"/toastergui/project/{TestProjectPage.project_id}/builds" |
| ) |
| |
| # check "Import layers" tab |
| import_layers_tab = get_tabs()[2] |
| import_layers_tab.find_element(By.TAG_NAME, 'a').click() |
| check_tab_link( |
| 2, |
| 'Import layer', |
| f"/toastergui/project/{TestProjectPage.project_id}/importlayer" |
| ) |
| |
| # check "New custom image" tab |
| new_custom_image_tab = get_tabs()[3] |
| new_custom_image_tab.find_element(By.TAG_NAME, 'a').click() |
| check_tab_link( |
| 3, |
| 'New custom image', |
| f"/toastergui/project/{TestProjectPage.project_id}/newcustomimage" |
| ) |
| |
| # 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() |
| self.wait_until_visible('#latest-builds') |
| lastest_builds = self.driver.find_elements( |
| By.XPATH, |
| '//div[@id="latest-builds"]', |
| ) |
| last_build = lastest_builds[0] |
| self.assertTrue( |
| 'core-image-minimal' in str(last_build.text) |
| ) |
| |
| def test_softwareRecipe_page(self): |
| """ Test software recipe page |
| - Check title "Compatible software recipes" is displayed |
| - Check search input |
| - Check "build recipe" button works |
| - Check software recipe table feature(show/hide column, pagination) |
| """ |
| self._navigate_to_config_nav('softwarerecipestable', 4) |
| # check title "Compatible software recipes" is displayed |
| self.assertTrue("Compatible software recipes" in self.get_page_source()) |
| # Test search input |
| self._mixin_test_table_search_input( |
| input_selector='search-input-softwarerecipestable', |
| input_text='busybox', |
| searchBtn_selector='search-submit-softwarerecipestable', |
| table_selector='softwarerecipestable' |
| ) |
| # check "build recipe" button works |
| rows = self.find_all('#softwarerecipestable tbody tr') |
| image_to_build = rows[0] |
| build_btn = image_to_build.find_element( |
| By.XPATH, |
| '//td[@class="add-del-layers"]//a[1]' |
| ) |
| build_btn.click() |
| build_state = wait_until_build(self, 'queued cloning starting parsing failed') |
| lastest_builds = self.driver.find_elements( |
| By.XPATH, |
| '//div[@id="latest-builds"]/div' |
| ) |
| 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) |
| self._navigate_to_config_nav('softwarerecipestable', 4) |
| column_list = [ |
| 'get_description_or_summary', |
| 'layer_version__get_vcs_reference', |
| 'layer_version__layer__name', |
| 'license', |
| 'recipe-file', |
| 'section', |
| 'version', |
| ] |
| self._mixin_test_table_edit_column( |
| 'softwarerecipestable', |
| 'edit-columns-button', |
| [f'checkbox-{column}' for column in column_list] |
| ) |
| self._navigate_to_config_nav('softwarerecipestable', 4) |
| # check show rows(pagination) |
| self._mixin_test_table_show_rows( |
| table_selector='softwarerecipestable', |
| to_skip=[150], |
| ) |
| |
| def test_machines_page(self): |
| """ Test Machine page |
| - Check if title "Compatible machines" is displayed |
| - Check search input |
| - Check "Select machine" button works |
| - Check "Add layer" button works |
| - Check Machine table feature(show/hide column, pagination) |
| """ |
| self._navigate_to_config_nav('machinestable', 5) |
| # check title "Compatible software recipes" is displayed |
| self.assertTrue("Compatible machines" in self.get_page_source()) |
| # Test search input |
| self._mixin_test_table_search_input( |
| input_selector='search-input-machinestable', |
| input_text='qemux86-64', |
| searchBtn_selector='search-submit-machinestable', |
| table_selector='machinestable' |
| ) |
| # check "Select machine" button works |
| rows = self.find_all('#machinestable tbody tr') |
| machine_to_select = rows[0] |
| select_btn = machine_to_select.find_element( |
| By.XPATH, |
| '//td[@class="add-del-layers"]//a[1]' |
| ) |
| select_btn.send_keys(Keys.RETURN) |
| self.wait_until_visible('#config-nav') |
| project_machine_name = self.find('#project-machine-name') |
| self.assertTrue( |
| 'qemux86-64' in project_machine_name.text |
| ) |
| # check "Add layer" button works |
| self._navigate_to_config_nav('machinestable', 5) |
| # Search for a machine whit layer not in project |
| self._mixin_test_table_search_input( |
| input_selector='search-input-machinestable', |
| input_text='qemux86-64-tpm2', |
| searchBtn_selector='search-submit-machinestable', |
| table_selector='machinestable' |
| ) |
| self.wait_until_visible('#machinestable tbody tr', poll=3) |
| rows = self.find_all('#machinestable tbody tr') |
| machine_to_add = rows[0] |
| add_btn = machine_to_add.find_element(By.XPATH, '//td[@class="add-del-layers"]') |
| add_btn.click() |
| self.wait_until_visible('#change-notification') |
| change_notification = self.find('#change-notification') |
| self.assertTrue( |
| f'You have added 1 layer to your project' in str(change_notification.text) |
| ) |
| # check Machine table feature(show/hide column, pagination) |
| self._navigate_to_config_nav('machinestable', 5) |
| column_list = [ |
| 'description', |
| 'layer_version__get_vcs_reference', |
| 'layer_version__layer__name', |
| 'machinefile', |
| ] |
| self._mixin_test_table_edit_column( |
| 'machinestable', |
| 'edit-columns-button', |
| [f'checkbox-{column}' for column in column_list] |
| ) |
| self._navigate_to_config_nav('machinestable', 5) |
| # check show rows(pagination) |
| self._mixin_test_table_show_rows( |
| table_selector='machinestable', |
| to_skip=[150], |
| ) |
| |
| def test_layers_page(self): |
| """ Test layers page |
| - Check if title "Compatible layerss" is displayed |
| - Check search input |
| - Check "Add layer" button works |
| - Check "Remove layer" button works |
| - Check layers table feature(show/hide column, pagination) |
| """ |
| self._navigate_to_config_nav('layerstable', 6) |
| # check title "Compatible layers" is displayed |
| self.assertTrue("Compatible layers" in self.get_page_source()) |
| # Test search input |
| input_text='meta-tanowrt' |
| self._mixin_test_table_search_input( |
| input_selector='search-input-layerstable', |
| input_text=input_text, |
| searchBtn_selector='search-submit-layerstable', |
| table_selector='layerstable' |
| ) |
| # check "Add layer" button works |
| self.wait_until_visible('#layerstable tbody tr', poll=3) |
| rows = self.find_all('#layerstable tbody tr') |
| layer_to_add = rows[0] |
| add_btn = layer_to_add.find_element( |
| By.XPATH, |
| '//td[@class="add-del-layers"]' |
| ) |
| add_btn.click() |
| # check modal is displayed |
| self.wait_until_visible('#dependencies-modal', poll=3) |
| list_dependencies = self.find_all('#dependencies-list li') |
| # click on add-layers button |
| add_layers_btn = self.driver.find_element( |
| By.XPATH, |
| '//form[@id="dependencies-modal-form"]//button[@class="btn btn-primary"]' |
| ) |
| add_layers_btn.click() |
| self.wait_until_visible('#change-notification') |
| change_notification = self.find('#change-notification') |
| self.assertTrue( |
| 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 |
| self.wait_until_visible('#layerstable tbody tr', poll=3) |
| rows = self.find_all('#layerstable tbody tr') |
| layer_to_remove = rows[0] |
| remove_btn = layer_to_remove.find_element( |
| By.XPATH, |
| '//td[@class="add-del-layers"]' |
| ) |
| remove_btn.click() |
| self.wait_until_visible('#change-notification', poll=2) |
| change_notification = self.find('#change-notification') |
| self.assertTrue( |
| f'You have removed 1 layer from your project: {input_text}' in str(change_notification.text) |
| ) |
| # check layers table feature(show/hide column, pagination) |
| self._navigate_to_config_nav('layerstable', 6) |
| column_list = [ |
| 'dependencies', |
| 'revision', |
| 'layer__vcs_url', |
| 'git_subdir', |
| 'layer__summary', |
| ] |
| self._mixin_test_table_edit_column( |
| 'layerstable', |
| 'edit-columns-button', |
| [f'checkbox-{column}' for column in column_list] |
| ) |
| self._navigate_to_config_nav('layerstable', 6) |
| # check show rows(pagination) |
| self._mixin_test_table_show_rows( |
| table_selector='layerstable', |
| to_skip=[150], |
| ) |
| |
| def test_distro_page(self): |
| """ Test distros page |
| - Check if title "Compatible distros" is displayed |
| - Check search input |
| - Check "Add layer" button works |
| - Check distro table feature(show/hide column, pagination) |
| """ |
| self._navigate_to_config_nav('distrostable', 7) |
| # check title "Compatible distros" is displayed |
| self.assertTrue("Compatible Distros" in self.get_page_source()) |
| # Test search input |
| input_text='poky-altcfg' |
| self._mixin_test_table_search_input( |
| input_selector='search-input-distrostable', |
| input_text=input_text, |
| searchBtn_selector='search-submit-distrostable', |
| table_selector='distrostable' |
| ) |
| # check "Add distro" button works |
| rows = self.find_all('#distrostable tbody tr') |
| distro_to_add = rows[0] |
| add_btn = distro_to_add.find_element( |
| By.XPATH, |
| '//td[@class="add-del-layers"]//a[1]' |
| ) |
| add_btn.click() |
| self.wait_until_visible('#change-notification', poll=2) |
| change_notification = self.find('#change-notification') |
| self.assertTrue( |
| f'You have changed the distro to: {input_text}' in str(change_notification.text) |
| ) |
| # check distro table feature(show/hide column, pagination) |
| self._navigate_to_config_nav('distrostable', 7) |
| column_list = [ |
| 'description', |
| 'templatefile', |
| 'layer_version__get_vcs_reference', |
| 'layer_version__layer__name', |
| ] |
| self._mixin_test_table_edit_column( |
| 'distrostable', |
| 'edit-columns-button', |
| [f'checkbox-{column}' for column in column_list] |
| ) |
| self._navigate_to_config_nav('distrostable', 7) |
| # check show rows(pagination) |
| self._mixin_test_table_show_rows( |
| table_selector='distrostable', |
| to_skip=[150], |
| ) |
| |
| def test_single_layer_page(self): |
| """ Test layer page |
| - Check if title is displayed |
| - Check add/remove layer button works |
| - Check tabs(layers, recipes, machines) are displayed |
| - Check left section is displayed |
| - Check layer name |
| - Check layer summary |
| - Check layer description |
| """ |
| url = reverse("layerdetails", args=(TestProjectPage.project_id, 8)) |
| self.get(url) |
| self.wait_until_visible('.page-header') |
| # check title is displayed |
| self.assertTrue(self.find('.page-header h1').is_displayed()) |
| |
| # check add layer button works |
| remove_layer_btn = self.find('#add-remove-layer-btn') |
| remove_layer_btn.click() |
| self.wait_until_visible('#change-notification', poll=2) |
| change_notification = self.find('#change-notification') |
| self.assertTrue( |
| f'You have removed 1 layer from your project' in str(change_notification.text) |
| ) |
| # check add layer button works, 18 is the random layer id |
| add_layer_btn = self.find('#add-remove-layer-btn') |
| add_layer_btn.click() |
| self.wait_until_visible('#change-notification') |
| change_notification = self.find('#change-notification') |
| self.assertTrue( |
| f'You have added 1 layer to your project' in str(change_notification.text) |
| ) |
| # check tabs(layers, recipes, machines) are displayed |
| tabs = self.find_all('.nav-tabs li') |
| self.assertEqual(len(tabs), 3) |
| # Check first tab |
| tabs[0].click() |
| self.assertTrue( |
| 'active' in str(self.find('#information').get_attribute('class')) |
| ) |
| # Check second tab |
| tabs[1].click() |
| self.assertTrue( |
| 'active' in str(self.find('#recipes').get_attribute('class')) |
| ) |
| # Check third tab |
| tabs[2].click() |
| self.assertTrue( |
| 'active' in str(self.find('#machines').get_attribute('class')) |
| ) |
| # Check left section is displayed |
| section = self.find('.well') |
| # Check layer name |
| self.assertTrue( |
| section.find_element(By.XPATH, '//h2[1]').is_displayed() |
| ) |
| # Check layer summary |
| self.assertTrue("Summary" in section.text) |
| # Check layer description |
| self.assertTrue("Description" in section.text) |
| |
| def test_single_recipe_page(self): |
| """ Test recipe page |
| - Check if title is displayed |
| - Check add recipe layer displayed |
| - Check left section is displayed |
| - Check recipe: name, summary, description, Version, Section, |
| License, Approx. packages included, Approx. size, Recipe file |
| """ |
| url = reverse("recipedetails", args=(TestProjectPage.project_id, 53428)) |
| self.get(url) |
| self.wait_until_visible('.page-header') |
| # check title is displayed |
| self.assertTrue(self.find('.page-header h1').is_displayed()) |
| # check add recipe layer displayed |
| add_recipe_layer_btn = self.find('#add-layer-btn') |
| self.assertTrue(add_recipe_layer_btn.is_displayed()) |
| # check left section is displayed |
| section = self.find('.well') |
| # Check recipe name |
| self.assertTrue( |
| section.find_element(By.XPATH, '//h2[1]').is_displayed() |
| ) |
| # Check recipe sections details info are displayed |
| self.assertTrue("Summary" in section.text) |
| self.assertTrue("Description" in section.text) |
| self.assertTrue("Version" in section.text) |
| self.assertTrue("Section" in section.text) |
| self.assertTrue("License" in section.text) |
| self.assertTrue("Approx. packages included" in section.text) |
| self.assertTrue("Approx. package size" in section.text) |
| self.assertTrue("Recipe file" in section.text) |