1#! /usr/bin/env python3 2# 3# BitBake Toaster Implementation 4# 5# SPDX-License-Identifier: GPL-2.0-only 6# 7# Copyright (C) 2013-2016 Intel Corporation 8# 9 10from django.urls import reverse 11from selenium.common.exceptions import ElementClickInterceptedException, TimeoutException 12from tests.browser.selenium_helpers import SeleniumTestCase 13 14from orm.models import Layer, Layer_Version, Project, LayerSource, Release 15from orm.models import BitbakeVersion 16 17from selenium.webdriver.support import expected_conditions as EC 18from selenium.webdriver.support.ui import WebDriverWait 19from selenium.webdriver.common.by import By 20 21 22class TestLayerDetailsPage(SeleniumTestCase): 23 """ Test layerdetails page works correctly """ 24 25 def __init__(self, *args, **kwargs): 26 super(TestLayerDetailsPage, self).__init__(*args, **kwargs) 27 28 self.initial_values = None 29 self.url = None 30 self.imported_layer_version = None 31 32 def setUp(self): 33 release = Release.objects.create( 34 name='baz', 35 bitbake_version=BitbakeVersion.objects.create(name='v1') 36 ) 37 38 # project to add new custom images to 39 self.project = Project.objects.create(name='foo', release=release) 40 41 name = "meta-imported" 42 vcs_url = "git://example.com/meta-imported" 43 subdir = "/layer" 44 gitrev = "d33d" 45 summary = "A imported layer" 46 description = "This was imported" 47 48 imported_layer = Layer.objects.create(name=name, 49 vcs_url=vcs_url, 50 summary=summary, 51 description=description) 52 53 self.imported_layer_version = Layer_Version.objects.create( 54 layer=imported_layer, 55 layer_source=LayerSource.TYPE_IMPORTED, 56 branch=gitrev, 57 commit=gitrev, 58 dirpath=subdir, 59 project=self.project) 60 61 self.initial_values = [name, vcs_url, subdir, gitrev, summary, 62 description] 63 self.url = reverse('layerdetails', 64 args=(self.project.pk, 65 self.imported_layer_version.pk)) 66 67 def _edit_layerdetails(self): 68 """ Edit all the editable fields for the layer refresh the page and 69 check that the new values exist""" 70 71 self.get(self.url) 72 self.wait_until_visible("#add-remove-layer-btn") 73 74 self.click("#add-remove-layer-btn") 75 self.click("#edit-layer-source") 76 self.click("#repo") 77 78 self.wait_until_visible("#layer-git-repo-url") 79 80 # Open every edit box 81 for btn in self.find_all("dd .glyphicon-edit"): 82 btn.click() 83 84 # Wait for the inputs to become visible after animation 85 self.wait_until_visible("#layer-git input[type=text]") 86 self.wait_until_visible("dd textarea") 87 self.wait_until_visible("dd .change-btn") 88 89 # Edit each value 90 for inputs in self.find_all("#layer-git input[type=text]") + \ 91 self.find_all("dd textarea"): 92 # ignore the tt inputs (twitter typeahead input) 93 if "tt-" in inputs.get_attribute("class"): 94 continue 95 96 value = inputs.get_attribute("value") 97 98 self.assertTrue(value in self.initial_values, 99 "Expecting any of \"%s\"but got \"%s\"" % 100 (self.initial_values, value)) 101 102 # Make sure the input visible beofre sending keys 103 self.wait_until_visible("#layer-git input[type=text]") 104 inputs.send_keys("-edited") 105 106 # Save the new values 107 for save_btn in self.find_all(".change-btn"): 108 save_btn.click() 109 110 try: 111 self.wait_until_visible("#save-changes-for-switch", poll=3) 112 btn_save_chg_for_switch = self.wait_until_clickable( 113 "#save-changes-for-switch", poll=3) 114 btn_save_chg_for_switch.click() 115 except ElementClickInterceptedException: 116 self.skipTest( 117 "save-changes-for-switch click intercepted. Element not visible or maybe covered by another element.") 118 except TimeoutException: 119 self.skipTest( 120 "save-changes-for-switch is not clickable within the specified timeout.") 121 122 self.wait_until_visible("#edit-layer-source") 123 124 # Refresh the page to see if the new values are returned 125 self.get(self.url) 126 127 new_values = ["%s-edited" % old_val 128 for old_val in self.initial_values] 129 130 for inputs in self.find_all('#layer-git input[type="text"]') + \ 131 self.find_all('dd textarea'): 132 # ignore the tt inputs (twitter typeahead input) 133 if "tt-" in inputs.get_attribute("class"): 134 continue 135 136 value = inputs.get_attribute("value") 137 138 self.assertTrue(value in new_values, 139 "Expecting any of \"%s\" but got \"%s\"" % 140 (new_values, value)) 141 142 # Now convert it to a local layer 143 self.click("#edit-layer-source") 144 self.click("#dir") 145 dir_input = self.wait_until_visible("#layer-dir-path-in-details") 146 147 new_dir = "/home/test/my-meta-dir" 148 dir_input.send_keys(new_dir) 149 150 try: 151 self.wait_until_visible("#save-changes-for-switch", poll=3) 152 btn_save_chg_for_switch = self.wait_until_clickable( 153 "#save-changes-for-switch", poll=3) 154 btn_save_chg_for_switch.click() 155 except ElementClickInterceptedException: 156 self.skipTest( 157 "save-changes-for-switch click intercepted. Element not properly visible or maybe behind another element.") 158 except TimeoutException: 159 self.skipTest( 160 "save-changes-for-switch is not clickable within the specified timeout.") 161 162 self.wait_until_visible("#edit-layer-source") 163 164 # Refresh the page to see if the new values are returned 165 self.get(self.url) 166 dir_input = self.find("#layer-dir-path-in-details") 167 self.assertTrue(new_dir in dir_input.get_attribute("value"), 168 "Expected %s in the dir value for layer directory" % 169 new_dir) 170 171 def test_edit_layerdetails_page(self): 172 try: 173 self._edit_layerdetails() 174 except ElementClickInterceptedException: 175 self.skipTest( 176 "ElementClickInterceptedException occured. Element not visible or maybe covered by another element.") 177 178 def test_delete_layer(self): 179 """ Delete the layer """ 180 181 self.get(self.url) 182 183 # Wait for the tables to load to avoid a race condition where the 184 # toaster tables have made an async request. If the layer is deleted 185 # before the request finishes it will cause an exception and fail this 186 # test. 187 wait = WebDriverWait(self.driver, 30) 188 189 wait.until(EC.text_to_be_present_in_element( 190 (By.CLASS_NAME, 191 "table-count-recipestable"), "0")) 192 193 wait.until(EC.text_to_be_present_in_element( 194 (By.CLASS_NAME, 195 "table-count-machinestable"), "0")) 196 197 self.click('a[data-target="#delete-layer-modal"]') 198 self.wait_until_visible("#delete-layer-modal") 199 self.click("#layer-delete-confirmed") 200 201 notification = self.wait_until_visible("#change-notification-msg") 202 expected_text = "You have deleted 1 layer from your project: %s" % \ 203 self.imported_layer_version.layer.name 204 205 self.assertTrue(expected_text in notification.text, 206 "Expected notification text \"%s\" not found instead" 207 "it was \"%s\"" % 208 (expected_text, notification.text)) 209 210 def test_addrm_to_project(self): 211 self.get(self.url) 212 213 # Add the layer 214 self.click("#add-remove-layer-btn") 215 216 notification = self.wait_until_visible("#change-notification-msg") 217 218 expected_text = "You have added 1 layer to your project: %s" % \ 219 self.imported_layer_version.layer.name 220 221 self.assertTrue(expected_text in notification.text, 222 "Expected notification text %s not found was " 223 " \"%s\" instead" % 224 (expected_text, notification.text)) 225 226 # Remove the layer 227 self.click("#add-remove-layer-btn") 228 229 notification = self.wait_until_visible("#change-notification-msg") 230 231 expected_text = "You have removed 1 layer from your project: %s" % \ 232 self.imported_layer_version.layer.name 233 234 self.assertTrue(expected_text in notification.text, 235 "Expected notification text %s not found was " 236 " \"%s\" instead" % 237 (expected_text, notification.text)) 238