~jcsackett/launchpad/series-need-usage-attributes-643902

« back to all changes in this revision

Viewing changes to lib/lp/translations/browser/tests/test_seriestemplatesview.py

  • Committer: Ian Booth
  • Date: 2010-09-14 21:32:16 UTC
  • mfrom: (11542 launchpad)
  • mto: This revision was merged to the branch mainline in revision 11564.
  • Revision ID: ian.booth@canonical.com-20100914213216-2cg4edr4sbglh1wo
Code review fixes

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright 2010 Canonical Ltd.  This software is licensed under the
 
2
# GNU Affero General Public License version 3 (see the file LICENSE).
 
3
 
 
4
"""Tests for `BaseSeriesTemplatesView` and descendants."""
 
5
 
 
6
__metaclass__ = type
 
7
 
 
8
import re
 
9
 
 
10
from zope.security.proxy import removeSecurityProxy
 
11
 
 
12
from canonical.launchpad.ftests import login
 
13
from canonical.launchpad.webapp.publisher import canonical_url
 
14
from canonical.launchpad.webapp.servers import LaunchpadTestRequest
 
15
from canonical.testing import DatabaseFunctionalLayer
 
16
from lp.registry.model.distroseries import DistroSeries
 
17
from lp.registry.model.productseries import ProductSeries
 
18
from lp.translations.browser.distroseries import DistroSeriesTemplatesView
 
19
from lp.translations.browser.productseries import ProductSeriesTemplatesView
 
20
from lp.testing import (
 
21
    login_person,
 
22
    TestCaseWithFactory,
 
23
    )
 
24
from lp.testing.sampledata import ADMIN_EMAIL
 
25
 
 
26
 
 
27
class SeriesTemplatesViewScenario:
 
28
 
 
29
    # The view class to test.
 
30
    view_class = None
 
31
 
 
32
    # The expected list of columns for the view_class, each shown as a
 
33
    # list holding the column's CSS class.
 
34
    columns = None
 
35
 
 
36
    def makeTemplateContext(self):
 
37
        """Create makePOTemplate arguments to create a series."""
 
38
        raise NotImplementedError()
 
39
 
 
40
    def _makeTemplate(self, **kwargs):
 
41
        """Create a distro- or productseries for the view."""
 
42
        args_dict = self.makeTemplateContext()
 
43
        args_dict.update(kwargs)
 
44
        return self.factory.makePOTemplate(**args_dict)
 
45
 
 
46
    def _getSeries(self, template):
 
47
        """Get `template`'s distro- or productseries."""
 
48
        return template.distroseries or template.productseries
 
49
 
 
50
    def _makeView(self, template=None):
 
51
        """Create a `BaseTemplatesView` containing `template`."""
 
52
        if template is None:
 
53
            template = self._makeTemplate()
 
54
        request = LaunchpadTestRequest()
 
55
        view = self.view_class(self._getSeries(template), request)
 
56
        view.initialize()
 
57
        return view
 
58
 
 
59
    def _findTagClasses(self, html, tag):
 
60
        """Find the CSS classes for all instances of `tag`s in `html`.
 
61
 
 
62
        Returns a list of lists.  The outer list represents instances of
 
63
        `tag`, in the order in which they are found.  The inner lists
 
64
        hold the respective sets of HTML classes for these tags, sorted
 
65
        alphabetically.
 
66
        """
 
67
        regex = '<%s [^>]*class="([^"]*)"' % tag
 
68
        return [
 
69
            sorted(css_class.split())
 
70
            for css_class in re.findall(regex, html)]
 
71
 
 
72
    def _findActions(self, html):
 
73
        """Find the available actions in an HTML actions column."""
 
74
        return re.findall('<[^>]*>([^<]*)</[^>]*', html)
 
75
 
 
76
    def test_has_the_right_columns(self):
 
77
        # Test the column headers against the expected list.
 
78
        view = self._makeView()
 
79
        header = view.renderTemplatesHeader()
 
80
        self.assertEqual(self.columns, self._findTagClasses(header, 'th'))
 
81
 
 
82
    def test_logging_in_adds_actions_column(self):
 
83
        # A logged-in user gets to see an extra "actions" column.
 
84
        template = self._makeTemplate()
 
85
        login_person(self.factory.makePerson())
 
86
        view = self._makeView(template)
 
87
        columns = self.columns + [['actions_column']]
 
88
        header = view.renderTemplatesHeader()
 
89
        self.assertEqual(columns, self._findTagClasses(header, 'th'))
 
90
        row = view.renderTemplateRow(template)
 
91
        self.assertEqual(columns, self._findTagClasses(row, 'td'))
 
92
 
 
93
    def test_user_actions(self):
 
94
        # The only action offered to regular users is Download.
 
95
        template = self._makeTemplate()
 
96
        url = canonical_url(template)
 
97
        login_person(self.factory.makePerson())
 
98
        view = self._makeView(template)
 
99
 
 
100
        self.assertEqual(
 
101
            ['Download'],
 
102
            self._findActions(view._renderActionsColumn(template, url)))
 
103
 
 
104
    def test_admin_actions(self):
 
