~launchpad-pqm/launchpad/devel

« back to all changes in this revision

Viewing changes to lib/lp/app/browser/tests/test_vocabulary.py

  • Committer: Launchpad Patch Queue Manager
  • Date: 2011-08-04 20:34:35 UTC
  • mfrom: (13608.1.4 person-picker-expand-1)
  • Revision ID: launchpad@pqm.canonical.com-20110804203435-o5cob6ra9jw8hev1
[r=jcsackett][bug=800361,
        798759] Restore picker expander and place it behind its own flag.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright 2011 Canonical Ltd.  This software is licensed under the
 
2
# GNU Affero General Public License version 3 (see the file LICENSE).
 
3
 
 
4
"""Test vocabulary adapters."""
 
5
 
 
6
__metaclass__ = type
 
7
 
 
8
from datetime import datetime
 
9
from urllib import urlencode
 
10
 
 
11
import pytz
 
12
import simplejson
 
13
 
 
14
from zope.app.form.interfaces import MissingInputError
 
15
from zope.component import (
 
16
    getSiteManager,
 
17
    getUtility,
 
18
    )
 
19
from zope.interface import implements
 
20
from zope.schema.interfaces import IVocabularyFactory
 
21
from zope.schema.vocabulary import SimpleTerm
 
22
from zope.security.proxy import removeSecurityProxy
 
23
 
 
24
 
 
25
from canonical.launchpad.interfaces.launchpad import ILaunchpadRoot
 
26
from canonical.launchpad.webapp.vocabulary import (
 
27
    CountableIterator,
 
28
    IHugeVocabulary,
 
29
    )
 
30
from canonical.testing.layers import DatabaseFunctionalLayer
 
31
from lp.app.browser.vocabulary import (
 
32
    IPickerEntry,
 
33
    MAX_DESCRIPTION_LENGTH,
 
34
    )
 
35
from lp.app.errors import UnexpectedFormData
 
36
from lp.registry.interfaces.irc import IIrcIDSet
 
37
from lp.services.features.testing import FeatureFixture
 
38
from lp.testing import (
 
39
    login_person,
 
40
    TestCaseWithFactory,
 
41
    )
 
42
from lp.testing.views import create_view
 
43
 
 
44
 
 
45
class PersonPickerEntryAdapterTestCase(TestCaseWithFactory):
 
46
 
 
47
    layer = DatabaseFunctionalLayer
 
48
 
 
49
    def test_person_to_pickerentry(self):
 
50
        # IPerson can be adpated to IPickerEntry.
 
51
        person = self.factory.makePerson()
 
52
        adapter = IPickerEntry(person)
 
53
        self.assertTrue(IPickerEntry.providedBy(adapter))
 
54
 
 
55
    def test_PersonPickerEntryAdapter_email_anonymous(self):
 
56
        # Anonymous users cannot see entry email addresses.
 
57
        person = self.factory.makePerson(email='snarf@eg.dom')
 
58
        entry = IPickerEntry(person).getPickerEntry(None)
 
59
        self.assertEqual('<email address hidden>', entry.description)
 
60
 
 
61
    def test_PersonPickerEntryAdapter_visible_email_logged_in(self):
 
62
        # Logged in users can see visible email addresses.
 
63
        observer = self.factory.makePerson()
 
64
        login_person(observer)
 
65
        person = self.factory.makePerson(email='snarf@eg.dom')
 
66
        entry = IPickerEntry(person).getPickerEntry(None)
 
67
        self.assertEqual('snarf@eg.dom', entry.description)
 
68
 
 
69
    def test_PersonPickerEntryAdapter_hidden_email_logged_in(self):
 
70
        # Logged in users cannot see hidden email addresses.
 
71
        person = self.factory.makePerson(email='snarf@eg.dom')
 
72
        login_person(person)
 
73
        person.hide_email_addresses = True
 
74
        observer = self.factory.makePerson()
 
75
        login_person(observer)
 
76
        entry = IPickerEntry(person).getPickerEntry(None)
 
77
        self.assertEqual('<email address hidden>', entry.description)
 
78
 
 
79
    def test_PersonPickerEntryAdapter_no_email_logged_in(self):
 
80
        # Teams without email address have no desriptions.
 
81
        team = self.factory.makeTeam()
 
82
        observer = self.factory.makePerson()
 
83
        login_person(observer)
 
84
        entry = IPickerEntry(team).getPickerEntry(None)
 
85
        self.assertEqual(None, entry.description)
 
86
 
 
87
    def test_PersonPickerEntryAdapter_logged_in(self):
 
88
        # Logged in users can see visible email addresses.
 
89
        observer = self.factory.makePerson()
 
