diff options
Diffstat (limited to 'bitbake/lib/toaster/tests/functional/test_project_config.py')
-rw-r--r-- | bitbake/lib/toaster/tests/functional/test_project_config.py | 341 |
1 files changed, 341 insertions, 0 deletions
diff --git a/bitbake/lib/toaster/tests/functional/test_project_config.py b/bitbake/lib/toaster/tests/functional/test_project_config.py new file mode 100644 index 0000000000..dbee36aa4e --- /dev/null +++ b/bitbake/lib/toaster/tests/functional/test_project_config.py | |||
@@ -0,0 +1,341 @@ | |||
1 | #! /usr/bin/env python3 # | ||
2 | # BitBake Toaster UI tests implementation | ||
3 | # | ||
4 | # Copyright (C) 2023 Savoir-faire Linux | ||
5 | # | ||
6 | # SPDX-License-Identifier: GPL-2.0-only | ||
7 | # | ||
8 | |||
9 | import string | ||
10 | import random | ||
11 | import pytest | ||
12 | from django.urls import reverse | ||
13 | from selenium.webdriver import Keys | ||
14 | from selenium.webdriver.support.select import Select | ||
15 | from selenium.common.exceptions import TimeoutException | ||
16 | from tests.functional.functional_helpers import SeleniumFunctionalTestCase | ||
17 | from selenium.webdriver.common.by import By | ||
18 | |||
19 | from .utils import get_projectId_from_url | ||
20 | |||
21 | |||
22 | @pytest.mark.django_db | ||
23 | @pytest.mark.order("last") | ||
24 | class TestProjectConfig(SeleniumFunctionalTestCase): | ||
25 | project_id = None | ||
26 | PROJECT_NAME = 'TestProjectConfig' | ||
27 | INVALID_PATH_START_TEXT = 'The directory path should either start with a /' | ||
28 | INVALID_PATH_CHAR_TEXT = 'The directory path cannot include spaces or ' \ | ||
29 | 'any of these characters' | ||
30 | |||
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): | ||
68 | config_nav = self.find('#config-nav') | ||
69 | return config_nav.find_elements(By.TAG_NAME, 'li')[index] | ||
70 | |||
71 | def _navigate_bbv_page(self): | ||
72 | """ Navigate to project BitBake variables page """ | ||
73 | # check if the menu is displayed | ||
74 | if TestProjectConfig.project_id is None: | ||
75 | self._create_project(project_name=self._random_string(10)) | ||
76 | current_url = self.driver.current_url | ||
77 | TestProjectConfig.project_id = get_projectId_from_url(current_url) | ||
78 | else: | ||
79 | url = reverse('projectconf', args=(TestProjectConfig.project_id,)) | ||
80 | self.get(url) | ||
81 | self.wait_until_visible('#config-nav', poll=3) | ||
82 | bbv_page_link = self._get_config_nav_item(9) | ||
83 | bbv_page_link.click() | ||
84 | self.wait_until_visible('#config-nav', poll=3) | ||
85 | |||
86 | def test_no_underscore_iamgefs_type(self): | ||
87 | """ | ||
88 | Should not accept IMAGEFS_TYPE with an underscore | ||
89 | """ | ||
90 | self._navigate_bbv_page() | ||
91 | imagefs_type = "foo_bar" | ||
92 | |||
93 | self.wait_until_visible('#change-image_fstypes-icon', poll=2) | ||
94 | |||
95 | self.click('#change-image_fstypes-icon') | ||
96 | |||
97 | self.enter_text('#new-imagefs_types', imagefs_type) | ||
98 | |||
99 | element = self.wait_until_visible('#hintError-image-fs_type', poll=2) | ||
100 | |||
101 | self.assertTrue(("A valid image type cannot include underscores" in element.text), | ||
102 | "Did not find underscore error message") | ||
103 | |||
104 | def test_checkbox_verification(self): | ||
105 | """ | ||
106 | Should automatically check the checkbox if user enters value | ||
107 | text box, if value is there in the checkbox. | ||
108 | """ | ||
109 | self._navigate_bbv_page() | ||
110 | |||
111 | imagefs_type = "btrfs" | ||
112 | |||
113 | self.wait_until_visible('#change-image_fstypes-icon', poll=2) | ||
114 | |||
115 | self.click('#change-image_fstypes-icon') | ||
116 | |||
117 | self.enter_text('#new-imagefs_types', imagefs_type) | ||
118 | |||
119 | checkboxes = self.driver.find_elements(By.XPATH, "//input[@class='fs-checkbox-fstypes']") | ||
120 | |||
121 | for checkbox in checkboxes: | ||
122 | if checkbox.get_attribute("value") == "btrfs": | ||
123 | self.assertEqual(checkbox.is_selected(), True) | ||
124 | |||
125 | def test_textbox_with_checkbox_verification(self): | ||
126 | """ | ||
127 | Should automatically add or remove value in textbox, if user checks | ||
128 | or unchecks checkboxes. | ||
129 | """ | ||
130 | self._navigate_bbv_page() | ||
131 | |||
132 | self.wait_until_visible('#change-image_fstypes-icon', poll=2) | ||
133 | |||
134 | self.click('#change-image_fstypes-icon') | ||
135 | |||
136 | checkboxes_selector = '.fs-checkbox-fstypes' | ||
137 | |||
138 | self.wait_until_visible(checkboxes_selector, poll=2) | ||
139 | checkboxes = self.find_all(checkboxes_selector) | ||
140 | |||
141 | for checkbox in checkboxes: | ||
142 | if checkbox.get_attribute("value") == "cpio": | ||
143 | checkbox.click() | ||
144 | element = self.driver.find_element(By.ID, 'new-imagefs_types') | ||
145 | |||
146 | self.wait_until_visible('#new-imagefs_types', poll=2) | ||
147 | |||
148 | self.assertTrue(("cpio" in element.get_attribute('value'), | ||
149 | "Imagefs not added into the textbox")) | ||
150 | checkbox.click() | ||
151 | self.assertTrue(("cpio" not in element.text), | ||
152 | "Image still present in the textbox") | ||
153 | |||
154 | def test_set_download_dir(self): | ||
155 | """ | ||
156 | Validate the allowed and disallowed types in the directory field for | ||
157 | DL_DIR | ||
158 | """ | ||
159 | self._navigate_bbv_page() | ||
160 | |||
161 | # activate the input to edit download dir | ||
162 | try: | ||
163 | change_dl_dir_btn = self.wait_until_visible('#change-dl_dir-icon', poll=2) | ||
164 | except TimeoutException: | ||
165 | # If download dir is not displayed, test is skipped | ||
166 | change_dl_dir_btn = None | ||
167 | |||
168 | 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() | ||
171 | |||
172 | # downloads dir path doesn't start with / or ${...} | ||
173 | input_field = self.wait_until_visible('#new-dl_dir', poll=2) | ||
174 | input_field.clear() | ||
175 | self.enter_text('#new-dl_dir', 'home/foo') | ||
176 | element = self.wait_until_visible('#hintError-initialChar-dl_dir', poll=2) | ||
177 | |||
178 | msg = 'downloads directory path starts with invalid character but ' \ | ||
179 | 'treated as valid' | ||
180 | self.assertTrue((self.INVALID_PATH_START_TEXT in element.text), msg) | ||
181 | |||
182 | # downloads dir path has a space | ||
183 | self.driver.find_element(By.ID, 'new-dl_dir').clear() | ||
184 | self.enter_text('#new-dl_dir', '/foo/bar a') | ||
185 | |||
186 | element = self.wait_until_visible('#hintError-dl_dir', poll=2) | ||
187 | msg = 'downloads directory path characters invalid but treated as valid' | ||
188 | self.assertTrue((self.INVALID_PATH_CHAR_TEXT in element.text), msg) | ||
189 | |||
190 | # downloads dir path starts with ${...} but has a space | ||
191 | self.driver.find_element(By.ID,'new-dl_dir').clear() | ||
192 | self.enter_text('#new-dl_dir', '${TOPDIR}/down foo') | ||
193 | |||
194 | element = self.wait_until_visible('#hintError-dl_dir', poll=2) | ||
195 | msg = 'downloads directory path characters invalid but treated as valid' | ||
196 | self.assertTrue((self.INVALID_PATH_CHAR_TEXT in element.text), msg) | ||
197 | |||
198 | # downloads dir path starts with / | ||
199 | self.driver.find_element(By.ID,'new-dl_dir').clear() | ||
200 | self.enter_text('#new-dl_dir', '/bar/foo') | ||
201 | |||
202 | hidden_element = self.driver.find_element(By.ID,'hintError-dl_dir') | ||
203 | self.assertEqual(hidden_element.is_displayed(), False, | ||
204 | 'downloads directory path valid but treated as invalid') | ||
205 | |||
206 | # downloads dir path starts with ${...} | ||
207 | self.driver.find_element(By.ID,'new-dl_dir').clear() | ||
208 | self.enter_text('#new-dl_dir', '${TOPDIR}/down') | ||
209 | |||
210 | hidden_element = self.driver.find_element(By.ID,'hintError-dl_dir') | ||
211 | self.assertEqual(hidden_element.is_displayed(), False, | ||
212 | 'downloads directory path valid but treated as invalid') | ||
213 | |||
214 | def test_set_sstate_dir(self): | ||
215 | """ | ||
216 | Validate the allowed and disallowed types in the directory field for | ||
217 | SSTATE_DIR | ||
218 | """ | ||
219 | self._navigate_bbv_page() | ||
220 | |||
221 | try: | ||
222 | btn_chg_sstate_dir = self.wait_until_visible( | ||
223 | '#change-sstate_dir-icon', | ||
224 | poll=2 | ||
225 | ) | ||
226 | self.click('#change-sstate_dir-icon') | ||
227 | except TimeoutException: | ||
228 | # If sstate_dir is not displayed, test is skipped | ||
229 | btn_chg_sstate_dir = None | ||
230 | |||
231 | if btn_chg_sstate_dir: # Skip continuation if sstate_dir is not displayed | ||
232 | # path doesn't start with / or ${...} | ||
233 | input_field = self.wait_until_visible('#new-sstate_dir', poll=2) | ||
234 | input_field.clear() | ||
235 | self.enter_text('#new-sstate_dir', 'home/foo') | ||
236 | element = self.wait_until_visible('#hintError-initialChar-sstate_dir', poll=2) | ||
237 | |||
238 | msg = 'sstate directory path starts with invalid character but ' \ | ||
239 | 'treated as valid' | ||
240 | self.assertTrue((self.INVALID_PATH_START_TEXT in element.text), msg) | ||
241 | |||
242 | # path has a space | ||
243 | self.driver.find_element(By.ID, 'new-sstate_dir').clear() | ||
244 | self.enter_text('#new-sstate_dir', '/foo/bar a') | ||
245 | |||
246 | element = self.wait_until_visible('#hintError-sstate_dir', poll=2) | ||
247 | msg = 'sstate directory path characters invalid but treated as valid' | ||
248 | self.assertTrue((self.INVALID_PATH_CHAR_TEXT in element.text), msg) | ||
249 | |||
250 | # path starts with ${...} but has a space | ||
251 | self.driver.find_element(By.ID,'new-sstate_dir').clear() | ||
252 | self.enter_text('#new-sstate_dir', '${TOPDIR}/down foo') | ||
253 | |||
254 | element = self.wait_until_visible('#hintError-sstate_dir', poll=2) | ||
255 | msg = 'sstate directory path characters invalid but treated as valid' | ||
256 | self.assertTrue((self.INVALID_PATH_CHAR_TEXT in element.text), msg) | ||
257 | |||
258 | # path starts with / | ||
259 | self.driver.find_element(By.ID,'new-sstate_dir').clear() | ||
260 | self.enter_text('#new-sstate_dir', '/bar/foo') | ||
261 | |||
262 | hidden_element = self.driver.find_element(By.ID, 'hintError-sstate_dir') | ||
263 | self.assertEqual(hidden_element.is_displayed(), False, | ||
264 | 'sstate directory path valid but treated as invalid') | ||
265 | |||
266 | # paths starts with ${...} | ||
267 | self.driver.find_element(By.ID, 'new-sstate_dir').clear() | ||
268 | self.enter_text('#new-sstate_dir', '${TOPDIR}/down') | ||
269 | |||
270 | hidden_element = self.driver.find_element(By.ID, 'hintError-sstate_dir') | ||
271 | self.assertEqual(hidden_element.is_displayed(), False, | ||
272 | 'sstate directory path valid but treated as invalid') | ||
273 | |||
274 | def _change_bbv_value(self, **kwargs): | ||
275 | var_name, field, btn_id, input_id, value, save_btn, *_ = kwargs.values() | ||
276 | """ Change bitbake variable value """ | ||
277 | self._navigate_bbv_page() | ||
278 | self.wait_until_visible(f'#{btn_id}', poll=2) | ||
279 | if kwargs.get('new_variable'): | ||
280 | self.find(f"#{btn_id}").clear() | ||
281 | self.enter_text(f"#{btn_id}", f"{var_name}") | ||
282 | else: | ||
283 | self.click(f'#{btn_id}') | ||
284 | self.wait_until_visible(f'#{input_id}', poll=2) | ||
285 | |||
286 | if kwargs.get('is_select'): | ||
287 | select = Select(self.find(f'#{input_id}')) | ||
288 | select.select_by_visible_text(value) | ||
289 | else: | ||
290 | self.find(f"#{input_id}").clear() | ||
291 | self.enter_text(f'#{input_id}', f'{value}') | ||
292 | self.click(f'#{save_btn}') | ||
293 | value_displayed = str(self.wait_until_visible(f'#{field}').text).lower() | ||
294 | msg = f'{var_name} variable not changed' | ||
295 | self.assertTrue(str(value).lower() in value_displayed, msg) | ||
296 | |||
297 | def test_change_distro_var(self): | ||
298 | """ Test changing distro variable """ | ||
299 | self._change_bbv_value( | ||
300 | var_name='DISTRO', | ||
301 | field='distro', | ||
302 | btn_id='change-distro-icon', | ||
303 | input_id='new-distro', | ||
304 | value='poky-changed', | ||
305 | save_btn="apply-change-distro", | ||
306 | ) | ||
307 | |||
308 | def test_set_image_install_append_var(self): | ||
309 | """ Test setting IMAGE_INSTALL:append variable """ | ||
310 | self._change_bbv_value( | ||
311 | var_name='IMAGE_INSTALL:append', | ||
312 | field='image_install', | ||
313 | btn_id='change-image_install-icon', | ||
314 | input_id='new-image_install', | ||
315 | value='bash, apt, busybox', | ||
316 | save_btn="apply-change-image_install", | ||
317 | ) | ||
318 | |||
319 | def test_set_package_classes_var(self): | ||
320 | """ Test setting PACKAGE_CLASSES variable """ | ||
321 | self._change_bbv_value( | ||
322 | var_name='PACKAGE_CLASSES', | ||
323 | field='package_classes', | ||
324 | btn_id='change-package_classes-icon', | ||
325 | input_id='package_classes-select', | ||
326 | value='package_deb', | ||
327 | save_btn="apply-change-package_classes", | ||
328 | is_select=True, | ||
329 | ) | ||
330 | |||
331 | def test_create_new_bbv(self): | ||
332 | """ Test creating new bitbake variable """ | ||
333 | self._change_bbv_value( | ||
334 | var_name='New_Custom_Variable', | ||
335 | field='configvar-list', | ||
336 | btn_id='variable', | ||
337 | input_id='value', | ||
338 | value='new variable value', | ||
339 | save_btn="add-configvar-button", | ||
340 | new_variable=True | ||
341 | ) | ||