Make unit testing less reliant on HTML fragments

Some tests check for equality with quite large HTML fragments, when
the test is only interested in certain specific things (and not style
changes etc). This patch alters some of those tests (specifically ones
that were tripping up a dependent patch, but can be expanded) to check
the context data of the response, since the actual rendering is tested
by horizon's tests.

It also adds tests to check context data in 'normal' circumstances so
that no test coverage should be lost by this.

Change-Id: I09be82058edc923521ffb4c022515845a18b158d
Closes-Bug: #1517084
This commit is contained in:
Steve McLellan 2015-11-17 12:36:31 -06:00
parent 8476319cd0
commit d76a61346e
7 changed files with 462 additions and 164 deletions

View File

@ -25,8 +25,6 @@ from mox3.mox import IsA # noqa
import six
from openstack_dashboard import api
from openstack_dashboard.dashboards.project.access_and_security \
.floating_ips import tables
from openstack_dashboard.test import helpers as test
from openstack_dashboard.usage import quotas
@ -214,6 +212,68 @@ class FloatingIpViewTests(test.TestCase):
res = self.client.post(INDEX_URL, {"action": action})
self.assertRedirectsNoFollow(res, INDEX_URL)
@test.create_stubs({api.network: ('floating_ip_supported',
'tenant_floating_ip_list',
'security_group_list',
'floating_ip_pools_list',),
api.nova: ('keypair_list',
'server_list',),
quotas: ('tenant_quota_usages',),
api.base: ('is_service_enabled',)})
def test_allocate_button_attributes(self):
keypairs = self.keypairs.list()
floating_ips = self.floating_ips.list()
floating_pools = self.pools.list()
quota_data = self.quota_usages.first()
quota_data['floating_ips']['available'] = 10
sec_groups = self.security_groups.list()
api.network.floating_ip_supported(
IsA(http.HttpRequest)) \
.AndReturn(True)
api.network.tenant_floating_ip_list(
IsA(http.HttpRequest)) \
.AndReturn(floating_ips)
api.network.security_group_list(
IsA(http.HttpRequest)).MultipleTimes()\
.AndReturn(sec_groups)
api.network.floating_ip_pools_list(
IsA(http.HttpRequest)) \
.AndReturn(floating_pools)
api.nova.keypair_list(
IsA(http.HttpRequest)) \
.AndReturn(keypairs)
api.nova.server_list(
IsA(http.HttpRequest)) \
.AndReturn([self.servers.list(), False])
quotas.tenant_quota_usages(
IsA(http.HttpRequest)).MultipleTimes() \
.AndReturn(quota_data)
api.base.is_service_enabled(
IsA(http.HttpRequest),
'network').MultipleTimes() \
.AndReturn(True)
api.base.is_service_enabled(
IsA(http.HttpRequest),
'ec2').MultipleTimes() \
.AndReturn(False)
self.mox.ReplayAll()
res = self.client.get(INDEX_URL +
"?tab=access_security_tabs__floating_ips_tab")
allocate_action = self.getAndAssertTableAction(res, 'floating_ips',
'allocate')
self.assertEqual(set(['ajax-modal']), set(allocate_action.classes))
self.assertEqual('Allocate IP To Project',
six.text_type(allocate_action.verbose_name))
self.assertEqual(None, allocate_action.policy_rules)
url = 'horizon:project:access_and_security:floating_ips:allocate'
self.assertEqual(url, allocate_action.url)
@test.create_stubs({api.network: ('floating_ip_supported',
'tenant_floating_ip_list',
'security_group_list',
@ -266,19 +326,12 @@ class FloatingIpViewTests(test.TestCase):
res = self.client.get(INDEX_URL +
"?tab=access_security_tabs__floating_ips_tab")
allocate_link = tables.AllocateIP()
url = allocate_link.get_link_url()
classes = (list(allocate_link.get_default_classes())
+ list(allocate_link.classes))
link_name = "%s (%s)" % (six.text_type(allocate_link.verbose_name),
"Quota exceeded")
expected_string = ("<a href='%s' title='%s' class='%s disabled' "
"id='floating_ips__action_allocate'>"
"<span class='fa fa-link'>"
"</span>%s</a>"
% (url, link_name, " ".join(classes), link_name))
self.assertContains(res, expected_string, html=True,
msg_prefix="The create button is not disabled")
allocate_action = self.getAndAssertTableAction(res, 'floating_ips',
'allocate')
self.assertTrue('disabled' in allocate_action.classes,
'The create button should be disabled')
self.assertEqual('Allocate IP To Project (Quota exceeded)',
six.text_type(allocate_action.verbose_name))
class FloatingIpNeutronViewTests(FloatingIpViewTests):

View File

@ -27,8 +27,6 @@ from horizon.workflows import views
from openstack_dashboard import api
from openstack_dashboard.dashboards.project.access_and_security \
import api_access
from openstack_dashboard.dashboards.project.access_and_security \
.security_groups import tables
from openstack_dashboard.test import helpers as test
from openstack_dashboard.usage import quotas
@ -163,6 +161,70 @@ class SecurityGroupTabTests(test.TestCase):
def setUp(self):
super(SecurityGroupTabTests, self).setUp()
@test.create_stubs({api.network: ('floating_ip_supported',
'tenant_floating_ip_list',
'security_group_list',
'floating_ip_pools_list',),
api.nova: ('keypair_list',
'server_list',),
quotas: ('tenant_quota_usages',),
api.base: ('is_service_enabled',)})
def test_create_button_attributes(self):
keypairs = self.keypairs.list()
floating_ips = self.floating_ips.list()
floating_pools = self.pools.list()
sec_groups = self.security_groups.list()
quota_data = self.quota_usages.first()
quota_data['security_groups']['available'] = 10
api.network.floating_ip_supported(
IsA(http.HttpRequest)) \
.AndReturn(True)
api.network.tenant_floating_ip_list(
IsA(http.HttpRequest)) \
.AndReturn(floating_ips)
api.network.floating_ip_pools_list(
IsA(http.HttpRequest)) \
.AndReturn(floating_pools)
api.network.security_group_list(
IsA(http.HttpRequest)) \
.AndReturn(sec_groups)
api.nova.keypair_list(
IsA(http.HttpRequest)) \
.AndReturn(keypairs)
api.nova.server_list(
IsA(http.HttpRequest)) \
.AndReturn([self.servers.list(), False])
quotas.tenant_quota_usages(
IsA(http.HttpRequest)).MultipleTimes() \
.AndReturn(quota_data)
api.base.is_service_enabled(
IsA(http.HttpRequest), 'network').MultipleTimes() \
.AndReturn(True)
api.base.is_service_enabled(
IsA(http.HttpRequest), 'ec2').MultipleTimes() \
.AndReturn(False)
self.mox.ReplayAll()
res = self.client.get(INDEX_URL +
"?tab=access_security_tabs__security_groups_tab")
security_groups = res.context['security_groups_table'].data
self.assertItemsEqual(security_groups, self.security_groups.list())
create_action = self.getAndAssertTableAction(res, 'security_groups',
'create')
self.assertEqual('Create Security Group',
six.text_type(create_action.verbose_name))
self.assertEqual(None, create_action.policy_rules)
self.assertEqual(set(['ajax-modal']), set(create_action.classes))
url = 'horizon:project:access_and_security:security_groups:create'
self.assertEqual(url, create_action.url)
@test.create_stubs({api.network: ('floating_ip_supported',
'tenant_floating_ip_list',
'security_group_list',
@ -217,18 +279,10 @@ class SecurityGroupTabTests(test.TestCase):
security_groups = res.context['security_groups_table'].data
self.assertItemsEqual(security_groups, self.security_groups.list())
create_link = tables.CreateGroup()
url = create_link.get_link_url()
classes = (list(create_link.get_default_classes())
+ list(create_link.classes))
link_name = "%s (%s)" % (six.text_type(create_link.verbose_name),
"Quota exceeded")
expected_string = "<a href='%s' title='%s' class='%s disabled' "\
"id='security_groups__action_create'>" \
"<span class='fa fa-plus'></span>%s</a>" \
% (url, link_name, " ".join(classes), link_name)
self.assertContains(res, expected_string, html=True,
msg_prefix="The create button is not disabled")
create_action = self.getAndAssertTableAction(res, 'security_groups',
'create')
self.assertTrue('disabled' in create_action.classes,
'The create button should be disabled')
def test_create_button_disabled_when_quota_exceeded_neutron_disabled(self):
self._test_create_button_disabled_when_quota_exceeded(False)

View File

@ -28,7 +28,6 @@ from django.core.urlresolvers import reverse
from django.forms import widgets
from django import http
import django.test
from django.utils import encoding
from django.utils.http import urlencode
from mox3.mox import IgnoreArg # noqa
from mox3.mox import IsA # noqa
@ -3587,6 +3586,54 @@ class InstanceTests(helpers.TestCase):
self.test_launch_form_instance_non_int_volume_size(
test_with_profile=True)
@helpers.create_stubs({
api.nova: ('flavor_list', 'server_list', 'tenant_absolute_limits',
'extension_supported',),
api.glance: ('image_list_detailed',),
api.network: ('floating_ip_simple_associate_supported',
'floating_ip_supported',
'servers_update_addresses',),
})
def test_launch_button_attributes(self):
servers = self.servers.list()
limits = self.limits['absolute']
limits['totalInstancesUsed'] = 0
api.nova.extension_supported('AdminActions',
IsA(http.HttpRequest)) \
.MultipleTimes().AndReturn(True)
api.nova.extension_supported('Shelve', IsA(http.HttpRequest)) \
.MultipleTimes().AndReturn(True)
api.nova.flavor_list(IsA(http.HttpRequest)) \
.AndReturn(self.flavors.list())
api.glance.image_list_detailed(IgnoreArg()) \
.AndReturn((self.images.list(), False, False))
search_opts = {'marker': None, 'paginate': True}
api.nova.server_list(IsA(http.HttpRequest), search_opts=search_opts) \
.AndReturn([servers, False])
api.network.servers_update_addresses(IsA(http.HttpRequest), servers)
api.nova.tenant_absolute_limits(IsA(http.HttpRequest), reserved=True) \
.MultipleTimes().AndReturn(limits)
api.network.floating_ip_supported(IsA(http.HttpRequest)) \
.MultipleTimes().AndReturn(True)
api.network.floating_ip_simple_associate_supported(
IsA(http.HttpRequest)).MultipleTimes().AndReturn(True)
self.mox.ReplayAll()
tables.LaunchLink()
res = self.client.get(INDEX_URL)
launch_action = self.getAndAssertTableAction(res, 'instances',
'launch')
self.assertEqual(set(['ajax-modal', 'ajax-update', 'btn-launch']),
set(launch_action.classes))
self.assertEqual('Launch Instance', launch_action.verbose_name)
self.assertEqual('horizon:project:instances:launch', launch_action.url)
self.assertEqual((('compute', 'compute:create'),),
launch_action.policy_rules)
@helpers.create_stubs({
api.nova: ('flavor_list', 'server_list', 'tenant_absolute_limits',
'extension_supported',),
@ -3622,27 +3669,16 @@ class InstanceTests(helpers.TestCase):
self.mox.ReplayAll()
launch = tables.LaunchLink()
url = launch.get_link_url()
classes = list(launch.get_default_classes()) + list(launch.classes)
link_name = "%s (%s)" % (six.text_type(launch.verbose_name),
"Quota exceeded")
tables.LaunchLink()
res = self.client.get(INDEX_URL)
if django.VERSION < (1, 8, 0):
resp_charset = res._charset
else:
resp_charset = res.charset
expected_string = encoding.smart_str(u'''
<a href="%s" title="%s" class="%s disabled"
data-update-url=
"/project/instances/?action=launch&amp;table=instances"
id="instances__action_launch">
<span class="fa fa-cloud-upload"></span>%s</a>
''' % (url, link_name, " ".join(classes), link_name), resp_charset)
self.assertContains(res, expected_string, html=True,
msg_prefix="The launch button is not disabled")
launch_action = self.getAndAssertTableAction(
res, 'instances', 'launch')
self.assertTrue('disabled' in launch_action.classes,
'The launch button should be disabled')
self.assertEqual('Launch Instance (Quota exceeded)',
six.text_type(launch_action.verbose_name))
@helpers.create_stubs({api.glance: ('image_list_detailed',),
api.neutron: ('network_list',),

View File

@ -19,10 +19,9 @@ from django.utils.html import escape
from horizon.workflows import views
from mox3.mox import IsA # noqa
import six
from openstack_dashboard import api
from openstack_dashboard.dashboards.project.networks.subnets import tables\
as subnets_tables
from openstack_dashboard.dashboards.project.networks import tables\
as networks_tables
from openstack_dashboard.dashboards.project.networks import workflows
@ -2013,7 +2012,8 @@ class NetworkSubnetTests(test.TestCase):
class NetworkViewTests(test.TestCase, NetworkStubMixin):
def _test_create_button_shown_when_quota_disabled(
self, expected_string):
self,
find_button_fn):
# if quota_data doesnt contain a networks|subnets|routers key or
# these keys are empty dicts, its disabled
quota_data = self.neutron_quota_usages.first()
@ -2033,11 +2033,14 @@ class NetworkViewTests(test.TestCase, NetworkStubMixin):
networks = res.context['networks_table'].data
self.assertItemsEqual(networks, self.networks.list())
self.assertContains(res, expected_string, True, html=True,
msg_prefix="The enabled create button not shown")
button = find_button_fn(res)
self.assertFalse('disabled' in button.classes,
"The create button should not be disabled")
return button
def _test_create_button_disabled_when_quota_exceeded(
self, expected_string, network_quota=5, subnet_quota=5):
self, find_button_fn, network_quota=5, subnet_quota=5, ):
quota_data = self.neutron_quota_usages.first()
@ -2056,69 +2059,55 @@ class NetworkViewTests(test.TestCase, NetworkStubMixin):
networks = res.context['networks_table'].data
self.assertItemsEqual(networks, self.networks.list())
self.assertContains(res, expected_string, True, html=True,
msg_prefix="The create button is not disabled")
button = find_button_fn(res)
self.assertTrue('disabled' in button.classes,
"The create button should be disabled")
return button
@test.create_stubs({api.neutron: ('network_list',),
quotas: ('tenant_quota_usages',)})
def test_network_create_button_disabled_when_quota_exceeded_index(self):
create_link = networks_tables.CreateNetwork()
url = create_link.get_link_url()
classes = (list(create_link.get_default_classes())
+ list(create_link.classes))
link_name = "%s (%s)" % (create_link.verbose_name, "Quota exceeded")
expected_string = "<a href='%s' title='%s' class='%s disabled' "\
"id='networks__action_create'>" \
"<span class='fa fa-plus'></span>%s</a>" \
% (url, link_name, " ".join(classes), link_name)
self._test_create_button_disabled_when_quota_exceeded(expected_string,
network_quota=0
)
networks_tables.CreateNetwork()
def _find_net_button(res):
return self.getAndAssertTableAction(res, 'networks', 'create')
self._test_create_button_disabled_when_quota_exceeded(_find_net_button,
network_quota=0)
@test.create_stubs({api.neutron: ('network_list',),
quotas: ('tenant_quota_usages',)})
def test_subnet_create_button_disabled_when_quota_exceeded_index(self):
network_id = self.networks.first().id
create_link = networks_tables.CreateSubnet()
url = reverse(create_link.get_link_url(), args=[network_id])
classes = (list(create_link.get_default_classes())
+ list(create_link.classes))
link_name = "%s (%s)" % (create_link.verbose_name, "Quota exceeded")
expected_string = "<a href='%s' class='%s disabled' " \
"id='networks__row_%s__action_subnet'>%s</a>" \
% (url, " ".join(classes), network_id, link_name)
self._test_create_button_disabled_when_quota_exceeded(expected_string,
subnet_quota=0
)
networks_tables.CreateSubnet()
def _find_subnet_button(res):
return self.getAndAssertTableRowAction(res, 'networks',
'subnet', network_id)
self._test_create_button_disabled_when_quota_exceeded(
_find_subnet_button, subnet_quota=0)
@test.create_stubs({api.neutron: ('network_list',),
quotas: ('tenant_quota_usages',)})
def test_network_create_button_shown_when_quota_disabled_index(self):
# if quota_data doesnt contain a networks["available"] key its disabled
create_link = networks_tables.CreateNetwork()
url = create_link.get_link_url()
classes = (list(create_link.get_default_classes())
+ list(create_link.classes))
expected_string = "<a href='%s' title='%s' class='%s' "\
"id='networks__action_create'>" \
"<span class='fa fa-plus'></span>%s</a>" \
% (url, create_link.verbose_name, " ".join(classes),
create_link.verbose_name)
self._test_create_button_shown_when_quota_disabled(expected_string)
networks_tables.CreateNetwork()
self._test_create_button_shown_when_quota_disabled(
lambda res: self.getAndAssertTableAction(res, 'networks', 'create')
)
@test.create_stubs({api.neutron: ('network_list',),
quotas: ('tenant_quota_usages',)})
def test_subnet_create_button_shown_when_quota_disabled_index(self):
# if quota_data doesnt contain a subnets["available"] key, its disabled
network_id = self.networks.first().id
create_link = networks_tables.CreateSubnet()
url = reverse(create_link.get_link_url(), args=[network_id])
classes = (list(create_link.get_default_classes())
+ list(create_link.classes))
expected_string = "<a href='%s' class='%s' "\
"id='networks__row_%s__action_subnet'>%s</a>" \
% (url, " ".join(classes), network_id, create_link.verbose_name)
self._test_create_button_shown_when_quota_disabled(expected_string)
def _find_subnet_button(res):
return self.getAndAssertTableRowAction(res, 'networks',
'subnet', network_id)
self._test_create_button_shown_when_quota_disabled(_find_subnet_button)
@test.create_stubs({api.neutron: ('network_get',
'subnet_list',
@ -2155,17 +2144,65 @@ class NetworkViewTests(test.TestCase, NetworkStubMixin):
subnets = res.context['subnets_table'].data
self.assertItemsEqual(subnets, self.subnets.list())
class FakeTable(object):
kwargs = {'network_id': network_id}
create_link = subnets_tables.CreateSubnet()
create_link.table = FakeTable()
url = create_link.get_link_url()
classes = (list(create_link.get_default_classes())
+ list(create_link.classes))
link_name = "%s (%s)" % (create_link.verbose_name, "Quota exceeded")
expected_string = "<a href='%s' title='%s' class='%s disabled' "\
"id='subnets__action_create'>" \
"<span class='fa fa-plus'></span>%s</a>" \
% (url, link_name, " ".join(classes), link_name)
self.assertContains(res, expected_string, html=True,
msg_prefix="The create button is not disabled")
create_action = self.getAndAssertTableAction(res, 'subnets', 'create')
self.assertTrue('disabled' in create_action.classes,
'The create button should be disabled')
@test.create_stubs({api.neutron: ('network_list',),
quotas: ('tenant_quota_usages',)})
def test_create_button_attributes(self):
create_action = self._test_create_button_shown_when_quota_disabled(
lambda res: self.getAndAssertTableAction(res, 'networks', 'create')
)
self.assertEqual(set(['ajax-modal']), set(create_action.classes))
self.assertEqual('horizon:project:networks:create', create_action.url)
self.assertEqual('Create Network',
six.text_type(create_action.verbose_name))
self.assertEqual((('network', 'create_network'),),
create_action.policy_rules)
@test.create_stubs({api.neutron: ('network_get',
'subnet_list',
'port_list',
'is_extension_supported',),
quotas: ('tenant_quota_usages',)})
def test_create_subnet_button_attributes(self):
network_id = self.networks.first().id
quota_data = self.neutron_quota_usages.first()
quota_data['subnets']['available'] = 1
api.neutron.network_get(
IsA(http.HttpRequest), network_id)\
.MultipleTimes().AndReturn(self.networks.first())
api.neutron.subnet_list(
IsA(http.HttpRequest), network_id=network_id)\
.AndReturn(self.subnets.list())
api.neutron.port_list(
IsA(http.HttpRequest), network_id=network_id)\
.AndReturn([self.ports.first()])
api.neutron.is_extension_supported(
IsA(http.HttpRequest), 'mac-learning')\
.AndReturn(False)
quotas.tenant_quota_usages(
IsA(http.HttpRequest)) \
.MultipleTimes().AndReturn(quota_data)
self.mox.ReplayAll()
res = self.client.get(reverse('horizon:project:networks:detail',
args=[network_id]))
self.assertTemplateUsed(res, 'project/networks/detail.html')
subnets = res.context['subnets_table'].data
self.assertItemsEqual(subnets, self.subnets.list())
create_action = self.getAndAssertTableAction(res, 'subnets', 'create')
self.assertEqual(set(['ajax-modal']), set(create_action.classes))
self.assertEqual('horizon:project:networks:addsubnet',
create_action.url)
self.assertEqual('Create Subnet',
six.text_type(create_action.verbose_name))
self.assertEqual((('network', 'create_subnet'),),
create_action.policy_rules)

View File

@ -23,7 +23,6 @@ import six
from openstack_dashboard import api
from openstack_dashboard.dashboards.project.routers.extensions.routerrules\
import rulemanager
from openstack_dashboard.dashboards.project.routers import tables
from openstack_dashboard.test import helpers as test
from openstack_dashboard.usage import quotas
@ -938,18 +937,11 @@ class RouterViewTests(RouterMixin, test.TestCase):
routers = res.context['Routers_table'].data
self.assertItemsEqual(routers, self.routers.list())
create_link = tables.CreateRouter()
url = create_link.get_link_url()
classes = (list(create_link.get_default_classes())
+ list(create_link.classes))
link_name = "%s (%s)" % (six.text_type(create_link.verbose_name),
"Quota exceeded")
expected_string = "<a href='%s' title='%s' class='%s disabled' "\
"id='Routers__action_create'>" \
"<span class='fa fa-plus'></span>%s</a>" \
% (url, link_name, " ".join(classes), link_name)
self.assertContains(res, expected_string, html=True,
msg_prefix="The create button is not disabled")
create_action = self.getAndAssertTableAction(res, 'Routers', 'create')
self.assertTrue('disabled' in create_action.classes,
'Create button is not disabled')
self.assertEqual('Create Router (Quota exceeded)',
create_action.verbose_name)
@test.create_stubs({api.neutron: ('router_list', 'network_list'),
quotas: ('tenant_quota_usages',)})
@ -973,14 +965,38 @@ class RouterViewTests(RouterMixin, test.TestCase):
routers = res.context['Routers_table'].data
self.assertItemsEqual(routers, self.routers.list())
create_link = tables.CreateRouter()
url = create_link.get_link_url()
classes = (list(create_link.get_default_classes())
+ list(create_link.classes))
link_name = "%s" % (six.text_type(create_link.verbose_name))
expected_string = "<a href='%s' title='%s' class='%s' "\
"id='Routers__action_create'>" \
"<span class='fa fa-plus'></span>%s</a>" \
% (url, link_name, " ".join(classes), link_name)
self.assertContains(res, expected_string, html=True,
msg_prefix="The create button is not displayed")
create_action = self.getAndAssertTableAction(res, 'Routers', 'create')
self.assertFalse('disabled' in create_action.classes,
'Create button should not be disabled')
self.assertEqual('Create Router',
create_action.verbose_name)
@test.create_stubs({api.neutron: ('router_list', 'network_list'),
quotas: ('tenant_quota_usages',)})
def test_create_button_attributes(self):
quota_data = self.neutron_quota_usages.first()
quota_data['routers']['available'] = 10
api.neutron.router_list(
IsA(http.HttpRequest),
tenant_id=self.tenant.id,
search_opts=None).AndReturn(self.routers.list())
quotas.tenant_quota_usages(
IsA(http.HttpRequest)) \
.MultipleTimes().AndReturn(quota_data)
self._mock_external_network_list()
self.mox.ReplayAll()
res = self.client.get(self.INDEX_URL)
self.assertTemplateUsed(res, 'project/routers/index.html')
routers = res.context['Routers_table'].data
self.assertItemsEqual(routers, self.routers.list())
create_action = self.getAndAssertTableAction(res, 'Routers', 'create')
self.assertEqual(set(['ajax-modal']), set(create_action.classes))
self.assertEqual('Create Router',
six.text_type(create_action.verbose_name))
self.assertEqual('horizon:project:routers:create', create_action.url)
self.assertEqual((('network', 'create_router'),),
create_action.policy_rules)

View File

@ -15,7 +15,6 @@
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import django
from django.core.urlresolvers import reverse
from django.forms import widgets
@ -24,11 +23,10 @@ from django.test.utils import override_settings
from mox3.mox import IsA # noqa
import six
from six import moves
from openstack_dashboard import api
from openstack_dashboard.api import cinder
from openstack_dashboard.dashboards.project.volumes \
.volumes import tables
from openstack_dashboard.test import helpers as test
from openstack_dashboard.usage import quotas
@ -1021,6 +1019,50 @@ class VolumeViewTests(test.TestCase):
server.id)
self.assertEqual(res.status_code, 200)
def _get_volume_row_action_from_ajax(self, res, action_name, row_id):
def _matches_row_id(context_row):
return (len(context_row.dicts) > 1 and
isinstance(context_row.dicts[1], dict) and
context_row.dicts[1].get('row_id', None) == row_id)
matching = list(moves.filter(lambda r: _matches_row_id(r),
res.context))
self.assertTrue(len(matching) > 1,
"Expected at least one row matching %s" % row_id)
row = matching[-1].dicts[1]
matching_actions = list(moves.filter(lambda a: a.name == action_name,
row['row_actions']))
self.assertEqual(1, len(matching_actions),
"Expected one row action named '%s'" % action_name)
return matching_actions[0]
@test.create_stubs({cinder: ('tenant_absolute_limits',
'volume_get',)})
def test_create_snapshot_button_attributes(self):
limits = {'maxTotalSnapshots': 2}
limits['totalSnapshotsUsed'] = 1
volume = self.cinder_volumes.first()
cinder.volume_get(IsA(http.HttpRequest), volume.id).AndReturn(volume)
cinder.tenant_absolute_limits(IsA(http.HttpRequest)).AndReturn(limits)
self.mox.ReplayAll()
res_url = (VOLUME_INDEX_URL +
"?action=row_update&table=volumes&obj_id=" + volume.id)
res = self.client.get(res_url, {},
HTTP_X_REQUESTED_WITH='XMLHttpRequest')
snapshot_action = self._get_volume_row_action_from_ajax(
res, 'snapshots', volume.id)
self.assertEqual('horizon:project:volumes:volumes:create_snapshot',
snapshot_action.url)
self.assertEqual(set(['ajax-modal']), set(snapshot_action.classes))
self.assertEqual('Create Snapshot',
six.text_type(snapshot_action.verbose_name))
self.assertEqual((('volume', 'volume:create_snapshot'),),
snapshot_action.policy_rules)
@test.create_stubs({cinder: ('tenant_absolute_limits',
'volume_get',)})
def test_create_snapshot_button_disabled_when_quota_exceeded(self):
@ -1032,25 +1074,57 @@ class VolumeViewTests(test.TestCase):
cinder.tenant_absolute_limits(IsA(http.HttpRequest)).AndReturn(limits)
self.mox.ReplayAll()
create_link = tables.CreateSnapshot()
url = reverse(create_link.get_link_url(), args=[volume.id])
res_url = (VOLUME_INDEX_URL +
"?action=row_update&table=volumes&obj_id=" + volume.id)
res = self.client.get(res_url, {},
HTTP_X_REQUESTED_WITH='XMLHttpRequest')
classes = (list(create_link.get_default_classes())
+ list(create_link.classes))
link_name = "%s (%s)" % (six.text_type(create_link.verbose_name),
"Quota exceeded")
expected_string = "<a href='%s' class=\"%s disabled\" "\
"id=\"volumes__row_%s__action_snapshots\">%s</a>" \
% (url, " ".join(classes), volume.id, link_name)
snapshot_action = self._get_volume_row_action_from_ajax(
res, 'snapshots', volume.id)
self.assertTrue('disabled' in snapshot_action.classes,
'The create snapshot button should be disabled')
self.assertContains(
res, expected_string, html=True,
msg_prefix="The create snapshot button is not disabled")
@test.create_stubs({cinder: ('tenant_absolute_limits',
'volume_list',
'volume_snapshot_list',
'volume_backup_supported',),
api.nova: ('server_list',)})
def test_create_button_attributes(self):
limits = self.cinder_limits['absolute']
limits['maxTotalVolumes'] = 10
limits['totalVolumesUsed'] = 1
volumes = self.cinder_volumes.list()
api.cinder.volume_backup_supported(IsA(http.HttpRequest)). \
MultipleTimes().AndReturn(True)
cinder.volume_list(IsA(http.HttpRequest), search_opts=None)\
.AndReturn(volumes)
cinder.volume_snapshot_list(IsA(http.HttpRequest),
search_opts=None).\
AndReturn([])
api.nova.server_list(IsA(http.HttpRequest), search_opts=None)\
.AndReturn([self.servers.list(), False])
cinder.tenant_absolute_limits(IsA(http.HttpRequest))\
.MultipleTimes().AndReturn(limits)
self.mox.ReplayAll()
res = self.client.get(VOLUME_INDEX_URL)
self.assertTemplateUsed(res, 'project/volumes/index.html')
volumes = res.context['volumes_table'].data
self.assertItemsEqual(volumes, self.cinder_volumes.list())
create_action = self.getAndAssertTableAction(res, 'volumes', 'create')
self.assertEqual(set(['ajax-modal', 'ajax-update', 'btn-create']),
set(create_action.classes))
self.assertEqual('Create Volume',
six.text_type(create_action.verbose_name))
self.assertEqual('horizon:project:volumes:volumes:create',
create_action.url)
self.assertEqual((('volume', 'volume:create'),),
create_action.policy_rules)
@test.create_stubs({cinder: ('tenant_absolute_limits',
'volume_list',
@ -1081,19 +1155,9 @@ class VolumeViewTests(test.TestCase):
volumes = res.context['volumes_table'].data
self.assertItemsEqual(volumes, self.cinder_volumes.list())
create_link = tables.CreateVolume()
url = create_link.get_link_url()
classes = (list(create_link.get_default_classes())
+ list(create_link.classes))
link_name = "%s (%s)" % (six.text_type(create_link.verbose_name),
"Quota exceeded")
expected_string = "<a href='%s' title='%s' class='%s disabled' "\
"id='volumes__action_create' data-update-url=" \
"'/project/volumes/?action=create&amp;table=volumes'> "\
"<span class='fa fa-plus'></span>%s</a>" \
% (url, link_name, " ".join(classes), link_name)
self.assertContains(res, expected_string, html=True,
msg_prefix="The create button is not disabled")
create_action = self.getAndAssertTableAction(res, 'volumes', 'create')
self.assertTrue('disabled' in create_action.classes,
'The create button should be disabled')
@test.create_stubs({cinder: ('tenant_absolute_limits',
'volume_get',),

View File

@ -280,6 +280,44 @@ class TestCase(horizon_helpers.TestCase):
def assertItemsCollectionEqual(self, response, items_list):
self.assertEqual(response.json, {"items": items_list})
def getAndAssertTableRowAction(self, response, table_name,
action_name, row_id):
table = response.context[table_name + '_table']
full_row_id = '%s__row__%s' % (table_name, row_id)
rows = list(moves.filter(lambda x: x.id == full_row_id,
table.get_rows()))
self.assertEqual(1, len(rows),
"Did not find a row matching id '%s'" % row_id)
row_actions = table.get_row_actions(rows[0])
msg_args = (table_name, action_name, row_id)
self.assertTrue(
len(row_actions) > 0,
"No action named '%s' found in table '%s' row '%s'" % msg_args)
self.assertEqual(
1, len(row_actions),
"Multiple actions '%s' found in table '%s' row '%s'" % msg_args)
return row_actions[0]
def getAndAssertTableAction(self, response, table_name, action_name):
table = response.context[table_name + '_table']
table_actions = table.get_table_actions()
actions = list(moves.filter(lambda x: x.name == action_name,
table_actions))
msg_args = (table_name, action_name)
self.assertTrue(
len(actions) > 0,
"No action named '%s' found in table '%s'" % msg_args)
self.assertEqual(
1, len(actions),
"More than one action named '%s' found in table '%s'" % msg_args)
return actions[0]
@staticmethod
def mock_rest_request(**args):
mock_args = {