diff options
Diffstat (limited to 'bitbake/lib/toaster/tests/functional/test_project_page_tab_config.py')
-rw-r--r-- | bitbake/lib/toaster/tests/functional/test_project_page_tab_config.py | 528 |
1 files changed, 528 insertions, 0 deletions
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 new file mode 100644 index 0000000000..eb905ddf3f --- /dev/null +++ b/bitbake/lib/toaster/tests/functional/test_project_page_tab_config.py | |||
@@ -0,0 +1,528 @@ | |||
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 ElementClickInterceptedException, NoSuchElementException, TimeoutException | ||
16 | from orm.models import Project | ||
17 | from tests.functional.functional_helpers import SeleniumFunctionalTestCase | ||
18 | from selenium.webdriver.common.by import By | ||
19 | |||
20 | from .utils import get_projectId_from_url, wait_until_build, wait_until_build_cancelled | ||
21 | |||
22 | |||
23 | @pytest.mark.django_db | ||
24 | @pytest.mark.order("last") | ||
25 | class TestProjectConfigTab(SeleniumFunctionalTestCase): | ||
26 | PROJECT_NAME = 'TestProjectConfigTab' | ||
27 | project_id = None | ||
28 | |||
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): | ||
67 | # Navigate to project page | ||
68 | if TestProjectConfigTab.project_id is None: | ||
69 | self._create_project(project_name=self._random_string(10)) | ||
70 | current_url = self.driver.current_url | ||
71 | TestProjectConfigTab.project_id = get_projectId_from_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') | ||
77 | |||
78 | def _create_builds(self): | ||
79 | # check search box can be use to build recipes | ||
80 | search_box = self.find('#build-input') | ||
81 | search_box.send_keys('foo') | ||
82 | self.find('#build-button').click() | ||
83 | self.wait_until_present('#latest-builds') | ||
84 | # loop until reach the parsing state | ||
85 | wait_until_build(self, 'queued cloning starting parsing failed') | ||
86 | lastest_builds = self.driver.find_elements( | ||
87 | By.XPATH, | ||
88 | '//div[@id="latest-builds"]/div', | ||
89 | ) | ||
90 | last_build = lastest_builds[0] | ||
91 | self.assertTrue( | ||
92 | 'foo' in str(last_build.text) | ||
93 | ) | ||
94 | last_build = lastest_builds[0] | ||
95 | try: | ||
96 | cancel_button = last_build.find_element( | ||
97 | By.XPATH, | ||
98 | '//span[@class="cancel-build-btn pull-right alert-link"]', | ||
99 | ) | ||
100 | cancel_button.click() | ||
101 | except NoSuchElementException: | ||
102 | # Skip if the build is already cancelled | ||
103 | pass | ||
104 | wait_until_build_cancelled(self) | ||
105 | |||
106 | def _get_tabs(self): | ||
107 | # tabs links list | ||
108 | return self.driver.find_elements( | ||
109 | By.XPATH, | ||
110 | '//div[@id="project-topbar"]//li' | ||
111 | ) | ||
112 | |||
113 | def _get_config_nav_item(self, index): | ||
114 | config_nav = self.find('#config-nav') | ||
115 | return config_nav.find_elements(By.TAG_NAME, 'li')[index] | ||
116 | |||
117 | def test_project_config_nav(self): | ||
118 | """ Test project config tab navigation: | ||
119 | - Check if the menu is displayed and contains the right elements: | ||
120 | - Configuration | ||
121 | - COMPATIBLE METADATA | ||
122 | - Custom images | ||
123 | - Image recipes | ||
124 | - Software recipes | ||
125 | - Machines | ||
126 | - Layers | ||
127 | - Distro | ||
128 | - EXTRA CONFIGURATION | ||
129 | - Bitbake variables | ||
130 | - Actions | ||
131 | - Delete project | ||
132 | """ | ||
133 | self._navigate_to_project_page() | ||
134 | |||
135 | def _get_config_nav_item(index): | ||
136 | config_nav = self.find('#config-nav') | ||
137 | return config_nav.find_elements(By.TAG_NAME, 'li')[index] | ||
138 | |||
139 | def check_config_nav_item(index, item_name, url): | ||
140 | item = _get_config_nav_item(index) | ||
141 | self.assertTrue(item_name in item.text) | ||
142 | self.assertTrue(item.get_attribute('class') == 'active') | ||
143 | self.assertTrue(url in self.driver.current_url) | ||
144 | |||
145 | # check if the menu contains the right elements | ||
146 | # COMPATIBLE METADATA | ||
147 | compatible_metadata = _get_config_nav_item(1) | ||
148 | self.assertTrue( | ||
149 | "compatible metadata" in compatible_metadata.text.lower() | ||
150 | ) | ||
151 | # EXTRA CONFIGURATION | ||
152 | extra_configuration = _get_config_nav_item(8) | ||
153 | self.assertTrue( | ||
154 | "extra configuration" in extra_configuration.text.lower() | ||
155 | ) | ||
156 | # Actions | ||
157 | actions = _get_config_nav_item(10) | ||
158 | self.assertTrue("actions" in str(actions.text).lower()) | ||
159 | |||
160 | conf_nav_list = [ | ||
161 | # config | ||
162 | [0, 'Configuration', | ||
163 | f"/toastergui/project/{TestProjectConfigTab.project_id}"], | ||
164 | # custom images | ||
165 | [2, 'Custom images', | ||
166 | f"/toastergui/project/{TestProjectConfigTab.project_id}/customimages"], | ||
167 | # image recipes | ||
168 | [3, 'Image recipes', | ||
169 | f"/toastergui/project/{TestProjectConfigTab.project_id}/images"], | ||
170 | # software recipes | ||
171 | [4, 'Software recipes', | ||
172 | f"/toastergui/project/{TestProjectConfigTab.project_id}/softwarerecipes"], | ||
173 | # machines | ||
174 | [5, 'Machines', | ||
175 | f"/toastergui/project/{TestProjectConfigTab.project_id}/machines"], | ||
176 | # layers | ||
177 | [6, 'Layers', | ||
178 | f"/toastergui/project/{TestProjectConfigTab.project_id}/layers"], | ||
179 | # distro | ||
180 | [7, 'Distros', | ||
181 | f"/toastergui/project/{TestProjectConfigTab.project_id}/distros"], | ||
182 | # [9, 'BitBake variables', f"/toastergui/project/{TestProjectConfigTab.project_id}/configuration"], # bitbake variables | ||
183 | ] | ||
184 | for index, item_name, url in conf_nav_list: | ||
185 | item = _get_config_nav_item(index) | ||
186 | if item.get_attribute('class') != 'active': | ||
187 | item.click() | ||
188 | check_config_nav_item(index, item_name, url) | ||
189 | |||
190 | def test_image_recipe_editColumn(self): | ||
191 | """ Test the edit column feature in image recipe table on project page """ | ||
192 | def test_edit_column(check_box_id): | ||
193 | # Check that we can hide/show table column | ||
194 | check_box = self.find(f'#{check_box_id}') | ||
195 | th_class = str(check_box_id).replace('checkbox-', '') | ||
196 | if check_box.is_selected(): | ||
197 | # check if column is visible in table | ||
198 | self.assertTrue( | ||
199 | self.find( | ||
200 | f'#imagerecipestable thead th.{th_class}' | ||
201 | ).is_displayed(), | ||
202 | f"The {th_class} column is checked in EditColumn dropdown, but it's not visible in table" | ||
203 | ) | ||
204 | check_box.click() | ||
205 | # check if column is hidden in table | ||
206 | self.assertFalse( | ||
207 | self.find( | ||
208 | f'#imagerecipestable thead th.{th_class}' | ||
209 | ).is_displayed(), | ||
210 | f"The {th_class} column is unchecked in EditColumn dropdown, but it's visible in table" | ||
211 | ) | ||
212 | else: | ||
213 | # check if column is hidden in table | ||
214 | self.assertFalse( | ||
215 | self.find( | ||
216 | f'#imagerecipestable thead th.{th_class}' | ||
217 | ).is_displayed(), | ||
218 | f"The {th_class} column is unchecked in EditColumn dropdown, but it's visible in table" | ||
219 | ) | ||
220 | check_box.click() | ||
221 | # check if column is visible in table | ||
222 | self.assertTrue( | ||
223 | self.find( | ||
224 | f'#imagerecipestable thead th.{th_class}' | ||
225 | ).is_displayed(), | ||
226 | f"The {th_class} column is checked in EditColumn dropdown, but it's not visible in table" | ||
227 | ) | ||
228 | |||
229 | self._navigate_to_project_page() | ||
230 | # navigate to project image recipe page | ||
231 | recipe_image_page_link = self._get_config_nav_item(3) | ||
232 | recipe_image_page_link.click() | ||
233 | self.wait_until_present('#imagerecipestable tbody tr') | ||
234 | |||
235 | # Check edit column | ||
236 | edit_column = self.find('#edit-columns-button') | ||
237 | self.assertTrue(edit_column.is_displayed()) | ||
238 | edit_column.click() | ||
239 | # Check dropdown is visible | ||
240 | self.wait_until_visible('ul.dropdown-menu.editcol') | ||
241 | |||
242 | # Check that we can hide the edit column | ||
243 | test_edit_column('checkbox-get_description_or_summary') | ||
244 | test_edit_column('checkbox-layer_version__get_vcs_reference') | ||
245 | test_edit_column('checkbox-layer_version__layer__name') | ||
246 | test_edit_column('checkbox-license') | ||
247 | test_edit_column('checkbox-recipe-file') | ||
248 | test_edit_column('checkbox-section') | ||
249 | test_edit_column('checkbox-version') | ||
250 | |||
251 | def test_image_recipe_show_rows(self): | ||
252 | """ Test the show rows feature in image recipe table on project page """ | ||
253 | def test_show_rows(row_to_show, show_row_link): | ||
254 | # Check that we can show rows == row_to_show | ||
255 | show_row_link.select_by_value(str(row_to_show)) | ||
256 | self.wait_until_visible('#imagerecipestable tbody tr', poll=3) | ||
257 | # check at least some rows are visible | ||
258 | self.assertTrue( | ||
259 | len(self.find_all('#imagerecipestable tbody tr')) > 0 | ||
260 | ) | ||
261 | |||
262 | self._navigate_to_project_page() | ||
263 | # navigate to project image recipe page | ||
264 | recipe_image_page_link = self._get_config_nav_item(3) | ||
265 | recipe_image_page_link.click() | ||
266 | self.wait_until_present('#imagerecipestable tbody tr') | ||
267 | |||
268 | show_rows = self.driver.find_elements( | ||
269 | By.XPATH, | ||
270 | '//select[@class="form-control pagesize-imagerecipestable"]' | ||
271 | ) | ||
272 | # Check show rows | ||
273 | for show_row_link in show_rows: | ||
274 | show_row_link = Select(show_row_link) | ||
275 | test_show_rows(10, show_row_link) | ||
276 | test_show_rows(25, show_row_link) | ||
277 | test_show_rows(50, show_row_link) | ||
278 | test_show_rows(100, show_row_link) | ||
279 | test_show_rows(150, show_row_link) | ||
280 | |||
281 | def test_project_config_tab_right_section(self): | ||
282 | """ Test project config tab right section contains five blocks: | ||
283 | - Machine: | ||
284 | - check 'Machine' is displayed | ||
285 | - check can change Machine | ||
286 | - Distro: | ||
287 | - check 'Distro' is displayed | ||
288 | - check can change Distro | ||
289 | - Most built recipes: | ||
290 | - check 'Most built recipes' is displayed | ||
291 | - check can select a recipe and build it | ||
292 | - Project release: | ||
293 | - check 'Project release' is displayed | ||
294 | - check project has right release displayed | ||
295 | - Layers: | ||
296 | - check can add a layer if exists | ||
297 | - check at least three layers are displayed | ||
298 | - openembedded-core | ||
299 | - meta-poky | ||
300 | - meta-yocto-bsp | ||
301 | """ | ||
302 | # Create a new project for this test | ||
303 | project_name = self._random_string(10) | ||
304 | self._create_project(project_name=project_name) | ||
305 | # check if the menu is displayed | ||
306 | self.wait_until_visible('#project-page') | ||
307 | block_l = self.driver.find_element( | ||
308 | By.XPATH, '//*[@id="project-page"]/div[2]') | ||
309 | project_release = self.driver.find_element( | ||
310 | By.XPATH, '//*[@id="project-page"]/div[1]/div[4]') | ||
311 | layers = block_l.find_element(By.ID, 'layer-container') | ||
312 | |||
313 | def check_machine_distro(self, item_name, new_item_name, block_id): | ||
314 | block = self.find(f'#{block_id}') | ||
315 | title = block.find_element(By.TAG_NAME, 'h3') | ||
316 | self.assertTrue(item_name.capitalize() in title.text) | ||
317 | edit_btn = self.find(f'#change-{item_name}-toggle') | ||
318 | edit_btn.click() | ||
319 | self.wait_until_visible(f'#{item_name}-change-input') | ||
320 | name_input = self.find(f'#{item_name}-change-input') | ||
321 | name_input.clear() | ||
322 | name_input.send_keys(new_item_name) | ||
323 | change_btn = self.find(f'#{item_name}-change-btn') | ||
324 | change_btn.click() | ||
325 | self.wait_until_visible(f'#project-{item_name}-name') | ||
326 | project_name = self.find(f'#project-{item_name}-name') | ||
327 | self.assertTrue(new_item_name in project_name.text) | ||
328 | # check change notificaiton is displayed | ||
329 | change_notification = self.find('#change-notification') | ||
330 | self.assertTrue( | ||
331 | f'You have changed the {item_name} to: {new_item_name}' in change_notification.text | ||
332 | ) | ||
333 | |||
334 | # Machine | ||
335 | check_machine_distro(self, 'machine', 'qemux86-64', 'machine-section') | ||
336 | # Distro | ||
337 | check_machine_distro(self, 'distro', 'poky-altcfg', 'distro-section') | ||
338 | |||
339 | # Project release | ||
340 | title = project_release.find_element(By.TAG_NAME, 'h3') | ||
341 | self.assertTrue("Project release" in title.text) | ||
342 | self.assertTrue( | ||
343 | "Yocto Project master" in self.find('#project-release-title').text | ||
344 | ) | ||
345 | # Layers | ||
346 | title = layers.find_element(By.TAG_NAME, 'h3') | ||
347 | self.assertTrue("Layers" in title.text) | ||
348 | # check at least three layers are displayed | ||
349 | # openembedded-core | ||
350 | # meta-poky | ||
351 | # meta-yocto-bsp | ||
352 | layers_list = layers.find_element(By.ID, 'layers-in-project-list') | ||
353 | layers_list_items = layers_list.find_elements(By.TAG_NAME, 'li') | ||
354 | # remove all layers except the first three layers | ||
355 | for i in range(3, len(layers_list_items)): | ||
356 | layers_list_items[i].find_element(By.TAG_NAME, 'span').click() | ||
357 | # check can add a layer if exists | ||
358 | add_layer_input = layers.find_element(By.ID, 'layer-add-input') | ||
359 | add_layer_input.send_keys('meta-oe') | ||
360 | self.wait_until_visible('#layer-container > form > div > span > div') | ||
361 | dropdown_item = self.driver.find_element( | ||
362 | By.XPATH, | ||
363 | '//*[@id="layer-container"]/form/div/span/div' | ||
364 | ) | ||
365 | try: | ||
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') | ||
371 | add_layer_btn.click() | ||
372 | 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 | |||
377 | def test_most_build_recipes(self): | ||
378 | """ Test most build recipes block contains""" | ||
379 | def rebuild_from_most_build_recipes(recipe_list_items): | ||
380 | checkbox = recipe_list_items[0].find_element(By.TAG_NAME, 'input') | ||
381 | checkbox.click() | ||
382 | build_btn = self.find('#freq-build-btn') | ||
383 | build_btn.click() | ||
384 | self.wait_until_visible('#latest-builds') | ||
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 | |||
433 | def test_project_page_tab_importlayer(self): | ||
434 | """ Test project page tab import layer """ | ||
435 | self._navigate_to_project_page() | ||
436 | # navigate to "Import layers" tab | ||
437 | import_layers_tab = self._get_tabs()[2] | ||
438 | import_layers_tab.find_element(By.TAG_NAME, 'a').click() | ||
439 | self.wait_until_visible('#layer-git-repo-url') | ||
440 | |||
441 | # Check git repo radio button | ||
442 | git_repo_radio = self.find('#git-repo-radio') | ||
443 | git_repo_radio.click() | ||
444 | |||
445 | # Set git repo url | ||
446 | input_repo_url = self.find('#layer-git-repo-url') | ||
447 | input_repo_url.send_keys('git://git.yoctoproject.org/meta-fake') | ||
448 | # Blur the input to trigger the validation | ||
449 | input_repo_url.send_keys(Keys.TAB) | ||
450 | |||
451 | # Check name is set | ||
452 | input_layer_name = self.find('#import-layer-name') | ||
453 | self.assertTrue(input_layer_name.get_attribute('value') == 'meta-fake') | ||
454 | |||
455 | # Set branch | ||
456 | input_branch = self.find('#layer-git-ref') | ||
457 | input_branch.send_keys('master') | ||
458 | |||
459 | # Import layer | ||
460 | self.find('#import-and-add-btn').click() | ||
461 | |||
462 | # Check layer is added | ||
463 | self.wait_until_visible('#layer-container') | ||
464 | block_l = self.driver.find_element( | ||
465 | By.XPATH, '//*[@id="project-page"]/div[2]') | ||
466 | layers = block_l.find_element(By.ID, 'layer-container') | ||
467 | layers_list = layers.find_element(By.ID, 'layers-in-project-list') | ||
468 | layers_list_items = layers_list.find_elements(By.TAG_NAME, 'li') | ||
469 | self.assertTrue( | ||
470 | 'meta-fake' in str(layers_list_items[-1].text) | ||
471 | ) | ||
472 | |||
473 | def test_project_page_custom_image_no_image(self): | ||
474 | """ Test project page tab "New custom image" when no custom image """ | ||
475 | project_name = self._random_string(10) | ||
476 | self._create_project(project_name=project_name) | ||
477 | current_url = self.driver.current_url | ||
478 | TestProjectConfigTab.project_id = get_projectId_from_url(current_url) | ||
479 | # navigate to "Custom image" tab | ||
480 | custom_image_section = self._get_config_nav_item(2) | ||
481 | custom_image_section.click() | ||
482 | self.wait_until_visible('#empty-state-customimagestable') | ||
483 | |||
484 | # Check message when no custom image | ||
485 | self.assertTrue( | ||
486 | "You have not created any custom images yet." in str( | ||
487 | self.find('#empty-state-customimagestable').text | ||
488 | ) | ||
489 | ) | ||
490 | div_empty_msg = self.find('#empty-state-customimagestable') | ||
491 | link_create_custom_image = div_empty_msg.find_element( | ||
492 | By.TAG_NAME, 'a') | ||
493 | self.assertTrue(TestProjectConfigTab.project_id is not None) | ||
494 | self.assertTrue( | ||
495 | f"/toastergui/project/{TestProjectConfigTab.project_id}/newcustomimage" in str( | ||
496 | link_create_custom_image.get_attribute('href') | ||
497 | ) | ||
498 | ) | ||
499 | self.assertTrue( | ||
500 | "Create your first custom image" in str( | ||
501 | link_create_custom_image.text | ||
502 | ) | ||
503 | ) | ||
504 | TestProjectConfigTab.project_id = None # reset project id | ||
505 | |||
506 | def test_project_page_image_recipe(self): | ||
507 | """ Test project page section images | ||
508 | - Check image recipes are displayed | ||
509 | - Check search input | ||
510 | - Check image recipe build button works | ||
511 | - Check image recipe table features(show/hide column, pagination) | ||
512 | """ | ||
513 | self._navigate_to_project_page() | ||
514 | # navigate to "Images section" | ||
515 | images_section = self._get_config_nav_item(3) | ||
516 | images_section.click() | ||
517 | self.wait_until_visible('#imagerecipestable') | ||
518 | rows = self.find_all('#imagerecipestable tbody tr') | ||
519 | self.assertTrue(len(rows) > 0) | ||
520 | |||
521 | # Test search input | ||
522 | self.wait_until_visible('#search-input-imagerecipestable') | ||
523 | recipe_input = self.find('#search-input-imagerecipestable') | ||
524 | recipe_input.send_keys('core-image-minimal') | ||
525 | self.find('#search-submit-imagerecipestable').click() | ||
526 | self.wait_until_visible('#imagerecipestable tbody tr') | ||
527 | rows = self.find_all('#imagerecipestable tbody tr') | ||
528 | self.assertTrue(len(rows) > 0) | ||