add: adding pagination package
This commit is contained in:
parent
fd9e5c32c5
commit
56743e5b56
13 changed files with 648 additions and 0 deletions
2
packages/pagination/.analysis-options
Normal file
2
packages/pagination/.analysis-options
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
analyzer:
|
||||||
|
strong-mode: true
|
58
packages/pagination/.gitignore
vendored
Normal file
58
packages/pagination/.gitignore
vendored
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
# See https://www.dartlang.org/tools/private-files.html
|
||||||
|
|
||||||
|
# Files and directories created by pub
|
||||||
|
.packages
|
||||||
|
.pub/
|
||||||
|
build/
|
||||||
|
# If you're building an application, you may want to check-in your pubspec.lock
|
||||||
|
pubspec.lock
|
||||||
|
|
||||||
|
# Directory created by dartdoc
|
||||||
|
# If you don't generate documentation locally you can remove this line.
|
||||||
|
doc/api/
|
||||||
|
### JetBrains template
|
||||||
|
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm
|
||||||
|
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
|
||||||
|
|
||||||
|
# User-specific stuff:
|
||||||
|
.idea/**/workspace.xml
|
||||||
|
.idea/**/tasks.xml
|
||||||
|
.idea/dictionaries
|
||||||
|
|
||||||
|
# Sensitive or high-churn files:
|
||||||
|
.idea/**/dataSources/
|
||||||
|
.idea/**/dataSources.ids
|
||||||
|
.idea/**/dataSources.xml
|
||||||
|
.idea/**/dataSources.local.xml
|
||||||
|
.idea/**/sqlDataSources.xml
|
||||||
|
.idea/**/dynamic.xml
|
||||||
|
.idea/**/uiDesigner.xml
|
||||||
|
|
||||||
|
# Gradle:
|
||||||
|
.idea/**/gradle.xml
|
||||||
|
.idea/**/libraries
|
||||||
|
|
||||||
|
# Mongo Explorer plugin:
|
||||||
|
.idea/**/mongoSettings.xml
|
||||||
|
|
||||||
|
## File-based project format:
|
||||||
|
*.iws
|
||||||
|
|
||||||
|
## Plugin-specific files:
|
||||||
|
|
||||||
|
# IntelliJ
|
||||||
|
/out/
|
||||||
|
|
||||||
|
# mpeltonen/sbt-idea plugin
|
||||||
|
.idea_modules/
|
||||||
|
|
||||||
|
# JIRA plugin
|
||||||
|
atlassian-ide-plugin.xml
|
||||||
|
|
||||||
|
# Crashlytics plugin (for Android Studio and IntelliJ)
|
||||||
|
com_crashlytics_export_strings.xml
|
||||||
|
crashlytics.properties
|
||||||
|
crashlytics-build.properties
|
||||||
|
fabric.properties
|
||||||
|
|
||||||
|
.dart_tool
|
12
packages/pagination/AUTHORS.md
Normal file
12
packages/pagination/AUTHORS.md
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
Primary Authors
|
||||||
|
===============
|
||||||
|
|
||||||
|
* __[Thomas Hii](dukefirehawk.apps@gmail.com)__
|
||||||
|
|
||||||
|
Thomas is the current maintainer of the code base. He has refactored and migrated the
|
||||||
|
code base to support NNBD.
|
||||||
|
|
||||||
|
* __[Tobe O](thosakwe@gmail.com)__
|
||||||
|
|
||||||
|
Tobe has written much of the original code prior to NNBD migration. He has moved on and
|
||||||
|
is no longer involved with the project.
|
49
packages/pagination/CHANGELOG.md
Normal file
49
packages/pagination/CHANGELOG.md
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
# Change Log
|
||||||
|
|
||||||
|
## 8.2.0
|
||||||
|
|
||||||
|
* Require Dart >= 3.3
|
||||||
|
* Updated `lints` to 4.0.0
|
||||||
|
|
||||||
|
## 8.1.0
|
||||||
|
|
||||||
|
* Updated repository link
|
||||||
|
* Updated `lints` to 3.0.0
|
||||||
|
* Fixed linter warnings
|
||||||
|
|
||||||
|
## 8.0.0
|
||||||
|
|
||||||
|
* Require Dart >= 3.0
|
||||||
|
|
||||||
|
## 7.0.0
|
||||||
|
|
||||||
|
* Require Dart >= 2.17
|
||||||
|
|
||||||
|
## 6.0.0
|
||||||
|
|
||||||
|
* Require Dart >= 2.16
|
||||||
|
|
||||||
|
## 5.0.0
|
||||||
|
|
||||||
|
* Skipped release
|
||||||
|
|
||||||
|
## 4.0.0
|
||||||
|
|
||||||
|
* Skipped release
|
||||||
|
|
||||||
|
## 3.1.0
|
||||||
|
|
||||||
|
* Updated linter to `package:lints`
|
||||||
|
|
||||||
|
## 3.0.1
|
||||||
|
|
||||||
|
* Updated README
|
||||||
|
|
||||||
|
## 3.0.0
|
||||||
|
|
||||||
|
* Migrated to support Dart >= 2.12 NNBD
|
||||||
|
|
||||||
|
## 2.0.0
|
||||||
|
|
||||||
|
* Dart2 + Angel2 update.
|
||||||
|
|
29
packages/pagination/LICENSE
Normal file
29
packages/pagination/LICENSE
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
BSD 3-Clause License
|
||||||
|
|
||||||
|
Copyright (c) 2021, dukefirehawk.com
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
1. Redistributions of source code must retain the above copyright notice, this
|
||||||
|
list of conditions and the following disclaimer.
|
||||||
|
|
||||||
|
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
this list of conditions and the following disclaimer in the documentation
|
||||||
|
and/or other materials provided with the distribution.
|
||||||
|
|
||||||
|
3. Neither the name of the copyright holder nor the names of its
|
||||||
|
contributors may be used to endorse or promote products derived from
|
||||||
|
this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||||
|
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
|
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||||
|
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
59
packages/pagination/README.md
Normal file
59
packages/pagination/README.md
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
# Angel3 Paginate
|
||||||
|
|
||||||
|
![Pub Version (including pre-releases)](https://img.shields.io/pub/v/platform_pagination?include_prereleases)
|
||||||
|
[![Null Safety](https://img.shields.io/badge/null-safety-brightgreen)](https://dart.dev/null-safety)
|
||||||
|
[![Discord](https://img.shields.io/discord/1060322353214660698)](https://discord.gg/3X6bxTUdCM)
|
||||||
|
[![License](https://img.shields.io/github/license/dart-backend/angel)](https://github.com/dart-backend/angel/tree/master/packages/paginate/LICENSE)
|
||||||
|
|
||||||
|
Platform-agnostic pagination library, with custom support for the [Angel3](https://angel3-framework.web.app/).
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
In your `pubspec.yaml` file:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
dependencies:
|
||||||
|
platform_pagination: ^6.0.0
|
||||||
|
```
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
This library exports a `Paginator<T>`, which can be used to efficiently produce instances of `PaginationResult<T>`. Pagination results, when serialized to JSON, look like this:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"total" : 75,
|
||||||
|
"items_per_page" : 10,
|
||||||
|
"previous_page" : 3,
|
||||||
|
"current_page" : 4,
|
||||||
|
"next_page" : 5,
|
||||||
|
"start_index" : 30,
|
||||||
|
"end_index" : 39,
|
||||||
|
"data" : ["<items...>"]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Results can be parsed from Maps using the `PaginationResult<T>.fromMap` constructor, and serialized via their `toJson()` method.
|
||||||
|
|
||||||
|
To create a paginator:
|
||||||
|
|
||||||
|
```dart
|
||||||
|
import 'package:platform_pagination/platform_pagination.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
var p = new Paginator(iterable);
|
||||||
|
|
||||||
|
// Get the current page (default: page 1)
|
||||||
|
var page = p.current;
|
||||||
|
print(page.total);
|
||||||
|
print(page.startIndex);
|
||||||
|
print(page.data); // The actual items on this page.
|
||||||
|
p.next(); // Advance a page
|
||||||
|
p.back(); // Back one page
|
||||||
|
p.goToPage(10); // Go to page number (1-based, not a 0-based index)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The entire Paginator API is documented, so check out the DartDocs.
|
||||||
|
|
||||||
|
Paginators by default cache paginations, to improve performance as you shift through pages. This can be especially helpful in a client-side application where your UX involves a fast response time, i.e. a search page.
|
1
packages/pagination/analysis_options.yaml
Normal file
1
packages/pagination/analysis_options.yaml
Normal file
|
@ -0,0 +1 @@
|
||||||
|
include: package:lints/recommended.yaml
|
15
packages/pagination/example/main.dart
Normal file
15
packages/pagination/example/main.dart
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
import 'package:platform_pagination/platform_pagination.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
var iterable = [1, 2, 3, 4];
|
||||||
|
var p = Paginator(iterable);
|
||||||
|
|
||||||
|
// Get the current page (default: page 1)
|
||||||
|
var page = p.current!;
|
||||||
|
print(page.total);
|
||||||
|
print(page.startIndex);
|
||||||
|
print(page.data); // The actual items on this page.
|
||||||
|
p.next(); // Advance a page
|
||||||
|
p.back(); // Back one page
|
||||||
|
p.goToPage(10); // Go to page number (1-based, not a 0-based index)
|
||||||
|
}
|
194
packages/pagination/lib/platform_pagination.dart
Normal file
194
packages/pagination/lib/platform_pagination.dart
Normal file
|
@ -0,0 +1,194 @@
|
||||||
|
/// Efficiently paginates collections of items in an object-oriented manner.
|
||||||
|
class Paginator<T> {
|
||||||
|
final Map<int, PaginationResult<T>> _cache = {};
|
||||||
|
PaginationResult<T>? _current;
|
||||||
|
int _page = 0;
|
||||||
|
|
||||||
|
/// The collection of items to be paginated.
|
||||||
|
final Iterable<T> _items;
|
||||||
|
|
||||||
|
/// The maximum number of items to be shown per page.
|
||||||
|
final int itemsPerPage;
|
||||||
|
|
||||||
|
/// If `true` (default), then the results of paginations will be saved by page number.
|
||||||
|
///
|
||||||
|
/// For example, you would only have to paginate page 1 once. Future calls would return a cached version.
|
||||||
|
final bool useCache;
|
||||||
|
|
||||||
|
Paginator(this._items, {this.itemsPerPage = 5, this.useCache = true});
|
||||||
|
|
||||||
|
/// Returns `true` if there are more items at lesser page indices than the current one.
|
||||||
|
bool get canGoBack => _page > 0;
|
||||||
|
|
||||||
|
/// Returns `true` if there are more items at greater page indices than the current one.
|
||||||
|
bool get canGoForward => _page < _lastPage();
|
||||||
|
|
||||||
|
/// The current page index.
|
||||||
|
int get index => _page;
|
||||||
|
|
||||||
|
/// Returns the greatest possible page number for this collection, given the number of [itemsPerPage].
|
||||||
|
int get lastPageNumber => _lastPage();
|
||||||
|
|
||||||
|
/// The current page number. This is not the same as [index].
|
||||||
|
///
|
||||||
|
/// This getter will return user-friendly numbers. The lowest value it will ever return is `1`.
|
||||||
|
int get pageNumber => _page < 1 ? 1 : (_page + 1);
|
||||||
|
|
||||||
|
/// Fetches the current page. This will be cached until [back] or [next] is called.
|
||||||
|
///
|
||||||
|
/// If [useCache] is `true` (default), then computations will be cached even after the page changes.
|
||||||
|
PaginationResult<T>? get current {
|
||||||
|
if (_current != null) {
|
||||||
|
return _current;
|
||||||
|
} else {
|
||||||
|
return _current = _getPage();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PaginationResult<T> _computePage() {
|
||||||
|
var len = _items.length;
|
||||||
|
//var it = _items.skip(_page * (itemsPerPage ?? 5));
|
||||||
|
var it = _items.skip(_page * (itemsPerPage));
|
||||||
|
var offset = len - it.length;
|
||||||
|
it = it.take(itemsPerPage);
|
||||||
|
var last = _lastPage();
|
||||||
|
// print('cur: $_page, last: $last');
|
||||||
|
return _PaginationResultImpl(it,
|
||||||
|
currentPage: _page + 1,
|
||||||
|
previousPage: _page <= 0 ? -1 : _page,
|
||||||
|
nextPage: _page >= last - 1 ? -1 : _page + 2,
|
||||||
|
startIndex: it.isEmpty ? -1 : offset,
|
||||||
|
endIndex: offset + it.length - 1,
|
||||||
|
itemsPerPage:
|
||||||
|
itemsPerPage < _items.length ? itemsPerPage : _items.length,
|
||||||
|
total: len);
|
||||||
|
}
|
||||||
|
|
||||||
|
PaginationResult<T> _getPage() {
|
||||||
|
if (useCache != false) {
|
||||||
|
return _cache.putIfAbsent(_page, () => _computePage());
|
||||||
|
} else {
|
||||||
|
return _computePage();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int _lastPage() {
|
||||||
|
var n = (_items.length / itemsPerPage).round();
|
||||||
|
// print('items: ${_items.length}');
|
||||||
|
// print('per page: $itemsPerPage');
|
||||||
|
// print('quotient: $n');
|
||||||
|
var remainder = _items.length - (n * itemsPerPage);
|
||||||
|
// print('remainder: $remainder');
|
||||||
|
return (remainder <= 0) ? n : n + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Attempts to go the specified page. If it fails, then it will remain on the current page.
|
||||||
|
///
|
||||||
|
/// Keep in mind - this just not be a zero-based index, but a one-based page number. The lowest
|
||||||
|
/// allowed value is `1`.
|
||||||
|
void goToPage(int page) {
|
||||||
|
if (page > 0 && page <= _lastPage()) {
|
||||||
|
_page = page - 1;
|
||||||
|
_current = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Moves the paginator back one page, if possible.
|
||||||
|
void back() {
|
||||||
|
if (_page > 0) {
|
||||||
|
_page--;
|
||||||
|
_current = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Advances the paginator one page, if possible.
|
||||||
|
void next() {
|
||||||
|
if (_page < _lastPage()) {
|
||||||
|
_page++;
|
||||||
|
_current = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Stores the result of a pagination.
|
||||||
|
abstract class PaginationResult<T> {
|
||||||
|
factory PaginationResult.fromMap(Map<String, dynamic> map) =>
|
||||||
|
_PaginationResultImpl((map['data'] as List).cast<T>(),
|
||||||
|
currentPage: map['current_page'] as int?,
|
||||||
|
endIndex: map['end_index'] as int?,
|
||||||
|
itemsPerPage: map['items_per_page'] as int?,
|
||||||
|
nextPage: map['next_page'] as int?,
|
||||||
|
previousPage: map['previous_page'] as int?,
|
||||||
|
startIndex: map['start_index'] as int?,
|
||||||
|
total: map['total'] as int?);
|
||||||
|
|
||||||
|
Iterable<T> get data;
|
||||||
|
|
||||||
|
int? get currentPage;
|
||||||
|
|
||||||
|
int? get previousPage;
|
||||||
|
|
||||||
|
int? get nextPage;
|
||||||
|
|
||||||
|
int? get itemsPerPage;
|
||||||
|
|
||||||
|
int? get total;
|
||||||
|
|
||||||
|
int? get startIndex;
|
||||||
|
|
||||||
|
int? get endIndex;
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _PaginationResultImpl<T> implements PaginationResult<T> {
|
||||||
|
final Iterable<T> _data;
|
||||||
|
Iterable<T>? _cachedData;
|
||||||
|
|
||||||
|
@override
|
||||||
|
final int? currentPage;
|
||||||
|
|
||||||
|
_PaginationResultImpl(this._data,
|
||||||
|
{this.currentPage,
|
||||||
|
this.endIndex,
|
||||||
|
this.itemsPerPage,
|
||||||
|
this.nextPage,
|
||||||
|
this.previousPage,
|
||||||
|
this.startIndex,
|
||||||
|
this.total});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Iterable<T> get data => _cachedData ?? (_cachedData = List<T>.from(_data));
|
||||||
|
|
||||||
|
@override
|
||||||
|
final int? endIndex;
|
||||||
|
|
||||||
|
@override
|
||||||
|
final int? itemsPerPage;
|
||||||
|
|
||||||
|
@override
|
||||||
|
final int? nextPage;
|
||||||
|
|
||||||
|
@override
|
||||||
|
final int? previousPage;
|
||||||
|
|
||||||
|
@override
|
||||||
|
final int? startIndex;
|
||||||
|
|
||||||
|
@override
|
||||||
|
final int? total;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
return {
|
||||||
|
'total': total,
|
||||||
|
'items_per_page': itemsPerPage,
|
||||||
|
'previous_page': previousPage,
|
||||||
|
'current_page': currentPage,
|
||||||
|
'next_page': nextPage,
|
||||||
|
'start_index': startIndex,
|
||||||
|
'end_index': endIndex,
|
||||||
|
'data': data
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
37
packages/pagination/pubspec.yaml
Normal file
37
packages/pagination/pubspec.yaml
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
name: platform_pagination
|
||||||
|
version: 8.2.0
|
||||||
|
description: Platform-agnostic pagination library, with custom support for the Angel3 framework.
|
||||||
|
homepage: https://angel3-framework.web.app/
|
||||||
|
repository: https://github.com/dart-backend/angel/tree/master/packages/paginate
|
||||||
|
environment:
|
||||||
|
sdk: '>=3.3.0 <4.0.0'
|
||||||
|
dependencies:
|
||||||
|
platform_foundation: ^8.0.0
|
||||||
|
dev_dependencies:
|
||||||
|
platform_testing: ^8.0.0
|
||||||
|
logging: ^1.2.0
|
||||||
|
test: ^1.24.0
|
||||||
|
lints: ^4.0.0
|
||||||
|
# dependency_overrides:
|
||||||
|
# angel3_framework:
|
||||||
|
# path: ../framework
|
||||||
|
# angel3_test:
|
||||||
|
# path: ../test
|
||||||
|
# angel3_container:
|
||||||
|
# path: ../container/angel_container
|
||||||
|
# angel3_http_exception:
|
||||||
|
# path: ../http_exception
|
||||||
|
# angel3_model:
|
||||||
|
# path: ../model
|
||||||
|
# angel3_route:
|
||||||
|
# path: ../route
|
||||||
|
# angel3_mock_request:
|
||||||
|
# path: ../mock_request
|
||||||
|
# angel3_websocket:
|
||||||
|
# path: ../websocket
|
||||||
|
# angel3_client:
|
||||||
|
# path: ../client
|
||||||
|
# angel3_auth:
|
||||||
|
# path: ../auth
|
||||||
|
# angel3_validate:
|
||||||
|
# path: ../validate
|
8
packages/pagination/test/all_test.dart
Normal file
8
packages/pagination/test/all_test.dart
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
import 'package:test/test.dart';
|
||||||
|
import 'bounds_test.dart' as bounds;
|
||||||
|
import 'paginate_test.dart' as paginate;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
group('bounds', bounds.main);
|
||||||
|
group('paginate', paginate.main);
|
||||||
|
}
|
57
packages/pagination/test/bounds_test.dart
Normal file
57
packages/pagination/test/bounds_test.dart
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
import 'dart:convert';
|
||||||
|
import 'package:platform_foundation/core.dart';
|
||||||
|
import 'package:platform_pagination/platform_pagination.dart';
|
||||||
|
import 'package:platform_testing/testing.dart';
|
||||||
|
import 'package:logging/logging.dart';
|
||||||
|
import 'package:test/test.dart';
|
||||||
|
|
||||||
|
final List<Map<String, String>> mjAlbums = [
|
||||||
|
{'billie': 'jean'},
|
||||||
|
{'off': 'the_wall'},
|
||||||
|
{'michael': 'jackson'}
|
||||||
|
];
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
late TestClient client;
|
||||||
|
|
||||||
|
setUp(() async {
|
||||||
|
var app = Application();
|
||||||
|
|
||||||
|
app.get('/api/songs', (req, res) {
|
||||||
|
var p = Paginator(mjAlbums, itemsPerPage: mjAlbums.length);
|
||||||
|
p.goToPage(int.parse(req.queryParameters['page'] as String? ?? '1'));
|
||||||
|
return p.current;
|
||||||
|
});
|
||||||
|
|
||||||
|
client = await connectTo(app);
|
||||||
|
|
||||||
|
app.logger = Logger('angel_paginate')
|
||||||
|
..onRecord.listen((rec) {
|
||||||
|
print(rec);
|
||||||
|
if (rec.error != null) print(rec.error);
|
||||||
|
if (rec.stackTrace != null) print(rec.stackTrace);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
tearDown(() => client.close());
|
||||||
|
|
||||||
|
test('limit exceeds size of collection', () async {
|
||||||
|
var response = await client.get(Uri(
|
||||||
|
path: '/api/songs',
|
||||||
|
queryParameters: {r'$limit': (mjAlbums.length + 1).toString()}));
|
||||||
|
|
||||||
|
var page = PaginationResult<Map<String, dynamic>>.fromMap(
|
||||||
|
json.decode(response.body) as Map<String, dynamic>);
|
||||||
|
|
||||||
|
print('page: ${page.toJson()}');
|
||||||
|
|
||||||
|
expect(page.total, mjAlbums.length);
|
||||||
|
expect(page.itemsPerPage, mjAlbums.length);
|
||||||
|
expect(page.previousPage, -1);
|
||||||
|
expect(page.currentPage, 1);
|
||||||
|
expect(page.nextPage, -1);
|
||||||
|
expect(page.startIndex, 0);
|
||||||
|
expect(page.endIndex, mjAlbums.length - 1);
|
||||||
|
expect(page.data, mjAlbums);
|
||||||
|
});
|
||||||
|
}
|
127
packages/pagination/test/paginate_test.dart
Normal file
127
packages/pagination/test/paginate_test.dart
Normal file
|
@ -0,0 +1,127 @@
|
||||||
|
import 'package:platform_pagination/platform_pagination.dart';
|
||||||
|
import 'package:test/test.dart';
|
||||||
|
|
||||||
|
// Count-down from 100, then 101 at the end...
|
||||||
|
final List<int> data = List<int>.generate(100, (i) => 100 - i)..add(101);
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
group('cache', () {
|
||||||
|
var cached = Paginator<int>(data),
|
||||||
|
uncached = Paginator<int>(data, useCache: false);
|
||||||
|
|
||||||
|
test('always cache current', () {
|
||||||
|
expect(cached.current, cached.current);
|
||||||
|
expect(uncached.current, uncached.current);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('only cache prev/next if useCache != false', () {
|
||||||
|
var cached1 = cached.current;
|
||||||
|
cached.goToPage(4);
|
||||||
|
var cached4 = cached.current;
|
||||||
|
cached.goToPage(1);
|
||||||
|
expect(cached.current, cached1);
|
||||||
|
cached.goToPage(4);
|
||||||
|
expect(cached.current, cached4);
|
||||||
|
|
||||||
|
var uncached1 = uncached.current;
|
||||||
|
uncached.goToPage(4);
|
||||||
|
var uncached4 = uncached.current;
|
||||||
|
uncached.goToPage(1);
|
||||||
|
expect(uncached.current, isNot(uncached1));
|
||||||
|
uncached.goToPage(4);
|
||||||
|
expect(uncached.current, isNot(uncached4));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('default state', () {
|
||||||
|
var paginator = Paginator<int>(data);
|
||||||
|
expect(paginator.index, 0);
|
||||||
|
expect(paginator.pageNumber, 1);
|
||||||
|
expect(paginator.itemsPerPage, 5);
|
||||||
|
expect(paginator.useCache, true);
|
||||||
|
expect(paginator.canGoBack, false);
|
||||||
|
expect(paginator.canGoForward, true);
|
||||||
|
expect(paginator.lastPageNumber, 21);
|
||||||
|
|
||||||
|
// Going back should do nothing
|
||||||
|
var p = paginator.pageNumber;
|
||||||
|
paginator.back();
|
||||||
|
expect(paginator.pageNumber, p);
|
||||||
|
});
|
||||||
|
|
||||||
|
group('paginate', () {
|
||||||
|
test('first page', () {
|
||||||
|
var paginator = Paginator<int>(data);
|
||||||
|
expect(paginator.pageNumber, 1);
|
||||||
|
var r = paginator.current!;
|
||||||
|
print(r.toJson());
|
||||||
|
expect(r.total, data.length);
|
||||||
|
expect(r.itemsPerPage, 5);
|
||||||
|
expect(r.previousPage, -1);
|
||||||
|
expect(r.currentPage, 1);
|
||||||
|
expect(r.nextPage, 2);
|
||||||
|
expect(r.startIndex, 0);
|
||||||
|
expect(r.endIndex, 4);
|
||||||
|
expect(r.data, data.skip(r.startIndex!).take(r.itemsPerPage!).toList());
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('third page', () {
|
||||||
|
var paginator = Paginator<int>(data)..goToPage(3);
|
||||||
|
expect(paginator.pageNumber, 3);
|
||||||
|
var r = paginator.current!;
|
||||||
|
print(r.toJson());
|
||||||
|
expect(r.total, data.length);
|
||||||
|
expect(r.itemsPerPage, 5);
|
||||||
|
expect(r.previousPage, 2);
|
||||||
|
expect(r.currentPage, 3);
|
||||||
|
expect(r.nextPage, 4);
|
||||||
|
expect(r.startIndex, 10);
|
||||||
|
expect(r.endIndex, 14);
|
||||||
|
expect(r.data, data.skip(r.startIndex!).take(r.itemsPerPage!).toList());
|
||||||
|
|
||||||
|
paginator.back();
|
||||||
|
expect(paginator.pageNumber, 2);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('last page', () {
|
||||||
|
var paginator = Paginator<int>(data);
|
||||||
|
paginator.goToPage(paginator.lastPageNumber);
|
||||||
|
var r = paginator.current!;
|
||||||
|
expect(r.total, data.length);
|
||||||
|
expect(r.itemsPerPage, 5);
|
||||||
|
expect(r.previousPage, paginator.lastPageNumber - 1);
|
||||||
|
expect(r.currentPage, paginator.lastPageNumber);
|
||||||
|
expect(r.nextPage, -1);
|
||||||
|
expect(r.startIndex, (paginator.lastPageNumber - 1) * 5);
|
||||||
|
expect(r.endIndex, r.startIndex);
|
||||||
|
expect(r.data, [data.last]);
|
||||||
|
expect(r.data, data.skip(r.startIndex!).take(r.itemsPerPage!).toList());
|
||||||
|
});
|
||||||
|
|
||||||
|
test('dump pages', () {
|
||||||
|
var paginator = Paginator<int>(data);
|
||||||
|
print('${paginator.lastPageNumber} page(s) of data:');
|
||||||
|
|
||||||
|
do {
|
||||||
|
print(' * Page #${paginator.pageNumber}: ${paginator.current!.data}');
|
||||||
|
paginator.next();
|
||||||
|
} while (paginator.canGoForward);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('empty collection', () {
|
||||||
|
var paginator = Paginator([]);
|
||||||
|
var page = paginator.current!;
|
||||||
|
print(page.toJson());
|
||||||
|
|
||||||
|
expect(page.total, 0);
|
||||||
|
expect(page.previousPage, -1);
|
||||||
|
expect(page.nextPage, -1);
|
||||||
|
expect(page.currentPage, 1);
|
||||||
|
expect(page.startIndex, -1);
|
||||||
|
expect(page.endIndex, -1);
|
||||||
|
expect(page.data, isEmpty);
|
||||||
|
expect(paginator.canGoBack, isFalse);
|
||||||
|
expect(paginator.canGoForward, isFalse);
|
||||||
|
});
|
||||||
|
}
|
Loading…
Reference in a new issue