summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorElliot Smith <elliot.smith@intel.com>2016-01-15 13:01:05 +0200
committerRichard Purdie <richard.purdie@linuxfoundation.org>2016-01-15 16:30:01 +0000
commitc4b50111e9887c4d69f6a8b5fb50824fc44f7128 (patch)
tree76073d697e3161606847c629ecf3747928398be5
parent88a262cbd231b452f0b50cc37bec7189e5796710 (diff)
downloadpoky-c4b50111e9887c4d69f6a8b5fb50824fc44f7128.tar.gz
bitbake: toaster tests: fix Django tests for new ToasterTable pages
The Django command-line tests can no longer test the content of the projects/, builds/ and projectbuilds/ pages, as ToasterTable pages are populated by JavaScript. Fix/remove affected tests by converting them to tests on the JSON returned by the ToasterTable. [YOCTO #8738] (Bitbake rev: 85efa9530fa6181855e051bfd14de1c15db9c3b7) Signed-off-by: Elliot Smith <elliot.smith@intel.com> Signed-off-by: Ed Bartosh <ed.bartosh@linux.intel.com> Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
-rw-r--r--bitbake/lib/toaster/toastergui/tests.py265
1 files changed, 180 insertions, 85 deletions
diff --git a/bitbake/lib/toaster/toastergui/tests.py b/bitbake/lib/toaster/toastergui/tests.py
index c927fe1d8d..0987721180 100644
--- a/bitbake/lib/toaster/toastergui/tests.py
+++ b/bitbake/lib/toaster/toastergui/tests.py
@@ -38,11 +38,14 @@ import toastergui
38 38
39from toastergui.tables import SoftwareRecipesTable 39from toastergui.tables import SoftwareRecipesTable
40import json 40import json
41from datetime import timedelta
41from bs4 import BeautifulSoup 42from bs4 import BeautifulSoup
42import re 43import re
43import string 44import string
45import json
44 46
45PROJECT_NAME = "test project" 47PROJECT_NAME = "test project"
48PROJECT_NAME2 = "test project 2"
46CLI_BUILDS_PROJECT_NAME = 'Command line builds' 49CLI_BUILDS_PROJECT_NAME = 'Command line builds'
47 50
48class ViewTests(TestCase): 51class ViewTests(TestCase):
@@ -54,14 +57,46 @@ class ViewTests(TestCase):
54 release = Release.objects.create(name="test release", 57 release = Release.objects.create(name="test release",
55 branch_name="master", 58 branch_name="master",
56 bitbake_version=bbv) 59 bitbake_version=bbv)
60 release2 = Release.objects.create(name="test release 2",
61 branch_name="master",
62 bitbake_version=bbv)
63
57 self.project = Project.objects.create_project(name=PROJECT_NAME, 64 self.project = Project.objects.create_project(name=PROJECT_NAME,
58 release=release) 65 release=release)
66
67 self.project2 = Project.objects.create_project(name=PROJECT_NAME2,
68 release=release2)
69
59 now = timezone.now() 70 now = timezone.now()
71 later = now + timedelta(days=1)
60 72
61 build = Build.objects.create(project=self.project, 73 build = Build.objects.create(project=self.project,
62 started_on=now, 74 started_on=now,
63 completed_on=now) 75 completed_on=now)
64 76
77 # for testing BuildsTable
78 build1 = Build.objects.create(project=self.project,
79 started_on=now,
80 completed_on=now,
81 outcome=Build.SUCCEEDED,
82 machine="raspberrypi2")
83
84 Build.objects.create(project=self.project,
85 started_on=later,
86 completed_on=later,
87 outcome=Build.FAILED,
88 machine="qemux86")
89
90 Build.objects.create(project=self.project2,
91 started_on=later,
92 completed_on=later,
93 outcome=Build.SUCCEEDED,
94 machine="qemux86")
95
96 # to test sorting by errors and warnings in BuildsTable
97 LogMessage.objects.create(build=build1, level=LogMessage.WARNING)
98 LogMessage.objects.create(build=build1, level=LogMessage.ERROR)
99
65 layersrc = LayerSource.objects.create(sourcetype=LayerSource.TYPE_IMPORTED) 100 layersrc = LayerSource.objects.create(sourcetype=LayerSource.TYPE_IMPORTED)
66 self.priority = ReleaseLayerSourcePriority.objects.create(release=release, 101 self.priority = ReleaseLayerSourcePriority.objects.create(release=release,
67 layer_source=layersrc) 102 layer_source=layersrc)
@@ -172,8 +207,7 @@ class ViewTests(TestCase):
172 response = self.client.get(reverse('all-projects'), follow=True) 207 response = self.client.get(reverse('all-projects'), follow=True)
173 self.assertEqual(response.status_code, 200) 208 self.assertEqual(response.status_code, 200)
174 self.assertTrue(response['Content-Type'].startswith('text/html')) 209 self.assertTrue(response['Content-Type'].startswith('text/html'))
175 self.assertTemplateUsed(response, "projects.html") 210 self.assertTemplateUsed(response, "projects-toastertable.html")
176 self.assertTrue(PROJECT_NAME in response.content)
177 211
178 def test_get_json_call_returns_json(self): 212 def test_get_json_call_returns_json(self):
179 """Test for all projects output in json format""" 213 """Test for all projects output in json format"""
@@ -191,13 +225,6 @@ class ViewTests(TestCase):
191 self.assertTrue(PROJECT_NAME in [x["name"] for x in data["rows"]]) 225 self.assertTrue(PROJECT_NAME in [x["name"] for x in data["rows"]])
192 self.assertTrue("id" in data["rows"][0]) 226 self.assertTrue("id" in data["rows"][0])
193 227
194 self.assertEqual(sorted(data["rows"][0]),
195 ['bitbake_version_id', 'created', 'id',
196 'is_default', 'layersTypeAheadUrl', 'name',
197 'num_builds', 'projectBuildsUrl', 'projectPageUrl',
198 'recipesTypeAheadUrl', 'release_id',
199 'short_description', 'updated', 'user_id'])
200
201 def test_typeaheads(self): 228 def test_typeaheads(self):
202 """Test typeahead ReST API""" 229 """Test typeahead ReST API"""
203 layers_url = reverse('xhr_layerstypeahead', args=(self.project.id,)) 230 layers_url = reverse('xhr_layerstypeahead', args=(self.project.id,))
@@ -450,7 +477,7 @@ class ViewTests(TestCase):
450 all_data = get_data(table) 477 all_data = get_data(table)
451 478
452 self.assertTrue(len(all_data['rows']) > 1, 479 self.assertTrue(len(all_data['rows']) > 1,
453 "Cannot test on a table with < 1 row") 480 "Cannot test on the table %s with < 1 row" % name)
454 481
455 if table.default_orderby: 482 if table.default_orderby:
456 row_one = all_data['rows'][0][table.default_orderby.strip("-")] 483 row_one = all_data['rows'][0][table.default_orderby.strip("-")]
@@ -512,16 +539,20 @@ class ViewTests(TestCase):
512 # This is the name of the filter:action 539 # This is the name of the filter:action
513 # e.g. project_filter:not_in_project 540 # e.g. project_filter:not_in_project
514 filter_string = "%s:%s" % (column['filter_name'], 541 filter_string = "%s:%s" % (column['filter_name'],
515 filter_action['name']) 542 filter_action['action_name'])
516 # Now get the data with the filter applied 543 # Now get the data with the filter applied
517 filtered_data = get_data(table_cls(), 544 filtered_data = get_data(table_cls(),
518 {"filter" : filter_string}) 545 {"filter" : filter_string})
519 self.assertEqual(len(filtered_data['rows']), 546
520 int(filter_action['count']), 547 # date range filter actions can't specify the
521 "We added a table filter for %s but " 548 # number of results they return, so their count is 0
522 "the number of rows returned was not " 549 if filter_action['count'] != None:
523 "what the filter info said there " 550 self.assertEqual(len(filtered_data['rows']),
524 "would be" % name) 551 int(filter_action['count']),
552 "We added a table filter for %s but "
553 "the number of rows returned was not "
554 "what the filter info said there "
555 "would be" % name)
525 556
526 557
527 # Test search functionality on the table 558 # Test search functionality on the table
@@ -673,6 +704,10 @@ class AllProjectsPageTests(TestCase):
673 value=self.MACHINE_NAME) 704 value=self.MACHINE_NAME)
674 project_var.save() 705 project_var.save()
675 706
707 def _get_row_for_project(self, data, project_id):
708 """ Get the object representing the table data for a project """
709 return [row for row in data['rows'] if row['id'] == project_id][0]
710
676 def test_default_project_hidden(self): 711 def test_default_project_hidden(self):
677 """ The default project should be hidden if it has no builds """ 712 """ The default project should be hidden if it has no builds """
678 params = {"count": 10, "orderby": "updated:-", "page": 1} 713 params = {"count": 10, "orderby": "updated:-", "page": 1}
@@ -688,11 +723,20 @@ class AllProjectsPageTests(TestCase):
688 self._add_build_to_default_project() 723 self._add_build_to_default_project()
689 724
690 params = {"count": 10, "orderby": "updated:-", "page": 1} 725 params = {"count": 10, "orderby": "updated:-", "page": 1}
691 response = self.client.get(reverse('all-projects'), params)
692 726
693 self.assertTrue('tr class="data"' in response.content, 727 response = self.client.get(
694 'should be a project row in the page') 728 reverse('all-projects'),
695 self.assertTrue(CLI_BUILDS_PROJECT_NAME in response.content, 729 {'format': 'json'},
730 params
731 )
732
733 data = json.loads(response.content)
734
735 # find the row for the default project
736 default_project_row = self._get_row_for_project(data, self.default_project.id)
737
738 # check its name template has the correct text
739 self.assertEqual(default_project_row['name'], CLI_BUILDS_PROJECT_NAME,
696 'default project "cli builds" should be in page') 740 'default project "cli builds" should be in page')
697 741
698 def test_default_project_release(self): 742 def test_default_project_release(self):
@@ -706,24 +750,32 @@ class AllProjectsPageTests(TestCase):
706 # another project to test, which should show release 750 # another project to test, which should show release
707 self._add_non_default_project() 751 self._add_non_default_project()
708 752
709 response = self.client.get(reverse('all-projects'), follow=True) 753 response = self.client.get(
710 soup = BeautifulSoup(response.content) 754 reverse('all-projects'),
755 {'format': 'json'},
756 follow=True
757 )
711 758
712 # check the release cell for the default project 759 data = json.loads(response.content)
713 attrs = {'data-project': str(self.default_project.id)} 760
714 rows = soup.find_all('tr', attrs=attrs) 761 # used to find the correct span in the template output
715 self.assertEqual(len(rows), 1, 'should be one row for default project') 762 attrs = {'data-project-field': 'release'}
716 cells = rows[0].find_all('td', attrs={'data-project-field': 'release'}) 763
717 self.assertEqual(len(cells), 1, 'should be one release cell') 764 # find the row for the default project
718 text = cells[0].select('span.muted')[0].text 765 default_project_row = self._get_row_for_project(data, self.default_project.id)
766
767 # check the release text for the default project
768 soup = BeautifulSoup(default_project_row['static:release'])
769 text = soup.find('span', attrs=attrs).select('span.muted')[0].text
719 self.assertEqual(text, 'Not applicable', 770 self.assertEqual(text, 'Not applicable',
720 'release should be not applicable for default project') 771 'release should be not applicable for default project')
721 772
773 # find the row for the default project
774 other_project_row = self._get_row_for_project(data, self.project.id)
775
722 # check the link in the release cell for the other project 776 # check the link in the release cell for the other project
723 attrs = {'data-project': str(self.project.id)} 777 soup = BeautifulSoup(other_project_row['static:release'])
724 rows = soup.find_all('tr', attrs=attrs) 778 text = soup.find('span', attrs=attrs).select('a')[0].text.strip()
725 cells = rows[0].find_all('td', attrs={'data-project-field': 'release'})
726 text = cells[0].select('a')[0].text
727 self.assertEqual(text, self.release.name, 779 self.assertEqual(text, self.release.name,
728 'release name should be shown for non-default project') 780 'release name should be shown for non-default project')
729 781
@@ -738,24 +790,32 @@ class AllProjectsPageTests(TestCase):
738 # another project to test, which should show machine 790 # another project to test, which should show machine
739 self._add_non_default_project() 791 self._add_non_default_project()
740 792
741 response = self.client.get(reverse('all-projects'), follow=True) 793 response = self.client.get(
742 soup = BeautifulSoup(response.content) 794 reverse('all-projects'),
795 {'format': 'json'},
796 follow=True
797 )
798
799 data = json.loads(response.content)
800
801 # used to find the correct span in the template output
802 attrs = {'data-project-field': 'machine'}
803
804 # find the row for the default project
805 default_project_row = self._get_row_for_project(data, self.default_project.id)
743 806
744 # check the machine cell for the default project 807 # check the machine cell for the default project
745 attrs = {'data-project': str(self.default_project.id)} 808 soup = BeautifulSoup(default_project_row['static:machine'])
746 rows = soup.find_all('tr', attrs=attrs) 809 text = soup.find('span', attrs=attrs).select('span.muted')[0].text.strip()
747 self.assertEqual(len(rows), 1, 'should be one row for default project')
748 cells = rows[0].find_all('td', attrs={'data-project-field': 'machine'})
749 self.assertEqual(len(cells), 1, 'should be one machine cell')
750 text = cells[0].select('span.muted')[0].text
751 self.assertEqual(text, 'Not applicable', 810 self.assertEqual(text, 'Not applicable',
752 'machine should be not applicable for default project') 811 'machine should be not applicable for default project')
812
813 # find the row for the default project
814 other_project_row = self._get_row_for_project(data, self.project.id)
753 815
754 # check the link in the machine cell for the other project 816 # check the link in the machine cell for the other project
755 attrs = {'data-project': str(self.project.id)} 817 soup = BeautifulSoup(other_project_row['static:machine'])
756 rows = soup.find_all('tr', attrs=attrs) 818 text = soup.find('span', attrs=attrs).find('a').text.strip()
757 cells = rows[0].find_all('td', attrs={'data-project-field': 'machine'})
758 text = cells[0].select('a')[0].text
759 self.assertEqual(text, self.MACHINE_NAME, 819 self.assertEqual(text, self.MACHINE_NAME,
760 'machine name should be shown for non-default project') 820 'machine name should be shown for non-default project')
761 821
@@ -769,24 +829,33 @@ class AllProjectsPageTests(TestCase):
769 # need a build, otherwise project doesn't display at all 829 # need a build, otherwise project doesn't display at all
770 self._add_build_to_default_project() 830 self._add_build_to_default_project()
771 831
772 # another project to test, which should show machine 832 # another project to test
773 self._add_non_default_project() 833 self._add_non_default_project()
774 834
775 response = self.client.get(reverse('all-projects'), follow=True) 835 response = self.client.get(
776 soup = BeautifulSoup(response.content) 836 reverse('all-projects'),
837 {'format': 'json'},
838 follow=True
839 )
840
841 data = json.loads(response.content)
777 842
778 # link for default project 843 # find the row for the default project
779 row = soup.find('tr', attrs={'data-project': self.default_project.id}) 844 default_project_row = self._get_row_for_project(data, self.default_project.id)
780 cell = row.find('td', attrs={'data-project-field': 'name'}) 845
846 # check the link on the name field
847 soup = BeautifulSoup(default_project_row['static:name'])
781 expected_url = reverse('projectbuilds', args=(self.default_project.id,)) 848 expected_url = reverse('projectbuilds', args=(self.default_project.id,))
782 self.assertEqual(cell.find('a')['href'], expected_url, 849 self.assertEqual(soup.find('a')['href'], expected_url,
783 'link on default project name should point to builds') 850 'link on default project name should point to builds')
784 851
785 # link for other project 852 # find the row for the other project
786 row = soup.find('tr', attrs={'data-project': self.project.id}) 853 other_project_row = self._get_row_for_project(data, self.project.id)
787 cell = row.find('td', attrs={'data-project-field': 'name'}) 854
855 # check the link for the other project
856 soup = BeautifulSoup(other_project_row['static:name'])
788 expected_url = reverse('project', args=(self.project.id,)) 857 expected_url = reverse('project', args=(self.project.id,))
789 self.assertEqual(cell.find('a')['href'], expected_url, 858 self.assertEqual(soup.find('a')['href'], expected_url,
790 'link on project name should point to configuration') 859 'link on project name should point to configuration')
791 860
792class ProjectBuildsPageTests(TestCase): 861class ProjectBuildsPageTests(TestCase):
@@ -846,9 +915,9 @@ class ProjectBuildsPageTests(TestCase):
846 def _get_rows_for_project(self, project_id): 915 def _get_rows_for_project(self, project_id):
847 """ Helper to retrieve HTML rows for a project """ 916 """ Helper to retrieve HTML rows for a project """
848 url = reverse("projectbuilds", args=(project_id,)) 917 url = reverse("projectbuilds", args=(project_id,))
849 response = self.client.get(url, follow=True) 918 response = self.client.get(url, {'format': 'json'}, follow=True)
850 soup = BeautifulSoup(response.content) 919 data = json.loads(response.content)
851 return soup.select('tr[class="data"]') 920 return data['rows']
852 921
853 def test_show_builds_for_project(self): 922 def test_show_builds_for_project(self):
854 """ Builds for a project should be displayed """ 923 """ Builds for a project should be displayed """
@@ -889,10 +958,14 @@ class ProjectBuildsPageTests(TestCase):
889 """ Task should be shown as suffix on build name """ 958 """ Task should be shown as suffix on build name """
890 build = Build.objects.create(**self.project1_build_success) 959 build = Build.objects.create(**self.project1_build_success)
891 Target.objects.create(build=build, target='bash', task='clean') 960 Target.objects.create(build=build, target='bash', task='clean')
892 url = reverse("projectbuilds", args=(self.project1.id,)) 961
893 response = self.client.get(url, follow=True) 962 url = reverse('projectbuilds', args=(self.project1.id,))
894 result = re.findall('^ +bash:clean$', response.content, re.MULTILINE) 963 response = self.client.get(url, {'format': 'json'}, follow=True)
895 self.assertEqual(len(result), 2) 964 data = json.loads(response.content)
965 cell = data['rows'][0]['static:target']
966
967 result = re.findall('^ +bash:clean', cell, re.MULTILINE)
968 self.assertEqual(len(result), 1)
896 969
897 def test_cli_builds_hides_tabs(self): 970 def test_cli_builds_hides_tabs(self):
898 """ 971 """
@@ -952,32 +1025,46 @@ class AllBuildsPageTests(TestCase):
952 "outcome": Build.SUCCEEDED 1025 "outcome": Build.SUCCEEDED
953 } 1026 }
954 1027
1028 def _get_row_for_build(self, data, build_id):
1029 """ Get the object representing the table data for a project """
1030 return [row for row in data['rows']
1031 if row['id'] == build_id][0]
1032
955 def test_show_tasks_in_allbuilds(self): 1033 def test_show_tasks_in_allbuilds(self):
956 """ Task should be shown as suffix on build name """ 1034 """ Task should be shown as suffix on build name """
957 build = Build.objects.create(**self.project1_build_success) 1035 build = Build.objects.create(**self.project1_build_success)
958 Target.objects.create(build=build, target='bash', task='clean') 1036 Target.objects.create(build=build, target='bash', task='clean')
1037
959 url = reverse('all-builds') 1038 url = reverse('all-builds')
960 response = self.client.get(url, follow=True) 1039 response = self.client.get(url, {'format': 'json'}, follow=True)
961 result = re.findall('bash:clean', response.content, re.MULTILINE) 1040 data = json.loads(response.content)
962 self.assertEqual(len(result), 3) 1041 cell = data['rows'][0]['static:target']
963 1042
964 def test_no_run_again_for_cli_build(self): 1043 result = re.findall('bash:clean', cell, re.MULTILINE)
965 """ "Run again" button should not be shown for command-line builds """ 1044 self.assertEqual(len(result), 1)
966 build = Build.objects.create(**self.default_project_build_success) 1045
1046 def test_run_again(self):
1047 """
1048 "Run again" button should not be shown for command-line builds,
1049 but should be shown for other builds
1050 """
1051 build1 = Build.objects.create(**self.project1_build_success)
1052 default_build = Build.objects.create(**self.default_project_build_success)
967 url = reverse('all-builds') 1053 url = reverse('all-builds')
968 response = self.client.get(url, follow=True) 1054 response = self.client.get(url, follow=True)
969 soup = BeautifulSoup(response.content) 1055 soup = BeautifulSoup(response.content)
970 1056
971 attrs = {'data-latest-build-result': build.id}
972 result = soup.find('div', attrs=attrs)
973
974 # shouldn't see a run again button for command-line builds 1057 # shouldn't see a run again button for command-line builds
1058 attrs = {'data-latest-build-result': default_build.id}
1059 result = soup.find('div', attrs=attrs)
975 run_again_button = result.select('button') 1060 run_again_button = result.select('button')
976 self.assertEqual(len(run_again_button), 0) 1061 self.assertEqual(len(run_again_button), 0)
977 1062
978 # should see a help icon for command-line builds 1063 # should see a run again button for non-command-line builds
979 help_icon = result.select('i.get-help-green') 1064 attrs = {'data-latest-build-result': build1.id}
980 self.assertEqual(len(help_icon), 1) 1065 result = soup.find('div', attrs=attrs)
1066 run_again_button = result.select('button')
1067 self.assertEqual(len(run_again_button), 1)
981 1068
982 def test_tooltips_on_project_name(self): 1069 def test_tooltips_on_project_name(self):
983 """ 1070 """
@@ -989,20 +1076,28 @@ class AllBuildsPageTests(TestCase):
989 default_build = Build.objects.create(**self.default_project_build_success) 1076 default_build = Build.objects.create(**self.default_project_build_success)
990 1077
991 url = reverse('all-builds') 1078 url = reverse('all-builds')
992 response = self.client.get(url, follow=True) 1079 response = self.client.get(url, {'format': 'json'}, follow=True)
993 soup = BeautifulSoup(response.content) 1080 data = json.loads(response.content)
1081
1082 # get the data row for the non-command-line builds project
1083 other_project_row = self._get_row_for_build(data, build1.id)
1084
1085 # make sure there is some HTML
1086 soup = BeautifulSoup(other_project_row['static:project'])
1087 self.assertEqual(len(soup.select('a')), 1,
1088 'should be a project name link')
994 1089
995 # no help icon on non-default project name 1090 # no help icon on non-default project name
996 result = soup.find('tr', attrs={'data-table-build-result': build1.id}) 1091 icons = soup.select('i.get-help')
997 name = result.select('td.project-name')[0]
998 icons = name.select('i.get-help')
999 self.assertEqual(len(icons), 0, 1092 self.assertEqual(len(icons), 0,
1000 'should not be a help icon for non-cli builds name') 1093 'should not be a help icon for non-cli builds name')
1001 1094
1095 # get the data row for the command-line builds project
1096 default_project_row = self._get_row_for_build(data, default_build.id)
1097
1002 # help icon on default project name 1098 # help icon on default project name
1003 result = soup.find('tr', attrs={'data-table-build-result': default_build.id}) 1099 soup = BeautifulSoup(default_project_row['static:project'])
1004 name = result.select('td.project-name')[0] 1100 icons = soup.select('i.get-help')
1005 icons = name.select('i.get-help')
1006 self.assertEqual(len(icons), 1, 1101 self.assertEqual(len(icons), 1,
1007 'should be a help icon for cli builds name') 1102 'should be a help icon for cli builds name')
1008 1103