From e2d0e4e486df08c2bebc41826e93edb6348084c9 Mon Sep 17 00:00:00 2001 From: Mohammad Bagher Fakouri Date: Thu, 22 Dec 2022 22:34:17 +0330 Subject: [PATCH] feat: Add `include` option to `getObject` and `fetch` (#798) --- packages/dart/CHANGELOG.md | 6 + packages/dart/lib/parse_server_sdk.dart | 1 - .../dart/lib/src/base/parse_constants.dart | 2 +- .../dart/lib/src/network/parse_query.dart | 14 - .../dart/lib/src/objects/parse_object.dart | 16 +- packages/dart/lib/src/utils/parse_utils.dart | 15 + packages/dart/pubspec.yaml | 2 +- .../test/parse_client_configuration_test.dart | 2 + packages/dart/test/parse_encoder_test.dart | 47 ++- packages/dart/test/parse_object_test.dart | 185 ++++++++++++ packages/dart/test/parse_query_test.dart | 285 +++++++++++------- .../repository_diet_plan_api_test.dart | 49 ++- .../repository_diet_plan_db_test.dart | 58 ++-- .../diet_plan/repository_diet_plan_test.dart | 20 +- .../test/parse_client_configuration_test.dart | 2 + 15 files changed, 498 insertions(+), 206 deletions(-) create mode 100644 packages/dart/test/parse_object_test.dart diff --git a/packages/dart/CHANGELOG.md b/packages/dart/CHANGELOG.md index a8b86c369..088fb9674 100644 --- a/packages/dart/CHANGELOG.md +++ b/packages/dart/CHANGELOG.md @@ -1,3 +1,9 @@ +## [3.1.7](https://github.com/parse-community/Parse-SDK-Flutter/compare/dart-3.1.6...dart-3.1.7) (2022-12-22) + +### Features + +* Add `include` option to `getObject` and `fetch` ([#798](https://github.com/parse-community/Parse-SDK-Flutter/issues/798)) + ## [3.1.6](https://github.com/parse-community/Parse-SDK-Flutter/compare/dart-3.1.5...dart-3.1.6) (2022-12-21) ### Bug Fixes diff --git a/packages/dart/lib/parse_server_sdk.dart b/packages/dart/lib/parse_server_sdk.dart index 180f27372..30ac047a8 100644 --- a/packages/dart/lib/parse_server_sdk.dart +++ b/packages/dart/lib/parse_server_sdk.dart @@ -5,7 +5,6 @@ import 'dart:convert'; import 'dart:io'; import 'dart:math'; import 'dart:typed_data'; - import 'package:dio/dio.dart'; import 'package:meta/meta.dart'; import 'package:mime_type/mime_type.dart'; diff --git a/packages/dart/lib/src/base/parse_constants.dart b/packages/dart/lib/src/base/parse_constants.dart index 133420012..2f843c3ed 100644 --- a/packages/dart/lib/src/base/parse_constants.dart +++ b/packages/dart/lib/src/base/parse_constants.dart @@ -1,7 +1,7 @@ part of flutter_parse_sdk; // Library -const String keySdkVersion = '3.1.6'; +const String keySdkVersion = '3.1.7'; const String keyLibraryName = 'Flutter Parse SDK'; // End Points diff --git a/packages/dart/lib/src/network/parse_query.dart b/packages/dart/lib/src/network/parse_query.dart index f25bb7c2c..e1037b61a 100644 --- a/packages/dart/lib/src/network/parse_query.dart +++ b/packages/dart/lib/src/network/parse_query.dart @@ -435,20 +435,6 @@ class QueryBuilder { return queryBuilder; } - String concatenateArray(List queries) { - String queryBuilder = ''; - - for (final String item in queries) { - if (item == queries.first) { - queryBuilder += item; - } else { - queryBuilder += ',$item'; - } - } - - return queryBuilder; - } - /// Creates a query param using the column, the value and the queryOperator /// that the column and value are being queried against MapEntry _buildQueryWithColumnValueAndOperator( diff --git a/packages/dart/lib/src/objects/parse_object.dart b/packages/dart/lib/src/objects/parse_object.dart index 3a2ee5728..772bf2e4f 100644 --- a/packages/dart/lib/src/objects/parse_object.dart +++ b/packages/dart/lib/src/objects/parse_object.dart @@ -33,9 +33,15 @@ class ParseObject extends ParseBase implements ParseCloneable { late ParseClient _client; /// Gets an object from the server using it's [String] objectId - Future getObject(String objectId) async { + /// + /// `List` include refers to other ParseObjects stored as a Pointer + Future getObject(String objectId, + {List? include}) async { try { - final String uri = '$_path/$objectId'; + String uri = '$_path/$objectId'; + if (include != null) { + uri = '$uri?include=${concatenateArray(include)}'; + } final Uri url = getSanitisedUri(_client, uri); final ParseNetworkResponse result = await _client.get(url.toString()); @@ -451,12 +457,14 @@ class ParseObject extends ParseBase implements ParseCloneable { ///Fetches this object with the data from the server. Call this whenever you want the state of the ///object to reflect exactly what is on the server. - Future fetch() async { + /// + /// `List` include refers to other ParseObjects stored as a Pointer + Future fetch({List? include}) async { if (objectId == null || objectId!.isEmpty) { throw 'can not fetch without a objectId'; } - final ParseResponse response = await getObject(objectId!); + final ParseResponse response = await getObject(objectId!, include: include); if (response.success && response.results != null) { return response.results!.first; diff --git a/packages/dart/lib/src/utils/parse_utils.dart b/packages/dart/lib/src/utils/parse_utils.dart index 39bff368f..16c5ed4ec 100644 --- a/packages/dart/lib/src/utils/parse_utils.dart +++ b/packages/dart/lib/src/utils/parse_utils.dart @@ -8,6 +8,21 @@ bool isDebugEnabled({bool? objectLevelDebug}) { return objectLevelDebug ?? ParseCoreData().debug; } +/// Convert list of strings to a string with commas +String concatenateArray(List list) { + String output = ''; + + for (final String item in list) { + if (item == list.first) { + output += item; + } else { + output += ',$item'; + } + } + + return output; +} + /// Converts the object to the correct value for JSON, /// /// Strings are wrapped with "" but integers and others are not diff --git a/packages/dart/pubspec.yaml b/packages/dart/pubspec.yaml index e642205d6..0ec8cbc7a 100644 --- a/packages/dart/pubspec.yaml +++ b/packages/dart/pubspec.yaml @@ -1,6 +1,6 @@ name: parse_server_sdk description: Dart plugin for Parse Server, (https://parseplatform.org), (https://back4app.com) -version: 3.1.6 +version: 3.1.7 homepage: https://github.com/parse-community/Parse-SDK-Flutter environment: diff --git a/packages/dart/test/parse_client_configuration_test.dart b/packages/dart/test/parse_client_configuration_test.dart index 446cebfa8..8c1c57624 100644 --- a/packages/dart/test/parse_client_configuration_test.dart +++ b/packages/dart/test/parse_client_configuration_test.dart @@ -3,6 +3,7 @@ import 'package:test/test.dart'; void main() { test('testBuilder', () async { + // arrange await Parse().initialize( 'appId', 'serverUrl', @@ -17,6 +18,7 @@ void main() { debug: true, ); + // assert expect(ParseCoreData().applicationId, 'appId'); expect(ParseCoreData().serverUrl, 'serverUrl'); expect(ParseCoreData().clientKey, 'clientKey'); diff --git a/packages/dart/test/parse_encoder_test.dart b/packages/dart/test/parse_encoder_test.dart index 97157520c..dc22fea38 100644 --- a/packages/dart/test/parse_encoder_test.dart +++ b/packages/dart/test/parse_encoder_test.dart @@ -1,3 +1,5 @@ +import 'dart:convert'; + import 'package:parse_server_sdk/parse_server_sdk.dart'; import 'package:test/test.dart'; @@ -8,7 +10,7 @@ void main() { // arrange await Parse().initialize( 'appId', - 'https://test.parse.com', + 'https://example.com', debug: true, // to prevent automatic detection fileDirectory: 'someDirectory', @@ -20,10 +22,10 @@ void main() { appVersion: 'someAppVersion', ); + // act ParseObject parseObject2 = ParseObject("objectId2"); parseObject2.objectId = "objectId2"; - // List and Map parseObject2 .setAdd("dataParseObjectList", ["ListText1", "ListText2", "ListText3"]); parseObject2.setAdd("dataParseObjectMap", { @@ -32,19 +34,44 @@ void main() { 'KeyTestMap3': 'ValueTestMap3', }); - // parseObject2 inside parseObject1 ParseObject parseObject1 = ParseObject("parseObject1"); parseObject1.objectId = "objectId1"; parseObject1.setAdd("dataParseObject2", parseObject2); - // desired output - String expectedResult = - "{className: parseObject1, objectId: objectId1, dataParseObject2: {__op: Add, objects: [{className: objectId2, objectId: objectId2, dataParseObjectList: {__op: Add, objects: [[ListText1, ListText2, ListText3]]}, dataParseObjectMap: {__op: Add, objects: [{KeyTestMap1: ValueTestMap1, KeyTestMap2: ValueTestMap2, KeyTestMap3: ValueTestMap3}]}}]}}"; - - // act dynamic actualResult = parseEncode(parseObject1, full: true); - //assert - expect(actualResult.toString(), expectedResult); + var objectDesiredOutput = { + "className": "parseObject1", + "objectId": "objectId1", + "dataParseObject2": { + "__op": "Add", + "objects": [ + { + "className": "objectId2", + "objectId": "objectId2", + "dataParseObjectList": { + "__op": "Add", + "objects": [ + ["ListText1", "ListText2", "ListText3"] + ], + }, + "dataParseObjectMap": { + "__op": "Add", + "objects": [ + { + "KeyTestMap1": "ValueTestMap1", + "KeyTestMap2": "ValueTestMap2", + "KeyTestMap3": "ValueTestMap3" + } + ] + } + } + ], + }, + }; + var objectJsonDesiredOutput = jsonEncode(objectDesiredOutput); + + // assert + expect(jsonEncode(actualResult), objectJsonDesiredOutput); }); } diff --git a/packages/dart/test/parse_object_test.dart b/packages/dart/test/parse_object_test.dart new file mode 100644 index 000000000..e08926e40 --- /dev/null +++ b/packages/dart/test/parse_object_test.dart @@ -0,0 +1,185 @@ +import 'dart:convert'; + +import 'package:mockito/annotations.dart'; +import 'package:mockito/mockito.dart'; +import 'package:parse_server_sdk/parse_server_sdk.dart'; +import 'package:test/test.dart'; +import 'parse_query_test.mocks.dart'; + +@GenerateMocks([ParseClient]) +void main() { + group('parseObject', () { + late MockParseClient client; + setUp(() async { + client = MockParseClient(); + + await Parse().initialize( + 'appId', + 'https://example.com', + debug: true, + // to prevent automatic detection + fileDirectory: 'someDirectory', + // to prevent automatic detection + appName: 'appName', + // to prevent automatic detection + appPackageName: 'somePackageName', + // to prevent automatic detection + appVersion: 'someAppVersion', + ); + }); + + test('should return expectedIncludeResult json when use fetch', () async { + // arrange + ParseObject myUserObject = ParseObject("MyUser", client: client); + myUserObject.objectId = "Mn1iJTkWTE"; + + var desiredOutput = { + "results": [ + { + "objectId": "Mn1iJTkWTE", + "phone": "+12025550463", + "createdAt": "2022-09-04T13:35:20.883Z", + "updatedAt": "2022-11-14T10:55:56.202Z", + "img": { + "objectId": "8nGrLj3Mvk", + "size": "67663", + "mime": "image/jpg", + "file": { + "__type": "File", + "name": "dc7320ee9146ee19aed8997722fd4e3c.bin", + "url": + "http://ip:port/api/files/myapp/dc7320ee9146ee19aed8997722fd4e3c.bin", + }, + "createdAt": "2022-11-14T10:55:56.025Z", + "updatedAt": "2022-11-14T10:55:56.025Z", + "__type": "Object", + "className": "MyFile", + }, + } + ] + }; + + when(client.get( + any, + options: anyNamed("options"), + onReceiveProgress: anyNamed("onReceiveProgress"), + )).thenAnswer((_) async => ParseNetworkResponse( + statusCode: 200, data: jsonEncode(desiredOutput))); + + // act + ParseObject parseObject = await myUserObject.fetch(include: ["img"]); + + var objectDesiredOutput = { + "className": "MyFile", + "objectId": "8nGrLj3Mvk", + "createdAt": "2022-11-14T10:55:56.025Z", + "updatedAt": "2022-11-14T10:55:56.025Z", + "size": "67663", + "mime": "image/jpg", + "file": { + "__type": "File", + "name": "dc7320ee9146ee19aed8997722fd4e3c.bin", + "url": + "http://ip:port/api/files/myapp/dc7320ee9146ee19aed8997722fd4e3c.bin", + }, + }; + var objectJsonDesiredOutput = jsonEncode(objectDesiredOutput); + + final Uri result = Uri.parse(verify(client.get( + captureAny, + options: anyNamed("options"), + onReceiveProgress: anyNamed("onReceiveProgress"), + )).captured.single); + + // assert + expect( + jsonEncode( + parseEncode(parseObject.get('img'), full: true)), + objectJsonDesiredOutput); + expect(parseObject['img'].objectId, "8nGrLj3Mvk"); + + expect(Uri.decodeComponent(result.path), + '/classes/MyUser/Mn1iJTkWTE?include=img'); + }); + + test('should return expectedIncludeResult json when use getObject', + () async { + // arrange + ParseObject myUserObject = ParseObject("MyUser", client: client); + myUserObject.objectId = "Mn1iJTkWTE"; + + var desiredOutput = { + "results": [ + { + "objectId": "Mn1iJTkWTE", + "phone": "+12025550463", + "createdAt": "2022-09-04T13:35:20.883Z", + "updatedAt": "2022-11-14T10:55:56.202Z", + "img": { + "objectId": "8nGrLj3Mvk", + "size": "67663", + "mime": "image/jpg", + "file": { + "__type": "File", + "name": "dc7320ee9146ee19aed8997722fd4e3c.bin", + "url": + "http://ip:port/api/files/myapp/dc7320ee9146ee19aed8997722fd4e3c.bin", + }, + "createdAt": "2022-11-14T10:55:56.025Z", + "updatedAt": "2022-11-14T10:55:56.025Z", + "__type": "Object", + "className": "MyFile", + }, + } + ] + }; + + when(client.get( + any, + options: anyNamed("options"), + onReceiveProgress: anyNamed("onReceiveProgress"), + )).thenAnswer((_) async => ParseNetworkResponse( + statusCode: 200, data: jsonEncode(desiredOutput))); + + // act + ParseResponse response = + await myUserObject.getObject("Mn1iJTkWTE", include: ["img"]); + + ParseObject parseObject = response.results?.first; + + var objectDesiredOutput = { + "className": "MyFile", + "objectId": "8nGrLj3Mvk", + "createdAt": "2022-11-14T10:55:56.025Z", + "updatedAt": "2022-11-14T10:55:56.025Z", + "size": "67663", + "mime": "image/jpg", + "file": { + "__type": "File", + "name": "dc7320ee9146ee19aed8997722fd4e3c.bin", + "url": + "http://ip:port/api/files/myapp/dc7320ee9146ee19aed8997722fd4e3c.bin", + }, + }; + var objectJsonDesiredOutput = jsonEncode(objectDesiredOutput); + + final Uri result = Uri.parse(verify(client.get( + captureAny, + options: anyNamed("options"), + onReceiveProgress: anyNamed("onReceiveProgress"), + )).captured.single); + + // assert + expect(response.results?.first, isA()); + + expect( + jsonEncode( + parseEncode(parseObject.get('img'), full: true)), + objectJsonDesiredOutput); + expect(parseObject['img'].objectId, "8nGrLj3Mvk"); + + expect(Uri.decodeComponent(result.path), + '/classes/MyUser/Mn1iJTkWTE?include=img'); + }); + }); +} diff --git a/packages/dart/test/parse_query_test.dart b/packages/dart/test/parse_query_test.dart index e93c2a77c..81ca24b37 100644 --- a/packages/dart/test/parse_query_test.dart +++ b/packages/dart/test/parse_query_test.dart @@ -2,18 +2,19 @@ import 'package:mockito/annotations.dart'; import 'package:mockito/mockito.dart'; import 'package:parse_server_sdk/parse_server_sdk.dart'; import 'package:test/test.dart'; - +import 'dart:convert'; import 'parse_query_test.mocks.dart'; @GenerateMocks([ParseClient]) void main() { group('queryBuilder', () { - test('whereRelatedTo', () async { - final MockParseClient client = MockParseClient(); + late MockParseClient client; + setUp(() async { + client = MockParseClient(); await Parse().initialize( 'appId', - 'https://test.parse.com', + 'https://example.com', debug: true, // to prevent automatic detection fileDirectory: 'someDirectory', @@ -24,62 +25,75 @@ void main() { // to prevent automatic detection appVersion: 'someAppVersion', ); + }); + test('whereRelatedTo', () async { + // arrange final QueryBuilder queryBuilder = QueryBuilder(ParseObject('_User', client: client)); queryBuilder.whereRelatedTo('likes', 'Post', '8TOXdXf3tz'); + var desiredOutput = { + "results": [ + { + "objectId": "eT9muOxBTJ", + "username": "test", + "createdAt": "2021-04-23T13:46:06.092Z", + "updatedAt": "2021-04-23T13:46:23.586Z", + "ACL": { + "*": {"read": true}, + "eT9muOxBTJ": {"read": true, "write": true} + }, + } + ] + }; + when(client.get( any, options: anyNamed("options"), onReceiveProgress: anyNamed("onReceiveProgress"), )).thenAnswer((_) async => ParseNetworkResponse( - statusCode: 200, - data: - "{\"results\":[{\"objectId\":\"eT9muOxBTJ\",\"username\":\"test\",\"createdAt\":\"2021-04-23T13:46:06.092Z\",\"updatedAt\":\"2021-04-23T13:46:23.586Z\",\"ACL\":{\"*\":{\"read\":true},\"eT9muOxBTJ\":{\"read\":true,\"write\":true}}}]}")); + statusCode: 200, data: jsonEncode(desiredOutput))); + // act ParseResponse response = await queryBuilder.query(); - expect(response.results?.first, isA()); - ParseObject parseObject = response.results?.first; - expect(parseObject.get(keyVarUsername), "test"); - expect(parseObject.objectId, "eT9muOxBTJ"); - expect(parseObject.createdAt, DateTime.parse("2021-04-23T13:46:06.092Z")); - expect(parseObject.updatedAt, DateTime.parse("2021-04-23T13:46:23.586Z")); - final Uri result = Uri.parse(verify(client.get( captureAny, options: anyNamed("options"), onReceiveProgress: anyNamed("onReceiveProgress"), )).captured.single); + var queryDesiredOutput = { + "\$relatedTo": { + "object": { + "__type": "Pointer", + "className": "Post", + "objectId": "8TOXdXf3tz", + }, + "key": "likes" + }, + }; + final Uri expectedQuery = + Uri(query: 'where=' + jsonEncode(queryDesiredOutput)); + + // assert + expect(response.results?.first, isA()); + + expect(parseObject.get(keyVarUsername), "test"); + expect(parseObject.objectId, "eT9muOxBTJ"); + expect(parseObject.createdAt, DateTime.parse("2021-04-23T13:46:06.092Z")); + expect(parseObject.updatedAt, DateTime.parse("2021-04-23T13:46:23.586Z")); + expect(result.path, '/classes/_User'); - final Uri expectedQuery = Uri( - query: - 'where={"\$relatedTo":{"object":{"__type":"Pointer","className":"Post","objectId":"8TOXdXf3tz"},"key":"likes"}}'); expect(result.query, expectedQuery.query); }); test('QueryBuilder.or', () async { - final MockParseClient client = MockParseClient(); - - await Parse().initialize( - 'appId', - 'https://test.parse.com', - debug: true, - // to prevent automatic detection - fileDirectory: 'someDirectory', - // to prevent automatic detection - appName: 'appName', - // to prevent automatic detection - appPackageName: 'somePackageName', - // to prevent automatic detection - appVersion: 'someAppVersion', - ); - + // arrange ParseObject user = ParseObject("_User", client: client); var firstName = QueryBuilder(user) ..regEx('firstName', "Liam"); @@ -92,57 +106,73 @@ void main() { [firstName, lastName], ); + var desiredOutput = { + "results": [ + { + "className": "_User", + "objectId": "fqx5BECOME", + "createdAt": "2022-10-25T06:04:47.138Z", + "updatedAt": "2022-10-25T06:05:22.328Z", + "firstName": "Liam1", + "lastName": "Johnson1", + }, + { + "className": "_User", + "objectId": "hAtRRYGrUO", + "createdAt": "2022-01-24T15:53:48.396Z", + "updatedAt": "2022-01-25T05:52:01.701Z", + "firstName": "Liam2", + "lastName": "Johnson2", + } + ] + }; + when(client.get( any, options: anyNamed("options"), onReceiveProgress: anyNamed("onReceiveProgress"), )).thenAnswer((_) async => ParseNetworkResponse( - statusCode: 200, - data: - "{\"results\": [{\"className\": \"_User\",\"objectId\": \"fqx5BECOME\",\"createdAt\": \"2022-10-25T06:04:47.138Z\",\"updatedAt\": \"2022-10-25T06:05:22.328Z\",\"firstName\": \"Liam1\",\"lastName\": \"Johnson1\"},{\"className\": \"_User\",\"objectId\": \"hAtRRYGrUO\",\"createdAt\": \"2022-01-24T15:53:48.396Z\",\"updatedAt\": \"2022-01-25T05:52:01.701Z\",\"firstName\": \"Liam2\",\"lastName\": \"Johnson2\"}]}")); + statusCode: 200, data: jsonEncode(desiredOutput))); + // act var response = await mainQuery.query(); - expect(response.results?.first, isA()); - ParseObject parseObject = response.results?.first; - expect(parseObject.get("firstName"), "Liam1"); - expect(parseObject.objectId, "fqx5BECOME"); - expect(parseObject.createdAt, DateTime.parse("2022-10-25T06:04:47.138Z")); - expect(parseObject.updatedAt, DateTime.parse("2022-10-25T06:05:22.328Z")); - final Uri result = Uri.parse(verify(client.get( captureAny, options: anyNamed("options"), onReceiveProgress: anyNamed("onReceiveProgress"), )).captured.single); + var queryDesiredOutput = { + "\$or": [ + { + "firstName": {"\$regex": "Liam"} + }, + { + "lastName": {"\$regex": "Johnson"}, + } + ], + }; + final Uri expectedQuery = + Uri(query: 'where=' + jsonEncode(queryDesiredOutput)); + + // assert + expect(response.results?.first, isA()); + + expect(parseObject.get("firstName"), "Liam1"); + expect(parseObject.objectId, "fqx5BECOME"); + expect(parseObject.createdAt, DateTime.parse("2022-10-25T06:04:47.138Z")); + expect(parseObject.updatedAt, DateTime.parse("2022-10-25T06:05:22.328Z")); + expect(result.path, '/classes/_User'); - final Uri expectedQuery = Uri( - query: - 'where={"\$or":[{"firstName":{"\$regex":"Liam"}},{"lastName":{"\$regex":"Johnson"}}]}'); expect(result.query, expectedQuery.query); }); test('QueryBuilder.and', () async { - final MockParseClient client = MockParseClient(); - - await Parse().initialize( - 'appId', - 'https://test.parse.com', - debug: true, - // to prevent automatic detection - fileDirectory: 'someDirectory', - // to prevent automatic detection - appName: 'appName', - // to prevent automatic detection - appPackageName: 'somePackageName', - // to prevent automatic detection - appVersion: 'someAppVersion', - ); - + // arrange ParseObject user = ParseObject("_User", client: client); var firstName = QueryBuilder(user) ..regEx('firstName', "jak"); @@ -154,57 +184,73 @@ void main() { [firstName, lastName], ); + var desiredOutput = { + "results": [ + { + "className": "_User", + "objectId": "fqx5BECOME", + "createdAt": "2022-10-25T06:04:47.138Z", + "updatedAt": "2022-10-25T06:05:22.328Z", + "firstName": "jak1", + "lastName": "jaki1", + }, + { + "className": "_User", + "objectId": "hAtRRYGrUO", + "createdAt": "2022-01-24T15:53:48.396Z", + "updatedAt": "2022-01-25T05:52:01.701Z", + "firstName": "jak2", + "lastName": "jaki2", + }, + ] + }; + when(client.get( any, options: anyNamed("options"), onReceiveProgress: anyNamed("onReceiveProgress"), )).thenAnswer((_) async => ParseNetworkResponse( - statusCode: 200, - data: - "{\"results\": [{\"className\": \"_User\",\"objectId\": \"fqx5BECOME\",\"createdAt\": \"2022-10-25T06:04:47.138Z\",\"updatedAt\": \"2022-10-25T06:05:22.328Z\",\"firstName\": \"jak1\",\"lastName\": \"jaki1\"},{\"className\": \"_User\",\"objectId\": \"hAtRRYGrUO\",\"createdAt\": \"2022-01-24T15:53:48.396Z\",\"updatedAt\": \"2022-01-25T05:52:01.701Z\",\"firstName\": \"jak2\",\"lastName\": \"jaki2\"}]}")); + statusCode: 200, data: jsonEncode(desiredOutput))); + // act var response = await mainQuery.query(); - expect(response.results?.first, isA()); - ParseObject parseObject = response.results?.first; - expect(parseObject.get("firstName"), "jak1"); - expect(parseObject.objectId, "fqx5BECOME"); - expect(parseObject.createdAt, DateTime.parse("2022-10-25T06:04:47.138Z")); - expect(parseObject.updatedAt, DateTime.parse("2022-10-25T06:05:22.328Z")); - final Uri result = Uri.parse(verify(client.get( captureAny, options: anyNamed("options"), onReceiveProgress: anyNamed("onReceiveProgress"), )).captured.single); + var queryDesiredOutput = { + "\$and": [ + { + "firstName": {"\$regex": "jak"} + }, + { + "lastName": {"\$regex": "jaki"}, + } + ], + }; + final Uri expectedQuery = + Uri(query: 'where=' + jsonEncode(queryDesiredOutput)); + + // assert + expect(response.results?.first, isA()); + + expect(parseObject.get("firstName"), "jak1"); + expect(parseObject.objectId, "fqx5BECOME"); + expect(parseObject.createdAt, DateTime.parse("2022-10-25T06:04:47.138Z")); + expect(parseObject.updatedAt, DateTime.parse("2022-10-25T06:05:22.328Z")); + expect(result.path, '/classes/_User'); - final Uri expectedQuery = Uri( - query: - 'where={"\$and":[{"firstName":{"\$regex":"jak"}},{"lastName":{"\$regex":"jaki"}}]}'); expect(result.query, expectedQuery.query); }); test('QueryBuilder.nor', () async { - final MockParseClient client = MockParseClient(); - - await Parse().initialize( - 'appId', - 'https://test.parse.com', - debug: true, - // to prevent automatic detection - fileDirectory: 'someDirectory', - // to prevent automatic detection - appName: 'appName', - // to prevent automatic detection - appPackageName: 'somePackageName', - // to prevent automatic detection - appVersion: 'someAppVersion', - ); - + // arrange ParseObject user = ParseObject("_User", client: client); var firstName = QueryBuilder(user) ..regEx('firstName', "Oliver"); @@ -217,37 +263,68 @@ void main() { [firstName, lastName], ); + var desiredOutput = { + "results": [ + { + "className": "_User", + "objectId": "fqx5BECOME", + "createdAt": "2022-10-25T06:04:47.138Z", + "updatedAt": "2022-10-25T06:05:22.328Z", + "firstName": "Oliver1", + "lastName": "Smith1", + }, + { + "className": "_User", + "objectId": "hAtRRYGrUO", + "createdAt": "2022-01-24T15:53:48.396Z", + "updatedAt": "2022-01-25T05:52:01.701Z", + "firstName": "Oliver2", + "lastName": "Smith2", + }, + ] + }; + when(client.get( any, options: anyNamed("options"), onReceiveProgress: anyNamed("onReceiveProgress"), )).thenAnswer((_) async => ParseNetworkResponse( - statusCode: 200, - data: - "{\"results\": [{\"className\": \"_User\",\"objectId\": \"fqx5BECOME\",\"createdAt\": \"2022-10-25T06:04:47.138Z\",\"updatedAt\": \"2022-10-25T06:05:22.328Z\",\"firstName\": \"Oliver1\",\"lastName\": \"Smith1\"},{\"className\": \"_User\",\"objectId\": \"hAtRRYGrUO\",\"createdAt\": \"2022-01-24T15:53:48.396Z\",\"updatedAt\": \"2022-01-25T05:52:01.701Z\",\"firstName\": \"Oliver2\",\"lastName\": \"Smith2\"}]}")); + statusCode: 200, data: jsonEncode(desiredOutput))); + // act var response = await mainQuery.query(); - expect(response.results?.first, isA()); - ParseObject parseObject = response.results?.first; - expect(parseObject.get("firstName"), "Oliver1"); - expect(parseObject.objectId, "fqx5BECOME"); - expect(parseObject.createdAt, DateTime.parse("2022-10-25T06:04:47.138Z")); - expect(parseObject.updatedAt, DateTime.parse("2022-10-25T06:05:22.328Z")); - final Uri result = Uri.parse(verify(client.get( captureAny, options: anyNamed("options"), onReceiveProgress: anyNamed("onReceiveProgress"), )).captured.single); + var queryDesiredOutput = { + "\$nor": [ + { + "firstName": {"\$regex": "Oliver"} + }, + { + "lastName": {"\$regex": "Smith"}, + } + ], + }; + final Uri expectedQuery = + Uri(query: 'where=' + jsonEncode(queryDesiredOutput)); + + // assert + expect(response.results?.first, isA()); + + expect(parseObject.get("firstName"), "Oliver1"); + expect(parseObject.objectId, "fqx5BECOME"); + expect(parseObject.createdAt, DateTime.parse("2022-10-25T06:04:47.138Z")); + expect(parseObject.updatedAt, DateTime.parse("2022-10-25T06:05:22.328Z")); + expect(result.path, '/classes/_User'); - final Uri expectedQuery = Uri( - query: - 'where={"\$nor":[{"firstName":{"\$regex":"Oliver"}},{"lastName":{"\$regex":"Smith"}}]}'); expect(result.query, expectedQuery.query); }); }); diff --git a/packages/flutter/example/test/data/repository/diet_plan/repository_diet_plan_api_test.dart b/packages/flutter/example/test/data/repository/diet_plan/repository_diet_plan_api_test.dart index dcd1a9d68..442e2a59c 100644 --- a/packages/flutter/example/test/data/repository/diet_plan/repository_diet_plan_api_test.dart +++ b/packages/flutter/example/test/data/repository/diet_plan/repository_diet_plan_api_test.dart @@ -7,7 +7,6 @@ import 'package:shared_preferences/shared_preferences.dart'; import '../repository_mock_utils.dart'; -// ignore_for_file: invalid_use_of_protected_member void main() { DietPlanProviderContract repository; SharedPreferences.setMockInitialValues({}); @@ -32,23 +31,22 @@ void main() { }); test('add DietPlan from API', () async { - // Given + // arrange final DietPlan expected = getDummyDietPlan(); expected['objectId'] = null; - // When + // act final ApiResponse response = await repository.add(expected); final DietPlan actual = response.result; - // CLEAR FROM DB await deleteFromApi(response.results); - // Then + // assert expect(actual.protein, expected.protein); }); test('addAll DietPlan from API', () async { - // Given + // arrange final List actual = []; final DietPlan item1 = getDummyDietPlan(); item1['objectId'] = null; @@ -59,57 +57,54 @@ void main() { item2.protein = 6; actual.add(item2); - // When + // act final ApiResponse response = await repository.addAll(actual); final List items = response.results; - // CLEAR FROM DB await deleteFromApi(response.results); - // Then + // assert expect(response.success, true); expect(actual[1].objectId, items[1].objectId); }); test('getById DietPlan from API', () async { - // Given + // arrange final DietPlan dummy = getDummyDietPlan(); dummy['objectId'] = null; - // When + // act final ApiResponse response = await repository.add(dummy); final DietPlan expected = response.result; final ApiResponse updateResponse = await repository.getById(expected.objectId); final DietPlan actual = updateResponse.result; - // CLEAR FROM DB await deleteFromApi(response.results); await deleteFromApi(updateResponse.results); - // Then + // assert expect(actual.objectId, expected.objectId); expect(actual.protein, expected.protein); }); test('getNewerThan DietPlan from API', () async { - // Given + // arrange final DietPlan dummy = getDummyDietPlan(); dummy['objectId'] = null; - // When + // act final ApiResponse baseResponse = await repository.add(dummy); final ApiResponse responseWithResult = await repository .getNewerThan(DateTime.now().subtract(const Duration(days: 1))); final ApiResponse responseWithoutResult = await repository .getNewerThan(DateTime.now().add(const Duration(days: 1))); - // CLEAR FROM DB await deleteFromApi(baseResponse.results); await deleteFromApi(responseWithoutResult.results); await deleteFromApi(responseWithResult.results); - // Then + // assert expect(responseWithResult.success, true); expect(responseWithoutResult.success, true); expect(responseWithResult.result, isNotNull); @@ -117,6 +112,7 @@ void main() { }); test('getAll DietPlan from API', () async { + // arrange final List actual = []; final DietPlan item1 = getDummyDietPlan(); @@ -128,40 +124,38 @@ void main() { item2.protein = 6; actual.add(item2); - // When + // act final ApiResponse response = await repository.addAll(actual); - // CLEAR FROM DB await deleteFromApi(response.results); - // Then + // assert expect(response.success, true); expect(response.result, isNotNull); }); test('update DietPlan from API', () async { - // Given + // arrange final DietPlan expected = getDummyDietPlan(); expected['objectId'] = null; final ApiResponse response = await repository.add(expected); final DietPlan initialResponse = response.result; - // When + // act initialResponse.protein = 10; final ApiResponse updateResponse = await repository.update(initialResponse); final DietPlan actual = updateResponse.result; - // CLEAR FROM DB await deleteFromApi(response.results); await deleteFromApi(updateResponse.results); - // Then + // assert expect(actual.protein, 10); }); test('updateAll DietPlan from API', () async { - // Given + // arrange final List actual = []; final DietPlan item1 = getDummyDietPlan(); @@ -174,16 +168,15 @@ void main() { actual.add(item2); await repository.addAll(actual); - // When + // act item1.protein = 9; item2.protein = 10; final ApiResponse updateResponse = await repository.updateAll(actual); final List updated = updateResponse.results; - // CLEAR FROM DB await deleteFromApi(updateResponse.results); - // Then + // assert expect(updated[0].protein, 9); expect(updated[1].protein, 10); }); diff --git a/packages/flutter/example/test/data/repository/diet_plan/repository_diet_plan_db_test.dart b/packages/flutter/example/test/data/repository/diet_plan/repository_diet_plan_db_test.dart index 9279f8e66..059c8e856 100644 --- a/packages/flutter/example/test/data/repository/diet_plan/repository_diet_plan_db_test.dart +++ b/packages/flutter/example/test/data/repository/diet_plan/repository_diet_plan_db_test.dart @@ -43,23 +43,22 @@ void main() { }); test('add DietPlan from DB', () async { - // Given + // arrange final DietPlan expected = getDummyDietPlan(); - // When + // act final ApiResponse response = await repository.add(expected); final DietPlan actual = response.result; - // CLEAR FROM DB await deleteFromApi(response.results); - // Then + // assert expect(actual.objectId, expected.objectId); expect(actual.protein, expected.protein); }); test('addAll DietPlan from DB', () async { - // Given + // arrange const String objectIdPrefix = '12345abc'; final List actual = []; @@ -71,101 +70,97 @@ void main() { item2.objectId = '${objectIdPrefix}1'; actual.add(item2); - // When + // act final ApiResponse response = await repository.addAll(actual); final List items = response.results; - // Then + // assert expect(response.success, true); expect(actual[0].objectId, items[0].objectId); expect(actual[1].objectId, items[1].objectId); }); test('getById DietPlan from DB', () async { - // Given + // arrange final DietPlan actual = getDummyDietPlan(); - // When + // act final ApiResponse response = await repository.add(actual); final ApiResponse updateResponse = await repository.getById('1234abcd'); - // CLEAR FROM DB await deleteFromApi(response.results); await deleteFromApi(updateResponse.results); - // Then + // assert final DietPlan expected = response.result; expect(actual.objectId, expected.objectId); expect(actual.protein, expected.protein); }); test('getAll DietPlan from DB', () async { - // Given + // arrange const String objectIdPrefix = '12345abc'; final DietPlan item1 = getDummyDietPlan()..objectId = '${objectIdPrefix}0'; final DietPlan item2 = getDummyDietPlan()..objectId = '${objectIdPrefix}1'; final List actual = [item1, item2]; - // When + // act final ApiResponse response = await repository.addAll(actual); - // Then final ApiResponse updateResponse = await repository.getAll(); final List expected = updateResponse.results; - // CLEAR FROM DB await deleteFromApi(response.results); await deleteFromApi(updateResponse.results); + // assert expect(2, expected.length); expect(actual[0].objectId, expected[0].objectId); expect(actual[1].objectId, expected[1].objectId); }); test('getNewerThan DietPlan from DB', () async { - // Given + // arrange final DietPlan expected = getDummyDietPlan(); - // ignore: invalid_use_of_protected_member + expected[keyVarUpdatedAt] = DateTime.now(); final ApiResponse response = await repository.add(expected); - // When + // act DateTime dateTime = DateTime.now(); dateTime = dateTime.subtract(const Duration(hours: 1)); final ApiResponse updateResponse = await repository.getNewerThan(dateTime); final List actual = updateResponse.results; - // CLEAR FROM DB await deleteFromApi(response.results); await deleteFromApi(updateResponse.results); - // Then + // assert expect(actual.isNotEmpty, true); expect(actual.first.objectId, expected.objectId); }); test('update DietPlan from DB', () async { - // Given + // arrange final DietPlan item = getDummyDietPlan(); item.protein = 1000; final ApiResponse apiResponse = await repository.add(item); - // When + // act item.protein = 1000; final ApiResponse updateResponse = await repository.update(item); final DietPlan userFood = updateResponse.result; - // CLEAR FROM DB await deleteFromApi(apiResponse.results); await deleteFromApi(updateResponse.results); - // Then + // assert expect(item.objectId, userFood.objectId); expect(userFood.protein, 1000); }); test('updateAll DietPlan from DB', () async { - // Given + // arrange const String objectIdPrefix = '12345abc'; final List actual = []; @@ -179,22 +174,19 @@ void main() { final ApiResponse apiResponse = await repository.addAll(actual); - // CLEAR FROM DB await deleteFromApi(apiResponse.results); - // When + // act actual[0].protein = 1000; actual[1].protein = 1000; final ApiResponse updateResponse = await repository.updateAll(actual); final List expected = updateResponse.results; - // CLEAR FROM DB await deleteFromApi(updateResponse.results); - // CLEAR FROM DB await deleteFromApi(updateResponse.results); await deleteFromApi(apiResponse.results); - // Then + // assert expect(actual[0].objectId, expected[0].objectId); expect(actual[1].objectId, expected[1].objectId); expect(expected[0].protein, 1000); @@ -202,15 +194,15 @@ void main() { }); test('delete DietPlan from DB', () async { - // Given + // arrange final DietPlan actual = getDummyDietPlan(); repository.add(actual); - // When + // act repository.remove(actual); final ApiResponse response = await repository.getById(actual.objectId); - // Then + // assert expect(response.result == null, true); }); } diff --git a/packages/flutter/example/test/data/repository/diet_plan/repository_diet_plan_test.dart b/packages/flutter/example/test/data/repository/diet_plan/repository_diet_plan_test.dart index 918f52eb3..1f11dc6e8 100644 --- a/packages/flutter/example/test/data/repository/diet_plan/repository_diet_plan_test.dart +++ b/packages/flutter/example/test/data/repository/diet_plan/repository_diet_plan_test.dart @@ -68,7 +68,7 @@ void main() { }); test('add DietPlan from DB', () async { - // Given && When + // arrange & act when(() { if (any) { repository.add(any); @@ -79,7 +79,7 @@ void main() { }); test('addAll DietPlan from DB', () async { - // Given && When + // arrange & act when(() { if (any) { repository.addAll(any); @@ -90,34 +90,34 @@ void main() { }); test('getAll DietPlan from DB', () async { - // Given && When + // arrange & act await repository.getAll(); - // Then + // assert verify(dbRepository.getAll()).called(1); verifyNever(apiRepository.getAll()); }); test('getAll DietPlan from API', () async { - // Given && When + // arrange & act await repository.getAll(fromApi: true); - // Then + // assert verifyNever(dbRepository.getAll()); verify(apiRepository.getAll()).called(1); }); test('getNewerThan DietPlan from DB', () async { - // Given && When + // arrange & act await repository.getNewerThan(DateTime.now()); - // Then + // assert verifyNever(dbRepository.getNewerThan(DateTime.now())); verify(apiRepository.getNewerThan(any)); }); test('updateAll DietPlan from DB', () async { - // Given && When + // arrange & act when(() { if (any) { repository.updateAll(any); @@ -128,7 +128,7 @@ void main() { }); test('delete DietPlan from DB', () async { - // Given && When + // arrange & act when(() { if (any) { repository.remove(any); diff --git a/packages/flutter/test/parse_client_configuration_test.dart b/packages/flutter/test/parse_client_configuration_test.dart index 6d4f38b95..9d4a50880 100644 --- a/packages/flutter/test/parse_client_configuration_test.dart +++ b/packages/flutter/test/parse_client_configuration_test.dart @@ -6,6 +6,7 @@ void main() { SharedPreferences.setMockInitialValues({}); test('testBuilder', () async { + // arrange await Parse().initialize('appId', 'serverUrl', clientKey: 'clientKey', liveQueryUrl: 'liveQueryUrl', @@ -17,6 +18,7 @@ void main() { fileDirectory: 'someDirectory', debug: true); + // assert expect(ParseCoreData().applicationId, 'appId'); expect(ParseCoreData().serverUrl, 'serverUrl'); expect(ParseCoreData().clientKey, 'clientKey');