1import json
2from nose import SkipTest
3from tests.test_stack import TestConfig, app_from_config
4from tg.support.paginate import Page
5from tg.controllers.util import _urlencode
6from tg import json_encode
7from tg.util.webtest import test_context
8
9
10def setup_noDB():
11    base_config = TestConfig(folder='rendering',
12            values={
13                'use_sqlalchemy': False,
14                'use_toscawidgets': False,
15                'use_toscawidgets2': False
16            })
17    return app_from_config(base_config)
18
19
20_pager = ('<div id="pager"><span class="pager_curpage">1</span>'
21    ' <a href="%(url)s?page=2">2</a>'
22    ' <a href="%(url)s?page=3">3</a>'
23    ' <span class="pager_dotdot">..</span>'
24    ' <a href="%(url)s?page=5">5</a></div>')
25
26_data = '<ul id="data">%s</ul>' % ''.join(
27        '<li>%d</li>' % i for i in range(10))
28
29
30class TestPagination:
31    def setup(self):
32        self.app = setup_noDB()
33
34    def test_basic_pagination(self):
35        url = '/paginated/42'
36        page = self.app.get(url)
37        assert _pager % locals() in page, page
38        assert _data in page, page
39        url = '/paginated/42?page=2'
40        page = self.app.get(url)
41        assert '<li>0</li>' not in page
42        assert '<li>10</li>' in page
43
44    def test_pagination_negative(self):
45        url = '/paginated/42?page=-1'
46        page = self.app.get(url)
47        assert '<li>0</li>' in page
48
49    def test_pagination_items_per_page(self):
50        url = '/paginated/42?items_per_page=20'
51        page = self.app.get(url)
52        assert '<li>0</li>' in page
53        assert '<li>19</li>' in page
54
55    def test_pagination_items_per_page_negative(self):
56        url = '/paginated/42?items_per_page=-1'
57        page = self.app.get(url)
58        assert '<li>0</li>' in page
59        assert '<li>10</li>' not in page
60
61    def test_pagination_non_paginable(self):
62        url = '/paginated_text'
63        page = self.app.get(url)
64        assert 'Some Text' in page
65
66    def test_pagination_with_validation(self):
67        url = '/paginated_validated/42'
68        page = self.app.get(url)
69        assert _pager % locals() in page, page
70        assert _data in page, page
71        url = '/paginated_validated/42?page=2'
72        page = self.app.get(url)
73        assert '<li>0</li>' not in page
74        assert '<li>10</li>' in page
75
76    def test_validation_with_pagination(self):
77        url = '/validated_paginated/42'
78        page = self.app.get(url)
79        assert _pager % locals() in page, page
80        assert _data in page, page
81        url = '/validated_paginated/42?page=2'
82        page = self.app.get(url)
83        assert '<li>0</li>' not in page
84        assert '<li>10</li>' in page
85
86    def test_pagination_with_link_args(self):
87        url = '/paginate_with_params/42'
88        page = self.app.get(url)
89        assert 'param1=hi' in page
90        assert 'param2=man' in page
91        assert 'partial' not in page
92        assert '/fake_url' in page
93        url = '/paginate_with_params/42?page=2'
94        page = self.app.get(url)
95        assert '<li>0</li>' not in page
96        assert '<li>10</li>' in page
97
98    def test_multiple_paginators(self):
99        url = '/multiple_paginators/42'
100
101        try:
102            from collections import OrderedDict
103            params = (('testdata_page', 2), ('testdata2_page', 2))
104            reverse_params = OrderedDict(reversed(params))
105            params = OrderedDict(params)
106        except ImportError:
107            reverse_params = params = {'testdata2_page': 2, 'testdata_page': 2}
108
109        goto_page2_link = url + '?' + _urlencode(params)
110        goto_page2_reverse_link = url + '?' + _urlencode(reverse_params)
111
112        page = self.app.get(url)
113        assert '/multiple_paginators/42?testdata2_page=2' in page, str(page)
114        assert '/multiple_paginators/42?testdata_page=2' in page, str(page)
115
116        url = '/multiple_paginators/42?testdata_page=2'
117        page = self.app.get(url)
118
119        assert (
120            goto_page2_link in page or goto_page2_reverse_link in page
121        ), str(page)
122        assert '/multiple_paginators/42?testdata_page=4' in page, str(page)
123
124        assert '<li>0</li>' not in page
125        assert '<li>10</li>' in page
126        assert '<li>142</li>' in page
127        assert '<li>151</li>' in page
128
129        url = '/multiple_paginators/42?testdata2_page=2'
130        page = self.app.get(url)
131
132        assert (
133            goto_page2_link in page or goto_page2_reverse_link in page
134        ), str(page)
135        assert '/multiple_paginators/42?testdata2_page=4' in page, str(page)
136
137        assert '<li>0</li>' in page
138        assert '<li>9</li>' in page
139        assert '<li>151</li>' not in page
140        assert '<li>161</li>' in page
141
142    def test_json_pagination(self):
143        url = '/paginated/42.json'
144        page = self.app.get(url)
145        assert '[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]' in page
146
147        url = '/paginated/42.json?page=2'
148        page = self.app.get(url)
149        assert '[10, 11, 12, 13, 14, 15, 16, 17, 18, 19]' in page
150
151
152class TestPage(object):
153    def test_not_a_number_page(self):
154        p = Page(range(100), items_per_page=10, page='A')
155        sec = list(p)
156        assert sec[-1] == 9, sec
157
158    def test_empty_list(self):
159        p = Page([], items_per_page=10, page=1)
160        assert list(p) == []
161
162    def test_page_out_of_bound(self):
163        p = Page(range(100), items_per_page=10, page=10000)
164        sec = list(p)
165        assert sec[-1] == 99, sec
166
167    def test_page_out_of_lower_bound(self):
168        p = Page(range(100), items_per_page=10, page=-5)
169        sec = list(p)
170        assert sec[-1] == 9, sec
171
172    def test_navigator_one_page(self):
173        with test_context(None, '/'):
174            p = Page(range(10), items_per_page=10, page=10)
175            assert p.pager() == ''
176
177    def test_navigator_middle_page(self):
178        with test_context(None, '/'):
179            p = Page(range(100), items_per_page=10, page=5)
180            pager = p.pager()
181
182            assert '?page=1' in pager
183            assert '?page=4' in pager
184            assert '?page=6' in pager
185            assert '?page=10' in pager
186
187    def test_navigator_ajax(self):
188        with test_context(None, '/'):
189            p = Page(range(100), items_per_page=10, page=5)
190            pager = p.pager(onclick='goto($page)')
191
192            assert 'goto(1)' in pager
193            assert 'goto(4)' in pager
194            assert 'goto(6)' in pager
195            assert 'goto(10)' in pager
196
197
198try:
199    import sqlite3
200except:
201    import pysqlite2
202from sqlalchemy import (MetaData, Table, Column, ForeignKey, Integer, String)
203from sqlalchemy.orm import create_session, mapper, relation
204
205metadata = MetaData('sqlite:///:memory:')
206
207test1 = Table('test1', metadata,
208    Column('id', Integer, primary_key=True),
209    Column('val', String(8)))
210
211test2 = Table('test2', metadata,
212    Column('id', Integer, primary_key=True),
213    Column('test1id', Integer, ForeignKey('test1.id')),
214    Column('val', String(8)))
215
216test3 = Table('test3', metadata,
217    Column('id', Integer, primary_key=True),
218    Column('val', String(8)))
219
220test4 = Table('test4', metadata,
221    Column('id', Integer, primary_key=True),
222    Column('val', String(8)))
223
224metadata.create_all()
225
226class Test2(object):
227    pass
228mapper(Test2, test2)
229
230class Test1(object):
231    pass
232mapper(Test1, test1, properties={'test2s': relation(Test2)})
233
234class Test3(object):
235    pass
236mapper(Test3, test3)
237
238class Test4(object):
239    pass
240mapper(Test4, test4)
241
242test1.insert().execute({'id': 1, 'val': 'bob'})
243test2.insert().execute({'id': 1, 'test1id': 1, 'val': 'fred'})
244test2.insert().execute({'id': 2, 'test1id': 1, 'val': 'alice'})
245test3.insert().execute({'id': 1, 'val': 'bob'})
246test4.insert().execute({'id': 1, 'val': 'alberto'})
247
248class TestPageSQLA(object):
249    def setup(self):
250        self.s = create_session()
251
252    def test_relationship(self):
253        t = self.s.query(Test1).get(1)
254        p = Page(t.test2s, items_per_page=1, page=1)
255        assert len(list(p)) == 1
256        assert list(p)[0].val == 'fred', list(p)
257
258    def test_query(self):
259        q = self.s.query(Test2)
260        p = Page(q, items_per_page=1, page=1)
261        assert len(list(p)) == 1
262        assert list(p)[0].val == 'fred', list(p)
263
264    def test_json_query(self):
265        q = self.s.query(Test2)
266        p = Page(q, items_per_page=1, page=1)
267        res = json.loads(json_encode(p))
268        assert len(res['entries']) == 1
269        assert res['total'] == 2
270        assert res['entries'][0]['val'] == 'fred'
271
272
273try:
274    import ming
275    from ming import create_datastore, Session, schema, ASCENDING
276    from ming.odm import ODMSession, FieldProperty, ForeignIdProperty, RelationProperty, Mapper
277    from ming.odm.declarative import MappedClass
278except ImportError:
279    ming = None
280
281
282class TestPageMing(object):
283    @classmethod
284    def setupClass(cls):
285        if ming is None:
286            raise SkipTest('Ming not available...')
287
288        cls.basic_session = Session(create_datastore('mim:///testdb'))
289        cls.s = ODMSession(cls.basic_session)
290
291        class Author(MappedClass):
292            class __mongometa__:
293                session = cls.s
294                name = 'wiki_author'
295
296            _id = FieldProperty(schema.ObjectId)
297            name = FieldProperty(str)
298            pages = RelationProperty('WikiPage')
299
300        class WikiPage(MappedClass):
301            class __mongometa__:
302                session = cls.s
303                name = 'wiki_page'
304
305            _id = FieldProperty(schema.ObjectId)
306            title = FieldProperty(str)
307            text = FieldProperty(str)
308            order = FieldProperty(int)
309            author_id = ForeignIdProperty(Author)
310            author = RelationProperty(Author)
311
312        cls.Author = Author
313        cls.WikiPage = WikiPage
314        Mapper.compile_all()
315
316        cls.author = Author(name='author1')
317        author2 = Author(name='author2')
318
319        WikiPage(title='Hello', text='Text', order=1, author=cls.author)
320        WikiPage(title='Another', text='Text', order=2, author=cls.author)
321        WikiPage(title='ThirdOne', text='Text', order=3, author=author2)
322        cls.s.flush()
323        cls.s.clear()
324
325    def teardown(self):
326        self.s.clear()
327
328    def test_query(self):
329        q = self.WikiPage.query.find().sort([('order', ASCENDING)])
330        p = Page(q, items_per_page=1, page=1)
331        assert len(list(p)) == 1
332        assert list(p)[0].title == 'Hello', list(p)
333
334    def test_json_query(self):
335        q = self.WikiPage.query.find().sort([('order', ASCENDING)])
336        p = Page(q, items_per_page=1, page=1)
337        res = json.loads(json_encode(p))
338        assert len(res['entries']) == 1
339        assert res['total'] == 3
340        assert res['entries'][0]['title'] == 'Hello', res['entries']
341        assert res['entries'][0]['author_id'] == str(self.author._id), res['entries']
342
343    def test_relation(self):
344        a = self.Author.query.find({'name': 'author1'}).first()
345        p = Page(a.pages, items_per_page=1, page=1)
346        assert len(list(p)) == 1
347        assert list(p)[0].title in ('Hello', 'Another'), list(p)
348