90
        login_person(observer)
 
91
        person = self.factory.makePerson(
 
92
            email='snarf@eg.dom', name='snarf')
 
93
        entry = IPickerEntry(person).getPickerEntry(None)
 
94
        self.assertEqual('sprite person', entry.css)
 
95
        self.assertEqual('sprite new-window', entry.link_css)
 
96
 
 
97
    def test_PersonPickerEntryAdapter_enhanced_picker_enabled_user(self):
 
98
        # The enhanced person picker provides more information for users.
 
99
        person = self.factory.makePerson(email='snarf@eg.dom', name='snarf')
 
100
        creation_date = datetime(
 
101
            2005, 01, 30, 0, 0, 0, 0, pytz.timezone('UTC'))
 
102
        removeSecurityProxy(person).datecreated = creation_date
 
103
        getUtility(IIrcIDSet).new(person, 'eg.dom', 'snarf')
 
104
        getUtility(IIrcIDSet).new(person, 'ex.dom', 'pting')
 
105
        entry = IPickerEntry(person).getPickerEntry(
 
106
            None, enhanced_picker_enabled=True,
 
107
            picker_expander_enabled=True)
 
108
        self.assertEqual('http://launchpad.dev/~snarf', entry.alt_title_link)
 
109
        self.assertEqual(
 
110
            ['snarf on eg.dom, pting on ex.dom', 'Member since 2005-01-30'],
 
111
            entry.details)
 
112
 
 
113
    def test_PersonPickerEntryAdapter_enhanced_picker_enabled_team(self):
 
114
        # The enhanced person picker provides more information for teams.
 
115
        team = self.factory.makeTeam(email='fnord@eg.dom', name='fnord')
 
116
        entry = IPickerEntry(team).getPickerEntry(
 
117
            None, enhanced_picker_enabled=True,
 
118
            picker_expander_enabled=True)
 
119
        self.assertEqual('http://launchpad.dev/~fnord', entry.alt_title_link)
 
120
        self.assertEqual(['Team members: 1'], entry.details)
 
121
 
 
122
    def test_PersonPickerEntryAdapter_enhanced_picker_enabled_badges(self):
 
123
        # The enhanced person picker provides affilliation information.
 
124
        person = self.factory.makePerson(email='snarf@eg.dom', name='snarf')
 
125
        project = self.factory.makeProduct(name='fnord', owner=person)
 
126
        bugtask = self.factory.makeBugTask(target=project)
 
127
        entry = IPickerEntry(person).getPickerEntry(
 
128
            bugtask, enhanced_picker_enabled=True,
 
129
            picker_expander_enabled=True)
 
130
        self.assertEqual(1, len(entry.badges))
 
131
        self.assertEqual('/@@/product-badge', entry.badges[0]['url'])
 
132
        self.assertEqual('Affiliated with Fnord', entry.badges[0]['alt'])
 
133
 
 
134
 
 
135
class TestPersonVocabulary:
 
136
    implements(IHugeVocabulary)
 
137
    test_persons = []
 
138
 
 
139
    @classmethod
 
140
    def setTestData(cls, person_list):
 
141
        cls.test_persons = person_list
 
142
 
 
143
    def __init__(self, context):
 
144
        self.context = context
 
145
 
 
146
    def toTerm(self, person):
 
147
        return SimpleTerm(person, person.name, person.displayname)
 
148
 
 
149
    def searchForTerms(self, query=None):
 
150
        found = [
 
151
            person for person in self.test_persons if query in person.name]
 
152
        return CountableIterator(len(found), found, self.toTerm)
 
153
 
 
154
 
 
155
class HugeVocabularyJSONViewTestCase(TestCaseWithFactory):
 
156
 
 
157
    layer = DatabaseFunctionalLayer
 
158
 
 
159
    def setUp(self):
 
160
        super(HugeVocabularyJSONViewTestCase, self).setUp()
 
161
        test_persons = []
 
162
        for name in range(1, 7):
 
163
            test_persons.append(
 
164
                self.factory.makePerson(name='pting-%s' % name))
 
165
        TestPersonVocabulary.setTestData(test_persons)
 
166
        getSiteManager().registerUtility(
 
167
            TestPersonVocabulary, IVocabularyFactory, 'TestPerson')
 
168
        self.addCleanup(
 
169
            getSiteManager().unregisterUtility,
 
170
            TestPersonVocabulary, IVocabularyFactory, 'TestPerson')
 
171
        self.addCleanup(TestPersonVocabulary.setTestData, [])
 
172
 
 
173
    @staticmethod
 
174
    def create_vocabulary_view(form):
 
175
        context = getUtility(ILaunchpadRoot)
 
