diff options
Diffstat (limited to 'lib/toaster/tests/functional/test_project_page.py')
-rw-r--r-- | lib/toaster/tests/functional/test_project_page.py | 792 |
1 files changed, 792 insertions, 0 deletions
diff --git a/lib/toaster/tests/functional/test_project_page.py b/lib/toaster/tests/functional/test_project_page.py new file mode 100644 index 000000000..adbe3587e --- /dev/null +++ b/lib/toaster/tests/functional/test_project_page.py @@ -0,0 +1,792 @@ +#! /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=3) + # check at least some rows are visible + self.assertTrue( + len(self.find_all(f'#{table_selector} tbody tr')) > 0 + ) + 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) |