summaryrefslogtreecommitdiffstats
path: root/bitbake/lib/toaster/tests/functional
diff options
context:
space:
mode:
Diffstat (limited to 'bitbake/lib/toaster/tests/functional')
-rw-r--r--bitbake/lib/toaster/tests/functional/functional_helpers.py86
-rw-r--r--bitbake/lib/toaster/tests/functional/test_create_new_project.py91
-rw-r--r--bitbake/lib/toaster/tests/functional/test_functional_basic.py198
-rw-r--r--bitbake/lib/toaster/tests/functional/test_project_config.py97
-rw-r--r--bitbake/lib/toaster/tests/functional/test_project_page.py415
-rw-r--r--bitbake/lib/toaster/tests/functional/test_project_page_tab_config.py311
-rw-r--r--bitbake/lib/toaster/tests/functional/utils.py21
7 files changed, 581 insertions, 638 deletions
diff --git a/bitbake/lib/toaster/tests/functional/functional_helpers.py b/bitbake/lib/toaster/tests/functional/functional_helpers.py
index 7c20437d14..e28f2024f5 100644
--- a/bitbake/lib/toaster/tests/functional/functional_helpers.py
+++ b/bitbake/lib/toaster/tests/functional/functional_helpers.py
@@ -12,9 +12,12 @@ import logging
12import subprocess 12import subprocess
13import signal 13import signal
14import re 14import re
15import requests
15 16
17from django.urls import reverse
16from tests.browser.selenium_helpers_base import SeleniumTestCaseBase 18from tests.browser.selenium_helpers_base import SeleniumTestCaseBase
17from selenium.webdriver.common.by import By 19from selenium.webdriver.common.by import By
20from selenium.webdriver.support.select import Select
18from selenium.common.exceptions import NoSuchElementException 21from selenium.common.exceptions import NoSuchElementException
19 22
20logger = logging.getLogger("toaster") 23logger = logging.getLogger("toaster")
@@ -136,3 +139,86 @@ class SeleniumFunctionalTestCase(SeleniumTestCaseBase):
136 except NoSuchElementException: 139 except NoSuchElementException:
137 return False 140 return False
138 return element 141 return element
142
143 def create_new_project(
144 self,
145 project_name,
146 release,
147 release_title,
148 merge_toaster_settings,
149 ):
150 """ Create/Test new project using:
151 - Project Name: Any string
152 - Release: Any string
153 - Merge Toaster settings: True or False
154 """
155
156 # Obtain a CSRF token from a suitable URL
157 projs = requests.get(self.live_server_url + reverse('newproject'))
158 csrftoken = projs.cookies.get('csrftoken')
159
160 # Use the projects typeahead to find out if the project already exists
161 req = requests.get(self.live_server_url + reverse('xhr_projectstypeahead'), {'search': project_name, 'format' : 'json'})
162 data = req.json()
163 # Delete any existing projects
164 for result in data['results']:
165 del_url = reverse('xhr_project', args=(result['id'],))
166 del_response = requests.delete(self.live_server_url + del_url, cookies={'csrftoken': csrftoken}, headers={'X-CSRFToken': csrftoken})
167 self.assertEqual(del_response.status_code, 200)
168
169 self.get(reverse('newproject'))
170 self.wait_until_visible('#new-project-name')
171 self.driver.find_element(By.ID,
172 "new-project-name").send_keys(project_name)
173
174 select = Select(self.find('#projectversion'))
175 select.select_by_value(release)
176
177 # check merge toaster settings
178 checkbox = self.find('.checkbox-mergeattr')
179 if merge_toaster_settings:
180 if not checkbox.is_selected():
181 checkbox.click()
182 else:
183 if checkbox.is_selected():
184 checkbox.click()
185
186 self.wait_until_clickable('#create-project-button')
187
188 self.driver.find_element(By.ID, "create-project-button").click()
189
190 element = self.wait_until_visible('#project-created-notification')
191 self.assertTrue(
192 self.element_exists('#project-created-notification'),
193 f"Project:{project_name} creation notification not shown"
194 )
195 self.assertTrue(
196 project_name in element.text,
197 f"New project name:{project_name} not in new project notification"
198 )
199
200 # Use the projects typeahead again to check the project now exists
201 req = requests.get(self.live_server_url + reverse('xhr_projectstypeahead'), {'search': project_name, 'format' : 'json'})
202 data = req.json()
203 self.assertGreater(len(data['results']), 0, f"New project:{project_name} not found in database")
204
205 project_id = data['results'][0]['id']
206
207 self.wait_until_visible('#project-release-title')
208
209 # check release
210 if release_title is not None:
211 self.assertTrue(re.search(
212 release_title,
213 self.driver.find_element(By.XPATH,
214 "//span[@id='project-release-title']"
215 ).text),
216 'The project release is not defined')
217
218 return project_id
219
220 def load_projects_page_helper(self):
221 self.wait_until_present('#projectstable')
222 # Need to wait for some data in the table too
223 self.wait_until_present('td[class="updated"]')
224
diff --git a/bitbake/lib/toaster/tests/functional/test_create_new_project.py b/bitbake/lib/toaster/tests/functional/test_create_new_project.py
index 94d90459e1..66213c736e 100644
--- a/bitbake/lib/toaster/tests/functional/test_create_new_project.py
+++ b/bitbake/lib/toaster/tests/functional/test_create_new_project.py
@@ -11,67 +11,10 @@ import pytest
11from django.urls import reverse 11from django.urls import reverse
12from selenium.webdriver.support.select import Select 12from selenium.webdriver.support.select import Select
13from tests.functional.functional_helpers import SeleniumFunctionalTestCase 13from tests.functional.functional_helpers import SeleniumFunctionalTestCase
14from orm.models import Project
15from selenium.webdriver.common.by import By 14from selenium.webdriver.common.by import By
16 15
17
18@pytest.mark.django_db
19@pytest.mark.order("last")
20class TestCreateNewProject(SeleniumFunctionalTestCase): 16class TestCreateNewProject(SeleniumFunctionalTestCase):
21 17
22 def _create_test_new_project(
23 self,
24 project_name,
25 release,
26 release_title,
27 merge_toaster_settings,
28 ):
29 """ Create/Test new project using:
30 - Project Name: Any string
31 - Release: Any string
32 - Merge Toaster settings: True or False
33 """
34 self.get(reverse('newproject'))
35 self.wait_until_visible('#new-project-name', poll=3)
36 self.driver.find_element(By.ID,
37 "new-project-name").send_keys(project_name)
38
39 select = Select(self.find('#projectversion'))
40 select.select_by_value(release)
41
42 # check merge toaster settings
43 checkbox = self.find('.checkbox-mergeattr')
44 if merge_toaster_settings:
45 if not checkbox.is_selected():
46 checkbox.click()
47 else:
48 if checkbox.is_selected():
49 checkbox.click()
50
51 self.driver.find_element(By.ID, "create-project-button").click()
52
53 element = self.wait_until_visible('#project-created-notification', poll=3)
54 self.assertTrue(
55 self.element_exists('#project-created-notification'),
56 f"Project:{project_name} creation notification not shown"
57 )
58 self.assertTrue(
59 project_name in element.text,
60 f"New project name:{project_name} not in new project notification"
61 )
62 self.assertTrue(
63 Project.objects.filter(name=project_name).count(),
64 f"New project:{project_name} not found in database"
65 )
66
67 # check release
68 self.assertTrue(re.search(
69 release_title,
70 self.driver.find_element(By.XPATH,
71 "//span[@id='project-release-title']"
72 ).text),
73 'The project release is not defined')
74
75 def test_create_new_project_master(self): 18 def test_create_new_project_master(self):
76 """ Test create new project using: 19 """ Test create new project using:
77 - Project Name: Any string 20 - Project Name: Any string
@@ -81,43 +24,43 @@ class TestCreateNewProject(SeleniumFunctionalTestCase):
81 release = '3' 24 release = '3'
82 release_title = 'Yocto Project master' 25 release_title = 'Yocto Project master'
83 project_name = 'projectmaster' 26 project_name = 'projectmaster'
84 self._create_test_new_project( 27 self.create_new_project(
85 project_name, 28 project_name,
86 release, 29 release,
87 release_title, 30 release_title,
88 False, 31 False,
89 ) 32 )
90 33
91 def test_create_new_project_kirkstone(self): 34 def test_create_new_project_scarthgap(self):
92 """ Test create new project using: 35 """ Test create new project using:
93 - Project Name: Any string 36 - Project Name: Any string
94 - Release: Yocto Project 4.0 "Kirkstone" (option value: 1) 37 - Release: Yocto Project 5.0 "Scarthgap" (option value: 1)
95 - Merge Toaster settings: True 38 - Merge Toaster settings: True
96 """ 39 """
97 release = '1' 40 release = '1'
98 release_title = 'Yocto Project 4.0 "Kirkstone"' 41 release_title = 'Yocto Project 5.0 "Scarthgap"'
99 project_name = 'projectkirkstone' 42 project_name = 'projectscarthgap'
100 self._create_test_new_project( 43 self.create_new_project(
101 project_name, 44 project_name,
102 release, 45 release,
103 release_title, 46 release_title,
104 True, 47 True,
105 ) 48 )
106 49
107 def test_create_new_project_dunfell(self): 50 def test_create_new_project_kirkstone(self):
108 """ Test create new project using: 51 """ Test create new project using:
109 - Project Name: Any string 52 - Project Name: Any string
110 - Release: Yocto Project 3.1 "Dunfell" (option value: 5) 53 - Release: Yocto Project 4.0 "Kirkstone" (option value: 6)
111 - Merge Toaster settings: False 54 - Merge Toaster settings: True
112 """ 55 """
113 release = '5' 56 release = '7'
114 release_title = 'Yocto Project 3.1 "Dunfell"' 57 release_title = 'Yocto Project 4.0 "Kirkstone"'
115 project_name = 'projectdunfell' 58 project_name = 'projectkirkstone'
116 self._create_test_new_project( 59 self.create_new_project(
117 project_name, 60 project_name,
118 release, 61 release,
119 release_title, 62 release_title,
120 False, 63 True,
121 ) 64 )
122 65
123 def test_create_new_project_local(self): 66 def test_create_new_project_local(self):
@@ -129,7 +72,7 @@ class TestCreateNewProject(SeleniumFunctionalTestCase):
129 release = '2' 72 release = '2'
130 release_title = 'Local Yocto Project' 73 release_title = 'Local Yocto Project'
131 project_name = 'projectlocal' 74 project_name = 'projectlocal'
132 self._create_test_new_project( 75 self.create_new_project(
133 project_name, 76 project_name,
134 release, 77 release,
135 release_title, 78 release_title,
@@ -172,8 +115,10 @@ class TestCreateNewProject(SeleniumFunctionalTestCase):
172 "import-project-dir").send_keys(wrong_path) 115 "import-project-dir").send_keys(wrong_path)
173 self.driver.find_element(By.ID, "create-project-button").click() 116 self.driver.find_element(By.ID, "create-project-button").click()
174 117
118 self.wait_until_visible('.alert-danger')
119
175 # check error message 120 # check error message
176 self.assertTrue(self.element_exists('.alert-danger'), 121 self.assertTrue(self.element_exists('.alert-danger'),
177 'Allert message not shown') 122 'Alert message not shown')
178 self.assertTrue(wrong_path in self.find('.alert-danger').text, 123 self.assertTrue(wrong_path in self.find('.alert-danger').text,
179 "Wrong path not in alert message") 124 "Wrong path not in alert message")
diff --git a/bitbake/lib/toaster/tests/functional/test_functional_basic.py b/bitbake/lib/toaster/tests/functional/test_functional_basic.py
index e4070fbb88..d5c9708617 100644
--- a/bitbake/lib/toaster/tests/functional/test_functional_basic.py
+++ b/bitbake/lib/toaster/tests/functional/test_functional_basic.py
@@ -17,145 +17,132 @@ from selenium.webdriver.common.by import By
17from tests.functional.utils import get_projectId_from_url 17from tests.functional.utils import get_projectId_from_url
18 18
19 19
20@pytest.mark.django_db
21@pytest.mark.order("second_to_last")
22class FuntionalTestBasic(SeleniumFunctionalTestCase): 20class FuntionalTestBasic(SeleniumFunctionalTestCase):
23 """Basic functional tests for Toaster""" 21 """Basic functional tests for Toaster"""
24 project_id = None 22 project_id = None
23 project_url = None
25 24
26 def setUp(self): 25 def setUp(self):
27 super(FuntionalTestBasic, self).setUp() 26 super(FuntionalTestBasic, self).setUp()
28 if not FuntionalTestBasic.project_id: 27 if not FuntionalTestBasic.project_id:
29 self._create_slenium_project() 28 FuntionalTestBasic.project_id = self.create_new_project('selenium-project', '3', None, False)
30 current_url = self.driver.current_url
31 FuntionalTestBasic.project_id = get_projectId_from_url(current_url)
32
33# testcase (1514)
34 def _create_slenium_project(self):
35 project_name = 'selenium-project'
36 self.get(reverse('newproject'))
37 self.wait_until_visible('#new-project-name', poll=3)
38 self.driver.find_element(By.ID, "new-project-name").send_keys(project_name)
39 self.driver.find_element(By.ID, 'projectversion').click()
40 self.driver.find_element(By.ID, "create-project-button").click()
41 element = self.wait_until_visible('#project-created-notification', poll=10)
42 self.assertTrue(self.element_exists('#project-created-notification'),'Project creation notification not shown')
43 self.assertTrue(project_name in element.text,
44 "New project name not in new project notification")
45 self.assertTrue(Project.objects.filter(name=project_name).count(),
46 "New project not found in database")
47 return Project.objects.last().id
48 29
49 # testcase (1515) 30 # testcase (1515)
50 def test_verify_left_bar_menu(self): 31 def test_verify_left_bar_menu(self):
51 self.get(reverse('all-projects')) 32 self.get(reverse('all-projects'))
52 self.wait_until_present('#projectstable', poll=10) 33 self.load_projects_page_helper()
53 self.find_element_by_link_text_in_table('projectstable', 'selenium-project').click() 34 self.find_element_by_link_text_in_table('projectstable', 'selenium-project').click()
54 self.wait_until_present('#config-nav', poll=10) 35 self.wait_until_present('#config-nav')
55 self.assertTrue(self.element_exists('#config-nav'),'Configuration Tab does not exist') 36 self.assertTrue(self.element_exists('#config-nav'),'Configuration Tab does not exist')
56 project_URL=self.get_URL() 37 project_URL=self.get_URL()
57 self.driver.find_element(By.XPATH, '//a[@href="'+project_URL+'"]').click() 38 self.driver.find_element(By.XPATH, '//a[@href="'+project_URL+'"]').click()
58 self.wait_until_present('#config-nav', poll=10)
59 39
60 try: 40 try:
41 self.wait_until_present('#config-nav')
61 self.driver.find_element(By.XPATH, "//*[@id='config-nav']/ul/li/a[@href="+'"'+project_URL+'customimages/"'+"]").click() 42 self.driver.find_element(By.XPATH, "//*[@id='config-nav']/ul/li/a[@href="+'"'+project_URL+'customimages/"'+"]").click()
62 self.wait_until_present('#config-nav', poll=10) 43 self.wait_until_present('#filter-modal-customimagestable')
63 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')
64 except: 44 except:
65 self.fail(msg='No Custom images tab available') 45 self.fail(msg='No Custom images tab available')
46 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')
66 47
67 try: 48 try:
68 self.driver.find_element(By.XPATH, "//*[@id='config-nav']/ul/li/a[@href="+'"'+project_URL+'images/"'+"]").click() 49 self.driver.find_element(By.XPATH, "//*[@id='config-nav']/ul/li/a[@href="+'"'+project_URL+'images/"'+"]").click()
69 self.wait_until_present('#config-nav', poll=10) 50 self.wait_until_present('#filter-modal-imagerecipestable')
70 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')
71 except: 51 except:
72 self.fail(msg='No Compatible image tab available') 52 self.fail(msg='No Compatible image tab available')
53 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')
73 54
74 try: 55 try:
75 self.driver.find_element(By.XPATH, "//*[@id='config-nav']/ul/li/a[@href="+'"'+project_URL+'softwarerecipes/"'+"]").click() 56 self.driver.find_element(By.XPATH, "//*[@id='config-nav']/ul/li/a[@href="+'"'+project_URL+'softwarerecipes/"'+"]").click()
76 self.wait_until_present('#config-nav', poll=10) 57 self.wait_until_present('#filter-modal-softwarerecipestable')
77 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')
78 except: 58 except:
79 self.fail(msg='No Compatible software recipe tab available') 59 self.fail(msg='No Compatible software recipe tab available')
60 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')
80 61
81 try: 62 try:
82 self.driver.find_element(By.XPATH, "//*[@id='config-nav']/ul/li/a[@href="+'"'+project_URL+'machines/"'+"]").click() 63 self.driver.find_element(By.XPATH, "//*[@id='config-nav']/ul/li/a[@href="+'"'+project_URL+'machines/"'+"]").click()
83 self.wait_until_present('#config-nav', poll=10) 64 self.wait_until_present('#filter-modal-machinestable')
84 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')
85 except: 65 except:
86 self.fail(msg='No Compatible machines tab available') 66 self.fail(msg='No Compatible machines tab available')
67 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')
87 68
88 try: 69 try:
89 self.driver.find_element(By.XPATH, "//*[@id='config-nav']/ul/li/a[@href="+'"'+project_URL+'layers/"'+"]").click() 70 self.driver.find_element(By.XPATH, "//*[@id='config-nav']/ul/li/a[@href="+'"'+project_URL+'layers/"'+"]").click()
90 self.wait_until_present('#config-nav', poll=10) 71 self.wait_until_present('#filter-modal-layerstable')
91 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')
92 except: 72 except:
93 self.fail(msg='No Compatible layers tab available') 73 self.fail(msg='No Compatible layers tab available')
74 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')
94 75
95 try: 76 try:
96 self.driver.find_element(By.XPATH, "//*[@id='config-nav']/ul/li/a[@href="+'"'+project_URL+'configuration"'+"]").click() 77 self.driver.find_element(By.XPATH, "//*[@id='config-nav']/ul/li/a[@href="+'"'+project_URL+'configuration"'+"]").click()
97 self.wait_until_present('#config-nav', poll=10) 78 self.wait_until_present('#configvar-list')
98 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')
99 except: 79 except:
100 self.fail(msg='No Bitbake variables tab available') 80 self.fail(msg='No Bitbake variables tab available')
81 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')
101 82
102# testcase (1516) 83# testcase (1516)
103 def test_review_configuration_information(self): 84 def test_review_configuration_information(self):
104 self.get(reverse('all-projects')) 85 self.get(reverse('all-projects'))
105 self.wait_until_present('#projectstable', poll=10) 86 self.load_projects_page_helper()
106 self.find_element_by_link_text_in_table('projectstable', 'selenium-project').click() 87 self.find_element_by_link_text_in_table('projectstable', 'selenium-project').click()
107 project_URL=self.get_URL() 88 project_URL=self.get_URL()
108 self.wait_until_present('#config-nav', poll=10) 89
90 # Machine section of page
91 self.wait_until_visible('#machine-section')
92 self.assertTrue(self.element_exists('#machine-section'),'Machine section for the project configuration page does not exist')
93 self.assertTrue(re.search("qemux86-64",self.driver.find_element(By.XPATH, "//span[@id='project-machine-name']").text),'The machine type is not assigned')
109 try: 94 try:
110 self.assertTrue(self.element_exists('#machine-section'),'Machine section for the project configuration page does not exist')
111 self.assertTrue(re.search("qemux86-64",self.driver.find_element(By.XPATH, "//span[@id='project-machine-name']").text),'The machine type is not assigned')
112 self.driver.find_element(By.XPATH, "//span[@id='change-machine-toggle']").click() 95 self.driver.find_element(By.XPATH, "//span[@id='change-machine-toggle']").click()
113 self.wait_until_visible('#select-machine-form', poll=10) 96 self.wait_until_visible('#select-machine-form')
114 self.wait_until_visible('#cancel-machine-change', poll=10) 97 self.wait_until_visible('#cancel-machine-change')
115 self.driver.find_element(By.XPATH, "//form[@id='select-machine-form']/a[@id='cancel-machine-change']").click() 98 self.driver.find_element(By.XPATH, "//form[@id='select-machine-form']/a[@id='cancel-machine-change']").click()
116 except: 99 except:
117 self.fail(msg='The machine information is wrong in the configuration page') 100 self.fail(msg='The machine information is wrong in the configuration page')
118 101
102 # Most built recipes section
103 self.wait_until_visible('#no-most-built')
119 try: 104 try:
120 self.driver.find_element(By.ID, 'no-most-built') 105 self.driver.find_element(By.ID, 'no-most-built')
121 except: 106 except:
122 self.fail(msg='No Most built information in project detail page') 107 self.fail(msg='No Most built information in project detail page')
123 108
124 try: 109 # Project Release title
125 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') 110 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 in the project detail page')
126 except:
127 self.fail(msg='No project release title information in project detail page')
128 111
112 # List of layers in project
113 self.wait_until_visible('#layer-container')
114 self.driver.find_element(By.XPATH, "//div[@id='layer-container']")
115 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')
129 try: 116 try:
130 self.driver.find_element(By.XPATH, "//div[@id='layer-container']")
131 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')
132 layer_list = self.driver.find_element(By.ID, "layers-in-project-list") 117 layer_list = self.driver.find_element(By.ID, "layers-in-project-list")
133 layers = layer_list.find_elements(By.TAG_NAME, "li") 118 layers = layer_list.find_elements(By.TAG_NAME, "li")
134 for layer in layers:
135 if re.match ("openembedded-core",layer.text):
136 print ("openembedded-core layer is a default layer in the project configuration")
137 elif re.match ("meta-poky",layer.text):
138 print ("meta-poky layer is a default layer in the project configuration")
139 elif re.match ("meta-yocto-bsp",layer.text):
140 print ("meta-yocto-bsp is a default layer in the project configuratoin")
141 else:
142 self.fail(msg='default layers are missing from the project configuration')
143 except: 119 except:
144 self.fail(msg='No Layer information in project detail page') 120 self.fail(msg='No Layer information in project detail page')
145 121
122 for layer in layers:
123 if re.match ("openembedded-core", layer.text):
124 print ("openembedded-core layer is a default layer in the project configuration")
125 elif re.match ("meta-poky", layer.text):
126 print ("meta-poky layer is a default layer in the project configuration")
127 elif re.match ("meta-yocto-bsp", layer.text):
128 print ("meta-yocto-bsp is a default layer in the project configuratoin")
129 else:
130 self.fail(msg='default layers are missing from the project configuration')
131
146# testcase (1517) 132# testcase (1517)
147 def test_verify_machine_information(self): 133 def test_verify_machine_information(self):
148 self.get(reverse('all-projects')) 134 self.get(reverse('all-projects'))
149 self.wait_until_present('#projectstable', poll=10) 135 self.load_projects_page_helper()
150 self.find_element_by_link_text_in_table('projectstable', 'selenium-project').click() 136 self.find_element_by_link_text_in_table('projectstable', 'selenium-project').click()
151 self.wait_until_present('#config-nav', poll=10)
152 137
138 self.wait_until_visible('#machine-section')
139 self.assertTrue(self.element_exists('#machine-section'),'Machine section for the project configuration page does not exist')
140 self.wait_until_visible('#project-machine-name')
141 self.assertTrue(re.search("qemux86-64",self.driver.find_element(By.ID, "project-machine-name").text),'The machine type is not assigned')
153 try: 142 try:
154 self.assertTrue(self.element_exists('#machine-section'),'Machine section for the project configuration page does not exist')
155 self.assertTrue(re.search("qemux86-64",self.driver.find_element(By.ID, "project-machine-name").text),'The machine type is not assigned')
156 self.driver.find_element(By.ID, "change-machine-toggle").click() 143 self.driver.find_element(By.ID, "change-machine-toggle").click()
157 self.wait_until_visible('#select-machine-form', poll=10) 144 self.wait_until_visible('#select-machine-form')
158 self.wait_until_visible('#cancel-machine-change', poll=10) 145 self.wait_until_visible('#cancel-machine-change')
159 self.driver.find_element(By.ID, "cancel-machine-change").click() 146 self.driver.find_element(By.ID, "cancel-machine-change").click()
160 except: 147 except:
161 self.fail(msg='The machine information is wrong in the configuration page') 148 self.fail(msg='The machine information is wrong in the configuration page')
@@ -163,83 +150,95 @@ class FuntionalTestBasic(SeleniumFunctionalTestCase):
163# testcase (1518) 150# testcase (1518)
164 def test_verify_most_built_recipes_information(self): 151 def test_verify_most_built_recipes_information(self):
165 self.get(reverse('all-projects')) 152 self.get(reverse('all-projects'))
166 self.wait_until_present('#projectstable', poll=10) 153 self.load_projects_page_helper()
167 self.find_element_by_link_text_in_table('projectstable', 'selenium-project').click() 154 self.find_element_by_link_text_in_table('projectstable', 'selenium-project').click()
168 self.wait_until_present('#config-nav', poll=10) 155 self.wait_until_present('#config-nav')
169 project_URL=self.get_URL() 156 project_URL=self.get_URL()
157
158 self.wait_until_visible('#no-most-built')
159 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')
170 try: 160 try:
171 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')
172 self.driver.find_element(By.XPATH, "//div[@id='no-most-built']/p/a[@href="+'"'+project_URL+'images/"'+"]").click() 161 self.driver.find_element(By.XPATH, "//div[@id='no-most-built']/p/a[@href="+'"'+project_URL+'images/"'+"]").click()
173 self.wait_until_present('#config-nav', poll=10)
174 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')
175 except: 162 except:
176 self.fail(msg='No Most built information in project detail page') 163 self.fail(msg='No Most built information in project detail page')
164 self.wait_until_visible('#config-nav')
165 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')
177 166
178# testcase (1519) 167# testcase (1519)
179 def test_verify_project_release_information(self): 168 def test_verify_project_release_information(self):
180 self.get(reverse('all-projects')) 169 self.get(reverse('all-projects'))
181 self.wait_until_present('#projectstable', poll=10) 170 self.load_projects_page_helper()
182 self.find_element_by_link_text_in_table('projectstable', 'selenium-project').click() 171 self.find_element_by_link_text_in_table('projectstable', 'selenium-project').click()
183 self.wait_until_present('#config-nav', poll=10) 172 self.wait_until_visible('#project-release-title')
184 173 self.assertTrue(re.search("Yocto Project master",self.driver.find_element(By.ID, "project-release-title").text), 'No project release title information in project detail page')
185 try:
186 self.assertTrue(re.search("Yocto Project master",self.driver.find_element(By.ID, "project-release-title").text),'The project release is not defined')
187 except:
188 self.fail(msg='No project release title information in project detail page')
189 174
190# testcase (1520) 175# testcase (1520)
191 def test_verify_layer_information(self): 176 def test_verify_layer_information(self):
192 self.get(reverse('all-projects')) 177 self.get(reverse('all-projects'))
193 self.wait_until_present('#projectstable', poll=10) 178 self.load_projects_page_helper()
194 self.find_element_by_link_text_in_table('projectstable', 'selenium-project').click() 179 self.find_element_by_link_text_in_table('projectstable', 'selenium-project').click()
195 self.wait_until_present('#config-nav', poll=10) 180 self.wait_until_present('#config-nav')
196 project_URL=self.get_URL() 181 project_URL=self.get_URL()
182 self.wait_until_visible('#layer-container')
183 self.driver.find_element(By.XPATH, "//div[@id='layer-container']")
184 self.wait_until_visible('#project-layers-count')
185 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')
186
197 try: 187 try:
198 self.driver.find_element(By.XPATH, "//div[@id='layer-container']")
199 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')
200 layer_list = self.driver.find_element(By.ID, "layers-in-project-list") 188 layer_list = self.driver.find_element(By.ID, "layers-in-project-list")
201 layers = layer_list.find_elements(By.TAG_NAME, "li") 189 layers = layer_list.find_elements(By.TAG_NAME, "li")
190 except:
191 self.fail(msg='No Layer information in project detail page')
202 192
203 for layer in layers: 193 for layer in layers:
204 if re.match ("openembedded-core",layer.text): 194 if re.match ("openembedded-core",layer.text):
205 print ("openembedded-core layer is a default layer in the project configuration") 195 print ("openembedded-core layer is a default layer in the project configuration")
206 elif re.match ("meta-poky",layer.text): 196 elif re.match ("meta-poky",layer.text):
207 print ("meta-poky layer is a default layer in the project configuration") 197 print ("meta-poky layer is a default layer in the project configuration")
208 elif re.match ("meta-yocto-bsp",layer.text): 198 elif re.match ("meta-yocto-bsp",layer.text):
209 print ("meta-yocto-bsp is a default layer in the project configuratoin") 199 print ("meta-yocto-bsp is a default layer in the project configuratoin")
210 else: 200 else:
211 self.fail(msg='default layers are missing from the project configuration') 201 self.fail(msg='default layers are missing from the project configuration')
212 202
203 try:
213 self.driver.find_element(By.XPATH, "//input[@id='layer-add-input']") 204 self.driver.find_element(By.XPATH, "//input[@id='layer-add-input']")
214 self.driver.find_element(By.XPATH, "//button[@id='add-layer-btn']") 205 self.driver.find_element(By.XPATH, "//button[@id='add-layer-btn']")
215 self.driver.find_element(By.XPATH, "//div[@id='layer-container']/form[@class='form-inline']/p/a[@id='view-compatible-layers']") 206 self.driver.find_element(By.XPATH, "//div[@id='layer-container']/form[@class='form-inline']/p/a[@id='view-compatible-layers']")
216 self.driver.find_element(By.XPATH, "//div[@id='layer-container']/form[@class='form-inline']/p/a[@href="+'"'+project_URL+'importlayer"'+"]") 207 self.driver.find_element(By.XPATH, "//div[@id='layer-container']/form[@class='form-inline']/p/a[@href="+'"'+project_URL+'importlayer"'+"]")
217 except: 208 except:
218 self.fail(msg='No Layer information in project detail page') 209 self.fail(msg='Layer configuration controls missing')
219 210
220# testcase (1521) 211# testcase (1521)
221 def test_verify_project_detail_links(self): 212 def test_verify_project_detail_links(self):
222 self.get(reverse('all-projects')) 213 self.get(reverse('all-projects'))
223 self.wait_until_present('#projectstable', poll=10) 214 self.load_projects_page_helper()
224 self.find_element_by_link_text_in_table('projectstable', 'selenium-project').click() 215 self.find_element_by_link_text_in_table('projectstable', 'selenium-project').click()
225 self.wait_until_present('#config-nav', poll=10) 216 self.wait_until_present('#config-nav')
226 project_URL=self.get_URL() 217 project_URL=self.get_URL()
227 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() 218 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()
228 self.wait_until_present('#config-nav', poll=10) 219 self.wait_until_visible('#topbar-configuration-tab')
229 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') 220 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')
230 221
231 try: 222 try:
232 self.driver.find_element(By.XPATH, "//div[@id='project-topbar']/ul[@class='nav nav-tabs']/li/a[@href="+'"'+project_URL+'builds/"'+"]").click() 223 self.driver.find_element(By.XPATH, "//div[@id='project-topbar']/ul[@class='nav nav-tabs']/li/a[@href="+'"'+project_URL+'builds/"'+"]").click()
233 self.wait_until_visible('#project-topbar', poll=10) 224 except:
234 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') 225 self.fail(msg='Builds tab information is not present')
226
227 self.wait_until_visible('#project-topbar')
228 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')
229 try:
235 self.driver.find_element(By.XPATH, "//div[@id='empty-state-projectbuildstable']") 230 self.driver.find_element(By.XPATH, "//div[@id='empty-state-projectbuildstable']")
236 except: 231 except:
237 self.fail(msg='Builds tab information is not present') 232 self.fail(msg='Builds tab information is not present')
238 233
239 try: 234 try:
240 self.driver.find_element(By.XPATH, "//div[@id='project-topbar']/ul[@class='nav nav-tabs']/li/a[@href="+'"'+project_URL+'importlayer"'+"]").click() 235 self.driver.find_element(By.XPATH, "//div[@id='project-topbar']/ul[@class='nav nav-tabs']/li/a[@href="+'"'+project_URL+'importlayer"'+"]").click()
241 self.wait_until_visible('#project-topbar', poll=10) 236 except:
242 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') 237 self.fail(msg='Import layer tab not loading properly')
238
239 self.wait_until_visible('#project-topbar')
240 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')
241 try:
243 self.driver.find_element(By.XPATH, "//fieldset[@id='repo-select']") 242 self.driver.find_element(By.XPATH, "//fieldset[@id='repo-select']")
244 self.driver.find_element(By.XPATH, "//fieldset[@id='git-repo']") 243 self.driver.find_element(By.XPATH, "//fieldset[@id='git-repo']")
245 except: 244 except:
@@ -247,11 +246,12 @@ class FuntionalTestBasic(SeleniumFunctionalTestCase):
247 246
248 try: 247 try:
249 self.driver.find_element(By.XPATH, "//div[@id='project-topbar']/ul[@class='nav nav-tabs']/li/a[@href="+'"'+project_URL+'newcustomimage/"'+"]").click() 248 self.driver.find_element(By.XPATH, "//div[@id='project-topbar']/ul[@class='nav nav-tabs']/li/a[@href="+'"'+project_URL+'newcustomimage/"'+"]").click()
250 self.wait_until_visible('#project-topbar', poll=10)
251 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')
252 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')
253 except: 249 except:
254 self.fail(msg='New custom image tab not loading properly') 250 self.fail(msg='New custom image tab not loading properly')
255 251
252 self.wait_until_visible('#project-topbar')
253 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')
254 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')
255
256 256
257 257
diff --git a/bitbake/lib/toaster/tests/functional/test_project_config.py b/bitbake/lib/toaster/tests/functional/test_project_config.py
index dbee36aa4e..fcb1bc3284 100644
--- a/bitbake/lib/toaster/tests/functional/test_project_config.py
+++ b/bitbake/lib/toaster/tests/functional/test_project_config.py
@@ -7,7 +7,6 @@
7# 7#
8 8
9import string 9import string
10import random
11import pytest 10import pytest
12from django.urls import reverse 11from django.urls import reverse
13from selenium.webdriver import Keys 12from selenium.webdriver import Keys
@@ -18,9 +17,6 @@ from selenium.webdriver.common.by import By
18 17
19from .utils import get_projectId_from_url 18from .utils import get_projectId_from_url
20 19
21
22@pytest.mark.django_db
23@pytest.mark.order("last")
24class TestProjectConfig(SeleniumFunctionalTestCase): 20class TestProjectConfig(SeleniumFunctionalTestCase):
25 project_id = None 21 project_id = None
26 PROJECT_NAME = 'TestProjectConfig' 22 PROJECT_NAME = 'TestProjectConfig'
@@ -28,42 +24,6 @@ class TestProjectConfig(SeleniumFunctionalTestCase):
28 INVALID_PATH_CHAR_TEXT = 'The directory path cannot include spaces or ' \ 24 INVALID_PATH_CHAR_TEXT = 'The directory path cannot include spaces or ' \
29 'any of these characters' 25 'any of these characters'
30 26
31 def _create_project(self, project_name):
32 """ Create/Test new project using:
33 - Project Name: Any string
34 - Release: Any string
35 - Merge Toaster settings: True or False
36 """
37 self.get(reverse('newproject'))
38 self.wait_until_visible('#new-project-name', poll=2)
39 self.find("#new-project-name").send_keys(project_name)
40 select = Select(self.find("#projectversion"))
41 select.select_by_value('3')
42
43 # check merge toaster settings
44 checkbox = self.find('.checkbox-mergeattr')
45 if not checkbox.is_selected():
46 checkbox.click()
47
48 if self.PROJECT_NAME != 'TestProjectConfig':
49 # Reset project name if it's not the default one
50 self.PROJECT_NAME = 'TestProjectConfig'
51
52 self.find("#create-project-button").click()
53
54 try:
55 self.wait_until_visible('#hint-error-project-name', poll=2)
56 url = reverse('project', args=(TestProjectConfig.project_id, ))
57 self.get(url)
58 self.wait_until_visible('#config-nav', poll=3)
59 except TimeoutException:
60 self.wait_until_visible('#config-nav', poll=3)
61
62 def _random_string(self, length):
63 return ''.join(
64 random.choice(string.ascii_letters) for _ in range(length)
65 )
66
67 def _get_config_nav_item(self, index): 27 def _get_config_nav_item(self, index):
68 config_nav = self.find('#config-nav') 28 config_nav = self.find('#config-nav')
69 return config_nav.find_elements(By.TAG_NAME, 'li')[index] 29 return config_nav.find_elements(By.TAG_NAME, 'li')[index]
@@ -72,16 +32,14 @@ class TestProjectConfig(SeleniumFunctionalTestCase):
72 """ Navigate to project BitBake variables page """ 32 """ Navigate to project BitBake variables page """
73 # check if the menu is displayed 33 # check if the menu is displayed
74 if TestProjectConfig.project_id is None: 34 if TestProjectConfig.project_id is None:
75 self._create_project(project_name=self._random_string(10)) 35 TestProjectConfig.project_id = self.create_new_project(self.PROJECT_NAME, '3', None, True)
76 current_url = self.driver.current_url 36
77 TestProjectConfig.project_id = get_projectId_from_url(current_url) 37 url = reverse('projectconf', args=(TestProjectConfig.project_id,))
78 else: 38 self.get(url)
79 url = reverse('projectconf', args=(TestProjectConfig.project_id,)) 39 self.wait_until_visible('#config-nav')
80 self.get(url)
81 self.wait_until_visible('#config-nav', poll=3)
82 bbv_page_link = self._get_config_nav_item(9) 40 bbv_page_link = self._get_config_nav_item(9)
83 bbv_page_link.click() 41 bbv_page_link.click()
84 self.wait_until_visible('#config-nav', poll=3) 42 self.wait_until_visible('#config-nav')
85 43
86 def test_no_underscore_iamgefs_type(self): 44 def test_no_underscore_iamgefs_type(self):
87 """ 45 """
@@ -90,13 +48,13 @@ class TestProjectConfig(SeleniumFunctionalTestCase):
90 self._navigate_bbv_page() 48 self._navigate_bbv_page()
91 imagefs_type = "foo_bar" 49 imagefs_type = "foo_bar"
92 50
93 self.wait_until_visible('#change-image_fstypes-icon', poll=2) 51 self.wait_until_visible('#change-image_fstypes-icon')
94 52
95 self.click('#change-image_fstypes-icon') 53 self.click('#change-image_fstypes-icon')
96 54
97 self.enter_text('#new-imagefs_types', imagefs_type) 55 self.enter_text('#new-imagefs_types', imagefs_type)
98 56
99 element = self.wait_until_visible('#hintError-image-fs_type', poll=2) 57 element = self.wait_until_visible('#hintError-image-fs_type')
100 58
101 self.assertTrue(("A valid image type cannot include underscores" in element.text), 59 self.assertTrue(("A valid image type cannot include underscores" in element.text),
102 "Did not find underscore error message") 60 "Did not find underscore error message")
@@ -110,7 +68,7 @@ class TestProjectConfig(SeleniumFunctionalTestCase):
110 68
111 imagefs_type = "btrfs" 69 imagefs_type = "btrfs"
112 70
113 self.wait_until_visible('#change-image_fstypes-icon', poll=2) 71 self.wait_until_visible('#change-image_fstypes-icon')
114 72
115 self.click('#change-image_fstypes-icon') 73 self.click('#change-image_fstypes-icon')
116 74
@@ -129,22 +87,20 @@ class TestProjectConfig(SeleniumFunctionalTestCase):
129 """ 87 """
130 self._navigate_bbv_page() 88 self._navigate_bbv_page()
131 89
132 self.wait_until_visible('#change-image_fstypes-icon', poll=2) 90 self.wait_until_visible('#change-image_fstypes-icon')
133
134 self.click('#change-image_fstypes-icon') 91 self.click('#change-image_fstypes-icon')
135 92
136 checkboxes_selector = '.fs-checkbox-fstypes' 93 checkboxes_selector = '.fs-checkbox-fstypes'
137 94
138 self.wait_until_visible(checkboxes_selector, poll=2) 95 self.wait_until_visible(checkboxes_selector)
139 checkboxes = self.find_all(checkboxes_selector) 96 checkboxes = self.find_all(checkboxes_selector)
140 97
141 for checkbox in checkboxes: 98 for checkbox in checkboxes:
142 if checkbox.get_attribute("value") == "cpio": 99 if checkbox.get_attribute("value") == "cpio":
143 checkbox.click() 100 checkbox.click()
101 self.wait_until_visible('#new-imagefs_types')
144 element = self.driver.find_element(By.ID, 'new-imagefs_types') 102 element = self.driver.find_element(By.ID, 'new-imagefs_types')
145 103
146 self.wait_until_visible('#new-imagefs_types', poll=2)
147
148 self.assertTrue(("cpio" in element.get_attribute('value'), 104 self.assertTrue(("cpio" in element.get_attribute('value'),
149 "Imagefs not added into the textbox")) 105 "Imagefs not added into the textbox"))
150 checkbox.click() 106 checkbox.click()
@@ -160,20 +116,19 @@ class TestProjectConfig(SeleniumFunctionalTestCase):
160 116
161 # activate the input to edit download dir 117 # activate the input to edit download dir
162 try: 118 try:
163 change_dl_dir_btn = self.wait_until_visible('#change-dl_dir-icon', poll=2) 119 change_dl_dir_btn = self.wait_until_visible('#change-dl_dir-icon')
164 except TimeoutException: 120 except TimeoutException:
165 # If download dir is not displayed, test is skipped 121 # If download dir is not displayed, test is skipped
166 change_dl_dir_btn = None 122 change_dl_dir_btn = None
167 123
168 if change_dl_dir_btn: 124 if change_dl_dir_btn:
169 change_dl_dir_btn = self.wait_until_visible('#change-dl_dir-icon', poll=2)
170 change_dl_dir_btn.click() 125 change_dl_dir_btn.click()
171 126
172 # downloads dir path doesn't start with / or ${...} 127 # downloads dir path doesn't start with / or ${...}
173 input_field = self.wait_until_visible('#new-dl_dir', poll=2) 128 input_field = self.wait_until_visible('#new-dl_dir')
174 input_field.clear() 129 input_field.clear()
175 self.enter_text('#new-dl_dir', 'home/foo') 130 self.enter_text('#new-dl_dir', 'home/foo')
176 element = self.wait_until_visible('#hintError-initialChar-dl_dir', poll=2) 131 element = self.wait_until_visible('#hintError-initialChar-dl_dir')
177 132
178 msg = 'downloads directory path starts with invalid character but ' \ 133 msg = 'downloads directory path starts with invalid character but ' \
179 'treated as valid' 134 'treated as valid'
@@ -183,7 +138,7 @@ class TestProjectConfig(SeleniumFunctionalTestCase):
183 self.driver.find_element(By.ID, 'new-dl_dir').clear() 138 self.driver.find_element(By.ID, 'new-dl_dir').clear()
184 self.enter_text('#new-dl_dir', '/foo/bar a') 139 self.enter_text('#new-dl_dir', '/foo/bar a')
185 140
186 element = self.wait_until_visible('#hintError-dl_dir', poll=2) 141 element = self.wait_until_visible('#hintError-dl_dir')
187 msg = 'downloads directory path characters invalid but treated as valid' 142 msg = 'downloads directory path characters invalid but treated as valid'
188 self.assertTrue((self.INVALID_PATH_CHAR_TEXT in element.text), msg) 143 self.assertTrue((self.INVALID_PATH_CHAR_TEXT in element.text), msg)
189 144
@@ -191,7 +146,7 @@ class TestProjectConfig(SeleniumFunctionalTestCase):
191 self.driver.find_element(By.ID,'new-dl_dir').clear() 146 self.driver.find_element(By.ID,'new-dl_dir').clear()
192 self.enter_text('#new-dl_dir', '${TOPDIR}/down foo') 147 self.enter_text('#new-dl_dir', '${TOPDIR}/down foo')
193 148
194 element = self.wait_until_visible('#hintError-dl_dir', poll=2) 149 element = self.wait_until_visible('#hintError-dl_dir')
195 msg = 'downloads directory path characters invalid but treated as valid' 150 msg = 'downloads directory path characters invalid but treated as valid'
196 self.assertTrue((self.INVALID_PATH_CHAR_TEXT in element.text), msg) 151 self.assertTrue((self.INVALID_PATH_CHAR_TEXT in element.text), msg)
197 152
@@ -219,10 +174,7 @@ class TestProjectConfig(SeleniumFunctionalTestCase):
219 self._navigate_bbv_page() 174 self._navigate_bbv_page()
220 175
221 try: 176 try:
222 btn_chg_sstate_dir = self.wait_until_visible( 177 btn_chg_sstate_dir = self.wait_until_visible('#change-sstate_dir-icon')
223 '#change-sstate_dir-icon',
224 poll=2
225 )
226 self.click('#change-sstate_dir-icon') 178 self.click('#change-sstate_dir-icon')
227 except TimeoutException: 179 except TimeoutException:
228 # If sstate_dir is not displayed, test is skipped 180 # If sstate_dir is not displayed, test is skipped
@@ -230,10 +182,10 @@ class TestProjectConfig(SeleniumFunctionalTestCase):
230 182
231 if btn_chg_sstate_dir: # Skip continuation if sstate_dir is not displayed 183 if btn_chg_sstate_dir: # Skip continuation if sstate_dir is not displayed
232 # path doesn't start with / or ${...} 184 # path doesn't start with / or ${...}
233 input_field = self.wait_until_visible('#new-sstate_dir', poll=2) 185 input_field = self.wait_until_visible('#new-sstate_dir')
234 input_field.clear() 186 input_field.clear()
235 self.enter_text('#new-sstate_dir', 'home/foo') 187 self.enter_text('#new-sstate_dir', 'home/foo')
236 element = self.wait_until_visible('#hintError-initialChar-sstate_dir', poll=2) 188 element = self.wait_until_visible('#hintError-initialChar-sstate_dir')
237 189
238 msg = 'sstate directory path starts with invalid character but ' \ 190 msg = 'sstate directory path starts with invalid character but ' \
239 'treated as valid' 191 'treated as valid'
@@ -243,7 +195,7 @@ class TestProjectConfig(SeleniumFunctionalTestCase):
243 self.driver.find_element(By.ID, 'new-sstate_dir').clear() 195 self.driver.find_element(By.ID, 'new-sstate_dir').clear()
244 self.enter_text('#new-sstate_dir', '/foo/bar a') 196 self.enter_text('#new-sstate_dir', '/foo/bar a')
245 197
246 element = self.wait_until_visible('#hintError-sstate_dir', poll=2) 198 element = self.wait_until_visible('#hintError-sstate_dir')
247 msg = 'sstate directory path characters invalid but treated as valid' 199 msg = 'sstate directory path characters invalid but treated as valid'
248 self.assertTrue((self.INVALID_PATH_CHAR_TEXT in element.text), msg) 200 self.assertTrue((self.INVALID_PATH_CHAR_TEXT in element.text), msg)
249 201
@@ -251,7 +203,7 @@ class TestProjectConfig(SeleniumFunctionalTestCase):
251 self.driver.find_element(By.ID,'new-sstate_dir').clear() 203 self.driver.find_element(By.ID,'new-sstate_dir').clear()
252 self.enter_text('#new-sstate_dir', '${TOPDIR}/down foo') 204 self.enter_text('#new-sstate_dir', '${TOPDIR}/down foo')
253 205
254 element = self.wait_until_visible('#hintError-sstate_dir', poll=2) 206 element = self.wait_until_visible('#hintError-sstate_dir')
255 msg = 'sstate directory path characters invalid but treated as valid' 207 msg = 'sstate directory path characters invalid but treated as valid'
256 self.assertTrue((self.INVALID_PATH_CHAR_TEXT in element.text), msg) 208 self.assertTrue((self.INVALID_PATH_CHAR_TEXT in element.text), msg)
257 209
@@ -275,13 +227,14 @@ class TestProjectConfig(SeleniumFunctionalTestCase):
275 var_name, field, btn_id, input_id, value, save_btn, *_ = kwargs.values() 227 var_name, field, btn_id, input_id, value, save_btn, *_ = kwargs.values()
276 """ Change bitbake variable value """ 228 """ Change bitbake variable value """
277 self._navigate_bbv_page() 229 self._navigate_bbv_page()
278 self.wait_until_visible(f'#{btn_id}', poll=2) 230 self.wait_until_visible(f'#{btn_id}')
279 if kwargs.get('new_variable'): 231 if kwargs.get('new_variable'):
280 self.find(f"#{btn_id}").clear() 232 self.find(f"#{btn_id}").clear()
281 self.enter_text(f"#{btn_id}", f"{var_name}") 233 self.enter_text(f"#{btn_id}", f"{var_name}")
282 else: 234 else:
283 self.click(f'#{btn_id}') 235 self.click(f'#{btn_id}')
284 self.wait_until_visible(f'#{input_id}', poll=2) 236
237 self.wait_until_visible(f'#{input_id}')
285 238
286 if kwargs.get('is_select'): 239 if kwargs.get('is_select'):
287 select = Select(self.find(f'#{input_id}')) 240 select = Select(self.find(f'#{input_id}'))
diff --git a/bitbake/lib/toaster/tests/functional/test_project_page.py b/bitbake/lib/toaster/tests/functional/test_project_page.py
index adbe3587e4..429d86feba 100644
--- a/bitbake/lib/toaster/tests/functional/test_project_page.py
+++ b/bitbake/lib/toaster/tests/functional/test_project_page.py
@@ -7,8 +7,8 @@
7# 7#
8 8
9import os 9import os
10import random
11import string 10import string
11import time
12from unittest import skip 12from unittest import skip
13import pytest 13import pytest
14from django.urls import reverse 14from django.urls import reverse
@@ -22,58 +22,17 @@ from selenium.webdriver.common.by import By
22 22
23from .utils import get_projectId_from_url, wait_until_build, wait_until_build_cancelled 23from .utils import get_projectId_from_url, wait_until_build, wait_until_build_cancelled
24 24
25 25class TestProjectPageBase(SeleniumFunctionalTestCase):
26@pytest.mark.django_db
27@pytest.mark.order("last")
28class TestProjectPage(SeleniumFunctionalTestCase):
29 project_id = None 26 project_id = None
30 PROJECT_NAME = 'TestProjectPage' 27 PROJECT_NAME = 'TestProjectPage'
31 28
32 def _create_project(self, project_name):
33 """ Create/Test new project using:
34 - Project Name: Any string
35 - Release: Any string
36 - Merge Toaster settings: True or False
37 """
38 self.get(reverse('newproject'))
39 self.wait_until_visible('#new-project-name')
40 self.find("#new-project-name").send_keys(project_name)
41 select = Select(self.find("#projectversion"))
42 select.select_by_value('3')
43
44 # check merge toaster settings
45 checkbox = self.find('.checkbox-mergeattr')
46 if not checkbox.is_selected():
47 checkbox.click()
48
49 if self.PROJECT_NAME != 'TestProjectPage':
50 # Reset project name if it's not the default one
51 self.PROJECT_NAME = 'TestProjectPage'
52
53 self.find("#create-project-button").click()
54
55 try:
56 self.wait_until_visible('#hint-error-project-name')
57 url = reverse('project', args=(TestProjectPage.project_id, ))
58 self.get(url)
59 self.wait_until_visible('#config-nav', poll=3)
60 except TimeoutException:
61 self.wait_until_visible('#config-nav', poll=3)
62
63 def _random_string(self, length):
64 return ''.join(
65 random.choice(string.ascii_letters) for _ in range(length)
66 )
67
68 def _navigate_to_project_page(self): 29 def _navigate_to_project_page(self):
69 # Navigate to project page 30 # Navigate to project page
70 if TestProjectPage.project_id is None: 31 if TestProjectPageBase.project_id is None:
71 self._create_project(project_name=self._random_string(10)) 32 TestProjectPageBase.project_id = self.create_new_project(self.PROJECT_NAME, '3', None, True)
72 current_url = self.driver.current_url 33
73 TestProjectPage.project_id = get_projectId_from_url(current_url) 34 url = reverse('project', args=(TestProjectPageBase.project_id,))
74 else: 35 self.get(url)
75 url = reverse('project', args=(TestProjectPage.project_id,))
76 self.get(url)
77 self.wait_until_visible('#config-nav') 36 self.wait_until_visible('#config-nav')
78 37
79 def _get_create_builds(self, **kwargs): 38 def _get_create_builds(self, **kwargs):
@@ -81,14 +40,14 @@ class TestProjectPage(SeleniumFunctionalTestCase):
81 # parameters for builds to associate with the projects 40 # parameters for builds to associate with the projects
82 now = timezone.now() 41 now = timezone.now()
83 self.project1_build_success = { 42 self.project1_build_success = {
84 'project': Project.objects.get(id=TestProjectPage.project_id), 43 'project': Project.objects.get(id=TestProjectPageBase.project_id),
85 'started_on': now, 44 'started_on': now,
86 'completed_on': now, 45 'completed_on': now,
87 'outcome': Build.SUCCEEDED 46 'outcome': Build.SUCCEEDED
88 } 47 }
89 48
90 self.project1_build_failure = { 49 self.project1_build_failure = {
91 'project': Project.objects.get(id=TestProjectPage.project_id), 50 'project': Project.objects.get(id=TestProjectPageBase.project_id),
92 'started_on': now, 51 'started_on': now,
93 'completed_on': now, 52 'completed_on': now,
94 'outcome': Build.FAILED 53 'outcome': Build.FAILED
@@ -133,7 +92,8 @@ class TestProjectPage(SeleniumFunctionalTestCase):
133 list_check_box_id: list 92 list_check_box_id: list
134 ): 93 ):
135 # Check edit column 94 # Check edit column
136 edit_column = self.find(f'#{edit_btn_id}') 95 finder = lambda driver: self.find(f'#{edit_btn_id}')
96 edit_column = self.wait_until_element_clickable(finder)
137 self.assertTrue(edit_column.is_displayed()) 97 self.assertTrue(edit_column.is_displayed())
138 edit_column.click() 98 edit_column.click()
139 # Check dropdown is visible 99 # Check dropdown is visible
@@ -192,7 +152,7 @@ class TestProjectPage(SeleniumFunctionalTestCase):
192 def test_show_rows(row_to_show, show_row_link): 152 def test_show_rows(row_to_show, show_row_link):
193 # Check that we can show rows == row_to_show 153 # Check that we can show rows == row_to_show
194 show_row_link.select_by_value(str(row_to_show)) 154 show_row_link.select_by_value(str(row_to_show))
195 self.wait_until_visible(f'#{table_selector} tbody tr', poll=3) 155 self.wait_until_visible(f'#{table_selector} tbody tr')
196 # check at least some rows are visible 156 # check at least some rows are visible
197 self.assertTrue( 157 self.assertTrue(
198 len(self.find_all(f'#{table_selector} tbody tr')) > 0 158 len(self.find_all(f'#{table_selector} tbody tr')) > 0
@@ -222,34 +182,7 @@ class TestProjectPage(SeleniumFunctionalTestCase):
222 rows = self.find_all(f'#{table_selector} tbody tr') 182 rows = self.find_all(f'#{table_selector} tbody tr')
223 self.assertTrue(len(rows) > 0) 183 self.assertTrue(len(rows) > 0)
224 184
225 def test_create_project(self): 185class TestProjectPage(TestProjectPageBase):
226 """ Create/Test new project using:
227 - Project Name: Any string
228 - Release: Any string
229 - Merge Toaster settings: True or False
230 """
231 self._create_project(project_name=self.PROJECT_NAME)
232
233 def test_image_recipe_editColumn(self):
234 """ Test the edit column feature in image recipe table on project page """
235 self._get_create_builds(success=10, failure=10)
236
237 url = reverse('projectimagerecipes', args=(TestProjectPage.project_id,))
238 self.get(url)
239 self.wait_until_present('#imagerecipestable tbody tr')
240
241 column_list = [
242 'get_description_or_summary', 'layer_version__get_vcs_reference',
243 'layer_version__layer__name', 'license', 'recipe-file', 'section',
244 'version'
245 ]
246
247 # Check that we can hide the edit column
248 self._mixin_test_table_edit_column(
249 'imagerecipestable',
250 'edit-columns-button',
251 [f'checkbox-{column}' for column in column_list]
252 )
253 186
254 def test_page_header_on_project_page(self): 187 def test_page_header_on_project_page(self):
255 """ Check page header in project page: 188 """ Check page header in project page:
@@ -272,8 +205,8 @@ class TestProjectPage(SeleniumFunctionalTestCase):
272 logo_img = logo.find_element(By.TAG_NAME, 'img') 205 logo_img = logo.find_element(By.TAG_NAME, 'img')
273 self.assertTrue(logo_img.is_displayed(), 206 self.assertTrue(logo_img.is_displayed(),
274 'Logo of Yocto project not found') 207 'Logo of Yocto project not found')
275 self.assertTrue( 208 self.assertIn(
276 '/static/img/logo.png' in str(logo_img.get_attribute('src')), 209 '/static/img/logo.png', str(logo_img.get_attribute('src')),
277 'Logo of Yocto project not found' 210 'Logo of Yocto project not found'
278 ) 211 )
279 # "Toaster"+" Information icon", clickable 212 # "Toaster"+" Information icon", clickable
@@ -282,34 +215,34 @@ class TestProjectPage(SeleniumFunctionalTestCase):
282 "//div[@class='toaster-navbar-brand']//a[@class='brand']", 215 "//div[@class='toaster-navbar-brand']//a[@class='brand']",
283 ) 216 )
284 self.assertTrue(toaster.is_displayed(), 'Toaster not found') 217 self.assertTrue(toaster.is_displayed(), 'Toaster not found')
285 self.assertTrue(toaster.text == 'Toaster') 218 self.assertEqual(toaster.text, 'Toaster')
286 info_sign = self.find('.glyphicon-info-sign') 219 info_sign = self.find('.glyphicon-info-sign')
287 self.assertTrue(info_sign.is_displayed()) 220 self.assertTrue(info_sign.is_displayed())
288 221
289 # "Server Icon" + "All builds" 222 # "Server Icon" + "All builds"
290 all_builds = self.find('#navbar-all-builds') 223 all_builds = self.find('#navbar-all-builds')
291 all_builds_link = all_builds.find_element(By.TAG_NAME, 'a') 224 all_builds_link = all_builds.find_element(By.TAG_NAME, 'a')
292 self.assertTrue("All builds" in all_builds_link.text) 225 self.assertIn("All builds", all_builds_link.text)
293 self.assertTrue( 226 self.assertIn(
294 '/toastergui/builds/' in str(all_builds_link.get_attribute('href')) 227 '/toastergui/builds/', str(all_builds_link.get_attribute('href'))
295 ) 228 )
296 server_icon = all_builds.find_element(By.TAG_NAME, 'i') 229 server_icon = all_builds.find_element(By.TAG_NAME, 'i')
297 self.assertTrue( 230 self.assertEqual(
298 server_icon.get_attribute('class') == 'glyphicon glyphicon-tasks' 231 server_icon.get_attribute('class'), 'glyphicon glyphicon-tasks'
299 ) 232 )
300 self.assertTrue(server_icon.is_displayed()) 233 self.assertTrue(server_icon.is_displayed())
301 234
302 # "Directory Icon" + "All projects" 235 # "Directory Icon" + "All projects"
303 all_projects = self.find('#navbar-all-projects') 236 all_projects = self.find('#navbar-all-projects')
304 all_projects_link = all_projects.find_element(By.TAG_NAME, 'a') 237 all_projects_link = all_projects.find_element(By.TAG_NAME, 'a')
305 self.assertTrue("All projects" in all_projects_link.text) 238 self.assertIn("All projects", all_projects_link.text)
306 self.assertTrue( 239 self.assertIn(
307 '/toastergui/projects/' in str(all_projects_link.get_attribute( 240 '/toastergui/projects/', str(all_projects_link.get_attribute(
308 'href')) 241 'href'))
309 ) 242 )
310 dir_icon = all_projects.find_element(By.TAG_NAME, 'i') 243 dir_icon = all_projects.find_element(By.TAG_NAME, 'i')
311 self.assertTrue( 244 self.assertEqual(
312 dir_icon.get_attribute('class') == 'icon-folder-open' 245 dir_icon.get_attribute('class'), 'icon-folder-open'
313 ) 246 )
314 self.assertTrue(dir_icon.is_displayed()) 247 self.assertTrue(dir_icon.is_displayed())
315 248
@@ -317,23 +250,23 @@ class TestProjectPage(SeleniumFunctionalTestCase):
317 toaster_docs_link = self.find('#navbar-docs') 250 toaster_docs_link = self.find('#navbar-docs')
318 toaster_docs_link_link = toaster_docs_link.find_element(By.TAG_NAME, 251 toaster_docs_link_link = toaster_docs_link.find_element(By.TAG_NAME,
319 'a') 252 'a')
320 self.assertTrue("Documentation" in toaster_docs_link_link.text) 253 self.assertIn("Documentation", toaster_docs_link_link.text)
321 self.assertTrue( 254 self.assertEqual(
322 toaster_docs_link_link.get_attribute('href') == 'http://docs.yoctoproject.org/toaster-manual/index.html#toaster-user-manual' 255 toaster_docs_link_link.get_attribute('href'), 'http://docs.yoctoproject.org/toaster-manual/index.html#toaster-user-manual'
323 ) 256 )
324 book_icon = toaster_docs_link.find_element(By.TAG_NAME, 'i') 257 book_icon = toaster_docs_link.find_element(By.TAG_NAME, 'i')
325 self.assertTrue( 258 self.assertEqual(
326 book_icon.get_attribute('class') == 'glyphicon glyphicon-book' 259 book_icon.get_attribute('class'), 'glyphicon glyphicon-book'
327 ) 260 )
328 self.assertTrue(book_icon.is_displayed()) 261 self.assertTrue(book_icon.is_displayed())
329 262
330 # AT RIGHT -> button "New project" 263 # AT RIGHT -> button "New project"
331 new_project_button = self.find('#new-project-button') 264 new_project_button = self.find('#new-project-button')
332 self.assertTrue(new_project_button.is_displayed()) 265 self.assertTrue(new_project_button.is_displayed())
333 self.assertTrue(new_project_button.text == 'New project') 266 self.assertEqual(new_project_button.text, 'New project')
334 new_project_button.click() 267 new_project_button.click()
335 self.assertTrue( 268 self.assertIn(
336 '/toastergui/newproject/' in str(self.driver.current_url) 269 '/toastergui/newproject/', str(self.driver.current_url)
337 ) 270 )
338 271
339 def test_edit_project_name(self): 272 def test_edit_project_name(self):
@@ -348,7 +281,8 @@ class TestProjectPage(SeleniumFunctionalTestCase):
348 281
349 # click on "Edit" icon button 282 # click on "Edit" icon button
350 self.wait_until_visible('#project-name-container') 283 self.wait_until_visible('#project-name-container')
351 edit_button = self.find('#project-change-form-toggle') 284 finder = lambda driver: self.find('#project-change-form-toggle')
285 edit_button = self.wait_until_element_clickable(finder)
352 edit_button.click() 286 edit_button.click()
353 project_name_input = self.find('#project-name-change-input') 287 project_name_input = self.find('#project-name-change-input')
354 self.assertTrue(project_name_input.is_displayed()) 288 self.assertTrue(project_name_input.is_displayed())
@@ -358,8 +292,8 @@ class TestProjectPage(SeleniumFunctionalTestCase):
358 292
359 # check project name is changed 293 # check project name is changed
360 self.wait_until_visible('#project-name-container') 294 self.wait_until_visible('#project-name-container')
361 self.assertTrue( 295 self.assertIn(
362 'New Name' in str(self.find('#project-name-container').text) 296 'New Name', str(self.find('#project-name-container').text)
363 ) 297 )
364 298
365 def test_project_page_tabs(self): 299 def test_project_page_tabs(self):
@@ -376,10 +310,10 @@ class TestProjectPage(SeleniumFunctionalTestCase):
376 # check "configuration" tab 310 # check "configuration" tab
377 self.wait_until_visible('#topbar-configuration-tab') 311 self.wait_until_visible('#topbar-configuration-tab')
378 config_tab = self.find('#topbar-configuration-tab') 312 config_tab = self.find('#topbar-configuration-tab')
379 self.assertTrue(config_tab.get_attribute('class') == 'active') 313 self.assertEqual(config_tab.get_attribute('class'), 'active')
380 self.assertTrue('Configuration' in str(config_tab.text)) 314 self.assertIn('Configuration', str(config_tab.text))
381 self.assertTrue( 315 self.assertIn(
382 f"/toastergui/project/{TestProjectPage.project_id}" in str(self.driver.current_url) 316 f"/toastergui/project/{TestProjectPageBase.project_id}", str(self.driver.current_url)
383 ) 317 )
384 318
385 def get_tabs(): 319 def get_tabs():
@@ -392,9 +326,9 @@ class TestProjectPage(SeleniumFunctionalTestCase):
392 def check_tab_link(tab_index, tab_name, url): 326 def check_tab_link(tab_index, tab_name, url):
393 tab = get_tabs()[tab_index] 327 tab = get_tabs()[tab_index]
394 tab_link = tab.find_element(By.TAG_NAME, 'a') 328 tab_link = tab.find_element(By.TAG_NAME, 'a')
395 self.assertTrue(url in tab_link.get_attribute('href')) 329 self.assertIn(url, tab_link.get_attribute('href'))
396 self.assertTrue(tab_name in tab_link.text) 330 self.assertIn(tab_name, tab_link.text)
397 self.assertTrue(tab.get_attribute('class') == 'active') 331 self.assertEqual(tab.get_attribute('class'), 'active')
398 332
399 # check "Builds" tab 333 # check "Builds" tab
400 builds_tab = get_tabs()[1] 334 builds_tab = get_tabs()[1]
@@ -402,7 +336,7 @@ class TestProjectPage(SeleniumFunctionalTestCase):
402 check_tab_link( 336 check_tab_link(
403 1, 337 1,
404 'Builds', 338 'Builds',
405 f"/toastergui/project/{TestProjectPage.project_id}/builds" 339 f"/toastergui/project/{TestProjectPageBase.project_id}/builds"
406 ) 340 )
407 341
408 # check "Import layers" tab 342 # check "Import layers" tab
@@ -411,7 +345,7 @@ class TestProjectPage(SeleniumFunctionalTestCase):
411 check_tab_link( 345 check_tab_link(
412 2, 346 2,
413 'Import layer', 347 'Import layer',
414 f"/toastergui/project/{TestProjectPage.project_id}/importlayer" 348 f"/toastergui/project/{TestProjectPageBase.project_id}/importlayer"
415 ) 349 )
416 350
417 # check "New custom image" tab 351 # check "New custom image" tab
@@ -420,7 +354,7 @@ class TestProjectPage(SeleniumFunctionalTestCase):
420 check_tab_link( 354 check_tab_link(
421 3, 355 3,
422 'New custom image', 356 'New custom image',
423 f"/toastergui/project/{TestProjectPage.project_id}/newcustomimage" 357 f"/toastergui/project/{TestProjectPageBase.project_id}/newcustomimage"
424 ) 358 )
425 359
426 # check search box can be use to build recipes 360 # check search box can be use to build recipes
@@ -428,13 +362,17 @@ class TestProjectPage(SeleniumFunctionalTestCase):
428 search_box.send_keys('core-image-minimal') 362 search_box.send_keys('core-image-minimal')
429 self.find('#build-button').click() 363 self.find('#build-button').click()
430 self.wait_until_visible('#latest-builds') 364 self.wait_until_visible('#latest-builds')
431 lastest_builds = self.driver.find_elements( 365 buildtext = "Loading"
432 By.XPATH, 366 while "Loading" in buildtext:
433 '//div[@id="latest-builds"]', 367 time.sleep(1)
434 ) 368 lastest_builds = self.driver.find_elements(
435 last_build = lastest_builds[0] 369 By.XPATH,
436 self.assertTrue( 370 '//div[@id="latest-builds"]',
437 'core-image-minimal' in str(last_build.text) 371 )
372 last_build = lastest_builds[0]
373 buildtext = last_build.text
374 self.assertIn(
375 'core-image-minimal', str(last_build.text)
438 ) 376 )
439 377
440 def test_softwareRecipe_page(self): 378 def test_softwareRecipe_page(self):
@@ -446,7 +384,7 @@ class TestProjectPage(SeleniumFunctionalTestCase):
446 """ 384 """
447 self._navigate_to_config_nav('softwarerecipestable', 4) 385 self._navigate_to_config_nav('softwarerecipestable', 4)
448 # check title "Compatible software recipes" is displayed 386 # check title "Compatible software recipes" is displayed
449 self.assertTrue("Compatible software recipes" in self.get_page_source()) 387 self.assertIn("Compatible software recipes", self.get_page_source())
450 # Test search input 388 # Test search input
451 self._mixin_test_table_search_input( 389 self._mixin_test_table_search_input(
452 input_selector='search-input-softwarerecipestable', 390 input_selector='search-input-softwarerecipestable',
@@ -455,12 +393,8 @@ class TestProjectPage(SeleniumFunctionalTestCase):
455 table_selector='softwarerecipestable' 393 table_selector='softwarerecipestable'
456 ) 394 )
457 # check "build recipe" button works 395 # check "build recipe" button works
458 rows = self.find_all('#softwarerecipestable tbody tr') 396 finder = lambda driver: self.find_all('#softwarerecipestable tbody tr')[0].find_element(By.XPATH, '//td[@class="add-del-layers"]/a')
459 image_to_build = rows[0] 397 build_btn = self.wait_until_element_clickable(finder)
460 build_btn = image_to_build.find_element(
461 By.XPATH,
462 '//td[@class="add-del-layers"]//a[1]'
463 )
464 build_btn.click() 398 build_btn.click()
465 build_state = wait_until_build(self, 'queued cloning starting parsing failed') 399 build_state = wait_until_build(self, 'queued cloning starting parsing failed')
466 lastest_builds = self.driver.find_elements( 400 lastest_builds = self.driver.find_elements(
@@ -468,11 +402,10 @@ class TestProjectPage(SeleniumFunctionalTestCase):
468 '//div[@id="latest-builds"]/div' 402 '//div[@id="latest-builds"]/div'
469 ) 403 )
470 self.assertTrue(len(lastest_builds) > 0) 404 self.assertTrue(len(lastest_builds) > 0)
471 last_build = lastest_builds[0] 405 # Find the latest builds, the last build and then the cancel button
472 cancel_button = last_build.find_element( 406
473 By.XPATH, 407 finder = lambda driver: driver.find_elements(By.XPATH, '//div[@id="latest-builds"]/div')[0].find_element(By.XPATH, '//span[@class="cancel-build-btn pull-right alert-link"]')
474 '//span[@class="cancel-build-btn pull-right alert-link"]', 408 cancel_button = self.wait_until_element_clickable(finder)
475 )
476 cancel_button.click() 409 cancel_button.click()
477 if 'starting' not in build_state: # change build state when cancelled in starting state 410 if 'starting' not in build_state: # change build state when cancelled in starting state
478 wait_until_build_cancelled(self) 411 wait_until_build_cancelled(self)
@@ -510,7 +443,7 @@ class TestProjectPage(SeleniumFunctionalTestCase):
510 """ 443 """
511 self._navigate_to_config_nav('machinestable', 5) 444 self._navigate_to_config_nav('machinestable', 5)
512 # check title "Compatible software recipes" is displayed 445 # check title "Compatible software recipes" is displayed
513 self.assertTrue("Compatible machines" in self.get_page_source()) 446 self.assertIn("Compatible machines", self.get_page_source())
514 # Test search input 447 # Test search input
515 self._mixin_test_table_search_input( 448 self._mixin_test_table_search_input(
516 input_selector='search-input-machinestable', 449 input_selector='search-input-machinestable',
@@ -519,17 +452,13 @@ class TestProjectPage(SeleniumFunctionalTestCase):
519 table_selector='machinestable' 452 table_selector='machinestable'
520 ) 453 )
521 # check "Select machine" button works 454 # check "Select machine" button works
522 rows = self.find_all('#machinestable tbody tr') 455 finder = lambda driver: self.find_all('#machinestable tbody tr')[0].find_element(By.XPATH, '//td[@class="add-del-layers"]')
523 machine_to_select = rows[0] 456 select_btn = self.wait_until_element_clickable(finder)
524 select_btn = machine_to_select.find_element( 457 select_btn.click()
525 By.XPATH, 458 self.wait_until_visible('#project-machine-name')
526 '//td[@class="add-del-layers"]//a[1]'
527 )
528 select_btn.send_keys(Keys.RETURN)
529 self.wait_until_visible('#config-nav')
530 project_machine_name = self.find('#project-machine-name') 459 project_machine_name = self.find('#project-machine-name')
531 self.assertTrue( 460 self.assertIn(
532 'qemux86-64' in project_machine_name.text 461 'qemux86-64', project_machine_name.text
533 ) 462 )
534 # check "Add layer" button works 463 # check "Add layer" button works
535 self._navigate_to_config_nav('machinestable', 5) 464 self._navigate_to_config_nav('machinestable', 5)
@@ -540,16 +469,23 @@ class TestProjectPage(SeleniumFunctionalTestCase):
540 searchBtn_selector='search-submit-machinestable', 469 searchBtn_selector='search-submit-machinestable',
541 table_selector='machinestable' 470 table_selector='machinestable'
542 ) 471 )
543 self.wait_until_visible('#machinestable tbody tr', poll=3) 472
544 rows = self.find_all('#machinestable tbody tr') 473 self.wait_until_visible('#machinestable tbody tr')
545 machine_to_add = rows[0] 474 # Locate a machine to add button
546 add_btn = machine_to_add.find_element(By.XPATH, '//td[@class="add-del-layers"]') 475 finder = lambda driver: self.find_all('#machinestable tbody tr')[0].find_element(By.XPATH, '//td[@class="add-del-layers"]')
476 add_btn = self.wait_until_element_clickable(finder)
547 add_btn.click() 477 add_btn.click()
548 self.wait_until_visible('#change-notification') 478 self.wait_until_visible('#change-notification')
549 change_notification = self.find('#change-notification') 479 change_notification = self.find('#change-notification')
550 self.assertTrue( 480 self.assertIn(
551 f'You have added 1 layer to your project' in str(change_notification.text) 481 f'You have added 1 layer to your project', str(change_notification.text)
552 ) 482 )
483
484 finder = lambda driver: self.find('#hide-alert')
485 hide_button = self.wait_until_element_clickable(finder)
486 hide_button.click()
487 self.wait_until_not_visible('#change-notification')
488
553 # check Machine table feature(show/hide column, pagination) 489 # check Machine table feature(show/hide column, pagination)
554 self._navigate_to_config_nav('machinestable', 5) 490 self._navigate_to_config_nav('machinestable', 5)
555 column_list = [ 491 column_list = [
@@ -580,7 +516,7 @@ class TestProjectPage(SeleniumFunctionalTestCase):
580 """ 516 """
581 self._navigate_to_config_nav('layerstable', 6) 517 self._navigate_to_config_nav('layerstable', 6)
582 # check title "Compatible layers" is displayed 518 # check title "Compatible layers" is displayed
583 self.assertTrue("Compatible layers" in self.get_page_source()) 519 self.assertIn("Compatible layers", self.get_page_source())
584 # Test search input 520 # Test search input
585 input_text='meta-tanowrt' 521 input_text='meta-tanowrt'
586 self._mixin_test_table_search_input( 522 self._mixin_test_table_search_input(
@@ -590,42 +526,44 @@ class TestProjectPage(SeleniumFunctionalTestCase):
590 table_selector='layerstable' 526 table_selector='layerstable'
591 ) 527 )
592 # check "Add layer" button works 528 # check "Add layer" button works
593 self.wait_until_visible('#layerstable tbody tr', poll=3) 529 self.wait_until_visible('#layerstable tbody tr')
594 rows = self.find_all('#layerstable tbody tr') 530 finder = lambda driver: self.find_all('#layerstable tbody tr')[0].find_element(By.XPATH, '//td[@class="add-del-layers"]/a[@data-directive="add"]')
595 layer_to_add = rows[0] 531 add_btn = self.wait_until_element_clickable(finder)
596 add_btn = layer_to_add.find_element(
597 By.XPATH,
598 '//td[@class="add-del-layers"]'
599 )
600 add_btn.click() 532 add_btn.click()
601 # check modal is displayed 533 # check modal is displayed
602 self.wait_until_visible('#dependencies-modal', poll=3) 534 self.wait_until_visible('#dependencies-modal')
603 list_dependencies = self.find_all('#dependencies-list li') 535 list_dependencies = self.find_all('#dependencies-list li')
604 # click on add-layers button 536 # click on add-layers button
605 add_layers_btn = self.driver.find_element( 537 finder = lambda driver: self.driver.find_element(By.XPATH, '//form[@id="dependencies-modal-form"]//button[@class="btn btn-primary"]')
606 By.XPATH, 538 add_layers_btn = self.wait_until_element_clickable(finder)
607 '//form[@id="dependencies-modal-form"]//button[@class="btn btn-primary"]'
608 )
609 add_layers_btn.click() 539 add_layers_btn.click()
610 self.wait_until_visible('#change-notification') 540 self.wait_until_visible('#change-notification')
611 change_notification = self.find('#change-notification') 541 change_notification = self.find('#change-notification')
612 self.assertTrue( 542 self.assertIn(
613 f'You have added {len(list_dependencies)+1} layers to your project: {input_text} and its dependencies' in str(change_notification.text) 543 f'You have added {len(list_dependencies)+1} layers to your project: {input_text} and its dependencies', str(change_notification.text)
614 ) 544 )
545
546 finder = lambda driver: self.find('#hide-alert')
547 hide_button = self.wait_until_element_clickable(finder)
548 hide_button.click()
549 self.wait_until_not_visible('#change-notification')
550
615 # check "Remove layer" button works 551 # check "Remove layer" button works
616 self.wait_until_visible('#layerstable tbody tr', poll=3) 552 self.wait_until_visible('#layerstable tbody tr')
617 rows = self.find_all('#layerstable tbody tr') 553 finder = lambda driver: self.find_all('#layerstable tbody tr')[0].find_element(By.XPATH, '//td[@class="add-del-layers"]/a[@data-directive="remove"]')
618 layer_to_remove = rows[0] 554 remove_btn = self.wait_until_element_clickable(finder)
619 remove_btn = layer_to_remove.find_element(
620 By.XPATH,
621 '//td[@class="add-del-layers"]'
622 )
623 remove_btn.click() 555 remove_btn.click()
624 self.wait_until_visible('#change-notification', poll=2) 556 self.wait_until_visible('#change-notification')
625 change_notification = self.find('#change-notification') 557 change_notification = self.find('#change-notification')
626 self.assertTrue( 558 self.assertIn(
627 f'You have removed 1 layer from your project: {input_text}' in str(change_notification.text) 559 f'You have removed 1 layer from your project: {input_text}', str(change_notification.text)
628 ) 560 )
561
562 finder = lambda driver: self.find('#hide-alert')
563 hide_button = self.wait_until_element_clickable(finder)
564 hide_button.click()
565 self.wait_until_not_visible('#change-notification')
566
629 # check layers table feature(show/hide column, pagination) 567 # check layers table feature(show/hide column, pagination)
630 self._navigate_to_config_nav('layerstable', 6) 568 self._navigate_to_config_nav('layerstable', 6)
631 column_list = [ 569 column_list = [
@@ -656,7 +594,7 @@ class TestProjectPage(SeleniumFunctionalTestCase):
656 """ 594 """
657 self._navigate_to_config_nav('distrostable', 7) 595 self._navigate_to_config_nav('distrostable', 7)
658 # check title "Compatible distros" is displayed 596 # check title "Compatible distros" is displayed
659 self.assertTrue("Compatible Distros" in self.get_page_source()) 597 self.assertIn("Compatible Distros", self.get_page_source())
660 # Test search input 598 # Test search input
661 input_text='poky-altcfg' 599 input_text='poky-altcfg'
662 self._mixin_test_table_search_input( 600 self._mixin_test_table_search_input(
@@ -666,17 +604,14 @@ class TestProjectPage(SeleniumFunctionalTestCase):
666 table_selector='distrostable' 604 table_selector='distrostable'
667 ) 605 )
668 # check "Add distro" button works 606 # check "Add distro" button works
669 rows = self.find_all('#distrostable tbody tr') 607 self.wait_until_visible(".add-del-layers")
670 distro_to_add = rows[0] 608 finder = lambda driver: self.find_all('#distrostable tbody tr')[0].find_element(By.XPATH, '//td[@class="add-del-layers"]')
671 add_btn = distro_to_add.find_element( 609 add_btn = self.wait_until_element_clickable(finder)
672 By.XPATH,
673 '//td[@class="add-del-layers"]//a[1]'
674 )
675 add_btn.click() 610 add_btn.click()
676 self.wait_until_visible('#change-notification', poll=2) 611 self.wait_until_visible('#change-notification')
677 change_notification = self.find('#change-notification') 612 change_notification = self.find('#change-notification')
678 self.assertTrue( 613 self.assertIn(
679 f'You have changed the distro to: {input_text}' in str(change_notification.text) 614 f'You have changed the distro to: {input_text}', str(change_notification.text)
680 ) 615 )
681 # check distro table feature(show/hide column, pagination) 616 # check distro table feature(show/hide column, pagination)
682 self._navigate_to_config_nav('distrostable', 7) 617 self._navigate_to_config_nav('distrostable', 7)
@@ -699,7 +634,7 @@ class TestProjectPage(SeleniumFunctionalTestCase):
699 ) 634 )
700 635
701 def test_single_layer_page(self): 636 def test_single_layer_page(self):
702 """ Test layer page 637 """ Test layer details page using meta-poky as an example (assumes is added to start with)
703 - Check if title is displayed 638 - Check if title is displayed
704 - Check add/remove layer button works 639 - Check add/remove layer button works
705 - Check tabs(layers, recipes, machines) are displayed 640 - Check tabs(layers, recipes, machines) are displayed
@@ -708,45 +643,62 @@ class TestProjectPage(SeleniumFunctionalTestCase):
708 - Check layer summary 643 - Check layer summary
709 - Check layer description 644 - Check layer description
710 """ 645 """
711 url = reverse("layerdetails", args=(TestProjectPage.project_id, 8)) 646 self._navigate_to_config_nav('layerstable', 6)
712 self.get(url) 647 layer_link = self.driver.find_element(By.XPATH, '//tr/td[@class="layer__name"]/a[contains(text(),"meta-poky")]')
648 layer_link.click()
713 self.wait_until_visible('.page-header') 649 self.wait_until_visible('.page-header')
714 # check title is displayed 650 # check title is displayed
715 self.assertTrue(self.find('.page-header h1').is_displayed()) 651 self.assertTrue(self.find('.page-header h1').is_displayed())
716 652
717 # check add layer button works 653 # check remove layer button works
718 remove_layer_btn = self.find('#add-remove-layer-btn') 654 finder = lambda driver: self.find('#add-remove-layer-btn')
655 remove_layer_btn = self.wait_until_element_clickable(finder)
719 remove_layer_btn.click() 656 remove_layer_btn.click()
720 self.wait_until_visible('#change-notification', poll=2) 657 self.wait_until_visible('#change-notification')
721 change_notification = self.find('#change-notification') 658 change_notification = self.find('#change-notification')
722 self.assertTrue( 659 self.assertIn(
723 f'You have removed 1 layer from your project' in str(change_notification.text) 660 f'You have removed 1 layer from your project', str(change_notification.text)
724 ) 661 )
725 # check add layer button works, 18 is the random layer id 662 finder = lambda driver: self.find('#hide-alert')
726 add_layer_btn = self.find('#add-remove-layer-btn') 663 hide_button = self.wait_until_element_clickable(finder)
664 hide_button.click()
665 # check add layer button works
666 self.wait_until_not_visible('#change-notification')
667 finder = lambda driver: self.find('#add-remove-layer-btn')
668 add_layer_btn = self.wait_until_element_clickable(finder)
727 add_layer_btn.click() 669 add_layer_btn.click()
728 self.wait_until_visible('#change-notification') 670 self.wait_until_visible('#change-notification')
729 change_notification = self.find('#change-notification') 671 change_notification = self.find('#change-notification')
730 self.assertTrue( 672 self.assertIn(
731 f'You have added 1 layer to your project' in str(change_notification.text) 673 f'You have added 1 layer to your project', str(change_notification.text)
732 ) 674 )
675 finder = lambda driver: self.find('#hide-alert')
676 hide_button = self.wait_until_element_clickable(finder)
677 hide_button.click()
678 self.wait_until_not_visible('#change-notification')
733 # check tabs(layers, recipes, machines) are displayed 679 # check tabs(layers, recipes, machines) are displayed
734 tabs = self.find_all('.nav-tabs li') 680 tabs = self.find_all('.nav-tabs li')
735 self.assertEqual(len(tabs), 3) 681 self.assertEqual(len(tabs), 3)
736 # Check first tab 682 # Check first tab
737 tabs[0].click() 683 tabs[0].click()
738 self.assertTrue( 684 self.assertIn(
739 'active' in str(self.find('#information').get_attribute('class')) 685 'active', str(self.find('#information').get_attribute('class'))
740 ) 686 )
741 # Check second tab 687 # Check second tab (recipes)
688 self.wait_until_visible('.nav-tabs')
689 # Ensure page is scrolled to the top
690 self.driver.execute_script('window.scrollTo({behavior: "instant", top: 0, left: 0})')
742 tabs[1].click() 691 tabs[1].click()
743 self.assertTrue( 692 self.assertIn(
744 'active' in str(self.find('#recipes').get_attribute('class')) 693 'active', str(self.find('#recipes').get_attribute('class'))
745 ) 694 )
746 # Check third tab 695 # Check third tab (machines)
696 self.wait_until_visible('.nav-tabs')
697 # Ensure page is scrolled to the top
698 self.driver.execute_script('window.scrollTo({behavior: "instant", top: 0, left: 0})')
747 tabs[2].click() 699 tabs[2].click()
748 self.assertTrue( 700 self.assertIn(
749 'active' in str(self.find('#machines').get_attribute('class')) 701 'active', str(self.find('#machines').get_attribute('class'))
750 ) 702 )
751 # Check left section is displayed 703 # Check left section is displayed
752 section = self.find('.well') 704 section = self.find('.well')
@@ -755,9 +707,13 @@ class TestProjectPage(SeleniumFunctionalTestCase):
755 section.find_element(By.XPATH, '//h2[1]').is_displayed() 707 section.find_element(By.XPATH, '//h2[1]').is_displayed()
756 ) 708 )
757 # Check layer summary 709 # Check layer summary
758 self.assertTrue("Summary" in section.text) 710 self.assertIn("Summary", section.text)
759 # Check layer description 711 # Check layer description
760 self.assertTrue("Description" in section.text) 712 self.assertIn("Description", section.text)
713
714@pytest.mark.django_db
715@pytest.mark.order("last")
716class TestProjectPageRecipes(TestProjectPageBase):
761 717
762 def test_single_recipe_page(self): 718 def test_single_recipe_page(self):
763 """ Test recipe page 719 """ Test recipe page
@@ -767,7 +723,12 @@ class TestProjectPage(SeleniumFunctionalTestCase):
767 - Check recipe: name, summary, description, Version, Section, 723 - Check recipe: name, summary, description, Version, Section,
768 License, Approx. packages included, Approx. size, Recipe file 724 License, Approx. packages included, Approx. size, Recipe file
769 """ 725 """
770 url = reverse("recipedetails", args=(TestProjectPage.project_id, 53428)) 726 # Use a recipe which is likely to exist in the layer index but not enabled
727 # in poky out the box - xen-image-minimal from meta-virtualization
728 self._navigate_to_project_page()
729 prj = Project.objects.get(pk=TestProjectPageBase.project_id)
730 recipe_id = prj.get_all_compatible_recipes().get(name="xen-image-minimal").pk
731 url = reverse("recipedetails", args=(TestProjectPageBase.project_id, recipe_id))
771 self.get(url) 732 self.get(url)
772 self.wait_until_visible('.page-header') 733 self.wait_until_visible('.page-header')
773 # check title is displayed 734 # check title is displayed
@@ -782,11 +743,33 @@ class TestProjectPage(SeleniumFunctionalTestCase):
782 section.find_element(By.XPATH, '//h2[1]').is_displayed() 743 section.find_element(By.XPATH, '//h2[1]').is_displayed()
783 ) 744 )
784 # Check recipe sections details info are displayed 745 # Check recipe sections details info are displayed
785 self.assertTrue("Summary" in section.text) 746 self.assertIn("Summary", section.text)
786 self.assertTrue("Description" in section.text) 747 self.assertIn("Description", section.text)
787 self.assertTrue("Version" in section.text) 748 self.assertIn("Version", section.text)
788 self.assertTrue("Section" in section.text) 749 self.assertIn("Section", section.text)
789 self.assertTrue("License" in section.text) 750 self.assertIn("License", section.text)
790 self.assertTrue("Approx. packages included" in section.text) 751 self.assertIn("Approx. packages included", section.text)
791 self.assertTrue("Approx. package size" in section.text) 752 self.assertIn("Approx. package size", section.text)
792 self.assertTrue("Recipe file" in section.text) 753 self.assertIn("Recipe file", section.text)
754
755 def test_image_recipe_editColumn(self):
756 """ Test the edit column feature in image recipe table on project page """
757 self._get_create_builds(success=10, failure=10)
758
759 url = reverse('projectimagerecipes', args=(TestProjectPageBase.project_id,))
760 self.get(url)
761 self.wait_until_present('#imagerecipestable tbody tr')
762
763 column_list = [
764 'get_description_or_summary', 'layer_version__get_vcs_reference',
765 'layer_version__layer__name', 'license', 'recipe-file', 'section',
766 'version'
767 ]
768
769 # Check that we can hide the edit column
770 self._mixin_test_table_edit_column(
771 'imagerecipestable',
772 'edit-columns-button',
773 [f'checkbox-{column}' for column in column_list]
774 )
775
diff --git a/bitbake/lib/toaster/tests/functional/test_project_page_tab_config.py b/bitbake/lib/toaster/tests/functional/test_project_page_tab_config.py
index eb905ddf3f..80c53e1544 100644
--- a/bitbake/lib/toaster/tests/functional/test_project_page_tab_config.py
+++ b/bitbake/lib/toaster/tests/functional/test_project_page_tab_config.py
@@ -7,72 +7,27 @@
7# 7#
8 8
9import string 9import string
10import random 10import time
11import pytest 11import pytest
12from django.urls import reverse 12from django.urls import reverse
13from selenium.webdriver import Keys 13from selenium.webdriver import Keys
14from selenium.webdriver.support.select import Select 14from selenium.webdriver.support.select import Select
15from selenium.common.exceptions import ElementClickInterceptedException, NoSuchElementException, TimeoutException 15from selenium.common.exceptions import ElementClickInterceptedException, NoSuchElementException, TimeoutException
16from orm.models import Project
17from tests.functional.functional_helpers import SeleniumFunctionalTestCase 16from tests.functional.functional_helpers import SeleniumFunctionalTestCase
18from selenium.webdriver.common.by import By 17from selenium.webdriver.common.by import By
19 18
20from .utils import get_projectId_from_url, wait_until_build, wait_until_build_cancelled 19from .utils import get_projectId_from_url, wait_until_build, wait_until_build_cancelled
21 20
22 21class TestProjectConfigTabBase(SeleniumFunctionalTestCase):
23@pytest.mark.django_db
24@pytest.mark.order("last")
25class TestProjectConfigTab(SeleniumFunctionalTestCase):
26 PROJECT_NAME = 'TestProjectConfigTab' 22 PROJECT_NAME = 'TestProjectConfigTab'
27 project_id = None 23 project_id = None
28 24
29 def _create_project(self, project_name, **kwargs):
30 """ Create/Test new project using:
31 - Project Name: Any string
32 - Release: Any string
33 - Merge Toaster settings: True or False
34 """
35 release = kwargs.get('release', '3')
36 self.get(reverse('newproject'))
37 self.wait_until_visible('#new-project-name')
38 self.find("#new-project-name").send_keys(project_name)
39 select = Select(self.find("#projectversion"))
40 select.select_by_value(release)
41
42 # check merge toaster settings
43 checkbox = self.find('.checkbox-mergeattr')
44 if not checkbox.is_selected():
45 checkbox.click()
46
47 if self.PROJECT_NAME != 'TestProjectConfigTab':
48 # Reset project name if it's not the default one
49 self.PROJECT_NAME = 'TestProjectConfigTab'
50
51 self.find("#create-project-button").click()
52
53 try:
54 self.wait_until_visible('#hint-error-project-name', poll=3)
55 url = reverse('project', args=(TestProjectConfigTab.project_id, ))
56 self.get(url)
57 self.wait_until_visible('#config-nav', poll=3)
58 except TimeoutException:
59 self.wait_until_visible('#config-nav', poll=3)
60
61 def _random_string(self, length):
62 return ''.join(
63 random.choice(string.ascii_letters) for _ in range(length)
64 )
65
66 def _navigate_to_project_page(self): 25 def _navigate_to_project_page(self):
67 # Navigate to project page 26 # Navigate to project page
68 if TestProjectConfigTab.project_id is None: 27 if TestProjectConfigTabBase.project_id is None:
69 self._create_project(project_name=self._random_string(10)) 28 TestProjectConfigTabBase.project_id = self.create_new_project(self.PROJECT_NAME, '3', None, True)
70 current_url = self.driver.current_url 29 url = reverse('project', args=(TestProjectConfigTabBase.project_id,))
71 TestProjectConfigTab.project_id = get_projectId_from_url( 30 self.get(url)
72 current_url)
73 else:
74 url = reverse('project', args=(TestProjectConfigTab.project_id,))
75 self.get(url)
76 self.wait_until_visible('#config-nav') 31 self.wait_until_visible('#config-nav')
77 32
78 def _create_builds(self): 33 def _create_builds(self):
@@ -88,8 +43,8 @@ class TestProjectConfigTab(SeleniumFunctionalTestCase):
88 '//div[@id="latest-builds"]/div', 43 '//div[@id="latest-builds"]/div',
89 ) 44 )
90 last_build = lastest_builds[0] 45 last_build = lastest_builds[0]
91 self.assertTrue( 46 self.assertIn(
92 'foo' in str(last_build.text) 47 'foo', str(last_build.text)
93 ) 48 )
94 last_build = lastest_builds[0] 49 last_build = lastest_builds[0]
95 try: 50 try:
@@ -114,6 +69,8 @@ class TestProjectConfigTab(SeleniumFunctionalTestCase):
114 config_nav = self.find('#config-nav') 69 config_nav = self.find('#config-nav')
115 return config_nav.find_elements(By.TAG_NAME, 'li')[index] 70 return config_nav.find_elements(By.TAG_NAME, 'li')[index]
116 71
72class TestProjectConfigTab(TestProjectConfigTabBase):
73
117 def test_project_config_nav(self): 74 def test_project_config_nav(self):
118 """ Test project config tab navigation: 75 """ Test project config tab navigation:
119 - Check if the menu is displayed and contains the right elements: 76 - Check if the menu is displayed and contains the right elements:
@@ -138,48 +95,48 @@ class TestProjectConfigTab(SeleniumFunctionalTestCase):
138 95
139 def check_config_nav_item(index, item_name, url): 96 def check_config_nav_item(index, item_name, url):
140 item = _get_config_nav_item(index) 97 item = _get_config_nav_item(index)
141 self.assertTrue(item_name in item.text) 98 self.assertIn(item_name, item.text)
142 self.assertTrue(item.get_attribute('class') == 'active') 99 self.assertEqual(item.get_attribute('class'), 'active')
143 self.assertTrue(url in self.driver.current_url) 100 self.assertIn(url, self.driver.current_url)
144 101
145 # check if the menu contains the right elements 102 # check if the menu contains the right elements
146 # COMPATIBLE METADATA 103 # COMPATIBLE METADATA
147 compatible_metadata = _get_config_nav_item(1) 104 compatible_metadata = _get_config_nav_item(1)
148 self.assertTrue( 105 self.assertIn(
149 "compatible metadata" in compatible_metadata.text.lower() 106 "compatible metadata", compatible_metadata.text.lower()
150 ) 107 )
151 # EXTRA CONFIGURATION 108 # EXTRA CONFIGURATION
152 extra_configuration = _get_config_nav_item(8) 109 extra_configuration = _get_config_nav_item(8)
153 self.assertTrue( 110 self.assertIn(
154 "extra configuration" in extra_configuration.text.lower() 111 "extra configuration", extra_configuration.text.lower()
155 ) 112 )
156 # Actions 113 # Actions
157 actions = _get_config_nav_item(10) 114 actions = _get_config_nav_item(10)
158 self.assertTrue("actions" in str(actions.text).lower()) 115 self.assertIn("actions", str(actions.text).lower())
159 116
160 conf_nav_list = [ 117 conf_nav_list = [
161 # config 118 # config
162 [0, 'Configuration', 119 [0, 'Configuration',
163 f"/toastergui/project/{TestProjectConfigTab.project_id}"], 120 f"/toastergui/project/{TestProjectConfigTabBase.project_id}"],
164 # custom images 121 # custom images
165 [2, 'Custom images', 122 [2, 'Custom images',
166 f"/toastergui/project/{TestProjectConfigTab.project_id}/customimages"], 123 f"/toastergui/project/{TestProjectConfigTabBase.project_id}/customimages"],
167 # image recipes 124 # image recipes
168 [3, 'Image recipes', 125 [3, 'Image recipes',
169 f"/toastergui/project/{TestProjectConfigTab.project_id}/images"], 126 f"/toastergui/project/{TestProjectConfigTabBase.project_id}/images"],
170 # software recipes 127 # software recipes
171 [4, 'Software recipes', 128 [4, 'Software recipes',
172 f"/toastergui/project/{TestProjectConfigTab.project_id}/softwarerecipes"], 129 f"/toastergui/project/{TestProjectConfigTabBase.project_id}/softwarerecipes"],
173 # machines 130 # machines
174 [5, 'Machines', 131 [5, 'Machines',
175 f"/toastergui/project/{TestProjectConfigTab.project_id}/machines"], 132 f"/toastergui/project/{TestProjectConfigTabBase.project_id}/machines"],
176 # layers 133 # layers
177 [6, 'Layers', 134 [6, 'Layers',
178 f"/toastergui/project/{TestProjectConfigTab.project_id}/layers"], 135 f"/toastergui/project/{TestProjectConfigTabBase.project_id}/layers"],
179 # distro 136 # distro
180 [7, 'Distros', 137 [7, 'Distros',
181 f"/toastergui/project/{TestProjectConfigTab.project_id}/distros"], 138 f"/toastergui/project/{TestProjectConfigTabBase.project_id}/distros"],
182 # [9, 'BitBake variables', f"/toastergui/project/{TestProjectConfigTab.project_id}/configuration"], # bitbake variables 139 # [9, 'BitBake variables', f"/toastergui/project/{TestProjectConfigTabBase.project_id}/configuration"], # bitbake variables
183 ] 140 ]
184 for index, item_name, url in conf_nav_list: 141 for index, item_name, url in conf_nav_list:
185 item = _get_config_nav_item(index) 142 item = _get_config_nav_item(index)
@@ -253,7 +210,7 @@ class TestProjectConfigTab(SeleniumFunctionalTestCase):
253 def test_show_rows(row_to_show, show_row_link): 210 def test_show_rows(row_to_show, show_row_link):
254 # Check that we can show rows == row_to_show 211 # Check that we can show rows == row_to_show
255 show_row_link.select_by_value(str(row_to_show)) 212 show_row_link.select_by_value(str(row_to_show))
256 self.wait_until_visible('#imagerecipestable tbody tr', poll=3) 213 self.wait_until_visible('#imagerecipestable tbody tr')
257 # check at least some rows are visible 214 # check at least some rows are visible
258 self.assertTrue( 215 self.assertTrue(
259 len(self.find_all('#imagerecipestable tbody tr')) > 0 216 len(self.find_all('#imagerecipestable tbody tr')) > 0
@@ -299,9 +256,11 @@ class TestProjectConfigTab(SeleniumFunctionalTestCase):
299 - meta-poky 256 - meta-poky
300 - meta-yocto-bsp 257 - meta-yocto-bsp
301 """ 258 """
302 # Create a new project for this test 259 project_id = self.create_new_project(self.PROJECT_NAME + "-ST", '3', None, True)
303 project_name = self._random_string(10) 260 url = reverse('project', args=(project_id,))
304 self._create_project(project_name=project_name) 261 self.get(url)
262 self.wait_until_visible('#config-nav')
263
305 # check if the menu is displayed 264 # check if the menu is displayed
306 self.wait_until_visible('#project-page') 265 self.wait_until_visible('#project-page')
307 block_l = self.driver.find_element( 266 block_l = self.driver.find_element(
@@ -313,7 +272,7 @@ class TestProjectConfigTab(SeleniumFunctionalTestCase):
313 def check_machine_distro(self, item_name, new_item_name, block_id): 272 def check_machine_distro(self, item_name, new_item_name, block_id):
314 block = self.find(f'#{block_id}') 273 block = self.find(f'#{block_id}')
315 title = block.find_element(By.TAG_NAME, 'h3') 274 title = block.find_element(By.TAG_NAME, 'h3')
316 self.assertTrue(item_name.capitalize() in title.text) 275 self.assertIn(item_name.capitalize(), title.text)
317 edit_btn = self.find(f'#change-{item_name}-toggle') 276 edit_btn = self.find(f'#change-{item_name}-toggle')
318 edit_btn.click() 277 edit_btn.click()
319 self.wait_until_visible(f'#{item_name}-change-input') 278 self.wait_until_visible(f'#{item_name}-change-input')
@@ -324,12 +283,15 @@ class TestProjectConfigTab(SeleniumFunctionalTestCase):
324 change_btn.click() 283 change_btn.click()
325 self.wait_until_visible(f'#project-{item_name}-name') 284 self.wait_until_visible(f'#project-{item_name}-name')
326 project_name = self.find(f'#project-{item_name}-name') 285 project_name = self.find(f'#project-{item_name}-name')
327 self.assertTrue(new_item_name in project_name.text) 286 self.assertIn(new_item_name, project_name.text)
328 # check change notificaiton is displayed 287 # check change notificaiton is displayed
329 change_notification = self.find('#change-notification') 288 change_notification = self.find('#change-notification')
330 self.assertTrue( 289 self.assertIn(
331 f'You have changed the {item_name} to: {new_item_name}' in change_notification.text 290 f'You have changed the {item_name} to: {new_item_name}', change_notification.text
332 ) 291 )
292 hide_button = self.find('#hide-alert')
293 hide_button.click()
294 self.wait_until_not_visible('#change-notification')
333 295
334 # Machine 296 # Machine
335 check_machine_distro(self, 'machine', 'qemux86-64', 'machine-section') 297 check_machine_distro(self, 'machine', 'qemux86-64', 'machine-section')
@@ -338,97 +300,51 @@ class TestProjectConfigTab(SeleniumFunctionalTestCase):
338 300
339 # Project release 301 # Project release
340 title = project_release.find_element(By.TAG_NAME, 'h3') 302 title = project_release.find_element(By.TAG_NAME, 'h3')
341 self.assertTrue("Project release" in title.text) 303 self.assertIn("Project release", title.text)
342 self.assertTrue( 304 self.assertIn(
343 "Yocto Project master" in self.find('#project-release-title').text 305 "Yocto Project master", self.find('#project-release-title').text
344 ) 306 )
345 # Layers 307 # Layers
346 title = layers.find_element(By.TAG_NAME, 'h3') 308 title = layers.find_element(By.TAG_NAME, 'h3')
347 self.assertTrue("Layers" in title.text) 309 self.assertIn("Layers", title.text)
310 self.wait_until_clickable('#layer-add-input')
348 # check at least three layers are displayed 311 # check at least three layers are displayed
349 # openembedded-core 312 # openembedded-core
350 # meta-poky 313 # meta-poky
351 # meta-yocto-bsp 314 # meta-yocto-bsp
352 layers_list = layers.find_element(By.ID, 'layers-in-project-list') 315 layer_list_items = []
353 layers_list_items = layers_list.find_elements(By.TAG_NAME, 'li') 316 starttime = time.time()
317 while len(layer_list_items) < 3:
318 layers_list = self.driver.find_element(By.ID, 'layers-in-project-list')
319 layer_list_items = layers_list.find_elements(By.TAG_NAME, 'li')
320 if time.time() > (starttime + 30):
321 self.fail("Layer list didn't contain at least 3 items within 30s (contained %d)" % len(layer_list_items))
322
354 # remove all layers except the first three layers 323 # remove all layers except the first three layers
355 for i in range(3, len(layers_list_items)): 324 for i in range(3, len(layer_list_items)):
356 layers_list_items[i].find_element(By.TAG_NAME, 'span').click() 325 layer_list_items[i].find_element(By.TAG_NAME, 'span').click()
326
357 # check can add a layer if exists 327 # check can add a layer if exists
358 add_layer_input = layers.find_element(By.ID, 'layer-add-input') 328 add_layer_input = layers.find_element(By.ID, 'layer-add-input')
359 add_layer_input.send_keys('meta-oe') 329 add_layer_input.send_keys('meta-oe')
360 self.wait_until_visible('#layer-container > form > div > span > div') 330 self.wait_until_visible('#layer-container > form > div > span > div')
361 dropdown_item = self.driver.find_element( 331 self.wait_until_visible('.dropdown-menu')
362 By.XPATH, 332 finder = lambda driver: driver.find_element(By.XPATH, '//*[@id="layer-container"]/form/div/span/div/div/div')
363 '//*[@id="layer-container"]/form/div/span/div' 333 dropdown_item = self.wait_until_element_clickable(finder)
364 ) 334 dropdown_item.click()
365 try: 335 self.wait_until_clickable('#add-layer-btn')
366 dropdown_item.click()
367 except ElementClickInterceptedException:
368 self.skipTest(
369 "layer-container dropdown item click intercepted. Element not properly visible.")
370 add_layer_btn = layers.find_element(By.ID, 'add-layer-btn') 336 add_layer_btn = layers.find_element(By.ID, 'add-layer-btn')
371 add_layer_btn.click() 337 add_layer_btn.click()
372 self.wait_until_visible('#layers-in-project-list') 338 self.wait_until_visible('#layers-in-project-list')
373 # check layer is added
374 layers_list_items = layers_list.find_elements(By.TAG_NAME, 'li')
375 self.assertTrue(len(layers_list_items) == 4)
376 339
377 def test_most_build_recipes(self): 340 # check layer is added
378 """ Test most build recipes block contains""" 341 layer_list_items = []
379 def rebuild_from_most_build_recipes(recipe_list_items): 342 starttime = time.time()
380 checkbox = recipe_list_items[0].find_element(By.TAG_NAME, 'input') 343 while len(layer_list_items) < 4:
381 checkbox.click() 344 layers_list = self.driver.find_element(By.ID, 'layers-in-project-list')
382 build_btn = self.find('#freq-build-btn') 345 layer_list_items = layers_list.find_elements(By.TAG_NAME, 'li')
383 build_btn.click() 346 if time.time() > (starttime + 30):
384 self.wait_until_visible('#latest-builds') 347 self.fail("Layer list didn't contain at least 4 items within 30s (contained %d)" % len(layer_list_items))
385 wait_until_build(self, 'queued cloning starting parsing failed')
386 lastest_builds = self.driver.find_elements(
387 By.XPATH,
388 '//div[@id="latest-builds"]/div'
389 )
390 self.assertTrue(len(lastest_builds) >= 2)
391 last_build = lastest_builds[0]
392 try:
393 cancel_button = last_build.find_element(
394 By.XPATH,
395 '//span[@class="cancel-build-btn pull-right alert-link"]',
396 )
397 cancel_button.click()
398 except NoSuchElementException:
399 # Skip if the build is already cancelled
400 pass
401 wait_until_build_cancelled(self)
402 # Create a new project for remaining asserts
403 project_name = self._random_string(10)
404 self._create_project(project_name=project_name, release='2')
405 current_url = self.driver.current_url
406 TestProjectConfigTab.project_id = get_projectId_from_url(current_url)
407 url = current_url.split('?')[0]
408
409 # Create a new builds
410 self._create_builds()
411
412 # back to project page
413 self.driver.get(url)
414
415 self.wait_until_visible('#project-page', poll=3)
416
417 # Most built recipes
418 most_built_recipes = self.driver.find_element(
419 By.XPATH, '//*[@id="project-page"]/div[1]/div[3]')
420 title = most_built_recipes.find_element(By.TAG_NAME, 'h3')
421 self.assertTrue("Most built recipes" in title.text)
422 # check can select a recipe and build it
423 self.wait_until_visible('#freq-build-list', poll=3)
424 recipe_list = self.find('#freq-build-list')
425 recipe_list_items = recipe_list.find_elements(By.TAG_NAME, 'li')
426 self.assertTrue(
427 len(recipe_list_items) > 0,
428 msg="Any recipes found in the most built recipes list",
429 )
430 rebuild_from_most_build_recipes(recipe_list_items)
431 TestProjectConfigTab.project_id = None # reset project id
432 348
433 def test_project_page_tab_importlayer(self): 349 def test_project_page_tab_importlayer(self):
434 """ Test project page tab import layer """ 350 """ Test project page tab import layer """
@@ -466,42 +382,42 @@ class TestProjectConfigTab(SeleniumFunctionalTestCase):
466 layers = block_l.find_element(By.ID, 'layer-container') 382 layers = block_l.find_element(By.ID, 'layer-container')
467 layers_list = layers.find_element(By.ID, 'layers-in-project-list') 383 layers_list = layers.find_element(By.ID, 'layers-in-project-list')
468 layers_list_items = layers_list.find_elements(By.TAG_NAME, 'li') 384 layers_list_items = layers_list.find_elements(By.TAG_NAME, 'li')
469 self.assertTrue( 385 self.assertIn(
470 'meta-fake' in str(layers_list_items[-1].text) 386 'meta-fake', str(layers_list_items[-1].text)
471 ) 387 )
472 388
473 def test_project_page_custom_image_no_image(self): 389 def test_project_page_custom_image_no_image(self):
474 """ Test project page tab "New custom image" when no custom image """ 390 """ Test project page tab "New custom image" when no custom image """
475 project_name = self._random_string(10) 391 project_id = self.create_new_project(self.PROJECT_NAME + "-CustomImage", '3', None, True)
476 self._create_project(project_name=project_name) 392 url = reverse('project', args=(project_id,))
477 current_url = self.driver.current_url 393 self.get(url)
478 TestProjectConfigTab.project_id = get_projectId_from_url(current_url) 394 self.wait_until_visible('#config-nav')
395
479 # navigate to "Custom image" tab 396 # navigate to "Custom image" tab
480 custom_image_section = self._get_config_nav_item(2) 397 custom_image_section = self._get_config_nav_item(2)
481 custom_image_section.click() 398 custom_image_section.click()
482 self.wait_until_visible('#empty-state-customimagestable') 399 self.wait_until_visible('#empty-state-customimagestable')
483 400
484 # Check message when no custom image 401 # Check message when no custom image
485 self.assertTrue( 402 self.assertIn(
486 "You have not created any custom images yet." in str( 403 "You have not created any custom images yet.", str(
487 self.find('#empty-state-customimagestable').text 404 self.find('#empty-state-customimagestable').text
488 ) 405 )
489 ) 406 )
490 div_empty_msg = self.find('#empty-state-customimagestable') 407 div_empty_msg = self.find('#empty-state-customimagestable')
491 link_create_custom_image = div_empty_msg.find_element( 408 link_create_custom_image = div_empty_msg.find_element(
492 By.TAG_NAME, 'a') 409 By.TAG_NAME, 'a')
493 self.assertTrue(TestProjectConfigTab.project_id is not None) 410 self.assertTrue(TestProjectConfigTabBase.project_id is not None)
494 self.assertTrue( 411 self.assertIn(
495 f"/toastergui/project/{TestProjectConfigTab.project_id}/newcustomimage" in str( 412 f"/toastergui/project/{project_id}/newcustomimage", str(
496 link_create_custom_image.get_attribute('href') 413 link_create_custom_image.get_attribute('href')
497 ) 414 )
498 ) 415 )
499 self.assertTrue( 416 self.assertIn(
500 "Create your first custom image" in str( 417 "Create your first custom image", str(
501 link_create_custom_image.text 418 link_create_custom_image.text
502 ) 419 )
503 ) 420 )
504 TestProjectConfigTab.project_id = None # reset project id
505 421
506 def test_project_page_image_recipe(self): 422 def test_project_page_image_recipe(self):
507 """ Test project page section images 423 """ Test project page section images
@@ -526,3 +442,66 @@ class TestProjectConfigTab(SeleniumFunctionalTestCase):
526 self.wait_until_visible('#imagerecipestable tbody tr') 442 self.wait_until_visible('#imagerecipestable tbody tr')
527 rows = self.find_all('#imagerecipestable tbody tr') 443 rows = self.find_all('#imagerecipestable tbody tr')
528 self.assertTrue(len(rows) > 0) 444 self.assertTrue(len(rows) > 0)
445
446@pytest.mark.django_db
447@pytest.mark.order("last")
448class TestProjectConfigTabDB(TestProjectConfigTabBase):
449
450 def test_most_build_recipes(self):
451 """ Test most build recipes block contains"""
452 def rebuild_from_most_build_recipes(recipe_list_items):
453 checkbox = recipe_list_items[0].find_element(By.TAG_NAME, 'input')
454 checkbox.click()
455 build_btn = self.find('#freq-build-btn')
456 build_btn.click()
457 self.wait_until_visible('#latest-builds')
458 wait_until_build(self, 'queued cloning starting parsing failed')
459 lastest_builds = self.driver.find_elements(
460 By.XPATH,
461 '//div[@id="latest-builds"]/div'
462 )
463 self.assertTrue(len(lastest_builds) >= 2)
464 last_build = lastest_builds[0]
465 try:
466 cancel_button = last_build.find_element(
467 By.XPATH,
468 '//span[@class="cancel-build-btn pull-right alert-link"]',
469 )
470 cancel_button.click()
471 except NoSuchElementException:
472 # Skip if the build is already cancelled
473 pass
474 wait_until_build_cancelled(self)
475
476 # Create a new project for remaining asserts
477 project_id = self.create_new_project(self.PROJECT_NAME + "-MostBuilt", '2', None, True)
478 url = reverse('project', args=(project_id,))
479 self.get(url)
480 self.wait_until_visible('#config-nav')
481
482 current_url = self.driver.current_url
483 url = current_url.split('?')[0]
484
485 # Create a new builds
486 self._create_builds()
487
488 # back to project page
489 self.driver.get(url)
490
491 self.wait_until_visible('#project-page')
492
493 # Most built recipes
494 most_built_recipes = self.driver.find_element(
495 By.XPATH, '//*[@id="project-page"]/div[1]/div[3]')
496 title = most_built_recipes.find_element(By.TAG_NAME, 'h3')
497 self.assertIn("Most built recipes", title.text)
498 # check can select a recipe and build it
499 self.wait_until_visible('#freq-build-list')
500 recipe_list = self.find('#freq-build-list')
501 recipe_list_items = recipe_list.find_elements(By.TAG_NAME, 'li')
502 self.assertTrue(
503 len(recipe_list_items) > 0,
504 msg="No recipes found in the most built recipes list",
505 )
506 rebuild_from_most_build_recipes(recipe_list_items)
507
diff --git a/bitbake/lib/toaster/tests/functional/utils.py b/bitbake/lib/toaster/tests/functional/utils.py
index 7269fa1805..72345aef9f 100644
--- a/bitbake/lib/toaster/tests/functional/utils.py
+++ b/bitbake/lib/toaster/tests/functional/utils.py
@@ -8,7 +8,7 @@
8 8
9 9
10from time import sleep 10from time import sleep
11from selenium.common.exceptions import NoSuchElementException, StaleElementReferenceException, TimeoutException 11from selenium.common.exceptions import NoSuchElementException, StaleElementReferenceException, TimeoutException, WebDriverException
12from selenium.webdriver.common.by import By 12from selenium.webdriver.common.by import By
13 13
14from orm.models import Build 14from orm.models import Build
@@ -36,7 +36,7 @@ def wait_until_build(test_instance, state):
36 if 'failed' in str(build_state).lower(): 36 if 'failed' in str(build_state).lower():
37 break 37 break
38 except NoSuchElementException: 38 except NoSuchElementException:
39 continue 39 pass
40 except TimeoutException: 40 except TimeoutException:
41 break 41 break
42 start_time += 1 42 start_time += 1
@@ -48,7 +48,6 @@ def wait_until_build_cancelled(test_instance):
48 """ 48 """
49 timeout = 30 49 timeout = 30
50 start_time = 0 50 start_time = 0
51 build = None
52 while True: 51 while True:
53 try: 52 try:
54 if start_time > timeout: 53 if start_time > timeout:
@@ -64,19 +63,17 @@ def wait_until_build_cancelled(test_instance):
64 if 'failed' in str(build_state).lower(): 63 if 'failed' in str(build_state).lower():
65 break 64 break
66 if 'cancelling' in str(build_state).lower(): 65 if 'cancelling' in str(build_state).lower():
67 # Change build state to cancelled 66 pass
68 if not build: # get build object only once
69 build = Build.objects.last()
70 build.outcome = Build.CANCELLED
71 build.save()
72 if 'cancelled' in str(build_state).lower(): 67 if 'cancelled' in str(build_state).lower():
73 break 68 break
74 except NoSuchElementException:
75 continue
76 except StaleElementReferenceException:
77 continue
78 except TimeoutException: 69 except TimeoutException:
79 break 70 break
71 except NoSuchElementException:
72 pass
73 except StaleElementReferenceException:
74 pass
75 except WebDriverException:
76 pass
80 start_time += 1 77 start_time += 1
81 sleep(1) # take a breath and try again 78 sleep(1) # take a breath and try again
82 79