1#! /usr/bin/env python3 2# 3# BitBake Toaster Implementation 4# 5# Copyright (C) 2013-2016 Intel Corporation 6# 7# SPDX-License-Identifier: GPL-2.0-only 8# 9 10import os 11import re 12 13from django.urls import reverse 14from django.utils import timezone 15from selenium.webdriver.support.select import Select 16from tests.browser.selenium_helpers import SeleniumTestCase 17 18from orm.models import BitbakeVersion, Release, Project, Build 19from orm.models import ProjectVariable 20 21from selenium.webdriver.common.by import By 22 23 24class TestAllProjectsPage(SeleniumTestCase): 25 """ Browser tests for projects page /projects/ """ 26 27 PROJECT_NAME = 'test project' 28 CLI_BUILDS_PROJECT_NAME = 'command line builds' 29 MACHINE_NAME = 'delorean' 30 31 def setUp(self): 32 """ Add default project manually """ 33 project = Project.objects.create_project( 34 self.CLI_BUILDS_PROJECT_NAME, None) 35 self.default_project = project 36 self.default_project.is_default = True 37 self.default_project.save() 38 39 # this project is only set for some of the tests 40 self.project = None 41 42 self.release = None 43 44 def _create_projects(self, nb_project=10): 45 projects = [] 46 for i in range(1, nb_project + 1): 47 projects.append( 48 Project( 49 name='test project {}'.format(i), 50 release=self.release, 51 ) 52 ) 53 Project.objects.bulk_create(projects) 54 55 def _add_build_to_default_project(self): 56 """ Add a build to the default project (not used in all tests) """ 57 now = timezone.now() 58 build = Build.objects.create(project=self.default_project, 59 started_on=now, 60 completed_on=now) 61 build.save() 62 63 def _add_non_default_project(self): 64 """ Add another project """ 65 builldir = os.environ.get('BUILDDIR', './') 66 bbv = BitbakeVersion.objects.create(name='test bbv', giturl=f'{builldir}/', 67 branch='master', dirpath='') 68 self.release = Release.objects.create(name='test release', 69 branch_name='master', 70 bitbake_version=bbv) 71 self.project = Project.objects.create_project( 72 self.PROJECT_NAME, self.release) 73 self.project.is_default = False 74 self.project.save() 75 76 # fake the MACHINE variable 77 project_var = ProjectVariable.objects.create(project=self.project, 78 name='MACHINE', 79 value=self.MACHINE_NAME) 80 project_var.save() 81 82 def _get_row_for_project(self, project_name): 83 """ Get the HTML row for a project, or None if not found """ 84 self.wait_until_visible('#projectstable tbody tr', poll=3) 85 rows = self.find_all('#projectstable tbody tr') 86 87 # find the row with a project name matching the one supplied 88 found_row = None 89 for row in rows: 90 if re.search(project_name, row.get_attribute('innerHTML')): 91 found_row = row 92 break 93 94 return found_row 95 96 def test_default_project_hidden(self): 97 """ 98 The default project should be hidden if it has no builds 99 and we should see the "no results" area 100 """ 101 url = reverse('all-projects') 102 self.get(url) 103 self.wait_until_visible('#empty-state-projectstable') 104 105 rows = self.find_all('#projectstable tbody tr') 106 self.assertEqual(len(rows), 0, 'should be no projects displayed') 107 108 def test_default_project_has_build(self): 109 """ The default project should be shown if it has builds """ 110 self._add_build_to_default_project() 111 112 url = reverse('all-projects') 113 self.get(url) 114 115 default_project_row = self._get_row_for_project( 116 self.default_project.name) 117 118 self.assertNotEqual(default_project_row, None, 119 'default project "cli builds" should be in page') 120 121 def test_default_project_release(self): 122 """ 123 The release for the default project should display as 124 'Not applicable' 125 """ 126 # need a build, otherwise project doesn't display at all 127 self._add_build_to_default_project() 128 129 # another project to test, which should show release 130 self._add_non_default_project() 131 132 self.get(reverse('all-projects')) 133 self.wait_until_visible("#projectstable tr") 134 135 # find the row for the default project 136 default_project_row = self._get_row_for_project( 137 self.default_project.name) 138 139 # check the release text for the default project 140 selector = 'span[data-project-field="release"] span.text-muted' 141 element = default_project_row.find_element(By.CSS_SELECTOR, selector) 142 text = element.text.strip() 143 self.assertEqual(text, 'Not applicable', 144 'release should be "not applicable" for default project') 145 146 # find the row for the default project 147 other_project_row = self._get_row_for_project(self.project.name) 148 149 # check the link in the release cell for the other project 150 selector = 'span[data-project-field="release"]' 151 element = other_project_row.find_element(By.CSS_SELECTOR, selector) 152 text = element.text.strip() 153 self.assertEqual(text, self.release.name, 154 'release name should be shown for non-default project') 155 156 def test_default_project_machine(self): 157 """ 158 The machine for the default project should display as 159 'Not applicable' 160 """ 161 # need a build, otherwise project doesn't display at all 162 self._add_build_to_default_project() 163 164 # another project to test, which should show machine 165 self._add_non_default_project() 166 167 self.get(reverse('all-projects')) 168 169 self.wait_until_visible("#projectstable tr") 170 171 # find the row for the default project 172 default_project_row = self._get_row_for_project( 173 self.default_project.name) 174 175 # check the machine cell for the default project 176 selector = 'span[data-project-field="machine"] span.text-muted' 177 element = default_project_row.find_element(By.CSS_SELECTOR, selector) 178 text = element.text.strip() 179 self.assertEqual(text, 'Not applicable', 180 'machine should be not applicable for default project') 181 182 # find the row for the default project 183 other_project_row = self._get_row_for_project(self.project.name) 184 185 # check the link in the machine cell for the other project 186 selector = 'span[data-project-field="machine"]' 187 element = other_project_row.find_element(By.CSS_SELECTOR, selector) 188 text = element.text.strip() 189 self.assertEqual(text, self.MACHINE_NAME, 190 'machine name should be shown for non-default project') 191 192 def test_project_page_links(self): 193 """ 194 Test that links for the default project point to the builds 195 page /projects/X/builds for that project, and that links for 196 other projects point to their configuration pages /projects/X/ 197 """ 198 199 # need a build, otherwise project doesn't display at all 200 self._add_build_to_default_project() 201 202 # another project to test 203 self._add_non_default_project() 204 205 self.get(reverse('all-projects')) 206 207 # find the row for the default project 208 default_project_row = self._get_row_for_project( 209 self.default_project.name) 210 211 # check the link on the name field 212 selector = 'span[data-project-field="name"] a' 213 element = default_project_row.find_element(By.CSS_SELECTOR, selector) 214 link_url = element.get_attribute('href').strip() 215 expected_url = reverse( 216 'projectbuilds', args=(self.default_project.id,)) 217 msg = 'link on default project name should point to builds but was %s' % link_url 218 self.assertTrue(link_url.endswith(expected_url), msg) 219 220 # find the row for the other project 221 other_project_row = self._get_row_for_project(self.project.name) 222 223 # check the link for the other project 224 selector = 'span[data-project-field="name"] a' 225 element = other_project_row.find_element(By.CSS_SELECTOR, selector) 226 link_url = element.get_attribute('href').strip() 227 expected_url = reverse('project', args=(self.project.id,)) 228 msg = 'link on project name should point to configuration but was %s' % link_url 229 self.assertTrue(link_url.endswith(expected_url), msg) 230 231 def test_allProject_table_search_box(self): 232 """ Test the search box in the all project table on the all projects page """ 233 self._create_projects() 234 235 url = reverse('all-projects') 236 self.get(url) 237 238 # Chseck search box is present and works 239 self.wait_until_visible('#projectstable tbody tr', poll=3) 240 search_box = self.find('#search-input-projectstable') 241 self.assertTrue(search_box.is_displayed()) 242 243 # Check that we can search for a project by project name 244 search_box.send_keys('test project 10') 245 search_btn = self.find('#search-submit-projectstable') 246 search_btn.click() 247 self.wait_until_visible('#projectstable tbody tr', poll=3) 248 rows = self.find_all('#projectstable tbody tr') 249 self.assertTrue(len(rows) == 1) 250 251 def test_allProject_table_editColumn(self): 252 """ Test the edit column feature in the projects table on the all projects page """ 253 self._create_projects() 254 255 def test_edit_column(check_box_id): 256 # Check that we can hide/show table column 257 check_box = self.find(f'#{check_box_id}') 258 th_class = str(check_box_id).replace('checkbox-', '') 259 if check_box.is_selected(): 260 # check if column is visible in table 261 self.assertTrue( 262 self.find( 263 f'#projectstable thead th.{th_class}' 264 ).is_displayed(), 265 f"The {th_class} column is checked in EditColumn dropdown, but it's not visible in table" 266 ) 267 check_box.click() 268 # check if column is hidden in table 269 self.assertFalse( 270 self.find( 271 f'#projectstable thead th.{th_class}' 272 ).is_displayed(), 273 f"The {th_class} column is unchecked in EditColumn dropdown, but it's visible in table" 274 ) 275 else: 276 # check if column is hidden in table 277 self.assertFalse( 278 self.find( 279 f'#projectstable thead th.{th_class}' 280 ).is_displayed(), 281 f"The {th_class} column is unchecked in EditColumn dropdown, but it's visible in table" 282 ) 283 check_box.click() 284 # check if column is visible in table 285 self.assertTrue( 286 self.find( 287 f'#projectstable thead th.{th_class}' 288 ).is_displayed(), 289 f"The {th_class} column is checked in EditColumn dropdown, but it's not visible in table" 290 ) 291 url = reverse('all-projects') 292 self.get(url) 293 self.wait_until_visible('#projectstable tbody tr', poll=3) 294 295 # Check edit column 296 edit_column = self.find('#edit-columns-button') 297 self.assertTrue(edit_column.is_displayed()) 298 edit_column.click() 299 # Check dropdown is visible 300 self.wait_until_visible('ul.dropdown-menu.editcol') 301 302 # Check that we can hide the edit column 303 test_edit_column('checkbox-errors') 304 test_edit_column('checkbox-image_files') 305 test_edit_column('checkbox-last_build_outcome') 306 test_edit_column('checkbox-recipe_name') 307 test_edit_column('checkbox-warnings') 308 309 def test_allProject_table_show_rows(self): 310 """ Test the show rows feature in the projects table on the all projects page """ 311 self._create_projects(nb_project=200) 312 313 def test_show_rows(row_to_show, show_row_link): 314 # Check that we can show rows == row_to_show 315 show_row_link.select_by_value(str(row_to_show)) 316 self.wait_until_visible('#projectstable tbody tr', poll=3) 317 # check at least some rows are visible 318 self.assertTrue( 319 len(self.find_all('#projectstable tbody tr')) > 0 320 ) 321 322 url = reverse('all-projects') 323 self.get(url) 324 self.wait_until_visible('#projectstable tbody tr', poll=3) 325 326 show_rows = self.driver.find_elements( 327 By.XPATH, 328 '//select[@class="form-control pagesize-projectstable"]' 329 ) 330 # Check show rows 331 for show_row_link in show_rows: 332 show_row_link = Select(show_row_link) 333 test_show_rows(10, show_row_link) 334 test_show_rows(25, show_row_link) 335 test_show_rows(50, show_row_link) 336 test_show_rows(100, show_row_link) 337 test_show_rows(150, show_row_link) 338