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 re 11 12from django.urls import reverse 13from django.utils import timezone 14from tests.browser.selenium_helpers import SeleniumTestCase 15 16from orm.models import BitbakeVersion, Release, Project, Build, Target 17 18 19class TestAllBuildsPage(SeleniumTestCase): 20 """ Tests for all builds page /builds/ """ 21 22 PROJECT_NAME = 'test project' 23 CLI_BUILDS_PROJECT_NAME = 'command line builds' 24 25 def setUp(self): 26 bbv = BitbakeVersion.objects.create(name='bbv1', giturl='/tmp/', 27 branch='master', dirpath='') 28 release = Release.objects.create(name='release1', 29 bitbake_version=bbv) 30 self.project1 = Project.objects.create_project(name=self.PROJECT_NAME, 31 release=release) 32 self.default_project = Project.objects.create_project( 33 name=self.CLI_BUILDS_PROJECT_NAME, 34 release=release 35 ) 36 self.default_project.is_default = True 37 self.default_project.save() 38 39 # parameters for builds to associate with the projects 40 now = timezone.now() 41 42 self.project1_build_success = { 43 'project': self.project1, 44 'started_on': now, 45 'completed_on': now, 46 'outcome': Build.SUCCEEDED 47 } 48 49 self.project1_build_failure = { 50 'project': self.project1, 51 'started_on': now, 52 'completed_on': now, 53 'outcome': Build.FAILED 54 } 55 56 self.default_project_build_success = { 57 'project': self.default_project, 58 'started_on': now, 59 'completed_on': now, 60 'outcome': Build.SUCCEEDED 61 } 62 63 def _get_build_time_element(self, build): 64 """ 65 Return the HTML element containing the build time for a build 66 in the recent builds area 67 """ 68 selector = 'div[data-latest-build-result="%s"] ' \ 69 '[data-role="data-recent-build-buildtime-field"]' % build.id 70 71 # because this loads via Ajax, wait for it to be visible 72 self.wait_until_present(selector) 73 74 build_time_spans = self.find_all(selector) 75 76 self.assertEqual(len(build_time_spans), 1) 77 78 return build_time_spans[0] 79 80 def _get_row_for_build(self, build): 81 """ Get the table row for the build from the all builds table """ 82 self.wait_until_present('#allbuildstable') 83 84 rows = self.find_all('#allbuildstable tr') 85 86 # look for the row with a download link on the recipe which matches the 87 # build ID 88 url = reverse('builddashboard', args=(build.id,)) 89 selector = 'td.target a[href="%s"]' % url 90 91 found_row = None 92 for row in rows: 93 94 outcome_links = row.find_elements_by_css_selector(selector) 95 if len(outcome_links) == 1: 96 found_row = row 97 break 98 99 self.assertNotEqual(found_row, None) 100 101 return found_row 102 103 def test_show_tasks_with_suffix(self): 104 """ Task should be shown as suffix on build name """ 105 build = Build.objects.create(**self.project1_build_success) 106 target = 'bash' 107 task = 'clean' 108 Target.objects.create(build=build, target=target, task=task) 109 110 url = reverse('all-builds') 111 self.get(url) 112 self.wait_until_present('td[class="target"]') 113 114 cell = self.find('td[class="target"]') 115 content = cell.get_attribute('innerHTML') 116 expected_text = '%s:%s' % (target, task) 117 118 self.assertTrue(re.search(expected_text, content), 119 '"target" cell should contain text %s' % expected_text) 120 121 def test_rebuild_buttons(self): 122 """ 123 Test 'Rebuild' buttons in recent builds section 124 125 'Rebuild' button should not be shown for command-line builds, 126 but should be shown for other builds 127 """ 128 build1 = Build.objects.create(**self.project1_build_success) 129 default_build = Build.objects.create(**self.default_project_build_success) 130 131 url = reverse('all-builds') 132 self.get(url) 133 134 # shouldn't see a rebuild button for command-line builds 135 selector = 'div[data-latest-build-result="%s"] .rebuild-btn' % default_build.id 136 run_again_button = self.find_all(selector) 137 self.assertEqual(len(run_again_button), 0, 138 'should not see a rebuild button for cli builds') 139 140 # should see a rebuild button for non-command-line builds 141 selector = 'div[data-latest-build-result="%s"] .rebuild-btn' % build1.id 142 run_again_button = self.find_all(selector) 143 self.assertEqual(len(run_again_button), 1, 144 'should see a rebuild button for non-cli builds') 145 146 def test_tooltips_on_project_name(self): 147 """ 148 Test tooltips shown next to project name in the main table 149 150 A tooltip should be present next to the command line 151 builds project name in the all builds page, but not for 152 other projects 153 """ 154 Build.objects.create(**self.project1_build_success) 155 Build.objects.create(**self.default_project_build_success) 156 157 url = reverse('all-builds') 158 self.get(url) 159 160 # get the project name cells from the table 161 cells = self.find_all('#allbuildstable td[class="project"]') 162 163 selector = 'span.get-help' 164 165 for cell in cells: 166 content = cell.get_attribute('innerHTML') 167 help_icons = cell.find_elements_by_css_selector(selector) 168 169 if re.search(self.PROJECT_NAME, content): 170 # no help icon next to non-cli project name 171 msg = 'should not be a help icon for non-cli builds name' 172 self.assertEqual(len(help_icons), 0, msg) 173 elif re.search(self.CLI_BUILDS_PROJECT_NAME, content): 174 # help icon next to cli project name 175 msg = 'should be a help icon for cli builds name' 176 self.assertEqual(len(help_icons), 1, msg) 177 else: 178 msg = 'found unexpected project name cell in all builds table' 179 self.fail(msg) 180 181 def test_builds_time_links(self): 182 """ 183 Successful builds should have links on the time column and in the 184 recent builds area; failed builds should not have links on the time column, 185 or in the recent builds area 186 """ 187 build1 = Build.objects.create(**self.project1_build_success) 188 build2 = Build.objects.create(**self.project1_build_failure) 189 190 # add some targets to these builds so they have recipe links 191 # (and so we can find the row in the ToasterTable corresponding to 192 # a particular build) 193 Target.objects.create(build=build1, target='foo') 194 Target.objects.create(build=build2, target='bar') 195 196 url = reverse('all-builds') 197 self.get(url) 198 199 # test recent builds area for successful build 200 element = self._get_build_time_element(build1) 201 links = element.find_elements_by_css_selector('a') 202 msg = 'should be a link on the build time for a successful recent build' 203 self.assertEquals(len(links), 1, msg) 204 205 # test recent builds area for failed build 206 element = self._get_build_time_element(build2) 207 links = element.find_elements_by_css_selector('a') 208 msg = 'should not be a link on the build time for a failed recent build' 209 self.assertEquals(len(links), 0, msg) 210 211 # test the time column for successful build 212 build1_row = self._get_row_for_build(build1) 213 links = build1_row.find_elements_by_css_selector('td.time a') 214 msg = 'should be a link on the build time for a successful build' 215 self.assertEquals(len(links), 1, msg) 216 217 # test the time column for failed build 218 build2_row = self._get_row_for_build(build2) 219 links = build2_row.find_elements_by_css_selector('td.time a') 220 msg = 'should not be a link on the build time for a failed build' 221 self.assertEquals(len(links), 0, msg) 222