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 = {