105
        # An administrator gets to see all actions on a template.
 
106
        template = self._makeTemplate()
 
107
        url = canonical_url(template)
 
108
        login(ADMIN_EMAIL)
 
109
        view = self._makeView(template)
 
110
 
 
111
        self.assertEqual(
 
112
            ['Edit', 'Upload', 'Download', 'Administer'],
 
113
            self._findActions(view._renderActionsColumn(template, url)))
 
114
 
 
115
    def test_edit_actions(self):
 
116
        # A non-admin user with edit rights gets the Edit, Upload, and
 
117
        # Download actions.
 
118
        template = self._makeTemplate()
 
119
        url = canonical_url(template)
 
120
        login_person(self.factory.makePerson())
 
121
        view = self._makeView(template)
 
122
        view.can_edit = True
 
123
 
 
124
        self.assertEqual(
 
125
            ['Edit', 'Upload', 'Download'],
 
126
            self._findActions(view._renderActionsColumn(template, url)))
 
127
 
 
128
    def test_constructs_correct_urls(self):
 
129
        # The view classes can override constructTemplateURL with
 
130
        # optimized versions.  These can produce either an absolute URL
 
131
        # that exactly matches the template's canonical_url, or a
 
132
        # relative one starting from the series' canonical_url.
 
133
        template = self._makeTemplate()
 
134
        view = self._makeView(template)
 
135
 
 
136
        series_url = canonical_url(
 
137
            self._getSeries(template), rootsite='translations')
 
138
        constructed_url = view.constructTemplateURL(template)
 
139
 
 
140
        self.assertIn(
 
141
            canonical_url(template),
 
142
            (constructed_url, '/'.join([series_url, constructed_url])))
 
143
 
 
144
    def test_renderTemplateLink(self):
 
145
        # _renderTemplateLink renders a link to the template.
 
146
        template = self._makeTemplate()
 
147
        view = self._makeView(template)
 
148
 
 
149
        url = view.constructTemplateURL(template)
 
150
        link = view._renderTemplateLink(template, url)
 
151
 
 
152
        self.assertIn('<a ', link)
 
153
        self.assertIn('href="%s"' % url, link)
 
154
        self.assertIn('>%s<' % template.name, link)
 
155
 
 
156
    def test_renderTemplateLink_marks_disabled(self):
 
157
        # _renderTemplateLinks marks disabled templates as "(inactive)."
 
158
        template = self._makeTemplate()
 
159
        view = self._makeView(template)
 
160
        url = canonical_url(template)
 
161
 
 
162
        removeSecurityProxy(template).iscurrent = True
 
163
        self.assertNotIn(
 
164
            '(inactive)', view._renderTemplateLink(template, url))
 
165
        removeSecurityProxy(template).iscurrent = False
 
166
        self.assertIn('(inactive)', view._renderTemplateLink(template, url))
 
167
 
 
168
    def test_renderLastUpdateDate_sets_sortkey(self):
 
169
        # _renderLastUpdateDate sets the full date as the column's sort
 
170
        # key, so that clicking on the column header sorts by date (even
 
171
        # if sorting alphabetically by the visible date might produce a
 
172
        # different ordering).
 
173
        template = self._makeTemplate()
 
174
        view = self._makeView(template)
 
175
 
 
176
        date_field = view._renderLastUpdateDate(template)
 
177
 
 
178
        # The sort key is set in a span of class "sortkey."
 
179
        sortkey_match = re.findall(
 
180
            '<span class="sortkey">([^<]*)</span>', date_field)
 
181
        self.assertIsNot(None, sortkey_match)
 
182
        self.assertEqual(1, len(sortkey_match))
 
183
 
 
184
        # The column also has the same full date as a tooltip.
 
185
        full_date = sortkey_match[0].strip()
 
186
        self.assertIn('title="%s"' % full_date, date_field)
 
187
 
 
188
    def test_renderAction_returns_empty_string_if_not_enabled(self):
 
189
        view = self._makeView()
 
190
        self.assertEqual(
 
191
            '',
 
192
            view._renderAction('url', 'name', 'path', 'sprite', False))
 
193
 
 
194
    def test_renderAction(self):
 
195
        # If enabled, _renderAction produces a link to an action form
 
196
        # for a given template.
 
197
        view = self._makeView()
 
198
 
 
199
        url = self.factory.getUniqueString()
 
200
        name = self.factory.getUniqueString()
 
201
        path = self.factory.getUniqueString()
 
202
        sprite = self.factory.getUniqueString()
 
203
 
 
204
        action = view._renderAction(url, name, path, sprite, True)
 
205
 
 
206
        self.assertIn('<a ', action)
 
207
        self.assertIn('href="%s/%s"' % (url, path), action)
 
208
        self.assertIn(name, action)
 
209
        self.assertIn('class="sprite %s"' % sprite, action)
 
210
 
 
211
    def test_renderField_returns_empty_string_if_no_content(self):
 
212
        view = self._makeView()
 
213
        self.assertEqual('', view._renderField('x', None, tag='y'))
 
214
 
 
215
    def test_renderField_returns_empty_field_for_empty_content(self):
 
