Browse Source

Merge "Make unit testing less reliant on HTML fragments"

tags/9.0.0.0b1
Jenkins 3 years ago
parent
commit
b06b36fcd0

+ 68
- 15
openstack_dashboard/dashboards/project/access_and_security/floating_ips/tests.py View File

@@ -25,8 +25,6 @@ from mox3.mox import IsA  # noqa
25 25
 import six
26 26
 
27 27
 from openstack_dashboard import api
28
-from openstack_dashboard.dashboards.project.access_and_security \
29
-    .floating_ips import tables
30 28
 from openstack_dashboard.test import helpers as test
31 29
 from openstack_dashboard.usage import quotas
32 30
 
@@ -214,6 +212,68 @@ class FloatingIpViewTests(test.TestCase):
214 212
         res = self.client.post(INDEX_URL, {"action": action})
215 213
         self.assertRedirectsNoFollow(res, INDEX_URL)
216 214
 
215
+    @test.create_stubs({api.network: ('floating_ip_supported',
216
+                                      'tenant_floating_ip_list',
217
+                                      'security_group_list',
218
+                                      'floating_ip_pools_list',),
219
+                        api.nova: ('keypair_list',
220
+                                   'server_list',),
221
+                        quotas: ('tenant_quota_usages',),
222
+                        api.base: ('is_service_enabled',)})
223
+    def test_allocate_button_attributes(self):
224
+        keypairs = self.keypairs.list()
225
+        floating_ips = self.floating_ips.list()
226
+        floating_pools = self.pools.list()
227
+        quota_data = self.quota_usages.first()
228
+        quota_data['floating_ips']['available'] = 10
229
+        sec_groups = self.security_groups.list()
230
+
231
+        api.network.floating_ip_supported(
232
+            IsA(http.HttpRequest)) \
233
+            .AndReturn(True)
234
+        api.network.tenant_floating_ip_list(
235
+            IsA(http.HttpRequest)) \
236
+            .AndReturn(floating_ips)
237
+        api.network.security_group_list(
238
+            IsA(http.HttpRequest)).MultipleTimes()\
239
+            .AndReturn(sec_groups)
240
+        api.network.floating_ip_pools_list(
241
+            IsA(http.HttpRequest)) \
242
+            .AndReturn(floating_pools)
243
+        api.nova.keypair_list(
244
+            IsA(http.HttpRequest)) \
245
+            .AndReturn(keypairs)
246
+        api.nova.server_list(
247
+            IsA(http.HttpRequest)) \
248
+            .AndReturn([self.servers.list(), False])
249
+        quotas.tenant_quota_usages(
250
+            IsA(http.HttpRequest)).MultipleTimes() \
251
+            .AndReturn(quota_data)
252
+
253
+        api.base.is_service_enabled(
254
+            IsA(http.HttpRequest),
255
+            'network').MultipleTimes() \
256
+            .AndReturn(True)
257
+        api.base.is_service_enabled(
258
+            IsA(http.HttpRequest),
259
+            'ec2').MultipleTimes() \
260
+            .AndReturn(False)
261
+
262
+        self.mox.ReplayAll()
263
+
264
+        res = self.client.get(INDEX_URL +
265
+                              "?tab=access_security_tabs__floating_ips_tab")
266
+
267
+        allocate_action = self.getAndAssertTableAction(res, 'floating_ips',
268
+                                                       'allocate')
269
+        self.assertEqual(set(['ajax-modal']), set(allocate_action.classes))
270
+        self.assertEqual('Allocate IP To Project',
271
+                         six.text_type(allocate_action.verbose_name))
272
+        self.assertEqual(None, allocate_action.policy_rules)
273
+
274
+        url = 'horizon:project:access_and_security:floating_ips:allocate'
275
+        self.assertEqual(url, allocate_action.url)
276
+
217 277
     @test.create_stubs({api.network: ('floating_ip_supported',
218 278
                                       'tenant_floating_ip_list',
219 279
                                       'security_group_list',
@@ -266,19 +326,12 @@ class FloatingIpViewTests(test.TestCase):
266 326
         res = self.client.get(INDEX_URL +
267 327
                               "?tab=access_security_tabs__floating_ips_tab")
268 328
 
269
-        allocate_link = tables.AllocateIP()
270
-        url = allocate_link.get_link_url()
271
-        classes = (list(allocate_link.get_default_classes())
272
-                   + list(allocate_link.classes))
273
-        link_name = "%s (%s)" % (six.text_type(allocate_link.verbose_name),
274
-                                 "Quota exceeded")
275
-        expected_string = ("<a href='%s' title='%s' class='%s disabled' "
276
-                           "id='floating_ips__action_allocate'>"
277
-                           "<span class='fa fa-link'>"
278
-                           "</span>%s</a>"
279
-                           % (url, link_name, " ".join(classes), link_name))
280
-        self.assertContains(res, expected_string, html=True,
281
-                            msg_prefix="The create button is not disabled")
329
+        allocate_action = self.getAndAssertTableAction(res, 'floating_ips',
330
+                                                       'allocate')
331
+        self.assertTrue('disabled' in allocate_action.classes,
332
+                        'The create button should be disabled')
333
+        self.assertEqual('Allocate IP To Project (Quota exceeded)',
334
+                         six.text_type(allocate_action.verbose_name))
282 335
 
283 336
 
284 337
 class FloatingIpNeutronViewTests(FloatingIpViewTests):

+ 68
- 14
openstack_dashboard/dashboards/project/access_and_security/tests.py View File

@@ -27,8 +27,6 @@ from horizon.workflows import views
27 27
 from openstack_dashboard import api
28 28
 from openstack_dashboard.dashboards.project.access_and_security \
29 29
     import api_access
30
-from openstack_dashboard.dashboards.project.access_and_security \
31
-    .security_groups import tables
32 30
 from openstack_dashboard.test import helpers as test
33 31
 from openstack_dashboard.usage import quotas
34 32
 
@@ -163,6 +161,70 @@ class SecurityGroupTabTests(test.TestCase):
163 161
     def setUp(self):
164 162
         super(SecurityGroupTabTests, self).setUp()
165 163
 
164
+    @test.create_stubs({api.network: ('floating_ip_supported',
165
+                                      'tenant_floating_ip_list',
166
+                                      'security_group_list',
167
+                                      'floating_ip_pools_list',),
168
+                        api.nova: ('keypair_list',
169
+                                   'server_list',),
170
+                        quotas: ('tenant_quota_usages',),
171
+                        api.base: ('is_service_enabled',)})
172
+    def test_create_button_attributes(self):
173
+        keypairs = self.keypairs.list()
174
+        floating_ips = self.floating_ips.list()
175
+        floating_pools = self.pools.list()
176
+        sec_groups = self.security_groups.list()
177
+        quota_data = self.quota_usages.first()
178
+        quota_data['security_groups']['available'] = 10
179
+
180
+        api.network.floating_ip_supported(
181
+            IsA(http.HttpRequest)) \
182
+            .AndReturn(True)
183
+        api.network.tenant_floating_ip_list(
184
+            IsA(http.HttpRequest)) \
185
+            .AndReturn(floating_ips)
186
+        api.network.floating_ip_pools_list(
187
+            IsA(http.HttpRequest)) \
188
+            .AndReturn(floating_pools)
189
+        api.network.security_group_list(
190
+            IsA(http.HttpRequest)) \
191
+            .AndReturn(sec_groups)
192
+        api.nova.keypair_list(
193
+            IsA(http.HttpRequest)) \
194
+            .AndReturn(keypairs)
195
+        api.nova.server_list(
196
+            IsA(http.HttpRequest)) \
197
+            .AndReturn([self.servers.list(), False])
198
+        quotas.tenant_quota_usages(
199
+            IsA(http.HttpRequest)).MultipleTimes() \
200
+            .AndReturn(quota_data)
201
+
202
+        api.base.is_service_enabled(
203
+            IsA(http.HttpRequest), 'network').MultipleTimes() \
204
+            .AndReturn(True)
205
+        api.base.is_service_enabled(
206
+            IsA(http.HttpRequest), 'ec2').MultipleTimes() \
207
+            .AndReturn(False)
208
+
209
+        self.mox.ReplayAll()
210
+
211
+        res = self.client.get(INDEX_URL +
212
+                              "?tab=access_security_tabs__security_groups_tab")
213
+
214
+        security_groups = res.context['security_groups_table'].data
215
+        self.assertItemsEqual(security_groups, self.security_groups.list())
216
+
217
+        create_action = self.getAndAssertTableAction(res, 'security_groups',
218
+                                                     'create')
219
+
220
+        self.assertEqual('Create Security Group',
221
+                         six.text_type(create_action.verbose_name))
222
+        self.assertEqual(None, create_action.policy_rules)
223
+        self.assertEqual(set(['ajax-modal']), set(create_action.classes))
224
+
225
+        url = 'horizon:project:access_and_security:security_groups:create'
226
+        self.assertEqual(url, create_action.url)
227
+
166 228
     @test.create_stubs({api.network: ('floating_ip_supported',
167 229
                                       'tenant_floating_ip_list',
168 230
                                       'security_group_list',
@@ -217,18 +279,10 @@ class SecurityGroupTabTests(test.TestCase):
217 279
         security_groups = res.context['security_groups_table'].data
218 280
         self.assertItemsEqual(security_groups, self.security_groups.list())
219 281
 
220
-        create_link = tables.CreateGroup()
221
-        url = create_link.get_link_url()
222
-        classes = (list(create_link.get_default_classes())
223
-                   + list(create_link.classes))
224
-        link_name = "%s (%s)" % (six.text_type(create_link.verbose_name),
225
-                                 "Quota exceeded")
226
-        expected_string = "<a href='%s' title='%s'  class='%s disabled' "\
227
-            "id='security_groups__action_create'>" \
228
-            "<span class='fa fa-plus'></span>%s</a>" \
229
-            % (url, link_name, " ".join(classes), link_name)
230
-        self.assertContains(res, expected_string, html=True,
231
-                            msg_prefix="The create button is not disabled")
282
+        create_action = self.getAndAssertTableAction(res, 'security_groups',
283
+                                                     'create')
284
+        self.assertTrue('disabled' in create_action.classes,
285
+                        'The create button should be disabled')
232 286
 
233 287
     def test_create_button_disabled_when_quota_exceeded_neutron_disabled(self):
234 288
         self._test_create_button_disabled_when_quota_exceeded(False)

+ 57
- 21
openstack_dashboard/dashboards/project/instances/tests.py View File

@@ -28,7 +28,6 @@ from django.core.urlresolvers import reverse
28 28
 from django.forms import widgets
29 29
 from django import http
30 30
 import django.test
31
-from django.utils import encoding
32 31
 from django.utils.http import urlencode
33 32
 from mox3.mox import IgnoreArg  # noqa
34 33
 from mox3.mox import IsA  # noqa
@@ -3587,6 +3586,54 @@ class InstanceTests(helpers.TestCase):
3587 3586
         self.test_launch_form_instance_non_int_volume_size(
3588 3587
             test_with_profile=True)
3589 3588
 
3589
+    @helpers.create_stubs({
3590
+        api.nova: ('flavor_list', 'server_list', 'tenant_absolute_limits',
3591
+                   'extension_supported',),
3592
+        api.glance: ('image_list_detailed',),
3593
+        api.network: ('floating_ip_simple_associate_supported',
3594
+                      'floating_ip_supported',
3595
+                      'servers_update_addresses',),
3596
+    })
3597
+    def test_launch_button_attributes(self):
3598
+        servers = self.servers.list()
3599
+        limits = self.limits['absolute']
3600
+        limits['totalInstancesUsed'] = 0
3601
+
3602
+        api.nova.extension_supported('AdminActions',
3603
+                                     IsA(http.HttpRequest)) \
3604
+            .MultipleTimes().AndReturn(True)
3605
+        api.nova.extension_supported('Shelve', IsA(http.HttpRequest)) \
3606
+            .MultipleTimes().AndReturn(True)
3607
+        api.nova.flavor_list(IsA(http.HttpRequest)) \
3608
+            .AndReturn(self.flavors.list())
3609
+        api.glance.image_list_detailed(IgnoreArg()) \
3610
+            .AndReturn((self.images.list(), False, False))
3611
+        search_opts = {'marker': None, 'paginate': True}
3612
+        api.nova.server_list(IsA(http.HttpRequest), search_opts=search_opts) \
3613
+            .AndReturn([servers, False])
3614
+        api.network.servers_update_addresses(IsA(http.HttpRequest), servers)
3615
+        api.nova.tenant_absolute_limits(IsA(http.HttpRequest), reserved=True) \
3616
+            .MultipleTimes().AndReturn(limits)
3617
+        api.network.floating_ip_supported(IsA(http.HttpRequest)) \
3618
+            .MultipleTimes().AndReturn(True)
3619
+        api.network.floating_ip_simple_associate_supported(
3620
+            IsA(http.HttpRequest)).MultipleTimes().AndReturn(True)
3621
+
3622
+        self.mox.ReplayAll()
3623
+
3624
+        tables.LaunchLink()
3625
+        res = self.client.get(INDEX_URL)
3626
+
3627
+        launch_action = self.getAndAssertTableAction(res, 'instances',
3628
+                                                     'launch')
3629
+
3630
+        self.assertEqual(set(['ajax-modal', 'ajax-update', 'btn-launch']),
3631
+                         set(launch_action.classes))
3632
+        self.assertEqual('Launch Instance', launch_action.verbose_name)
3633
+        self.assertEqual('horizon:project:instances:launch', launch_action.url)
3634
+        self.assertEqual((('compute', 'compute:create'),),
3635
+                         launch_action.policy_rules)
3636
+
3590 3637
     @helpers.create_stubs({
3591 3638
         api.nova: ('flavor_list', 'server_list', 'tenant_absolute_limits',
3592 3639
                    'extension_supported',),
@@ -3622,27 +3669,16 @@ class InstanceTests(helpers.TestCase):
3622 3669
 
3623 3670
         self.mox.ReplayAll()
3624 3671
 
3625
-        launch = tables.LaunchLink()
3626
-        url = launch.get_link_url()
3627
-        classes = list(launch.get_default_classes()) + list(launch.classes)
3628
-        link_name = "%s (%s)" % (six.text_type(launch.verbose_name),
3629
-                                 "Quota exceeded")
3630
-
3672
+        tables.LaunchLink()
3631 3673
         res = self.client.get(INDEX_URL)
3632
-        if django.VERSION < (1, 8, 0):
3633
-            resp_charset = res._charset
3634
-        else:
3635
-            resp_charset = res.charset
3636
-        expected_string = encoding.smart_str(u'''
3637
-            <a href="%s" title="%s" class="%s disabled"
3638
-            data-update-url=
3639
-            "/project/instances/?action=launch&amp;table=instances"
3640
-            id="instances__action_launch">
3641
-            <span class="fa fa-cloud-upload"></span>%s</a>
3642
-            ''' % (url, link_name, " ".join(classes), link_name), resp_charset)
3643
-
3644
-        self.assertContains(res, expected_string, html=True,
3645
-                            msg_prefix="The launch button is not disabled")
3674
+
3675
+        launch_action = self.getAndAssertTableAction(
3676
+            res, 'instances', 'launch')
3677
+
3678
+        self.assertTrue('disabled' in launch_action.classes,
3679
+                        'The launch button should be disabled')
3680
+        self.assertEqual('Launch Instance (Quota exceeded)',
3681
+                         six.text_type(launch_action.verbose_name))
3646 3682
 
3647 3683
     @helpers.create_stubs({api.glance: ('image_list_detailed',),
3648 3684
                            api.neutron: ('network_list',),

+ 100
- 63
openstack_dashboard/dashboards/project/networks/tests.py View File

@@ -19,10 +19,9 @@ from django.utils.html import escape
19 19
 from horizon.workflows import views
20 20
 
21 21
 from mox3.mox import IsA  # noqa
22
+import six
22 23
 
23 24
 from openstack_dashboard import api
24
-from openstack_dashboard.dashboards.project.networks.subnets import tables\
25
-    as subnets_tables
26 25
 from openstack_dashboard.dashboards.project.networks import tables\
27 26
     as networks_tables
28 27
 from openstack_dashboard.dashboards.project.networks import workflows
@@ -2013,7 +2012,8 @@ class NetworkSubnetTests(test.TestCase):
2013 2012
 class NetworkViewTests(test.TestCase, NetworkStubMixin):
2014 2013
 
2015 2014
     def _test_create_button_shown_when_quota_disabled(
2016
-            self, expected_string):
2015
+            self,
2016
+            find_button_fn):
2017 2017
         # if quota_data doesnt contain a networks|subnets|routers key or
2018 2018
         # these keys are empty dicts, its disabled
2019 2019
         quota_data = self.neutron_quota_usages.first()
@@ -2033,11 +2033,14 @@ class NetworkViewTests(test.TestCase, NetworkStubMixin):
2033 2033
 
2034 2034
         networks = res.context['networks_table'].data
2035 2035
         self.assertItemsEqual(networks, self.networks.list())
2036
-        self.assertContains(res, expected_string, True, html=True,
2037
-                            msg_prefix="The enabled create button not shown")
2036
+
2037
+        button = find_button_fn(res)
2038
+        self.assertFalse('disabled' in button.classes,
2039
+                         "The create button should not be disabled")
2040
+        return button
2038 2041
 
2039 2042
     def _test_create_button_disabled_when_quota_exceeded(
2040
-            self, expected_string, network_quota=5, subnet_quota=5):
2043
+            self, find_button_fn, network_quota=5, subnet_quota=5, ):
2041 2044
 
2042 2045
         quota_data = self.neutron_quota_usages.first()
2043 2046
 
@@ -2056,69 +2059,55 @@ class NetworkViewTests(test.TestCase, NetworkStubMixin):
2056 2059
 
2057 2060
         networks = res.context['networks_table'].data
2058 2061
         self.assertItemsEqual(networks, self.networks.list())
2059
-        self.assertContains(res, expected_string, True, html=True,
2060
-                            msg_prefix="The create button is not disabled")
2062
+
2063
+        button = find_button_fn(res)
2064
+        self.assertTrue('disabled' in button.classes,
2065
+                        "The create button should be disabled")
2066
+        return button
2061 2067
 
2062 2068
     @test.create_stubs({api.neutron: ('network_list',),
2063 2069
                         quotas: ('tenant_quota_usages',)})
2064 2070
     def test_network_create_button_disabled_when_quota_exceeded_index(self):
2065
-        create_link = networks_tables.CreateNetwork()
2066
-        url = create_link.get_link_url()
2067
-        classes = (list(create_link.get_default_classes())
2068
-                   + list(create_link.classes))
2069
-        link_name = "%s (%s)" % (create_link.verbose_name, "Quota exceeded")
2070
-        expected_string = "<a href='%s' title='%s'  class='%s disabled' "\
2071
-            "id='networks__action_create'>" \
2072
-            "<span class='fa fa-plus'></span>%s</a>" \
2073
-            % (url, link_name, " ".join(classes), link_name)
2074
-        self._test_create_button_disabled_when_quota_exceeded(expected_string,
2075
-                                                              network_quota=0
2076
-                                                              )
2071
+        networks_tables.CreateNetwork()
2072
+
2073
+        def _find_net_button(res):
2074
+            return self.getAndAssertTableAction(res, 'networks', 'create')
2075
+        self._test_create_button_disabled_when_quota_exceeded(_find_net_button,
2076
+                                                              network_quota=0)
2077 2077
 
2078 2078
     @test.create_stubs({api.neutron: ('network_list',),
2079 2079
                         quotas: ('tenant_quota_usages',)})
2080 2080
     def test_subnet_create_button_disabled_when_quota_exceeded_index(self):
2081 2081
         network_id = self.networks.first().id
2082
-        create_link = networks_tables.CreateSubnet()
2083
-        url = reverse(create_link.get_link_url(), args=[network_id])
2084
-        classes = (list(create_link.get_default_classes())
2085
-                   + list(create_link.classes))
2086
-        link_name = "%s (%s)" % (create_link.verbose_name, "Quota exceeded")
2087
-        expected_string = "<a href='%s' class='%s disabled' " \
2088
-                          "id='networks__row_%s__action_subnet'>%s</a>" \
2089
-                          % (url, " ".join(classes), network_id, link_name)
2090
-        self._test_create_button_disabled_when_quota_exceeded(expected_string,
2091
-                                                              subnet_quota=0
2092
-                                                              )
2082
+        networks_tables.CreateSubnet()
2083
+
2084
+        def _find_subnet_button(res):
2085
+            return self.getAndAssertTableRowAction(res, 'networks',
2086
+                                                   'subnet', network_id)
2087
+
2088
+        self._test_create_button_disabled_when_quota_exceeded(
2089
+            _find_subnet_button, subnet_quota=0)
2093 2090
 
2094 2091
     @test.create_stubs({api.neutron: ('network_list',),
2095 2092
                         quotas: ('tenant_quota_usages',)})
2096 2093
     def test_network_create_button_shown_when_quota_disabled_index(self):
2097 2094
         # if quota_data doesnt contain a networks["available"] key its disabled
2098
-        create_link = networks_tables.CreateNetwork()
2099
-        url = create_link.get_link_url()
2100
-        classes = (list(create_link.get_default_classes())
2101
-                   + list(create_link.classes))
2102
-        expected_string = "<a href='%s' title='%s'  class='%s' "\
2103
-            "id='networks__action_create'>" \
2104
-            "<span class='fa fa-plus'></span>%s</a>" \
2105
-            % (url, create_link.verbose_name, " ".join(classes),
2106
-               create_link.verbose_name)
2107
-        self._test_create_button_shown_when_quota_disabled(expected_string)
2095
+        networks_tables.CreateNetwork()
2096
+        self._test_create_button_shown_when_quota_disabled(
2097
+            lambda res: self.getAndAssertTableAction(res, 'networks', 'create')
2098
+        )
2108 2099
 
2109 2100
     @test.create_stubs({api.neutron: ('network_list',),
2110 2101
                         quotas: ('tenant_quota_usages',)})
2111 2102
     def test_subnet_create_button_shown_when_quota_disabled_index(self):
2112 2103
         # if quota_data doesnt contain a subnets["available"] key, its disabled
2113 2104
         network_id = self.networks.first().id
2114
-        create_link = networks_tables.CreateSubnet()
2115
-        url = reverse(create_link.get_link_url(), args=[network_id])
2116
-        classes = (list(create_link.get_default_classes())
2117
-                   + list(create_link.classes))
2118
-        expected_string = "<a href='%s' class='%s' "\
2119
-            "id='networks__row_%s__action_subnet'>%s</a>" \
2120
-            % (url, " ".join(classes), network_id, create_link.verbose_name)
2121
-        self._test_create_button_shown_when_quota_disabled(expected_string)
2105
+
2106
+        def _find_subnet_button(res):
2107
+            return self.getAndAssertTableRowAction(res, 'networks',
2108
+                                                   'subnet', network_id)
2109
+
2110
+        self._test_create_button_shown_when_quota_disabled(_find_subnet_button)
2122 2111
 
2123 2112
     @test.create_stubs({api.neutron: ('network_get',
2124 2113
                                       'subnet_list',
@@ -2155,17 +2144,65 @@ class NetworkViewTests(test.TestCase, NetworkStubMixin):
2155 2144
         subnets = res.context['subnets_table'].data
2156 2145
         self.assertItemsEqual(subnets, self.subnets.list())
2157 2146
 
2158
-        class FakeTable(object):
2159
-            kwargs = {'network_id': network_id}
2160
-        create_link = subnets_tables.CreateSubnet()
2161
-        create_link.table = FakeTable()
2162
-        url = create_link.get_link_url()
2163
-        classes = (list(create_link.get_default_classes())
2164
-                   + list(create_link.classes))
2165
-        link_name = "%s (%s)" % (create_link.verbose_name, "Quota exceeded")
2166
-        expected_string = "<a href='%s' title='%s'  class='%s disabled' "\
2167
-            "id='subnets__action_create'>" \
2168
-            "<span class='fa fa-plus'></span>%s</a>" \
2169
-            % (url, link_name, " ".join(classes), link_name)
2170
-        self.assertContains(res, expected_string, html=True,
2171
-                            msg_prefix="The create button is not disabled")
2147
+        create_action = self.getAndAssertTableAction(res, 'subnets', 'create')
2148
+        self.assertTrue('disabled' in create_action.classes,
2149
+                        'The create button should be disabled')
2150
+
2151
+    @test.create_stubs({api.neutron: ('network_list',),
2152
+                        quotas: ('tenant_quota_usages',)})
2153
+    def test_create_button_attributes(self):
2154
+        create_action = self._test_create_button_shown_when_quota_disabled(
2155
+            lambda res: self.getAndAssertTableAction(res, 'networks', 'create')
2156
+        )
2157
+
2158
+        self.assertEqual(set(['ajax-modal']), set(create_action.classes))
2159
+        self.assertEqual('horizon:project:networks:create', create_action.url)
2160
+        self.assertEqual('Create Network',
2161
+                         six.text_type(create_action.verbose_name))
2162
+        self.assertEqual((('network', 'create_network'),),
2163
+                         create_action.policy_rules)
2164
+
2165
+    @test.create_stubs({api.neutron: ('network_get',
2166
+                                      'subnet_list',
2167
+                                      'port_list',
2168
+                                      'is_extension_supported',),
2169
+                        quotas: ('tenant_quota_usages',)})
2170
+    def test_create_subnet_button_attributes(self):
2171
+        network_id = self.networks.first().id
2172
+        quota_data = self.neutron_quota_usages.first()
2173
+        quota_data['subnets']['available'] = 1
2174
+
2175
+        api.neutron.network_get(
2176
+            IsA(http.HttpRequest), network_id)\
2177
+            .MultipleTimes().AndReturn(self.networks.first())
2178
+        api.neutron.subnet_list(
2179
+            IsA(http.HttpRequest), network_id=network_id)\
2180
+            .AndReturn(self.subnets.list())
2181
+        api.neutron.port_list(
2182
+            IsA(http.HttpRequest), network_id=network_id)\
2183
+            .AndReturn([self.ports.first()])
2184
+        api.neutron.is_extension_supported(
2185
+            IsA(http.HttpRequest), 'mac-learning')\
2186
+            .AndReturn(False)
2187
+        quotas.tenant_quota_usages(
2188
+            IsA(http.HttpRequest)) \
2189
+            .MultipleTimes().AndReturn(quota_data)
2190
+
2191
+        self.mox.ReplayAll()
2192
+
2193
+        res = self.client.get(reverse('horizon:project:networks:detail',
2194
+                                      args=[network_id]))
2195
+        self.assertTemplateUsed(res, 'project/networks/detail.html')
2196
+
2197
+        subnets = res.context['subnets_table'].data
2198
+        self.assertItemsEqual(subnets, self.subnets.list())
2199
+
2200
+        create_action = self.getAndAssertTableAction(res, 'subnets', 'create')
2201
+
2202
+        self.assertEqual(set(['ajax-modal']), set(create_action.classes))
2203
+        self.assertEqual('horizon:project:networks:addsubnet',
2204
+                         create_action.url)
2205
+        self.assertEqual('Create Subnet',
2206
+                         six.text_type(create_action.verbose_name))
2207
+        self.assertEqual((('network', 'create_subnet'),),
2208
+                         create_action.policy_rules)

+ 40
- 24
openstack_dashboard/dashboards/project/routers/tests.py View File

@@ -23,7 +23,6 @@ import six
23 23
 from openstack_dashboard import api
24 24
 from openstack_dashboard.dashboards.project.routers.extensions.routerrules\
25 25
     import rulemanager
26
-from openstack_dashboard.dashboards.project.routers import tables
27 26
 from openstack_dashboard.test import helpers as test
28 27
 from openstack_dashboard.usage import quotas
29 28
 
@@ -938,18 +937,11 @@ class RouterViewTests(RouterMixin, test.TestCase):
938 937
         routers = res.context['Routers_table'].data
939 938
         self.assertItemsEqual(routers, self.routers.list())
940 939
 
941
-        create_link = tables.CreateRouter()
942
-        url = create_link.get_link_url()
943
-        classes = (list(create_link.get_default_classes())
944
-                   + list(create_link.classes))
945
-        link_name = "%s (%s)" % (six.text_type(create_link.verbose_name),
946
-                                 "Quota exceeded")
947
-        expected_string = "<a href='%s' title='%s'  class='%s disabled' "\
948
-            "id='Routers__action_create'>" \
949
-            "<span class='fa fa-plus'></span>%s</a>" \
950
-            % (url, link_name, " ".join(classes), link_name)
951
-        self.assertContains(res, expected_string, html=True,
952
-                            msg_prefix="The create button is not disabled")
940
+        create_action = self.getAndAssertTableAction(res, 'Routers', 'create')
941
+        self.assertTrue('disabled' in create_action.classes,
942
+                        'Create button is not disabled')
943
+        self.assertEqual('Create Router (Quota exceeded)',
944
+                         create_action.verbose_name)
953 945
 
954 946
     @test.create_stubs({api.neutron: ('router_list', 'network_list'),
955 947
                         quotas: ('tenant_quota_usages',)})
@@ -973,14 +965,38 @@ class RouterViewTests(RouterMixin, test.TestCase):
973 965
         routers = res.context['Routers_table'].data
974 966
         self.assertItemsEqual(routers, self.routers.list())
975 967
 
976
-        create_link = tables.CreateRouter()
977
-        url = create_link.get_link_url()
978
-        classes = (list(create_link.get_default_classes())
979
-                   + list(create_link.classes))
980
-        link_name = "%s" % (six.text_type(create_link.verbose_name))
981
-        expected_string = "<a href='%s' title='%s'  class='%s' "\
982
-            "id='Routers__action_create'>" \
983
-            "<span class='fa fa-plus'></span>%s</a>" \
984
-            % (url, link_name, " ".join(classes), link_name)
985
-        self.assertContains(res, expected_string, html=True,
986
-                            msg_prefix="The create button is not displayed")
968
+        create_action = self.getAndAssertTableAction(res, 'Routers', 'create')
969
+        self.assertFalse('disabled' in create_action.classes,
970
+                         'Create button should not be disabled')
971
+        self.assertEqual('Create Router',
972
+                         create_action.verbose_name)
973
+
974
+    @test.create_stubs({api.neutron: ('router_list', 'network_list'),
975
+                        quotas: ('tenant_quota_usages',)})
976
+    def test_create_button_attributes(self):
977
+        quota_data = self.neutron_quota_usages.first()
978
+        quota_data['routers']['available'] = 10
979
+        api.neutron.router_list(
980
+            IsA(http.HttpRequest),
981
+            tenant_id=self.tenant.id,
982
+            search_opts=None).AndReturn(self.routers.list())
983
+        quotas.tenant_quota_usages(
984
+            IsA(http.HttpRequest)) \
985
+            .MultipleTimes().AndReturn(quota_data)
986
+
987
+        self._mock_external_network_list()
988
+        self.mox.ReplayAll()
989
+
990
+        res = self.client.get(self.INDEX_URL)
991
+        self.assertTemplateUsed(res, 'project/routers/index.html')
992
+
993
+        routers = res.context['Routers_table'].data
994
+        self.assertItemsEqual(routers, self.routers.list())
995
+
996
+        create_action = self.getAndAssertTableAction(res, 'Routers', 'create')
997
+        self.assertEqual(set(['ajax-modal']), set(create_action.classes))
998
+        self.assertEqual('Create Router',
999
+                         six.text_type(create_action.verbose_name))
1000
+        self.assertEqual('horizon:project:routers:create', create_action.url)
1001
+        self.assertEqual((('network', 'create_router'),),
1002
+                         create_action.policy_rules)

+ 92
- 28
openstack_dashboard/dashboards/project/volumes/volumes/tests.py View File

@@ -15,7 +15,6 @@
15 15
 #    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
16 16
 #    License for the specific language governing permissions and limitations
17 17
 #    under the License.
18
-
19 18
 import django
20 19
 from django.core.urlresolvers import reverse
21 20
 from django.forms import widgets
@@ -24,11 +23,10 @@ from django.test.utils import override_settings
24 23
 
25 24
 from mox3.mox import IsA  # noqa
26 25
 import six
26
+from six import moves
27 27
 
28 28
 from openstack_dashboard import api
29 29
 from openstack_dashboard.api import cinder
30
-from openstack_dashboard.dashboards.project.volumes \
31
-    .volumes import tables
32 30
 from openstack_dashboard.test import helpers as test
33 31
 from openstack_dashboard.usage import quotas
34 32
 
@@ -1021,6 +1019,50 @@ class VolumeViewTests(test.TestCase):
1021 1019
                          server.id)
1022 1020
         self.assertEqual(res.status_code, 200)
1023 1021
 
1022
+    def _get_volume_row_action_from_ajax(self, res, action_name, row_id):
1023
+        def _matches_row_id(context_row):
1024
+            return (len(context_row.dicts) > 1 and
1025
+                    isinstance(context_row.dicts[1], dict) and
1026
+                    context_row.dicts[1].get('row_id', None) == row_id)
1027
+
1028
+        matching = list(moves.filter(lambda r: _matches_row_id(r),
1029
+                                     res.context))
1030
+        self.assertTrue(len(matching) > 1,
1031
+                        "Expected at least one row matching %s" % row_id)
1032
+        row = matching[-1].dicts[1]
1033
+        matching_actions = list(moves.filter(lambda a: a.name == action_name,
1034
+                                             row['row_actions']))
1035
+        self.assertEqual(1, len(matching_actions),
1036
+                         "Expected one row action named '%s'" % action_name)
1037
+        return matching_actions[0]
1038
+
1039
+    @test.create_stubs({cinder: ('tenant_absolute_limits',
1040
+                                 'volume_get',)})
1041
+    def test_create_snapshot_button_attributes(self):
1042
+        limits = {'maxTotalSnapshots': 2}
1043
+        limits['totalSnapshotsUsed'] = 1
1044
+        volume = self.cinder_volumes.first()
1045
+
1046
+        cinder.volume_get(IsA(http.HttpRequest), volume.id).AndReturn(volume)
1047
+        cinder.tenant_absolute_limits(IsA(http.HttpRequest)).AndReturn(limits)
1048
+        self.mox.ReplayAll()
1049
+
1050
+        res_url = (VOLUME_INDEX_URL +
1051
+                   "?action=row_update&table=volumes&obj_id=" + volume.id)
1052
+
1053
+        res = self.client.get(res_url, {},
1054
+                              HTTP_X_REQUESTED_WITH='XMLHttpRequest')
1055
+
1056
+        snapshot_action = self._get_volume_row_action_from_ajax(
1057
+            res, 'snapshots', volume.id)
1058
+        self.assertEqual('horizon:project:volumes:volumes:create_snapshot',
1059
+                         snapshot_action.url)
1060
+        self.assertEqual(set(['ajax-modal']), set(snapshot_action.classes))
1061
+        self.assertEqual('Create Snapshot',
1062
+                         six.text_type(snapshot_action.verbose_name))
1063
+        self.assertEqual((('volume', 'volume:create_snapshot'),),
1064
+                         snapshot_action.policy_rules)
1065
+
1024 1066
     @test.create_stubs({cinder: ('tenant_absolute_limits',
1025 1067
                                  'volume_get',)})
1026 1068
     def test_create_snapshot_button_disabled_when_quota_exceeded(self):
@@ -1032,25 +1074,57 @@ class VolumeViewTests(test.TestCase):
1032 1074
         cinder.tenant_absolute_limits(IsA(http.HttpRequest)).AndReturn(limits)
1033 1075
         self.mox.ReplayAll()
1034 1076
 
1035
-        create_link = tables.CreateSnapshot()
1036
-        url = reverse(create_link.get_link_url(), args=[volume.id])
1037 1077
         res_url = (VOLUME_INDEX_URL +
1038 1078
                    "?action=row_update&table=volumes&obj_id=" + volume.id)
1039 1079
 
1040 1080
         res = self.client.get(res_url, {},
1041 1081
                               HTTP_X_REQUESTED_WITH='XMLHttpRequest')
1042 1082
 
1043
-        classes = (list(create_link.get_default_classes())
1044
-                   + list(create_link.classes))
1045
-        link_name = "%s (%s)" % (six.text_type(create_link.verbose_name),
1046
-                                 "Quota exceeded")
1047
-        expected_string = "<a href='%s' class=\"%s disabled\" "\
1048
-            "id=\"volumes__row_%s__action_snapshots\">%s</a>" \
1049
-            % (url, " ".join(classes), volume.id, link_name)
1083
+        snapshot_action = self._get_volume_row_action_from_ajax(
1084
+            res, 'snapshots', volume.id)
1085
+        self.assertTrue('disabled' in snapshot_action.classes,
1086
+                        'The create snapshot button should be disabled')
1087
+
1088
+    @test.create_stubs({cinder: ('tenant_absolute_limits',
1089
+                                 'volume_list',
1090
+                                 'volume_snapshot_list',
1091
+                                 'volume_backup_supported',),
1092
+                        api.nova: ('server_list',)})
1093
+    def test_create_button_attributes(self):
1094
+        limits = self.cinder_limits['absolute']
1095
+        limits['maxTotalVolumes'] = 10
1096
+        limits['totalVolumesUsed'] = 1
1097
+        volumes = self.cinder_volumes.list()
1098
+
1099
+        api.cinder.volume_backup_supported(IsA(http.HttpRequest)). \
1100
+            MultipleTimes().AndReturn(True)
1101
+        cinder.volume_list(IsA(http.HttpRequest), search_opts=None)\
1102
+            .AndReturn(volumes)
1103
+        cinder.volume_snapshot_list(IsA(http.HttpRequest),
1104
+                                    search_opts=None).\
1105
+            AndReturn([])
1106
+        api.nova.server_list(IsA(http.HttpRequest), search_opts=None)\
1107
+            .AndReturn([self.servers.list(), False])
1108
+        cinder.tenant_absolute_limits(IsA(http.HttpRequest))\
1109
+            .MultipleTimes().AndReturn(limits)
1110
+        self.mox.ReplayAll()
1111
+
1112
+        res = self.client.get(VOLUME_INDEX_URL)
1113
+        self.assertTemplateUsed(res, 'project/volumes/index.html')
1114
+
1115
+        volumes = res.context['volumes_table'].data
1116
+        self.assertItemsEqual(volumes, self.cinder_volumes.list())
1117
+
1118
+        create_action = self.getAndAssertTableAction(res, 'volumes', 'create')
1050 1119
 
1051
-        self.assertContains(
1052
-            res, expected_string, html=True,
1053
-            msg_prefix="The create snapshot button is not disabled")
1120
+        self.assertEqual(set(['ajax-modal', 'ajax-update', 'btn-create']),
1121
+                         set(create_action.classes))
1122
+        self.assertEqual('Create Volume',
1123
+                         six.text_type(create_action.verbose_name))
1124
+        self.assertEqual('horizon:project:volumes:volumes:create',
1125
+                         create_action.url)
1126
+        self.assertEqual((('volume', 'volume:create'),),
1127
+                         create_action.policy_rules)
1054 1128
 
1055 1129
     @test.create_stubs({cinder: ('tenant_absolute_limits',
1056 1130
                                  'volume_list',
@@ -1081,19 +1155,9 @@ class VolumeViewTests(test.TestCase):
1081 1155
         volumes = res.context['volumes_table'].data
1082 1156
         self.assertItemsEqual(volumes, self.cinder_volumes.list())
1083 1157
 
1084
-        create_link = tables.CreateVolume()
1085
-        url = create_link.get_link_url()
1086
-        classes = (list(create_link.get_default_classes())
1087
-                   + list(create_link.classes))
1088
-        link_name = "%s (%s)" % (six.text_type(create_link.verbose_name),
1089
-                                 "Quota exceeded")
1090
-        expected_string = "<a href='%s' title='%s'  class='%s disabled' "\
1091
-            "id='volumes__action_create'  data-update-url=" \
1092
-            "'/project/volumes/?action=create&amp;table=volumes'> "\
1093
-            "<span class='fa fa-plus'></span>%s</a>" \
1094
-            % (url, link_name, " ".join(classes), link_name)
1095
-        self.assertContains(res, expected_string, html=True,
1096
-                            msg_prefix="The create button is not disabled")
1158
+        create_action = self.getAndAssertTableAction(res, 'volumes', 'create')
1159
+        self.assertTrue('disabled' in create_action.classes,
1160
+                        'The create button should be disabled')
1097 1161
 
1098 1162
     @test.create_stubs({cinder: ('tenant_absolute_limits',
1099 1163
                                  'volume_get',),

+ 38
- 0
openstack_dashboard/test/helpers.py View File

@@ -280,6 +280,44 @@ class TestCase(horizon_helpers.TestCase):
280 280
     def assertItemsCollectionEqual(self, response, items_list):
281 281
         self.assertEqual(response.json, {"items": items_list})
282 282
 
283
+    def getAndAssertTableRowAction(self, response, table_name,
284
+                                   action_name, row_id):
285
+        table = response.context[table_name + '_table']
286
+        full_row_id = '%s__row__%s' % (table_name, row_id)
287
+        rows = list(moves.filter(lambda x: x.id == full_row_id,
288
+                                 table.get_rows()))
289
+        self.assertEqual(1, len(rows),
290
+                         "Did not find a row matching id '%s'" % row_id)
291
+        row_actions = table.get_row_actions(rows[0])
292
+
293
+        msg_args = (table_name, action_name, row_id)
294
+        self.assertTrue(
295
+            len(row_actions) > 0,
296
+            "No action named '%s' found in table '%s' row '%s'" % msg_args)
297
+
298
+        self.assertEqual(
299
+            1, len(row_actions),
300
+            "Multiple actions '%s' found in table '%s' row '%s'" % msg_args)
301
+
302
+        return row_actions[0]
303
+
304
+    def getAndAssertTableAction(self, response, table_name, action_name):
305
+
306
+        table = response.context[table_name + '_table']
307
+        table_actions = table.get_table_actions()
308
+        actions = list(moves.filter(lambda x: x.name == action_name,
309
+                                    table_actions))
310
+        msg_args = (table_name, action_name)
311
+        self.assertTrue(
312
+            len(actions) > 0,
313
+            "No action named '%s' found in table '%s'" % msg_args)
314
+
315
+        self.assertEqual(
316
+            1, len(actions),
317
+            "More than one action named '%s' found in table '%s'" % msg_args)
318
+
319
+        return actions[0]
320
+
283 321
     @staticmethod
284 322
     def mock_rest_request(**args):
285 323
         mock_args = {

Loading…
Cancel
Save