diff mbox series

[1/1] toaster:update selenium version and code syntax

Message ID 20231003172623.72071-1-marlon.rodriguez-garcia@savoirfairelinux.com
State New
Headers show
Series [1/1] toaster:update selenium version and code syntax | expand

Commit Message

Marlon Rodriguez Garcia Oct. 3, 2023, 5:26 p.m. UTC
Updated selenium version to latest 4.13.0, changed selenum specific version syntax elements to accomplish test success

Signed-off-by: Marlon Rodriguez Garcia <marlon.rodriguez-garcia@savoirfairelinux.com>
---
 lib/toaster/orm/models.py                     |   2 +-
 .../tests/browser/selenium_helpers_base.py    |   9 +-
 .../tests/browser/test_all_builds_page.py     |  26 ++--
 .../tests/browser/test_all_projects_page.py   |  14 +-
 .../tests/browser/test_builddashboard_page.py |  10 +-
 .../browser/test_most_recent_builds_states.py |  12 +-
 .../browser/test_new_custom_image_page.py     |   4 +
 .../tests/browser/test_new_project_page.py    |  12 +-
 .../tests/browser/test_project_config_page.py |  29 +++--
 .../tests/browser/test_toastertable_ui.py     |   7 +-
 .../tests/builds/test_core_image_min.py       |   1 +
 .../tests/functional/functional_helpers.py    |  21 +--
 .../tests/functional/test_functional_basic.py | 120 +++++++++---------
 .../tests/toaster-tests-requirements.txt      |   2 +-
 lib/toaster/tests/views/test_views.py         |   4 +
 lib/toaster/toastergui/widgets.py             |   1 +
 16 files changed, 156 insertions(+), 118 deletions(-)

Comments

Richard Purdie Oct. 3, 2023, 9:56 p.m. UTC | #1
On Tue, 2023-10-03 at 13:26 -0400, Marlon Rodriguez Garcia wrote:
> Updated selenium version to latest 4.13.0, changed selenum specific version syntax elements to accomplish test success
> 
> Signed-off-by: Marlon Rodriguez Garcia <marlon.rodriguez-garcia@savoirfairelinux.com>
> ---
>  lib/toaster/orm/models.py                     |   2 +-
>  .../tests/browser/selenium_helpers_base.py    |   9 +-
>  .../tests/browser/test_all_builds_page.py     |  26 ++--
>  .../tests/browser/test_all_projects_page.py   |  14 +-
>  .../tests/browser/test_builddashboard_page.py |  10 +-
>  .../browser/test_most_recent_builds_states.py |  12 +-
>  .../browser/test_new_custom_image_page.py     |   4 +
>  .../tests/browser/test_new_project_page.py    |  12 +-
>  .../tests/browser/test_project_config_page.py |  29 +++--
>  .../tests/browser/test_toastertable_ui.py     |   7 +-
>  .../tests/builds/test_core_image_min.py       |   1 +
>  .../tests/functional/functional_helpers.py    |  21 +--
>  .../tests/functional/test_functional_basic.py | 120 +++++++++---------
>  .../tests/toaster-tests-requirements.txt      |   2 +-
>  lib/toaster/tests/views/test_views.py         |   4 +
>  lib/toaster/toastergui/widgets.py             |   1 +
>  16 files changed, 156 insertions(+), 118 deletions(-)

At a quick glance this seems reasonable to me but I'm far from an
expert in this area. I've realised that for toaster patches you should
copy the toaster mailing list as well. I'll likely wait a short while
for feedback then merge if there isn't any. At this point our automated
testing won't cover anything here but I know we plan to change that!

Cheers,

Richard
Tim Orling Oct. 12, 2023, 3:58 a.m. UTC | #2
On Tue, Oct 3, 2023 at 10:26 AM Marlon Rodriguez Garcia <
marlon.rodriguez-garcia@savoirfairelinux.com> wrote:

> Updated selenium version to latest 4.13.0, changed selenum specific
> version syntax elements to accomplish test success
>
>
Please update the README for how to run the tests.

https://git.yoctoproject.org/poky/tree/bitbake/lib/toaster/tests/browser/README


