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
import json
from datetime import timedelta
from bs4 import BeautifulSoup
import re
import string
import json
PROJECT_NAME = "test project"
PROJECT_NAME2 = "test project 2"
CLI_BUILDS_PROJECT_NAME = 'Command line builds'
class ViewTests(TestCase):
@ -54,14 +57,46 @@ class ViewTests(TestCase):
release = Release.objects.create(name="test release",
branch_name="master",
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,
release=release)
self.project2 = Project.objects.create_project(name=PROJECT_NAME2,
release=release2)
now = timezone.now()
later = now + timedelta(days=1)
build = Build.objects.create(project=self.project,
started_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)
self.priority = ReleaseLayerSourcePriority.objects.create(release=release,
layer_source=layersrc)
@ -172,8 +207,7 @@ class ViewTests(TestCase):
response = self.client.get(reverse('all-projects'), follow=True)
self.assertEqual(response.status_code, 200)
self.assertTrue(response['Content-Type'].startswith('text/html'))
self.assertTemplateUsed(response, "projects.html")
self.assertTrue(PROJECT_NAME in response.content)
self.assertTemplateUsed(response, "projects-toastertable.html")
def test_get_json_call_returns_json(self):
"""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("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):
"""Test typeahead ReST API"""
layers_url = reverse('xhr_layerstypeahead', args=(self.project.id,))
@ -450,7 +477,7 @@ class ViewTests(TestCase):
all_data = get_data(table)
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:
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
# e.g. project_filter:not_in_project
filter_string = "%s:%s" % (column['filter_name'],
filter_action['name'])
filter_action['action_name'])
# Now get the data with the filter applied
filtered_data = get_data(table_cls(),
{"filter" : filter_string})
self.assertEqual(len(filtered_data['rows']),
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)
# date range filter actions can't specify the
# number of results they return, so their count is 0
if filter_action['count'] != None:
self.assertEqual(len(filtered_data['rows']),
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
@ -673,6 +704,10 @@ class AllProjectsPageTests(TestCase):
value=self.MACHINE_NAME)
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):
""" The default project should be hidden if it has no builds """
params = {"count": 10, "orderby": "updated:-", "page": 1}
@ -688,11 +723,20 @@ class AllProjectsPageTests(TestCase):
self._add_build_to_default_project()
params = {"count": 10, "orderby": "updated:-", "page": 1}
response = self.client.get(reverse('all-projects'), params)
self.assertTrue('tr class="data"' in response.content,
'should be a project row in the page')
self.assertTrue(CLI_BUILDS_PROJECT_NAME in response.content,
response = self.client.get(
reverse('all-projects'),
{'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')
def test_default_project_release(self):
@ -706,24 +750,32 @@ class AllProjectsPageTests(TestCase):
# another project to test, which should show release
self._add_non_default_project()
response = self.client.get(reverse('all-projects'), follow=True)
soup = BeautifulSoup(response.content)
response = self.client.get(
reverse('all-projects'),
{'format': 'json'},
follow=True
)
# check the release cell for the default project
attrs = {'data-project': str(self.default_project.id)}
rows = soup.find_all('tr', attrs=attrs)
self.assertEqual(len(rows), 1, 'should be one row for default project')
cells = rows[0].find_all('td', attrs={'data-project-field': 'release'})
self.assertEqual(len(cells), 1, 'should be one release cell')
text = cells[0].select('span.muted')[0].text
data = json.loads(response.content)
# used to find the correct span in the template output
attrs = {'data-project-field': 'release'}
# find the row for the default project
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',
'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
attrs = {'data-project': str(self.project.id)}
rows = soup.find_all('tr', attrs=attrs)
cells = rows[0].find_all('td', attrs={'data-project-field': 'release'})
text = cells[0].select('a')[0].text
soup = BeautifulSoup(other_project_row['static:release'])
text = soup.find('span', attrs=attrs).select('a')[0].text.strip()
self.assertEqual(text, self.release.name,
'release name should be shown for non-default project')
@ -738,24 +790,32 @@ class AllProjectsPageTests(TestCase):
# another project to test, which should show machine
self._add_non_default_project()
response = self.client.get(reverse('all-projects'), follow=True)
soup = BeautifulSoup(response.content)
response = self.client.get(
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
attrs = {'data-project': str(self.default_project.id)}
rows = soup.find_all('tr', attrs=attrs)
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
soup = BeautifulSoup(default_project_row['static:machine'])
text = soup.find('span', attrs=attrs).select('span.muted')[0].text.strip()
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
attrs = {'data-project': str(self.project.id)}
rows = soup.find_all('tr', attrs=attrs)
cells = rows[0].find_all('td', attrs={'data-project-field': 'machine'})
text = cells[0].select('a')[0].text
soup = BeautifulSoup(other_project_row['static:machine'])
text = soup.find('span', attrs=attrs).find('a').text.strip()
self.assertEqual(text, self.MACHINE_NAME,
'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
self._add_build_to_default_project()
# another project to test, which should show machine
# another project to test
self._add_non_default_project()
response = self.client.get(reverse('all-projects'), follow=True)
soup = BeautifulSoup(response.content)
response = self.client.get(
reverse('all-projects'),
{'format': 'json'},
follow=True
)
# link for default project
row = soup.find('tr', attrs={'data-project': self.default_project.id})
cell = row.find('td', attrs={'data-project-field': 'name'})
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 the link on the name field
soup = BeautifulSoup(default_project_row['static:name'])
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 for other project
row = soup.find('tr', attrs={'data-project': self.project.id})
cell = row.find('td', attrs={'data-project-field': 'name'})
# find the row for the other project
other_project_row = self._get_row_for_project(data, self.project.id)
# check the link for the other project
soup = BeautifulSoup(other_project_row['static:name'])
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')
class ProjectBuildsPageTests(TestCase):
@ -846,9 +915,9 @@ class ProjectBuildsPageTests(TestCase):
def _get_rows_for_project(self, project_id):
""" Helper to retrieve HTML rows for a project """
url = reverse("projectbuilds", args=(project_id,))
response = self.client.get(url, follow=True)
soup = BeautifulSoup(response.content)
return soup.select('tr[class="data"]')
response = self.client.get(url, {'format': 'json'}, follow=True)
data = json.loads(response.content)
return data['rows']
def test_show_builds_for_project(self):
""" Builds for a project should be displayed """
@ -889,10 +958,14 @@ class ProjectBuildsPageTests(TestCase):
""" Task should be shown as suffix on build name """
build = Build.objects.create(**self.project1_build_success)
Target.objects.create(build=build, target='bash', task='clean')
url = reverse("projectbuilds", args=(self.project1.id,))
response = self.client.get(url, follow=True)
result = re.findall('^ +bash:clean$', response.content, re.MULTILINE)
self.assertEqual(len(result), 2)
url = reverse('projectbuilds', args=(self.project1.id,))
response = self.client.get(url, {'format': 'json'}, follow=True)
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):
"""
@ -952,32 +1025,46 @@ class AllBuildsPageTests(TestCase):
"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):
""" Task should be shown as suffix on build name """
build = Build.objects.create(**self.project1_build_success)
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):
""" "Run again" button should not be shown for command-line builds """
build = Build.objects.create(**self.default_project_build_success)
url = reverse('all-builds')
response = self.client.get(url, {'format': 'json'}, follow=True)
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')
response = self.client.get(url, follow=True)
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
attrs = {'data-latest-build-result': default_build.id}
result = soup.find('div', attrs=attrs)
run_again_button = result.select('button')
self.assertEqual(len(run_again_button), 0)
# should see a help icon for command-line builds
help_icon = result.select('i.get-help-green')
self.assertEqual(len(help_icon), 1)
# should see a run again button for non-command-line builds
attrs = {'data-latest-build-result': build1.id}
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):
"""
@ -989,20 +1076,28 @@ class AllBuildsPageTests(TestCase):
default_build = Build.objects.create(**self.default_project_build_success)
url = reverse('all-builds')
response = self.client.get(url, follow=True)
soup = BeautifulSoup(response.content)
response = self.client.get(url, {'format': 'json'}, follow=True)
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
result = soup.find('tr', attrs={'data-table-build-result': build1.id})
name = result.select('td.project-name')[0]
icons = name.select('i.get-help')
icons = soup.select('i.get-help')
self.assertEqual(len(icons), 0,
'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
result = soup.find('tr', attrs={'data-table-build-result': default_build.id})
name = result.select('td.project-name')[0]
icons = name.select('i.get-help')
soup = BeautifulSoup(default_project_row['static:project'])
icons = soup.select('i.get-help')
self.assertEqual(len(icons), 1,
'should be a help icon for cli builds name')