216
        field = self._makeView()._renderField('class', '', tag='tag')
 
217
        self.assertIn('<tag class="class">', field)
 
218
        self.assertIn('</tag>', field)
 
219
 
 
220
    def test_renderField(self):
 
221
        column_class = self.factory.getUniqueString()
 
222
        content = self.factory.getUniqueString()
 
223
        tag = self.factory.getUniqueString()
 
224
 
 
225
        field = self._makeView()._renderField(column_class, content, tag=tag)
 
226
 
 
227
        self.assertIn('<%s class="%s">' % (tag, column_class), field)
 
228
        self.assertIn(content, field)
 
229
        self.assertIn('</%s>' % tag, field)
 
230
 
 
231
    def test_renderTemplateRow(self):
 
232
        template = self._makeTemplate()
 
233
        view = self._makeView(template)
 
234
 
 
235
        row = view.renderTemplateRow(template)
 
236
 
 
237
        self.assertEqual(
 
238
            [sorted(['template_row', view.rowCSSClass(template)])],
 
239
            self._findTagClasses(row, 'tr'))
 
240
 
 
241
        self.assertEqual(self.columns, self._findTagClasses(row, 'td'))
 
242
 
 
243
 
 
244
class TestDistroSeriesTemplatesView(SeriesTemplatesViewScenario,
 
245
                                    TestCaseWithFactory):
 
246
    """Run the test scenario against `DistroSeriesTemplatesView`."""
 
247
 
 
248
    layer = DatabaseFunctionalLayer
 
249
 
 
250
    view_class = DistroSeriesTemplatesView
 
251
 
 
252
    columns = [
 
253
        ['priority_column'],
 
254
        ['sourcepackage_column'],
 
255
        ['template_column'],
 
256
        ['length_column'],
 
257
        ['lastupdate_column'],
 
258
    ]
 
259
 
 
260
    def makeTemplateContext(self):
 
261
        """See `SeriesTemplatesViewScenario`."""
 
262
        return dict(
 
263
            sourcepackagename=self.factory.makeSourcePackageName(),
 
264
            distroseries=self.factory.makeDistroSeries())
 
265
 
 
266
    def test_makeTemplate(self):
 
267
        # In this test case, _makeTemplate produces a distroseries
 
268
        # template.
 
269
        template = self._makeTemplate()
 
270
        self.assertIsInstance(template.distroseries, DistroSeries)
 
271
        self.assertIs(None, template.productseries)
 
272
 
 
273
    def test_findTagClasses(self):
 
274
        # Tested here arbitrarily (no need to repeat it): the
 
275
        # _findTagClasses helper.
 
276
        self.assertEqual(
 
277
            [['b', 'c'], ['a']],
 
278
            self._findTagClasses('<x class="c b" /><x class="a">', 'x'))
 
279
 
 
280
    def test_findActions(self):
 
281
        # Tested here arbitrarily (no need to repeat it): the
 
282
        # _findActions helper.
 
283
        self.assertEqual(['Foo'], self._findActions('<a class="bar">Foo</a>'))
 
284
 
 
285
    def test_is_distroseries(self):
 
286
        self.assertTrue(self._makeView().is_distroseries)
 
287
 
 
288
    def test_renderSourcePackage(self):
 
289
        # _renderSourcePackage returns the template's source-package
 
290
        # name for a productseries view.
 
291
        template = self._makeTemplate()
 
292
        view = self._makeView(template)
 
293
 
 
294
        return self.assertEqual(
 
295
            template.sourcepackagename.name,
 
296
            view._renderSourcePackage(template))
 
297
 
 
298
 
 
299
class TestProductSeriesTemplatesView(SeriesTemplatesViewScenario,
 
300
                                     TestCaseWithFactory):
 
301
    """Run the test scenario against `ProductSeriesTemplatesView`."""
 
302
 
 
303
    layer = DatabaseFunctionalLayer
 
304
 
 
305
    view_class = ProductSeriesTemplatesView
 
306
 
 
307
    columns = [
 
308
        ['priority_column'],
 
309
        ['template_column'],
 
310
        ['length_column'],
 
311
        ['lastupdate_column'],
 
312
    ]
 
313
 
 
314
    def makeTemplateContext(self):
 
315
        """See `SeriesTemplatesViewScenario`."""
 
316
        return dict(productseries=self.factory.makeProductSeries())
 
317
 
 
318
    def test_makeTemplate(self):
 
319
        # In this test case, _makeTemplate produces a productseries
 
320
        # template.
 
321
        template = self._makeTemplate()
 
322
        self.assertIs(None, template.distroseries)
 
323
        self.assertIsInstance(template.productseries, ProductSeries)
 
324
 
 
325
    def test_is_distroseries(self):
 
326
        self.assertFalse(self._makeView().is_distroseries)
 
327
 
 
328
    def test_renderSourcePackage(self):
 
329
        # _renderSourcePackage returns None for a productseries view.
 
330
        template = self._makeTemplate()
 
331
        view = self._makeView(template)
 
332
        self.assertIs(None, view._renderSourcePackage(template))