176
        query_string = urlencode(form)
 
177
        return create_view(
 
178
            context, '+huge-vocabulary', form=form, query_string=query_string)
 
179
 
 
180
    def test_name_field_missing_error(self):
 
181
        view = self.create_vocabulary_view({})
 
182
        self.assertRaisesWithContent(
 
183
            MissingInputError, "('name', '', None)", view.__call__)
 
184
 
 
185
    def test_search_text_field_missing_error(self):
 
186
        view = self.create_vocabulary_view({'name': 'TestPerson'})
 
187
        self.assertRaisesWithContent(
 
188
            MissingInputError, "('search_text', '', None)", view.__call__)
 
189
 
 
190
    def test_vocabulary_name_unknown_error(self):
 
191
        form = dict(name='snarf', search_text='pting')
 
192
        view = self.create_vocabulary_view(form)
 
193
        self.assertRaisesWithContent(
 
194
            UnexpectedFormData, "Unknown vocabulary 'snarf'", view.__call__)
 
195
 
 
196
    def test_json_entries(self):
 
197
        # The results are JSON encoded.
 
198
        feature_flag = {
 
199
            'disclosure.picker_enhancements.enabled': 'on',
 
200
            'disclosure.picker_expander.enabled': 'on',
 
201
            }
 
202
        flags = FeatureFixture(feature_flag)
 
203
        flags.setUp()
 
204
        self.addCleanup(flags.cleanUp)
 
205
        team = self.factory.makeTeam(name='pting-team')
 
206
        TestPersonVocabulary.test_persons.append(team)
 
207
        form = dict(name='TestPerson', search_text='pting-team')
 
208
        view = self.create_vocabulary_view(form)
 
209
        result = simplejson.loads(view())
 
210
        expected = {
 
211
            "alt_title": team.name,
 
212
            "alt_title_link": "http://launchpad.dev/~%s" % team.name,
 
213
            "api_uri": "/~%s" % team.name,
 
214
            "css": "sprite team",
 
215
            "details": ['Team members: 1'],
 
216
            "link_css": "sprite new-window",
 
217
            "metadata": "team",
 
218
            "title": team.displayname,
 
219
            "value": team.name
 
220
            }
 
221
        self.assertTrue('entries' in result)
 
222
        self.assertContentEqual(
 
223
            expected.items(), result['entries'][0].items())
 
224
 
 
225
    def test_max_description_size(self):
 
226
        # Descriptions over 120 characters are truncated and ellipsised.
 
227
        email = 'pting-' * 19 + '@example.dom'
 
228
        person = self.factory.makePerson(name='pting-n', email=email)
 
229
        TestPersonVocabulary.test_persons.append(person)
 
230
        # Login to gain permission to know the email address that used
 
231
        # for the description
 
232
        login_person(person)
 
233
        form = dict(name='TestPerson', search_text='pting-n')
 
234
        view = self.create_vocabulary_view(form)
 
235
        result = simplejson.loads(view())
 
236
        expected = (email[:MAX_DESCRIPTION_LENGTH - 3] + '...')
 
237
        self.assertEqual(
 
238
            'pting-n', result['entries'][0]['value'])
 
239
        self.assertEqual(
 
240
            expected, result['entries'][0]['description'])
 
241
 
 
242
    def test_default_batch_size(self):
 
243
        # The results are batched.
 
244
        form = dict(name='TestPerson', search_text='pting')
 
245
        view = self.create_vocabulary_view(form)
 
246
        result = simplejson.loads(view())
 
247
        total_size = result['total_size']
 
248
        entries = len(result['entries'])
 
249
        self.assertTrue(
 
250
            total_size > entries,
 
251
            'total_size: %d is less than entries: %d' % (total_size, entries))
 
252
 
 
253
    def test_batch_size(self):
 
254
        # A The batch size can be specified with the batch param.
 
255
        form = dict(
 
256
            name='TestPerson', search_text='pting',
 
257
            start='0', batch='1')
 
258
        view = self.create_vocabulary_view(form)
 
259
        result = simplejson.loads(view())
 
260
        self.assertEqual(6, result['total_size'])
 
261
        self.assertEqual(1, len(result['entries']))
 
262
 
 
263
    def test_start_offset(self):
 
264
        # The offset of the batch is specified with the start param.
 
265
        form = dict(
 
266
            name='TestPerson', search_text='pting',
 
267
            start='1', batch='1')
 
268
        view = self.create_vocabulary_view(form)
 
269
        result = simplejson.loads(view())
 
270
        self.assertEqual(6, result['total_size'])
 
271
        self.assertEqual(1, len(result['entries']))
 
272
        self.assertEqual('pting-2', result['entries'][0]['value'])