> Signed-off-by: Marlon Rodriguez Garcia <
> marlon.rodriguez-garcia@savoirfairelinux.com>
> ---
>  lib/toaster/orm/models.py                     |   2 +-
>  .../tests/browser/selenium_helpers_base.py    |   9 +-
>  .../tests/browser/test_all_builds_page.py     |  26 ++--
>  .../tests/browser/test_all_projects_page.py   |  14 +-
>  .../tests/browser/test_builddashboard_page.py |  10 +-
>  .../browser/test_most_recent_builds_states.py |  12 +-
>  .../browser/test_new_custom_image_page.py     |   4 +
>  .../tests/browser/test_new_project_page.py    |  12 +-
>  .../tests/browser/test_project_config_page.py |  29 +++--
>  .../tests/browser/test_toastertable_ui.py     |   7 +-
>  .../tests/builds/test_core_image_min.py       |   1 +
>  .../tests/functional/functional_helpers.py    |  21 +--
>  .../tests/functional/test_functional_basic.py | 120 +++++++++---------
>  .../tests/toaster-tests-requirements.txt      |   2 +-
>  lib/toaster/tests/views/test_views.py         |   4 +
>  lib/toaster/toastergui/widgets.py             |   1 +
>  16 files changed, 156 insertions(+), 118 deletions(-)
>
> diff --git a/lib/toaster/orm/models.py b/lib/toaster/orm/models.py
> index f9fcf9e4..0d503a51 100644
> --- a/lib/toaster/orm/models.py
> +++ b/lib/toaster/orm/models.py
> @@ -1733,7 +1733,7 @@ class CustomImageRecipe(Recipe):
>          packages_conf += "\""
>
>          base_recipe_path = self.get_base_recipe_file()
> -        if base_recipe_path:
> +        if base_recipe_path and os.path.isfile(base_recipe_path):
>              base_recipe = open(base_recipe_path, 'r').read()
>          else:
>              # Pass back None to trigger error message to user
> diff --git a/lib/toaster/tests/browser/selenium_helpers_base.py
> b/lib/toaster/tests/browser/selenium_helpers_base.py
> index 644d45fe..9a4e27a3 100644
> --- a/lib/toaster/tests/browser/selenium_helpers_base.py
> +++ b/lib/toaster/tests/browser/selenium_helpers_base.py
> @@ -21,6 +21,7 @@ import unittest
>
>  from selenium import webdriver
>  from selenium.webdriver.support.ui import WebDriverWait
> +from selenium.webdriver.common.by import By
>  from selenium.webdriver.common.desired_capabilities import
> DesiredCapabilities
>  from selenium.common.exceptions import NoSuchElementException, \
>          StaleElementReferenceException, TimeoutException
> @@ -32,9 +33,7 @@ def create_selenium_driver(cls,browser='chrome'):
>          browser = env_browser
>
>      if browser == 'chrome':
> -        return webdriver.Chrome(
> -            service_args=["--verbose", "--log-path=selenium.log"]
> -        )
> +        return webdriver.Chrome()
>      elif browser == 'firefox':
>          return webdriver.Firefox()
>      elif browser == 'marionette':
> @@ -153,11 +152,11 @@ class SeleniumTestCaseBase(unittest.TestCase):
>
>      def find(self, selector):
>          """ Find single element by CSS selector """
> -        return self.driver.find_element_by_css_selector(selector)
> +        return self.driver.find_element(By.CSS_SELECTOR, selector)
>
>      def find_all(self, selector):
>          """ Find all elements matching CSS selector """
> -        return self.driver.find_elements_by_css_selector(selector)
> +        return self.driver.find_elements(By.CSS_SELECTOR, selector)
>
>      def element_exists(self, selector):
>          """
> diff --git a/lib/toaster/tests/browser/test_all_builds_page.py
> b/lib/toaster/tests/browser/test_all_builds_page.py
> index 8423d3da..d4312bb3 100644
> --- a/lib/toaster/tests/browser/test_all_builds_page.py
> +++ b/lib/toaster/tests/browser/test_all_builds_page.py
> @@ -7,7 +7,7 @@
>  # SPDX-License-Identifier: GPL-2.0-only
>  #
>
> -import re
> +import re, time
>
>  from django.urls import reverse
>  from django.utils import timezone
> @@ -15,6 +15,8 @@ from tests.browser.selenium_helpers import
> SeleniumTestCase
>
>  from orm.models import BitbakeVersion, Release, Project, Build, Target
>
> +from selenium.webdriver.common.by import By
> +
>
>  class TestAllBuildsPage(SeleniumTestCase):
>      """ Tests for all builds page /builds/ """
> @@ -91,7 +93,7 @@ class TestAllBuildsPage(SeleniumTestCase):
>          found_row = None
>          for row in rows:
>
> -            outcome_links = row.find_elements_by_css_selector(selector)
> +            outcome_links = row.find_elements(By.CSS_SELECTOR, selector)
>              if len(outcome_links) == 1:
>                  found_row = row
>                  break
> @@ -131,17 +133,19 @@ class TestAllBuildsPage(SeleniumTestCase):
>          url = reverse('all-builds')
>          self.get(url)
>
> +        # should see a rebuild button for non-command-line builds
> +        selector = 'div[data-latest-build-result="%s"] .rebuild-btn' %
> build1.id
> +        time.sleep(2)
> +        run_again_button = self.find_all(selector)
> +        self.assertEqual(len(run_again_button), 1,
> +                         'should see a rebuild button for non-cli builds')
> +
>          # shouldn't see a rebuild button for command-line builds
>          selector = 'div[data-latest-build-result="%s"] .rebuild-btn' %
> default_build.id
>          run_again_button = self.find_all(selector)
>          self.assertEqual(len(run_again_button), 0,
>                           'should not see a rebuild button for cli builds')
>
> -        # should see a rebuild button for non-command-line builds
> -        selector = 'div[data-latest-build-result="%s"] .rebuild-btn' %
> build1.id
> -        run_again_button = self.find_all(selector)
> -        self.assertEqual(len(run_again_button), 1,
> -                         'should see a rebuild button for non-cli builds')
>
>      def test_tooltips_on_project_name(self):
>          """
> @@ -198,24 +202,24 @@ class TestAllBuildsPage(SeleniumTestCase):
>
>          # test recent builds area for successful build
>          element = self._get_build_time_element(build1)
> -        links = element.find_elements_by_css_selector('a')
> +        links = element.find_elements(By.CSS_SELECTOR, 'a')
>          msg = 'should be a link on the build time for a successful recent
> build'
>          self.assertEquals(len(links), 1, msg)
>
>          # test recent builds area for failed build
>          element = self._get_build_time_element(build2)
> -        links = element.find_elements_by_css_selector('a')
> +        links = element.find_elements(By.CSS_SELECTOR, 'a')
>          msg = 'should not be a link on the build time for a failed recent
> build'
>          self.assertEquals(len(links), 0, msg)
>
>          # test the time column for successful build
>          build1_row = self._get_row_for_build(build1)
> -        links = build1_row.find_elements_by_css_selector('td.time a')
> +        links = build1_row.find_elements(By.CSS_SELECTOR, 'td.time a')
>          msg = 'should be a link on the build time for a successful build'
>          self.assertEquals(len(links), 1, msg)
>
>          # test the time column for failed build
>          build2_row = self._get_row_for_build(build2)
> -        links = build2_row.find_elements_by_css_selector('td.time a')
> +        links = build2_row.find_elements(By.CSS_SELECTOR, 'td.time a')
>          msg = 'should not be a link on the build time for a failed build'
>          self.assertEquals(len(links), 0, msg)
> diff --git a/lib/toaster/tests/browser/test_all_projects_page.py
> b/lib/toaster/tests/browser/test_all_projects_page.py
> index 15b03400..3389d323 100644
> --- a/lib/toaster/tests/browser/test_all_projects_page.py
> +++ b/lib/toaster/tests/browser/test_all_projects_page.py
> @@ -16,6 +16,8 @@ from tests.browser.selenium_helpers import
> SeleniumTestCase
>  from orm.models import BitbakeVersion, Release, Project, Build
>  from orm.models import ProjectVariable
>
> +from selenium.webdriver.common.by import By
> +
>  class TestAllProjectsPage(SeleniumTestCase):
>      """ Browser tests for projects page /projects/ """
>
> @@ -117,7 +119,7 @@ class TestAllProjectsPage(SeleniumTestCase):
>
>          # check the release text for the default project
>          selector = 'span[data-project-field="release"] span.text-muted'
> -        element =
> default_project_row.find_element_by_css_selector(selector)
> +        element = default_project_row.find_element(By.CSS_SELECTOR,
> selector)
>          text = element.text.strip()
>          self.assertEqual(text, 'Not applicable',
>                           'release should be "not applicable" for default
> project')
> @@ -127,7 +129,7 @@ class TestAllProjectsPage(SeleniumTestCase):
>
>          # check the link in the release cell for the other project
>          selector = 'span[data-project-field="release"]'
> -        element = other_project_row.find_element_by_css_selector(selector)
> +        element = other_project_row.find_element(By.CSS_SELECTOR,
> selector)
>          text = element.text.strip()
>          self.assertEqual(text, self.release.name,
>                           'release name should be shown for non-default
> project')
> @@ -152,7 +154,7 @@ class TestAllProjectsPage(SeleniumTestCase):
>
>          # check the machine cell for the default project
>          selector = 'span[data-project-field="machine"] span.text-muted'
> -        element =
> default_project_row.find_element_by_css_selector(selector)
> +        element = default_project_row.find_element(By.CSS_SELECTOR,
> selector)
>          text = element.text.strip()
>          self.assertEqual(text, 'Not applicable',
>                           'machine should be not applicable for default
> project')
> @@ -162,7 +164,7 @@ class TestAllProjectsPage(SeleniumTestCase):
>
>          # check the link in the machine cell for the other project
>          selector = 'span[data-project-field="machine"]'
> -        element = other_project_row.find_element_by_css_selector(selector)
> +        element = other_project_row.find_element(By.CSS_SELECTOR,
> selector)
>          text = element.text.strip()
>          self.assertEqual(text, self.MACHINE_NAME,
>                           'machine name should be shown for non-default
> project')
> @@ -187,7 +189,7 @@ class TestAllProjectsPage(SeleniumTestCase):
>
>          # check the link on the name field
>          selector = 'span[data-project-field="name"] a'
> -        element =
> default_project_row.find_element_by_css_selector(selector)
> +        element = default_project_row.find_element(By.CSS_SELECTOR,
> selector)
>          link_url = element.get_attribute('href').strip()
>          expected_url = reverse('projectbuilds', args=(
> self.default_project.id,))
>          msg = 'link on default project name should point to builds but
> was %s' % link_url
> @@ -198,7 +200,7 @@ class TestAllProjectsPage(SeleniumTestCase):
>
>          # check the link for the other project
>          selector = 'span[data-project-field="name"] a'
> -        element = other_project_row.find_element_by_css_selector(selector)
> +        element = other_project_row.find_element(By.CSS_SELECTOR,
> selector)
>          link_url = element.get_attribute('href').strip()
>          expected_url = reverse('project', args=(self.project.id,))
>          msg = 'link on project name should point to configuration but was
> %s' % link_url
> diff --git a/lib/toaster/tests/browser/test_builddashboard_page.py
> b/lib/toaster/tests/browser/test_builddashboard_page.py
> index efcd89b3..1afa4a4d 100644
> --- a/lib/toaster/tests/browser/test_builddashboard_page.py
> +++ b/lib/toaster/tests/browser/test_builddashboard_page.py
> @@ -15,6 +15,8 @@ from tests.browser.selenium_helpers import
> SeleniumTestCase
>  from orm.models import Project, Release, BitbakeVersion, Build, LogMessage
>  from orm.models import Layer, Layer_Version, Recipe, CustomImageRecipe,
> Variable
>
> +from selenium.webdriver.common.by import By
> +
>  class TestBuildDashboardPage(SeleniumTestCase):
>      """ Tests for the build dashboard /build/X """
>
> @@ -183,7 +185,7 @@ class TestBuildDashboardPage(SeleniumTestCase):
>
>          found = False
>          for element in message_elements:
> -            log_message_text =
> element.find_element_by_tag_name('pre').text.strip()
> +            log_message_text = element.find_element(By.TAG_NAME,
> 'pre').text.strip()
>              text_matches = (log_message_text == expected_text)
>
>              log_message_pk = element.get_attribute('data-log-message-id')
> @@ -213,7 +215,7 @@ class TestBuildDashboardPage(SeleniumTestCase):
>          the WebElement modal match the list of text values in expected
>          """
>          # labels containing the radio buttons we're testing for
> -        labels = modal.find_elements_by_css_selector(".radio")
> +        labels = modal.find_elements(By.CSS_SELECTOR,".radio")
>
>          labels_text = [lab.text for lab in labels]
>          self.assertEqual(len(labels_text), len(expected))
> @@ -248,7 +250,7 @@ class TestBuildDashboardPage(SeleniumTestCase):
>          selector = '[data-role="edit-custom-image-trigger"]'
>          self.click(selector)
>
> -        modal = self.driver.find_element_by_id('edit-custom-image-modal')
> +        modal = self.driver.find_element(By.ID, 'edit-custom-image-modal')
>          self.wait_until_visible("#edit-custom-image-modal")
>
>          # recipes we expect to see in the edit custom image modal
> @@ -270,7 +272,7 @@ class TestBuildDashboardPage(SeleniumTestCase):
>          selector = '[data-role="new-custom-image-trigger"]'
>          self.click(selector)
>
> -        modal = self.driver.find_element_by_id('new-custom-image-modal')
> +        modal = self.driver.find_element(By.ID,'new-custom-image-modal')
>          self.wait_until_visible("#new-custom-image-modal")
>
>          # recipes we expect to see in the new custom image modal
> diff --git a/lib/toaster/tests/browser/test_most_recent_builds_states.py
> b/lib/toaster/tests/browser/test_most_recent_builds_states.py
> index 7844aaa3..a34a0928 100644
> --- a/lib/toaster/tests/browser/test_most_recent_builds_states.py
> +++ b/lib/toaster/tests/browser/test_most_recent_builds_states.py
> @@ -6,7 +6,7 @@
>  #
>  # Copyright (C) 2013-2016 Intel Corporation
>  #
> -
> +import time
>  from django.urls import reverse
>  from django.utils import timezone
>  from tests.browser.selenium_helpers import SeleniumTestCase
> @@ -14,6 +14,8 @@ from tests.browser.selenium_helpers_base import Wait
>  from orm.models import Project, Build, Task, Recipe, Layer, Layer_Version
>  from bldcontrol.models import BuildRequest
>
> +from selenium.webdriver.common.by import By
> +
>  class TestMostRecentBuildsStates(SeleniumTestCase):
>      """ Test states update correctly in most recent builds area """
>
> @@ -62,7 +64,7 @@ class TestMostRecentBuildsStates(SeleniumTestCase):
>          element = self.wait_until_visible(selector)
>
>          bar_selector = '#recipes-parsed-percentage-bar-%s' % build.id
> -        bar_element = element.find_element_by_css_selector(bar_selector)
> +        bar_element = element.find_element(By.CSS_SELECTOR, bar_selector)
>          self.assertEqual(bar_element.value_of_css_property('width'),
> '0px',
>              'recipe parse progress should be at 0')
>
> @@ -73,7 +75,7 @@ class TestMostRecentBuildsStates(SeleniumTestCase):
>          self.get(url)
>
>          element = self.wait_until_visible(selector)
> -        bar_element = element.find_element_by_css_selector(bar_selector)
> +        bar_element = element.find_element(By.CSS_SELECTOR, bar_selector)
>          recipe_bar_updated = lambda driver: \
>              bar_element.get_attribute('style') == 'width: 50%;'
>          msg = 'recipe parse progress bar should update to 50%'
> @@ -107,7 +109,7 @@ class TestMostRecentBuildsStates(SeleniumTestCase):
>          element = self.wait_until_visible(selector)
>
>          bar_selector = '#build-pc-done-bar-%s' % build.id
> -        bar_element = element.find_element_by_css_selector(bar_selector)
> +        bar_element = element.find_element(By.CSS_SELECTOR, bar_selector)
>
>          task_bar_updated = lambda driver: \
>              bar_element.get_attribute('style') == 'width: 50%;'
> @@ -121,7 +123,7 @@ class TestMostRecentBuildsStates(SeleniumTestCase):
>          self.get(url)
>
>          element = self.wait_until_visible(selector)
> -        bar_element = element.find_element_by_css_selector(bar_selector)
> +        bar_element = element.find_element(By.CSS_SELECTOR, bar_selector)
>          task_bar_updated = lambda driver: \
>              bar_element.get_attribute('style') == 'width: 100%;'
>          msg = 'tasks progress bar should update to 100%'
> diff --git a/lib/toaster/tests/browser/test_new_custom_image_page.py
> b/lib/toaster/tests/browser/test_new_custom_image_page.py
> index 9906ae42..6361f403 100644
> --- a/lib/toaster/tests/browser/test_new_custom_image_page.py
> +++ b/lib/toaster/tests/browser/test_new_custom_image_page.py
> @@ -6,6 +6,7 @@
>  #
>  # SPDX-License-Identifier: GPL-2.0-only
>  #
> +from bldcontrol.models import BuildEnvironment
>
>  from django.urls import reverse
>  from tests.browser.selenium_helpers import SeleniumTestCase
> @@ -18,6 +19,9 @@ class TestNewCustomImagePage(SeleniumTestCase):
>      CUSTOM_IMAGE_NAME = 'roopa-doopa'
>
>      def setUp(self):
> +        BuildEnvironment.objects.get_or_create(
> +            betype=BuildEnvironment.TYPE_LOCAL,
> +        )
>          release = Release.objects.create(
>              name='baz',
>              bitbake_version=BitbakeVersion.objects.create(name='v1')
> diff --git a/lib/toaster/tests/browser/test_new_project_page.py
> b/lib/toaster/tests/browser/test_new_project_page.py
> index e20a1f68..f4b2708f 100644
> --- a/lib/toaster/tests/browser/test_new_project_page.py
> +++ b/lib/toaster/tests/browser/test_new_project_page.py
> @@ -6,11 +6,13 @@
>  #
>  # SPDX-License-Identifier: GPL-2.0-only
>  #
> +import time
>
>  from django.urls import reverse
>  from tests.browser.selenium_helpers import SeleniumTestCase
>  from selenium.webdriver.support.ui import Select
>  from selenium.common.exceptions import InvalidElementStateException
> +from selenium.webdriver.common.by import By
>
>  from orm.models import Project, Release, BitbakeVersion
>
> @@ -47,13 +49,14 @@ class TestNewProjectPage(SeleniumTestCase):
>
>          url = reverse('newproject')
>          self.get(url)
> -
>          self.enter_text('#new-project-name', project_name)
>
>          select = Select(self.find('#projectversion'))
>          select.select_by_value(str(self.release.pk))
>
> +        time.sleep(1)
>          self.click("#create-project-button")
> +        time.sleep(2)
>
>          # We should get redirected to the new project's page with the
>          # notification at the top
> @@ -84,6 +87,12 @@ class TestNewProjectPage(SeleniumTestCase):
>          select = Select(self.find('#projectversion'))
>          select.select_by_value(str(self.release.pk))
>
> +        radio = self.driver.find_element(By.ID, 'type-new')
> +        radio.click()
> +
> +        self.click("#create-project-button")
> +        time.sleep(2)
> +
>          element = self.wait_until_visible('#hint-error-project-name')
>
>          self.assertTrue(("Project names must be unique" in element.text),
> @@ -96,6 +105,7 @@ class TestNewProjectPage(SeleniumTestCase):
>          except InvalidElementStateException:
>              pass
>
> +        time.sleep(2)
>          self.assertTrue(
>              (Project.objects.filter(name=project_name).count() == 1),
>              "New project not found in database")
> diff --git a/lib/toaster/tests/browser/test_project_config_page.py
> b/lib/toaster/tests/browser/test_project_config_page.py
> index 944bcb26..7b21460e 100644
> --- a/lib/toaster/tests/browser/test_project_config_page.py
> +++ b/lib/toaster/tests/browser/test_project_config_page.py
> @@ -11,6 +11,7 @@ from django.urls import reverse
>  from tests.browser.selenium_helpers import SeleniumTestCase
>
>  from orm.models import BitbakeVersion, Release, Project, ProjectVariable
> +from selenium.webdriver.common.by import By
>
>  class TestProjectConfigsPage(SeleniumTestCase):
>      """ Test data at /project/X/builds is displayed correctly """
> @@ -66,7 +67,7 @@ class TestProjectConfigsPage(SeleniumTestCase):
>
>          self.enter_text('#new-imagefs_types', imagefs_type)
>
> -        checkboxes =
> self.driver.find_elements_by_xpath("//input[@class='fs-checkbox-fstypes']")
> +        checkboxes = self.driver.find_elements(By.XPATH,
> "//input[@class='fs-checkbox-fstypes']")
>
>          for checkbox in checkboxes:
>              if checkbox.get_attribute("value") == "btrfs":
> @@ -95,7 +96,7 @@ class TestProjectConfigsPage(SeleniumTestCase):
>          for checkbox in checkboxes:
>              if checkbox.get_attribute("value") == "cpio":
>                 checkbox.click()
> -               element =
> self.driver.find_element_by_id('new-imagefs_types')
> +               element = self.driver.find_element(By.ID,
> 'new-imagefs_types')
>
>                 self.wait_until_visible('#new-imagefs_types')
>
> @@ -129,7 +130,7 @@ class TestProjectConfigsPage(SeleniumTestCase):
>          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.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')
> @@ -137,7 +138,7 @@ class TestProjectConfigsPage(SeleniumTestCase):
>          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.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')
> @@ -145,18 +146,18 @@ class TestProjectConfigsPage(SeleniumTestCase):
>          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.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')
> +        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.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')
> +        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')
>
> @@ -184,7 +185,7 @@ class TestProjectConfigsPage(SeleniumTestCase):
>          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.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')
> @@ -192,7 +193,7 @@ class TestProjectConfigsPage(SeleniumTestCase):
>          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.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')
> @@ -200,18 +201,18 @@ class TestProjectConfigsPage(SeleniumTestCase):
>          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.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')
> +        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.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')
> +        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')
>
> diff --git a/lib/toaster/tests/browser/test_toastertable_ui.py
> b/lib/toaster/tests/browser/test_toastertable_ui.py
> index e82d5ec6..e00c30a8 100644
> --- a/lib/toaster/tests/browser/test_toastertable_ui.py
> +++ b/lib/toaster/tests/browser/test_toastertable_ui.py
> @@ -13,6 +13,7 @@ from django.urls import reverse
>  from django.utils import timezone
>  from tests.browser.selenium_helpers import SeleniumTestCase
>  from orm.models import BitbakeVersion, Release, Project, Build
> +from selenium.webdriver.common.by import By
>
>  class TestToasterTableUI(SeleniumTestCase):
>      """
> @@ -33,7 +34,7 @@ class TestToasterTableUI(SeleniumTestCase):
>          table: WebElement for a ToasterTable
>          """
>          selector = 'thead a.sorted'
> -        heading = table.find_element_by_css_selector(selector)
> +        heading = table.find_element(By.CSS_SELECTOR, selector)
>          return heading.get_attribute('innerHTML').strip()
>
>      def _get_datetime_from_cell(self, row, selector):
> @@ -45,7 +46,7 @@ class TestToasterTableUI(SeleniumTestCase):
>          selector: CSS selector to use to find the cell containing the
> date time
>          string
>          """
> -        cell = row.find_element_by_css_selector(selector)
> +        cell = row.find_element(By.CSS_SELECTOR, selector)
>          cell_text = cell.get_attribute('innerHTML').strip()
>          return datetime.strptime(cell_text, '%d/%m/%y %H:%M')
>
> @@ -105,7 +106,7 @@ class TestToasterTableUI(SeleniumTestCase):
>          self.click('#checkbox-started_on')
>
>          # sort by started_on column
> -        links = table.find_elements_by_css_selector('th.started_on a')
> +        links = table.find_elements(By.CSS_SELECTOR, 'th.started_on a')
>          for link in links:
>              if link.get_attribute('innerHTML').strip() == 'Started on':
>                  link.click()
> diff --git a/lib/toaster/tests/builds/test_core_image_min.py
> b/lib/toaster/tests/builds/test_core_image_min.py
> index 44b6cbec..9cdaa15f 100644
> --- a/lib/toaster/tests/builds/test_core_image_min.py
> +++ b/lib/toaster/tests/builds/test_core_image_min.py
> @@ -26,6 +26,7 @@ class BuildCoreImageMinimal(BuildTest):
>
>      def setUp(self):
>          self.completed_build = self.build("core-image-minimal")
> +        self.built = self.target_already_built("core-image-minimal")
>
>      # Check if build name is unique - tc_id=795
>      def test_Build_Unique_Name(self):
> diff --git a/lib/toaster/tests/functional/functional_helpers.py
> b/lib/toaster/tests/functional/functional_helpers.py
> index 5c4ea717..c3191f66 100644
> --- a/lib/toaster/tests/functional/functional_helpers.py
> +++ b/lib/toaster/tests/functional/functional_helpers.py
> @@ -16,6 +16,9 @@ import re
>
>  from tests.browser.selenium_helpers_base import SeleniumTestCaseBase
>  from tests.builds.buildtest import load_build_environment
> +from bldcontrol.models import BuildEnvironment
> +from selenium.webdriver.common.by import By
> +from selenium.common.exceptions import NoSuchElementException
>
>  logger = logging.getLogger("toaster")
>
> @@ -30,6 +33,8 @@ class SeleniumFunctionalTestCase(SeleniumTestCaseBase):
>              raise RuntimeError("Please initialise django with the tests
> settings:  " \
>                  "DJANGO_SETTINGS_MODULE='toastermain.settings_test'")
>
> +        if BuildEnvironment.objects.count() == 0:
> +
> BuildEnvironment.objects.create(betype=BuildEnvironment.TYPE_LOCAL)
>          load_build_environment()
>
>          # start toaster
> @@ -74,8 +79,8 @@ class SeleniumFunctionalTestCase(SeleniumTestCaseBase):
>          """
>          try:
>              table_element = self.get_table_element(table_id)
> -            element = table_element.find_element_by_link_text(link_text)
> -        except self.NoSuchElementException:
> +            element = table_element.find_element(By.LINK_TEXT, link_text)
> +        except NoSuchElementException:
>              print('no element found')
>              raise
>          return element
> @@ -85,8 +90,8 @@ class SeleniumFunctionalTestCase(SeleniumTestCaseBase):
>  #return whole-table element
>              element_xpath = "//*[@id='" + table_id + "']"
>              try:
> -                element = self.driver.find_element_by_xpath(element_xpath)
> -            except self.NoSuchElementException:
> +                element = self.driver.find_element(By.XPATH,
> element_xpath)
> +            except NoSuchElementException:
>                  raise
>              return element
>          row = coordinate[0]
> @@ -95,8 +100,8 @@ class SeleniumFunctionalTestCase(SeleniumTestCaseBase):
>  #return whole-row element
>              element_xpath = "//*[@id='" + table_id + "']/tbody/tr[" +
> str(row) + "]"
>              try:
> -                element = self.driver.find_element_by_xpath(element_xpath)
> -            except self.NoSuchElementException:
> +                element = self.driver.find_element(By.XPATH,
> element_xpath)
> +            except NoSuchElementException:
>                  return False
>              return element
>  #now we are looking for an element with specified X and Y
> @@ -104,7 +109,7 @@ class SeleniumFunctionalTestCase(SeleniumTestCaseBase):
>
>          element_xpath = "//*[@id='" + table_id + "']/tbody/tr[" +
> str(row) + "]/td[" + str(column) + "]"
>          try:
> -            element = self.driver.find_element_by_xpath(element_xpath)
> -        except self.NoSuchElementException:
> +            element = self.driver.find_element(By.XPATH, element_xpath)
> +        except NoSuchElementException:
>              return False
>          return element
> diff --git a/lib/toaster/tests/functional/test_functional_basic.py
> b/lib/toaster/tests/functional/test_functional_basic.py
> index 5683e387..067ad99a 100644
> --- a/lib/toaster/tests/functional/test_functional_basic.py
> +++ b/lib/toaster/tests/functional/test_functional_basic.py
> @@ -10,6 +10,7 @@
>  import re
>  from tests.functional.functional_helpers import SeleniumFunctionalTestCase
>  from orm.models import Project
> +from selenium.webdriver.common.by import By
>
>  class FuntionalTestBasic(SeleniumFunctionalTestCase):
>
> @@ -17,10 +18,10 @@ class FuntionalTestBasic(SeleniumFunctionalTestCase):
>      def test_create_slenium_project(self):
>          project_name = 'selenium-project'
>          self.get('')
> -        self.driver.find_element_by_link_text("To start building, create
> your first Toaster project").click()
> -
> self.driver.find_element_by_id("new-project-name").send_keys(project_name)
> -        self.driver.find_element_by_id('projectversion').click()
> -        self.driver.find_element_by_id("create-project-button").click()
> +        self.driver.find_element(By.LINK_TEXT, "To start building, create
> your first Toaster project").click()
> +        self.driver.find_element(By.ID,
> "new-project-name").send_keys(project_name)
> +        self.driver.find_element(By.ID, 'projectversion').click()
> +        self.driver.find_element(By.ID, "create-project-button").click()
>          element = self.wait_until_visible('#project-created-notification')
>
>  self.assertTrue(self.element_exists('#project-created-notification'),'Project
> creation notification not shown')
>          self.assertTrue(project_name in element.text,
> @@ -35,77 +36,77 @@ class FuntionalTestBasic(SeleniumFunctionalTestCase):
>          self.find_element_by_link_text_in_table('projectstable',
> 'selenium-project').click()
>          self.assertTrue(self.element_exists('#config-nav'),'Configuration
> Tab does not exist')
>          project_URL=self.get_URL()
> -
> self.driver.find_element_by_xpath('//a[@href="'+project_URL+'"]').click()
> +        self.driver.find_element(By.XPATH,
> '//a[@href="'+project_URL+'"]').click()
>
>          try:
> -
> self.driver.find_element_by_xpath("//*[@id='config-nav']/ul/li/a[@href="+'"'+project_URL+'customimages/"'+"]").click()
> -            self.assertTrue(re.search("Custom
> images",self.driver.find_element_by_xpath("//div[@class='col-md-10']").text),'Custom
> images information is not loading properly')
> +            self.driver.find_element(By.XPATH,
> "//*[@id='config-nav']/ul/li/a[@href="+'"'+project_URL+'customimages/"'+"]").click()
> +            self.assertTrue(re.search("Custom
> images",self.driver.find_element(By.XPATH,
> "//div[@class='col-md-10']").text),'Custom images information is not
> loading properly')
>          except:
>              self.fail(msg='No Custom images tab available')
>
>          try:
> -
> self.driver.find_element_by_xpath("//*[@id='config-nav']/ul/li/a[@href="+'"'+project_URL+'images/"'+"]").click()
> -            self.assertTrue(re.search("Compatible image
> recipes",self.driver.find_element_by_xpath("//div[@class='col-md-10']").text),'The
> Compatible image recipes information is not loading properly')
> +            self.driver.find_element(By.XPATH,
> "//*[@id='config-nav']/ul/li/a[@href="+'"'+project_URL+'images/"'+"]").click()
> +            self.assertTrue(re.search("Compatible image
> recipes",self.driver.find_element(By.XPATH,
> "//div[@class='col-md-10']").text),'The Compatible image recipes
> information is not loading properly')
>          except:
>              self.fail(msg='No Compatible image tab available')
>
>          try:
> -
> self.driver.find_element_by_xpath("//*[@id='config-nav']/ul/li/a[@href="+'"'+project_URL+'softwarerecipes/"'+"]").click()
> -            self.assertTrue(re.search("Compatible software
> recipes",self.driver.find_element_by_xpath("//div[@class='col-md-10']").text),'The
> Compatible software recipe information is not loading properly')
> +            self.driver.find_element(By.XPATH,
> "//*[@id='config-nav']/ul/li/a[@href="+'"'+project_URL+'softwarerecipes/"'+"]").click()
> +            self.assertTrue(re.search("Compatible software
> recipes",self.driver.find_element(By.XPATH,
> "//div[@class='col-md-10']").text),'The Compatible software recipe
> information is not loading properly')
>          except:
>              self.fail(msg='No Compatible software recipe tab available')
>
>          try:
> -
> self.driver.find_element_by_xpath("//*[@id='config-nav']/ul/li/a[@href="+'"'+project_URL+'machines/"'+"]").click()
> -            self.assertTrue(re.search("Compatible
> machines",self.driver.find_element_by_xpath("//div[@class='col-md-10']").text),'The
> Compatible machine information is not loading properly')
> +            self.driver.find_element(By.XPATH,
> "//*[@id='config-nav']/ul/li/a[@href="+'"'+project_URL+'machines/"'+"]").click()
> +            self.assertTrue(re.search("Compatible
> machines",self.driver.find_element(By.XPATH,
> "//div[@class='col-md-10']").text),'The Compatible machine information is
> not loading properly')
>          except:
>              self.fail(msg='No Compatible machines tab available')
>
>          try:
> -
> self.driver.find_element_by_xpath("//*[@id='config-nav']/ul/li/a[@href="+'"'+project_URL+'layers/"'+"]").click()
> -            self.assertTrue(re.search("Compatible
> layers",self.driver.find_element_by_xpath("//div[@class='col-md-10']").text),'The
> Compatible layer information is not loading properly')
> +            self.driver.find_element(By.XPATH,
> "//*[@id='config-nav']/ul/li/a[@href="+'"'+project_URL+'layers/"'+"]").click()
> +            self.assertTrue(re.search("Compatible
> layers",self.driver.find_element(By.XPATH,
> "//div[@class='col-md-10']").text),'The Compatible layer information is not
> loading properly')
>          except:
>              self.fail(msg='No Compatible layers tab available')
>
>          try:
> -
> self.driver.find_element_by_xpath("//*[@id='config-nav']/ul/li/a[@href="+'"'+project_URL+'configuration"'+"]").click()
> -            self.assertTrue(re.search("Bitbake
> variables",self.driver.find_element_by_xpath("//div[@class='col-md-10']").text),'The
> Bitbake variables information is not loading properly')
> +            self.driver.find_element(By.XPATH,
> "//*[@id='config-nav']/ul/li/a[@href="+'"'+project_URL+'configuration"'+"]").click()
> +            self.assertTrue(re.search("Bitbake
> variables",self.driver.find_element(By.XPATH,
> "//div[@class='col-md-10']").text),'The Bitbake variables information is
> not loading properly')
>          except:
>              self.fail(msg='No Bitbake variables tab available')
>
>  #   testcase (1516)
>      def test_review_configuration_information(self):
>          self.get('')
> -
> self.driver.find_element_by_xpath("//div[@id='global-nav']/ul/li/a[@href="+'"'+'/toastergui/projects/'+'"'+"]").click()
> +        self.driver.find_element(By.XPATH,
> "//div[@id='global-nav']/ul/li/a[@href="+'"'+'/toastergui/projects/'+'"'+"]").click()
>          self.wait_until_visible('#projectstable')
>          self.find_element_by_link_text_in_table('projectstable',
> 'selenium-project').click()
>          project_URL=self.get_URL()
>
>          try:
>
> self.assertTrue(self.element_exists('#machine-section'),'Machine section
> for the project configuration page does not exist')
> -
>  self.assertTrue(re.search("qemux86",self.driver.find_element_by_xpath("//span[@id='project-machine-name']").text),'The
> machine type is not assigned')
> -
>  self.driver.find_element_by_xpath("//span[@id='change-machine-toggle']").click()
> +
>  self.assertTrue(re.search("qemux86",self.driver.find_element(By.XPATH,
> "//span[@id='project-machine-name']").text),'The machine type is not
> assigned')
> +           self.driver.find_element(By.XPATH,
> "//span[@id='change-machine-toggle']").click()
>             self.wait_until_visible('#select-machine-form')
>             self.wait_until_visible('#cancel-machine-change')
> -
>  self.driver.find_element_by_xpath("//form[@id='select-machine-form']/a[@id='cancel-machine-change']").click()
> +           self.driver.find_element(By.XPATH,
> "//form[@id='select-machine-form']/a[@id='cancel-machine-change']").click()
>          except:
>             self.fail(msg='The machine information is wrong in the
> configuration page')
>
>          try:
> -           self.driver.find_element_by_id('no-most-built')
> +           self.driver.find_element(By.ID, 'no-most-built')
>          except:
>             self.fail(msg='No Most built information in project detail
> page')
>
>          try:
> -           self.assertTrue(re.search("Yocto Project
> master",self.driver.find_element_by_xpath("//span[@id='project-release-title']").text),'The
> project release is not defined')
> +           self.assertTrue(re.search("Yocto Project
> master",self.driver.find_element(By.XPATH,
> "//span[@id='project-release-title']").text),'The project release is not
> defined')
>          except:
>             self.fail(msg='No project release title information in project
> detail page')
>
>          try:
> -
>  self.driver.find_element_by_xpath("//div[@id='layer-container']")
> -
>  self.assertTrue(re.search("3",self.driver.find_element_by_id("project-layers-count").text),'There
> should be 3 layers listed in the layer count')
> -           layer_list =
> self.driver.find_element_by_id("layers-in-project-list")
> -           layers = layer_list.find_elements_by_tag_name("li")
> +           self.driver.find_element(By.XPATH,
> "//div[@id='layer-container']")
> +           self.assertTrue(re.search("3",self.driver.find_element(By.ID,
> "project-layers-count").text),'There should be 3 layers listed in the layer
> count')
> +           layer_list = self.driver.find_element(By.ID,
> "layers-in-project-list")
> +           layers = layer_list.find_elements(By.TAG_NAME, "li")
>             for layer in layers:
>                 if re.match ("openembedded-core",layer.text):
>                      print ("openembedded-core layer is a default layer in
> the project configuration")
> @@ -121,60 +122,61 @@ class FuntionalTestBasic(SeleniumFunctionalTestCase):
>  #   testcase (1517)
>      def test_verify_machine_information(self):
>          self.get('')
> -
> self.driver.find_element_by_xpath("//div[@id='global-nav']/ul/li/a[@href="+'"'+'/toastergui/projects/'+'"'+"]").click()
> +        self.driver.find_element(By.XPATH,
> "//div[@id='global-nav']/ul/li/a[@href="+'"'+'/toastergui/projects/'+'"'+"]").click()
>          self.wait_until_visible('#projectstable')
>          self.find_element_by_link_text_in_table('projectstable',
> 'selenium-project').click()
>
>          try:
>
>  self.assertTrue(self.element_exists('#machine-section'),'Machine section
> for the project configuration page does not exist')
> -
> self.assertTrue(re.search("qemux86",self.driver.find_element_by_id("project-machine-name").text),'The
> machine type is not assigned')
> -
> self.driver.find_element_by_id("change-machine-toggle").click()
> +
> self.assertTrue(re.search("qemux86",self.driver.find_element(By.ID,
> "project-machine-name").text),'The machine type is not assigned')
> +            self.driver.find_element(By.ID,
> "change-machine-toggle").click()
>              self.wait_until_visible('#select-machine-form')
>              self.wait_until_visible('#cancel-machine-change')
> -
> self.driver.find_element_by_id("cancel-machine-change").click()
> +            self.driver.find_element(By.ID,
> "cancel-machine-change").click()
>          except:
>              self.fail(msg='The machine information is wrong in the
> configuration page')
>
>  #   testcase (1518)
>      def test_verify_most_built_recipes_information(self):
>          self.get('')
> -
> self.driver.find_element_by_xpath("//div[@id='global-nav']/ul/li/a[@href="+'"'+'/toastergui/projects/'+'"'+"]").click()
> +        self.driver.find_element(By.XPATH,
> "//div[@id='global-nav']/ul/li/a[@href="+'"'+'/toastergui/projects/'+'"'+"]").click()
> +
>          self.wait_until_visible('#projectstable')
>          self.find_element_by_link_text_in_table('projectstable',
> 'selenium-project').click()
>          project_URL=self.get_URL()
>
>          try:
> -            self.assertTrue(re.search("You haven't built any recipes
> yet",self.driver.find_element_by_id("no-most-built").text),'Default message
> of no builds is not present')
> -
> self.driver.find_element_by_xpath("//div[@id='no-most-built']/p/a[@href="+'"'+project_URL+'images/"'+"]").click()
> -            self.assertTrue(re.search("Compatible image
> recipes",self.driver.find_element_by_xpath("//div[@class='col-md-10']").text),'The
> Choose a recipe to build link  is not working  properly')
> +            self.assertTrue(re.search("You haven't built any recipes
> yet",self.driver.find_element(By.ID, "no-most-built").text),'Default
> message of no builds is not present')
> +            self.driver.find_element(By.XPATH,
> "//div[@id='no-most-built']/p/a[@href="+'"'+project_URL+'images/"'+"]").click()
> +            self.assertTrue(re.search("Compatible image
> recipes",self.driver.find_element(By.XPATH,
> "//div[@class='col-md-10']").text),'The Choose a recipe to build link  is
> not working  properly')
>          except:
>              self.fail(msg='No Most built information in project detail
> page')
>
>  #   testcase (1519)
>      def test_verify_project_release_information(self):
>          self.get('')
> -
> self.driver.find_element_by_xpath("//div[@id='global-nav']/ul/li/a[@href="+'"'+'/toastergui/projects/'+'"'+"]").click()
> +        self.driver.find_element(By.XPATH,
> "//div[@id='global-nav']/ul/li/a[@href="+'"'+'/toastergui/projects/'+'"'+"]").click()
>          self.wait_until_visible('#projectstable')
>          self.find_element_by_link_text_in_table('projectstable',
> 'selenium-project').click()
>
>          try:
> -            self.assertTrue(re.search("Yocto Project
> master",self.driver.find_element_by_id("project-release-title").text),'The
> project release is not defined')
> +            self.assertTrue(re.search("Yocto Project
> master",self.driver.find_element(By.ID, "project-release-title").text),'The
> project release is not defined')
>          except:
>              self.fail(msg='No project release title information in
> project detail page')
>
>  #   testcase (1520)
>      def test_verify_layer_information(self):
>          self.get('')
> -
> self.driver.find_element_by_xpath("//div[@id='global-nav']/ul/li/a[@href="+'"'+'/toastergui/projects/'+'"'+"]").click()
> +        self.driver.find_element(By.XPATH,
> "//div[@id='global-nav']/ul/li/a[@href="+'"'+'/toastergui/projects/'+'"'+"]").click()
>          self.wait_until_visible('#projectstable')
>          self.find_element_by_link_text_in_table('projectstable',
> 'selenium-project').click()
>          project_URL=self.get_URL()
>
>          try:
> -
>  self.driver.find_element_by_xpath("//div[@id='layer-container']")
> -
>  self.assertTrue(re.search("3",self.driver.find_element_by_id("project-layers-count").text),'There
> should be 3 layers listed in the layer count')
> -           layer_list =
> self.driver.find_element_by_id("layers-in-project-list")
> -           layers = layer_list.find_elements_by_tag_name("li")
> +           self.driver.find_element(By.XPATH,
> "//div[@id='layer-container']")
> +           self.assertTrue(re.search("3",self.driver.find_element(By.ID,
> "project-layers-count").text),'There should be 3 layers listed in the layer
> count')
> +           layer_list = self.driver.find_element(By.ID,
> "layers-in-project-list")
> +           layers = layer_list.find_element(By.TAG_NAME, "li")
>
>             for layer in layers:
>                 if re.match ("openembedded-core",layer.text):
> @@ -186,43 +188,43 @@ class FuntionalTestBasic(SeleniumFunctionalTestCase):
>                 else:
>                    self.fail(msg='default layers are missing from the
> project configuration')
>
> -
>  self.driver.find_element_by_xpath("//input[@id='layer-add-input']")
> -
>  self.driver.find_element_by_xpath("//button[@id='add-layer-btn']")
> -
>  self.driver.find_element_by_xpath("//div[@id='layer-container']/form[@class='form-inline']/p/a[@id='view-compatible-layers']")
> -
>  self.driver.find_element_by_xpath("//div[@id='layer-container']/form[@class='form-inline']/p/a[@href="+'"'+project_URL+'importlayer"'+"]")
> +           self.driver.find_element(By.XPATH,
> "//input[@id='layer-add-input']")
> +           self.driver.find_element(By.XPATH,
> "//button[@id='add-layer-btn']")
> +           self.driver.find_element(By.XPATH,
> "//div[@id='layer-container']/form[@class='form-inline']/p/a[@id='view-compatible-layers']")
> +           self.driver.find_element(By.XPATH,
> "//div[@id='layer-container']/form[@class='form-inline']/p/a[@href="+'"'+project_URL+'importlayer"'+"]")
>          except:
>              self.fail(msg='No Layer information in project detail page')
>
>  #   testcase (1521)
>      def test_verify_project_detail_links(self):
>          self.get('')
> -
> self.driver.find_element_by_xpath("//div[@id='global-nav']/ul/li/a[@href="+'"'+'/toastergui/projects/'+'"'+"]").click()
> +        self.driver.find_element(By.XPATH,
> "//div[@id='global-nav']/ul/li/a[@href="+'"'+'/toastergui/projects/'+'"'+"]").click()
>          self.wait_until_visible('#projectstable')
>          self.find_element_by_link_text_in_table('projectstable',
> 'selenium-project').click()
>          project_URL=self.get_URL()
>
> -
> self.driver.find_element_by_xpath("//div[@id='project-topbar']/ul[@class='nav
> nav-tabs']/li[@id='topbar-configuration-tab']/a[@href="+'"'+project_URL+'"'+"]").click()
> -
> self.assertTrue(re.search("Configuration",self.driver.find_element_by_xpath("//div[@id='project-topbar']/ul[@class='nav
> nav-tabs']/li[@id='topbar-configuration-tab']/a[@href="+'"'+project_URL+'"'+"]").text),
> 'Configuration tab in project topbar is misspelled')
> +        self.driver.find_element(By.XPATH,
> "//div[@id='project-topbar']/ul[@class='nav
> nav-tabs']/li[@id='topbar-configuration-tab']/a[@href="+'"'+project_URL+'"'+"]").click()
> +
> self.assertTrue(re.search("Configuration",self.driver.find_element(By.XPATH,
> "//div[@id='project-topbar']/ul[@class='nav
> nav-tabs']/li[@id='topbar-configuration-tab']/a[@href="+'"'+project_URL+'"'+"]").text),
> 'Configuration tab in project topbar is misspelled')
>
>          try:
> -
> self.driver.find_element_by_xpath("//div[@id='project-topbar']/ul[@class='nav
> nav-tabs']/li/a[@href="+'"'+project_URL+'builds/"'+"]").click()
> -
> self.assertTrue(re.search("Builds",self.driver.find_element_by_xpath("//div[@id='project-topbar']/ul[@class='nav
> nav-tabs']/li/a[@href="+'"'+project_URL+'builds/"'+"]").text), 'Builds tab
> in project topbar is misspelled')
> -
> self.driver.find_element_by_xpath("//div[@id='empty-state-projectbuildstable']")
> +            self.driver.find_element(By.XPATH,
> "//div[@id='project-topbar']/ul[@class='nav
> nav-tabs']/li/a[@href="+'"'+project_URL+'builds/"'+"]").click()
> +
> self.assertTrue(re.search("Builds",self.driver.find_element(By.XPATH,
> "//div[@id='project-topbar']/ul[@class='nav
> nav-tabs']/li/a[@href="+'"'+project_URL+'builds/"'+"]").text), 'Builds tab
> in project topbar is misspelled')
> +            self.driver.find_element(By.XPATH,
> "//div[@id='empty-state-projectbuildstable']")
>          except:
>              self.fail(msg='Builds tab information is not present')
>
>          try:
> -
> self.driver.find_element_by_xpath("//div[@id='project-topbar']/ul[@class='nav
> nav-tabs']/li/a[@href="+'"'+project_URL+'importlayer"'+"]").click()
> -            self.assertTrue(re.search("Import
> layer",self.driver.find_element_by_xpath("//div[@id='project-topbar']/ul[@class='nav
> nav-tabs']/li/a[@href="+'"'+project_URL+'importlayer"'+"]").text), 'Import
> layer tab in project topbar is misspelled')
> -
> self.driver.find_element_by_xpath("//fieldset[@id='repo-select']")
> -
> self.driver.find_element_by_xpath("//fieldset[@id='git-repo']")
> +            self.driver.find_element(By.XPATH,
> "//div[@id='project-topbar']/ul[@class='nav
> nav-tabs']/li/a[@href="+'"'+project_URL+'importlayer"'+"]").click()
> +            self.assertTrue(re.search("Import
> layer",self.driver.find_element(By.XPATH,
> "//div[@id='project-topbar']/ul[@class='nav
> nav-tabs']/li/a[@href="+'"'+project_URL+'importlayer"'+"]").text), 'Import
> layer tab in project topbar is misspelled')
> +            self.driver.find_element(By.XPATH,
> "//fieldset[@id='repo-select']")
> +            self.driver.find_element(By.XPATH,
> "//fieldset[@id='git-repo']")
>          except:
>              self.fail(msg='Import layer tab not loading properly')
>
>          try:
> -
> self.driver.find_element_by_xpath("//div[@id='project-topbar']/ul[@class='nav
> nav-tabs']/li/a[@href="+'"'+project_URL+'newcustomimage/"'+"]").click()
> -            self.assertTrue(re.search("New custom
> image",self.driver.find_element_by_xpath("//div[@id='project-topbar']/ul[@class='nav
> nav-tabs']/li/a[@href="+'"'+project_URL+'newcustomimage/"'+"]").text), 'New
> custom image tab in project topbar is misspelled')
> -            self.assertTrue(re.search("Select the image recipe you want
> to
> customise",self.driver.find_element_by_xpath("//div[@class='col-md-12']/h2").text),'The
> new custom image tab is not loading correctly')
> +            self.driver.find_element(By.XPATH,
> "//div[@id='project-topbar']/ul[@class='nav
> nav-tabs']/li/a[@href="+'"'+project_URL+'newcustomimage/"'+"]").click()
> +            self.assertTrue(re.search("New custom
> image",self.driver.find_element(By.XPATH,
> "//div[@id='project-topbar']/ul[@class='nav
> nav-tabs']/li/a[@href="+'"'+project_URL+'newcustomimage/"'+"]").text), 'New
> custom image tab in project topbar is misspelled')
> +            self.assertTrue(re.search("Select the image recipe you want
> to customise",self.driver.find_element(By.XPATH,
> "//div[@class='col-md-12']/h2").text),'The new custom image tab is not
> loading correctly')
>          except:
>              self.fail(msg='New custom image tab not loading properly')
>
> diff --git a/lib/toaster/tests/toaster-tests-requirements.txt
> b/lib/toaster/tests/toaster-tests-requirements.txt
> index 4f9fcc46..f30ac070 100644
> --- a/lib/toaster/tests/toaster-tests-requirements.txt
> +++ b/lib/toaster/tests/toaster-tests-requirements.txt
> @@ -1 +1 @@
> -selenium==2.49.2
> +selenium>=4.13.0
> diff --git a/lib/toaster/tests/views/test_views.py
> b/lib/toaster/tests/views/test_views.py
> index 735d596b..f962e762 100644
> --- a/lib/toaster/tests/views/test_views.py
> +++ b/lib/toaster/tests/views/test_views.py
> @@ -19,6 +19,7 @@ from orm.models import Layer_Version, Recipe
>  from orm.models import CustomImageRecipe
>  from orm.models import CustomImagePackage
>
> +from bldcontrol.models import BuildEnvironment
>  import inspect
>  import toastergui
>
> @@ -45,6 +46,9 @@ class ViewTests(TestCase):
>          self.cust_package = CustomImagePackage.objects.first()
>          self.package = Package.objects.first()
>          self.lver = Layer_Version.objects.first()
> +        if BuildEnvironment.objects.count() == 0:
> +
> BuildEnvironment.objects.create(betype=BuildEnvironment.TYPE_LOCAL)
> +
>
>      def test_get_base_call_returns_html(self):
>          """Basic test for all-projects view"""
> diff --git a/lib/toaster/toastergui/widgets.py
> b/lib/toaster/toastergui/widgets.py
> index 53696912..6e87285c 100644
> --- a/lib/toaster/toastergui/widgets.py
> +++ b/lib/toaster/toastergui/widgets.py
> @@ -305,6 +305,7 @@ class ToasterTable(TemplateView):
>
>          self.setup_columns(**kwargs)
>
> +        self.apply_orderby('pk')
>          if search:
>              self.apply_search(search)
>          if filters:
> --
> 2.34.1
>
>
> -=-=-=-=-=-=-=-=-=-=-=-
> Links: You receive all messages sent to this group.
> View/Reply Online (#15177):
> https://lists.openembedded.org/g/bitbake-devel/message/15177
> Mute This Topic: https://lists.openembedded.org/mt/101738649/924729
> Group Owner: bitbake-devel+owner@lists.openembedded.org
> Unsubscribe: https://lists.openembedded.org/g/bitbake-devel/unsub [
> ticotimo@gmail.com]
> -=-=-=-=-=-=-=-=-=-=-=-
>
>
diff mbox series

Patch

diff --git a/lib/toaster/orm/models.py b/lib/toaster/orm/models.py
index f9fcf9e4..0d503a51 100644
--- a/lib/toaster/orm/models.py
+++ b/lib/toaster/orm/models.py
@@ -1733,7 +1733,7 @@  class CustomImageRecipe(Recipe):
         packages_conf += "\""
 
         base_recipe_path = self.get_base_recipe_file()
-        if base_recipe_path:
+        if base_recipe_path and os.path.isfile(base_recipe_path):
             base_recipe = open(base_recipe_path, 'r').read()
         else:
             # Pass back None to trigger error message to user
diff --git a/lib/toaster/tests/browser/selenium_helpers_base.py b/lib/toaster/tests/browser/selenium_helpers_base.py
index 644d45fe..9a4e27a3 100644
--- a/lib/toaster/tests/browser/selenium_helpers_base.py
+++ b/lib/toaster/tests/browser/selenium_helpers_base.py
@@ -21,6 +21,7 @@  import unittest
 
 from selenium import webdriver
 from selenium.webdriver.support.ui import WebDriverWait
+from selenium.webdriver.common.by import By
 from selenium.webdriver.common.desired_capabilities import DesiredCapabilities
 from selenium.common.exceptions import NoSuchElementException, \
         StaleElementReferenceException, TimeoutException
@@ -32,9 +33,7 @@  def create_selenium_driver(cls,browser='chrome'):
         browser = env_browser
 
     if browser == 'chrome':
-        return webdriver.Chrome(
-            service_args=["--verbose", "--log-path=selenium.log"]
-        )
+        return webdriver.Chrome()
     elif browser == 'firefox':
         return webdriver.Firefox()
     elif browser == 'marionette':
@@ -153,11 +152,11 @@  class SeleniumTestCaseBase(unittest.TestCase):
 
     def find(self, selector):
         """ Find single element by CSS selector """
-        return self.driver.find_element_by_css_selector(selector)
+        return self.driver.find_element(By.CSS_SELECTOR, selector)
 
     def find_all(self, selector):
         """ Find all elements matching CSS selector """
-        return self.driver.find_elements_by_css_selector(selector)
+        return self.driver.find_elements(By.CSS_SELECTOR, selector)
 
     def element_exists(self, selector):
         """
diff --git a/lib/toaster/tests/browser/test_all_builds_page.py b/lib/toaster/tests/browser/test_all_builds_page.py
index 8423d3da..d4312bb3 100644
--- a/lib/toaster/tests/browser/test_all_builds_page.py
+++ b/lib/toaster/tests/browser/test_all_builds_page.py
@@ -7,7 +7,7 @@ 
 # SPDX-License-Identifier: GPL-2.0-only
 #
 
-import re
+import re, time
 
 from django.urls import reverse
 from django.utils import timezone
@@ -15,6 +15,8 @@  from tests.browser.selenium_helpers import SeleniumTestCase
 
 from orm.models import BitbakeVersion, Release, Project, Build, Target
 
+from selenium.webdriver.common.by import By
+
 
 class TestAllBuildsPage(SeleniumTestCase):
     """ Tests for all builds page /builds/ """
@@ -91,7 +93,7 @@  class TestAllBuildsPage(SeleniumTestCase):
         found_row = None
         for row in rows:
 
-            outcome_links = row.find_elements_by_css_selector(selector)
+            outcome_links = row.find_elements(By.CSS_SELECTOR, selector)
             if len(outcome_links) == 1:
                 found_row = row
                 break
@@ -131,17 +133,19 @@  class TestAllBuildsPage(SeleniumTestCase):
         url = reverse('all-builds')
         self.get(url)
 
+        # should see a rebuild button for non-command-line builds
+        selector = 'div[data-latest-build-result="%s"] .rebuild-btn' % build1.id
+        time.sleep(2)
+        run_again_button = self.find_all(selector)
+        self.assertEqual(len(run_again_button), 1,
+                         'should see a rebuild button for non-cli builds')
+
         # shouldn't see a rebuild button for command-line builds
         selector = 'div[data-latest-build-result="%s"] .rebuild-btn' % default_build.id
         run_again_button = self.find_all(selector)
         self.assertEqual(len(run_again_button), 0,
                          'should not see a rebuild button for cli builds')
 
-        # should see a rebuild button for non-command-line builds
-        selector = 'div[data-latest-build-result="%s"] .rebuild-btn' % build1.id
-        run_again_button = self.find_all(selector)
-        self.assertEqual(len(run_again_button), 1,
-                         'should see a rebuild button for non-cli builds')
 
     def test_tooltips_on_project_name(self):
         """
@@ -198,24 +202,24 @@  class TestAllBuildsPage(SeleniumTestCase):
 
         # test recent builds area for successful build
         element = self._get_build_time_element(build1)
-        links = element.find_elements_by_css_selector('a')
+        links = element.find_elements(By.CSS_SELECTOR, 'a')
         msg = 'should be a link on the build time for a successful recent build'
         self.assertEquals(len(links), 1, msg)
 
         # test recent builds area for failed build
         element = self._get_build_time_element(build2)
-        links = element.find_elements_by_css_selector('a')
+        links = element.find_elements(By.CSS_SELECTOR, 'a')
         msg = 'should not be a link on the build time for a failed recent build'
         self.assertEquals(len(links), 0, msg)
 
         # test the time column for successful build
         build1_row = self._get_row_for_build(build1)
-        links = build1_row.find_elements_by_css_selector('td.time a')
+        links = build1_row.find_elements(By.CSS_SELECTOR, 'td.time a')
         msg = 'should be a link on the build time for a successful build'
         self.assertEquals(len(links), 1, msg)
 
         # test the time column for failed build
         build2_row = self._get_row_for_build(build2)
-        links = build2_row.find_elements_by_css_selector('td.time a')
+        links = build2_row.find_elements(By.CSS_SELECTOR, 'td.time a')
         msg = 'should not be a link on the build time for a failed build'
         self.assertEquals(len(links), 0, msg)
diff --git a/lib/toaster/tests/browser/test_all_projects_page.py b/lib/toaster/tests/browser/test_all_projects_page.py
index 15b03400..3389d323 100644
--- a/lib/toaster/tests/browser/test_all_projects_page.py
+++ b/lib/toaster/tests/browser/test_all_projects_page.py
@@ -16,6 +16,8 @@  from tests.browser.selenium_helpers import SeleniumTestCase
 from orm.models import BitbakeVersion, Release, Project, Build
 from orm.models import ProjectVariable
 
+from selenium.webdriver.common.by import By
+
 class TestAllProjectsPage(SeleniumTestCase):
     """ Browser tests for projects page /projects/ """
 
@@ -117,7 +119,7 @@  class TestAllProjectsPage(SeleniumTestCase):
 
         # check the release text for the default project
         selector = 'span[data-project-field="release"] span.text-muted'
-        element = default_project_row.find_element_by_css_selector(selector)
+        element = default_project_row.find_element(By.CSS_SELECTOR, selector)
         text = element.text.strip()
         self.assertEqual(text, 'Not applicable',
                          'release should be "not applicable" for default project')
@@ -127,7 +129,7 @@  class TestAllProjectsPage(SeleniumTestCase):
 
         # check the link in the release cell for the other project
         selector = 'span[data-project-field="release"]'
-        element = other_project_row.find_element_by_css_selector(selector)
+        element = other_project_row.find_element(By.CSS_SELECTOR, selector)
         text = element.text.strip()
         self.assertEqual(text, self.release.name,
                          'release name should be shown for non-default project')
@@ -152,7 +154,7 @@  class TestAllProjectsPage(SeleniumTestCase):
 
         # check the machine cell for the default project
         selector = 'span[data-project-field="machine"] span.text-muted'
-        element = default_project_row.find_element_by_css_selector(selector)
+        element = default_project_row.find_element(By.CSS_SELECTOR, selector)
         text = element.text.strip()
         self.assertEqual(text, 'Not applicable',
                          'machine should be not applicable for default project')
@@ -162,7 +164,7 @@  class TestAllProjectsPage(SeleniumTestCase):
 
         # check the link in the machine cell for the other project
         selector = 'span[data-project-field="machine"]'
-        element = other_project_row.find_element_by_css_selector(selector)
+        element = other_project_row.find_element(By.CSS_SELECTOR, selector)
         text = element.text.strip()
         self.assertEqual(text, self.MACHINE_NAME,
                          'machine name should be shown for non-default project')
@@ -187,7 +189,7 @@  class TestAllProjectsPage(SeleniumTestCase):
 
         # check the link on the name field
         selector = 'span[data-project-field="name"] a'
-        element = default_project_row.find_element_by_css_selector(selector)
+        element = default_project_row.find_element(By.CSS_SELECTOR, selector)
         link_url = element.get_attribute('href').strip()
         expected_url = reverse('projectbuilds', args=(self.default_project.id,))
         msg = 'link on default project name should point to builds but was %s' % link_url
@@ -198,7 +200,7 @@  class TestAllProjectsPage(SeleniumTestCase):
 
         # check the link for the other project
         selector = 'span[data-project-field="name"] a'
-        element = other_project_row.find_element_by_css_selector(selector)
+        element = other_project_row.find_element(By.CSS_SELECTOR, selector)
         link_url = element.get_attribute('href').strip()
         expected_url = reverse('project', args=(self.project.id,))
         msg = 'link on project name should point to configuration but was %s' % link_url
diff --git a/lib/toaster/tests/browser/test_builddashboard_page.py b/lib/toaster/tests/browser/test_builddashboard_page.py
index efcd89b3..1afa4a4d 100644
--- a/lib/toaster/tests/browser/test_builddashboard_page.py
+++ b/lib/toaster/tests/browser/test_builddashboard_page.py
@@ -15,6 +15,8 @@  from tests.browser.selenium_helpers import SeleniumTestCase
 from orm.models import Project, Release, BitbakeVersion, Build, LogMessage
 from orm.models import Layer, Layer_Version, Recipe, CustomImageRecipe, Variable
 
+from selenium.webdriver.common.by import By
+
 class TestBuildDashboardPage(SeleniumTestCase):
     """ Tests for the build dashboard /build/X """
 
@@ -183,7 +185,7 @@  class TestBuildDashboardPage(SeleniumTestCase):
 
         found = False
         for element in message_elements:
-            log_message_text = element.find_element_by_tag_name('pre').text.strip()
+            log_message_text = element.find_element(By.TAG_NAME, 'pre').text.strip()
             text_matches = (log_message_text == expected_text)
 
             log_message_pk = element.get_attribute('data-log-message-id')
@@ -213,7 +215,7 @@  class TestBuildDashboardPage(SeleniumTestCase):
         the WebElement modal match the list of text values in expected
         """
         # labels containing the radio buttons we're testing for
-        labels = modal.find_elements_by_css_selector(".radio")
+        labels = modal.find_elements(By.CSS_SELECTOR,".radio")
 
         labels_text = [lab.text for lab in labels]
         self.assertEqual(len(labels_text), len(expected))
@@ -248,7 +250,7 @@  class TestBuildDashboardPage(SeleniumTestCase):
         selector = '[data-role="edit-custom-image-trigger"]'
         self.click(selector)
 
-        modal = self.driver.find_element_by_id('edit-custom-image-modal')
+        modal = self.driver.find_element(By.ID, 'edit-custom-image-modal')
         self.wait_until_visible("#edit-custom-image-modal")
 
         # recipes we expect to see in the edit custom image modal
@@ -270,7 +272,7 @@  class TestBuildDashboardPage(SeleniumTestCase):
         selector = '[data-role="new-custom-image-trigger"]'
         self.click(selector)
 
-        modal = self.driver.find_element_by_id('new-custom-image-modal')
+        modal = self.driver.find_element(By.ID,'new-custom-image-modal')
         self.wait_until_visible("#new-custom-image-modal")
 
         # recipes we expect to see in the new custom image modal
diff --git a/lib/toaster/tests/browser/test_most_recent_builds_states.py b/lib/toaster/tests/browser/test_most_recent_builds_states.py
index 7844aaa3..a34a0928 100644
--- a/lib/toaster/tests/browser/test_most_recent_builds_states.py
+++ b/lib/toaster/tests/browser/test_most_recent_builds_states.py
@@ -6,7 +6,7 @@ 
 #
 # Copyright (C) 2013-2016 Intel Corporation
 #
-
+import time
 from django.urls import reverse
 from django.utils import timezone
 from tests.browser.selenium_helpers import SeleniumTestCase
@@ -14,6 +14,8 @@  from tests.browser.selenium_helpers_base import Wait
 from orm.models import Project, Build, Task, Recipe, Layer, Layer_Version
 from bldcontrol.models import BuildRequest
 
+from selenium.webdriver.common.by import By
+
 class TestMostRecentBuildsStates(SeleniumTestCase):
     """ Test states update correctly in most recent builds area """
 
@@ -62,7 +64,7 @@  class TestMostRecentBuildsStates(SeleniumTestCase):
         element = self.wait_until_visible(selector)
 
         bar_selector = '#recipes-parsed-percentage-bar-%s' % build.id
-        bar_element = element.find_element_by_css_selector(bar_selector)
+        bar_element = element.find_element(By.CSS_SELECTOR, bar_selector)
         self.assertEqual(bar_element.value_of_css_property('width'), '0px',
             'recipe parse progress should be at 0')
 
@@ -73,7 +75,7 @@  class TestMostRecentBuildsStates(SeleniumTestCase):
         self.get(url)
 
         element = self.wait_until_visible(selector)
-        bar_element = element.find_element_by_css_selector(bar_selector)
+        bar_element = element.find_element(By.CSS_SELECTOR, bar_selector)
         recipe_bar_updated = lambda driver: \
             bar_element.get_attribute('style') == 'width: 50%;'
         msg = 'recipe parse progress bar should update to 50%'
@@ -107,7 +109,7 @@  class TestMostRecentBuildsStates(SeleniumTestCase):
         element = self.wait_until_visible(selector)
 
         bar_selector = '#build-pc-done-bar-%s' % build.id
-        bar_element = element.find_element_by_css_selector(bar_selector)
+        bar_element = element.find_element(By.CSS_SELECTOR, bar_selector)
 
         task_bar_updated = lambda driver: \
             bar_element.get_attribute('style') == 'width: 50%;'
@@ -121,7 +123,7 @@  class TestMostRecentBuildsStates(SeleniumTestCase):
         self.get(url)
 
         element = self.wait_until_visible(selector)
-        bar_element = element.find_element_by_css_selector(bar_selector)
+        bar_element = element.find_element(By.CSS_SELECTOR, bar_selector)
         task_bar_updated = lambda driver: \
             bar_element.get_attribute('style') == 'width: 100%;'
         msg = 'tasks progress bar should update to 100%'
diff --git a/lib/toaster/tests/browser/test_new_custom_image_page.py b/lib/toaster/tests/browser/test_new_custom_image_page.py
index 9906ae42..6361f403 100644
--- a/lib/toaster/tests/browser/test_new_custom_image_page.py
+++ b/lib/toaster/tests/browser/test_new_custom_image_page.py
@@ -6,6 +6,7 @@ 
 #
 # SPDX-License-Identifier: GPL-2.0-only
 #
+from bldcontrol.models import BuildEnvironment
 
 from django.urls import reverse
 from tests.browser.selenium_helpers import SeleniumTestCase
@@ -18,6 +19,9 @@  class TestNewCustomImagePage(SeleniumTestCase):
     CUSTOM_IMAGE_NAME = 'roopa-doopa'
 
     def setUp(self):
+        BuildEnvironment.objects.get_or_create(
+            betype=BuildEnvironment.TYPE_LOCAL,
+        )
         release = Release.objects.create(
             name='baz',
             bitbake_version=BitbakeVersion.objects.create(name='v1')
diff --git a/lib/toaster/tests/browser/test_new_project_page.py b/lib/toaster/tests/browser/test_new_project_page.py
index e20a1f68..f4b2708f 100644
--- a/lib/toaster/tests/browser/test_new_project_page.py
+++ b/lib/toaster/tests/browser/test_new_project_page.py
@@ -6,11 +6,13 @@ 
 #
 # SPDX-License-Identifier: GPL-2.0-only
 #
+import time
 
 from django.urls import reverse
 from tests.browser.selenium_helpers import SeleniumTestCase
 from selenium.webdriver.support.ui import Select
 from selenium.common.exceptions import InvalidElementStateException
+from selenium.webdriver.common.by import By
 
 from orm.models import Project, Release, BitbakeVersion
 
@@ -47,13 +49,14 @@  class TestNewProjectPage(SeleniumTestCase):
 
         url = reverse('newproject')
         self.get(url)
-
         self.enter_text('#new-project-name', project_name)
 
         select = Select(self.find('#projectversion'))
         select.select_by_value(str(self.release.pk))
 
+        time.sleep(1)
         self.click("#create-project-button")
+        time.sleep(2)
 
         # We should get redirected to the new project's page with the
         # notification at the top
@@ -84,6 +87,12 @@  class TestNewProjectPage(SeleniumTestCase):
         select = Select(self.find('#projectversion'))
         select.select_by_value(str(self.release.pk))
 
+        radio = self.driver.find_element(By.ID, 'type-new')
+        radio.click()
+
+        self.click("#create-project-button")
+        time.sleep(2)
+
         element = self.wait_until_visible('#hint-error-project-name')
 
         self.assertTrue(("Project names must be unique" in element.text),
@@ -96,6 +105,7 @@  class TestNewProjectPage(SeleniumTestCase):
         except InvalidElementStateException:
             pass
 
+        time.sleep(2)
         self.assertTrue(
             (Project.objects.filter(name=project_name).count() == 1),
             "New project not found in database")
diff --git a/lib/toaster/tests/browser/test_project_config_page.py b/lib/toaster/tests/browser/test_project_config_page.py
index 944bcb26..7b21460e 100644
--- a/lib/toaster/tests/browser/test_project_config_page.py
+++ b/lib/toaster/tests/browser/test_project_config_page.py
@@ -11,6 +11,7 @@  from django.urls import reverse
 from tests.browser.selenium_helpers import SeleniumTestCase
 
 from orm.models import BitbakeVersion, Release, Project, ProjectVariable
+from selenium.webdriver.common.by import By
 
 class TestProjectConfigsPage(SeleniumTestCase):
     """ Test data at /project/X/builds is displayed correctly """
@@ -66,7 +67,7 @@  class TestProjectConfigsPage(SeleniumTestCase):
 
         self.enter_text('#new-imagefs_types', imagefs_type)
 
-        checkboxes = self.driver.find_elements_by_xpath("//input[@class='fs-checkbox-fstypes']")
+        checkboxes = self.driver.find_elements(By.XPATH, "//input[@class='fs-checkbox-fstypes']")
 
         for checkbox in checkboxes:
             if checkbox.get_attribute("value") == "btrfs":
@@ -95,7 +96,7 @@  class TestProjectConfigsPage(SeleniumTestCase):
         for checkbox in checkboxes:
             if checkbox.get_attribute("value") == "cpio":
                checkbox.click()
-               element = self.driver.find_element_by_id('new-imagefs_types')
+               element = self.driver.find_element(By.ID, 'new-imagefs_types')
 
                self.wait_until_visible('#new-imagefs_types')
 
@@ -129,7 +130,7 @@  class TestProjectConfigsPage(SeleniumTestCase):
         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.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')
@@ -137,7 +138,7 @@  class TestProjectConfigsPage(SeleniumTestCase):
         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.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')
@@ -145,18 +146,18 @@  class TestProjectConfigsPage(SeleniumTestCase):
         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.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')
+        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.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')
+        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')
 
@@ -184,7 +185,7 @@  class TestProjectConfigsPage(SeleniumTestCase):
         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.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')
@@ -192,7 +193,7 @@  class TestProjectConfigsPage(SeleniumTestCase):
         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.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')
@@ -200,18 +201,18 @@  class TestProjectConfigsPage(SeleniumTestCase):
         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.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')
+        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.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')
+        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')
 
diff --git a/lib/toaster/tests/browser/test_toastertable_ui.py b/lib/toaster/tests/browser/test_toastertable_ui.py
index e82d5ec6..e00c30a8 100644
--- a/lib/toaster/tests/browser/test_toastertable_ui.py
+++ b/lib/toaster/tests/browser/test_toastertable_ui.py
@@ -13,6 +13,7 @@  from django.urls import reverse
 from django.utils import timezone
 from tests.browser.selenium_helpers import SeleniumTestCase
 from orm.models import BitbakeVersion, Release, Project, Build
+from selenium.webdriver.common.by import By
 
 class TestToasterTableUI(SeleniumTestCase):
     """
@@ -33,7 +34,7 @@  class TestToasterTableUI(SeleniumTestCase):
         table: WebElement for a ToasterTable
         """
         selector = 'thead a.sorted'
-        heading = table.find_element_by_css_selector(selector)
+        heading = table.find_element(By.CSS_SELECTOR, selector)
         return heading.get_attribute('innerHTML').strip()
 
     def _get_datetime_from_cell(self, row, selector):
@@ -45,7 +46,7 @@  class TestToasterTableUI(SeleniumTestCase):
         selector: CSS selector to use to find the cell containing the date time
         string
         """
-        cell = row.find_element_by_css_selector(selector)
+        cell = row.find_element(By.CSS_SELECTOR, selector)
         cell_text = cell.get_attribute('innerHTML').strip()
         return datetime.strptime(cell_text, '%d/%m/%y %H:%M')
 
@@ -105,7 +106,7 @@  class TestToasterTableUI(SeleniumTestCase):
         self.click('#checkbox-started_on')
 
         # sort by started_on column
-        links = table.find_elements_by_css_selector('th.started_on a')
+        links = table.find_elements(By.CSS_SELECTOR, 'th.started_on a')
         for link in links:
             if link.get_attribute('innerHTML').strip() == 'Started on':
                 link.click()
diff --git a/lib/toaster/tests/builds/test_core_image_min.py b/lib/toaster/tests/builds/test_core_image_min.py
index 44b6cbec..9cdaa15f 100644
--- a/lib/toaster/tests/builds/test_core_image_min.py
+++ b/lib/toaster/tests/builds/test_core_image_min.py
@@ -26,6 +26,7 @@  class BuildCoreImageMinimal(BuildTest):
 
     def setUp(self):
         self.completed_build = self.build("core-image-minimal")
+        self.built = self.target_already_built("core-image-minimal")
 
     # Check if build name is unique - tc_id=795
     def test_Build_Unique_Name(self):
diff --git a/lib/toaster/tests/functional/functional_helpers.py b/lib/toaster/tests/functional/functional_helpers.py
index 5c4ea717..c3191f66 100644
--- a/lib/toaster/tests/functional/functional_helpers.py
+++ b/lib/toaster/tests/functional/functional_helpers.py
@@ -16,6 +16,9 @@  import re
 
 from tests.browser.selenium_helpers_base import SeleniumTestCaseBase
 from tests.builds.buildtest import load_build_environment
+from bldcontrol.models import BuildEnvironment
+from selenium.webdriver.common.by import By
+from selenium.common.exceptions import NoSuchElementException
 
 logger = logging.getLogger("toaster")
 
@@ -30,6 +33,8 @@  class SeleniumFunctionalTestCase(SeleniumTestCaseBase):
             raise RuntimeError("Please initialise django with the tests settings:  " \
                 "DJANGO_SETTINGS_MODULE='toastermain.settings_test'")
 
+        if BuildEnvironment.objects.count() == 0:
+            BuildEnvironment.objects.create(betype=BuildEnvironment.TYPE_LOCAL)
         load_build_environment()
 
         # start toaster
@@ -74,8 +79,8 @@  class SeleniumFunctionalTestCase(SeleniumTestCaseBase):
         """
         try:
             table_element = self.get_table_element(table_id)
-            element = table_element.find_element_by_link_text(link_text)
-        except self.NoSuchElementException:
+            element = table_element.find_element(By.LINK_TEXT, link_text)
+        except NoSuchElementException:
             print('no element found')
             raise
         return element
@@ -85,8 +90,8 @@  class SeleniumFunctionalTestCase(SeleniumTestCaseBase):
 #return whole-table element
             element_xpath = "//*[@id='" + table_id + "']"
             try:
-                element = self.driver.find_element_by_xpath(element_xpath)
-            except self.NoSuchElementException:
+                element = self.driver.find_element(By.XPATH, element_xpath)
+            except NoSuchElementException:
                 raise
             return element
         row = coordinate[0]
@@ -95,8 +100,8 @@  class SeleniumFunctionalTestCase(SeleniumTestCaseBase):
 #return whole-row element
             element_xpath = "//*[@id='" + table_id + "']/tbody/tr[" + str(row) + "]"
             try:
-                element = self.driver.find_element_by_xpath(element_xpath)
-            except self.NoSuchElementException:
+                element = self.driver.find_element(By.XPATH, element_xpath)
+            except NoSuchElementException:
                 return False
             return element
 #now we are looking for an element with specified X and Y
@@ -104,7 +109,7 @@  class SeleniumFunctionalTestCase(SeleniumTestCaseBase):
 
         element_xpath = "//*[@id='" + table_id + "']/tbody/tr[" + str(row) + "]/td[" + str(column) + "]"
         try:
-            element = self.driver.find_element_by_xpath(element_xpath)
-        except self.NoSuchElementException:
+            element = self.driver.find_element(By.XPATH, element_xpath)
+        except NoSuchElementException:
             return False
         return element
diff --git a/lib/toaster/tests/functional/test_functional_basic.py b/lib/toaster/tests/functional/test_functional_basic.py
index 5683e387..067ad99a 100644
--- a/lib/toaster/tests/functional/test_functional_basic.py
+++ b/lib/toaster/tests/functional/test_functional_basic.py
@@ -10,6 +10,7 @@ 
 import re
 from tests.functional.functional_helpers import SeleniumFunctionalTestCase
 from orm.models import Project
+from selenium.webdriver.common.by import By
 
 class FuntionalTestBasic(SeleniumFunctionalTestCase):
 
@@ -17,10 +18,10 @@  class FuntionalTestBasic(SeleniumFunctionalTestCase):
     def test_create_slenium_project(self):
         project_name = 'selenium-project'
         self.get('')
-        self.driver.find_element_by_link_text("To start building, create your first Toaster project").click()
-        self.driver.find_element_by_id("new-project-name").send_keys(project_name)
-        self.driver.find_element_by_id('projectversion').click()
-        self.driver.find_element_by_id("create-project-button").click()
+        self.driver.find_element(By.LINK_TEXT, "To start building, create your first Toaster project").click()
+        self.driver.find_element(By.ID, "new-project-name").send_keys(project_name)
+        self.driver.find_element(By.ID, 'projectversion').click()
+        self.driver.find_element(By.ID, "create-project-button").click()
         element = self.wait_until_visible('#project-created-notification')
         self.assertTrue(self.element_exists('#project-created-notification'),'Project creation notification not shown')
         self.assertTrue(project_name in element.text,
@@ -35,77 +36,77 @@  class FuntionalTestBasic(SeleniumFunctionalTestCase):
         self.find_element_by_link_text_in_table('projectstable', 'selenium-project').click()
         self.assertTrue(self.element_exists('#config-nav'),'Configuration Tab does not exist')
         project_URL=self.get_URL()
-        self.driver.find_element_by_xpath('//a[@href="'+project_URL+'"]').click()
+        self.driver.find_element(By.XPATH, '//a[@href="'+project_URL+'"]').click()
 
         try:
-            self.driver.find_element_by_xpath("//*[@id='config-nav']/ul/li/a[@href="+'"'+project_URL+'customimages/"'+"]").click()
-            self.assertTrue(re.search("Custom images",self.driver.find_element_by_xpath("//div[@class='col-md-10']").text),'Custom images information is not loading properly')
+            self.driver.find_element(By.XPATH, "//*[@id='config-nav']/ul/li/a[@href="+'"'+project_URL+'customimages/"'+"]").click()
+            self.assertTrue(re.search("Custom images",self.driver.find_element(By.XPATH, "//div[@class='col-md-10']").text),'Custom images information is not loading properly')
         except:
             self.fail(msg='No Custom images tab available')
 
         try:
-            self.driver.find_element_by_xpath("//*[@id='config-nav']/ul/li/a[@href="+'"'+project_URL+'images/"'+"]").click()
-            self.assertTrue(re.search("Compatible image recipes",self.driver.find_element_by_xpath("//div[@class='col-md-10']").text),'The Compatible image recipes information is not loading properly')
+            self.driver.find_element(By.XPATH, "//*[@id='config-nav']/ul/li/a[@href="+'"'+project_URL+'images/"'+"]").click()
+            self.assertTrue(re.search("Compatible image recipes",self.driver.find_element(By.XPATH, "//div[@class='col-md-10']").text),'The Compatible image recipes information is not loading properly')
         except:
             self.fail(msg='No Compatible image tab available')
 
         try:
-            self.driver.find_element_by_xpath("//*[@id='config-nav']/ul/li/a[@href="+'"'+project_URL+'softwarerecipes/"'+"]").click()
-            self.assertTrue(re.search("Compatible software recipes",self.driver.find_element_by_xpath("//div[@class='col-md-10']").text),'The Compatible software recipe information is not loading properly')
+            self.driver.find_element(By.XPATH, "//*[@id='config-nav']/ul/li/a[@href="+'"'+project_URL+'softwarerecipes/"'+"]").click()
+            self.assertTrue(re.search("Compatible software recipes",self.driver.find_element(By.XPATH, "//div[@class='col-md-10']").text),'The Compatible software recipe information is not loading properly')
         except:
             self.fail(msg='No Compatible software recipe tab available')
 
         try:
-            self.driver.find_element_by_xpath("//*[@id='config-nav']/ul/li/a[@href="+'"'+project_URL+'machines/"'+"]").click()
-            self.assertTrue(re.search("Compatible machines",self.driver.find_element_by_xpath("//div[@class='col-md-10']").text),'The Compatible machine information is not loading properly')
+            self.driver.find_element(By.XPATH, "//*[@id='config-nav']/ul/li/a[@href="+'"'+project_URL+'machines/"'+"]").click()
+            self.assertTrue(re.search("Compatible machines",self.driver.find_element(By.XPATH, "//div[@class='col-md-10']").text),'The Compatible machine information is not loading properly')
         except:
             self.fail(msg='No Compatible machines tab available')
 
         try:
-            self.driver.find_element_by_xpath("//*[@id='config-nav']/ul/li/a[@href="+'"'+project_URL+'layers/"'+"]").click()
-            self.assertTrue(re.search("Compatible layers",self.driver.find_element_by_xpath("//div[@class='col-md-10']").text),'The Compatible layer information is not loading properly')
+            self.driver.find_element(By.XPATH, "//*[@id='config-nav']/ul/li/a[@href="+'"'+project_URL+'layers/"'+"]").click()
+            self.assertTrue(re.search("Compatible layers",self.driver.find_element(By.XPATH, "//div[@class='col-md-10']").text),'The Compatible layer information is not loading properly')
         except:
             self.fail(msg='No Compatible layers tab available')
 
         try:
-            self.driver.find_element_by_xpath("//*[@id='config-nav']/ul/li/a[@href="+'"'+project_URL+'configuration"'+"]").click()
-            self.assertTrue(re.search("Bitbake variables",self.driver.find_element_by_xpath("//div[@class='col-md-10']").text),'The Bitbake variables information is not loading properly')
+            self.driver.find_element(By.XPATH, "//*[@id='config-nav']/ul/li/a[@href="+'"'+project_URL+'configuration"'+"]").click()
+            self.assertTrue(re.search("Bitbake variables",self.driver.find_element(By.XPATH, "//div[@class='col-md-10']").text),'The Bitbake variables information is not loading properly')
         except:
             self.fail(msg='No Bitbake variables tab available')
 
 #   testcase (1516)
     def test_review_configuration_information(self):
         self.get('')
-        self.driver.find_element_by_xpath("//div[@id='global-nav']/ul/li/a[@href="+'"'+'/toastergui/projects/'+'"'+"]").click()
+        self.driver.find_element(By.XPATH, "//div[@id='global-nav']/ul/li/a[@href="+'"'+'/toastergui/projects/'+'"'+"]").click()
         self.wait_until_visible('#projectstable')
         self.find_element_by_link_text_in_table('projectstable', 'selenium-project').click()
         project_URL=self.get_URL()
 
         try:
            self.assertTrue(self.element_exists('#machine-section'),'Machine section for the project configuration page does not exist')
-           self.assertTrue(re.search("qemux86",self.driver.find_element_by_xpath("//span[@id='project-machine-name']").text),'The machine type is not assigned')
-           self.driver.find_element_by_xpath("//span[@id='change-machine-toggle']").click()
+           self.assertTrue(re.search("qemux86",self.driver.find_element(By.XPATH, "//span[@id='project-machine-name']").text),'The machine type is not assigned')
+           self.driver.find_element(By.XPATH, "//span[@id='change-machine-toggle']").click()
            self.wait_until_visible('#select-machine-form')
            self.wait_until_visible('#cancel-machine-change')
-           self.driver.find_element_by_xpath("//form[@id='select-machine-form']/a[@id='cancel-machine-change']").click()
+           self.driver.find_element(By.XPATH, "//form[@id='select-machine-form']/a[@id='cancel-machine-change']").click()
         except:
            self.fail(msg='The machine information is wrong in the configuration page')
 
         try:
-           self.driver.find_element_by_id('no-most-built')
+           self.driver.find_element(By.ID, 'no-most-built')
         except:
            self.fail(msg='No Most built information in project detail page')
 
         try:
-           self.assertTrue(re.search("Yocto Project master",self.driver.find_element_by_xpath("//span[@id='project-release-title']").text),'The project release is not defined')
+           self.assertTrue(re.search("Yocto Project master",self.driver.find_element(By.XPATH, "//span[@id='project-release-title']").text),'The project release is not defined')
         except:
            self.fail(msg='No project release title information in project detail page')
 
         try:
-           self.driver.find_element_by_xpath("//div[@id='layer-container']")
-           self.assertTrue(re.search("3",self.driver.find_element_by_id("project-layers-count").text),'There should be 3 layers listed in the layer count')
-           layer_list = self.driver.find_element_by_id("layers-in-project-list")
-           layers = layer_list.find_elements_by_tag_name("li")
+           self.driver.find_element(By.XPATH, "//div[@id='layer-container']")
+           self.assertTrue(re.search("3",self.driver.find_element(By.ID, "project-layers-count").text),'There should be 3 layers listed in the layer count')
+           layer_list = self.driver.find_element(By.ID, "layers-in-project-list")
+           layers = layer_list.find_elements(By.TAG_NAME, "li")
            for layer in layers:
                if re.match ("openembedded-core",layer.text):
                     print ("openembedded-core layer is a default layer in the project configuration")
@@ -121,60 +122,61 @@  class FuntionalTestBasic(SeleniumFunctionalTestCase):
 #   testcase (1517)
     def test_verify_machine_information(self):
         self.get('')
-        self.driver.find_element_by_xpath("//div[@id='global-nav']/ul/li/a[@href="+'"'+'/toastergui/projects/'+'"'+"]").click()
+        self.driver.find_element(By.XPATH, "//div[@id='global-nav']/ul/li/a[@href="+'"'+'/toastergui/projects/'+'"'+"]").click()
         self.wait_until_visible('#projectstable')
         self.find_element_by_link_text_in_table('projectstable', 'selenium-project').click()
 
         try:
             self.assertTrue(self.element_exists('#machine-section'),'Machine section for the project configuration page does not exist')
-            self.assertTrue(re.search("qemux86",self.driver.find_element_by_id("project-machine-name").text),'The machine type is not assigned')
-            self.driver.find_element_by_id("change-machine-toggle").click()
+            self.assertTrue(re.search("qemux86",self.driver.find_element(By.ID, "project-machine-name").text),'The machine type is not assigned')
+            self.driver.find_element(By.ID, "change-machine-toggle").click()
             self.wait_until_visible('#select-machine-form')
             self.wait_until_visible('#cancel-machine-change')
-            self.driver.find_element_by_id("cancel-machine-change").click()
+            self.driver.find_element(By.ID, "cancel-machine-change").click()
         except:
             self.fail(msg='The machine information is wrong in the configuration page')
 
 #   testcase (1518)
     def test_verify_most_built_recipes_information(self):
         self.get('')
-        self.driver.find_element_by_xpath("//div[@id='global-nav']/ul/li/a[@href="+'"'+'/toastergui/projects/'+'"'+"]").click()
+        self.driver.find_element(By.XPATH, "//div[@id='global-nav']/ul/li/a[@href="+'"'+'/toastergui/projects/'+'"'+"]").click()
+
         self.wait_until_visible('#projectstable')
         self.find_element_by_link_text_in_table('projectstable', 'selenium-project').click()
         project_URL=self.get_URL()
 
         try:
-            self.assertTrue(re.search("You haven't built any recipes yet",self.driver.find_element_by_id("no-most-built").text),'Default message of no builds is not present')
-            self.driver.find_element_by_xpath("//div[@id='no-most-built']/p/a[@href="+'"'+project_URL+'images/"'+"]").click()
-            self.assertTrue(re.search("Compatible image recipes",self.driver.find_element_by_xpath("//div[@class='col-md-10']").text),'The Choose a recipe to build link  is not working  properly')
+            self.assertTrue(re.search("You haven't built any recipes yet",self.driver.find_element(By.ID, "no-most-built").text),'Default message of no builds is not present')
+            self.driver.find_element(By.XPATH, "//div[@id='no-most-built']/p/a[@href="+'"'+project_URL+'images/"'+"]").click()
+            self.assertTrue(re.search("Compatible image recipes",self.driver.find_element(By.XPATH, "//div[@class='col-md-10']").text),'The Choose a recipe to build link  is not working  properly')
         except:
             self.fail(msg='No Most built information in project detail page')
 
 #   testcase (1519)
     def test_verify_project_release_information(self):
         self.get('')
-        self.driver.find_element_by_xpath("//div[@id='global-nav']/ul/li/a[@href="+'"'+'/toastergui/projects/'+'"'+"]").click()
+        self.driver.find_element(By.XPATH, "//div[@id='global-nav']/ul/li/a[@href="+'"'+'/toastergui/projects/'+'"'+"]").click()
         self.wait_until_visible('#projectstable')
         self.find_element_by_link_text_in_table('projectstable', 'selenium-project').click()
 
         try:
-            self.assertTrue(re.search("Yocto Project master",self.driver.find_element_by_id("project-release-title").text),'The project release is not defined')
+            self.assertTrue(re.search("Yocto Project master",self.driver.find_element(By.ID, "project-release-title").text),'The project release is not defined')
         except:
             self.fail(msg='No project release title information in project detail page')
 
 #   testcase (1520)
     def test_verify_layer_information(self):
         self.get('')
-        self.driver.find_element_by_xpath("//div[@id='global-nav']/ul/li/a[@href="+'"'+'/toastergui/projects/'+'"'+"]").click()
+        self.driver.find_element(By.XPATH, "//div[@id='global-nav']/ul/li/a[@href="+'"'+'/toastergui/projects/'+'"'+"]").click()
         self.wait_until_visible('#projectstable')
         self.find_element_by_link_text_in_table('projectstable', 'selenium-project').click()
         project_URL=self.get_URL()
 
         try:
-           self.driver.find_element_by_xpath("//div[@id='layer-container']")
-           self.assertTrue(re.search("3",self.driver.find_element_by_id("project-layers-count").text),'There should be 3 layers listed in the layer count')
-           layer_list = self.driver.find_element_by_id("layers-in-project-list")
-           layers = layer_list.find_elements_by_tag_name("li")
+           self.driver.find_element(By.XPATH, "//div[@id='layer-container']")
+           self.assertTrue(re.search("3",self.driver.find_element(By.ID, "project-layers-count").text),'There should be 3 layers listed in the layer count')
+           layer_list = self.driver.find_element(By.ID, "layers-in-project-list")
+           layers = layer_list.find_element(By.TAG_NAME, "li")
 
            for layer in layers:
                if re.match ("openembedded-core",layer.text):
@@ -186,43 +188,43 @@  class FuntionalTestBasic(SeleniumFunctionalTestCase):
                else:
                   self.fail(msg='default layers are missing from the project configuration')
 
-           self.driver.find_element_by_xpath("//input[@id='layer-add-input']")
-           self.driver.find_element_by_xpath("//button[@id='add-layer-btn']")
-           self.driver.find_element_by_xpath("//div[@id='layer-container']/form[@class='form-inline']/p/a[@id='view-compatible-layers']")
-           self.driver.find_element_by_xpath("//div[@id='layer-container']/form[@class='form-inline']/p/a[@href="+'"'+project_URL+'importlayer"'+"]")
+           self.driver.find_element(By.XPATH, "//input[@id='layer-add-input']")
+           self.driver.find_element(By.XPATH, "//button[@id='add-layer-btn']")
+           self.driver.find_element(By.XPATH, "//div[@id='layer-container']/form[@class='form-inline']/p/a[@id='view-compatible-layers']")
+           self.driver.find_element(By.XPATH, "//div[@id='layer-container']/form[@class='form-inline']/p/a[@href="+'"'+project_URL+'importlayer"'+"]")
         except:
             self.fail(msg='No Layer information in project detail page')
 
 #   testcase (1521)
     def test_verify_project_detail_links(self):
         self.get('')
-        self.driver.find_element_by_xpath("//div[@id='global-nav']/ul/li/a[@href="+'"'+'/toastergui/projects/'+'"'+"]").click()
+        self.driver.find_element(By.XPATH, "//div[@id='global-nav']/ul/li/a[@href="+'"'+'/toastergui/projects/'+'"'+"]").click()
         self.wait_until_visible('#projectstable')
         self.find_element_by_link_text_in_table('projectstable', 'selenium-project').click()
         project_URL=self.get_URL()
 
-        self.driver.find_element_by_xpath("//div[@id='project-topbar']/ul[@class='nav nav-tabs']/li[@id='topbar-configuration-tab']/a[@href="+'"'+project_URL+'"'+"]").click()
-        self.assertTrue(re.search("Configuration",self.driver.find_element_by_xpath("//div[@id='project-topbar']/ul[@class='nav nav-tabs']/li[@id='topbar-configuration-tab']/a[@href="+'"'+project_URL+'"'+"]").text), 'Configuration tab in project topbar is misspelled')
+        self.driver.find_element(By.XPATH, "//div[@id='project-topbar']/ul[@class='nav nav-tabs']/li[@id='topbar-configuration-tab']/a[@href="+'"'+project_URL+'"'+"]").click()
+        self.assertTrue(re.search("Configuration",self.driver.find_element(By.XPATH, "//div[@id='project-topbar']/ul[@class='nav nav-tabs']/li[@id='topbar-configuration-tab']/a[@href="+'"'+project_URL+'"'+"]").text), 'Configuration tab in project topbar is misspelled')
 
         try:
-            self.driver.find_element_by_xpath("//div[@id='project-topbar']/ul[@class='nav nav-tabs']/li/a[@href="+'"'+project_URL+'builds/"'+"]").click()
-            self.assertTrue(re.search("Builds",self.driver.find_element_by_xpath("//div[@id='project-topbar']/ul[@class='nav nav-tabs']/li/a[@href="+'"'+project_URL+'builds/"'+"]").text), 'Builds tab in project topbar is misspelled')
-            self.driver.find_element_by_xpath("//div[@id='empty-state-projectbuildstable']")
+            self.driver.find_element(By.XPATH, "//div[@id='project-topbar']/ul[@class='nav nav-tabs']/li/a[@href="+'"'+project_URL+'builds/"'+"]").click()
+            self.assertTrue(re.search("Builds",self.driver.find_element(By.XPATH, "//div[@id='project-topbar']/ul[@class='nav nav-tabs']/li/a[@href="+'"'+project_URL+'builds/"'+"]").text), 'Builds tab in project topbar is misspelled')
+            self.driver.find_element(By.XPATH, "//div[@id='empty-state-projectbuildstable']")
         except:
             self.fail(msg='Builds tab information is not present')
 
         try:
-            self.driver.find_element_by_xpath("//div[@id='project-topbar']/ul[@class='nav nav-tabs']/li/a[@href="+'"'+project_URL+'importlayer"'+"]").click()
-            self.assertTrue(re.search("Import layer",self.driver.find_element_by_xpath("//div[@id='project-topbar']/ul[@class='nav nav-tabs']/li/a[@href="+'"'+project_URL+'importlayer"'+"]").text), 'Import layer tab in project topbar is misspelled')
-            self.driver.find_element_by_xpath("//fieldset[@id='repo-select']")
-            self.driver.find_element_by_xpath("//fieldset[@id='git-repo']")
+            self.driver.find_element(By.XPATH, "//div[@id='project-topbar']/ul[@class='nav nav-tabs']/li/a[@href="+'"'+project_URL+'importlayer"'+"]").click()
+            self.assertTrue(re.search("Import layer",self.driver.find_element(By.XPATH, "//div[@id='project-topbar']/ul[@class='nav nav-tabs']/li/a[@href="+'"'+project_URL+'importlayer"'+"]").text), 'Import layer tab in project topbar is misspelled')
+            self.driver.find_element(By.XPATH, "//fieldset[@id='repo-select']")
+            self.driver.find_element(By.XPATH, "//fieldset[@id='git-repo']")
         except:
             self.fail(msg='Import layer tab not loading properly')
 
         try:
-            self.driver.find_element_by_xpath("//div[@id='project-topbar']/ul[@class='nav nav-tabs']/li/a[@href="+'"'+project_URL+'newcustomimage/"'+"]").click()
-            self.assertTrue(re.search("New custom image",self.driver.find_element_by_xpath("//div[@id='project-topbar']/ul[@class='nav nav-tabs']/li/a[@href="+'"'+project_URL+'newcustomimage/"'+"]").text), 'New custom image tab in project topbar is misspelled')
-            self.assertTrue(re.search("Select the image recipe you want to customise",self.driver.find_element_by_xpath("//div[@class='col-md-12']/h2").text),'The new custom image tab is not loading correctly')
+            self.driver.find_element(By.XPATH, "//div[@id='project-topbar']/ul[@class='nav nav-tabs']/li/a[@href="+'"'+project_URL+'newcustomimage/"'+"]").click()
+            self.assertTrue(re.search("New custom image",self.driver.find_element(By.XPATH, "//div[@id='project-topbar']/ul[@class='nav nav-tabs']/li/a[@href="+'"'+project_URL+'newcustomimage/"'+"]").text), 'New custom image tab in project topbar is misspelled')
+            self.assertTrue(re.search("Select the image recipe you want to customise",self.driver.find_element(By.XPATH, "//div[@class='col-md-12']/h2").text),'The new custom image tab is not loading correctly')
         except:
             self.fail(msg='New custom image tab not loading properly')
 
diff --git a/lib/toaster/tests/toaster-tests-requirements.txt b/lib/toaster/tests/toaster-tests-requirements.txt
index 4f9fcc46..f30ac070 100644
--- a/lib/toaster/tests/toaster-tests-requirements.txt
+++ b/lib/toaster/tests/toaster-tests-requirements.txt
@@ -1 +1 @@ 
-selenium==2.49.2
+selenium>=4.13.0
diff --git a/lib/toaster/tests/views/test_views.py b/lib/toaster/tests/views/test_views.py
index 735d596b..f962e762 100644
--- a/lib/toaster/tests/views/test_views.py
+++ b/lib/toaster/tests/views/test_views.py
@@ -19,6 +19,7 @@  from orm.models import Layer_Version, Recipe
 from orm.models import CustomImageRecipe
 from orm.models import CustomImagePackage
 
+from bldcontrol.models import BuildEnvironment
 import inspect
 import toastergui
 
@@ -45,6 +46,9 @@  class ViewTests(TestCase):
         self.cust_package = CustomImagePackage.objects.first()
         self.package = Package.objects.first()
         self.lver = Layer_Version.objects.first()
+        if BuildEnvironment.objects.count() == 0:
+            BuildEnvironment.objects.create(betype=BuildEnvironment.TYPE_LOCAL)
+
 
     def test_get_base_call_returns_html(self):
         """Basic test for all-projects view"""
diff --git a/lib/toaster/toastergui/widgets.py b/lib/toaster/toastergui/widgets.py
index 53696912..6e87285c 100644
--- a/lib/toaster/toastergui/widgets.py
+++ b/lib/toaster/toastergui/widgets.py
@@ -305,6 +305,7 @@  class ToasterTable(TemplateView):
 
         self.setup_columns(**kwargs)
 
+        self.apply_orderby('pk')
         if search:
             self.apply_search(search)
         if filters: