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>
This commit is contained in:
Elliot Smith 2016-01-15 13:01:05 +02:00 committed by Richard Purdie
parent 88a262cbd2
commit c4b50111e9
1 changed files with 181 additions and 86 deletions

View File

@ -38,11 +38,14 @@ import toastergui
from toastergui.tables import SoftwareRecipesTable from toastergui.tables import SoftwareRecipesTable
import json import json
from datetime import timedelta
from bs4 import BeautifulSoup from bs4 import BeautifulSoup
import re import re
import string import string
import json
PROJECT_NAME = "test project" PROJECT_NAME = "test project"
PROJECT_NAME2 = "test project 2"
CLI_BUILDS_PROJECT_NAME = 'Command line builds' CLI_BUILDS_PROJECT_NAME = 'Command line builds'
class ViewTests(TestCase): class ViewTests(TestCase):
@ -54,14 +57,46 @@ class ViewTests(TestCase):
release = Release.objects.create(name="test release", release = Release.objects.create(name="test release",
branch_name="master", branch_name="master",
bitbake_version=bbv) bitbake_version=bbv)
release2 = Release.objects.create(name="test release 2",
branch_name="master",
bitbake_version=bbv)
self.project = Project.objects.create_project(name=PROJECT_NAME, self.project = Project.objects.create_project(name=PROJECT_NAME,
release=release) release=release)
self.project2 = Project.objects.create_project(name=PROJECT_NAME2,
release=release2)
now = timezone.now() now = timezone.now()
later = now + timedelta(days=1)
build = Build.objects.create(project=self.project, build = Build.objects.create(project=self.project,
started_on=now, started_on=now,
completed_on=now) completed_on=now)
# for testing BuildsTable
build1 = Build.objects.create(project=self.project,
started_on=now,
completed_on=now,
outcome=Build.SUCCEEDED,
machine="raspberrypi2")
Build.objects.create(project=self.project,
started_on=later,
completed_on=later,
outcome=Build.FAILED,
machine="qemux86")
Build.objects.create(project=self.project2,
started_on=later,
completed_on=later,
outcome=Build.SUCCEEDED,
machine="qemux86")
# to test sorting by errors and warnings in BuildsTable
LogMessage.objects.create(build=build1, level=LogMessage.WARNING)
LogMessage.objects.create(build=build1, level=LogMessage.ERROR)
layersrc = LayerSource.objects.create(sourcetype=LayerSource.TYPE_IMPORTED) layersrc = LayerSource.objects.create(sourcetype=LayerSource.TYPE_IMPORTED)
self.priority = ReleaseLayerSourcePriority.objects.create(release=release, self.priority = ReleaseLayerSourcePriority.objects.create(release=release,
layer_source=layersrc) layer_source=layersrc)
@ -172,8 +207,7 @@ class ViewTests(TestCase):
response = self.client.get(reverse('all-projects'), follow=True) response = self.client.get(reverse('all-projects'), follow=True)
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
self.assertTrue(response['Content-Type'].startswith('text/html')) self.assertTrue(response['Content-Type'].startswith('text/html'))
self.assertTemplateUsed(response, "projects.html") self.assertTemplateUsed(response, "projects-toastertable.html")
self.assertTrue(PROJECT_NAME in response.content)
def test_get_json_call_returns_json(self): def test_get_json_call_returns_json(self):
"""Test for all projects output in json format""" """Test for all projects output in json format"""
@ -191,13 +225,6 @@ class ViewTests(TestCase):
self.assertTrue(PROJECT_NAME in [x["name"] for x in data["rows"]]) self.assertTrue(PROJECT_NAME in [x["name"] for x in data["rows"]])
self.assertTrue("id" in data["rows"][0]) self.assertTrue("id" in data["rows"][0])
self.assertEqual(sorted(data["rows"][0]),
['bitbake_version_id', 'created', 'id',
'is_default', 'layersTypeAheadUrl', 'name',
'num_builds', 'projectBuildsUrl', 'projectPageUrl',
'recipesTypeAheadUrl', 'release_id',
'short_description', 'updated', 'user_id'])
def test_typeaheads(self): def test_typeaheads(self):
"""Test typeahead ReST API""" """Test typeahead ReST API"""
layers_url = reverse('xhr_layerstypeahead', args=(self.project.id,)) layers_url = reverse('xhr_layerstypeahead', args=(self.project.id,))
@ -450,7 +477,7 @@ class ViewTests(TestCase):
all_data = get_data(table) all_data = get_data(table)
self.assertTrue(len(all_data['rows']) > 1, self.assertTrue(len(all_data['rows']) > 1,
"Cannot test on a table with < 1 row") "Cannot test on the table %s with < 1 row" % name)
if table.default_orderby: if table.default_orderby:
row_one = all_data['rows'][0][table.default_orderby.strip("-")] row_one = all_data['rows'][0][table.default_orderby.strip("-")]
@ -512,16 +539,20 @@ class ViewTests(TestCase):
# This is the name of the filter:action # This is the name of the filter:action
# e.g. project_filter:not_in_project # e.g. project_filter:not_in_project
filter_string = "%s:%s" % (column['filter_name'], filter_string = "%s:%s" % (column['filter_name'],
filter_action['name']) filter_action['action_name'])
# Now get the data with the filter applied # Now get the data with the filter applied
filtered_data = get_data(table_cls(), filtered_data = get_data(table_cls(),
{"filter" : filter_string}) {"filter" : filter_string})
self.assertEqual(len(filtered_data['rows']),
int(filter_action['count']), # date range filter actions can't specify the
"We added a table filter for %s but " # number of results they return, so their count is 0
"the number of rows returned was not " if filter_action['count'] != None:
"what the filter info said there " self.assertEqual(len(filtered_data['rows']),
"would be" % name) int(filter_action['count']),
"We added a table filter for %s but "
"the number of rows returned was not "
"what the filter info said there "
"would be" % name)
# Test search functionality on the table # Test search functionality on the table
@ -673,6 +704,10 @@ class AllProjectsPageTests(TestCase):
value=self.MACHINE_NAME) value=self.MACHINE_NAME)
project_var.save() project_var.save()
def _get_row_for_project(self, data, project_id):
""" Get the object representing the table data for a project """
return [row for row in data['rows'] if row['id'] == project_id][0]
def test_default_project_hidden(self): def test_default_project_hidden(self):
""" The default project should be hidden if it has no builds """ """ The default project should be hidden if it has no builds """
params = {"count": 10, "orderby": "updated:-", "page": 1} params = {"count": 10, "orderby": "updated:-", "page": 1}
@ -688,11 +723,20 @@ class AllProjectsPageTests(TestCase):
self._add_build_to_default_project() self._add_build_to_default_project()
params = {"count": 10, "orderby": "updated:-", "page": 1} params = {"count": 10, "orderby": "updated:-", "page": 1}
response = self.client.get(reverse('all-projects'), params)
self.assertTrue('tr class="data"' in response.content, response = self.client.get(
'should be a project row in the page') reverse('all-projects'),
self.assertTrue(CLI_BUILDS_PROJECT_NAME in response.content, {'format': 'json'},
params
)
data = json.loads(response.content)
# find the row for the default project
default_project_row = self._get_row_for_project(data, self.default_project.id)
# check its name template has the correct text
self.assertEqual(default_project_row['name'], CLI_BUILDS_PROJECT_NAME,
'default project "cli builds" should be in page') 'default project "cli builds" should be in page')
def test_default_project_release(self): def test_default_project_release(self):
@ -706,24 +750,32 @@ class AllProjectsPageTests(TestCase):
# another project to test, which should show release # another project to test, which should show release
self._add_non_default_project() self._add_non_default_project()
response = self.client.get(reverse('all-projects'), follow=True) response = self.client.get(
soup = BeautifulSoup(response.content) reverse('all-projects'),
{'format': 'json'},
follow=True
)
# check the release cell for the default project data = json.loads(response.content)
attrs = {'data-project': str(self.default_project.id)}
rows = soup.find_all('tr', attrs=attrs) # used to find the correct span in the template output
self.assertEqual(len(rows), 1, 'should be one row for default project') attrs = {'data-project-field': 'release'}
cells = rows[0].find_all('td', attrs={'data-project-field': 'release'})
self.assertEqual(len(cells), 1, 'should be one release cell') # find the row for the default project
text = cells[0].select('span.muted')[0].text default_project_row = self._get_row_for_project(data, self.default_project.id)
# check the release text for the default project
soup = BeautifulSoup(default_project_row['static:release'])
text = soup.find('span', attrs=attrs).select('span.muted')[0].text
self.assertEqual(text, 'Not applicable', self.assertEqual(text, 'Not applicable',
'release should be not applicable for default project') 'release should be not applicable for default project')
# find the row for the default project
other_project_row = self._get_row_for_project(data, self.project.id)
# check the link in the release cell for the other project # check the link in the release cell for the other project
attrs = {'data-project': str(self.project.id)} soup = BeautifulSoup(other_project_row['static:release'])
rows = soup.find_all('tr', attrs=attrs) text = soup.find('span', attrs=attrs).select('a')[0].text.strip()
cells = rows[0].find_all('td', attrs={'data-project-field': 'release'})
text = cells[0].select('a')[0].text
self.assertEqual(text, self.release.name, self.assertEqual(text, self.release.name,
'release name should be shown for non-default project') 'release name should be shown for non-default project')
@ -738,24 +790,32 @@ class AllProjectsPageTests(TestCase):
# another project to test, which should show machine # another project to test, which should show machine
self._add_non_default_project() self._add_non_default_project()
response = self.client.get(reverse('all-projects'), follow=True) response = self.client.get(
soup = BeautifulSoup(response.content) reverse('all-projects'),
{'format': 'json'},
follow=True
)
data = json.loads(response.content)
# used to find the correct span in the template output
attrs = {'data-project-field': 'machine'}
# find the row for the default project
default_project_row = self._get_row_for_project(data, self.default_project.id)
# check the machine cell for the default project # check the machine cell for the default project
attrs = {'data-project': str(self.default_project.id)} soup = BeautifulSoup(default_project_row['static:machine'])
rows = soup.find_all('tr', attrs=attrs) text = soup.find('span', attrs=attrs).select('span.muted')[0].text.strip()
self.assertEqual(len(rows), 1, 'should be one row for default project')
cells = rows[0].find_all('td', attrs={'data-project-field': 'machine'})
self.assertEqual(len(cells), 1, 'should be one machine cell')
text = cells[0].select('span.muted')[0].text
self.assertEqual(text, 'Not applicable', self.assertEqual(text, 'Not applicable',
'machine should be not applicable for default project') 'machine should be not applicable for default project')
# find the row for the default project
other_project_row = self._get_row_for_project(data, self.project.id)
# check the link in the machine cell for the other project # check the link in the machine cell for the other project
attrs = {'data-project': str(self.project.id)} soup = BeautifulSoup(other_project_row['static:machine'])
rows = soup.find_all('tr', attrs=attrs) text = soup.find('span', attrs=attrs).find('a').text.strip()
cells = rows[0].find_all('td', attrs={'data-project-field': 'machine'})
text = cells[0].select('a')[0].text
self.assertEqual(text, self.MACHINE_NAME, self.assertEqual(text, self.MACHINE_NAME,
'machine name should be shown for non-default project') 'machine name should be shown for non-default project')
@ -769,24 +829,33 @@ class AllProjectsPageTests(TestCase):
# need a build, otherwise project doesn't display at all # need a build, otherwise project doesn't display at all
self._add_build_to_default_project() self._add_build_to_default_project()
# another project to test, which should show machine # another project to test
self._add_non_default_project() self._add_non_default_project()
response = self.client.get(reverse('all-projects'), follow=True) response = self.client.get(
soup = BeautifulSoup(response.content) reverse('all-projects'),
{'format': 'json'},
follow=True
)
# link for default project data = json.loads(response.content)
row = soup.find('tr', attrs={'data-project': self.default_project.id})
cell = row.find('td', attrs={'data-project-field': 'name'}) # find the row for the default project
default_project_row = self._get_row_for_project(data, self.default_project.id)
# check the link on the name field
soup = BeautifulSoup(default_project_row['static:name'])
expected_url = reverse('projectbuilds', args=(self.default_project.id,)) expected_url = reverse('projectbuilds', args=(self.default_project.id,))
self.assertEqual(cell.find('a')['href'], expected_url, self.assertEqual(soup.find('a')['href'], expected_url,
'link on default project name should point to builds') 'link on default project name should point to builds')
# link for other project # find the row for the other project
row = soup.find('tr', attrs={'data-project': self.project.id}) other_project_row = self._get_row_for_project(data, self.project.id)
cell = row.find('td', attrs={'data-project-field': 'name'})
# check the link for the other project
soup = BeautifulSoup(other_project_row['static:name'])
expected_url = reverse('project', args=(self.project.id,)) expected_url = reverse('project', args=(self.project.id,))
self.assertEqual(cell.find('a')['href'], expected_url, self.assertEqual(soup.find('a')['href'], expected_url,
'link on project name should point to configuration') 'link on project name should point to configuration')
class ProjectBuildsPageTests(TestCase): class ProjectBuildsPageTests(TestCase):
@ -846,9 +915,9 @@ class ProjectBuildsPageTests(TestCase):
def _get_rows_for_project(self, project_id): def _get_rows_for_project(self, project_id):
""" Helper to retrieve HTML rows for a project """ """ Helper to retrieve HTML rows for a project """
url = reverse("projectbuilds", args=(project_id,)) url = reverse("projectbuilds", args=(project_id,))
response = self.client.get(url, follow=True) response = self.client.get(url, {'format': 'json'}, follow=True)
soup = BeautifulSoup(response.content) data = json.loads(response.content)
return soup.select('tr[class="data"]') return data['rows']
def test_show_builds_for_project(self): def test_show_builds_for_project(self):
""" Builds for a project should be displayed """ """ Builds for a project should be displayed """
@ -889,10 +958,14 @@ class ProjectBuildsPageTests(TestCase):
""" Task should be shown as suffix on build name """ """ Task should be shown as suffix on build name """
build = Build.objects.create(**self.project1_build_success) build = Build.objects.create(**self.project1_build_success)
Target.objects.create(build=build, target='bash', task='clean') Target.objects.create(build=build, target='bash', task='clean')
url = reverse("projectbuilds", args=(self.project1.id,))
response = self.client.get(url, follow=True) url = reverse('projectbuilds', args=(self.project1.id,))
result = re.findall('^ +bash:clean$', response.content, re.MULTILINE) response = self.client.get(url, {'format': 'json'}, follow=True)
self.assertEqual(len(result), 2) data = json.loads(response.content)
cell = data['rows'][0]['static:target']
result = re.findall('^ +bash:clean', cell, re.MULTILINE)
self.assertEqual(len(result), 1)
def test_cli_builds_hides_tabs(self): def test_cli_builds_hides_tabs(self):
""" """
@ -952,32 +1025,46 @@ class AllBuildsPageTests(TestCase):
"outcome": Build.SUCCEEDED "outcome": Build.SUCCEEDED
} }
def _get_row_for_build(self, data, build_id):
""" Get the object representing the table data for a project """
return [row for row in data['rows']
if row['id'] == build_id][0]
def test_show_tasks_in_allbuilds(self): def test_show_tasks_in_allbuilds(self):
""" Task should be shown as suffix on build name """ """ Task should be shown as suffix on build name """
build = Build.objects.create(**self.project1_build_success) build = Build.objects.create(**self.project1_build_success)
Target.objects.create(build=build, target='bash', task='clean') Target.objects.create(build=build, target='bash', task='clean')
url = reverse('all-builds')
response = self.client.get(url, follow=True)
result = re.findall('bash:clean', response.content, re.MULTILINE)
self.assertEqual(len(result), 3)
def test_no_run_again_for_cli_build(self): url = reverse('all-builds')
""" "Run again" button should not be shown for command-line builds """ response = self.client.get(url, {'format': 'json'}, follow=True)
build = Build.objects.create(**self.default_project_build_success) data = json.loads(response.content)
cell = data['rows'][0]['static:target']
result = re.findall('bash:clean', cell, re.MULTILINE)
self.assertEqual(len(result), 1)
def test_run_again(self):
"""
"Run again" button should not be shown for command-line builds,
but should be shown for other builds
"""
build1 = Build.objects.create(**self.project1_build_success)
default_build = Build.objects.create(**self.default_project_build_success)
url = reverse('all-builds') url = reverse('all-builds')
response = self.client.get(url, follow=True) response = self.client.get(url, follow=True)
soup = BeautifulSoup(response.content) soup = BeautifulSoup(response.content)
attrs = {'data-latest-build-result': build.id}
result = soup.find('div', attrs=attrs)
# shouldn't see a run again button for command-line builds # shouldn't see a run again button for command-line builds
attrs = {'data-latest-build-result': default_build.id}
result = soup.find('div', attrs=attrs)
run_again_button = result.select('button') run_again_button = result.select('button')
self.assertEqual(len(run_again_button), 0) self.assertEqual(len(run_again_button), 0)
# should see a help icon for command-line builds # should see a run again button for non-command-line builds
help_icon = result.select('i.get-help-green') attrs = {'data-latest-build-result': build1.id}
self.assertEqual(len(help_icon), 1) result = soup.find('div', attrs=attrs)
run_again_button = result.select('button')
self.assertEqual(len(run_again_button), 1)
def test_tooltips_on_project_name(self): def test_tooltips_on_project_name(self):
""" """
@ -989,20 +1076,28 @@ class AllBuildsPageTests(TestCase):
default_build = Build.objects.create(**self.default_project_build_success) default_build = Build.objects.create(**self.default_project_build_success)
url = reverse('all-builds') url = reverse('all-builds')
response = self.client.get(url, follow=True) response = self.client.get(url, {'format': 'json'}, follow=True)
soup = BeautifulSoup(response.content) data = json.loads(response.content)
# get the data row for the non-command-line builds project
other_project_row = self._get_row_for_build(data, build1.id)
# make sure there is some HTML
soup = BeautifulSoup(other_project_row['static:project'])
self.assertEqual(len(soup.select('a')), 1,
'should be a project name link')
# no help icon on non-default project name # no help icon on non-default project name
result = soup.find('tr', attrs={'data-table-build-result': build1.id}) icons = soup.select('i.get-help')
name = result.select('td.project-name')[0]
icons = name.select('i.get-help')
self.assertEqual(len(icons), 0, self.assertEqual(len(icons), 0,
'should not be a help icon for non-cli builds name') 'should not be a help icon for non-cli builds name')
# get the data row for the command-line builds project
default_project_row = self._get_row_for_build(data, default_build.id)
# help icon on default project name # help icon on default project name
result = soup.find('tr', attrs={'data-table-build-result': default_build.id}) soup = BeautifulSoup(default_project_row['static:project'])
name = result.select('td.project-name')[0] icons = soup.select('i.get-help')
icons = name.select('i.get-help')
self.assertEqual(len(icons), 1, self.assertEqual(len(icons), 1,
'should be a help icon for cli builds name') 'should be a help icon for cli builds name')