From patchwork Tue Nov 21 13:50:37 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Alassane Yattara X-Patchwork-Id: 34954 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id 083EDC61D92 for ; Tue, 21 Nov 2023 13:51:00 +0000 (UTC) Received: from mail.savoirfairelinux.com (mail.savoirfairelinux.com [208.88.110.44]) by mx.groups.io with SMTP id smtpd.web11.40854.1700574649715304937 for ; Tue, 21 Nov 2023 05:50:49 -0800 Authentication-Results: mx.groups.io; dkim=pass header.i=@savoirfairelinux.com header.s=DFC430D2-D198-11EC-948E-34200CB392D2 header.b=scDqBxCQ; spf=pass (domain: savoirfairelinux.com, ip: 208.88.110.44, mailfrom: alassane.yattara@savoirfairelinux.com) Received: from localhost (localhost [127.0.0.1]) by mail.savoirfairelinux.com (Postfix) with ESMTP id 10B8A9C3675 for ; Tue, 21 Nov 2023 08:50:49 -0500 (EST) Received: from mail.savoirfairelinux.com ([127.0.0.1]) by localhost (mail.savoirfairelinux.com [127.0.0.1]) (amavis, port 10032) with ESMTP id K6ktJyIvr6Bs; Tue, 21 Nov 2023 08:50:48 -0500 (EST) Received: from localhost (localhost [127.0.0.1]) by mail.savoirfairelinux.com (Postfix) with ESMTP id 4390C9C3500; Tue, 21 Nov 2023 08:50:48 -0500 (EST) DKIM-Filter: OpenDKIM Filter v2.10.3 mail.savoirfairelinux.com 4390C9C3500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=savoirfairelinux.com; s=DFC430D2-D198-11EC-948E-34200CB392D2; t=1700574648; bh=Y6q0/szke9Hv2egvcEYhi57k18HN1LYA/zjUDnTHL0U=; h=From:To:Date:Message-Id:MIME-Version; b=scDqBxCQS+wpVz0zlEH2bOC3i+QkMN02HYn2ZHOcyp3lUce+D4ylodOjumlKj6vBS xszQUwGSMMqSF+YtiA4cyLFkCtt/Yc84s+NndT5zU+Goqlae1ZR3Mv7j+uiPvNEH4P jwydY3MCT1rSmsL3eS/qZBUPUd90C8yB8b2nif91+razzOSBGAwzXcThEkf1Rj/4EZ 8w/10rSV6Z10Q8da0O4ypi3lIIA/lLtPXU1RLi3Xx6D70/p2+Hk4cKtHlOfRaFG+nR v0SiflpIHsXnQUVFvlu7lB+6G+D9Z5t9cB4bvSZAuADHdA0oqpt83lvLdSe67weiry ZDhvVYwihuzIg== X-Virus-Scanned: amavis at mail.savoirfairelinux.com Received: from mail.savoirfairelinux.com ([127.0.0.1]) by localhost (mail.savoirfairelinux.com [127.0.0.1]) (amavis, port 10026) with ESMTP id S-qVhiHzgfCH; Tue, 21 Nov 2023 08:50:48 -0500 (EST) Received: from jedi.. (unknown [196.127.183.75]) by mail.savoirfairelinux.com (Postfix) with ESMTPSA id 531CC9C2AE3; Tue, 21 Nov 2023 08:50:47 -0500 (EST) From: Alassane Yattara To: toaster@lists.yoctoproject.org Cc: Alassane Yattara Subject: [PATCH 1/7] Toaster: Write UI TestCase -> Test project config tab navigation: Date: Tue, 21 Nov 2023 14:50:37 +0100 Message-Id: <20231121135043.105385-1-alassane.yattara@savoirfairelinux.com> X-Mailer: git-send-email 2.34.1 MIME-Version: 1.0 List-Id: X-Webhook-Received: from li982-79.members.linode.com [45.33.32.79] by aws-us-west-2-korg-lkml-1.web.codeaurora.org with HTTPS for ; Tue, 21 Nov 2023 13:51:00 -0000 X-Groupsio-URL: https://lists.yoctoproject.org/g/toaster/message/6012 - Check if the menu is displayed and contains the right elements: - Configuration - COMPATIBLE METADATA - Custom images - Image recipes - Software recipes - Machines - Layers - Distro - EXTRA CONFIGURATION - Bitbake variables - Actions - Delete project Signed-off-by: Alassane Yattara --- .../test_project_page_tab_config.py | 176 ++++++++++++++++++ 1 file changed, 176 insertions(+) create mode 100644 lib/toaster/tests/functional/test_project_page_tab_config.py diff --git a/lib/toaster/tests/functional/test_project_page_tab_config.py b/lib/toaster/tests/functional/test_project_page_tab_config.py new file mode 100644 index 00000000..f0c6aacf --- /dev/null +++ b/lib/toaster/tests/functional/test_project_page_tab_config.py @@ -0,0 +1,176 @@ +#! /usr/bin/env python3 # +# BitBake Toaster UI tests implementation +# +# Copyright (C) 2023 Savoir-faire Linux +# +# SPDX-License-Identifier: GPL-2.0-only +# + +from time import sleep +import pytest +from django.urls import reverse +from selenium.webdriver.support.select import Select +from selenium.common.exceptions import NoSuchElementException +from tests.functional.functional_helpers import SeleniumFunctionalTestCase +from selenium.webdriver.common.by import By + + +@pytest.mark.django_db +class TestProjectConfigTab(SeleniumFunctionalTestCase): + + 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, + ): + """ 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) + + # check merge toaster settings + checkbox = self.find('.checkbox-mergeattr') + if merge_toaster_settings: + if not checkbox.is_selected(): + checkbox.click() + 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) + + 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') + lastest_builds = self.driver.find_elements( + By.XPATH, + '//div[@id="latest-builds"]/div', + ) + last_build = lastest_builds[0] + self.assertTrue( + 'core-image-minimal' in str(last_build.text) + ) + cancel_button = last_build.find_element( + By.XPATH, + '//span[@class="cancel-build-btn pull-right alert-link"]', + ) + cancel_button.click() + sleep(1) + self._wait_until_build('cancelled') + + def _get_tabs(self): + # tabs links list + return self.driver.find_elements( + By.XPATH, + '//div[@id="project-topbar"]//li' + ) + + def _get_config_nav_item(self, index): + config_nav = self.find('#config-nav') + return config_nav.find_elements(By.TAG_NAME, 'li')[index] + + def test_project_config_nav(self): + """ Test project config tab navigation: + - Check if the menu is displayed and contains the right elements: + - Configuration + - COMPATIBLE METADATA + - Custom images + - Image recipes + - Software recipes + - Machines + - Layers + - Distro + - EXTRA CONFIGURATION + - Bitbake variables + - 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') + + def _get_config_nav_item(index): + config_nav = self.find('#config-nav') + return config_nav.find_elements(By.TAG_NAME, 'li')[index] + + def check_config_nav_item(index, item_name, url): + item = _get_config_nav_item(index) + self.assertTrue(item_name in item.text) + self.assertTrue(item.get_attribute('class') == 'active') + self.assertTrue(url in self.driver.current_url) + + # check if the menu contains the right elements + # COMPATIBLE METADATA + compatible_metadata = _get_config_nav_item(1) + self.assertTrue( + "compatible metadata" in compatible_metadata.text.lower() + ) + # EXTRA CONFIGURATION + extra_configuration = _get_config_nav_item(8) + self.assertTrue( + "extra configuration" in extra_configuration.text.lower() + ) + # Actions + actions = _get_config_nav_item(10) + 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 + ] + for index, item_name, url in conf_nav_list: + item = _get_config_nav_item(index) + if item.get_attribute('class') != 'active': + item.click() + check_config_nav_item(index, item_name, url)