diff --git a/openstack_dashboard/dashboards/project/access_and_security/floating_ips/tests.py b/openstack_dashboard/dashboards/project/access_and_security/floating_ips/tests.py
index b189195c58..e6c85215cc 100644
--- a/openstack_dashboard/dashboards/project/access_and_security/floating_ips/tests.py
+++ b/openstack_dashboard/dashboards/project/access_and_security/floating_ips/tests.py
@@ -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 = (""
- ""
- "%s"
- % (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):
diff --git a/openstack_dashboard/dashboards/project/access_and_security/tests.py b/openstack_dashboard/dashboards/project/access_and_security/tests.py
index f6f374a76b..046c74fd1e 100644
--- a/openstack_dashboard/dashboards/project/access_and_security/tests.py
+++ b/openstack_dashboard/dashboards/project/access_and_security/tests.py
@@ -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 = "" \
- "%s" \
- % (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)
diff --git a/openstack_dashboard/dashboards/project/instances/tests.py b/openstack_dashboard/dashboards/project/instances/tests.py
index 2708e59b25..4c99966b67 100644
--- a/openstack_dashboard/dashboards/project/instances/tests.py
+++ b/openstack_dashboard/dashboards/project/instances/tests.py
@@ -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'''
-
- %s
- ''' % (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',),
diff --git a/openstack_dashboard/dashboards/project/networks/tests.py b/openstack_dashboard/dashboards/project/networks/tests.py
index 4b50302d85..7e433bd371 100644
--- a/openstack_dashboard/dashboards/project/networks/tests.py
+++ b/openstack_dashboard/dashboards/project/networks/tests.py
@@ -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 = "" \
- "%s" \
- % (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 = "%s" \
- % (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 = "" \
- "%s" \
- % (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 = "%s" \
- % (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 = "" \
- "%s" \
- % (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)
diff --git a/openstack_dashboard/dashboards/project/routers/tests.py b/openstack_dashboard/dashboards/project/routers/tests.py
index 5aad93ddb5..ce0116c123 100644
--- a/openstack_dashboard/dashboards/project/routers/tests.py
+++ b/openstack_dashboard/dashboards/project/routers/tests.py
@@ -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 = "" \
- "%s" \
- % (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 = "" \
- "%s" \
- % (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)
diff --git a/openstack_dashboard/dashboards/project/volumes/volumes/tests.py b/openstack_dashboard/dashboards/project/volumes/volumes/tests.py
index ab7fbb641f..b6a70d0554 100644
--- a/openstack_dashboard/dashboards/project/volumes/volumes/tests.py
+++ b/openstack_dashboard/dashboards/project/volumes/volumes/tests.py
@@ -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 = "%s" \
- % (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 = " "\
- "%s" \
- % (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',),
diff --git a/openstack_dashboard/test/helpers.py b/openstack_dashboard/test/helpers.py
index b108d6be0e..b01216b0d0 100644
--- a/openstack_dashboard/test/helpers.py
+++ b/openstack_dashboard/test/helpers.py
@@ -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 = {