1import pytest
2
3
4def test_paginated_children(pad):
5    page1 = pad.get("/projects", page_num=1)
6
7    assert page1 is not None
8    assert page1["_model"] == "projects"
9    assert page1["_template"] == "projects.html"
10
11    assert page1.datamodel.pagination_config.per_page == 4
12
13    assert page1.children.count() == 8
14    assert page1.page_num == 1
15    assert page1.pagination.items.count() == 4
16
17    children = page1.pagination.items.all()
18    assert len(children) == 4
19    assert [x["name"] for x in children] == [
20        u"Coffee",
21        u"Bagpipe",
22        u"Master",
23        u"Oven",
24    ]
25
26    assert ("projects", "_primary", "1") in pad.cache.persistent
27    assert ("projects", "_primary", "2") not in pad.cache.persistent
28
29    page2 = pad.get("/projects", page_num=2)
30
31    assert page2.children.count() == 8
32    assert page2.page_num == 2
33    assert page2.pagination.items.count() == 3
34
35    children = page2.pagination.items.all()
36    assert len(children) == 3
37    assert [x["name"] for x in children] == [
38        u"Postage",
39        u"Slave",
40        u"Wolf",
41    ]
42
43    assert ("projects", "_primary", "2") in pad.cache.persistent
44
45
46def test_unpaginated_children(pad):
47    page_all = pad.get("/projects")
48
49    assert page_all
50
51    assert page_all.pagination.items.count() == 7
52    assert page_all.page_num is None
53
54    children = page_all.pagination.items.all()
55    assert len(children) == 7
56    assert [x["name"] for x in children] == [
57        u"Coffee",
58        u"Bagpipe",
59        u"Master",
60        u"Oven",
61        u"Postage",
62        u"Slave",
63        u"Wolf",
64    ]
65
66
67def test_pagination_access(pad):
68    page = pad.get("/projects", page_num=1)
69
70    assert page.pagination.page == 1
71    assert page.pagination.next.pagination.page == 2
72    assert page.pagination.for_page(2).pagination.page == 2
73
74    assert page.pagination.for_page(0) is None
75    assert page.pagination.for_page(3) is None
76
77    assert pad.get("/projects@1").page_num == 1
78    assert pad.get("/projects@2").page_num == 2
79    assert pad.get("/projects@3").page_num == 3
80
81
82def test_pagination_attributes(pad):
83    page = pad.get("/projects", page_num=1)
84    assert page.pagination.current is page
85    assert page.pagination.next is not None
86    assert page.pagination.next.page_num == 2
87    assert page.pagination.prev is None
88    assert page.pagination.pages == 2
89    assert page.pagination.prev_num is None
90    assert page.pagination.next_num == 2
91    assert page.pagination.has_next
92    assert not page.pagination.has_prev
93
94    page = page.pagination.next
95    assert page.pagination.current is page
96    assert page.pagination.next is None
97    assert page.pagination.prev.page_num == 1
98    assert not page.pagination.has_next
99    assert page.pagination.next is None
100    assert page.pagination.next_num is None
101
102
103def test_url_matching_for_pagination(pad):
104    page1 = pad.resolve_url_path("/projects/")
105    assert page1.page_num == 1
106
107    page2 = pad.resolve_url_path("/projects/page/2/")
108    assert page2.page_num == 2
109
110    page1_explicit = pad.resolve_url_path("/projects/page/1/")
111    assert page1_explicit is None
112
113
114def test_parent_access(pad):
115    page2 = pad.resolve_url_path("/projects/page/2/")
116    assert page2["_path"] == "/projects"
117    assert page2.path == "/projects@2"
118    assert page2.page_num == 2
119
120    child = page2.pagination.items.first()
121    assert child.parent.path == page2["_path"]
122    assert child.parent.page_num is None
123
124
125def test_pagination_url_paths(pad):
126    # Even though this is paginated, getting to the non paginated version
127    # just looks like going to the first page.  We do this because it
128    # is more convenient for usage.
129    project = pad.get("/projects")
130    assert project.url_path == "/projects/"
131
132    # However first page looks the same
133    project = pad.get("/projects", page_num=1)
134    assert project.url_path == "/projects/"
135
136    # second page is different
137    project = pad.get("/projects", page_num=2)
138    assert project.url_path == "/projects/page/2/"
139
140
141def test_unpaginated_children_other_alt(pad):
142    page_all = pad.get("/projects", alt="de")
143
144    assert page_all.pagination.items.count() == 8
145    assert page_all.page_num is None
146
147    children = page_all.pagination.items.all()
148    assert len(children) == 8
149    assert [x["name"] for x in children] == [
150        u"Kaffee",
151        u"Dudelsack",
152        u"Meister",
153        u"Ofen",
154        u"Porto",
155        u"Sklave",
156        u"Wolf",
157        u"Zaun",
158    ]
159
160
161def test_prev_next(pad):
162    # coffee, bagpipe, filtered, master have seq numbers 9, 8, 7, 6.
163    # They disagree with alphabetization, to ensure we use the pagination
164    # query order, "-seq".
165    bagpipe = pad.get("/projects/bagpipe")
166    assert bagpipe.get_siblings().prev_page["_id"] == "coffee"
167
168    # Next child "filtered" is skipped by pagination query, skip to "master".
169    assert bagpipe.get_siblings().next_page["_id"] == "master"
170
171    # Postage is on the previous page before oven, but prev / next ignore pages.
172    oven = pad.get("/projects/oven")
173    assert oven.get_siblings().prev_page["_id"] == "master"
174    assert oven.get_siblings().next_page["_id"] == "postage"
175
176
177def test_url_matching_for_alt_pagination(pad):
178    page1 = pad.resolve_url_path("/de/projects/")
179    assert page1.alt == "de"
180    assert page1.page_num == 1
181
182    page2 = pad.resolve_url_path("/de//projects/page/2/")
183    assert page2.alt == "de"
184    assert page2.page_num == 2
185
186    page1_explicit = pad.resolve_url_path("/de/projects/page/1/")
187    assert page1_explicit is None
188
189
190def test_pagination_items_filter(pad):
191    # This tests that items are excluded from the pagination based on a
192    # query if needed.
193    blog = pad.get("/blog", page_num=1)
194    assert (
195        blog.datamodel.pagination_config.items
196        == "this.children.filter(F._model == 'blog-post')"
197    )
198
199    assert blog.children.count() == 3
200    assert sorted(x["_id"] for x in blog.children) == ["dummy.xml", "post1", "post2"]
201
202    assert blog.pagination.items.count() == 2
203    # Sort order is pub_date descending, so post 2 is first.
204    assert blog.pagination.items.first()["_id"] == "post2"
205
206    dummy = blog.children.get("dummy.xml")
207    assert dummy is not None
208    assert dummy["_model"] == "none"
209
210
211def test_virtual_path_behavior(pad):
212    # Base record
213    blog = pad.get("/blog")
214    assert blog.path == "/blog"
215    assert blog["_path"] == "/blog"
216    assert blog.page_num is None
217    assert blog.url_path == "/blog/"
218    assert blog.record is blog
219
220    # Record for page 1 which is a bit special
221    blog_page1 = pad.get("/blog@1")
222    assert blog_page1.path == "/blog@1"
223    assert blog_page1["_path"] == "/blog"
224    assert blog_page1.url_path == "/blog/"
225    assert blog_page1.page_num == 1
226    assert blog_page1.parent is pad.root
227    assert blog_page1.record is blog
228
229    # Record for page 2 which is slightly less special
230    blog_page2 = pad.get("/blog@2")
231    assert blog_page2.path == "/blog@2"
232    assert blog_page2["_path"] == "/blog"
233    assert blog_page2.url_path == "/blog/page/2/"
234    assert blog_page2.page_num == 2
235    assert blog_page2.parent is pad.root
236    assert blog_page2.record is blog
237
238    # Make sure URL generation works as you would expect:
239    assert blog.url_to("@1", absolute=True) == "/blog/"
240    assert blog.url_to("@2", absolute=True) == "/blog/page/2/"
241    assert blog.url_to("..", absolute=True) == "/"
242    assert blog_page2.url_to("..", absolute=True) == "/"
243    assert blog_page2.url_to("@1", absolute=True) == "/blog/"
244    assert blog_page2.url_to("@3", absolute=True) == "/blog/page/3/"
245
246
247class Test_Pagination_iter_pages(object):
248    @pytest.fixture
249    def pagination(self, pad, page, pages, monkeypatch):
250        projects = pad.get("/projects", page_num=1)
251        pagination = projects.pagination
252        # hack in the desired page and total
253        monkeypatch.setattr(pagination, "page", page)
254        monkeypatch.setattr(pagination, "total", pages)
255        monkeypatch.setattr(pagination, "per_page", 1)
256        assert pagination.pages == pages
257        return pagination
258
259    @pytest.mark.parametrize(
260        (
261            "page",
262            "pages",
263            "left_edge",
264            "left_current",
265            "right_current",
266            "right_edge",
267            "expected",
268        ),
269        [
270            (1, 9, 0, 0, 0, 0, [1, None]),
271            (2, 9, 0, 0, 0, 0, [None, 2, None]),
272            (8, 9, 0, 0, 0, 0, [None, 8, None]),
273            (9, 9, 0, 0, 0, 0, [None, 9]),
274            (1, 9, 1, 0, 0, 1, [1, None, 9]),
275            (2, 9, 1, 0, 0, 1, [1, 2, None, 9]),
276            (3, 9, 1, 0, 0, 1, [1, None, 3, None, 9]),
277            (7, 9, 1, 0, 0, 1, [1, None, 7, None, 9]),
278            (8, 9, 1, 0, 0, 1, [1, None, 8, 9]),
279            (9, 9, 1, 0, 0, 1, [1, None, 9]),
280            (1, 9, 0, 1, 1, 0, [1, 2, None]),
281            (2, 9, 0, 1, 1, 0, [1, 2, 3, None]),
282            (3, 9, 0, 1, 1, 0, [None, 2, 3, 4, None]),
283            (7, 9, 0, 1, 1, 0, [None, 6, 7, 8, None]),
284            (8, 9, 0, 1, 1, 0, [None, 7, 8, 9]),
285            (9, 9, 0, 1, 1, 0, [None, 8, 9]),
286            (1, 9, 1, 1, 1, 1, [1, 2, None, 9]),
287            (2, 9, 1, 1, 1, 1, [1, 2, 3, None, 9]),
288            (3, 9, 1, 1, 1, 1, [1, 2, 3, 4, None, 9]),
289            (4, 9, 1, 1, 1, 1, [1, None, 3, 4, 5, None, 9]),
290            (6, 9, 1, 1, 1, 1, [1, None, 5, 6, 7, None, 9]),
291            (7, 9, 1, 1, 1, 1, [1, None, 6, 7, 8, 9]),
292            (8, 9, 1, 1, 1, 1, [1, None, 7, 8, 9]),
293            (9, 9, 1, 1, 1, 1, [1, None, 8, 9]),
294        ],
295    )
296    def test(
297        self,
298        pagination,
299        page,
300        left_edge,
301        left_current,
302        right_current,
303        right_edge,
304        expected,
305    ):
306        assert (
307            list(
308                pagination.iter_pages(
309                    left_edge, left_current, right_current, right_edge
310                )
311            )
312            == expected
313        )
314