From 89c58f67aa6b7daaf40bfffc38df9a50820b9e5c Mon Sep 17 00:00:00 2001 From: Manuel Trezza <5673677+mtrezza@users.noreply.github.com> Date: Fri, 28 Nov 2025 15:59:04 +0100 Subject: [PATCH 01/14] feat --- .../ISSUE_TEMPLATE/---3-version-conflict.md | 77 ++++++ .github/dependabot.yml | 34 +++ .github/workflows/ci.yml | 10 + .github/workflows/dependency-audit.yml | 134 +++++++++ CONTRIBUTING.md | 14 + MIGRATION_GUIDES.md | 254 +++++++++++++++++ packages/dart/README.md | 21 +- packages/flutter/README.md | 24 +- scripts/check-version-support.js | 256 ++++++++++++++++++ 9 files changed, 822 insertions(+), 2 deletions(-) create mode 100644 .github/ISSUE_TEMPLATE/---3-version-conflict.md create mode 100644 .github/dependabot.yml create mode 100644 .github/workflows/dependency-audit.yml create mode 100644 MIGRATION_GUIDES.md create mode 100644 scripts/check-version-support.js diff --git a/.github/ISSUE_TEMPLATE/---3-version-conflict.md b/.github/ISSUE_TEMPLATE/---3-version-conflict.md new file mode 100644 index 000000000..43aeaaee7 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/---3-version-conflict.md @@ -0,0 +1,77 @@ +--- +name: "\U0001F4E6 Version Conflict" +about: Report a version conflict with Parse SDK dependencies +title: '[Version Conflict] ' +labels: 'dependencies, version-conflict' +assignees: '' +--- + +## Version Conflict Description + + + +## Environment + +**Parse SDK Version:** +- Dart SDK: [e.g., 8.0.2] +- Flutter SDK (if applicable): [e.g., 9.0.0] + +**Framework Version:** +- Dart: [e.g., 3.2.6] +- Flutter (if applicable): [e.g., 3.16.9] + +**Platform:** +- [ ] Dart +- [ ] Flutter (Web) +- [ ] Flutter (Mobile - iOS) +- [ ] Flutter (Mobile - Android) +- [ ] Flutter (Desktop - macOS) +- [ ] Flutter (Desktop - Windows) +- [ ] Flutter (Desktop - Linux) + +## Conflict Details + +**Conflicting Package:** +[e.g., dio, http, sembast] + +**Required Version:** +[e.g., Package X requires dio ^6.0.0 but Parse SDK requires ^5.0.0] + +**Error Message:** +``` +Paste the full error message from `dart pub get` or `flutter pub get` +``` + +## Your pubspec.yaml + +```yaml +# Paste relevant sections of your pubspec.yaml +dependencies: + parse_server_sdk: ^8.0.0 + # ... other dependencies +``` + +## Dependency Tree + +```bash +# Run: dart pub deps or flutter pub deps +# Paste the output here +``` + +## Steps Tried + + + +- [ ] Updated to latest Parse SDK version +- [ ] Ran `dart pub outdated` / `flutter pub outdated` +- [ ] Checked [MIGRATION_GUIDES.md](https://github.com/parse-community/Parse-SDK-Flutter/blob/master/MIGRATION_GUIDES.md) +- [ ] Tried `dependency_overrides` (temporary workaround) +- [ ] Searched existing issues + +## Workaround + + + +## Additional Context + + diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 000000000..77d1fc0c6 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,34 @@ +version: 2 +updates: + # Dart package dependencies + - package-ecosystem: "pub" + directory: "/packages/dart" + schedule: + interval: "daily" + open-pull-requests-limit: 10 + labels: + - "dart" + commit-message: + prefix: "feat" + + # Flutter package dependencies + - package-ecosystem: "pub" + directory: "/packages/flutter" + schedule: + interval: "daily" + open-pull-requests-limit: 10 + labels: + - "flutter" + commit-message: + prefix: "feat" + + # GitHub Actions + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" + open-pull-requests-limit: 5 + labels: + - "ci" + commit-message: + prefix: "refactor" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 965b31ec7..850fcd67b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -14,6 +14,9 @@ jobs: strategy: matrix: include: + # Version Support Policy: 6 months after next significant version release + # See VERSIONING_POLICY.md for full details + # # Dart framework may contain breaking changes in minor version releases, not following semver. # The latest Dart framework (below) is tested on all architectures (Ubuntu, macOS, Windows). - name: Dart 3.5, Ubuntu @@ -26,6 +29,7 @@ jobs: os: windows-latest sdk: 3.5.3 # Older Dart framework versions (below) are only tested with Ubuntu to reduce CI resource usage. + # These versions should be removed when they reach end-of-support (6 months after next version). - name: Dart 3.4 os: ubuntu-latest sdk: 3.4.4 @@ -35,6 +39,7 @@ jobs: - name: Dart 3.2 os: ubuntu-latest sdk: 3.2.6 + # Beta channel helps identify breaking changes early - name: Dart beta os: ubuntu-latest sdk: beta @@ -79,6 +84,9 @@ jobs: strategy: matrix: include: + # Version Support Policy: 6 months after next significant version release + # See VERSIONING_POLICY.md for full details + # # Flutter framework may contain breaking changes in minor version releases, not following semver. # The latest Flutter framework (below) is tested on all architectures (Ubuntu, macOS, Windows). - name: Flutter 3.24, Ubuntu @@ -91,6 +99,7 @@ jobs: os: windows-latest sdk: 3.24.3 # Older Flutter framework versions (below) are only tested with Ubuntu to reduce CI resource usage. + # These versions should be removed when they reach end-of-support (6 months after next version). - name: Flutter 3.22 os: ubuntu-latest sdk: 3.22.3 @@ -100,6 +109,7 @@ jobs: - name: Flutter 3.16 os: ubuntu-latest sdk: 3.16.9 + # Beta channel helps identify breaking changes early - name: Flutter beta os: ubuntu-latest sdk: beta diff --git a/.github/workflows/dependency-audit.yml b/.github/workflows/dependency-audit.yml new file mode 100644 index 000000000..47599053a --- /dev/null +++ b/.github/workflows/dependency-audit.yml @@ -0,0 +1,134 @@ +name: dependency-audit + +on: + schedule: + # Run on the first day of every month at 9:00 AM UTC + - cron: '0 9 1 * *' + workflow_dispatch: # Allow manual triggering + +permissions: + contents: read + issues: write + +jobs: + audit-dart: + name: Audit Dart Package Dependencies + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Setup Dart + uses: dart-lang/setup-dart@v1 + with: + sdk: stable + + - name: Get dependencies + working-directory: packages/dart + run: dart pub get + + - name: Check for outdated dependencies + id: outdated + working-directory: packages/dart + run: | + echo "## Dart Package - Outdated Dependencies" >> $GITHUB_STEP_SUMMARY + dart pub outdated --mode=outdated || true + dart pub outdated --mode=outdated >> $GITHUB_STEP_SUMMARY || true + + - name: Security audit + id: audit + working-directory: packages/dart + run: | + echo "## Dart Package - Security Audit" >> $GITHUB_STEP_SUMMARY + dart pub audit || true + dart pub audit >> $GITHUB_STEP_SUMMARY || true + + audit-flutter: + name: Audit Flutter Package Dependencies + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Setup Flutter + uses: subosito/flutter-action@v2 + with: + channel: stable + + - name: Get dependencies + working-directory: packages/flutter + run: flutter pub get + + - name: Check for outdated dependencies + id: outdated + working-directory: packages/flutter + run: | + echo "## Flutter Package - Outdated Dependencies" >> $GITHUB_STEP_SUMMARY + flutter pub outdated --mode=outdated || true + flutter pub outdated --mode=outdated >> $GITHUB_STEP_SUMMARY || true + + - name: Security audit + id: audit + working-directory: packages/flutter + run: | + echo "## Flutter Package - Security Audit" >> $GITHUB_STEP_SUMMARY + dart pub audit || true + dart pub audit >> $GITHUB_STEP_SUMMARY || true + + create-issue: + name: Create Issue if Security Vulnerabilities Found + needs: [audit-dart, audit-flutter] + runs-on: ubuntu-latest + if: failure() + steps: + - name: Create issue for security vulnerabilities + uses: actions/github-script@v7 + with: + script: | + const title = '[Security] Dependency vulnerabilities detected'; + const body = `## Security Vulnerabilities Detected + + The monthly dependency audit has detected security vulnerabilities in our dependencies. + + ### Action Required + 1. Review the [workflow run](${context.payload.repository.html_url}/actions/runs/${context.runId}) + 2. Update affected dependencies + 3. Test thoroughly + 4. Create a PR with security fixes + + ### Resources + - [VERSIONING_POLICY.md](${context.payload.repository.html_url}/blob/master/VERSIONING_POLICY.md) + - [Dart Security Best Practices](https://dart.dev/guides/libraries/secure) + + --- + **Auto-generated by dependency-audit workflow** + `; + + // Check if similar issue exists + const issues = await github.rest.issues.listForRepo({ + owner: context.repo.owner, + repo: context.repo.repo, + state: 'open', + labels: 'security,dependencies' + }); + + const existingIssue = issues.data.find(issue => + issue.title.includes('[Security] Dependency vulnerabilities') + ); + + if (!existingIssue) { + await github.rest.issues.create({ + owner: context.repo.owner, + repo: context.repo.repo, + title: title, + body: body, + labels: ['security', 'dependencies', 'high-priority'] + }); + } else { + await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: existingIssue.number, + body: `New vulnerabilities detected in [workflow run](${context.payload.repository.html_url}/actions/runs/${context.runId})` + }); + } diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 5f35995ba..d5f929557 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -10,6 +10,20 @@ We actively welcome your pull requests. When we get one, we'll run some Parse-sp 2. Add unit tests for any new code you add. 3. If you've changed APIs, update the documentation. 4. Ensure the test suite passes. +5. Follow the commit message format for automated versioning (see below). +6. For dependency updates, see the [Dependency Management](#dependency-management) section. + +## Dependency Management + +The Parse SDK follows a structured approach to dependency management. See [VERSIONING_POLICY.md](VERSIONING_POLICY.md) for full details. + +## Framework Support Policy + +The Parse SDK supports Dart and Flutter versions for 6 months after the next significant version release. When contributing: + +- Ensure compatibility with all supported versions (see README compatibility tables) +- Don't use features only available in the latest version without version checks +- CI will test your PR against all supported versions ## Code of Conduct diff --git a/MIGRATION_GUIDES.md b/MIGRATION_GUIDES.md new file mode 100644 index 000000000..f2a52fcb2 --- /dev/null +++ b/MIGRATION_GUIDES.md @@ -0,0 +1,254 @@ +# Migration Guides + +This document provides guidance for handling version conflicts, upgrading between Parse SDK versions, and resolving common dependency issues. + +## Table of Contents + +- [Handling Version Conflicts](#handling-version-conflicts) +- [Common Scenarios](#common-scenarios) + - [CI/CD Pipeline Fails After Parse SDK Update](#cicd-pipeline-fails-after-parse-sdk-update) + - [Using Flutter Web and Version Conflicts](#using-flutter-web-and-version-conflicts) + - [Support Older Devices](#support-older-devices) +- [Additional Resources](#additional-resources) + +## Handling Version Conflicts + +Version conflicts occur when multiple packages in your project have incompatible requirements. Here's how to resolve them. + +### Scenario 1: Parse SDK Blocks Newer Package + +**Problem:** +``` +Because your_app depends on parse_server_sdk ^8.0.0 which depends on dio ^5.0.0, + and your_app depends on another_package ^2.0.0 which depends on dio ^6.0.0, + version solving failed. +``` + +**Solutions (in order of preference):** + +#### 1. Update Parse SDK (Recommended) +Check if a newer version of Parse SDK supports the required dependency version: + +```bash +# Check for Parse SDK updates +cd packages/dart +dart pub outdated + +# Or for Flutter package +cd packages/flutter +flutter pub outdated +``` + +Update your `pubspec.yaml`: +```yaml +dependencies: + parse_server_sdk: ^9.0.0 # Newer version may support dio ^6.0.0 +``` + +#### 2. Request Parse SDK Upgrade +If Parse SDK doesn't support the required version, create a GitHub issue: + +1. Go to https://github.com/parse-community/Parse-SDK-Flutter/issues +2. Title: "Support for [dependency] ^X.0.0" +3. Include: + - Current Parse SDK version you're using + - Dependency version you need + - Why you need the newer version + - Any relevant error messages + +#### 3. Use dependency_overrides (Temporary) +**Warning:** Only for development/testing, not for production! + +```yaml +dependency_overrides: + dio: ^6.0.0 # Override to resolve conflict temporarily +``` + +**Risks:** +- May cause runtime errors if Parse SDK uses deprecated APIs +- Not guaranteed to work +- Should never be published to pub.dev +- Must test thoroughly + +#### 4. Fork Parse SDK (Last Resort) +See [When to Fork](#when-to-fork) section below. + +### Scenario 2: Parse SDK Requires Newer Dart/Flutter + +**Problem:** +``` +Because parse_server_sdk >=8.0.0 requires SDK version >=3.2.6 <4.0.0, + and your project has SDK version 2.19.0, version solving failed. +``` + +**Solutions (in order of preference):** + +#### 1. Upgrade Dart/Flutter (Recommended) + +**For Dart projects:** +```bash +# Check current Dart version +dart --version + +# Upgrade Dart (via brew on macOS) +brew upgrade dart + +# Or download from https://dart.dev/get-dart +``` + +**For Flutter projects:** +```bash +# Check current Flutter version +flutter --version + +# Upgrade Flutter +flutter upgrade + +# Or upgrade to specific channel +flutter channel stable +flutter upgrade +``` + +**Migration guides:** +- Dart migration: https://dart.dev/guides/language/evolution +- Flutter migration: https://docs.flutter.dev/release/breaking-changes + +#### 2. Use Older Parse SDK Version + +Check compatibility table in the Dart or Flutter package README and use an older Parse SDK version: + +```yaml +dependencies: + parse_server_sdk: ^7.0.0 # Supports older Dart versions +``` + +**Note:** Older versions: +- May lack newer features +- May have unfixed bugs +- May have security vulnerabilities +- Consider upgrading your Dart/Flutter instead + +#### 3. Review Breaking Changes + +When upgrading Dart/Flutter, review breaking changes: + +**Dart 3.x breaking changes:** +- Null safety enforced (no longer opt-in) +- Some deprecated APIs removed +- Enhanced enums with members +- Records and patterns introduced + +**Flutter 3.x breaking changes:** +- Material 3 as default +- Deprecated APIs removed +- iOS minimum version increased +- Android minimum SDK version updated + +**Resources:** +- [Dart changelog](https://github.com/dart-lang/sdk/blob/main/CHANGELOG.md) +- [Flutter release notes](https://docs.flutter.dev/release/release-notes) + +### Scenario 3: Multiple Packages Have Conflicts + +**Problem:** +``` +Because package_a depends on http ^0.13.0 and package_b depends on http ^1.0.0, + and both are required by your app, version solving failed. +``` + +**Solutions:** + +#### 1. Update All Packages +```bash +# Update all dependencies to latest compatible versions +dart pub upgrade + +# Or for Flutter +flutter pub upgrade +``` + +#### 2. Check for Parse SDK Update +The Parse SDK update might have newer dependency constraints that resolve the conflict. + +#### 3. Report Issue with Full Context +Create a GitHub issue with your complete dependency tree: + +```bash +# Generate dependency tree +dart pub deps + +# Or for Flutter +flutter pub deps +``` + +Include the output in your GitHub issue. + +## Common Scenarios + +#### I Need Dart 3+ Features But Parse SDK Doesn't Support Them Yet + +**Solution:** +1. Check if newest Parse SDK supports Dart 3+ +2. If not, check GitHub issues for timeline +3. Consider contributing the update yourself +4. Temporarily use Dart 2.x compatible patterns + +### CI/CD Pipeline Fails After Parse SDK Update + +**Troubleshooting:** + +1. **Check Dart/Flutter version in CI:** + ```yaml + # Example for GitHub Actions + - uses: dart-lang/setup-dart@v1 + with: + sdk: 3.2.6 # Ensure matches Parse SDK requirements + ``` + +2. **Update cache keys:** + ```yaml + - uses: actions/cache@v3 + with: + path: ~/.pub-cache + key: ${{ runner.os }}-pub-${{ hashFiles('**/pubspec.lock') }} + ``` + +3. **Clear cached dependencies:** + ```bash + dart pub cache repair + ``` + +### Using Flutter Web and Version Conflicts + +**Common issue:** `sembast_web` version conflicts + +**Solution:** +```yaml +dependencies: + parse_server_sdk_flutter: ^9.0.0 + sembast_web: ^2.2.0 # Match version used by Parse SDK + +# Check Parse SDK's pubspec.yaml for exact versions +``` + +### Support Older Devices + +If newer Parse SDK requires newer Flutter/Dart that doesn't support your target devices: + +1. **Check minimum requirements:** + - iOS: Check Flutter's minimum iOS version + - Android: Check Flutter's minimum SDK version + - Web: Check browser compatibility + +2. **Options:** + - Upgrade device requirements + - Use older Parse SDK version + - Fork and maintain custom version + +## Additional Resources + +- [VERSIONING_POLICY.md](VERSIONING_POLICY.md) - Full versioning policy +- [Dart Packages](https://dart.dev/guides/packages) - Official Dart package guide +- [Using Packages](https://docs.flutter.dev/packages-and-plugins/using-packages) - Flutter package guide +- [Pub Versioning](https://dart.dev/tools/pub/versioning) - How Dart versions work +- [Semantic Versioning](https://semver.org) - Version numbering explained diff --git a/packages/dart/README.md b/packages/dart/README.md index 001c7274c..03f00d787 100644 --- a/packages/dart/README.md +++ b/packages/dart/README.md @@ -20,6 +20,8 @@ This library gives you access to the powerful Parse Server backend from your Dar --- - [Compatibility](#compatibility) + - [Currently Supported Versions](#currently-supported-versions) + - [Handling Version Conflicts](#handling-version-conflicts) - [Getting Started](#getting-started) - [Documentation](#documentation) - [Contributing](#contributing) @@ -28,7 +30,12 @@ This library gives you access to the powerful Parse Server backend from your Dar ## Compatibility -The Parse Dart SDK is continuously tested with the most recent release of the Dart framework to ensure compatibility. To give developers time to upgrade their app to a newer Dart framework, previous Dart framework releases are supported for at least 1 year after the [release date](https://dart.dev/get-dart/archive) of the next higher significant version. +The Parse Dart SDK is continuously tested with the most recent release of the Dart framework to ensure compatibility. Previous Dart framework releases are supported for **6 months** after the [release date](https://dart.dev/get-dart/archive) of the next higher significant version (major or minor). + +> [!Note] +> Support windows are calculated from official Dart release dates. When a version's support period expires, it will be dropped in the next Parse SDK major release without advance notice. For full details, see [VERSIONING_POLICY.md](../../VERSIONING_POLICY.md). + +### Currently Supported Versions | Version | Latest Version | End of Support | Compatible | |-----------|----------------|----------------|------------| @@ -37,6 +44,18 @@ The Parse Dart SDK is continuously tested with the most recent release of the Da | Dart 3.4 | 3.4.4 | Jun 2025 | ✅ Yes | | Dart 3.5 | 3.5.3 | Sep 2025 | ✅ Yes | +### Handling Version Conflicts + +If you encounter version conflicts with the Parse SDK: + +1. Check if a newer Parse Dart SDK version resolves your conflict. +2. Review your dependencies by running `dart pub outdated` to see available updates. +3. Check [Parse Dart SDK compatibility](../dart/README.md#compatibility). +4. Check [Migration Guides](../../MIGRATION_GUIDES.md) for common scenarios. +5. [Create an issue](https://github.com/parse-community/Parse-SDK-Flutter/issues) with your full dependency tree. + +For detailed troubleshooting, see our [Version Conflict Guide](../../MIGRATION_GUIDES.md#handling-version-conflicts). + ## Getting Started To install, add the Parse Dart SDK as a [dependency](https://pub.dev/packages/parse_server_sdk/install) in your `pubspec.yaml` file. diff --git a/packages/flutter/README.md b/packages/flutter/README.md index 179e046a5..fec525170 100644 --- a/packages/flutter/README.md +++ b/packages/flutter/README.md @@ -20,6 +20,8 @@ This library gives you access to the powerful Parse Server backend from your Flu --- - [Compatibility](#compatibility) + - [Currently Supported Versions](#currently-supported-versions) + - [Handling Version Conflicts](#handling-version-conflicts) - [Getting Started](#getting-started) - [Documentation](#documentation) - [Contributing](#contributing) @@ -28,7 +30,15 @@ This library gives you access to the powerful Parse Server backend from your Flu ## Compatibility -The Parse Flutter SDK is continuously tested with the most recent release of the Flutter framework to ensure compatibility. To give developers time to upgrade their app to a newer Flutter framework, previous Flutter framework releases are supported for at least 1 year after the [release date](https://docs.flutter.dev/release/archive?tab=linux) of the next higher significant version. The Parse Flutter SDK depends on the Parse Dart SDK which may require a higher Dart framework version than the Flutter framework version, in which case the specific Flutter framework version cannot be supported. +The Parse Flutter SDK is continuously tested with the most recent release of the Flutter framework to ensure compatibility. Previous Flutter framework releases are supported for **6 months** after the [release date](https://docs.flutter.dev/release/archive?tab=linux) of the next higher significant version (major or minor). + +> [!Important] +> The Parse Flutter SDK depends on the Parse Dart SDK which may require a higher Dart framework version than the Flutter framework version, in which case the specific Flutter framework version cannot be supported. Check both SDK compatibility tables. + +> [!Note] +> Support windows are calculated from official Flutter release dates. When a version's support period expires, it will be dropped in the next Parse SDK major release without advance notice. For full details, see [VERSIONING_POLICY.md](../../VERSIONING_POLICY.md). + +### Currently Supported Versions | Version | Latest Version | End of Support | Compatible | |--------------|----------------|----------------|------------| @@ -37,6 +47,18 @@ The Parse Flutter SDK is continuously tested with the most recent release of the | Flutter 3.22 | 3.22.3 | Jul 2025 | ✅ Yes | | Flutter 3.24 | 3.24.3 | Sep 2025 | ✅ Yes | +### Handling Version Conflicts + +If you encounter version conflicts with the Parse SDK: + +1. Check if a newer Parse Flutter SDK version resolves your conflict. +2. Review your dependencies by running `flutter pub outdated` to see available updates. +3. Check [Parse Dart SDK compatibility](../dart/README.md#compatibility). +4. Check [Migration Guides](../../MIGRATION_GUIDES.md) for common scenarios. +5. [Create an issue](https://github.com/parse-community/Parse-SDK-Flutter/issues) with your full dependency tree. + +For detailed troubleshooting, see our [Version Conflict Guide](../../MIGRATION_GUIDES.md#handling-version-conflicts). + ## Getting Started To install, add the Parse Flutter SDK as a [dependency](https://pub.dev/packages/parse_server_sdk_flutter/install) in your `pubspec.yaml` file. diff --git a/scripts/check-version-support.js b/scripts/check-version-support.js new file mode 100644 index 000000000..e893ae887 --- /dev/null +++ b/scripts/check-version-support.js @@ -0,0 +1,256 @@ +#!/usr/bin/env node + +/** + * Check Version Support Script + * + * This script checks if Dart/Flutter versions are approaching end-of-life (EOL) + * based on the 6-month support window policy. It helps maintain the README + * compatibility tables and identify versions that need to be dropped. + * + * Usage: + * node scripts/check-version-support.js + * node scripts/check-version-support.js --json # Output as JSON + */ + +const SUPPORT_WINDOW_MONTHS = 6; + +// Dart version releases (update this manually as new versions are released) +const DART_RELEASES = [ + { version: '3.2', latest: '3.2.6', releaseDate: '2023-11-15' }, + { version: '3.3', latest: '3.3.4', releaseDate: '2024-02-15' }, + { version: '3.4', latest: '3.4.4', releaseDate: '2024-05-15' }, + { version: '3.5', latest: '3.5.3', releaseDate: '2024-08-15' }, +]; + +// Flutter version releases (update this manually as new versions are released) +const FLUTTER_RELEASES = [ + { version: '3.16', latest: '3.16.9', releaseDate: '2023-11-15' }, + { version: '3.19', latest: '3.19.6', releaseDate: '2024-02-15' }, + { version: '3.22', latest: '3.22.3', releaseDate: '2024-05-15' }, + { version: '3.24', latest: '3.24.3', releaseDate: '2024-08-15' }, +]; + +/** + * Calculate end-of-support date for a version + * @param {string} nextReleaseDate - Release date of next significant version + * @returns {Date} End-of-support date + */ +function calculateEOL(nextReleaseDate) { + const date = new Date(nextReleaseDate); + date.setMonth(date.getMonth() + SUPPORT_WINDOW_MONTHS); + return date; +} + +/** + * Format date as YYYY-MM-DD + * @param {Date} date + * @returns {string} + */ +function formatDate(date) { + return date.toISOString().split('T')[0]; +} + +/** + * Format date as human-readable (e.g., "Jan 2025") + * @param {Date} date + * @returns {string} + */ +function formatHumanDate(date) { + const months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']; + return `${months[date.getMonth()]} ${date.getFullYear()}`; +} + +/** + * Check version support status + * @param {Array} releases - Array of release objects + * @param {string} framework - 'Dart' or 'Flutter' + * @returns {Array} Array of version status objects + */ +function checkVersionSupport(releases, framework) { + const today = new Date(); + const results = []; + + for (let i = 0; i < releases.length; i++) { + const release = releases[i]; + const nextRelease = releases[i + 1]; + + const releaseDate = new Date(release.releaseDate); + const eolDate = nextRelease ? calculateEOL(nextRelease.releaseDate) : null; + + const daysUntilEOL = eolDate ? Math.floor((eolDate - today) / (1000 * 60 * 60 * 24)) : null; + + let status = 'supported'; + let warning = null; + + if (eolDate) { + if (today >= eolDate) { + status = 'expired'; + warning = `Support expired on ${formatHumanDate(eolDate)}`; + } else if (daysUntilEOL <= 30) { + status = 'expiring-soon'; + warning = `Support expires in ${daysUntilEOL} days`; + } else if (daysUntilEOL <= 60) { + status = 'warning'; + warning = `Support expires in ${Math.floor(daysUntilEOL / 30)} month(s)`; + } + } + + results.push({ + framework, + version: release.version, + latest: release.latest, + releaseDate: release.releaseDate, + eolDate: eolDate ? formatDate(eolDate) : 'N/A', + eolHuman: eolDate ? formatHumanDate(eolDate) : 'N/A', + daysUntilEOL, + status, + warning, + compatible: status !== 'expired' + }); + } + + return results; +} + +/** + * Generate README-compatible markdown table + * @param {Array} results - Version status results + * @returns {string} Markdown table + */ +function generateMarkdownTable(results) { + let table = '| Version | Latest Version | End of Support | Compatible |\n'; + table += '|---------|----------------|----------------|------------|\n'; + + for (const result of results) { + const version = result.framework === 'Dart' + ? `Dart ${result.version}` + : `Flutter ${result.version}`; + const compatible = result.compatible ? '✅ Yes' : '❌ No'; + table += `| ${version} | ${result.latest} | ${result.eolHuman} | ${compatible} |\n`; + } + + return table; +} + +/** + * Main function + */ +function main() { + const args = process.argv.slice(2); + const jsonOutput = args.includes('--json'); + + const dartResults = checkVersionSupport(DART_RELEASES, 'Dart'); + const flutterResults = checkVersionSupport(FLUTTER_RELEASES, 'Flutter'); + const allResults = [...dartResults, ...flutterResults]; + + if (jsonOutput) { + console.log(JSON.stringify({ + dart: dartResults, + flutter: flutterResults, + supportWindowMonths: SUPPORT_WINDOW_MONTHS, + generatedAt: new Date().toISOString() + }, null, 2)); + return; + } + + // Human-readable output + console.log('╔════════════════════════════════════════════════════════════════╗'); + console.log('║ Parse SDK Version Support Status Check ║'); + console.log('╚════════════════════════════════════════════════════════════════╝\n'); + + console.log(`Support Window: ${SUPPORT_WINDOW_MONTHS} months after next significant version\n`); + + // Dart versions + console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'); + console.log('📦 DART VERSIONS'); + console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n'); + + for (const result of dartResults) { + const icon = result.status === 'expired' ? '❌' : + result.status === 'expiring-soon' ? '⚠️' : + result.status === 'warning' ? '⚡' : '✅'; + + console.log(`${icon} Dart ${result.version} (${result.latest})`); + console.log(` Release: ${result.releaseDate}`); + console.log(` EOL: ${result.eolHuman}`); + if (result.warning) { + console.log(` ⚠️ ${result.warning}`); + } + console.log(); + } + + // Flutter versions + console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'); + console.log('📱 FLUTTER VERSIONS'); + console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n'); + + for (const result of flutterResults) { + const icon = result.status === 'expired' ? '❌' : + result.status === 'expiring-soon' ? '⚠️' : + result.status === 'warning' ? '⚡' : '✅'; + + console.log(`${icon} Flutter ${result.version} (${result.latest})`); + console.log(` Release: ${result.releaseDate}`); + console.log(` EOL: ${result.eolHuman}`); + if (result.warning) { + console.log(` ⚠️ ${result.warning}`); + } + console.log(); + } + + // Summary + console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'); + console.log('📊 SUMMARY'); + console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n'); + + const expired = allResults.filter(r => r.status === 'expired').length; + const expiringSoon = allResults.filter(r => r.status === 'expiring-soon').length; + const warning = allResults.filter(r => r.status === 'warning').length; + const supported = allResults.filter(r => r.status === 'supported').length; + + console.log(`✅ Supported: ${supported}`); + console.log(`⚡ Warning (< 2 months): ${warning}`); + console.log(`⚠️ Expiring Soon (< 1 month): ${expiringSoon}`); + console.log(`❌ Expired: ${expired}`); + console.log(); + + if (expired > 0) { + console.log('⚠️ ACTION REQUIRED: Remove expired versions from CI and README'); + console.log(' Create a major version bump PR to drop support for:'); + allResults.filter(r => r.status === 'expired').forEach(r => { + console.log(` - ${r.framework} ${r.version}`); + }); + console.log(); + } + + if (expiringSoon > 0 || warning > 0) { + console.log('ℹ️ FYI: Some versions approaching EOL'); + allResults.filter(r => r.status === 'expiring-soon' || r.status === 'warning').forEach(r => { + console.log(` - ${r.framework} ${r.version}: ${r.warning}`); + }); + console.log(); + } + + // Markdown tables for README + console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'); + console.log('📝 README MARKDOWN TABLES'); + console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n'); + + console.log('Dart Package (packages/dart/README.md):\n'); + console.log(generateMarkdownTable(dartResults)); + console.log(); + + console.log('Flutter Package (packages/flutter/README.md):\n'); + console.log(generateMarkdownTable(flutterResults)); + console.log(); + + // Exit code + process.exit(expired > 0 ? 1 : 0); +} + +// Run if called directly +if (require.main === module) { + main(); +} + +module.exports = { checkVersionSupport, calculateEOL, generateMarkdownTable }; From 949466785892b8a860cead3b3b2539665ed9ab10 Mon Sep 17 00:00:00 2001 From: Manuel Trezza <5673677+mtrezza@users.noreply.github.com> Date: Fri, 28 Nov 2025 16:06:21 +0100 Subject: [PATCH 02/14] Create VERSIONING_POLICY.md --- VERSIONING_POLICY.md | 68 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 VERSIONING_POLICY.md diff --git a/VERSIONING_POLICY.md b/VERSIONING_POLICY.md new file mode 100644 index 000000000..e5a5fda8b --- /dev/null +++ b/VERSIONING_POLICY.md @@ -0,0 +1,68 @@ +# Versioning and Support Policy + +Parse SDK for Dart/Flutter versioning, framework compatibility, dependency management, and release strategy. + +## Framework Version Support + +The support window is **6 months** after the next significant version (major/minor, not patch). + +**Example:** +- Dart 3.3.0 released: February 15, 2024 +- Dart 3.2.x end-of-support: August 15, 2024 + +> [!Important] +> There are no deprecation notices. Versions are dropped immediately when support expires. + +See the README files in `/packages/dart/` and `/packages/flutter/` for current compatibility tables. + +## Dependency Management + +### Constraints + +Use **caret constraints** (`^`) following Dart conventions: + +```yaml +dependencies: + dio: ^5.7.0 # Allows >=5.7.0 <6.0.0 + http: ^1.2.0 # Allows >=1.2.0 <2.0.0 +``` + +### Updates + +**Dependabot checks daily** and creates PRs immediately when updates available and PRs are merged as soon as the CI passes. + +### Version Bumps + +| Dependency Change | Parse SDK Bump | +|-------------------|----------------| +| Major with breaking API impact | Major | +| Major with non-breaking API impact | Minor | +| Major without API impact | Patch | +| Minor | Patch | +| Patch | Patch | + +## Handling Version Conflicts + +See [MIGRATION_GUIDES.md](MIGRATION_GUIDES.md) for detailed solutions. + +**Quick fixes:** +1. Update Parse SDK to latest version +2. Update all dependencies: `flutter pub upgrade` +3. Check GitHub issues for known conflicts + +## Testing + +**CI Matrix:** +- Latest: All platforms (Ubuntu, macOS, Windows) +- 1-2 previous: Ubuntu only +- Beta: Ubuntu only + +## Automated Management + +- Dependabot: Regular checks for Dart/Flutter dependencies. + +- Security Audit: Monthly `dart pub audit`, creates issues for vulnerabilities. + +## Resources + +- [MIGRATION_GUIDES.md](MIGRATION_GUIDES.md) - Conflict resolution From 7064b1ae45c64a1bf911c088f5e4a9b223ee04db Mon Sep 17 00:00:00 2001 From: Manuel Trezza <5673677+mtrezza@users.noreply.github.com> Date: Fri, 28 Nov 2025 16:16:22 +0100 Subject: [PATCH 03/14] Update ci.yml --- .github/workflows/ci.yml | 46 +++++++++++----------------------------- 1 file changed, 12 insertions(+), 34 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 850fcd67b..221fbc342 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -19,26 +19,15 @@ jobs: # # Dart framework may contain breaking changes in minor version releases, not following semver. # The latest Dart framework (below) is tested on all architectures (Ubuntu, macOS, Windows). - - name: Dart 3.5, Ubuntu + - name: Dart 3.6, Ubuntu os: ubuntu-latest - sdk: 3.5.3 - - name: Dart 3.5, macOS + sdk: 3.6.0 + - name: Dart 3.6, macOS os: macos-latest - sdk: 3.5.3 - - name: Dart 3.5, Windows + sdk: 3.6.0 + - name: Dart 3.6, Windows os: windows-latest - sdk: 3.5.3 - # Older Dart framework versions (below) are only tested with Ubuntu to reduce CI resource usage. - # These versions should be removed when they reach end-of-support (6 months after next version). - - name: Dart 3.4 - os: ubuntu-latest - sdk: 3.4.4 - - name: Dart 3.3 - os: ubuntu-latest - sdk: 3.3.4 - - name: Dart 3.2 - os: ubuntu-latest - sdk: 3.2.6 + sdk: 3.6.0 # Beta channel helps identify breaking changes early - name: Dart beta os: ubuntu-latest @@ -89,26 +78,15 @@ jobs: # # Flutter framework may contain breaking changes in minor version releases, not following semver. # The latest Flutter framework (below) is tested on all architectures (Ubuntu, macOS, Windows). - - name: Flutter 3.24, Ubuntu + - name: Flutter 3.27, Ubuntu os: ubuntu-latest - sdk: 3.24.3 - - name: Flutter 3.24, macOS + sdk: 3.27.0 + - name: Flutter 3.27, macOS os: macos-latest - sdk: 3.24.3 - - name: Flutter 3.24, Windows + sdk: 3.27.0 + - name: Flutter 3.27, Windows os: windows-latest - sdk: 3.24.3 - # Older Flutter framework versions (below) are only tested with Ubuntu to reduce CI resource usage. - # These versions should be removed when they reach end-of-support (6 months after next version). - - name: Flutter 3.22 - os: ubuntu-latest - sdk: 3.22.3 - - name: Flutter 3.19 - os: ubuntu-latest - sdk: 3.19.6 - - name: Flutter 3.16 - os: ubuntu-latest - sdk: 3.16.9 + sdk: 3.27.0 # Beta channel helps identify breaking changes early - name: Flutter beta os: ubuntu-latest From 7bd8658023dbd3340c5925619d75ce3eaeeda859 Mon Sep 17 00:00:00 2001 From: Manuel Trezza <5673677+mtrezza@users.noreply.github.com> Date: Fri, 28 Nov 2025 16:41:10 +0100 Subject: [PATCH 04/14] fix --- .github/workflows/ci.yml | 24 ++++++++++++------------ packages/dart/pubspec.yaml | 4 ++-- packages/flutter/pubspec.yaml | 4 ++-- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 221fbc342..b02d002ac 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -19,15 +19,15 @@ jobs: # # Dart framework may contain breaking changes in minor version releases, not following semver. # The latest Dart framework (below) is tested on all architectures (Ubuntu, macOS, Windows). - - name: Dart 3.6, Ubuntu + - name: Dart 3.10, Ubuntu os: ubuntu-latest - sdk: 3.6.0 - - name: Dart 3.6, macOS + sdk: 3.10.0 + - name: Dart 3.10, macOS os: macos-latest - sdk: 3.6.0 - - name: Dart 3.6, Windows + sdk: 3.10.0 + - name: Dart 3.10, Windows os: windows-latest - sdk: 3.6.0 + sdk: 3.10.0 # Beta channel helps identify breaking changes early - name: Dart beta os: ubuntu-latest @@ -78,15 +78,15 @@ jobs: # # Flutter framework may contain breaking changes in minor version releases, not following semver. # The latest Flutter framework (below) is tested on all architectures (Ubuntu, macOS, Windows). - - name: Flutter 3.27, Ubuntu + - name: Flutter 3.38, Ubuntu os: ubuntu-latest - sdk: 3.27.0 - - name: Flutter 3.27, macOS + sdk: 3.38.0 + - name: Flutter 3.38, macOS os: macos-latest - sdk: 3.27.0 - - name: Flutter 3.27, Windows + sdk: 3.38.0 + - name: Flutter 3.38, Windows os: windows-latest - sdk: 3.27.0 + sdk: 3.38.0 # Beta channel helps identify breaking changes early - name: Flutter beta os: ubuntu-latest diff --git a/packages/dart/pubspec.yaml b/packages/dart/pubspec.yaml index 016673a48..5341e3d00 100644 --- a/packages/dart/pubspec.yaml +++ b/packages/dart/pubspec.yaml @@ -18,7 +18,7 @@ topics: - backend environment: - sdk: ">=3.2.6 <4.0.0" + sdk: ">=3.10.0 <4.0.0" dependencies: # Networking @@ -46,7 +46,7 @@ dev_dependencies: # Testing build_runner: ^2.4.9 - mockito: ^5.4.4 + mockito: ^5.4.6 test: ^1.25.7 screenshots: diff --git a/packages/flutter/pubspec.yaml b/packages/flutter/pubspec.yaml index 21f3d0a46..6ecb5f3b6 100644 --- a/packages/flutter/pubspec.yaml +++ b/packages/flutter/pubspec.yaml @@ -18,8 +18,8 @@ topics: - backend environment: - sdk: ">=3.2.6 <4.0.0" - flutter: ">=3.16.9" + sdk: ">=3.10.0 <4.0.0" + flutter: ">=3.38.0" dependencies: flutter: From 9434ae00bd629adc3d123503a77b6515df9e1752 Mon Sep 17 00:00:00 2001 From: Manuel Trezza <5673677+mtrezza@users.noreply.github.com> Date: Fri, 28 Nov 2025 21:50:47 +0100 Subject: [PATCH 05/14] chore: Apply Dart 3.10 formatting --- packages/dart/lib/parse_server_sdk.dart | 3 +- .../dart/lib/src/data/parse_core_data.dart | 6 +- .../lib/src/data/parse_subclass_handler.dart | 21 +- .../dart/lib/src/network/parse_client.dart | 9 +- .../lib/src/network/parse_dio_client.dart | 11 +- .../lib/src/network/parse_http_client.dart | 8 +- .../lib/src/network/parse_live_query.dart | 7 +- .../dart/lib/src/network/parse_query.dart | 3 +- packages/dart/lib/src/objects/parse_acl.dart | 6 +- packages/dart/lib/src/objects/parse_base.dart | 5 +- .../dart/lib/src/objects/parse_config.dart | 12 +- .../dart/lib/src/objects/parse_file_base.dart | 13 +- .../dart/lib/src/objects/parse_function.dart | 10 +- .../dart/lib/src/objects/parse_geo_point.dart | 27 +- .../lib/src/objects/parse_installation.dart | 24 +- .../dart/lib/src/objects/parse_object.dart | 6 +- .../parse_operation/parse_operation.dart | 4 +- .../parse_remove_relation_operation.dart | 5 +- .../dart/lib/src/objects/parse_relation.dart | 4 +- .../dart/lib/src/objects/parse_session.dart | 2 +- packages/dart/lib/src/objects/parse_user.dart | 24 +- .../dart/lib/src/objects/parse_x_file.dart | 3 +- .../lib/src/storage/core_store_sem_impl.dart | 37 +- .../dart/lib/src/utils/parse_decoder.dart | 4 +- .../dart/lib/src/utils/parse_encoder.dart | 4 +- .../dart/lib/src/utils/parse_live_list.dart | 541 ++++++---- packages/dart/lib/src/utils/parse_logger.dart | 17 +- .../lib/src/utils/parse_login_helpers.dart | 4 +- packages/dart/lib/src/utils/parse_utils.dart | 90 +- .../dart/test/parse_query_test.mocks.dart | 132 ++- .../src/network/parse_live_query_test.dart | 12 +- .../test/src/network/parse_query_test.dart | 331 +++--- .../src/network/parse_query_test.mocks.dart | 270 ++--- .../test/src/objects/parse_base_test.dart | 110 +- .../parse_object/parse_object_array_test.dart | 991 ++++++++---------- .../parse_object_create_test.dart | 123 +-- .../parse_object_delete_eventually_test.dart | 25 +- .../parse_object_delete_test.dart | 113 +- .../parse_object_distinct_test.dart | 33 +- .../parse_object/parse_object_fetch_test.dart | 238 +++-- .../parse_object_get_all_test.dart | 75 +- .../parse_object_get_object_test.dart | 186 ++-- ...parse_object_increment_decrement_test.dart | 318 +++--- .../parse_object/parse_object_query_test.dart | 30 +- .../parse_object_relation_test.dart | 533 +++++----- .../parse_object_save_eventually_test.dart | 30 +- .../parse_object/parse_object_save_test.dart | 365 +++---- .../parse_object/parse_object_test.dart | 3 +- .../parse_object/parse_object_test.mocks.dart | 270 ++--- .../parse_object/parse_object_unset_test.dart | 230 ++-- .../parse_object_update_test.dart | 114 +- .../response/parse_response_utils_test.dart | 186 ++-- .../test/src/utils/parse_encoder_test.dart | 26 +- packages/dart/test/test_utils.dart | 34 +- 54 files changed, 2853 insertions(+), 2835 deletions(-) diff --git a/packages/dart/lib/parse_server_sdk.dart b/packages/dart/lib/parse_server_sdk.dart index 3c622171a..85e9ab092 100644 --- a/packages/dart/lib/parse_server_sdk.dart +++ b/packages/dart/lib/parse_server_sdk.dart @@ -172,7 +172,8 @@ class Parse { }) async { final bool debugLocal = isDebugEnabled(objectLevelDebug: debug); - final ParseClient clientLocal = client ?? + final ParseClient clientLocal = + client ?? ParseCoreData().clientCreator( sendSessionId: sendSessionIdByDefault ?? ParseCoreData().autoSendSessionId, diff --git a/packages/dart/lib/src/data/parse_core_data.dart b/packages/dart/lib/src/data/parse_core_data.dart index e96e632e5..aaf2ced58 100644 --- a/packages/dart/lib/src/data/parse_core_data.dart +++ b/packages/dart/lib/src/data/parse_core_data.dart @@ -52,7 +52,8 @@ class ParseCoreData { _instance.sessionId = sessionId; _instance.autoSendSessionId = autoSendSessionId; _instance.securityContext = securityContext; - _instance.liveListRetryIntervals = liveListRetryIntervals ?? + _instance.liveListRetryIntervals = + liveListRetryIntervals ?? (parseIsWeb ? [0, 500, 1000, 2000, 5000] : [0, 500, 1000, 2000, 5000, 10000]); @@ -64,7 +65,8 @@ class ParseCoreData { _instance.connectivityProvider = connectivityProvider; _instance.fileDirectory = fileDirectory; _instance.appResumedStream = appResumedStream; - _instance.clientCreator = clientCreator ?? + _instance.clientCreator = + clientCreator ?? (({required bool sendSessionId, SecurityContext? securityContext}) => ParseHTTPClient( sendSessionId: sendSessionId, diff --git a/packages/dart/lib/src/data/parse_subclass_handler.dart b/packages/dart/lib/src/data/parse_subclass_handler.dart index c0eb25323..9481f8c99 100644 --- a/packages/dart/lib/src/data/parse_subclass_handler.dart +++ b/packages/dart/lib/src/data/parse_subclass_handler.dart @@ -1,16 +1,17 @@ part of '../../parse_server_sdk.dart'; typedef ParseObjectConstructor = ParseObject Function(); -typedef ParseUserConstructor = ParseUser Function( - String? username, - String? password, - String? emailAddress, { - String? sessionToken, - bool? debug, - ParseClient? client, -}); -typedef ParseFileConstructor = ParseFileBase Function( - {String? name, String? url}); +typedef ParseUserConstructor = + ParseUser Function( + String? username, + String? password, + String? emailAddress, { + String? sessionToken, + bool? debug, + ParseClient? client, + }); +typedef ParseFileConstructor = + ParseFileBase Function({String? name, String? url}); class ParseSubClassHandler { ParseSubClassHandler({ diff --git a/packages/dart/lib/src/network/parse_client.dart b/packages/dart/lib/src/network/parse_client.dart index cd34ac476..2104fc20a 100644 --- a/packages/dart/lib/src/network/parse_client.dart +++ b/packages/dart/lib/src/network/parse_client.dart @@ -1,9 +1,10 @@ part of '../../parse_server_sdk.dart'; -typedef ParseClientCreator = ParseClient Function({ - required bool sendSessionId, - SecurityContext? securityContext, -}); +typedef ParseClientCreator = + ParseClient Function({ + required bool sendSessionId, + SecurityContext? securityContext, + }); abstract class ParseClient { Future get( diff --git a/packages/dart/lib/src/network/parse_dio_client.dart b/packages/dart/lib/src/network/parse_dio_client.dart index a8d64b262..bb13373aa 100644 --- a/packages/dart/lib/src/network/parse_dio_client.dart +++ b/packages/dart/lib/src/network/parse_dio_client.dart @@ -196,7 +196,7 @@ class ParseDioClient extends ParseClient { /// Creates a custom version of HTTP Client that has Parse Data Preset class _ParseDioClient with dio.DioMixin implements dio.Dio { _ParseDioClient({bool sendSessionId = false, dynamic securityContext}) - : _sendSessionId = sendSessionId { + : _sendSessionId = sendSessionId { options = dio.BaseOptions(); httpClientAdapter = createHttpClientAdapter(securityContext); } @@ -290,8 +290,9 @@ class _ParseDioClient with dio.DioMixin implements dio.Dio { class _Options extends dio.Options { _Options({super.headers, super.responseType, String? contentType}) - : super( - contentType: contentType ?? - (headers ?? {})[dio.Headers.contentTypeHeader], - ); + : super( + contentType: + contentType ?? + (headers ?? {})[dio.Headers.contentTypeHeader], + ); } diff --git a/packages/dart/lib/src/network/parse_http_client.dart b/packages/dart/lib/src/network/parse_http_client.dart index 8d8f3bf15..b319b2820 100644 --- a/packages/dart/lib/src/network/parse_http_client.dart +++ b/packages/dart/lib/src/network/parse_http_client.dart @@ -138,10 +138,10 @@ class _ParseHTTPClient extends http.BaseClient { _ParseHTTPClient({ bool sendSessionId = false, SecurityContext? securityContext, - }) : _sendSessionId = sendSessionId, - _client = securityContext != null - ? getClient(securityContext) - : http.Client(); + }) : _sendSessionId = sendSessionId, + _client = securityContext != null + ? getClient(securityContext) + : http.Client(); final http.Client _client; final bool _sendSessionId; diff --git a/packages/dart/lib/src/network/parse_live_query.dart b/packages/dart/lib/src/network/parse_live_query.dart index a9cd93310..693859dc5 100644 --- a/packages/dart/lib/src/network/parse_live_query.dart +++ b/packages/dart/lib/src/network/parse_live_query.dart @@ -138,8 +138,8 @@ class LiveQueryClient { bool? autoSendSessionId, }) { _clientEventStreamController = StreamController(); - _clientEventStream = - _clientEventStreamController.stream.asBroadcastStream(); + _clientEventStream = _clientEventStreamController.stream + .asBroadcastStream(); _debug = isDebugEnabled(objectLevelDebug: debug); _sendSessionId = autoSendSessionId ?? ParseCoreData().autoSendSessionId; @@ -169,7 +169,8 @@ class LiveQueryClient { liveQueryURL = liveQueryURL.replaceAll('http', 'ws'); } } - LiveQueryClient instance = _instance ?? + LiveQueryClient instance = + _instance ?? LiveQueryClient._internal( liveQueryURL, debug: debug, diff --git a/packages/dart/lib/src/network/parse_query.dart b/packages/dart/lib/src/network/parse_query.dart index 79898c24a..c2ddf8111 100644 --- a/packages/dart/lib/src/network/parse_query.dart +++ b/packages/dart/lib/src/network/parse_query.dart @@ -769,8 +769,7 @@ class QueryBuilder { Future first() async { ParseResponse parseResponse = await (QueryBuilder.copy( this, - )..setLimit(1)) - .query(); + )..setLimit(1)).query(); if (parseResponse.success) { return parseResponse.results?.first; } diff --git a/packages/dart/lib/src/objects/parse_acl.dart b/packages/dart/lib/src/objects/parse_acl.dart index 26d00d146..eabbc4f9e 100644 --- a/packages/dart/lib/src/objects/parse_acl.dart +++ b/packages/dart/lib/src/objects/parse_acl.dart @@ -127,7 +127,7 @@ class _ACLPermissions { bool get writePermission => _writePermission; Map toJson() => { - _keyReadPermission: _readPermission, - _keyWritePermission: _writePermission, - }; + _keyReadPermission: _readPermission, + _keyWritePermission: _writePermission, + }; } diff --git a/packages/dart/lib/src/objects/parse_base.dart b/packages/dart/lib/src/objects/parse_base.dart index 96c1e6386..2db6967f5 100644 --- a/packages/dart/lib/src/objects/parse_base.dart +++ b/packages/dart/lib/src/objects/parse_base.dart @@ -270,8 +270,9 @@ abstract class ParseBase { if (result is _ParseRelation) { return (result - ..parent = (this as ParseObject) - ..key = key) as T?; + ..parent = (this as ParseObject) + ..key = key) + as T?; } return result as T?; diff --git a/packages/dart/lib/src/objects/parse_config.dart b/packages/dart/lib/src/objects/parse_config.dart index 06869119e..6cd25fe22 100644 --- a/packages/dart/lib/src/objects/parse_config.dart +++ b/packages/dart/lib/src/objects/parse_config.dart @@ -3,12 +3,12 @@ part of '../../parse_server_sdk.dart'; class ParseConfig extends ParseObject { /// Creates an instance of ParseConfig so that you can grab all configs from the server ParseConfig({bool? debug, ParseClient? client, bool? autoSendSessionId}) - : super( - 'config', - debug: debug, - client: client, - autoSendSessionId: autoSendSessionId, - ); + : super( + 'config', + debug: debug, + client: client, + autoSendSessionId: autoSendSessionId, + ); /// Gets all configs from the server Future getConfigs() async { diff --git a/packages/dart/lib/src/objects/parse_file_base.dart b/packages/dart/lib/src/objects/parse_file_base.dart index c944863ee..a71908708 100644 --- a/packages/dart/lib/src/objects/parse_file_base.dart +++ b/packages/dart/lib/src/objects/parse_file_base.dart @@ -11,11 +11,11 @@ abstract class ParseFileBase extends ParseObject { ParseClient? client, bool? autoSendSessionId, }) : super( - keyFileClassname, - debug: debug, - autoSendSessionId: autoSendSessionId, - client: client, - ) { + keyFileClassname, + debug: debug, + autoSendSessionId: autoSendSessionId, + client: client, + ) { _path = '/files/$name'; this.name = name; if (url != null) this.url = url; @@ -34,8 +34,7 @@ abstract class ParseFileBase extends ParseObject { bool full = false, bool forApiRQ = false, bool allowCustomObjectId = false, - }) => - {'__type': keyFile, 'name': name, 'url': url}; + }) => {'__type': keyFile, 'name': name, 'url': url}; @override String toString() => json.encode(toJson(full: true)); diff --git a/packages/dart/lib/src/objects/parse_function.dart b/packages/dart/lib/src/objects/parse_function.dart index d512d0b2c..191cd6391 100644 --- a/packages/dart/lib/src/objects/parse_function.dart +++ b/packages/dart/lib/src/objects/parse_function.dart @@ -10,11 +10,11 @@ class ParseCloudFunction extends ParseObject { ParseClient? client, bool? autoSendSessionId, }) : super( - functionName, - client: client, - autoSendSessionId: autoSendSessionId, - debug: debug, - ) { + functionName, + client: client, + autoSendSessionId: autoSendSessionId, + debug: debug, + ) { _path = '/functions/$functionName'; } diff --git a/packages/dart/lib/src/objects/parse_geo_point.dart b/packages/dart/lib/src/objects/parse_geo_point.dart index cdc36b978..95579ff62 100644 --- a/packages/dart/lib/src/objects/parse_geo_point.dart +++ b/packages/dart/lib/src/objects/parse_geo_point.dart @@ -6,20 +6,19 @@ const String keyLongitude = 'longitude'; class ParseGeoPoint { /// Creates a Parse Object of type GeoPoint ParseGeoPoint({this.latitude = 0.0, this.longitude = 0.0}) - : assert( - latitude < 90, 'Latitude must be within the range (-90.0, 90.0).'), - assert( - latitude > -90, - 'Latitude must be within the range (-90.0, 90.0).', - ), - assert( - latitude < 180, - 'Longitude must be within the range (-180.0, 180.0).', - ), - assert( - latitude > -180, - 'Longitude must be within the range (-180.0, 180.0).', - ); + : assert(latitude < 90, 'Latitude must be within the range (-90.0, 90.0).'), + assert( + latitude > -90, + 'Latitude must be within the range (-90.0, 90.0).', + ), + assert( + latitude < 180, + 'Longitude must be within the range (-180.0, 180.0).', + ), + assert( + latitude > -180, + 'Longitude must be within the range (-180.0, 180.0).', + ); double latitude, longitude; diff --git a/packages/dart/lib/src/objects/parse_installation.dart b/packages/dart/lib/src/objects/parse_installation.dart index 84e5840ae..9408038a2 100644 --- a/packages/dart/lib/src/objects/parse_installation.dart +++ b/packages/dart/lib/src/objects/parse_installation.dart @@ -3,12 +3,12 @@ part of '../../parse_server_sdk.dart'; class ParseInstallation extends ParseObject { /// Creates an instance of ParseInstallation ParseInstallation({bool? debug, ParseClient? client, bool? autoSendSessionId}) - : super( - keyClassInstallation, - client: client, - autoSendSessionId: autoSendSessionId, - debug: debug, - ); + : super( + keyClassInstallation, + client: client, + autoSendSessionId: autoSendSessionId, + debug: debug, + ); ParseInstallation.forQuery() : super(keyClassUser); @@ -25,9 +25,9 @@ class ParseInstallation extends ParseObject { //Getters/setters Map get acl => super.get>( - keyVarAcl, - defaultValue: {}, - )!; + keyVarAcl, + defaultValue: {}, + )!; set acl(Map acl) => set>(keyVarAcl, acl); @@ -178,9 +178,9 @@ class ParseInstallation extends ParseObject { installation._installationId = _currentInstallationId; await installation._updateInstallation(); await ParseCoreData().getStore().setString( - keyParseStoreInstallation, - json.encode(installation.toJson(full: true)), - ); + keyParseStoreInstallation, + json.encode(installation.toJson(full: true)), + ); return installation; } diff --git a/packages/dart/lib/src/objects/parse_object.dart b/packages/dart/lib/src/objects/parse_object.dart index 7252be52f..3163c8067 100644 --- a/packages/dart/lib/src/objects/parse_object.dart +++ b/packages/dart/lib/src/objects/parse_object.dart @@ -28,7 +28,8 @@ class ParseObject extends ParseBase implements ParseCloneable { _aggregatepath = '$keyEndPointAggregate$className'; _debug = isDebugEnabled(objectLevelDebug: debug); - _client = client ?? + _client = + client ?? ParseCoreData().clientCreator( sendSessionId: autoSendSessionId ?? ParseCoreData().autoSendSessionId, securityContext: ParseCoreData().securityContext, @@ -847,7 +848,8 @@ class ParseObject extends ParseBase implements ParseCloneable { bool? autoSendSessionId, }) async { // get client - ParseClient localClient = client ?? + ParseClient localClient = + client ?? ParseCoreData().clientCreator( sendSessionId: autoSendSessionId ?? ParseCoreData().autoSendSessionId, securityContext: ParseCoreData().securityContext, diff --git a/packages/dart/lib/src/objects/parse_operation/parse_operation.dart b/packages/dart/lib/src/objects/parse_operation/parse_operation.dart index fc5a512f7..c990530d4 100644 --- a/packages/dart/lib/src/objects/parse_operation/parse_operation.dart +++ b/packages/dart/lib/src/objects/parse_operation/parse_operation.dart @@ -246,8 +246,8 @@ abstract class _ParseRelationOperation final Set? objectsForAPIRequest = json['valueForAPIRequest'] == null - ? null - : Set.from(parseDecode(json['valueForAPIRequest'])); + ? null + : Set.from(parseDecode(json['valueForAPIRequest'])); final _ParseRelationOperation relationOperation; switch (json['__op']) { diff --git a/packages/dart/lib/src/objects/parse_operation/parse_remove_relation_operation.dart b/packages/dart/lib/src/objects/parse_operation/parse_remove_relation_operation.dart index 7ef202b70..fc4571237 100644 --- a/packages/dart/lib/src/objects/parse_operation/parse_remove_relation_operation.dart +++ b/packages/dart/lib/src/objects/parse_operation/parse_remove_relation_operation.dart @@ -28,8 +28,9 @@ class _ParseRemoveRelationOperation extends _ParseRelationOperation { valueForApiRequest.addAll(value); - final parseObjectToRemoveByIds = - value.where((e) => e.objectId != null).map((e) => e.objectId!); + final parseObjectToRemoveByIds = value + .where((e) => e.objectId != null) + .map((e) => e.objectId!); value = previousValue ..removeWhere( diff --git a/packages/dart/lib/src/objects/parse_relation.dart b/packages/dart/lib/src/objects/parse_relation.dart index 12f0c3d61..8ba1c647a 100644 --- a/packages/dart/lib/src/objects/parse_relation.dart +++ b/packages/dart/lib/src/objects/parse_relation.dart @@ -243,8 +243,8 @@ class _ParseRelation @override void onSaving() { _lastPreformedOperationBeforeSaving = lastPreformedOperation; - _valueForApiRequestBeforeSaving = - lastPreformedOperation?.valueForApiRequest.toList(); + _valueForApiRequestBeforeSaving = lastPreformedOperation?.valueForApiRequest + .toList(); } @override diff --git a/packages/dart/lib/src/objects/parse_session.dart b/packages/dart/lib/src/objects/parse_session.dart index e07700a7b..3b5df78e8 100644 --- a/packages/dart/lib/src/objects/parse_session.dart +++ b/packages/dart/lib/src/objects/parse_session.dart @@ -2,7 +2,7 @@ part of '../../parse_server_sdk.dart'; class ParseSession extends ParseObject implements ParseCloneable { ParseSession({bool? debug, ParseClient? client}) - : super(keyClassSession, client: client, debug: debug); + : super(keyClassSession, client: client, debug: debug); @override ParseSession clone(Map map) { diff --git a/packages/dart/lib/src/objects/parse_user.dart b/packages/dart/lib/src/objects/parse_user.dart index f8cb19220..c89a5e87d 100644 --- a/packages/dart/lib/src/objects/parse_user.dart +++ b/packages/dart/lib/src/objects/parse_user.dart @@ -19,11 +19,11 @@ class ParseUser extends ParseObject implements ParseCloneable { bool? debug, ParseClient? client, }) : super( - keyClassUser, - client: client, - autoSendSessionId: true, - debug: debug, - ) { + keyClassUser, + client: client, + autoSendSessionId: true, + debug: debug, + ) { if (username != null) this.username = username; if (emailAddress != null) this.emailAddress = emailAddress; if (password != null) this.password = password; @@ -33,7 +33,7 @@ class ParseUser extends ParseObject implements ParseCloneable { ParseUser.forQuery() : super(keyClassUser); ParseUser.clone(Map map) - : this(map[keyVarUsername], null, map[keyVarEmail]); + : this(map[keyVarUsername], null, map[keyVarEmail]); @override dynamic clone(Map map) => @@ -56,9 +56,9 @@ class ParseUser extends ParseObject implements ParseCloneable { } Map get acl => super.get>( - keyVarAcl, - defaultValue: {}, - )!; + keyVarAcl, + defaultValue: {}, + )!; set acl(Map acl) => set>(keyVarAcl, acl); @@ -126,7 +126,8 @@ class ParseUser extends ParseObject implements ParseCloneable { ParseClient? client, }) async { final bool debugLocal = isDebugEnabled(objectLevelDebug: debug); - final ParseClient clientLocal = client ?? + final ParseClient clientLocal = + client ?? ParseCoreData().clientCreator( sendSessionId: true, securityContext: ParseCoreData().securityContext, @@ -545,7 +546,8 @@ class ParseUser extends ParseObject implements ParseCloneable { final ParseUser emptyUser = _getEmptyUser(); final bool debugLocal = isDebugEnabled(objectLevelDebug: debug); - final ParseClient clientLocal = client ?? + final ParseClient clientLocal = + client ?? ParseCoreData().clientCreator( sendSessionId: true, securityContext: ParseCoreData().securityContext, diff --git a/packages/dart/lib/src/objects/parse_x_file.dart b/packages/dart/lib/src/objects/parse_x_file.dart index f6ac5ab11..2cc6e2059 100644 --- a/packages/dart/lib/src/objects/parse_x_file.dart +++ b/packages/dart/lib/src/objects/parse_x_file.dart @@ -98,7 +98,8 @@ class ParseXFile extends ParseFileBase { // If neither provides a type, let the server infer from the filename String? contentType; if (parseIsWeb) { - contentType = file?.mimeType ?? + contentType = + file?.mimeType ?? lookupMimeType( url ?? file?.name ?? name, headerBytes: await file?.readAsBytes(), diff --git a/packages/dart/lib/src/storage/core_store_sem_impl.dart b/packages/dart/lib/src/storage/core_store_sem_impl.dart index f0bb4451b..63ea3102b 100644 --- a/packages/dart/lib/src/storage/core_store_sem_impl.dart +++ b/packages/dart/lib/src/storage/core_store_sem_impl.dart @@ -6,34 +6,47 @@ class CoreStoreSembastImp implements CoreStore { static CoreStoreSembastImp? _instance; - static Future getInstance(String dbPath, - {DatabaseFactory? factory, String? password}) async { + static Future getInstance( + String dbPath, { + DatabaseFactory? factory, + String? password, + }) async { password ??= 'flutter_sdk'; if (_instance == null) { factory ??= !parseIsWeb ? databaseFactoryIo : databaseFactoryWeb; assert(() { if (parseIsWeb) { print( - '***********************************************************************************************************'); + '***********************************************************************************************************', + ); print( - 'Warning: CoreStoreSembastImp of the Parse_Server_SDK does not encrypt the database on WEB.'); + 'Warning: CoreStoreSembastImp of the Parse_Server_SDK does not encrypt the database on WEB.', + ); print( - '***********************************************************************************************************'); + '***********************************************************************************************************', + ); } if (password == 'flutter_sdk') { print( - '***********************************************************************************************************'); + '***********************************************************************************************************', + ); print( - 'Warning: CoreStoreSembastImp uses the default password. Specify a custom password for increased security.'); + 'Warning: CoreStoreSembastImp uses the default password. Specify a custom password for increased security.', + ); print( - '***********************************************************************************************************'); + '***********************************************************************************************************', + ); } return true; }()); - final Database db = await factory.openDatabase(dbPath, - codec: !parseIsWeb ? getXXTeaSembastCodec(password: password) : null); - _instance = - CoreStoreSembastImp._internal(db, StoreRef.main()); + final Database db = await factory.openDatabase( + dbPath, + codec: !parseIsWeb ? getXXTeaSembastCodec(password: password) : null, + ); + _instance = CoreStoreSembastImp._internal( + db, + StoreRef.main(), + ); } return _instance!; diff --git a/packages/dart/lib/src/utils/parse_decoder.dart b/packages/dart/lib/src/utils/parse_decoder.dart index 3a3f043f0..796ac87b4 100644 --- a/packages/dart/lib/src/utils/parse_decoder.dart +++ b/packages/dart/lib/src/utils/parse_decoder.dart @@ -65,7 +65,9 @@ dynamic parseDecode(dynamic value) { final num latitude = map['latitude'] ?? 0.0; final num longitude = map['longitude'] ?? 0.0; return ParseGeoPoint( - latitude: latitude.toDouble(), longitude: longitude.toDouble()); + latitude: latitude.toDouble(), + longitude: longitude.toDouble(), + ); case 'Relation': return ParseRelation.fromJson(map); } diff --git a/packages/dart/lib/src/utils/parse_encoder.dart b/packages/dart/lib/src/utils/parse_encoder.dart index c957a39e9..c73f0b6e3 100644 --- a/packages/dart/lib/src/utils/parse_encoder.dart +++ b/packages/dart/lib/src/utils/parse_encoder.dart @@ -76,7 +76,7 @@ Map _encodeUint8List(Uint8List value) { Map _encodeDate(DateTime date) { return { '__type': 'Date', - 'iso': _parseDateFormat.format(date) + 'iso': _parseDateFormat.format(date), }; } @@ -84,6 +84,6 @@ Map encodeObject(String className, String objectId) { return { '__type': 'Pointer', keyVarClassName: className, - keyVarObjectId: objectId + keyVarObjectId: objectId, }; } diff --git a/packages/dart/lib/src/utils/parse_live_list.dart b/packages/dart/lib/src/utils/parse_live_list.dart index 43464c5e7..86ad72ad3 100644 --- a/packages/dart/lib/src/utils/parse_live_list.dart +++ b/packages/dart/lib/src/utils/parse_live_list.dart @@ -2,9 +2,12 @@ part of '../../parse_server_sdk.dart'; // ignore_for_file: invalid_use_of_protected_member class ParseLiveList { - ParseLiveList._(this._query, this._listeningIncludes, this._lazyLoading, - {List? preloadedColumns}) - : _preloadedColumns = preloadedColumns ?? const [] { + ParseLiveList._( + this._query, + this._listeningIncludes, + this._lazyLoading, { + List? preloadedColumns, + }) : _preloadedColumns = preloadedColumns ?? const [] { _debug = isDebugEnabled(); } @@ -19,7 +22,8 @@ class ParseLiveList { query, listenOnAllSubItems == true ? _toIncludeMap( - query.limiters['include']?.toString().split(',') ?? []) + query.limiters['include']?.toString().split(',') ?? [], + ) : _toIncludeMap(listeningIncludes ?? []), lazyLoading, preloadedColumns: preloadedColumns ?? const [], @@ -157,110 +161,129 @@ class ParseLiveList { final ParseResponse parseResponse = await _runQuery(); if (parseResponse.success) { - _list = parseResponse.results - ?.map>((dynamic element) => - ParseLiveListElement(element, - updatedSubItems: _listeningIncludes, - loaded: !_lazyLoading)) + _list = + parseResponse.results + ?.map>( + (dynamic element) => ParseLiveListElement( + element, + updatedSubItems: _listeningIncludes, + loaded: !_lazyLoading, + ), + ) .toList() ?? >[]; } - LiveQuery() - .client - .subscribe(QueryBuilder.copy(_query), - copyObject: _query.object.clone(_query.object.toJson())) + LiveQuery().client + .subscribe( + QueryBuilder.copy(_query), + copyObject: _query.object.clone(_query.object.toJson()), + ) .then((Subscription subscription) { - _liveQuerySubscription = subscription; - - //This should synchronize the events. Not sure if it is necessary, but it should help preventing unexpected results. - subscription.on(LiveQueryEvent.create, - (T object) => _updateQueue.whenComplete(() => _objectAdded(object))); - subscription.on( - LiveQueryEvent.update, - (T object) => - _updateQueue.whenComplete(() => _objectUpdated(object))); - subscription.on(LiveQueryEvent.enter, - (T object) => _updateQueue.whenComplete(() => _objectAdded(object))); - subscription.on( - LiveQueryEvent.leave, - (T object) => - _updateQueue.whenComplete(() => _objectDeleted(object))); - subscription.on( - LiveQueryEvent.delete, - (T object) => - _updateQueue.whenComplete(() => _objectDeleted(object))); -// subscription.on(LiveQueryEvent.create, _objectAdded); -// subscription.on(LiveQueryEvent.update, _objectUpdated); -// subscription.on(LiveQueryEvent.enter, _objectAdded); -// subscription.on(LiveQueryEvent.leave, _objectDeleted); -// subscription.on(LiveQueryEvent.delete, _objectDeleted); - }); + _liveQuerySubscription = subscription; + + //This should synchronize the events. Not sure if it is necessary, but it should help preventing unexpected results. + subscription.on( + LiveQueryEvent.create, + (T object) => _updateQueue.whenComplete(() => _objectAdded(object)), + ); + subscription.on( + LiveQueryEvent.update, + (T object) => + _updateQueue.whenComplete(() => _objectUpdated(object)), + ); + subscription.on( + LiveQueryEvent.enter, + (T object) => _updateQueue.whenComplete(() => _objectAdded(object)), + ); + subscription.on( + LiveQueryEvent.leave, + (T object) => + _updateQueue.whenComplete(() => _objectDeleted(object)), + ); + subscription.on( + LiveQueryEvent.delete, + (T object) => + _updateQueue.whenComplete(() => _objectDeleted(object)), + ); + // subscription.on(LiveQueryEvent.create, _objectAdded); + // subscription.on(LiveQueryEvent.update, _objectUpdated); + // subscription.on(LiveQueryEvent.enter, _objectAdded); + // subscription.on(LiveQueryEvent.leave, _objectDeleted); + // subscription.on(LiveQueryEvent.delete, _objectDeleted); + }); - _liveQueryClientEventSubscription = LiveQuery() - .client - .getClientEventStream + _liveQueryClientEventSubscription = LiveQuery().client.getClientEventStream .listen((LiveQueryClientEvent event) async { - if (event == LiveQueryClientEvent.connected) { - _updateQueue.whenComplete(() async { - List> tasks = >[]; - final ParseResponse parseResponse = await _runQuery(); - if (parseResponse.success) { - final List newList = parseResponse.results as List? ?? []; - - //update List - for (int i = 0; i < _list.length; i++) { - final ParseObject currentObject = _list[i].object; - final String? currentObjectId = currentObject.objectId; - - bool stillInList = false; - - for (int j = 0; j < newList.length; j++) { - if (newList[j].get(keyVarObjectId) == currentObjectId) { - stillInList = true; - if (newList[j] - .get(keyVarUpdatedAt)! - .isAfter(currentObject.get(keyVarUpdatedAt)!)) { - final QueryBuilder queryBuilder = - QueryBuilder.copy(_query) - ..whereEqualTo(keyVarObjectId, currentObjectId); - tasks.add(queryBuilder - .query() - .then((ParseResponse result) async { - List? results = result.results; - if (result.success && results != null) { - await _objectUpdated(results.first); + if (event == LiveQueryClientEvent.connected) { + _updateQueue.whenComplete(() async { + List> tasks = >[]; + final ParseResponse parseResponse = await _runQuery(); + if (parseResponse.success) { + final List newList = + parseResponse.results as List? ?? []; + + //update List + for (int i = 0; i < _list.length; i++) { + final ParseObject currentObject = _list[i].object; + final String? currentObjectId = currentObject.objectId; + + bool stillInList = false; + + for (int j = 0; j < newList.length; j++) { + if (newList[j].get(keyVarObjectId) == + currentObjectId) { + stillInList = true; + if (newList[j] + .get(keyVarUpdatedAt)! + .isAfter( + currentObject.get(keyVarUpdatedAt)!, + )) { + final QueryBuilder queryBuilder = + QueryBuilder.copy(_query) + ..whereEqualTo(keyVarObjectId, currentObjectId); + tasks.add( + queryBuilder.query().then(( + ParseResponse result, + ) async { + List? results = result.results; + if (result.success && results != null) { + await _objectUpdated(results.first); + } + }), + ); } - })); + newList.removeAt(j); + j--; + break; + } } - newList.removeAt(j); - j--; - break; + if (!stillInList) { + _objectDeleted(currentObject as T); + i--; + } + } + + for (int i = 0; i < newList.length; i++) { + tasks.add(_objectAdded(newList[i], loaded: false)); } } - if (!stillInList) { - _objectDeleted(currentObject as T); - i--; + await Future.wait(tasks); + tasks = >[]; + for (ParseLiveListElement element in _list) { + tasks.add(element.reconnected()); } - } - - for (int i = 0; i < newList.length; i++) { - tasks.add(_objectAdded(newList[i], loaded: false)); - } + await Future.wait(tasks); + }); } - await Future.wait(tasks); - tasks = >[]; - for (ParseLiveListElement element in _list) { - tasks.add(element.reconnected()); - } - await Future.wait(tasks); }); - } - }); } - static Future _loadIncludes(ParseObject? object, - {ParseObject? oldObject, Map? paths}) async { + static Future _loadIncludes( + ParseObject? object, { + ParseObject? oldObject, + Map? paths, + }) async { if (object == null || paths == null || paths.isEmpty) { return; } @@ -281,53 +304,65 @@ class ParseLiveList { includedObject.objectId != keyInOld.objectId) { //fetch from web including sub objects //same as down there - final QueryBuilder queryBuilder = QueryBuilder< - ParseObject>(ParseObject(includedObject.parseClassName)) - ..whereEqualTo(keyVarObjectId, includedObject.objectId) - ..includeObject(_toIncludeStringList(paths[key])); - loadingNodes.add(queryBuilder - .query() - .then((ParseResponse parseResponse) { - List? results = parseResponse.results; - if (parseResponse.success && - results != null && - results.length == 1) { - object[key] = results[0]; - } - })); + final QueryBuilder queryBuilder = + QueryBuilder( + ParseObject(includedObject.parseClassName), + ) + ..whereEqualTo(keyVarObjectId, includedObject.objectId) + ..includeObject(_toIncludeStringList(paths[key])); + loadingNodes.add( + queryBuilder.query().then(( + ParseResponse parseResponse, + ) { + List? results = parseResponse.results; + if (parseResponse.success && + results != null && + results.length == 1) { + object[key] = results[0]; + } + }), + ); continue; } else { includedObject = keyInOld; object[key] = includedObject; //recursion - loadingNodes - .add(_loadIncludes(includedObject, paths: paths[key])); + loadingNodes.add( + _loadIncludes(includedObject, paths: paths[key]), + ); continue; } } else { //fetch from web including sub objects //same as up there - final QueryBuilder queryBuilder = QueryBuilder< - ParseObject>(ParseObject(includedObject.parseClassName)) - ..whereEqualTo(keyVarObjectId, includedObject.objectId) - ..includeObject(_toIncludeStringList(paths[key])); - loadingNodes.add(queryBuilder - .query() - .then((ParseResponse parseResponse) { - List? results = parseResponse.results; - if (parseResponse.success && - results != null && - results.length == 1) { - object[key] = results[0]; - } - })); + final QueryBuilder queryBuilder = + QueryBuilder( + ParseObject(includedObject.parseClassName), + ) + ..whereEqualTo(keyVarObjectId, includedObject.objectId) + ..includeObject(_toIncludeStringList(paths[key])); + loadingNodes.add( + queryBuilder.query().then((ParseResponse parseResponse) { + List? results = parseResponse.results; + if (parseResponse.success && + results != null && + results.length == 1) { + object[key] = results[0]; + } + }), + ); continue; } } } else { //recursion - loadingNodes.add(_loadIncludes(includedObject, - oldObject: oldObject?.get(key), paths: paths[key])); + loadingNodes.add( + _loadIncludes( + includedObject, + oldObject: oldObject?.get(key), + paths: paths[key], + ), + ); continue; } } else { @@ -345,14 +380,18 @@ class ParseLiveList { // ignore: avoid_as if ((includes[key] as Map).isNotEmpty) { includeList.addAll( - _toIncludeStringList(includes[key]).map((String e) => '$key.$e')); + _toIncludeStringList(includes[key]).map((String e) => '$key.$e'), + ); } } return includeList; } - Future _objectAdded(T object, - {bool loaded = true, bool fetchedIncludes = false}) async { + Future _objectAdded( + T object, { + bool loaded = true, + bool fetchedIncludes = false, + }) async { //This line seems unnecessary, but without this, weird things happen. //(Hide first element, hide second, view first, view second => second is displayed twice) object = object.clone(object.toJson(full: true)); @@ -363,36 +402,63 @@ class ParseLiveList { for (int i = 0; i < _list.length; i++) { if (after(object, _list[i].object) != true) { _list.insert( - i, - ParseLiveListElement(object, - loaded: loaded, updatedSubItems: _listeningIncludes)); - _eventStreamController.sink.add(ParseLiveListAddEvent( - i, object.clone(object.toJson(full: true)))); + i, + ParseLiveListElement( + object, + loaded: loaded, + updatedSubItems: _listeningIncludes, + ), + ); + _eventStreamController.sink.add( + ParseLiveListAddEvent(i, object.clone(object.toJson(full: true))), + ); return; } } - _list.add(ParseLiveListElement(object, - loaded: loaded, updatedSubItems: _listeningIncludes)); - _eventStreamController.sink.add(ParseLiveListAddEvent( - _list.length - 1, object.clone(object.toJson(full: true)))); + _list.add( + ParseLiveListElement( + object, + loaded: loaded, + updatedSubItems: _listeningIncludes, + ), + ); + _eventStreamController.sink.add( + ParseLiveListAddEvent( + _list.length - 1, + object.clone(object.toJson(full: true)), + ), + ); } Future _objectUpdated(T object) async { for (int i = 0; i < _list.length; i++) { if (_list[i].object.get(keyVarObjectId) == object.get(keyVarObjectId)) { - await _loadIncludes(object, - oldObject: _list[i].object, paths: _includePaths); + await _loadIncludes( + object, + oldObject: _list[i].object, + paths: _includePaths, + ); if (after(_list[i].object, object) == null) { _list[i].object = object.clone(object.toJson(full: true)); - _eventStreamController.sink.add(ParseLiveListUpdateEvent( - i, object.clone(object.toJson(full: true)))); + _eventStreamController.sink.add( + ParseLiveListUpdateEvent( + i, + object.clone(object.toJson(full: true)), + ), + ); } else { _list.removeAt(i).dispose(); - _eventStreamController.sink.add(ParseLiveListDeleteEvent( - i, object.clone(object.toJson(full: true)))); - await _objectAdded(object.clone(object.toJson(full: true)), - fetchedIncludes: true); + _eventStreamController.sink.add( + ParseLiveListDeleteEvent( + i, + object.clone(object.toJson(full: true)), + ), + ); + await _objectAdded( + object.clone(object.toJson(full: true)), + fetchedIncludes: true, + ); } break; } @@ -403,11 +469,18 @@ class ParseLiveList { for (int i = 0; i < _list.length; i++) { if (_list[i].object.get(keyVarObjectId) == object.get(keyVarObjectId)) { - await _loadIncludes(object, - oldObject: _list[i].object, paths: _includePaths); + await _loadIncludes( + object, + oldObject: _list[i].object, + paths: _includePaths, + ); _list.removeAt(i).dispose(); - _eventStreamController.sink.add(ParseLiveListDeleteEvent( - i, object.clone(object.toJson(full: true)))); + _eventStreamController.sink.add( + ParseLiveListDeleteEvent( + i, + object.clone(object.toJson(full: true)), + ), + ); break; } } @@ -418,12 +491,15 @@ class ParseLiveList { if (!_list[index].loaded) { final QueryBuilder queryBuilder = QueryBuilder.copy(_query) ..whereEqualTo( - keyVarObjectId, _list[index].object.get(keyVarObjectId)) + keyVarObjectId, + _list[index].object.get(keyVarObjectId), + ) ..setLimit(1); final ParseResponse response = await queryBuilder.query(); if (_list.isEmpty) { yield* _createStreamError( - ParseError(message: 'ParseLiveList: _list is empty')); + ParseError(message: 'ParseLiveList: _list is empty'), + ); return; } if (response.success) { @@ -434,8 +510,8 @@ class ParseLiveList { return; } } -// just for testing -// await Future.delayed(const Duration(seconds: 2)); + // just for testing + // await Future.delayed(const Duration(seconds: 2)); yield _list[index].object; yield* _list[index].stream; } @@ -490,10 +566,13 @@ class ParseLiveList { class ParseLiveElement extends ParseLiveListElement { ParseLiveElement(T object, {bool loaded = false, List? includeObject}) - : super(object, - loaded: loaded, - updatedSubItems: - ParseLiveList._toIncludeMap(includeObject ?? [])) { + : super( + object, + loaded: loaded, + updatedSubItems: ParseLiveList._toIncludeMap( + includeObject ?? [], + ), + ) { _includes = ParseLiveList._toIncludeMap(includeObject ?? []); queryBuilder = QueryBuilder(object.clone({})) ..whereEqualTo(keyVarObjectId, object.objectId); @@ -507,8 +586,11 @@ class ParseLiveElement extends ParseLiveListElement { Map? _includes; late QueryBuilder queryBuilder; - Future _init(T object, - {bool loaded = false, List? includeObject}) async { + Future _init( + T object, { + bool loaded = false, + List? includeObject, + }) async { if (!loaded) { final ParseResponse parseResponse = await queryBuilder.query(); if (parseResponse.success) { @@ -517,20 +599,23 @@ class ParseLiveElement extends ParseLiveListElement { } Subscription subscription = await LiveQuery().client.subscribe( - QueryBuilder.copy(queryBuilder), - copyObject: object.clone({})); + QueryBuilder.copy(queryBuilder), + copyObject: object.clone({}), + ); _subscription = subscription; subscription.on(LiveQueryEvent.update, (T newObject) async { - await ParseLiveList._loadIncludes(newObject, - oldObject: super.object, paths: _includes); + await ParseLiveList._loadIncludes( + newObject, + oldObject: super.object, + paths: _includes, + ); super.object = newObject; }); - LiveQuery() - .client - .getClientEventStream - .listen((LiveQueryClientEvent event) { + LiveQuery().client.getClientEventStream.listen(( + LiveQueryClientEvent event, + ) { _subscriptionQueue.whenComplete(() async { // ignore: missing_enum_constant_in_switch switch (event) { @@ -561,11 +646,14 @@ class ParseLiveElement extends ParseLiveListElement { } class ParseLiveListElement { - ParseLiveListElement(this._object, - {bool loaded = false, Map? updatedSubItems}) - : _loaded = loaded { - _updatedSubItems = - _toSubscriptionMap(updatedSubItems ?? {}); + ParseLiveListElement( + this._object, { + bool loaded = false, + Map? updatedSubItems, + }) : _loaded = loaded { + _updatedSubItems = _toSubscriptionMap( + updatedSubItems ?? {}, + ); if (_updatedSubItems.isNotEmpty) { _liveQuery = LiveQuery(); _subscribe(); @@ -605,8 +693,14 @@ class ParseLiveListElement { if (_updatedSubItems.isNotEmpty) { final List> tasks = >[]; for (PathKey key in _updatedSubItems.keys) { - tasks.add(_subscribeSubItem(object, key, - object.get(key.key), _updatedSubItems[key])); + tasks.add( + _subscribeSubItem( + object, + key, + object.get(key.key), + _updatedSubItems[key], + ), + ); } await Future.wait(tasks); } @@ -625,43 +719,64 @@ class ParseLiveListElement { } } - Future _subscribeSubItem(ParseObject parentObject, PathKey currentKey, - ParseObject? subObject, Map path) async { + Future _subscribeSubItem( + ParseObject parentObject, + PathKey currentKey, + ParseObject? subObject, + Map path, + ) async { LiveQuery? liveQuery = _liveQuery; if (liveQuery != null && subObject != null) { final List> tasks = >[]; for (PathKey key in path.keys) { - tasks.add(_subscribeSubItem( - subObject, key, subObject.get(key.key), path[key])); + tasks.add( + _subscribeSubItem( + subObject, + key, + subObject.get(key.key), + path[key], + ), + ); } - final QueryBuilder queryBuilder = - QueryBuilder(subObject) - ..whereEqualTo(keyVarObjectId, subObject.objectId); - - tasks.add(liveQuery.client - .subscribe(queryBuilder) - .then((Subscription subscription) { - currentKey.subscription = subscription; - subscription.on(LiveQueryEvent.update, (ParseObject newObject) async { - _subscriptionQueue.whenComplete(() async { - await ParseLiveList._loadIncludes(newObject, - oldObject: subObject, paths: _toKeyMap(path)); - // ignore: deprecated_member_use_from_same_package - parentObject[currentKey.key] = newObject; - if (!_streamController.isClosed) { - _streamController.add(object); - //Resubscribe subitems - // TODO(any): only resubscribe on changed pointers - _unsubscribe(path); - for (PathKey key in path.keys) { - tasks.add(_subscribeSubItem(newObject, key, - newObject.get(key.key), path[key])); + final QueryBuilder queryBuilder = QueryBuilder( + subObject, + )..whereEqualTo(keyVarObjectId, subObject.objectId); + + tasks.add( + liveQuery.client.subscribe(queryBuilder).then(( + Subscription subscription, + ) { + currentKey.subscription = subscription; + subscription.on(LiveQueryEvent.update, (ParseObject newObject) async { + _subscriptionQueue.whenComplete(() async { + await ParseLiveList._loadIncludes( + newObject, + oldObject: subObject, + paths: _toKeyMap(path), + ); + // ignore: deprecated_member_use_from_same_package + parentObject[currentKey.key] = newObject; + if (!_streamController.isClosed) { + _streamController.add(object); + //Resubscribe subitems + // TODO(any): only resubscribe on changed pointers + _unsubscribe(path); + for (PathKey key in path.keys) { + tasks.add( + _subscribeSubItem( + newObject, + key, + newObject.get(key.key), + path[key], + ), + ); + } } - } - await Future.wait(tasks); + await Future.wait(tasks); + }); }); - }); - })); + }), + ); await Future.wait(tasks); } } @@ -685,7 +800,7 @@ class ParseLiveListElement { if (loaded) { _subscriptionQueue.whenComplete(() async { await _updateSubItems(_object, _updatedSubItems); -// _streamController.add(_object?.clone(_object.toJson(full: true))); + // _streamController.add(_object?.clone(_object.toJson(full: true))); }); } } @@ -695,13 +810,16 @@ class ParseLiveListElement { for (PathKey key in path.keys) { includes.add(key.key); includes.addAll( - _getIncludeList(path[key]).map((String e) => '${key.key}.$e')); + _getIncludeList(path[key]).map((String e) => '${key.key}.$e'), + ); } return includes; } Future _updateSubItems( - ParseObject root, Map path) async { + ParseObject root, + Map path, + ) async { final List> tasks = >[]; for (PathKey key in path.keys) { ParseObject? subObject = root.get(key.key); @@ -721,7 +839,7 @@ class ParseLiveListElement { final ParseResponse parseResponse = await queryBuilder.query(); if (parseResponse.success) { subObject = parseResponse.result.first; -// root.getObjectData()[key.key] = subObject; + // root.getObjectData()[key.key] = subObject; Subscription? subscription = key.subscription; if (subscription != null && subscription.eventCallbacks.containsKey('update') == true) { @@ -730,7 +848,7 @@ class ParseLiveListElement { eventCallback(subObject); } } -// key.subscription.eventCallbacks["update"](subObject); + // key.subscription.eventCallbacks["update"](subObject); break; } } @@ -781,8 +899,11 @@ class ParseLiveListDeleteEvent } class ParseLiveListElementSnapshot { - ParseLiveListElementSnapshot( - {this.loadedData, this.error, this.preLoadedData}); + ParseLiveListElementSnapshot({ + this.loadedData, + this.error, + this.preLoadedData, + }); final T? loadedData; final T? preLoadedData; diff --git a/packages/dart/lib/src/utils/parse_logger.dart b/packages/dart/lib/src/utils/parse_logger.dart index e67bf6b8d..bb6c2c898 100644 --- a/packages/dart/lib/src/utils/parse_logger.dart +++ b/packages/dart/lib/src/utils/parse_logger.dart @@ -1,7 +1,10 @@ part of '../../parse_server_sdk.dart'; void logAPIResponse( - String className, String type, ParseResponse parseResponse) { + String className, + String type, + ParseResponse parseResponse, +) { const String spacer = ' \n'; String responseString = ''; @@ -20,8 +23,9 @@ void logAPIResponse( responseString += '\nStatus Code: ${parseResponse.error!.code}'; responseString += '\nType: ${parseResponse.error!.type}'; - final String errorOrException = - parseResponse.error!.exception != null ? 'Exception' : 'Error'; + final String errorOrException = parseResponse.error!.exception != null + ? 'Exception' + : 'Error'; responseString += '\n$errorOrException: ${parseResponse.error!.message}'; } @@ -32,7 +36,12 @@ void logAPIResponse( } void logRequest( - String? appName, String className, String type, String uri, String body) { + String? appName, + String className, + String type, + String uri, + String body, +) { String requestString = ' \n'; final String name = appName != null ? '$appName ' : ''; requestString += '----\n${name}API Request ($className : $type) :'; diff --git a/packages/dart/lib/src/utils/parse_login_helpers.dart b/packages/dart/lib/src/utils/parse_login_helpers.dart index 8147bf30c..5f5dc04a9 100644 --- a/packages/dart/lib/src/utils/parse_login_helpers.dart +++ b/packages/dart/lib/src/utils/parse_login_helpers.dart @@ -4,7 +4,7 @@ Map facebook(String token, String id, DateTime expires) { return { 'access_token': token, 'id': id, - 'expiration_date': expires.toString() + 'expiration_date': expires.toString(), }; } @@ -12,7 +12,7 @@ Map google(String token, String id, String idToken) { return { 'access_token': token, 'id': id, - 'id_token': idToken + 'id_token': idToken, }; } diff --git a/packages/dart/lib/src/utils/parse_utils.dart b/packages/dart/lib/src/utils/parse_utils.dart index 13dee46e5..32e018f42 100644 --- a/packages/dart/lib/src/utils/parse_utils.dart +++ b/packages/dart/lib/src/utils/parse_utils.dart @@ -39,33 +39,43 @@ dynamic convertValueToCorrectType(dynamic value) { } /// Sanitises a url -Uri getSanitisedUri(ParseClient client, String pathToAppend, - {Map? queryParams, String? query}) { +Uri getSanitisedUri( + ParseClient client, + String pathToAppend, { + Map? queryParams, + String? query, +}) { final Uri tempUri = Uri.parse(ParseCoreData().serverUrl); final Uri url = Uri( - scheme: tempUri.scheme, - host: tempUri.host, - port: tempUri.port, - path: '${tempUri.path}$pathToAppend', - queryParameters: queryParams, - query: query); + scheme: tempUri.scheme, + host: tempUri.host, + port: tempUri.port, + path: '${tempUri.path}$pathToAppend', + queryParameters: queryParams, + query: query, + ); return url; } /// Sanitises a url -Uri getCustomUri(ParseClient client, String path, - {Map? queryParams, String? query}) { +Uri getCustomUri( + ParseClient client, + String path, { + Map? queryParams, + String? query, +}) { final Uri tempUri = Uri.parse(ParseCoreData().serverUrl); final Uri url = Uri( - scheme: tempUri.scheme, - host: tempUri.host, - port: tempUri.port, - path: path, - queryParameters: queryParams, - query: query); + scheme: tempUri.scheme, + host: tempUri.host, + port: tempUri.port, + path: path, + queryParameters: queryParams, + query: query, + ); return url; } @@ -81,21 +91,33 @@ String removeTrailingSlash(String serverUrl) { } Future batchRequest( - List requests, List objects, - {ParseClient? client, bool? debug}) async { + List requests, + List objects, { + ParseClient? client, + bool? debug, +}) async { debug = isDebugEnabled(objectLevelDebug: debug); - client = client ?? + client = + client ?? ParseCoreData().clientCreator( - sendSessionId: ParseCoreData().autoSendSessionId, - securityContext: ParseCoreData().securityContext); + sendSessionId: ParseCoreData().autoSendSessionId, + securityContext: ParseCoreData().securityContext, + ); try { final Uri url = getSanitisedUri(client, '/batch'); final String body = json.encode({'requests': requests}); - final ParseNetworkResponse result = - await client.post(url.toString(), data: body); + final ParseNetworkResponse result = await client.post( + url.toString(), + data: body, + ); return handleResponse( - objects, result, ParseApiRQ.batch, debug, 'parse_utils'); + objects, + result, + ParseApiRQ.batch, + debug, + 'parse_utils', + ); } on Exception catch (e) { return handleException(e, ParseApiRQ.batch, debug, 'parse_utils'); } @@ -111,17 +133,12 @@ List removeDuplicateParseObjectByObjectId(Iterable iterable) { final foldedGroupedByObjectId = list .whereType() .where((e) => e.objectId != null) - .groupFoldBy( - (e) => e.objectId!, - (previous, element) => element, - ); + .groupFoldBy((e) => e.objectId!, (previous, element) => element); - list.removeWhere( - (e) { - return e is ParseObject && - foldedGroupedByObjectId.keys.contains(e.objectId); - }, - ); + list.removeWhere((e) { + return e is ParseObject && + foldedGroupedByObjectId.keys.contains(e.objectId); + }); list.addAll(foldedGroupedByObjectId.values); @@ -141,8 +158,9 @@ Future checkObjectsExistForEventually() async { } } - List? listDeletes = - await coreStore.getStringList(keyParseStoreDeletes); + List? listDeletes = await coreStore.getStringList( + keyParseStoreDeletes, + ); if (listDeletes != null) { if (listDeletes.isNotEmpty) { diff --git a/packages/dart/test/parse_query_test.mocks.dart b/packages/dart/test/parse_query_test.mocks.dart index f60d00e1a..8fc8a4312 100644 --- a/packages/dart/test/parse_query_test.mocks.dart +++ b/packages/dart/test/parse_query_test.mocks.dart @@ -26,78 +26,114 @@ class MockParseClient extends i1.Mock implements i2.ParseClient { } @override - i2.ParseCoreData get data => (super.noSuchMethod(Invocation.getter(#data), - returnValue: _FakeParseCoreData()) as i2.ParseCoreData); + i2.ParseCoreData get data => + (super.noSuchMethod( + Invocation.getter(#data), + returnValue: _FakeParseCoreData(), + ) + as i2.ParseCoreData); @override - i3.Future get(String? path, - {i2.ParseNetworkOptions? options, - i2.ProgressCallback? onReceiveProgress}) => + i3.Future get( + String? path, { + i2.ParseNetworkOptions? options, + i2.ProgressCallback? onReceiveProgress, + }) => (super.noSuchMethod( - Invocation.method(#get, [path], - {#options: options, #onReceiveProgress: onReceiveProgress}), - returnValue: Future.value( - _FakeParseNetworkResponse())) + Invocation.method( + #get, + [path], + {#options: options, #onReceiveProgress: onReceiveProgress}, + ), + returnValue: Future.value( + _FakeParseNetworkResponse(), + ), + ) as i3.Future); @override - i3.Future put(String? path, - {String? data, i2.ParseNetworkOptions? options}) => + i3.Future put( + String? path, { + String? data, + i2.ParseNetworkOptions? options, + }) => (super.noSuchMethod( - Invocation.method(#put, [path], {#data: data, #options: options}), - returnValue: Future.value( - _FakeParseNetworkResponse())) + Invocation.method(#put, [path], {#data: data, #options: options}), + returnValue: Future.value( + _FakeParseNetworkResponse(), + ), + ) as i3.Future); @override - i3.Future post(String? path, - {String? data, i2.ParseNetworkOptions? options}) => - super.noSuchMethod( - Invocation.method(#post, [path], {#data: data, #options: options}), - returnValue: Future.value( - _FakeParseNetworkResponse())); + i3.Future post( + String? path, { + String? data, + i2.ParseNetworkOptions? options, + }) => super.noSuchMethod( + Invocation.method(#post, [path], {#data: data, #options: options}), + returnValue: Future.value( + _FakeParseNetworkResponse(), + ), + ); @override - i3.Future postBytes(String? path, - {i3.Stream>? data, - i2.ParseNetworkOptions? options, - i2.ProgressCallback? onSendProgress, - dynamic cancelToken}) => + i3.Future postBytes( + String? path, { + i3.Stream>? data, + i2.ParseNetworkOptions? options, + i2.ProgressCallback? onSendProgress, + dynamic cancelToken, + }) => (super.noSuchMethod( - Invocation.method(#postBytes, [ - path - ], { + Invocation.method( + #postBytes, + [path], + { #data: data, #options: options, #onSendProgress: onSendProgress, - #cancelToken: cancelToken - }), - returnValue: Future.value( - _FakeParseNetworkResponse())) + #cancelToken: cancelToken, + }, + ), + returnValue: Future.value( + _FakeParseNetworkResponse(), + ), + ) as i3.Future); @override - i3.Future delete(String? path, - {i2.ParseNetworkOptions? options}) => + i3.Future delete( + String? path, { + i2.ParseNetworkOptions? options, + }) => (super.noSuchMethod( - Invocation.method(#delete, [path], {#options: options}), - returnValue: Future.value( - _FakeParseNetworkResponse())) + Invocation.method(#delete, [path], {#options: options}), + returnValue: Future.value( + _FakeParseNetworkResponse(), + ), + ) as i3.Future); @override - i3.Future getBytes(String? path, - {i2.ParseNetworkOptions? options, - i2.ProgressCallback? onReceiveProgress, - dynamic cancelToken}) => + i3.Future getBytes( + String? path, { + i2.ParseNetworkOptions? options, + i2.ProgressCallback? onReceiveProgress, + dynamic cancelToken, + }) => (super.noSuchMethod( - Invocation.method(#getBytes, [ - path - ], { + Invocation.method( + #getBytes, + [path], + { #options: options, #onReceiveProgress: onReceiveProgress, - #cancelToken: cancelToken - }), - returnValue: Future.value( - _FakeParseNetworkByteResponse())) + #cancelToken: cancelToken, + }, + ), + returnValue: Future.value( + _FakeParseNetworkByteResponse(), + ), + ) as i3.Future); } diff --git a/packages/dart/test/src/network/parse_live_query_test.dart b/packages/dart/test/src/network/parse_live_query_test.dart index a7dc928e8..a6259be89 100644 --- a/packages/dart/test/src/network/parse_live_query_test.dart +++ b/packages/dart/test/src/network/parse_live_query_test.dart @@ -31,16 +31,18 @@ void main() { test('should exist installationId in connect LiveQuery', () async { // arrange - QueryBuilder query = - QueryBuilder(ParseObject('Test')); + QueryBuilder query = QueryBuilder( + ParseObject('Test'), + ); // Set installationId ParseInstallation parseInstallation = ParseInstallation(); parseInstallation.set(keyInstallationId, "1234"); final String objectJson = json.encode(parseInstallation.toJson(full: true)); - await ParseCoreData() - .getStore() - .setString(keyParseStoreInstallation, objectJson); + await ParseCoreData().getStore().setString( + keyParseStoreInstallation, + objectJson, + ); // Initialize LiveQuery final LiveQuery liveQuery = LiveQuery(); diff --git a/packages/dart/test/src/network/parse_query_test.dart b/packages/dart/test/src/network/parse_query_test.dart index 394ef73b4..2448ee278 100644 --- a/packages/dart/test/src/network/parse_query_test.dart +++ b/packages/dart/test/src/network/parse_query_test.dart @@ -23,8 +23,9 @@ void main() { test('whereRelatedTo', () async { // arrange - final QueryBuilder queryBuilder = - QueryBuilder(ParseObject('_User', client: client)); + final QueryBuilder queryBuilder = QueryBuilder( + ParseObject('_User', client: client), + ); queryBuilder.whereRelatedTo('likes', 'Post', '8TOXdXf3tz'); var desiredOutput = { @@ -36,29 +37,39 @@ void main() { "updatedAt": "2021-04-23T13:46:23.586Z", "ACL": { "*": {"read": true}, - "eT9muOxBTJ": {"read": true, "write": true} + "eT9muOxBTJ": {"read": true, "write": true}, }, - } - ] + }, + ], }; - when(client.get( - any, - options: anyNamed("options"), - onReceiveProgress: anyNamed("onReceiveProgress"), - )).thenAnswer((_) async => ParseNetworkResponse( - statusCode: 200, data: jsonEncode(desiredOutput))); + when( + client.get( + any, + options: anyNamed("options"), + onReceiveProgress: anyNamed("onReceiveProgress"), + ), + ).thenAnswer( + (_) async => ParseNetworkResponse( + statusCode: 200, + data: jsonEncode(desiredOutput), + ), + ); // act ParseResponse response = await queryBuilder.query(); ParseObject parseObject = response.results?.first; - final Uri result = Uri.parse(verify(client.get( - captureAny, - options: anyNamed("options"), - onReceiveProgress: anyNamed("onReceiveProgress"), - )).captured.single); + final Uri result = Uri.parse( + verify( + client.get( + captureAny, + options: anyNamed("options"), + onReceiveProgress: anyNamed("onReceiveProgress"), + ), + ).captured.single, + ); var queryDesiredOutput = { "\$relatedTo": { @@ -67,11 +78,12 @@ void main() { "className": "Post", "objectId": "8TOXdXf3tz", }, - "key": "likes" + "key": "likes", }, }; - final Uri expectedQuery = - Uri(query: 'where=${jsonEncode(queryDesiredOutput)}'); + final Uri expectedQuery = Uri( + query: 'where=${jsonEncode(queryDesiredOutput)}', + ); // assert expect(response.results?.first, isA()); @@ -95,10 +107,10 @@ void main() { var lastName = QueryBuilder(user) ..regEx('lastName', "Johnson"); - QueryBuilder mainQuery = QueryBuilder.or( - user, - [firstName, lastName], - ); + QueryBuilder mainQuery = QueryBuilder.or(user, [ + firstName, + lastName, + ]); var desiredOutput = { "results": [ @@ -117,40 +129,51 @@ void main() { "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: jsonEncode(desiredOutput))); + when( + client.get( + any, + options: anyNamed("options"), + onReceiveProgress: anyNamed("onReceiveProgress"), + ), + ).thenAnswer( + (_) async => ParseNetworkResponse( + statusCode: 200, + data: jsonEncode(desiredOutput), + ), + ); // act var response = await mainQuery.query(); ParseObject parseObject = response.results?.first; - final Uri result = Uri.parse(verify(client.get( - captureAny, - options: anyNamed("options"), - onReceiveProgress: anyNamed("onReceiveProgress"), - )).captured.single); + final Uri result = Uri.parse( + verify( + client.get( + captureAny, + options: anyNamed("options"), + onReceiveProgress: anyNamed("onReceiveProgress"), + ), + ).captured.single, + ); var queryDesiredOutput = { "\$or": [ { - "firstName": {"\$regex": "Liam"} + "firstName": {"\$regex": "Liam"}, }, { "lastName": {"\$regex": "Johnson"}, - } + }, ], }; - final Uri expectedQuery = - Uri(query: 'where=${jsonEncode(queryDesiredOutput)}'); + final Uri expectedQuery = Uri( + query: 'where=${jsonEncode(queryDesiredOutput)}', + ); // assert expect(response.results?.first, isA()); @@ -173,10 +196,10 @@ void main() { var lastName = QueryBuilder(user)..regEx('lastName', "jaki"); - QueryBuilder mainQuery = QueryBuilder.and( - user, - [firstName, lastName], - ); + QueryBuilder mainQuery = QueryBuilder.and(user, [ + firstName, + lastName, + ]); var desiredOutput = { "results": [ @@ -196,39 +219,50 @@ void main() { "firstName": "jak2", "lastName": "jaki2", }, - ] + ], }; - when(client.get( - any, - options: anyNamed("options"), - onReceiveProgress: anyNamed("onReceiveProgress"), - )).thenAnswer((_) async => ParseNetworkResponse( - statusCode: 200, data: jsonEncode(desiredOutput))); + when( + client.get( + any, + options: anyNamed("options"), + onReceiveProgress: anyNamed("onReceiveProgress"), + ), + ).thenAnswer( + (_) async => ParseNetworkResponse( + statusCode: 200, + data: jsonEncode(desiredOutput), + ), + ); // act var response = await mainQuery.query(); ParseObject parseObject = response.results?.first; - final Uri result = Uri.parse(verify(client.get( - captureAny, - options: anyNamed("options"), - onReceiveProgress: anyNamed("onReceiveProgress"), - )).captured.single); + final Uri result = Uri.parse( + verify( + client.get( + captureAny, + options: anyNamed("options"), + onReceiveProgress: anyNamed("onReceiveProgress"), + ), + ).captured.single, + ); var queryDesiredOutput = { "\$and": [ { - "firstName": {"\$regex": "jak"} + "firstName": {"\$regex": "jak"}, }, { "lastName": {"\$regex": "jaki"}, - } + }, ], }; - final Uri expectedQuery = - Uri(query: 'where=${jsonEncode(queryDesiredOutput)}'); + final Uri expectedQuery = Uri( + query: 'where=${jsonEncode(queryDesiredOutput)}', + ); // assert expect(response.results?.first, isA()); @@ -252,10 +286,10 @@ void main() { var lastName = QueryBuilder(user) ..regEx('lastName', "Smith"); - QueryBuilder mainQuery = QueryBuilder.nor( - user, - [firstName, lastName], - ); + QueryBuilder mainQuery = QueryBuilder.nor(user, [ + firstName, + lastName, + ]); var desiredOutput = { "results": [ @@ -275,39 +309,50 @@ void main() { "firstName": "Oliver2", "lastName": "Smith2", }, - ] + ], }; - when(client.get( - any, - options: anyNamed("options"), - onReceiveProgress: anyNamed("onReceiveProgress"), - )).thenAnswer((_) async => ParseNetworkResponse( - statusCode: 200, data: jsonEncode(desiredOutput))); + when( + client.get( + any, + options: anyNamed("options"), + onReceiveProgress: anyNamed("onReceiveProgress"), + ), + ).thenAnswer( + (_) async => ParseNetworkResponse( + statusCode: 200, + data: jsonEncode(desiredOutput), + ), + ); // act var response = await mainQuery.query(); ParseObject parseObject = response.results?.first; - final Uri result = Uri.parse(verify(client.get( - captureAny, - options: anyNamed("options"), - onReceiveProgress: anyNamed("onReceiveProgress"), - )).captured.single); + final Uri result = Uri.parse( + verify( + client.get( + captureAny, + options: anyNamed("options"), + onReceiveProgress: anyNamed("onReceiveProgress"), + ), + ).captured.single, + ); var queryDesiredOutput = { "\$nor": [ { - "firstName": {"\$regex": "Oliver"} + "firstName": {"\$regex": "Oliver"}, }, { "lastName": {"\$regex": "Smith"}, - } + }, ], }; - final Uri expectedQuery = - Uri(query: 'where=${jsonEncode(queryDesiredOutput)}'); + final Uri expectedQuery = Uri( + query: 'where=${jsonEncode(queryDesiredOutput)}', + ); // assert expect(response.results?.first, isA()); @@ -324,12 +369,15 @@ void main() { test('wherePolygonContains', () async { // arrange - final QueryBuilder queryBuilder = - QueryBuilder(ParseObject('TEST_SCHEMA', client: client)); + final QueryBuilder queryBuilder = QueryBuilder( + ParseObject('TEST_SCHEMA', client: client), + ); double latitude = 84.17724609375; double longitude = -53.69670647530323; - ParseGeoPoint point = - ParseGeoPoint(latitude: latitude, longitude: longitude); + ParseGeoPoint point = ParseGeoPoint( + latitude: latitude, + longitude: longitude, + ); queryBuilder.wherePolygonContains("geometry", point); var desiredOutput = { @@ -346,30 +394,40 @@ void main() { [83.1884765625, -54.61025498157913], [84.814453125, -55.14120964449505], [85.67138671875, -54.40614309031968], - [84.17724609375, -53.69670647530323] - ] - ] - } - } - ] + [84.17724609375, -53.69670647530323], + ], + ], + }, + }, + ], }; - when(client.get( - any, - options: anyNamed("options"), - onReceiveProgress: anyNamed("onReceiveProgress"), - )).thenAnswer((_) async => ParseNetworkResponse( - statusCode: 200, data: jsonEncode(desiredOutput))); + when( + client.get( + any, + options: anyNamed("options"), + onReceiveProgress: anyNamed("onReceiveProgress"), + ), + ).thenAnswer( + (_) async => ParseNetworkResponse( + statusCode: 200, + data: jsonEncode(desiredOutput), + ), + ); // act ParseResponse response = await queryBuilder.query(); ParseObject parseObject = response.results?.first; - final Uri result = Uri.parse(verify(client.get( - captureAny, - options: anyNamed("options"), - onReceiveProgress: anyNamed("onReceiveProgress"), - )).captured.single); + final Uri result = Uri.parse( + verify( + client.get( + captureAny, + options: anyNamed("options"), + onReceiveProgress: anyNamed("onReceiveProgress"), + ), + ).captured.single, + ); var queryDesiredOutput = { "geometry": { @@ -377,13 +435,14 @@ void main() { "\$point": { "__type": "GeoPoint", "latitude": latitude, - "longitude": longitude - } - } - } + "longitude": longitude, + }, + }, + }, }; - final Uri expectedQuery = - Uri(query: 'where=${jsonEncode(queryDesiredOutput)}'); + final Uri expectedQuery = Uri( + query: 'where=${jsonEncode(queryDesiredOutput)}', + ); // assert expect(response.results?.first, isA()); @@ -394,19 +453,20 @@ void main() { }); test( - 'The resulting query should include "redirectClassNameForKey" as a query parameter', - () { - // arrange - final queryBuilder = QueryBuilder.name('Diet_Plans'); + 'The resulting query should include "redirectClassNameForKey" as a query parameter', + () { + // arrange + final queryBuilder = QueryBuilder.name('Diet_Plans'); - // act - queryBuilder.setRedirectClassNameForKey('Plan'); + // act + queryBuilder.setRedirectClassNameForKey('Plan'); - // assert - final query = queryBuilder.buildQuery(); + // assert + final query = queryBuilder.buildQuery(); - expect(query, equals('where={}&redirectClassNameForKey=Plan')); - }); + expect(query, equals('where={}&redirectClassNameForKey=Plan')); + }, + ); test('whereMatchesQuery', () async { // arrange @@ -444,31 +504,40 @@ void main() { "firstName": "Oliver2", "lastName": "Smith2", }, - ] + ], }; - when(client.get( - any, - options: anyNamed("options"), - onReceiveProgress: anyNamed("onReceiveProgress"), - )).thenAnswer((_) async => ParseNetworkResponse( - statusCode: 200, data: jsonEncode(desiredOutput))); + when( + client.get( + any, + options: anyNamed("options"), + onReceiveProgress: anyNamed("onReceiveProgress"), + ), + ).thenAnswer( + (_) async => ParseNetworkResponse( + statusCode: 200, + data: jsonEncode(desiredOutput), + ), + ); // act await mainQuery.query(); - final Uri result = Uri.parse(verify(client.get( - captureAny, - options: anyNamed("options"), - onReceiveProgress: anyNamed("onReceiveProgress"), - )).captured.single); + final Uri result = Uri.parse( + verify( + client.get( + captureAny, + options: anyNamed("options"), + onReceiveProgress: anyNamed("onReceiveProgress"), + ), + ).captured.single, + ); // assert expect(result.query.contains("%22object2%22,%22%22include%22"), true); }); - test('the result query should contains encoded special characters values', - () { + test('the result query should contains encoded special characters values', () { // arrange final queryBuilder = QueryBuilder.name('Diet_Plans'); diff --git a/packages/dart/test/src/network/parse_query_test.mocks.dart b/packages/dart/test/src/network/parse_query_test.mocks.dart index 87bb7d7f3..4cdbf3808 100644 --- a/packages/dart/test/src/network/parse_query_test.mocks.dart +++ b/packages/dart/test/src/network/parse_query_test.mocks.dart @@ -24,35 +24,20 @@ import 'package:parse_server_sdk/parse_server_sdk.dart' as _i2; // ignore_for_file: invalid_use_of_internal_member class _FakeParseCoreData_0 extends _i1.SmartFake implements _i2.ParseCoreData { - _FakeParseCoreData_0( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); + _FakeParseCoreData_0(Object parent, Invocation parentInvocation) + : super(parent, parentInvocation); } class _FakeParseNetworkResponse_1 extends _i1.SmartFake implements _i2.ParseNetworkResponse { - _FakeParseNetworkResponse_1( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); + _FakeParseNetworkResponse_1(Object parent, Invocation parentInvocation) + : super(parent, parentInvocation); } class _FakeParseNetworkByteResponse_2 extends _i1.SmartFake implements _i2.ParseNetworkByteResponse { - _FakeParseNetworkByteResponse_2( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); + _FakeParseNetworkByteResponse_2(Object parent, Invocation parentInvocation) + : super(parent, parentInvocation); } /// A class which mocks [ParseClient]. @@ -64,13 +49,12 @@ class MockParseClient extends _i1.Mock implements _i2.ParseClient { } @override - _i2.ParseCoreData get data => (super.noSuchMethod( - Invocation.getter(#data), - returnValue: _FakeParseCoreData_0( - this, - Invocation.getter(#data), - ), - ) as _i2.ParseCoreData); + _i2.ParseCoreData get data => + (super.noSuchMethod( + Invocation.getter(#data), + returnValue: _FakeParseCoreData_0(this, Invocation.getter(#data)), + ) + as _i2.ParseCoreData); @override _i3.Future<_i2.ParseNetworkResponse> get( @@ -79,27 +63,23 @@ class MockParseClient extends _i1.Mock implements _i2.ParseClient { _i2.ProgressCallback? onReceiveProgress, }) => (super.noSuchMethod( - Invocation.method( - #get, - [path], - { - #options: options, - #onReceiveProgress: onReceiveProgress, - }, - ), - returnValue: _i3.Future<_i2.ParseNetworkResponse>.value( - _FakeParseNetworkResponse_1( - this, - Invocation.method( - #get, - [path], - { - #options: options, - #onReceiveProgress: onReceiveProgress, - }, - ), - )), - ) as _i3.Future<_i2.ParseNetworkResponse>); + Invocation.method( + #get, + [path], + {#options: options, #onReceiveProgress: onReceiveProgress}, + ), + returnValue: _i3.Future<_i2.ParseNetworkResponse>.value( + _FakeParseNetworkResponse_1( + this, + Invocation.method( + #get, + [path], + {#options: options, #onReceiveProgress: onReceiveProgress}, + ), + ), + ), + ) + as _i3.Future<_i2.ParseNetworkResponse>); @override _i3.Future<_i2.ParseNetworkResponse> put( @@ -108,27 +88,19 @@ class MockParseClient extends _i1.Mock implements _i2.ParseClient { _i2.ParseNetworkOptions? options, }) => (super.noSuchMethod( - Invocation.method( - #put, - [path], - { - #data: data, - #options: options, - }, - ), - returnValue: _i3.Future<_i2.ParseNetworkResponse>.value( - _FakeParseNetworkResponse_1( - this, - Invocation.method( - #put, - [path], - { - #data: data, - #options: options, - }, - ), - )), - ) as _i3.Future<_i2.ParseNetworkResponse>); + Invocation.method(#put, [path], {#data: data, #options: options}), + returnValue: _i3.Future<_i2.ParseNetworkResponse>.value( + _FakeParseNetworkResponse_1( + this, + Invocation.method( + #put, + [path], + {#data: data, #options: options}, + ), + ), + ), + ) + as _i3.Future<_i2.ParseNetworkResponse>); @override _i3.Future<_i2.ParseNetworkResponse> post( @@ -137,27 +109,19 @@ class MockParseClient extends _i1.Mock implements _i2.ParseClient { _i2.ParseNetworkOptions? options, }) => (super.noSuchMethod( - Invocation.method( - #post, - [path], - { - #data: data, - #options: options, - }, - ), - returnValue: _i3.Future<_i2.ParseNetworkResponse>.value( - _FakeParseNetworkResponse_1( - this, - Invocation.method( - #post, - [path], - { - #data: data, - #options: options, - }, - ), - )), - ) as _i3.Future<_i2.ParseNetworkResponse>); + Invocation.method(#post, [path], {#data: data, #options: options}), + returnValue: _i3.Future<_i2.ParseNetworkResponse>.value( + _FakeParseNetworkResponse_1( + this, + Invocation.method( + #post, + [path], + {#data: data, #options: options}, + ), + ), + ), + ) + as _i3.Future<_i2.ParseNetworkResponse>); @override _i3.Future<_i2.ParseNetworkResponse> postBytes( @@ -168,31 +132,33 @@ class MockParseClient extends _i1.Mock implements _i2.ParseClient { dynamic cancelToken, }) => (super.noSuchMethod( - Invocation.method( - #postBytes, - [path], - { - #data: data, - #options: options, - #onSendProgress: onSendProgress, - #cancelToken: cancelToken, - }, - ), - returnValue: _i3.Future<_i2.ParseNetworkResponse>.value( - _FakeParseNetworkResponse_1( - this, - Invocation.method( - #postBytes, - [path], - { - #data: data, - #options: options, - #onSendProgress: onSendProgress, - #cancelToken: cancelToken, - }, - ), - )), - ) as _i3.Future<_i2.ParseNetworkResponse>); + Invocation.method( + #postBytes, + [path], + { + #data: data, + #options: options, + #onSendProgress: onSendProgress, + #cancelToken: cancelToken, + }, + ), + returnValue: _i3.Future<_i2.ParseNetworkResponse>.value( + _FakeParseNetworkResponse_1( + this, + Invocation.method( + #postBytes, + [path], + { + #data: data, + #options: options, + #onSendProgress: onSendProgress, + #cancelToken: cancelToken, + }, + ), + ), + ), + ) + as _i3.Future<_i2.ParseNetworkResponse>); @override _i3.Future<_i2.ParseNetworkResponse> delete( @@ -200,21 +166,15 @@ class MockParseClient extends _i1.Mock implements _i2.ParseClient { _i2.ParseNetworkOptions? options, }) => (super.noSuchMethod( - Invocation.method( - #delete, - [path], - {#options: options}, - ), - returnValue: _i3.Future<_i2.ParseNetworkResponse>.value( - _FakeParseNetworkResponse_1( - this, - Invocation.method( - #delete, - [path], - {#options: options}, - ), - )), - ) as _i3.Future<_i2.ParseNetworkResponse>); + Invocation.method(#delete, [path], {#options: options}), + returnValue: _i3.Future<_i2.ParseNetworkResponse>.value( + _FakeParseNetworkResponse_1( + this, + Invocation.method(#delete, [path], {#options: options}), + ), + ), + ) + as _i3.Future<_i2.ParseNetworkResponse>); @override _i3.Future<_i2.ParseNetworkByteResponse> getBytes( @@ -224,27 +184,29 @@ class MockParseClient extends _i1.Mock implements _i2.ParseClient { dynamic cancelToken, }) => (super.noSuchMethod( - Invocation.method( - #getBytes, - [path], - { - #options: options, - #onReceiveProgress: onReceiveProgress, - #cancelToken: cancelToken, - }, - ), - returnValue: _i3.Future<_i2.ParseNetworkByteResponse>.value( - _FakeParseNetworkByteResponse_2( - this, - Invocation.method( - #getBytes, - [path], - { - #options: options, - #onReceiveProgress: onReceiveProgress, - #cancelToken: cancelToken, - }, - ), - )), - ) as _i3.Future<_i2.ParseNetworkByteResponse>); + Invocation.method( + #getBytes, + [path], + { + #options: options, + #onReceiveProgress: onReceiveProgress, + #cancelToken: cancelToken, + }, + ), + returnValue: _i3.Future<_i2.ParseNetworkByteResponse>.value( + _FakeParseNetworkByteResponse_2( + this, + Invocation.method( + #getBytes, + [path], + { + #options: options, + #onReceiveProgress: onReceiveProgress, + #cancelToken: cancelToken, + }, + ), + ), + ), + ) + as _i3.Future<_i2.ParseNetworkByteResponse>); } diff --git a/packages/dart/test/src/objects/parse_base_test.dart b/packages/dart/test/src/objects/parse_base_test.dart index 82ba945a3..fd5182b24 100644 --- a/packages/dart/test/src/objects/parse_base_test.dart +++ b/packages/dart/test/src/objects/parse_base_test.dart @@ -66,7 +66,7 @@ main() { "somePointer": { "__type": "Object", "className": "Plan", - "name": "plan1" + "name": "plan1", }, }); @@ -83,30 +83,32 @@ main() { }); test( - 'should return true for containsValue() if the object contains the value', - () { - // arrange - dietPlansObject.set('someKey', 1); + 'should return true for containsValue() if the object contains the value', + () { + // arrange + dietPlansObject.set('someKey', 1); - // act - final containsValue = dietPlansObject.containsValue(1); + // act + final containsValue = dietPlansObject.containsValue(1); - // assert - expect(containsValue, isTrue); - }); + // assert + expect(containsValue, isTrue); + }, + ); test( - 'should return true for containsKey() if the object contains the passed key', - () { - // arrange - dietPlansObject.set('someKey', 1); + 'should return true for containsKey() if the object contains the passed key', + () { + // arrange + dietPlansObject.set('someKey', 1); - // act - final containsKey = dietPlansObject.containsKey('someKey'); + // act + final containsKey = dietPlansObject.containsKey('someKey'); - // assert - expect(containsKey, isTrue); - }); + // assert + expect(containsKey, isTrue); + }, + ); test('test the [] operator', () { // arrange @@ -130,41 +132,43 @@ main() { expect(dietPlansObject.getACL(), equals(acl)); }); - test('fromJsonForManualObject() should put all the values in unsaved state', - () { - // arrange - final createdAt = DateTime.now(); - final updatedAt = DateTime.now(); - final manualJsonObject = { - keyVarCreatedAt: createdAt, - keyVarUpdatedAt: updatedAt, - "array": [1, 2, 3], - 'number': 2, - }; + test( + 'fromJsonForManualObject() should put all the values in unsaved state', + () { + // arrange + final createdAt = DateTime.now(); + final updatedAt = DateTime.now(); + final manualJsonObject = { + keyVarCreatedAt: createdAt, + keyVarUpdatedAt: updatedAt, + "array": [1, 2, 3], + 'number': 2, + }; - // act - dietPlansObject.fromJsonForManualObject(manualJsonObject); + // act + dietPlansObject.fromJsonForManualObject(manualJsonObject); - // assert - expect(dietPlansObject.isDirty(key: 'array'), isTrue); - expect(dietPlansObject.isDirty(key: 'number'), isTrue); - - expect(dietPlansObject.createdAt, equals(createdAt)); - expect(dietPlansObject.updatedAt, equals(updatedAt)); - - final valueForAPiRequest = dietPlansObject.toJson(forApiRQ: true); - final expectedValueForAPiRequest = { - "array": [1, 2, 3], - "number": 2 - }; - - expect( - DeepCollectionEquality().equals( - valueForAPiRequest, - expectedValueForAPiRequest, - ), - isTrue, - ); - }); + // assert + expect(dietPlansObject.isDirty(key: 'array'), isTrue); + expect(dietPlansObject.isDirty(key: 'number'), isTrue); + + expect(dietPlansObject.createdAt, equals(createdAt)); + expect(dietPlansObject.updatedAt, equals(updatedAt)); + + final valueForAPiRequest = dietPlansObject.toJson(forApiRQ: true); + final expectedValueForAPiRequest = { + "array": [1, 2, 3], + "number": 2, + }; + + expect( + DeepCollectionEquality().equals( + valueForAPiRequest, + expectedValueForAPiRequest, + ), + isTrue, + ); + }, + ); }); } diff --git a/packages/dart/test/src/objects/parse_object/parse_object_array_test.dart b/packages/dart/test/src/objects/parse_object/parse_object_array_test.dart index 07f636d80..49c07f134 100644 --- a/packages/dart/test/src/objects/parse_object/parse_object_array_test.dart +++ b/packages/dart/test/src/objects/parse_object/parse_object_array_test.dart @@ -13,25 +13,23 @@ void main() { await initializeParse(); }); - group( - 'Array', - () { - late MockParseClient client; + group('Array', () { + late MockParseClient client; - late ParseObject dietPlansObject; + late ParseObject dietPlansObject; - const keyArray = 'array'; + const keyArray = 'array'; - setUp(() { - client = MockParseClient(); + setUp(() { + client = MockParseClient(); - dietPlansObject = ParseObject("Diet_Plans", client: client); - }); + dietPlansObject = ParseObject("Diet_Plans", client: client); + }); - test( - 'adding values using setAdd() and then calling get(keyArray) ' - 'should return Instance of Iterable that contains all the added values ', - () { + test( + 'adding values using setAdd() and then calling get(keyArray) ' + 'should return Instance of Iterable that contains all the added values ', + () { // act dietPlansObject.setAdd(keyArray, 1); dietPlansObject.setAdd(keyArray, 2); @@ -43,28 +41,25 @@ void main() { expect(array, isA()); expect( - DeepCollectionEquality.unordered().equals( - array, - [1, 2, 1], - ), + DeepCollectionEquality.unordered().equals(array, [1, 2, 1]), isTrue, ); - }); - - test( - 'setAdd() operation should not be mergeable with any other' - 'operation other than setAddAll()', () { - testUnmergeableOperationShouldThrow( - parseObject: dietPlansObject, - testingOn: dietPlansObject.setAdd, - excludeMergeableOperations: [dietPlansObject.setAddAll], - ); - }); + }, + ); + + test('setAdd() operation should not be mergeable with any other' + 'operation other than setAddAll()', () { + testUnmergeableOperationShouldThrow( + parseObject: dietPlansObject, + testingOn: dietPlansObject.setAdd, + excludeMergeableOperations: [dietPlansObject.setAddAll], + ); + }); - test( - 'adding values using setAddAll() and then calling get(keyArray) ' - 'should return Instance of Iterable that contains all the added values', - () { + test( + 'adding values using setAddAll() and then calling get(keyArray) ' + 'should return Instance of Iterable that contains all the added values', + () { // act dietPlansObject.setAddAll(keyArray, [1, 2, 1]); @@ -74,95 +69,82 @@ void main() { expect(array, isA()); expect( - DeepCollectionEquality.unordered().equals( - array, - [1, 2, 1], - ), + DeepCollectionEquality.unordered().equals(array, [1, 2, 1]), isTrue, ); - }); - - test( - 'setAddAll() operation should not be mergeable with any other' - 'operation other than setAdd()', () { - testUnmergeableOperationShouldThrow( - parseObject: dietPlansObject, - testingOn: dietPlansObject.setAddAll, - excludeMergeableOperations: [dietPlansObject.setAdd], - ); - }); - - test( - 'adding values using setAddUnique() and then calling get(keyArray) ' - 'should return Instance of Iterable that contains all the added values' - ' with out any duplication in the values', () { - // act - dietPlansObject.setAddUnique(keyArray, 1); - dietPlansObject.setAddUnique(keyArray, 2); - dietPlansObject.setAddUnique(keyArray, 1); - dietPlansObject.setAddUnique(keyArray, 3); - dietPlansObject.setAddUnique(keyArray, 1); - dietPlansObject.setAddUnique(keyArray, 4); - - // assert - final array = dietPlansObject.get(keyArray); - - expect(array, isA()); - - expect( - DeepCollectionEquality.unordered().equals( - array, - [1, 2, 3, 4], - ), - isTrue, - ); - }); - - test( - 'setAddUnique() operation should not be mergeable with any other' - 'operation other than setAddAllUnique()', () { - testUnmergeableOperationShouldThrow( - parseObject: dietPlansObject, - testingOn: dietPlansObject.setAddUnique, - excludeMergeableOperations: [dietPlansObject.setAddAllUnique], - ); - }); + }, + ); + + test('setAddAll() operation should not be mergeable with any other' + 'operation other than setAdd()', () { + testUnmergeableOperationShouldThrow( + parseObject: dietPlansObject, + testingOn: dietPlansObject.setAddAll, + excludeMergeableOperations: [dietPlansObject.setAdd], + ); + }); + + test('adding values using setAddUnique() and then calling get(keyArray) ' + 'should return Instance of Iterable that contains all the added values' + ' with out any duplication in the values', () { + // act + dietPlansObject.setAddUnique(keyArray, 1); + dietPlansObject.setAddUnique(keyArray, 2); + dietPlansObject.setAddUnique(keyArray, 1); + dietPlansObject.setAddUnique(keyArray, 3); + dietPlansObject.setAddUnique(keyArray, 1); + dietPlansObject.setAddUnique(keyArray, 4); + + // assert + final array = dietPlansObject.get(keyArray); + + expect(array, isA()); + + expect( + DeepCollectionEquality.unordered().equals(array, [1, 2, 3, 4]), + isTrue, + ); + }); + + test('setAddUnique() operation should not be mergeable with any other' + 'operation other than setAddAllUnique()', () { + testUnmergeableOperationShouldThrow( + parseObject: dietPlansObject, + testingOn: dietPlansObject.setAddUnique, + excludeMergeableOperations: [dietPlansObject.setAddAllUnique], + ); + }); - test( - 'adding values using setAddAllUnique() and then calling get(keyArray) ' - 'should return Instance of Iterable that contains all the added values' - ' with out any duplication in the values', () { - // act - dietPlansObject.setAddAllUnique(keyArray, [1, 2, 1, 3, 1, 4, 1]); + test('adding values using setAddAllUnique() and then calling get(keyArray) ' + 'should return Instance of Iterable that contains all the added values' + ' with out any duplication in the values', () { + // act + dietPlansObject.setAddAllUnique(keyArray, [1, 2, 1, 3, 1, 4, 1]); - // assert - final array = dietPlansObject.get(keyArray); + // assert + final array = dietPlansObject.get(keyArray); - expect(array, isA()); + expect(array, isA()); - expect( - DeepCollectionEquality.unordered().equals( - array, - [1, 2, 3, 4], - ), - isTrue, - ); - }); - - test( - 'setAddAllUnique() operation should not be mergeable with any other' - 'operation other than setAddUnique()', () { - testUnmergeableOperationShouldThrow( - parseObject: dietPlansObject, - testingOn: dietPlansObject.setAddAllUnique, - excludeMergeableOperations: [dietPlansObject.setAddUnique], - ); - }); + expect( + DeepCollectionEquality.unordered().equals(array, [1, 2, 3, 4]), + isTrue, + ); + }); + + test('setAddAllUnique() operation should not be mergeable with any other' + 'operation other than setAddUnique()', () { + testUnmergeableOperationShouldThrow( + parseObject: dietPlansObject, + testingOn: dietPlansObject.setAddAllUnique, + excludeMergeableOperations: [dietPlansObject.setAddUnique], + ); + }); - test( - 'removing values using setRemove() and then calling get(keyArray) ' - 'should return Instance of Iterable that NOT contains the removed values', - () { + test( + 'removing values using setRemove() and then calling get(keyArray) ' + 'should return Instance of Iterable that NOT contains the removed values', + () { // arrange const resultFromServer = { "objectId": "O6BHlwV48Z", @@ -171,10 +153,7 @@ void main() { keyArray: [1, 2, 3, 4], }; - dietPlansObject = ParseObject('Diet_Plans') - ..fromJson( - resultFromServer, - ); + dietPlansObject = ParseObject('Diet_Plans')..fromJson(resultFromServer); // act dietPlansObject.setRemove(keyArray, 4); @@ -185,18 +164,16 @@ void main() { expect(array, isA()); expect( - DeepCollectionEquality.unordered().equals( - array, - [1, 2, 3], - ), + DeepCollectionEquality.unordered().equals(array, [1, 2, 3]), isTrue, ); - }); + }, + ); - test( - 'removing values using setRemoveAll() and then calling get(keyArray) ' - 'should return Instance of Iterable that NOT contains the removed values', - () { + test( + 'removing values using setRemoveAll() and then calling get(keyArray) ' + 'should return Instance of Iterable that NOT contains the removed values', + () { // arrange const resultFromServer = { "objectId": "O6BHlwV48Z", @@ -205,10 +182,7 @@ void main() { keyArray: [1, 2, 3, 4], }; - dietPlansObject = ParseObject('Diet_Plans') - ..fromJson( - resultFromServer, - ); + dietPlansObject = ParseObject('Diet_Plans')..fromJson(resultFromServer); // act dietPlansObject.setRemoveAll(keyArray, [3, 4]); @@ -219,170 +193,135 @@ void main() { expect(array, isA()); expect( - DeepCollectionEquality.unordered().equals( - array, - [1, 2], - ), + DeepCollectionEquality.unordered().equals(array, [1, 2]), isTrue, ); - }); + }, + ); - test( - 'the array should not been affected by removing non existent ' - 'values using setRemove()', () { - // arrange - const resultFromServer = { - "objectId": "O6BHlwV48Z", - "createdAt": "2023-02-26T13:23:03.073Z", - "updatedAt": "2023-03-01T03:38:16.390Z", - keyArray: [1, 2, 3, 4], - }; + test('the array should not been affected by removing non existent ' + 'values using setRemove()', () { + // arrange + const resultFromServer = { + "objectId": "O6BHlwV48Z", + "createdAt": "2023-02-26T13:23:03.073Z", + "updatedAt": "2023-03-01T03:38:16.390Z", + keyArray: [1, 2, 3, 4], + }; - dietPlansObject = ParseObject('Diet_Plans') - ..fromJson( - resultFromServer, - ); + dietPlansObject = ParseObject('Diet_Plans')..fromJson(resultFromServer); - // act - dietPlansObject.setRemove(keyArray, 15); - dietPlansObject.setRemove(keyArray, 16); + // act + dietPlansObject.setRemove(keyArray, 15); + dietPlansObject.setRemove(keyArray, 16); - // assert - final array = dietPlansObject.get(keyArray); + // assert + final array = dietPlansObject.get(keyArray); - expect(array, isA()); - - expect( - DeepCollectionEquality.unordered().equals( - array, - [1, 2, 3, 4], - ), - isTrue, - ); - }); + expect(array, isA()); - test( - 'the array should not been affected by removing non existent ' - 'values using setRemoveAll()', () { - // arrange - const resultFromServer = { - "objectId": "O6BHlwV48Z", - "createdAt": "2023-02-26T13:23:03.073Z", - "updatedAt": "2023-03-01T03:38:16.390Z", - keyArray: [1, 2, 3, 4], - }; + expect( + DeepCollectionEquality.unordered().equals(array, [1, 2, 3, 4]), + isTrue, + ); + }); - dietPlansObject = ParseObject('Diet_Plans') - ..fromJson( - resultFromServer, - ); + test('the array should not been affected by removing non existent ' + 'values using setRemoveAll()', () { + // arrange + const resultFromServer = { + "objectId": "O6BHlwV48Z", + "createdAt": "2023-02-26T13:23:03.073Z", + "updatedAt": "2023-03-01T03:38:16.390Z", + keyArray: [1, 2, 3, 4], + }; - // act - dietPlansObject.setRemoveAll(keyArray, [15, 16]); + dietPlansObject = ParseObject('Diet_Plans')..fromJson(resultFromServer); - // assert - final array = dietPlansObject.get(keyArray); + // act + dietPlansObject.setRemoveAll(keyArray, [15, 16]); - expect(array, isA()); + // assert + final array = dietPlansObject.get(keyArray); - expect( - DeepCollectionEquality.unordered().equals( - array, - [1, 2, 3, 4], - ), - isTrue, - ); - }); + expect(array, isA()); - test( - 'adding to an array and then removing from it should result in error ' - 'the user can not add and remove in the same time', () { - // act - dietPlansObject.setAdd(keyArray, 1); - dietPlansObject.setAdd(keyArray, 2); + expect( + DeepCollectionEquality.unordered().equals(array, [1, 2, 3, 4]), + isTrue, + ); + }); + + test('adding to an array and then removing from it should result in error ' + 'the user can not add and remove in the same time', () { + // act + dietPlansObject.setAdd(keyArray, 1); + dietPlansObject.setAdd(keyArray, 2); + + // assert + expect( + () => dietPlansObject.setRemove(keyArray, 2), + throwsA(isA()), + ); - // assert - expect( - () => dietPlansObject.setRemove(keyArray, 2), - throwsA(isA()), - ); + final array = dietPlansObject.get(keyArray); - final array = dietPlansObject.get(keyArray); + expect(array, isA()); - expect(array, isA()); + expect(DeepCollectionEquality.unordered().equals(array, [1, 2]), isTrue); + }); - expect( - DeepCollectionEquality.unordered().equals( - array, - [1, 2], - ), - isTrue, - ); - }); + test('removing from an array and then adding to it should result in error ' + 'the user can not remove and add in the same time', () { + // arrange + const resultFromServer = { + "objectId": "O6BHlwV48Z", + "createdAt": "2023-02-26T13:23:03.073Z", + "updatedAt": "2023-03-01T03:38:16.390Z", + keyArray: [1, 2, 3, 4], + }; - test( - 'removing from an array and then adding to it should result in error ' - 'the user can not remove and add in the same time', () { - // arrange - const resultFromServer = { - "objectId": "O6BHlwV48Z", - "createdAt": "2023-02-26T13:23:03.073Z", - "updatedAt": "2023-03-01T03:38:16.390Z", - keyArray: [1, 2, 3, 4], - }; + dietPlansObject = ParseObject('Diet_Plans')..fromJson(resultFromServer); - dietPlansObject = ParseObject('Diet_Plans') - ..fromJson( - resultFromServer, - ); + // act + dietPlansObject.setRemove(keyArray, 4); + dietPlansObject.setRemove(keyArray, 3); - // act - dietPlansObject.setRemove(keyArray, 4); - dietPlansObject.setRemove(keyArray, 3); + // assert + expect( + () => dietPlansObject.setAdd(keyArray, 5), + throwsA(isA()), + ); - // assert - expect( - () => dietPlansObject.setAdd(keyArray, 5), - throwsA(isA()), - ); + final array = dietPlansObject.get(keyArray); - final array = dietPlansObject.get(keyArray); + expect(array, isA()); - expect(array, isA()); + expect(DeepCollectionEquality.unordered().equals(array, [1, 2]), isTrue); + }); - expect( - DeepCollectionEquality.unordered().equals( - array, - [1, 2], - ), - isTrue, - ); - }); - - test( - 'setRemove() operation should not be mergeable with any other' - 'operation other than setRemoveAll()', () { - testUnmergeableOperationShouldThrow( - parseObject: dietPlansObject, - testingOn: dietPlansObject.setRemove, - excludeMergeableOperations: [dietPlansObject.setRemoveAll], - ); - }); - - test( - 'setRemoveAll() operation should not be mergeable with any other' - 'operation other than setRemove()', () { - testUnmergeableOperationShouldThrow( - parseObject: dietPlansObject, - testingOn: dietPlansObject.setRemoveAll, - excludeMergeableOperations: [dietPlansObject.setRemove], - ); - }); + test('setRemove() operation should not be mergeable with any other' + 'operation other than setRemoveAll()', () { + testUnmergeableOperationShouldThrow( + parseObject: dietPlansObject, + testingOn: dietPlansObject.setRemove, + excludeMergeableOperations: [dietPlansObject.setRemoveAll], + ); + }); + + test('setRemoveAll() operation should not be mergeable with any other' + 'operation other than setRemove()', () { + testUnmergeableOperationShouldThrow( + parseObject: dietPlansObject, + testingOn: dietPlansObject.setRemoveAll, + excludeMergeableOperations: [dietPlansObject.setRemove], + ); + }); - test( - 'Array should be in setMode when using "set" to add an array to the parse object ' - 'and any operation on the array should not create any conflict with the previous operation', - () { + test( + 'Array should be in setMode when using "set" to add an array to the parse object ' + 'and any operation on the array should not create any conflict with the previous operation', + () { // arrange void operations() { // act @@ -395,84 +334,84 @@ void main() { // assert expect(() => operations(), returnsNormally); - }); + }, + ); - test( - 'The array internal state should be identical before and after ' - 'storing it in data store', () async { - // arrange - dietPlansObject.objectId = "someId"; + test('The array internal state should be identical before and after ' + 'storing it in data store', () async { + // arrange + dietPlansObject.objectId = "someId"; - dietPlansObject.set(keyArray, [1, 2]); - dietPlansObject.setAdd(keyArray, 3); - dietPlansObject.setAddUnique(keyArray, 3); - dietPlansObject.setAddUnique(keyArray, 4); - dietPlansObject.setRemove(keyArray, 1); + dietPlansObject.set(keyArray, [1, 2]); + dietPlansObject.setAdd(keyArray, 3); + dietPlansObject.setAddUnique(keyArray, 3); + dietPlansObject.setAddUnique(keyArray, 4); + dietPlansObject.setRemove(keyArray, 1); - final listBeforePin = dietPlansObject.get(keyArray); - final toJsonBeforePin = dietPlansObject.toJson(forApiRQ: true); + final listBeforePin = dietPlansObject.get(keyArray); + final toJsonBeforePin = dietPlansObject.toJson(forApiRQ: true); - // act - await dietPlansObject.pin(); + // act + await dietPlansObject.pin(); - final objectFromPin = await dietPlansObject.fromPin('someId'); + final objectFromPin = await dietPlansObject.fromPin('someId'); - // assert - final listAfterPin = objectFromPin.get(keyArray); - final toJsonAfterPin = objectFromPin.toJson(forApiRQ: true); + // assert + final listAfterPin = objectFromPin.get(keyArray); + final toJsonAfterPin = objectFromPin.toJson(forApiRQ: true); - expect( - DeepCollectionEquality().equals(listBeforePin, listAfterPin), - isTrue, - ); + expect( + DeepCollectionEquality().equals(listBeforePin, listAfterPin), + isTrue, + ); - expect( - DeepCollectionEquality().equals(toJsonBeforePin, toJsonAfterPin), - isTrue, - ); - }); + expect( + DeepCollectionEquality().equals(toJsonBeforePin, toJsonAfterPin), + isTrue, + ); + }); - test( - 'The saved modified array internal state should be identical ' - 'before and after storing it in data store', () async { - // arrange - dietPlansObject.fromJson({ - keyArray: [1, 2], - "objectId": "someId" - }); // assume this coming from the server + test('The saved modified array internal state should be identical ' + 'before and after storing it in data store', () async { + // arrange + dietPlansObject.fromJson({ + keyArray: [1, 2], + "objectId": "someId", + }); // assume this coming from the server - dietPlansObject.setAddUnique(keyArray, 3); + dietPlansObject.setAddUnique(keyArray, 3); - final listBeforePin = dietPlansObject.get(keyArray); - final toJsonBeforePin = dietPlansObject.toJson(forApiRQ: true); + final listBeforePin = dietPlansObject.get(keyArray); + final toJsonBeforePin = dietPlansObject.toJson(forApiRQ: true); - // act - await dietPlansObject.pin(); + // act + await dietPlansObject.pin(); - final objectFromPin = await dietPlansObject.fromPin('someId'); + final objectFromPin = await dietPlansObject.fromPin('someId'); - // assert - final listAfterPin = objectFromPin.get(keyArray); - final toJsonAfterPin = objectFromPin.toJson(forApiRQ: true); + // assert + final listAfterPin = objectFromPin.get(keyArray); + final toJsonAfterPin = objectFromPin.toJson(forApiRQ: true); - expect( - DeepCollectionEquality().equals(listBeforePin, listAfterPin), - isTrue, - ); + expect( + DeepCollectionEquality().equals(listBeforePin, listAfterPin), + isTrue, + ); - expect( - DeepCollectionEquality().equals(toJsonBeforePin, toJsonAfterPin), - isTrue, - ); - }); + expect( + DeepCollectionEquality().equals(toJsonBeforePin, toJsonAfterPin), + isTrue, + ); + }); - test( - 'The saved array should not be in setMode. i.e. any conflicting operation' - ' should not be allowed and throw an exception', () { + test( + 'The saved array should not be in setMode. i.e. any conflicting operation' + ' should not be allowed and throw an exception', + () { // arrange dietPlansObject.fromJson({ keyArray: [1, 2], - "objectId": "someId" + "objectId": "someId", }); // assume this coming from the server // act @@ -482,31 +421,32 @@ void main() { op() => dietPlansObject.setRemove(keyArray, 3); expect(() => op(), throwsA(isA())); - }); + }, + ); - test( - 'After the save() function runs successfully for an API request, ' - 'the ParseArray internal value for API request should be empty', - () async { + test( + 'After the save() function runs successfully for an API request, ' + 'the ParseArray internal value for API request should be empty', + () async { // arrange const resultFromServer = { keyVarObjectId: "DLde4rYA8C", - keyVarCreatedAt: "2023-02-26T00:20:37.187Z" + keyVarCreatedAt: "2023-02-26T00:20:37.187Z", }; - when(client.post( - any, - options: anyNamed("options"), - data: anyNamed('data'), - )).thenAnswer( - (_) async { - await Future.delayed(Duration(milliseconds: 500)); - return ParseNetworkResponse( - statusCode: 200, - data: jsonEncode(resultFromServer), - ); - }, - ); + when( + client.post( + any, + options: anyNamed("options"), + data: anyNamed('data'), + ), + ).thenAnswer((_) async { + await Future.delayed(Duration(milliseconds: 500)); + return ParseNetworkResponse( + statusCode: 200, + data: jsonEncode(resultFromServer), + ); + }); dietPlansObject.setAddAll(keyArray, [1, 2, 3]); @@ -524,8 +464,8 @@ void main() { final expectedValueForApiReqBeforeSave = { keyArray: { "__op": "Add", - "objects": [1, 2, 3] - } + "objects": [1, 2, 3], + }, }; expect( @@ -545,231 +485,216 @@ void main() { ); expect(valueForApiReqAfterSave.isEmpty, isTrue); - }); - - test( - 'If an Add operation is performed during the save() function, the result' - ' of the operation should be present in the internal state of the ' - 'ParseArray as a value that has not been saved. The data that has ' - 'been saved should be moved to the saved state', - () async { - // arrange - const resultFromServer = { - keyVarObjectId: "DLde4rYA8C", - keyVarCreatedAt: "2023-02-26T00:20:37.187Z" - }; - - when(client.post( + }, + ); + + test( + 'If an Add operation is performed during the save() function, the result' + ' of the operation should be present in the internal state of the ' + 'ParseArray as a value that has not been saved. The data that has ' + 'been saved should be moved to the saved state', + () async { + // arrange + const resultFromServer = { + keyVarObjectId: "DLde4rYA8C", + keyVarCreatedAt: "2023-02-26T00:20:37.187Z", + }; + + when( + client.post( any, options: anyNamed("options"), data: anyNamed('data'), - )).thenAnswer( - (_) async { - await Future.delayed(Duration(milliseconds: 100)); - return ParseNetworkResponse( - statusCode: 200, - data: jsonEncode(resultFromServer), - ); - }, + ), + ).thenAnswer((_) async { + await Future.delayed(Duration(milliseconds: 100)); + return ParseNetworkResponse( + statusCode: 200, + data: jsonEncode(resultFromServer), ); + }); - dietPlansObject.setAdd(keyArray, 1); - dietPlansObject.setAdd(keyArray, 2); + dietPlansObject.setAdd(keyArray, 1); + dietPlansObject.setAdd(keyArray, 2); - final listBeforeSave = dietPlansObject.get(keyArray); - final valueForApiReqBeforeSave = - dietPlansObject.toJson(forApiRQ: true); + final listBeforeSave = dietPlansObject.get(keyArray); + final valueForApiReqBeforeSave = dietPlansObject.toJson(forApiRQ: true); - // act - dietPlansObject.save(); + // act + dietPlansObject.save(); - // async gap, this could be anything in the app like a click of a button - await Future.delayed(Duration.zero); + // async gap, this could be anything in the app like a click of a button + await Future.delayed(Duration.zero); - // Then suddenly the user added a value to the list - dietPlansObject.setAdd(keyArray, 3); - dietPlansObject.setAdd(keyArray, 4); + // Then suddenly the user added a value to the list + dietPlansObject.setAdd(keyArray, 3); + dietPlansObject.setAdd(keyArray, 4); - // Await the save function to be done - await Future.delayed(Duration(milliseconds: 150)); + // Await the save function to be done + await Future.delayed(Duration(milliseconds: 150)); - // assert - expect( - DeepCollectionEquality().equals(listBeforeSave, [1, 2]), - isTrue, - ); + // assert + expect(DeepCollectionEquality().equals(listBeforeSave, [1, 2]), isTrue); - final listAfterSave = dietPlansObject.get(keyArray); - expect( - DeepCollectionEquality().equals(listAfterSave, [1, 2, 3, 4]), - isTrue, - ); + final listAfterSave = dietPlansObject.get(keyArray); + expect( + DeepCollectionEquality().equals(listAfterSave, [1, 2, 3, 4]), + isTrue, + ); - const expectedValueForApiReqBeforeSave = { - keyArray: { - "__op": "Add", - "objects": [1, 2] - } - }; - expect( - DeepCollectionEquality().equals( - valueForApiReqBeforeSave, - expectedValueForApiReqBeforeSave, - ), - isTrue, - ); + const expectedValueForApiReqBeforeSave = { + keyArray: { + "__op": "Add", + "objects": [1, 2], + }, + }; + expect( + DeepCollectionEquality().equals( + valueForApiReqBeforeSave, + expectedValueForApiReqBeforeSave, + ), + isTrue, + ); - final valueForApiReqAfterSave = - dietPlansObject.toJson(forApiRQ: true); - const expectedValueForApiReqAfterSave = { - keyArray: { - "__op": "Add", - "objects": [3, 4] - } - }; - expect( - DeepCollectionEquality().equals( - valueForApiReqAfterSave, - expectedValueForApiReqAfterSave, - ), - isTrue, - ); - }, - ); + final valueForApiReqAfterSave = dietPlansObject.toJson(forApiRQ: true); + const expectedValueForApiReqAfterSave = { + keyArray: { + "__op": "Add", + "objects": [3, 4], + }, + }; + expect( + DeepCollectionEquality().equals( + valueForApiReqAfterSave, + expectedValueForApiReqAfterSave, + ), + isTrue, + ); + }, + ); + + test( + 'If an Remove operation is performed during the save() function, the result' + ' of the operation should be present in the internal state of the ' + 'ParseArray as a value that has not been saved. The data that has ' + 'been saved should be moved to the saved state', + () async { + // arrange + const resultFromServer = { + keyVarObjectId: "DLde4rYA8C", + keyVarCreatedAt: "2023-02-26T00:20:37.187Z", + }; - test( - 'If an Remove operation is performed during the save() function, the result' - ' of the operation should be present in the internal state of the ' - 'ParseArray as a value that has not been saved. The data that has ' - 'been saved should be moved to the saved state', - () async { - // arrange - const resultFromServer = { - keyVarObjectId: "DLde4rYA8C", - keyVarCreatedAt: "2023-02-26T00:20:37.187Z" - }; - - when(client.post( + when( + client.post( any, options: anyNamed("options"), data: anyNamed('data'), - )).thenAnswer( - (_) async { - await Future.delayed(Duration(milliseconds: 100)); - return ParseNetworkResponse( - statusCode: 200, - data: jsonEncode(resultFromServer), - ); - }, + ), + ).thenAnswer((_) async { + await Future.delayed(Duration(milliseconds: 100)); + return ParseNetworkResponse( + statusCode: 200, + data: jsonEncode(resultFromServer), ); + }); - dietPlansObject.fromJson({ - keyArray: [1, 2, 3, 4] - }); + dietPlansObject.fromJson({ + keyArray: [1, 2, 3, 4], + }); - final listBeforeSave = dietPlansObject.get(keyArray); - final valueForApiReqBeforeSave = - dietPlansObject.toJson(forApiRQ: true); + final listBeforeSave = dietPlansObject.get(keyArray); + final valueForApiReqBeforeSave = dietPlansObject.toJson(forApiRQ: true); - // act - dietPlansObject.save(); + // act + dietPlansObject.save(); - // async gap, this could be anything in the app like a click of a button - await Future.delayed(Duration.zero); + // async gap, this could be anything in the app like a click of a button + await Future.delayed(Duration.zero); - // Then suddenly the user remove a value from the list - dietPlansObject.setRemoveAll(keyArray, [3, 4]); + // Then suddenly the user remove a value from the list + dietPlansObject.setRemoveAll(keyArray, [3, 4]); - // Await the save function to be done - await Future.delayed(Duration(milliseconds: 150)); + // Await the save function to be done + await Future.delayed(Duration(milliseconds: 150)); - // assert - expect(listBeforeSave, orderedEquals([1, 2, 3, 4])); + // assert + expect(listBeforeSave, orderedEquals([1, 2, 3, 4])); - final listAfterSave = dietPlansObject.get(keyArray); - expect( - listAfterSave, - orderedEquals([1, 2]), - ); + final listAfterSave = dietPlansObject.get(keyArray); + expect(listAfterSave, orderedEquals([1, 2])); - expect( - valueForApiReqBeforeSave.isEmpty, - isTrue, - ); + expect(valueForApiReqBeforeSave.isEmpty, isTrue); - final valueForApiReqAfterSave = - dietPlansObject.toJson(forApiRQ: true); - const expectedValueForApiReqAfterSave = { - keyArray: { - "__op": "Remove", - "objects": [3, 4] - } - }; - expect( - DeepCollectionEquality().equals( - valueForApiReqAfterSave, - expectedValueForApiReqAfterSave, - ), - isTrue, - ); - }, - ); + final valueForApiReqAfterSave = dietPlansObject.toJson(forApiRQ: true); + const expectedValueForApiReqAfterSave = { + keyArray: { + "__op": "Remove", + "objects": [3, 4], + }, + }; + expect( + DeepCollectionEquality().equals( + valueForApiReqAfterSave, + expectedValueForApiReqAfterSave, + ), + isTrue, + ); + }, + ); - test( - 'When calling clearUnsavedChanges() the array should be reverted back' - ' to its original state before any modifications were made', () { - // arrange - dietPlansObject.fromJson({ - keyArray: [1, 2], - "objectId": "someId" - }); // assume this coming from the server + test('When calling clearUnsavedChanges() the array should be reverted back' + ' to its original state before any modifications were made', () { + // arrange + dietPlansObject.fromJson({ + keyArray: [1, 2], + "objectId": "someId", + }); // assume this coming from the server - dietPlansObject.setAdd(keyArray, 3); + dietPlansObject.setAdd(keyArray, 3); - // act - dietPlansObject.clearUnsavedChanges(); + // act + dietPlansObject.clearUnsavedChanges(); - // assert - final listValue = dietPlansObject.get(keyArray); + // assert + final listValue = dietPlansObject.get(keyArray); - expect(listValue, orderedEquals([1, 2])); - }); + expect(listValue, orderedEquals([1, 2])); + }); - test( - 'The list value and the value for api request should be identical ' - 'before and after the save() failed to save the object', () async { - // arrange + test('The list value and the value for api request should be identical ' + 'before and after the save() failed to save the object', () async { + // arrange - when(client.post( - any, - options: anyNamed("options"), - data: anyNamed("data"), - )).thenThrow(Exception('error')); + when( + client.post(any, options: anyNamed("options"), data: anyNamed("data")), + ).thenThrow(Exception('error')); - dietPlansObject.setAddAll(keyArray, [1, 2]); + dietPlansObject.setAddAll(keyArray, [1, 2]); - final valueForApiReqBeforeErrorSave = - dietPlansObject.toJson(forApiRQ: true); + final valueForApiReqBeforeErrorSave = dietPlansObject.toJson( + forApiRQ: true, + ); - // act - await dietPlansObject.save(); + // act + await dietPlansObject.save(); - // assert - final listValue = dietPlansObject.get(keyArray); + // assert + final listValue = dietPlansObject.get(keyArray); - expect(listValue, orderedEquals([1, 2])); + expect(listValue, orderedEquals([1, 2])); - final valueForApiReqAfterErrorSave = - dietPlansObject.toJson(forApiRQ: true); + final valueForApiReqAfterErrorSave = dietPlansObject.toJson( + forApiRQ: true, + ); - expect( - DeepCollectionEquality().equals( - valueForApiReqAfterErrorSave, - valueForApiReqBeforeErrorSave, - ), - isTrue, - ); - }); - }, - ); + expect( + DeepCollectionEquality().equals( + valueForApiReqAfterErrorSave, + valueForApiReqBeforeErrorSave, + ), + isTrue, + ); + }); + }); } diff --git a/packages/dart/test/src/objects/parse_object/parse_object_create_test.dart b/packages/dart/test/src/objects/parse_object/parse_object_create_test.dart index ee4c205bd..4d7365cfd 100644 --- a/packages/dart/test/src/objects/parse_object/parse_object_create_test.dart +++ b/packages/dart/test/src/objects/parse_object/parse_object_create_test.dart @@ -36,80 +36,71 @@ void main() { }); test( - 'create() should create new object on the server, return the created ' - 'object in ParseResponse results and update the calling object ' - 'with the new data (objectId,createdAt). i.e: mutate the object state', - () async { - // arrange + 'create() should create new object on the server, return the created ' + 'object in ParseResponse results and update the calling object ' + 'with the new data (objectId,createdAt). i.e: mutate the object state', + () async { + // arrange - const resultFromServer = { - keyVarObjectId: "DLde4rYA8C", - keyVarCreatedAt: "2023-02-26T00:20:37.187Z" - }; - final postData = jsonEncode(dietPlansObject.toJson(forApiRQ: true)); + const resultFromServer = { + keyVarObjectId: "DLde4rYA8C", + keyVarCreatedAt: "2023-02-26T00:20:37.187Z", + }; + final postData = jsonEncode(dietPlansObject.toJson(forApiRQ: true)); - when(client.post( - postPath, - options: anyNamed("options"), - data: postData, - )).thenAnswer( - (_) async => ParseNetworkResponse( - statusCode: 200, - data: jsonEncode(resultFromServer), - ), - ); + when( + client.post(postPath, options: anyNamed("options"), data: postData), + ).thenAnswer( + (_) async => ParseNetworkResponse( + statusCode: 200, + data: jsonEncode(resultFromServer), + ), + ); - // act - ParseResponse response = await dietPlansObject.create(); + // act + ParseResponse response = await dietPlansObject.create(); - // assert - final resultList = response.results; + // assert + final resultList = response.results; - expect(resultList, isNotNull); + expect(resultList, isNotNull); - expect(resultList, isA>()); + expect(resultList, isA>()); - expect(resultList!.first, isNotNull); + expect(resultList!.first, isNotNull); - expect(resultList.first, isA()); + expect(resultList.first, isA()); - final parseObject = (resultList.first as ParseObject); + final parseObject = (resultList.first as ParseObject); - expect( - parseObject.createdAt!.toIso8601String(), - equals(resultFromServer[keyVarCreatedAt]), - ); + expect( + parseObject.createdAt!.toIso8601String(), + equals(resultFromServer[keyVarCreatedAt]), + ); - expect( - dietPlansObject.createdAt!.toIso8601String(), - equals(resultFromServer[keyVarCreatedAt]), - ); + expect( + dietPlansObject.createdAt!.toIso8601String(), + equals(resultFromServer[keyVarCreatedAt]), + ); - expect( - parseObject.objectId, - equals(resultFromServer[keyVarObjectId]), - ); + expect(parseObject.objectId, equals(resultFromServer[keyVarObjectId])); - expect( - dietPlansObject.objectId, - equals(resultFromServer[keyVarObjectId]), - ); + expect( + dietPlansObject.objectId, + equals(resultFromServer[keyVarObjectId]), + ); - // the calling object (dietPlansObject) will be identical to the object - // in the ParseResponse results - expect( - identical(dietPlansObject, parseObject), - isTrue, - ); + // the calling object (dietPlansObject) will be identical to the object + // in the ParseResponse results + expect(identical(dietPlansObject, parseObject), isTrue); - verify(client.post( - postPath, - options: anyNamed("options"), - data: postData, - )).called(1); + verify( + client.post(postPath, options: anyNamed("options"), data: postData), + ).called(1); - verifyNoMoreInteractions(client); - }); + verifyNoMoreInteractions(client); + }, + ); test('create() should return error', () async { // arrange @@ -117,11 +108,9 @@ void main() { final postData = jsonEncode(dietPlansObject.toJson(forApiRQ: true)); final errorData = jsonEncode({keyCode: -1, keyError: "someError"}); - when(client.post( - postPath, - options: anyNamed("options"), - data: postData, - )).thenAnswer( + when( + client.post(postPath, options: anyNamed("options"), data: postData), + ).thenAnswer( (_) async => ParseNetworkResponse(data: errorData, statusCode: -1), ); @@ -147,11 +136,9 @@ void main() { expect(dietPlansObject.createdAt, isNull); - verify(client.post( - postPath, - options: anyNamed("options"), - data: postData, - )).called(1); + verify( + client.post(postPath, options: anyNamed("options"), data: postData), + ).called(1); verifyNoMoreInteractions(client); }); diff --git a/packages/dart/test/src/objects/parse_object/parse_object_delete_eventually_test.dart b/packages/dart/test/src/objects/parse_object/parse_object_delete_eventually_test.dart index 0e333eb13..3391ff6e7 100644 --- a/packages/dart/test/src/objects/parse_object/parse_object_delete_eventually_test.dart +++ b/packages/dart/test/src/objects/parse_object/parse_object_delete_eventually_test.dart @@ -31,23 +31,22 @@ void main() { 'should exist parse object in ParseCoreData next saveEventually', () async { // arrange - when(client1.delete( - any, - options: anyNamed("options"), - )).thenThrow(Exception('NetworkError')); - - when(client2.delete( - "$serverUrl/classes/Diet_Plans/fakeObjectId", - options: anyNamed("options"), - )).thenAnswer( + when( + client1.delete(any, options: anyNamed("options")), + ).thenThrow(Exception('NetworkError')); + + when( + client2.delete( + "$serverUrl/classes/Diet_Plans/fakeObjectId", + options: anyNamed("options"), + ), + ).thenAnswer( (_) async => ParseNetworkResponse( statusCode: 200, data: jsonEncode([ { - "success": { - "delete": "ok", - } - } + "success": {"delete": "ok"}, + }, ]), ), ); diff --git a/packages/dart/test/src/objects/parse_object/parse_object_delete_test.dart b/packages/dart/test/src/objects/parse_object/parse_object_delete_test.dart index 397ab9ef5..bf088008b 100644 --- a/packages/dart/test/src/objects/parse_object/parse_object_delete_test.dart +++ b/packages/dart/test/src/objects/parse_object/parse_object_delete_test.dart @@ -23,8 +23,7 @@ void main() { dietPlansObject = ParseObject("Diet_Plans", client: client); }); - test( - 'delete() should delete object form the server and all the object ' + test('delete() should delete object form the server and all the object ' 'data/state should be the same after the deletion', () async { // arrange dietPlansObject.objectId = "cmWCmCAyQQ"; @@ -39,9 +38,7 @@ void main() { final resultFromServer = {}; - when(client.delete( - deletePath, - )).thenAnswer( + when(client.delete(deletePath)).thenAnswer( (_) async => ParseNetworkResponse( statusCode: 200, data: jsonEncode(resultFromServer), @@ -79,9 +76,7 @@ void main() { equals(jsonEncode(dietPlansObjectDataBeforeDeletion)), ); - verify(client.delete( - deletePath, - )).called(1); + verify(client.delete(deletePath)).called(1); verifyNoMoreInteractions(client); }); @@ -92,8 +87,9 @@ void main() { dietPlansObject.set('Fat', 15); - final dietPlansObjectDataBeforeDeletion = - dietPlansObject.toJson(full: true); + final dietPlansObjectDataBeforeDeletion = dietPlansObject.toJson( + full: true, + ); final deletePath = Uri.parse( '$serverUrl$keyEndPointClasses${dietPlansObject.parseClassName}/${dietPlansObject.objectId}', @@ -101,9 +97,7 @@ void main() { final error = Exception('error'); - when(client.delete( - deletePath, - )).thenThrow(error); + when(client.delete(deletePath)).thenThrow(error); // act final response = await dietPlansObject.delete(); @@ -123,87 +117,82 @@ void main() { expect(response.error!.code, equals(ParseError.otherCause)); - final dietPlansObjectDataAfterDeletion = - dietPlansObject.toJson(full: true); + final dietPlansObjectDataAfterDeletion = dietPlansObject.toJson( + full: true, + ); expect( jsonEncode(dietPlansObjectDataAfterDeletion), equals(jsonEncode(dietPlansObjectDataBeforeDeletion)), ); - verify(client.delete( - deletePath, - )).called(1); + verify(client.delete(deletePath)).called(1); verifyNoMoreInteractions(client); }); test( - 'delete(id: id) should delete object form the server and all the object ' - 'data/state should be the same after the deletion using id parameter', - () async { - // arrange - const id = "cmWCmCAyQQ"; + 'delete(id: id) should delete object form the server and all the object ' + 'data/state should be the same after the deletion using id parameter', + () async { + // arrange + const id = "cmWCmCAyQQ"; - dietPlansObject.set('Fat', 15); + dietPlansObject.set('Fat', 15); - final dietPlansObjectDataBeforeDeletion = dietPlansObject.toJson(); + final dietPlansObjectDataBeforeDeletion = dietPlansObject.toJson(); - final deletePath = Uri.parse( - '$serverUrl$keyEndPointClasses${dietPlansObject.parseClassName}/$id', - ).toString(); + final deletePath = Uri.parse( + '$serverUrl$keyEndPointClasses${dietPlansObject.parseClassName}/$id', + ).toString(); - final resultFromServer = {}; + final resultFromServer = {}; - when(client.delete( - deletePath, - )).thenAnswer( - (_) async => ParseNetworkResponse( - statusCode: 200, - data: jsonEncode(resultFromServer), - ), - ); + when(client.delete(deletePath)).thenAnswer( + (_) async => ParseNetworkResponse( + statusCode: 200, + data: jsonEncode(resultFromServer), + ), + ); - // act - final response = await dietPlansObject.delete(id: id); + // act + final response = await dietPlansObject.delete(id: id); - // assert + // assert - expect(response.success, isTrue); + expect(response.success, isTrue); - expect(response.count, equals(1)); + expect(response.count, equals(1)); - expect(response.error, isNull); + expect(response.error, isNull); - expect(response.results, isNotNull); + expect(response.results, isNotNull); - expect(response.results, isA>()); + expect(response.results, isA>()); - expect(response.results!.isNotEmpty, isTrue); + expect(response.results!.isNotEmpty, isTrue); - final objectFromResponse = response.results!.first; + final objectFromResponse = response.results!.first; - expect(objectFromResponse, isA()); + expect(objectFromResponse, isA()); - expect(identical(objectFromResponse, dietPlansObject), isTrue); + expect(identical(objectFromResponse, dietPlansObject), isTrue); - final dietPlansObjectDataAfterDeletion = - (objectFromResponse as ParseObject).toJson(); + final dietPlansObjectDataAfterDeletion = + (objectFromResponse as ParseObject).toJson(); - expect( - jsonEncode(dietPlansObjectDataAfterDeletion), - equals(jsonEncode(dietPlansObjectDataBeforeDeletion)), - ); + expect( + jsonEncode(dietPlansObjectDataAfterDeletion), + equals(jsonEncode(dietPlansObjectDataBeforeDeletion)), + ); - verify(client.delete( - deletePath, - )).called(1); + verify(client.delete(deletePath)).called(1); - verifyNoMoreInteractions(client); - }); + verifyNoMoreInteractions(client); + }, + ); - test( - 'delete() should throw Exception if both objectId and id parameter ' + test('delete() should throw Exception if both objectId and id parameter ' 'is null or empty', () async { // arrange dietPlansObject.objectId = null; diff --git a/packages/dart/test/src/objects/parse_object/parse_object_distinct_test.dart b/packages/dart/test/src/objects/parse_object/parse_object_distinct_test.dart index d61384cdd..99ed8886f 100644 --- a/packages/dart/test/src/objects/parse_object/parse_object_distinct_test.dart +++ b/packages/dart/test/src/objects/parse_object/parse_object_distinct_test.dart @@ -34,8 +34,7 @@ void main() { getPath = getURI.replace(query: stringDistinctQuery).toString(); }); - test( - 'distinct() should return the expected result from server with ' + test('distinct() should return the expected result from server with ' 'distinct aggregate on a column', () async { // arrange const resultFromServer = { @@ -52,12 +51,12 @@ void main() { "user": { "__type": "Pointer", "className": "_User", - "objectId": "cmWCmCAyQQ" + "objectId": "cmWCmCAyQQ", }, "createdAt": "2023-02-24T15:39:44.800Z", "updatedAt": "2023-02-24T22:28:17.867Z", "location": {"__type": "GeoPoint", "latitude": 50, "longitude": 0}, - "anArray": ["3", "4"] + "anArray": ["3", "4"], }, { "objectId": "15NCdmBFBw", @@ -71,7 +70,7 @@ void main() { "user": { "__type": "Pointer", "className": "_User", - "objectId": "cmWCmCAyQQ" + "objectId": "cmWCmCAyQQ", }, "createdAt": "2023-02-24T15:44:17.781Z", "updatedAt": "2023-02-24T22:28:45.446Z", @@ -80,19 +79,17 @@ void main() { "afile": { "__type": "File", "name": "33b6acb416c0mmer-wallpapers.png", - "url": "https://parsefiles.back4app.com/gyBkQBRSapgwfxB/cers.png" - } - } - ] + "url": "https://parsefiles.back4app.com/gyBkQBRSapgwfxB/cers.png", + }, + }, + ], }; final resultList = resultFromServer["results"]!; final firstObject = ParseObject('Diet_Plans').fromJson(resultList[0]); final secondObject = ParseObject('Diet_Plans').fromJson(resultList[1]); - when(client.get( - getPath, - )).thenAnswer( + when(client.get(getPath)).thenAnswer( (_) async => ParseNetworkResponse( statusCode: 200, data: jsonEncode(resultFromServer), @@ -124,9 +121,7 @@ void main() { equals(jsonEncode(secondObject.toJson())), ); - verify(client.get( - getPath, - )).called(1); + verify(client.get(getPath)).called(1); verifyNoMoreInteractions(client); }); @@ -135,9 +130,7 @@ void main() { // arrange final error = Exception('error'); - when(client.get( - getPath, - )).thenThrow(error); + when(client.get(getPath)).thenThrow(error); // act final response = await dietPlansObject.distinct(stringDistinctQuery); @@ -157,9 +150,7 @@ void main() { expect(response.error!.code, equals(ParseError.otherCause)); - verify(client.get( - getPath, - )).called(1); + verify(client.get(getPath)).called(1); verifyNoMoreInteractions(client); }); diff --git a/packages/dart/test/src/objects/parse_object/parse_object_fetch_test.dart b/packages/dart/test/src/objects/parse_object/parse_object_fetch_test.dart index bd3863b1d..c3bcb377b 100644 --- a/packages/dart/test/src/objects/parse_object/parse_object_fetch_test.dart +++ b/packages/dart/test/src/objects/parse_object/parse_object_fetch_test.dart @@ -24,95 +24,95 @@ void main() { }); test( - 'fetch() should return fresh data from the server about an object using its ID', - () async { - // arrange - dietPlansObject.objectId = "Mn1iJTkWTE"; - - final getPath = Uri.parse( - '$serverUrl$keyEndPointClasses${dietPlansObject.parseClassName}/${dietPlansObject.objectId}', - ).replace(query: 'include=afile,owner').toString(); - - const resultsFromServer = { - "results": [ - { - "objectId": "Mn1iJTkWTE", - "Name": "A string", - "Description": "A string", - "Carbs": 1, - "Fat": 1, - "Status": true, - "Protein": 1, - "co_owner": { - "__type": "Pointer", - "className": "_User", - "objectId": "cmWCmCAyQQ" - }, - "owner": { - "objectId": "eTSwGnAOAq", - "username": "n@g.com", - "createdAt": "2023-03-01T03:37:41.011Z", - "updatedAt": "2023-03-01T03:37:41.011Z", - "ACL": { - "*": {"read": true}, - "eTSwGnAOAq": {"read": true, "write": true} + 'fetch() should return fresh data from the server about an object using its ID', + () async { + // arrange + dietPlansObject.objectId = "Mn1iJTkWTE"; + + final getPath = Uri.parse( + '$serverUrl$keyEndPointClasses${dietPlansObject.parseClassName}/${dietPlansObject.objectId}', + ).replace(query: 'include=afile,owner').toString(); + + const resultsFromServer = { + "results": [ + { + "objectId": "Mn1iJTkWTE", + "Name": "A string", + "Description": "A string", + "Carbs": 1, + "Fat": 1, + "Status": true, + "Protein": 1, + "co_owner": { + "__type": "Pointer", + "className": "_User", + "objectId": "cmWCmCAyQQ", }, - "__type": "Object", - "className": "_User" - }, - "location": { - "__type": "GeoPoint", - "latitude": 40, - "longitude": -30 - }, - "anArray": [1, "a string"], - "afile": { - "__type": "File", - "name": "resume.txt", - "url": "https://exampe.com/gyBUISapgwfxB/resume.txt" + "owner": { + "objectId": "eTSwGnAOAq", + "username": "n@g.com", + "createdAt": "2023-03-01T03:37:41.011Z", + "updatedAt": "2023-03-01T03:37:41.011Z", + "ACL": { + "*": {"read": true}, + "eTSwGnAOAq": {"read": true, "write": true}, + }, + "__type": "Object", + "className": "_User", + }, + "location": { + "__type": "GeoPoint", + "latitude": 40, + "longitude": -30, + }, + "anArray": [1, "a string"], + "afile": { + "__type": "File", + "name": "resume.txt", + "url": "https://exampe.com/gyBUISapgwfxB/resume.txt", + }, + "createdAt": "2023-03-05T00:25:31.466Z", + "updatedAt": "2023-03-05T00:25:31.466Z", + "users": {"__type": "Relation", "className": "_User"}, }, - "createdAt": "2023-03-05T00:25:31.466Z", - "updatedAt": "2023-03-05T00:25:31.466Z", - "users": {"__type": "Relation", "className": "_User"} - }, - ] - }; - - final expectedParseObject = ParseObject('Diet_Plans') - ..fromJson(resultsFromServer['results']!.first); - - when(client.get( - getPath, - options: anyNamed("options"), - onReceiveProgress: anyNamed("onReceiveProgress"), - )).thenAnswer( - (_) async => ParseNetworkResponse( - statusCode: 200, - data: jsonEncode(resultsFromServer), - ), - ); - - // act - ParseObject parseObject = await dietPlansObject.fetch( - include: ['afile', 'owner'], - ); - - // assert - - expect( - jsonEncode(parseObject.toJson(full: true)), - equals(jsonEncode(expectedParseObject.toJson(full: true))), - ); - - verify(client.get( - getPath, - )).called(1); - - verifyNoMoreInteractions(client); - }); - - test( - 'fetch() should return the same calling object in case of an error and ' + ], + }; + + final expectedParseObject = ParseObject('Diet_Plans') + ..fromJson(resultsFromServer['results']!.first); + + when( + client.get( + getPath, + options: anyNamed("options"), + onReceiveProgress: anyNamed("onReceiveProgress"), + ), + ).thenAnswer( + (_) async => ParseNetworkResponse( + statusCode: 200, + data: jsonEncode(resultsFromServer), + ), + ); + + // act + ParseObject parseObject = await dietPlansObject.fetch( + include: ['afile', 'owner'], + ); + + // assert + + expect( + jsonEncode(parseObject.toJson(full: true)), + equals(jsonEncode(expectedParseObject.toJson(full: true))), + ); + + verify(client.get(getPath)).called(1); + + verifyNoMoreInteractions(client); + }, + ); + + test('fetch() should return the same calling object in case of an error and ' 'the object data should remain the same', () async { // arrange dietPlansObject.objectId = "Mn1iJTkWTE"; @@ -128,11 +128,13 @@ void main() { final error = Exception('error'); - when(client.get( - getPath, - options: anyNamed("options"), - onReceiveProgress: anyNamed("onReceiveProgress"), - )).thenThrow(error); + when( + client.get( + getPath, + options: anyNamed("options"), + onReceiveProgress: anyNamed("onReceiveProgress"), + ), + ).thenThrow(error); // act ParseObject parseObject = await dietPlansObject.fetch(); @@ -142,33 +144,37 @@ void main() { expect(dietPlansObject.get(keyFat), equals(keyFatValue)); - verify(client.get( - getPath, - options: anyNamed("options"), - onReceiveProgress: anyNamed("onReceiveProgress"), - )).called(1); + verify( + client.get( + getPath, + options: anyNamed("options"), + onReceiveProgress: anyNamed("onReceiveProgress"), + ), + ).called(1); verifyNoMoreInteractions(client); }); - test('fetch() should throw exception if the objectId is null or empty ', - () async { - // arrange - dietPlansObject.objectId = null; - - expect( - () async => await dietPlansObject.fetch(), - throwsA(isA()), - reason: 'the objectId is null', - ); - - dietPlansObject.objectId = ''; - - expect( - () async => await dietPlansObject.fetch(), - throwsA(isA()), - reason: 'the objectId is empty', - ); - }); + test( + 'fetch() should throw exception if the objectId is null or empty ', + () async { + // arrange + dietPlansObject.objectId = null; + + expect( + () async => await dietPlansObject.fetch(), + throwsA(isA()), + reason: 'the objectId is null', + ); + + dietPlansObject.objectId = ''; + + expect( + () async => await dietPlansObject.fetch(), + throwsA(isA()), + reason: 'the objectId is empty', + ); + }, + ); }); } diff --git a/packages/dart/test/src/objects/parse_object/parse_object_get_all_test.dart b/packages/dart/test/src/objects/parse_object/parse_object_get_all_test.dart index a43747d5b..366258e6c 100644 --- a/packages/dart/test/src/objects/parse_object/parse_object_get_all_test.dart +++ b/packages/dart/test/src/objects/parse_object/parse_object_get_all_test.dart @@ -46,12 +46,12 @@ void main() { "user": { "__type": "Pointer", "className": "_User", - "objectId": "cmWCmCAyQQ" + "objectId": "cmWCmCAyQQ", }, "createdAt": "2023-02-24T15:39:44.800Z", "updatedAt": "2023-02-24T22:28:17.867Z", "location": {"__type": "GeoPoint", "latitude": 50, "longitude": 0}, - "anArray": ["3", "4"] + "anArray": ["3", "4"], }, { "objectId": "15NCdmBFBw", @@ -65,7 +65,7 @@ void main() { "user": { "__type": "Pointer", "className": "_User", - "objectId": "cmWCmCAyQQ" + "objectId": "cmWCmCAyQQ", }, "createdAt": "2023-02-24T15:44:17.781Z", "updatedAt": "2023-02-24T22:28:45.446Z", @@ -74,20 +74,24 @@ void main() { "afile": { "__type": "File", "name": "33b6acb416c0mmer-wallpapers.png", - "url": "https://parsefiles.back4app.com/gyBkQBRSapgwfxB/cers.png" - } - } - ] + "url": "https://parsefiles.back4app.com/gyBkQBRSapgwfxB/cers.png", + }, + }, + ], }; - when(client.get( - getPath, - options: anyNamed("options"), - onReceiveProgress: anyNamed("onReceiveProgress"), - )).thenAnswer((_) async => ParseNetworkResponse( - statusCode: 200, - data: jsonEncode(desiredOutput), - )); + when( + client.get( + getPath, + options: anyNamed("options"), + onReceiveProgress: anyNamed("onReceiveProgress"), + ), + ).thenAnswer( + (_) async => ParseNetworkResponse( + statusCode: 200, + data: jsonEncode(desiredOutput), + ), + ); // act ParseResponse response = await dietPlansObject.getAll(); @@ -101,16 +105,15 @@ void main() { expect(response.count, equals(2)); - expect( - listParseObject.length, - equals(desiredOutput["results"]!.length), - ); + expect(listParseObject.length, equals(desiredOutput["results"]!.length)); - verify(client.get( - getPath, - options: anyNamed("options"), - onReceiveProgress: anyNamed("onReceiveProgress"), - )).called(1); + verify( + client.get( + getPath, + options: anyNamed("options"), + onReceiveProgress: anyNamed("onReceiveProgress"), + ), + ).called(1); verifyNoMoreInteractions(client); }); @@ -120,11 +123,13 @@ void main() { final error = Exception('error'); - when(client.get( - getPath, - options: anyNamed("options"), - onReceiveProgress: anyNamed("onReceiveProgress"), - )).thenThrow(error); + when( + client.get( + getPath, + options: anyNamed("options"), + onReceiveProgress: anyNamed("onReceiveProgress"), + ), + ).thenThrow(error); // act ParseResponse response = await dietPlansObject.getAll(); @@ -144,11 +149,13 @@ void main() { expect(response.error!.code, equals(-1)); - verify(client.get( - getPath, - options: anyNamed("options"), - onReceiveProgress: anyNamed("onReceiveProgress"), - )).called(1); + verify( + client.get( + getPath, + options: anyNamed("options"), + onReceiveProgress: anyNamed("onReceiveProgress"), + ), + ).called(1); verifyNoMoreInteractions(client); }); diff --git a/packages/dart/test/src/objects/parse_object/parse_object_get_object_test.dart b/packages/dart/test/src/objects/parse_object/parse_object_get_object_test.dart index 73c6adf24..c5e5c970b 100644 --- a/packages/dart/test/src/objects/parse_object/parse_object_get_object_test.dart +++ b/packages/dart/test/src/objects/parse_object/parse_object_get_object_test.dart @@ -33,109 +33,113 @@ void main() { ).replace(query: 'include=afile,owner').toString(); }); - test('getObject() should get a object form the server using its objectId', - () async { - // arrange - const resultsFromServer = { - "results": [ - { - "objectId": "Mn1iJTkWTE", - "Name": "A string", - "Description": "A string", - "Carbs": 1, - "Fat": 1, - "Status": true, - "Protein": 1, - "co_owner": { - "__type": "Pointer", - "className": "_User", - "objectId": "cmWCmCAyQQ" - }, - "owner": { - "objectId": "eTSwGnAOAq", - "username": "n@g.com", - "createdAt": "2023-03-01T03:37:41.011Z", - "updatedAt": "2023-03-01T03:37:41.011Z", - "ACL": { - "*": {"read": true}, - "eTSwGnAOAq": {"read": true, "write": true} + test( + 'getObject() should get a object form the server using its objectId', + () async { + // arrange + const resultsFromServer = { + "results": [ + { + "objectId": "Mn1iJTkWTE", + "Name": "A string", + "Description": "A string", + "Carbs": 1, + "Fat": 1, + "Status": true, + "Protein": 1, + "co_owner": { + "__type": "Pointer", + "className": "_User", + "objectId": "cmWCmCAyQQ", }, - "__type": "Object", - "className": "_User" - }, - "location": { - "__type": "GeoPoint", - "latitude": 40, - "longitude": -30 - }, - "anArray": [1, "a string"], - "afile": { - "__type": "File", - "name": "resume.txt", - "url": "https://exampe.com/gyBUISapgwfxB/resume.txt" + "owner": { + "objectId": "eTSwGnAOAq", + "username": "n@g.com", + "createdAt": "2023-03-01T03:37:41.011Z", + "updatedAt": "2023-03-01T03:37:41.011Z", + "ACL": { + "*": {"read": true}, + "eTSwGnAOAq": {"read": true, "write": true}, + }, + "__type": "Object", + "className": "_User", + }, + "location": { + "__type": "GeoPoint", + "latitude": 40, + "longitude": -30, + }, + "anArray": [1, "a string"], + "afile": { + "__type": "File", + "name": "resume.txt", + "url": "https://exampe.com/gyBUISapgwfxB/resume.txt", + }, + "createdAt": "2023-03-05T00:25:31.466Z", + "updatedAt": "2023-03-05T00:25:31.466Z", + "users": {"__type": "Relation", "className": "_User"}, }, - "createdAt": "2023-03-05T00:25:31.466Z", - "updatedAt": "2023-03-05T00:25:31.466Z", - "users": {"__type": "Relation", "className": "_User"} - }, - ] - }; - - final expectedParseObject = ParseObject('Diet_Plans') - ..fromJson(resultsFromServer['results']!.first); - - when(client.get( - getPath, - options: anyNamed("options"), - onReceiveProgress: anyNamed("onReceiveProgress"), - )).thenAnswer( - (_) async => ParseNetworkResponse( - statusCode: 200, - data: jsonEncode(resultsFromServer), - ), - ); + ], + }; - // act - final response = await dietPlansObject.getObject( - objectId, - include: ['afile', 'owner'], - ); + final expectedParseObject = ParseObject('Diet_Plans') + ..fromJson(resultsFromServer['results']!.first); - // assert - expect(response.count, equals(1)); + when( + client.get( + getPath, + options: anyNamed("options"), + onReceiveProgress: anyNamed("onReceiveProgress"), + ), + ).thenAnswer( + (_) async => ParseNetworkResponse( + statusCode: 200, + data: jsonEncode(resultsFromServer), + ), + ); - expect(response.success, isTrue); + // act + final response = await dietPlansObject.getObject( + objectId, + include: ['afile', 'owner'], + ); - expect(response.results, isNotNull); + // assert + expect(response.count, equals(1)); - expect(response.results, isA>()); + expect(response.success, isTrue); - final responseResults = List.from(response.results!); + expect(response.results, isNotNull); - final parseObjectFromServer = responseResults.first; + expect(response.results, isA>()); - expect( - jsonEncode(parseObjectFromServer.toJson(full: true)), - equals(jsonEncode(expectedParseObject.toJson(full: true))), - ); + final responseResults = List.from(response.results!); - verify(client.get( - getPath, - )).called(1); + final parseObjectFromServer = responseResults.first; - verifyNoMoreInteractions(client); - }); + expect( + jsonEncode(parseObjectFromServer.toJson(full: true)), + equals(jsonEncode(expectedParseObject.toJson(full: true))), + ); + + verify(client.get(getPath)).called(1); + + verifyNoMoreInteractions(client); + }, + ); test('getObject() should return error', () async { // arrange final error = Exception('error'); - when(client.get( - getPath, - options: anyNamed("options"), - onReceiveProgress: anyNamed("onReceiveProgress"), - )).thenThrow(error); + when( + client.get( + getPath, + options: anyNamed("options"), + onReceiveProgress: anyNamed("onReceiveProgress"), + ), + ).thenThrow(error); // act final response = await dietPlansObject.getObject( @@ -158,11 +162,13 @@ void main() { expect(response.error!.code, equals(ParseError.otherCause)); - verify(client.get( - getPath, - options: anyNamed("options"), - onReceiveProgress: anyNamed("onReceiveProgress"), - )).called(1); + verify( + client.get( + getPath, + options: anyNamed("options"), + onReceiveProgress: anyNamed("onReceiveProgress"), + ), + ).called(1); verifyNoMoreInteractions(client); }); diff --git a/packages/dart/test/src/objects/parse_object/parse_object_increment_decrement_test.dart b/packages/dart/test/src/objects/parse_object/parse_object_increment_decrement_test.dart index eb7c5dcf6..3accfe6df 100644 --- a/packages/dart/test/src/objects/parse_object/parse_object_increment_decrement_test.dart +++ b/packages/dart/test/src/objects/parse_object/parse_object_increment_decrement_test.dart @@ -26,8 +26,7 @@ void main() { dietPlansObject = ParseObject("Diet_Plans", client: client); }); - test( - 'Incrementing values using setIncrement() and then calling get(key) ' + test('Incrementing values using setIncrement() and then calling get(key) ' 'should return Instance of num that hold the result of incrementing ' 'the value by the amount parameter', () { // arrange @@ -45,8 +44,7 @@ void main() { expect(fatValue, equals(3.5)); }); - test( - 'Incrementing not existing values should be handled by assuming' + test('Incrementing not existing values should be handled by assuming' 'that the default value is 0 and operate on it', () { // act dietPlansObject.setIncrement(keyFat, 1); @@ -61,33 +59,17 @@ void main() { }); test( - 'Incrementing should work with already present values decoded from API', - () { - // arrange - const resultFromServer = { - "objectId": "O6BHlwV48Z", - "createdAt": "2023-02-26T13:23:03.073Z", - "updatedAt": "2023-03-01T03:38:16.390Z", - keyFat: 2.5, - }; - - dietPlansObject = ParseObject('Diet_Plans')..fromJson(resultFromServer); - - // act - dietPlansObject.setIncrement(keyFat, 2.5); - - // assert - final fatValue = dietPlansObject.get(keyFat); - - expect(fatValue, isA()); - - expect(fatValue, equals(5)); - }); - test( - 'setIncrement() should account for pervasively set value', + 'Incrementing should work with already present values decoded from API', () { // arrange - dietPlansObject.set(keyFat, 5); + const resultFromServer = { + "objectId": "O6BHlwV48Z", + "createdAt": "2023-02-26T13:23:03.073Z", + "updatedAt": "2023-03-01T03:38:16.390Z", + keyFat: 2.5, + }; + + dietPlansObject = ParseObject('Diet_Plans')..fromJson(resultFromServer); // act dietPlansObject.setIncrement(keyFat, 2.5); @@ -97,12 +79,25 @@ void main() { expect(fatValue, isA()); - expect(fatValue, equals(7.5)); + expect(fatValue, equals(5)); }, ); + test('setIncrement() should account for pervasively set value', () { + // arrange + dietPlansObject.set(keyFat, 5); - test( - 'setIncrement() operation should not be mergeable with any other' + // act + dietPlansObject.setIncrement(keyFat, 2.5); + + // assert + final fatValue = dietPlansObject.get(keyFat); + + expect(fatValue, isA()); + + expect(fatValue, equals(7.5)); + }); + + test('setIncrement() operation should not be mergeable with any other' 'operation other than setDecrement()', () { testUnmergeableOperationShouldThrow( parseObject: dietPlansObject, @@ -128,15 +123,11 @@ void main() { equals(5.0), ); - expect( - dietPlansObject.get(keyFat), - equals(15.0), - ); + expect(dietPlansObject.get(keyFat), equals(15.0)); }, ); - test( - 'Decrementing values using setDecrement() and then calling get(key) ' + test('Decrementing values using setDecrement() and then calling get(key) ' 'should return Instance of num that hold the result of decrementing ' 'the value by the amount parameter', () { // arrange @@ -154,8 +145,7 @@ void main() { expect(fatValue, equals(-3.5)); }); - test( - 'Decrementing not existing values should be handled by assuming' + test('Decrementing not existing values should be handled by assuming' 'that the default value is 0 and operate on it', () { // act dietPlansObject.setDecrement(keyFat, 1); @@ -170,47 +160,45 @@ void main() { }); test( - 'Decrementing should work with already present values decoded from API', - () { - // arrange - const resultFromServer = { - "objectId": "O6BHlwV48Z", - "createdAt": "2023-02-26T13:23:03.073Z", - "updatedAt": "2023-03-01T03:38:16.390Z", - keyFat: 3.5, - }; - - dietPlansObject = ParseObject('Diet_Plans')..fromJson(resultFromServer); - - // act - dietPlansObject.setDecrement(keyFat, 2.5); - - // assert - final fatValue = dietPlansObject.get(keyFat); - - expect(fatValue, isA()); - - expect(fatValue, equals(1)); - }); - - test( - 'setDecrement() should account for pervasively set value', + 'Decrementing should work with already present values decoded from API', () { // arrange - dietPlansObject.set(keyFat, 5); + const resultFromServer = { + "objectId": "O6BHlwV48Z", + "createdAt": "2023-02-26T13:23:03.073Z", + "updatedAt": "2023-03-01T03:38:16.390Z", + keyFat: 3.5, + }; + + dietPlansObject = ParseObject('Diet_Plans')..fromJson(resultFromServer); // act - dietPlansObject.setDecrement(keyFat, 3); + dietPlansObject.setDecrement(keyFat, 2.5); // assert final fatValue = dietPlansObject.get(keyFat); expect(fatValue, isA()); - expect(fatValue, equals(2)); + expect(fatValue, equals(1)); }, ); + test('setDecrement() should account for pervasively set value', () { + // arrange + dietPlansObject.set(keyFat, 5); + + // act + dietPlansObject.setDecrement(keyFat, 3); + + // assert + final fatValue = dietPlansObject.get(keyFat); + + expect(fatValue, isA()); + + expect(fatValue, equals(2)); + }); + test( 'the amount that should be subtracted from a value for the API should be incremental' ' and independent from the estimated value (the decrement operation result)', @@ -228,15 +216,11 @@ void main() { equals(-5.0), ); - expect( - dietPlansObject.get(keyFat), - equals(5.0), - ); + expect(dietPlansObject.get(keyFat), equals(5.0)); }, ); - test( - 'mixing and matching Decrements and Increments should not cause ' + test('mixing and matching Decrements and Increments should not cause ' 'any issue', () { // act dietPlansObject.setDecrement(keyFat, 2.5); @@ -255,8 +239,7 @@ void main() { expect(fatValue, equals(1)); }); - test( - 'setDecrement() operation should not be mergeable with any other' + test('setDecrement() operation should not be mergeable with any other' 'operation other than setIncrement()', () { testUnmergeableOperationShouldThrow( parseObject: dietPlansObject, @@ -265,13 +248,12 @@ void main() { ); }); - test( - 'When calling clearUnsavedChanges() the number should be reverted back' + test('When calling clearUnsavedChanges() the number should be reverted back' ' to its original state before any modifications were made', () { // arrange dietPlansObject.fromJson({ 'myNumber': 5, - "objectId": "someId" + "objectId": "someId", }); // assume this coming from the server dietPlansObject.setIncrement('myNumber', 5); @@ -285,13 +267,12 @@ void main() { expect(number, equals(5)); }); - test( - 'The number internal state should be identical ' + test('The number internal state should be identical ' 'before and after storing it in data store', () async { // arrange dietPlansObject.fromJson({ 'myNumber': 5, - "objectId": "someId" + "objectId": "someId", }); // assume this coming from the server dietPlansObject.setIncrement('myNumber', 5); @@ -316,113 +297,112 @@ void main() { ); }); - test( - 'If an Increment/Decrement operation is performed during the save() ' - 'function, the result of the operation should be present in the internal ' - 'state of the ParseNumber as a value that has not been saved. The data ' - 'that has been saved should be moved to the saved state', - () async { - // arrange - const resultFromServer = { - keyVarObjectId: "DLde4rYA8C", - keyVarCreatedAt: "2023-02-26T00:20:37.187Z" - }; + test('If an Increment/Decrement operation is performed during the save() ' + 'function, the result of the operation should be present in the internal ' + 'state of the ParseNumber as a value that has not been saved. The data ' + 'that has been saved should be moved to the saved state', () async { + // arrange + const resultFromServer = { + keyVarObjectId: "DLde4rYA8C", + keyVarCreatedAt: "2023-02-26T00:20:37.187Z", + }; - when(client.post( - any, - options: anyNamed("options"), - data: anyNamed('data'), - )).thenAnswer( - (_) async { - await Future.delayed(Duration(milliseconds: 100)); - return ParseNetworkResponse( - statusCode: 200, - data: jsonEncode(resultFromServer), - ); - }, + when( + client.post(any, options: anyNamed("options"), data: anyNamed('data')), + ).thenAnswer((_) async { + await Future.delayed(Duration(milliseconds: 100)); + return ParseNetworkResponse( + statusCode: 200, + data: jsonEncode(resultFromServer), ); + }); - dietPlansObject.setIncrement('myNumber', 1); + dietPlansObject.setIncrement('myNumber', 1); - final numberBeforeSave = dietPlansObject.get('myNumber'); - final valueForApiReqBeforeSave = dietPlansObject.toJson(forApiRQ: true); + final numberBeforeSave = dietPlansObject.get('myNumber'); + final valueForApiReqBeforeSave = dietPlansObject.toJson(forApiRQ: true); - // act - dietPlansObject.save(); + // act + dietPlansObject.save(); - // async gap, this could be anything in the app like a click of a button - await Future.delayed(Duration.zero); + // async gap, this could be anything in the app like a click of a button + await Future.delayed(Duration.zero); - // Then suddenly the user increment the value - dietPlansObject.setIncrement('myNumber', 3); + // Then suddenly the user increment the value + dietPlansObject.setIncrement('myNumber', 3); - // Await the save function to be done - await Future.delayed(Duration(milliseconds: 150)); + // Await the save function to be done + await Future.delayed(Duration(milliseconds: 150)); - // assert - expect(numberBeforeSave, equals(1)); + // assert + expect(numberBeforeSave, equals(1)); - final numberAfterSave = dietPlansObject.get('myNumber'); - expect(numberAfterSave, equals(4)); + final numberAfterSave = dietPlansObject.get('myNumber'); + expect(numberAfterSave, equals(4)); - const expectedValueForApiReqBeforeSave = { - "myNumber": {"__op": "Increment", "amount": 1} - }; - expect( - DeepCollectionEquality().equals( - valueForApiReqBeforeSave, - expectedValueForApiReqBeforeSave, - ), - isTrue, - ); + const expectedValueForApiReqBeforeSave = { + "myNumber": {"__op": "Increment", "amount": 1}, + }; + expect( + DeepCollectionEquality().equals( + valueForApiReqBeforeSave, + expectedValueForApiReqBeforeSave, + ), + isTrue, + ); - final valueForApiReqAfterSave = dietPlansObject.toJson(forApiRQ: true); - const expectedValueForApiReqAfterSave = { - "myNumber": {"__op": "Increment", "amount": 3} - }; - expect( - DeepCollectionEquality().equals( - valueForApiReqAfterSave, - expectedValueForApiReqAfterSave, - ), - isTrue, - ); - }, - ); + final valueForApiReqAfterSave = dietPlansObject.toJson(forApiRQ: true); + const expectedValueForApiReqAfterSave = { + "myNumber": {"__op": "Increment", "amount": 3}, + }; + expect( + DeepCollectionEquality().equals( + valueForApiReqAfterSave, + expectedValueForApiReqAfterSave, + ), + isTrue, + ); + }); test( - 'The number value and the number value for api request should be identical ' - 'before and after the save() failed to save the object', () { - // arrange + 'The number value and the number value for api request should be identical ' + 'before and after the save() failed to save the object', + () { + // arrange - when(client.post( - any, - options: anyNamed("options"), - data: anyNamed("data"), - )).thenThrow(Exception('error')); + when( + client.post( + any, + options: anyNamed("options"), + data: anyNamed("data"), + ), + ).thenThrow(Exception('error')); - dietPlansObject.setIncrement('myNumber', 1); + dietPlansObject.setIncrement('myNumber', 1); - final valueForApiReqBeforeErrorSave = - dietPlansObject.toJson(forApiRQ: true); + final valueForApiReqBeforeErrorSave = dietPlansObject.toJson( + forApiRQ: true, + ); - // act - dietPlansObject.save(); + // act + dietPlansObject.save(); - // assert - final numberValue = dietPlansObject.get('myNumber'); + // assert + final numberValue = dietPlansObject.get('myNumber'); - expect(numberValue, equals(1)); + expect(numberValue, equals(1)); - final valueForApiReqAfterErrorSave = - dietPlansObject.toJson(forApiRQ: true); - expect( - DeepCollectionEquality().equals( - valueForApiReqAfterErrorSave, - valueForApiReqBeforeErrorSave, - ), - isTrue, - ); - }); + final valueForApiReqAfterErrorSave = dietPlansObject.toJson( + forApiRQ: true, + ); + expect( + DeepCollectionEquality().equals( + valueForApiReqAfterErrorSave, + valueForApiReqBeforeErrorSave, + ), + isTrue, + ); + }, + ); }); } diff --git a/packages/dart/test/src/objects/parse_object/parse_object_query_test.dart b/packages/dart/test/src/objects/parse_object/parse_object_query_test.dart index 0f067abe3..cb23f8f82 100644 --- a/packages/dart/test/src/objects/parse_object/parse_object_query_test.dart +++ b/packages/dart/test/src/objects/parse_object/parse_object_query_test.dart @@ -55,12 +55,12 @@ void main() { "user": { "__type": "Pointer", "className": "_User", - "objectId": "cmWCmCAyQQ" + "objectId": "cmWCmCAyQQ", }, "createdAt": "2023-02-24T15:39:44.800Z", "updatedAt": "2023-02-24T22:28:17.867Z", "location": {"__type": "GeoPoint", "latitude": 50, "longitude": 0}, - "anArray": ["3", "4"] + "anArray": ["3", "4"], }, { "objectId": "15NCdmBFBw", @@ -74,7 +74,7 @@ void main() { "user": { "__type": "Pointer", "className": "_User", - "objectId": "cmWCmCAyQQ" + "objectId": "cmWCmCAyQQ", }, "createdAt": "2023-02-24T15:44:17.781Z", "updatedAt": "2023-02-24T22:28:45.446Z", @@ -83,19 +83,17 @@ void main() { "afile": { "__type": "File", "name": "33b6acb416c0mmer-wallpapers.png", - "url": "https://parsefiles.back4app.com/gyBkQBRSapgwfxB/cers.png" - } - } - ] + "url": "https://parsefiles.back4app.com/gyBkQBRSapgwfxB/cers.png", + }, + }, + ], }; final resultList = resultFromServer["results"]!; final firstObject = ParseObject('Diet_Plans').fromJson(resultList[0]); final secondObject = ParseObject('Diet_Plans').fromJson(resultList[1]); - when(client.get( - getPath, - )).thenAnswer( + when(client.get(getPath)).thenAnswer( (_) async => ParseNetworkResponse( statusCode: 200, data: jsonEncode(resultFromServer), @@ -129,9 +127,7 @@ void main() { equals(jsonEncode(secondObject.toJson())), ); - verify(client.get( - getPath, - )).called(1); + verify(client.get(getPath)).called(1); verifyNoMoreInteractions(client); }); @@ -140,9 +136,7 @@ void main() { // arrange final error = Exception('error'); - when(client.get( - getPath, - )).thenThrow(error); + when(client.get(getPath)).thenThrow(error); // act final response = await dietPlansObject.query(stringQuery); @@ -162,9 +156,7 @@ void main() { expect(response.error!.code, equals(ParseError.otherCause)); - verify(client.get( - getPath, - )).called(1); + verify(client.get(getPath)).called(1); verifyNoMoreInteractions(client); }); diff --git a/packages/dart/test/src/objects/parse_object/parse_object_relation_test.dart b/packages/dart/test/src/objects/parse_object/parse_object_relation_test.dart index 773cd58c8..7150f1b1b 100644 --- a/packages/dart/test/src/objects/parse_object/parse_object_relation_test.dart +++ b/packages/dart/test/src/objects/parse_object/parse_object_relation_test.dart @@ -41,20 +41,13 @@ void main() { "__op": "AddRelation", "objects": [ {"__type": "Pointer", "className": "_User", "objectId": "user1"}, - { - "__type": "Pointer", - "className": "_User", - "objectId": "user2", - } - ] - } + {"__type": "Pointer", "className": "_User", "objectId": "user2"}, + ], + }, }; expect( - DeepCollectionEquality().equals( - expectedToJson, - toJsonAfterAddRelation, - ), + DeepCollectionEquality().equals(expectedToJson, toJsonAfterAddRelation), isTrue, ); }); @@ -94,13 +87,15 @@ void main() { ); }); - test('removeRelation() operation should not be mergeable with any other', - () { - testUnmergeableOperationShouldThrow( - parseObject: dietPlansObject, - testingOn: dietPlansObject.removeRelation, - ); - }); + test( + 'removeRelation() operation should not be mergeable with any other', + () { + testUnmergeableOperationShouldThrow( + parseObject: dietPlansObject, + testingOn: dietPlansObject.removeRelation, + ); + }, + ); test('getParent() should rerun the parent of the relation', () { // arrange @@ -125,20 +120,20 @@ void main() { }); test( - 'getTargetClass() should rerun null if the relation target class not known yet', - () { - // arrange - final relation = dietPlansObject.getRelation('someRelationKey'); + 'getTargetClass() should rerun null if the relation target class not known yet', + () { + // arrange + final relation = dietPlansObject.getRelation('someRelationKey'); - // act - final targetClass = relation.targetClass; + // act + final targetClass = relation.targetClass; - // assert - expect(targetClass, isNull); - }); + // assert + expect(targetClass, isNull); + }, + ); - test( - 'getTargetClass() should rerun the target class for the relation if ' + test('getTargetClass() should rerun the target class for the relation if ' 'the user adds an object from the relation', () { // arrange final relation = dietPlansObject.getRelation('someRelationKey'); @@ -151,8 +146,7 @@ void main() { expect(targetClass, equals('someClassNameAsTargetClass')); }); - test( - 'getTargetClass() should rerun the target class for the relation if ' + test('getTargetClass() should rerun the target class for the relation if ' 'the user removes an object from the relation', () { // arrange final relation = dietPlansObject.getRelation('someRelationKey'); @@ -165,15 +159,14 @@ void main() { expect(targetClass, equals('someClassNameAsTargetClass')); }); - test( - 'getTargetClass() should return the target class for a relation when' + test('getTargetClass() should return the target class for a relation when' ' the object is received from the server', () { // arrange dietPlansObject.fromJson({ "someRelationKey": { "__type": "Relation", - "className": "someClassNameAsTargetClass" - } + "className": "someClassNameAsTargetClass", + }, }); // assume this from the server final relation = dietPlansObject.getRelation('someRelationKey'); @@ -185,17 +178,21 @@ void main() { expect(targetClass, equals('someClassNameAsTargetClass')); }); - test('getQuery() should throw exception if the parent objectId is null ', - () { - // arrange - final relation = dietPlansObject.getRelation('someRelationKey'); + test( + 'getQuery() should throw exception if the parent objectId is null ', + () { + // arrange + final relation = dietPlansObject.getRelation('someRelationKey'); - // assert - expect(() => relation.getQuery(), throwsA(isA())); - }); + // assert + expect( + () => relation.getQuery(), + throwsA(isA()), + ); + }, + ); - test( - 'getQuery() should return QueryBuilder utilizing the ' + test('getQuery() should return QueryBuilder utilizing the ' 'redirectClassNameForKey feature if the target class is null ', () { // arrange dietPlansObject.objectId = "someParentID"; @@ -231,64 +228,69 @@ void main() { }); test( - 'should throw an exception when trying to modify the target class if it is not null', - () { - // arrange - final relation = dietPlansObject.getRelation('someRelationKey'); - relation.add(ParseObject('someClassNameAsTargetClass')); + 'should throw an exception when trying to modify the target class if it is not null', + () { + // arrange + final relation = dietPlansObject.getRelation('someRelationKey'); + relation.add(ParseObject('someClassNameAsTargetClass')); - // assert - expect( - () => relation.setTargetClass = "someOtherTargetClass", - throwsA(isA()), - ); - }); + // assert + expect( + () => relation.setTargetClass = "someOtherTargetClass", + throwsA(isA()), + ); + }, + ); test( - 'When calling clearUnsavedChanges() the Relation should be reverted back' - ' to its original state before any modifications were made', () { - // arrange + 'When calling clearUnsavedChanges() the Relation should be reverted back' + ' to its original state before any modifications were made', + () { + // arrange - dietPlansObject.addRelation('someRelationKey', [user1, user1]); + dietPlansObject.addRelation('someRelationKey', [user1, user1]); - // act - dietPlansObject.clearUnsavedChanges(); + // act + dietPlansObject.clearUnsavedChanges(); - // assert - final valueForApiReqAfterClearUnSaved = - dietPlansObject.toJson(forApiRQ: true); + // assert + final valueForApiReqAfterClearUnSaved = dietPlansObject.toJson( + forApiRQ: true, + ); - expect(valueForApiReqAfterClearUnSaved.isEmpty, isTrue); + expect(valueForApiReqAfterClearUnSaved.isEmpty, isTrue); - final relationValueForApiReq = - dietPlansObject.getRelation('someRelationKey').toJson(); - expect(relationValueForApiReq.isEmpty, isTrue); - }); + final relationValueForApiReq = dietPlansObject + .getRelation('someRelationKey') + .toJson(); + expect(relationValueForApiReq.isEmpty, isTrue); + }, + ); - test( - 'The Relation value and the value for api request should be identical ' + test('The Relation value and the value for api request should be identical ' 'before and after the save() failed to save the object', () async { // arrange - when(client.post( - any, - options: anyNamed("options"), - data: anyNamed("data"), - )).thenThrow(Exception('error')); + when( + client.post(any, options: anyNamed("options"), data: anyNamed("data")), + ).thenThrow(Exception('error')); dietPlansObject.addRelation('someRelationKey', [user1, user1]); - final valueForApiReqBeforeErrorSave = - dietPlansObject.toJson(forApiRQ: true); + final valueForApiReqBeforeErrorSave = dietPlansObject.toJson( + forApiRQ: true, + ); - final relationInternalStateBeforeErrorSave = - dietPlansObject.getRelation('someRelationKey').toJson(full: true); + final relationInternalStateBeforeErrorSave = dietPlansObject + .getRelation('someRelationKey') + .toJson(full: true); // act await dietPlansObject.save(); // assert - final valueForApiReqAfterErrorSave = - dietPlansObject.toJson(forApiRQ: true); + final valueForApiReqAfterErrorSave = dietPlansObject.toJson( + forApiRQ: true, + ); expect( DeepCollectionEquality().equals( valueForApiReqAfterErrorSave, @@ -297,8 +299,9 @@ void main() { isTrue, ); - final relationInternalStateAfterErrorSave = - dietPlansObject.getRelation('someRelationKey').toJson(full: true); + final relationInternalStateAfterErrorSave = dietPlansObject + .getRelation('someRelationKey') + .toJson(full: true); expect( DeepCollectionEquality().equals( @@ -310,262 +313,251 @@ void main() { }); test( - 'After the save() function runs successfully for an API request, ' - 'the ParseRelation internal value for API request should be empty', - () async { - // arrange - - // batch arrange - const resultFromServerForBatch = [ - { - "success": { - keyVarObjectId: 'YAfSAWwXbL', - keyVarCreatedAt: "2023-03-10T12:23:45.678Z", - } - } - ]; + 'After the save() function runs successfully for an API request, ' + 'the ParseRelation internal value for API request should be empty', + () async { + // arrange - final batchData = jsonEncode( - { + // batch arrange + const resultFromServerForBatch = [ + { + "success": { + keyVarObjectId: 'YAfSAWwXbL', + keyVarCreatedAt: "2023-03-10T12:23:45.678Z", + }, + }, + ]; + + final batchData = jsonEncode({ "requests": [ { 'method': 'PUT', 'path': '$keyEndPointClasses${user1.parseClassName}/${user1.objectId}', 'body': user1.toJson(forApiRQ: true), - } - ] - }, - ); + }, + ], + }); - final batchPath = Uri.parse('$serverUrl/batch').toString(); + final batchPath = Uri.parse('$serverUrl/batch').toString(); - when(client.post( - batchPath, - options: anyNamed("options"), - data: batchData, - )).thenAnswer( - (_) async { + when( + client.post(batchPath, options: anyNamed("options"), data: batchData), + ).thenAnswer((_) async { return ParseNetworkResponse( statusCode: 200, data: jsonEncode(resultFromServerForBatch), ); - }, - ); - - // post arrange - const resultFromServer = { - keyVarObjectId: "DLde4rYA8C", - keyVarCreatedAt: "2023-02-26T00:20:37.187Z" - }; - - final postPath = Uri.parse( - '$serverUrl$keyEndPointClasses${dietPlansObject.parseClassName}', - ).toString(); - - when(client.post( - postPath, - options: anyNamed("options"), - data: anyNamed("data"), - )).thenAnswer( - (_) async => ParseNetworkResponse( - statusCode: 200, - data: jsonEncode(resultFromServer), - ), - ); + }); + + // post arrange + const resultFromServer = { + keyVarObjectId: "DLde4rYA8C", + keyVarCreatedAt: "2023-02-26T00:20:37.187Z", + }; + + final postPath = Uri.parse( + '$serverUrl$keyEndPointClasses${dietPlansObject.parseClassName}', + ).toString(); + + when( + client.post( + postPath, + options: anyNamed("options"), + data: anyNamed("data"), + ), + ).thenAnswer( + (_) async => ParseNetworkResponse( + statusCode: 200, + data: jsonEncode(resultFromServer), + ), + ); - dietPlansObject.addRelation('someRelationKey', [user1]); + dietPlansObject.addRelation('someRelationKey', [user1]); - // act - await dietPlansObject.save(); + // act + await dietPlansObject.save(); - // assert - final relationValueForApiReq = - dietPlansObject.getRelation('someRelationKey').toJson(); - expect(relationValueForApiReq.isEmpty, isTrue); - }); + // assert + final relationValueForApiReq = dietPlansObject + .getRelation('someRelationKey') + .toJson(); + expect(relationValueForApiReq.isEmpty, isTrue); + }, + ); test( - 'If a Relation operation is performed during the save() function, the result' - ' of the operation should be present in the internal state of the ' - 'ParseRelation as a value that has not been saved. The data that has ' - 'been saved should not be in value for API request', () async { - // arrange + 'If a Relation operation is performed during the save() function, the result' + ' of the operation should be present in the internal state of the ' + 'ParseRelation as a value that has not been saved. The data that has ' + 'been saved should not be in value for API request', + () async { + // arrange - // batch arrange - const resultFromServerForBatch = [ - { - "success": { - keyVarUpdatedAt: "2023-03-10T12:23:45.678Z", - } - } - ]; + // batch arrange + const resultFromServerForBatch = [ + { + "success": {keyVarUpdatedAt: "2023-03-10T12:23:45.678Z"}, + }, + ]; - final batchData = jsonEncode( - { + final batchData = jsonEncode({ "requests": [ { 'method': 'PUT', 'path': '$keyEndPointClasses${user1.parseClassName}/${user1.objectId}', 'body': user1.toJson(forApiRQ: true), - } - ] - }, - ); + }, + ], + }); - final batchPath = Uri.parse('$serverUrl/batch').toString(); + final batchPath = Uri.parse('$serverUrl/batch').toString(); - when(client.post( - batchPath, - options: anyNamed("options"), - data: batchData, - )).thenAnswer( - (_) async { + when( + client.post(batchPath, options: anyNamed("options"), data: batchData), + ).thenAnswer((_) async { return ParseNetworkResponse( statusCode: 200, data: jsonEncode(resultFromServerForBatch), ); - }, - ); - - // post arrange - const resultFromServer = { - keyVarObjectId: "DLde4rYA8C", - keyVarCreatedAt: "2023-02-26T00:20:37.187Z" - }; - - final postPath = Uri.parse( - '$serverUrl$keyEndPointClasses${dietPlansObject.parseClassName}', - ).toString(); - - when(client.post( - postPath, - options: anyNamed("options"), - data: anyNamed("data"), - )).thenAnswer( - (_) async { + }); + + // post arrange + const resultFromServer = { + keyVarObjectId: "DLde4rYA8C", + keyVarCreatedAt: "2023-02-26T00:20:37.187Z", + }; + + final postPath = Uri.parse( + '$serverUrl$keyEndPointClasses${dietPlansObject.parseClassName}', + ).toString(); + + when( + client.post( + postPath, + options: anyNamed("options"), + data: anyNamed("data"), + ), + ).thenAnswer((_) async { await Future.delayed(Duration(milliseconds: 100)); return ParseNetworkResponse( statusCode: 200, data: jsonEncode(resultFromServer), ); - }, - ); + }); - dietPlansObject.addRelation('someRelationKey', [user1]); + dietPlansObject.addRelation('someRelationKey', [user1]); - // act - dietPlansObject.save(); + // act + dietPlansObject.save(); - // async gap, this could be anything in the app like a click of a button - await Future.delayed(Duration.zero); + // async gap, this could be anything in the app like a click of a button + await Future.delayed(Duration.zero); - // Then suddenly the user adds a object to the relation - dietPlansObject.addRelation('someRelationKey', [user2]); + // Then suddenly the user adds a object to the relation + dietPlansObject.addRelation('someRelationKey', [user2]); - // Await the save function to be done - await Future.delayed(Duration(milliseconds: 150)); + // Await the save function to be done + await Future.delayed(Duration(milliseconds: 150)); - // assert - final relationValueForApiReq = - dietPlansObject.getRelation('someRelationKey').toJson(); + // assert + final relationValueForApiReq = dietPlansObject + .getRelation('someRelationKey') + .toJson(); - final expectedValueAfterSave = { - '__op': 'AddRelation', - 'objects': parseEncode([user2]) - }; + final expectedValueAfterSave = { + '__op': 'AddRelation', + 'objects': parseEncode([user2]), + }; - expect( - DeepCollectionEquality().equals( - relationValueForApiReq, - expectedValueAfterSave, - ), - isTrue, - ); - }); + expect( + DeepCollectionEquality().equals( + relationValueForApiReq, + expectedValueAfterSave, + ), + isTrue, + ); + }, + ); - test( - 'ParseRelation value for api request should be identical ' + test('ParseRelation value for api request should be identical ' 'before and after the save() failed to save the object', () async { // arrange // batch arrange const resultFromServerForBatch = [ { - "success": { - keyVarUpdatedAt: "2023-03-10T12:23:45.678Z", - } - } + "success": {keyVarUpdatedAt: "2023-03-10T12:23:45.678Z"}, + }, ]; - final batchData = jsonEncode( - { - "requests": [ - { - 'method': 'PUT', - 'path': - '$keyEndPointClasses${user1.parseClassName}/${user1.objectId}', - 'body': user1.toJson(forApiRQ: true), - } - ] - }, - ); + final batchData = jsonEncode({ + "requests": [ + { + 'method': 'PUT', + 'path': + '$keyEndPointClasses${user1.parseClassName}/${user1.objectId}', + 'body': user1.toJson(forApiRQ: true), + }, + ], + }); final batchPath = Uri.parse('$serverUrl/batch').toString(); - when(client.post( - batchPath, - options: anyNamed("options"), - data: batchData, - )).thenAnswer( - (_) async { - return ParseNetworkResponse( - statusCode: 200, - data: jsonEncode(resultFromServerForBatch), - ); - }, - ); + when( + client.post(batchPath, options: anyNamed("options"), data: batchData), + ).thenAnswer((_) async { + return ParseNetworkResponse( + statusCode: 200, + data: jsonEncode(resultFromServerForBatch), + ); + }); // post arrange final postPath = Uri.parse( '$serverUrl$keyEndPointClasses${dietPlansObject.parseClassName}', ).toString(); - when(client.post( - postPath, - options: anyNamed("options"), - data: anyNamed("data"), - )).thenThrow(Exception('error')); + when( + client.post( + postPath, + options: anyNamed("options"), + data: anyNamed("data"), + ), + ).thenThrow(Exception('error')); dietPlansObject.addRelation('someRelationKey', [user1]); - final relationValueForApiReqBeforeErrorSave = - dietPlansObject.getRelation('someRelationKey').toJson(); + final relationValueForApiReqBeforeErrorSave = dietPlansObject + .getRelation('someRelationKey') + .toJson(); // act await dietPlansObject.save(); // assert - final relationValueForApiReqAfterErrorSave = - dietPlansObject.getRelation('someRelationKey').toJson(); + final relationValueForApiReqAfterErrorSave = dietPlansObject + .getRelation('someRelationKey') + .toJson(); expect( - DeepCollectionEquality().equals( - relationValueForApiReqBeforeErrorSave, - relationValueForApiReqAfterErrorSave, - ), - isTrue); + DeepCollectionEquality().equals( + relationValueForApiReqBeforeErrorSave, + relationValueForApiReqAfterErrorSave, + ), + isTrue, + ); }); - test( - 'The Relation internal state should be identical before and after ' + test('The Relation internal state should be identical before and after ' 'storing it in data store', () async { // arrange dietPlansObject.objectId = "someId"; - final ParseRelation relation = - dietPlansObject.getRelation('someRelationKey'); + final ParseRelation relation = dietPlansObject.getRelation( + 'someRelationKey', + ); relation.remove(ParseObject('someClassName')); final toJsonBeforePin = relation.toJson(full: true); @@ -576,8 +568,9 @@ void main() { final ParseObject objectFromPin = await dietPlansObject.fromPin('someId'); // assert - final toJsonAfterPin = - objectFromPin.getRelation('someRelationKey').toJson(full: true); + final toJsonAfterPin = objectFromPin + .getRelation('someRelationKey') + .toJson(full: true); expect( DeepCollectionEquality().equals(toJsonBeforePin, toJsonAfterPin), @@ -585,12 +578,12 @@ void main() { ); }); - test( - 'should throw an exception if the user adds/removes a parse object' + test('should throw an exception if the user adds/removes a parse object' ' with different target class', () { // arrange - final ParseRelation relation = - dietPlansObject.getRelation('someRelationKey'); + final ParseRelation relation = dietPlansObject.getRelation( + 'someRelationKey', + ); relation.remove(ParseObject('someClassName')..objectId = "123"); relation.remove(ParseObject('someClassName')..objectId = '456'); @@ -603,8 +596,7 @@ void main() { ); }); - test( - 'If the value for API request is empty in ParseRelation then the' + test('If the value for API request is empty in ParseRelation then the' ' ParseRelation should not be part of the end map for' ' API request of an object', () { // arrange @@ -621,8 +613,7 @@ void main() { expect(valueFroApiRequest.isEmpty, isTrue); }); - test( - 'Should throw exception when getRelation() called on key' + test('Should throw exception when getRelation() called on key' ' holds value other than Relation or null', () { // arrange dietPlansObject.set('someRelationKey', 'some String'); diff --git a/packages/dart/test/src/objects/parse_object/parse_object_save_eventually_test.dart b/packages/dart/test/src/objects/parse_object/parse_object_save_eventually_test.dart index 5ba020a01..4624f88d7 100644 --- a/packages/dart/test/src/objects/parse_object/parse_object_save_eventually_test.dart +++ b/packages/dart/test/src/objects/parse_object/parse_object_save_eventually_test.dart @@ -27,25 +27,27 @@ void main() { 'should exist parse object in ParseCoreData next saveEventually', () async { // arrange - when(client.post( - any, - options: anyNamed("options"), - data: anyNamed("data"), - )).thenThrow(Exception('NetworkError')); + when( + client.post( + any, + options: anyNamed("options"), + data: anyNamed("data"), + ), + ).thenThrow(Exception('NetworkError')); - when(client.post( - "$serverUrl/batch", - options: anyNamed("options"), - data: anyNamed("data"), - )).thenAnswer( + when( + client.post( + "$serverUrl/batch", + options: anyNamed("options"), + data: anyNamed("data"), + ), + ).thenAnswer( (_) async => ParseNetworkResponse( statusCode: 200, data: jsonEncode([ { - "success": { - "add": "ok", - } - } + "success": {"add": "ok"}, + }, ]), ), ); diff --git a/packages/dart/test/src/objects/parse_object/parse_object_save_test.dart b/packages/dart/test/src/objects/parse_object/parse_object_save_test.dart index 9738d143f..071500378 100644 --- a/packages/dart/test/src/objects/parse_object/parse_object_save_test.dart +++ b/packages/dart/test/src/objects/parse_object/parse_object_save_test.dart @@ -24,140 +24,136 @@ void main() { }); test( - 'save should store an object online. but store its children objects first ' - 'then store the parent object. the children should be stored using batch request ' - 'and the parent using normal request ', () async { - // arrange - final planObject = ParseObject('Plans')..set('PlanName', 'plan name'); + 'save should store an object online. but store its children objects first ' + 'then store the parent object. the children should be stored using batch request ' + 'and the parent using normal request ', + () async { + // arrange + final planObject = ParseObject('Plans')..set('PlanName', 'plan name'); - dietPlansObject.set('Plan', planObject); + dietPlansObject.set('Plan', planObject); - dietPlansObject.set('Fat', 15); + dietPlansObject.set('Fat', 15); - // batch arrange - const planObjectIdFromServer = "YAfSAWwXbL"; - const planCreatedAtFromServer = "2023-03-10T12:23:45.678Z"; + // batch arrange + const planObjectIdFromServer = "YAfSAWwXbL"; + const planCreatedAtFromServer = "2023-03-10T12:23:45.678Z"; - const resultFromServerForBatch = [ - { - "success": { - keyVarCreatedAt: planCreatedAtFromServer, - keyVarObjectId: planObjectIdFromServer - } - } - ]; + const resultFromServerForBatch = [ + { + "success": { + keyVarCreatedAt: planCreatedAtFromServer, + keyVarObjectId: planObjectIdFromServer, + }, + }, + ]; - // parse server batch syntax - // see https://docs.parseplatform.org/rest/guide/#batch-operations - final batchData = jsonEncode( - { + // parse server batch syntax + // see https://docs.parseplatform.org/rest/guide/#batch-operations + final batchData = jsonEncode({ "requests": [ // object to be saved in batch { 'method': 'POST', 'path': '$keyEndPointClasses${planObject.parseClassName}', 'body': planObject.toJson(forApiRQ: true), - } - ] - }, - ); + }, + ], + }); - final batchPath = Uri.parse('$serverUrl/batch').toString(); + final batchPath = Uri.parse('$serverUrl/batch').toString(); - when(client.post( - batchPath, - options: anyNamed("options"), - data: batchData, - )).thenAnswer( - (_) async => ParseNetworkResponse( - statusCode: 200, - data: jsonEncode(resultFromServerForBatch), - ), - ); + when( + client.post(batchPath, options: anyNamed("options"), data: batchData), + ).thenAnswer( + (_) async => ParseNetworkResponse( + statusCode: 200, + data: jsonEncode(resultFromServerForBatch), + ), + ); - // post arrange + // post arrange - final postPath = Uri.parse( - '$serverUrl$keyEndPointClasses${dietPlansObject.parseClassName}', - ).toString(); + final postPath = Uri.parse( + '$serverUrl$keyEndPointClasses${dietPlansObject.parseClassName}', + ).toString(); - const dietPlansObjectIdFromServer = "BBERrYA8C"; - const dietPlansCreatedAtFromServer = "2023-02-26T00:20:37.187Z"; + const dietPlansObjectIdFromServer = "BBERrYA8C"; + const dietPlansCreatedAtFromServer = "2023-02-26T00:20:37.187Z"; - const resultFromServer = { - keyVarObjectId: dietPlansObjectIdFromServer, - keyVarCreatedAt: dietPlansCreatedAtFromServer, - }; + const resultFromServer = { + keyVarObjectId: dietPlansObjectIdFromServer, + keyVarCreatedAt: dietPlansCreatedAtFromServer, + }; - when(client.post( - postPath, - options: anyNamed("options"), - data: anyNamed("data"), - )).thenAnswer( - (_) async => ParseNetworkResponse( - statusCode: 200, - data: jsonEncode(resultFromServer), - ), - ); + when( + client.post( + postPath, + options: anyNamed("options"), + data: anyNamed("data"), + ), + ).thenAnswer( + (_) async => ParseNetworkResponse( + statusCode: 200, + data: jsonEncode(resultFromServer), + ), + ); - // act - final response = await dietPlansObject.save(); + // act + final response = await dietPlansObject.save(); - // assert - expect(response.success, isTrue); + // assert + expect(response.success, isTrue); - expect(response.error, isNull); + expect(response.error, isNull); - expect(response.count, equals(1)); + expect(response.count, equals(1)); - final resultList = response.results; + final resultList = response.results; - expect(resultList, isNotNull); + expect(resultList, isNotNull); - expect(resultList!.length, equals(1)); + expect(resultList!.length, equals(1)); - expect(resultList, isA>()); + expect(resultList, isA>()); - final savedDietPlansObject = (resultList.first as ParseObject); + final savedDietPlansObject = (resultList.first as ParseObject); - // the calling object (dietPlansObject) will be identical to the object - // in the ParseResponse results - expect( - identical(dietPlansObject, savedDietPlansObject), - isTrue, - ); + // the calling object (dietPlansObject) will be identical to the object + // in the ParseResponse results + expect(identical(dietPlansObject, savedDietPlansObject), isTrue); - expect(dietPlansObject.objectId, equals(dietPlansObjectIdFromServer)); + expect(dietPlansObject.objectId, equals(dietPlansObjectIdFromServer)); - expect(planObject.objectId, equals(planObjectIdFromServer)); + expect(planObject.objectId, equals(planObjectIdFromServer)); - expect( - dietPlansObject.createdAt!.toIso8601String(), - equals(dietPlansCreatedAtFromServer), - ); + expect( + dietPlansObject.createdAt!.toIso8601String(), + equals(dietPlansCreatedAtFromServer), + ); - expect( - planObject.createdAt!.toIso8601String(), - equals(planCreatedAtFromServer), - ); + expect( + planObject.createdAt!.toIso8601String(), + equals(planCreatedAtFromServer), + ); - verify(client.post( - batchPath, - options: anyNamed("options"), - data: batchData, - )).called(1); + verify( + client.post(batchPath, options: anyNamed("options"), data: batchData), + ).called(1); - verify(client.post( - postPath, - options: anyNamed("options"), - data: anyNamed('data'), - )).called(1); + verify( + client.post( + postPath, + options: anyNamed("options"), + data: anyNamed('data'), + ), + ).called(1); - verifyNoMoreInteractions(client); - }); + verifyNoMoreInteractions(client); + }, + ); - test( - 'should return unsuccessful response if there is a unbreakable ' + test('should return unsuccessful response if there is a unbreakable ' 'cycle between two or more unsaved objects', () async { // arrange final planObject = ParseObject('Plans'); @@ -176,8 +172,7 @@ void main() { verifyZeroInteractions(client); }); - test( - 'save should updated an object online and store and updated any object ' + test('save should updated an object online and store and updated any object ' 'added via relation', () async { // arrange dietPlansObject.objectId = "NNAfSGGHHbL"; @@ -189,10 +184,10 @@ void main() { ..objectId = "EEWAfSdXbL" ..set('PlanName', 'plan 2'); - dietPlansObject.addRelation( - 'relatedPlans', - [planObjectToCreate, planObjectToUpdate], - ); + dietPlansObject.addRelation('relatedPlans', [ + planObjectToCreate, + planObjectToUpdate, + ]); dietPlansObject.set('Fat', 15); @@ -206,41 +201,35 @@ void main() { { "success": { keyVarCreatedAt: planToCreateCreatedAtFromServer, - keyVarObjectId: planToCreateObjectIdFromServer - } + keyVarObjectId: planToCreateObjectIdFromServer, + }, }, { - "success": { - keyVarUpdatedAt: planToUpdateUpdatedAtFromServer, - } - } + "success": {keyVarUpdatedAt: planToUpdateUpdatedAtFromServer}, + }, ]; - final batchData = jsonEncode( - { - "requests": [ - { - 'method': 'POST', - 'path': '$keyEndPointClasses${planObjectToCreate.parseClassName}', - 'body': planObjectToCreate.toJson(forApiRQ: true), - }, - { - 'method': 'PUT', - 'path': - '$keyEndPointClasses${planObjectToUpdate.parseClassName}/${planObjectToUpdate.objectId}', - 'body': planObjectToUpdate.toJson(forApiRQ: true), - } - ] - }, - ); + final batchData = jsonEncode({ + "requests": [ + { + 'method': 'POST', + 'path': '$keyEndPointClasses${planObjectToCreate.parseClassName}', + 'body': planObjectToCreate.toJson(forApiRQ: true), + }, + { + 'method': 'PUT', + 'path': + '$keyEndPointClasses${planObjectToUpdate.parseClassName}/${planObjectToUpdate.objectId}', + 'body': planObjectToUpdate.toJson(forApiRQ: true), + }, + ], + }); final batchPath = Uri.parse('$serverUrl/batch').toString(); - when(client.post( - batchPath, - options: anyNamed("options"), - data: batchData, - )).thenAnswer( + when( + client.post(batchPath, options: anyNamed("options"), data: batchData), + ).thenAnswer( (_) async => ParseNetworkResponse( statusCode: 200, data: jsonEncode(resultFromServerForBatch), @@ -254,15 +243,15 @@ void main() { ).toString(); const dietPlansUpdatedAtFromServer = "2023-02-26T00:20:37.187Z"; - const resultFromServer = { - keyVarUpdatedAt: dietPlansUpdatedAtFromServer, - }; - - when(client.put( - putPath, - data: anyNamed("data"), - options: anyNamed("options"), - )).thenAnswer( + const resultFromServer = {keyVarUpdatedAt: dietPlansUpdatedAtFromServer}; + + when( + client.put( + putPath, + data: anyNamed("data"), + options: anyNamed("options"), + ), + ).thenAnswer( (_) async => ParseNetworkResponse( statusCode: 200, data: jsonEncode(resultFromServer), @@ -291,10 +280,7 @@ void main() { // the calling object (dietPlansObject) will be identical to the object // in the ParseResponse results - expect( - identical(dietPlansObject, savedDietPlansObject), - isTrue, - ); + expect(identical(dietPlansObject, savedDietPlansObject), isTrue); expect( dietPlansObject.updatedAt!.toIso8601String(), @@ -316,17 +302,17 @@ void main() { equals(planToUpdateUpdatedAtFromServer), ); - verify(client.post( - batchPath, - options: anyNamed("options"), - data: batchData, - )).called(1); + verify( + client.post(batchPath, options: anyNamed("options"), data: batchData), + ).called(1); - verify(client.put( - putPath, - options: anyNamed("options"), - data: anyNamed('data'), - )).called(1); + verify( + client.put( + putPath, + options: anyNamed("options"), + data: anyNamed('data'), + ), + ).called(1); verifyNoMoreInteractions(client); }); @@ -352,40 +338,36 @@ void main() { "success": { keyVarObjectId: "YAfSAWwXbL", keyVarCreatedAt: "2023-03-10T12:23:45.678Z", - } + }, }, { "error": { "code": ParseError.internalServerError, "error": "internal server error", - } - } + }, + }, ]; - final batchData = jsonEncode( - { - "requests": [ - { - 'method': 'POST', - 'path': '$keyEndPointClasses${planObject1.parseClassName}', - 'body': planObject1.toJson(forApiRQ: true), - }, - { - 'method': 'POST', - 'path': '$keyEndPointClasses${planObject2.parseClassName}', - 'body': planObject2.toJson(forApiRQ: true), - } - ] - }, - ); + final batchData = jsonEncode({ + "requests": [ + { + 'method': 'POST', + 'path': '$keyEndPointClasses${planObject1.parseClassName}', + 'body': planObject1.toJson(forApiRQ: true), + }, + { + 'method': 'POST', + 'path': '$keyEndPointClasses${planObject2.parseClassName}', + 'body': planObject2.toJson(forApiRQ: true), + }, + ], + }); final batchPath = Uri.parse('$serverUrl/batch').toString(); - when(client.post( - batchPath, - options: anyNamed("options"), - data: batchData, - )).thenAnswer( + when( + client.post(batchPath, options: anyNamed("options"), data: batchData), + ).thenAnswer( (_) async => ParseNetworkResponse( statusCode: 200, data: jsonEncode(resultFromServerForBatch), @@ -453,10 +435,7 @@ void main() { // to promote the secondResponse var to ParseError (secondResponse as ParseError); - expect( - secondResponse.code, - equals(errorResponseFromServer['code']), - ); + expect(secondResponse.code, equals(errorResponseFromServer['code'])); expect( secondResponse.message, @@ -469,17 +448,17 @@ void main() { reason: 'dietPlansObject should not be saved', ); - verify(client.post( - batchPath, - options: anyNamed("options"), - data: batchData, - )).called(1); + verify( + client.post(batchPath, options: anyNamed("options"), data: batchData), + ).called(1); - verifyNever(client.post( - postPath, - options: anyNamed("options"), - data: anyNamed('data'), - )); + verifyNever( + client.post( + postPath, + options: anyNamed("options"), + data: anyNamed('data'), + ), + ); verifyNoMoreInteractions(client); }, diff --git a/packages/dart/test/src/objects/parse_object/parse_object_test.dart b/packages/dart/test/src/objects/parse_object/parse_object_test.dart index 02fbf8f63..3848916f3 100644 --- a/packages/dart/test/src/objects/parse_object/parse_object_test.dart +++ b/packages/dart/test/src/objects/parse_object/parse_object_test.dart @@ -6,8 +6,7 @@ import '../../../test_utils.dart'; @GenerateMocks([ParseClient]) void main() { - test( - 'The parseClassName property in the ParseObject class should be equal ' + test('The parseClassName property in the ParseObject class should be equal ' 'to the name passed via the constructor', () async { // arrange diff --git a/packages/dart/test/src/objects/parse_object/parse_object_test.mocks.dart b/packages/dart/test/src/objects/parse_object/parse_object_test.mocks.dart index 53fd24c44..39a066aef 100644 --- a/packages/dart/test/src/objects/parse_object/parse_object_test.mocks.dart +++ b/packages/dart/test/src/objects/parse_object/parse_object_test.mocks.dart @@ -24,35 +24,20 @@ import 'package:parse_server_sdk/parse_server_sdk.dart' as _i2; // ignore_for_file: invalid_use_of_internal_member class _FakeParseCoreData_0 extends _i1.SmartFake implements _i2.ParseCoreData { - _FakeParseCoreData_0( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); + _FakeParseCoreData_0(Object parent, Invocation parentInvocation) + : super(parent, parentInvocation); } class _FakeParseNetworkResponse_1 extends _i1.SmartFake implements _i2.ParseNetworkResponse { - _FakeParseNetworkResponse_1( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); + _FakeParseNetworkResponse_1(Object parent, Invocation parentInvocation) + : super(parent, parentInvocation); } class _FakeParseNetworkByteResponse_2 extends _i1.SmartFake implements _i2.ParseNetworkByteResponse { - _FakeParseNetworkByteResponse_2( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); + _FakeParseNetworkByteResponse_2(Object parent, Invocation parentInvocation) + : super(parent, parentInvocation); } /// A class which mocks [ParseClient]. @@ -64,13 +49,12 @@ class MockParseClient extends _i1.Mock implements _i2.ParseClient { } @override - _i2.ParseCoreData get data => (super.noSuchMethod( - Invocation.getter(#data), - returnValue: _FakeParseCoreData_0( - this, - Invocation.getter(#data), - ), - ) as _i2.ParseCoreData); + _i2.ParseCoreData get data => + (super.noSuchMethod( + Invocation.getter(#data), + returnValue: _FakeParseCoreData_0(this, Invocation.getter(#data)), + ) + as _i2.ParseCoreData); @override _i3.Future<_i2.ParseNetworkResponse> get( @@ -79,27 +63,23 @@ class MockParseClient extends _i1.Mock implements _i2.ParseClient { _i2.ProgressCallback? onReceiveProgress, }) => (super.noSuchMethod( - Invocation.method( - #get, - [path], - { - #options: options, - #onReceiveProgress: onReceiveProgress, - }, - ), - returnValue: _i3.Future<_i2.ParseNetworkResponse>.value( - _FakeParseNetworkResponse_1( - this, - Invocation.method( - #get, - [path], - { - #options: options, - #onReceiveProgress: onReceiveProgress, - }, - ), - )), - ) as _i3.Future<_i2.ParseNetworkResponse>); + Invocation.method( + #get, + [path], + {#options: options, #onReceiveProgress: onReceiveProgress}, + ), + returnValue: _i3.Future<_i2.ParseNetworkResponse>.value( + _FakeParseNetworkResponse_1( + this, + Invocation.method( + #get, + [path], + {#options: options, #onReceiveProgress: onReceiveProgress}, + ), + ), + ), + ) + as _i3.Future<_i2.ParseNetworkResponse>); @override _i3.Future<_i2.ParseNetworkResponse> put( @@ -108,27 +88,19 @@ class MockParseClient extends _i1.Mock implements _i2.ParseClient { _i2.ParseNetworkOptions? options, }) => (super.noSuchMethod( - Invocation.method( - #put, - [path], - { - #data: data, - #options: options, - }, - ), - returnValue: _i3.Future<_i2.ParseNetworkResponse>.value( - _FakeParseNetworkResponse_1( - this, - Invocation.method( - #put, - [path], - { - #data: data, - #options: options, - }, - ), - )), - ) as _i3.Future<_i2.ParseNetworkResponse>); + Invocation.method(#put, [path], {#data: data, #options: options}), + returnValue: _i3.Future<_i2.ParseNetworkResponse>.value( + _FakeParseNetworkResponse_1( + this, + Invocation.method( + #put, + [path], + {#data: data, #options: options}, + ), + ), + ), + ) + as _i3.Future<_i2.ParseNetworkResponse>); @override _i3.Future<_i2.ParseNetworkResponse> post( @@ -137,27 +109,19 @@ class MockParseClient extends _i1.Mock implements _i2.ParseClient { _i2.ParseNetworkOptions? options, }) => (super.noSuchMethod( - Invocation.method( - #post, - [path], - { - #data: data, - #options: options, - }, - ), - returnValue: _i3.Future<_i2.ParseNetworkResponse>.value( - _FakeParseNetworkResponse_1( - this, - Invocation.method( - #post, - [path], - { - #data: data, - #options: options, - }, - ), - )), - ) as _i3.Future<_i2.ParseNetworkResponse>); + Invocation.method(#post, [path], {#data: data, #options: options}), + returnValue: _i3.Future<_i2.ParseNetworkResponse>.value( + _FakeParseNetworkResponse_1( + this, + Invocation.method( + #post, + [path], + {#data: data, #options: options}, + ), + ), + ), + ) + as _i3.Future<_i2.ParseNetworkResponse>); @override _i3.Future<_i2.ParseNetworkResponse> postBytes( @@ -168,31 +132,33 @@ class MockParseClient extends _i1.Mock implements _i2.ParseClient { dynamic cancelToken, }) => (super.noSuchMethod( - Invocation.method( - #postBytes, - [path], - { - #data: data, - #options: options, - #onSendProgress: onSendProgress, - #cancelToken: cancelToken, - }, - ), - returnValue: _i3.Future<_i2.ParseNetworkResponse>.value( - _FakeParseNetworkResponse_1( - this, - Invocation.method( - #postBytes, - [path], - { - #data: data, - #options: options, - #onSendProgress: onSendProgress, - #cancelToken: cancelToken, - }, - ), - )), - ) as _i3.Future<_i2.ParseNetworkResponse>); + Invocation.method( + #postBytes, + [path], + { + #data: data, + #options: options, + #onSendProgress: onSendProgress, + #cancelToken: cancelToken, + }, + ), + returnValue: _i3.Future<_i2.ParseNetworkResponse>.value( + _FakeParseNetworkResponse_1( + this, + Invocation.method( + #postBytes, + [path], + { + #data: data, + #options: options, + #onSendProgress: onSendProgress, + #cancelToken: cancelToken, + }, + ), + ), + ), + ) + as _i3.Future<_i2.ParseNetworkResponse>); @override _i3.Future<_i2.ParseNetworkResponse> delete( @@ -200,21 +166,15 @@ class MockParseClient extends _i1.Mock implements _i2.ParseClient { _i2.ParseNetworkOptions? options, }) => (super.noSuchMethod( - Invocation.method( - #delete, - [path], - {#options: options}, - ), - returnValue: _i3.Future<_i2.ParseNetworkResponse>.value( - _FakeParseNetworkResponse_1( - this, - Invocation.method( - #delete, - [path], - {#options: options}, - ), - )), - ) as _i3.Future<_i2.ParseNetworkResponse>); + Invocation.method(#delete, [path], {#options: options}), + returnValue: _i3.Future<_i2.ParseNetworkResponse>.value( + _FakeParseNetworkResponse_1( + this, + Invocation.method(#delete, [path], {#options: options}), + ), + ), + ) + as _i3.Future<_i2.ParseNetworkResponse>); @override _i3.Future<_i2.ParseNetworkByteResponse> getBytes( @@ -224,27 +184,29 @@ class MockParseClient extends _i1.Mock implements _i2.ParseClient { dynamic cancelToken, }) => (super.noSuchMethod( - Invocation.method( - #getBytes, - [path], - { - #options: options, - #onReceiveProgress: onReceiveProgress, - #cancelToken: cancelToken, - }, - ), - returnValue: _i3.Future<_i2.ParseNetworkByteResponse>.value( - _FakeParseNetworkByteResponse_2( - this, - Invocation.method( - #getBytes, - [path], - { - #options: options, - #onReceiveProgress: onReceiveProgress, - #cancelToken: cancelToken, - }, - ), - )), - ) as _i3.Future<_i2.ParseNetworkByteResponse>); + Invocation.method( + #getBytes, + [path], + { + #options: options, + #onReceiveProgress: onReceiveProgress, + #cancelToken: cancelToken, + }, + ), + returnValue: _i3.Future<_i2.ParseNetworkByteResponse>.value( + _FakeParseNetworkByteResponse_2( + this, + Invocation.method( + #getBytes, + [path], + { + #options: options, + #onReceiveProgress: onReceiveProgress, + #cancelToken: cancelToken, + }, + ), + ), + ), + ) + as _i3.Future<_i2.ParseNetworkByteResponse>); } diff --git a/packages/dart/test/src/objects/parse_object/parse_object_unset_test.dart b/packages/dart/test/src/objects/parse_object/parse_object_unset_test.dart index 0e893707b..844aafb7b 100644 --- a/packages/dart/test/src/objects/parse_object/parse_object_unset_test.dart +++ b/packages/dart/test/src/objects/parse_object/parse_object_unset_test.dart @@ -29,8 +29,10 @@ void main() { dietPlansObject.set(keyFat, 2); // act - final ParseResponse parseResponse = - await dietPlansObject.unset(keyFat, offlineOnly: true); + final ParseResponse parseResponse = await dietPlansObject.unset( + keyFat, + offlineOnly: true, + ); // assert expect(parseResponse.success, isTrue); @@ -40,159 +42,155 @@ void main() { verifyZeroInteractions(client); }); - test('unset() should unset a value from ParseObject locally on online', - () async { - // arrange + test( + 'unset() should unset a value from ParseObject locally on online', + () async { + // arrange - dietPlansObject.set(keyFat, 2); - dietPlansObject.objectId = "O6BHlwV48Z"; - - final putPath = Uri.parse( - '$serverUrl$keyEndPointClasses${dietPlansObject.parseClassName}/${dietPlansObject.objectId}', - ).toString(); - - const String putData = '{"$keyFat":{"__op":"Delete"}}'; - const resultFromServer = { - keyVarUpdatedAt: "2023-03-04T03:34:35.076Z", - }; - - when(client.put( - putPath, - options: anyNamed("options"), - data: putData, - )).thenAnswer( - (_) async => ParseNetworkResponse( - statusCode: 200, - data: jsonEncode(resultFromServer), - ), - ); + dietPlansObject.set(keyFat, 2); + dietPlansObject.objectId = "O6BHlwV48Z"; - // act - final ParseResponse parseResponse = - await dietPlansObject.unset(keyFat, offlineOnly: false); + final putPath = Uri.parse( + '$serverUrl$keyEndPointClasses${dietPlansObject.parseClassName}/${dietPlansObject.objectId}', + ).toString(); - // assert - expect(parseResponse.success, isTrue); + const String putData = '{"$keyFat":{"__op":"Delete"}}'; + const resultFromServer = {keyVarUpdatedAt: "2023-03-04T03:34:35.076Z"}; - expect(dietPlansObject.get(keyFat), isNull); + when( + client.put(putPath, options: anyNamed("options"), data: putData), + ).thenAnswer( + (_) async => ParseNetworkResponse( + statusCode: 200, + data: jsonEncode(resultFromServer), + ), + ); - verify(client.put( - putPath, - options: anyNamed("options"), - data: putData, - )).called(1); + // act + final ParseResponse parseResponse = await dietPlansObject.unset( + keyFat, + offlineOnly: false, + ); - verifyNoMoreInteractions(client); - }); + // assert + expect(parseResponse.success, isTrue); + + expect(dietPlansObject.get(keyFat), isNull); + + verify( + client.put(putPath, options: anyNamed("options"), data: putData), + ).called(1); + + verifyNoMoreInteractions(client); + }, + ); test( - 'If objectId is null, unset() should unset a value from ParseObject ' - 'locally and not make any call to the server and return unsuccessful Response', - () async { - // arrange - dietPlansObject.set(keyFat, 2); + 'If objectId is null, unset() should unset a value from ParseObject ' + 'locally and not make any call to the server and return unsuccessful Response', + () async { + // arrange + dietPlansObject.set(keyFat, 2); - // act - final parseResponse = - await dietPlansObject.unset(keyFat, offlineOnly: false); + // act + final parseResponse = await dietPlansObject.unset( + keyFat, + offlineOnly: false, + ); - // assert - expect(parseResponse.success, isFalse); + // assert + expect(parseResponse.success, isFalse); - expect(parseResponse.result, isNull); + expect(parseResponse.result, isNull); - expect(parseResponse.count, isZero); + expect(parseResponse.count, isZero); - expect(parseResponse.statusCode, ParseError.otherCause); + expect(parseResponse.statusCode, ParseError.otherCause); - expect(dietPlansObject.get(keyFat), isNull); + expect(dietPlansObject.get(keyFat), isNull); - verifyZeroInteractions(client); - }); + verifyZeroInteractions(client); + }, + ); test( - 'unset() should keep the the key and its value if the request was unsuccessful', - () async { - // arrange + 'unset() should keep the the key and its value if the request was unsuccessful', + () async { + // arrange - dietPlansObject.objectId = "O6BHlwV48Z"; - dietPlansObject.set(keyFat, 2); + dietPlansObject.objectId = "O6BHlwV48Z"; + dietPlansObject.set(keyFat, 2); - final errorData = jsonEncode({keyCode: -1, keyError: "someError"}); - - when(client.put( - any, - options: anyNamed("options"), - data: anyNamed("data"), - )).thenAnswer( - (_) async => ParseNetworkResponse( - statusCode: -1, - data: errorData, - ), - ); + final errorData = jsonEncode({keyCode: -1, keyError: "someError"}); - // act - final parseResponse = - await dietPlansObject.unset(keyFat, offlineOnly: false); + when( + client.put(any, options: anyNamed("options"), data: anyNamed("data")), + ).thenAnswer( + (_) async => ParseNetworkResponse(statusCode: -1, data: errorData), + ); - // assert - expect(parseResponse.success, isFalse); + // act + final parseResponse = await dietPlansObject.unset( + keyFat, + offlineOnly: false, + ); - expect(parseResponse.error, isNotNull); + // assert + expect(parseResponse.success, isFalse); - expect(parseResponse.error!.message, equals('someError')); + expect(parseResponse.error, isNotNull); - expect(parseResponse.error!.code, equals(-1)); + expect(parseResponse.error!.message, equals('someError')); - expect(dietPlansObject.get(keyFat), equals(2)); + expect(parseResponse.error!.code, equals(-1)); - verify(client.put( - any, - options: anyNamed("options"), - data: anyNamed("data"), - )).called(1); + expect(dietPlansObject.get(keyFat), equals(2)); - verifyNoMoreInteractions(client); - }); + verify( + client.put(any, options: anyNamed("options"), data: anyNamed("data")), + ).called(1); + + verifyNoMoreInteractions(client); + }, + ); test( - 'unset() should keep the the key and its value if the request throws an exception', - () async { - // arrange + 'unset() should keep the the key and its value if the request throws an exception', + () async { + // arrange - dietPlansObject.objectId = "O6BHlwV48Z"; - dietPlansObject.set(keyFat, 2); + dietPlansObject.objectId = "O6BHlwV48Z"; + dietPlansObject.set(keyFat, 2); - final error = Exception('error'); + final error = Exception('error'); - when(client.put( - any, - options: anyNamed("options"), - data: anyNamed("data"), - )).thenThrow(error); + when( + client.put(any, options: anyNamed("options"), data: anyNamed("data")), + ).thenThrow(error); - // act - final parseResponse = - await dietPlansObject.unset(keyFat, offlineOnly: false); + // act + final parseResponse = await dietPlansObject.unset( + keyFat, + offlineOnly: false, + ); - // assert - expect(parseResponse.success, isFalse); + // assert + expect(parseResponse.success, isFalse); - expect(parseResponse.error, isNotNull); + expect(parseResponse.error, isNotNull); - expect(parseResponse.error!.exception, equals(error)); + expect(parseResponse.error!.exception, equals(error)); - expect(parseResponse.error!.code, equals(-1)); + expect(parseResponse.error!.code, equals(-1)); - expect(dietPlansObject.get(keyFat), equals(2)); + expect(dietPlansObject.get(keyFat), equals(2)); - verify(client.put( - any, - options: anyNamed("options"), - data: anyNamed("data"), - )).called(1); + verify( + client.put(any, options: anyNamed("options"), data: anyNamed("data")), + ).called(1); - verifyNoMoreInteractions(client); - }); + verifyNoMoreInteractions(client); + }, + ); }); } diff --git a/packages/dart/test/src/objects/parse_object/parse_object_update_test.dart b/packages/dart/test/src/objects/parse_object/parse_object_update_test.dart index ca8e9085e..12f5e37ee 100644 --- a/packages/dart/test/src/objects/parse_object/parse_object_update_test.dart +++ b/packages/dart/test/src/objects/parse_object/parse_object_update_test.dart @@ -38,24 +38,19 @@ void main() { '$serverUrl$keyEndPointClasses${dietPlansObject.parseClassName}/${dietPlansObject.objectId}', ).toString(); }); - test( - 'update() should update an object on the server, return the updated ' + test('update() should update an object on the server, return the updated ' 'object in ParseResponse results and update the calling object ' 'with the new data (updatedAt).' 'i.e: mutate the object state to reflect the new update', () async { // arrange - const resultFromServer = { - keyVarUpdatedAt: "2023-02-26T13:25:27.865Z", - }; + const resultFromServer = {keyVarUpdatedAt: "2023-02-26T13:25:27.865Z"}; final putData = jsonEncode(dietPlansObject.toJson(forApiRQ: true)); - when(client.put( - putPath, - options: anyNamed("options"), - data: putData, - )).thenAnswer( + when( + client.put(putPath, options: anyNamed("options"), data: putData), + ).thenAnswer( (_) async => ParseNetworkResponse( statusCode: 200, data: jsonEncode(resultFromServer), @@ -83,89 +78,72 @@ void main() { equals(resultFromServer[keyVarUpdatedAt]), ); - expect( - parseObjectFromRes.get(keyName), - equals(newNameValue), - ); - expect( - parseObjectFromRes.get(keyFat), - equals(newFatValue), - ); + expect(parseObjectFromRes.get(keyName), equals(newNameValue)); + expect(parseObjectFromRes.get(keyFat), equals(newFatValue)); // the calling object (dietPlansObject) will be identical to the object // in the ParseResponse results - expect( - identical(dietPlansObject, parseObjectFromRes), - isTrue, - ); + expect(identical(dietPlansObject, parseObjectFromRes), isTrue); - verify(client.put( - putPath, - options: anyNamed("options"), - data: putData, - )).called(1); + verify( + client.put(putPath, options: anyNamed("options"), data: putData), + ).called(1); verifyNoMoreInteractions(client); }); test( - 'update() should return error and the updated values should remain the same', - () async { - // arrange + 'update() should return error and the updated values should remain the same', + () async { + // arrange - final putData = jsonEncode(dietPlansObject.toJson(forApiRQ: true)); - final error = Exception('error'); + final putData = jsonEncode(dietPlansObject.toJson(forApiRQ: true)); + final error = Exception('error'); - when(client.put( - putPath, - options: anyNamed("options"), - data: putData, - )).thenThrow(error); + when( + client.put(putPath, options: anyNamed("options"), data: putData), + ).thenThrow(error); - // act - ParseResponse response = await dietPlansObject.update(); + // act + ParseResponse response = await dietPlansObject.update(); - // assert - expect(response.success, isFalse); + // assert + expect(response.success, isFalse); - expect(response.result, isNull); + expect(response.result, isNull); - expect(response.count, isZero); + expect(response.count, isZero); - expect(response.results, isNull); + expect(response.results, isNull); - expect(response.error, isNotNull); + expect(response.error, isNotNull); - expect(response.error!.exception, equals(error)); + expect(response.error!.exception, equals(error)); - expect(response.error!.code, equals(ParseError.otherCause)); + expect(response.error!.code, equals(ParseError.otherCause)); - // even if the update failed, the updated values should remain the same - expect(dietPlansObject.get(keyName), equals(newNameValue)); - expect(dietPlansObject.get(keyFat), equals(newFatValue)); + // even if the update failed, the updated values should remain the same + expect(dietPlansObject.get(keyName), equals(newNameValue)); + expect(dietPlansObject.get(keyFat), equals(newFatValue)); - verify(client.put( - putPath, - options: anyNamed("options"), - data: putData, - )).called(1); + verify( + client.put(putPath, options: anyNamed("options"), data: putData), + ).called(1); - verifyNoMoreInteractions(client); - }); + verifyNoMoreInteractions(client); + }, + ); - test( - 'update() should return error form the server and the' + test('update() should return error form the server and the' ' updated values should remain the same', () async { // arrange final putData = jsonEncode(dietPlansObject.toJson(forApiRQ: true)); final errorData = jsonEncode({keyCode: -1, keyError: "someError"}); - when(client.put( - putPath, - options: anyNamed("options"), - data: putData, - )).thenAnswer( + when( + client.put(putPath, options: anyNamed("options"), data: putData), + ).thenAnswer( (_) async => ParseNetworkResponse(data: errorData, statusCode: -1), ); @@ -191,11 +169,9 @@ void main() { expect(dietPlansObject.get(keyName), equals(newNameValue)); expect(dietPlansObject.get(keyFat), equals(newFatValue)); - verify(client.put( - putPath, - options: anyNamed("options"), - data: putData, - )).called(1); + verify( + client.put(putPath, options: anyNamed("options"), data: putData), + ).called(1); verifyNoMoreInteractions(client); }); diff --git a/packages/dart/test/src/objects/response/parse_response_utils_test.dart b/packages/dart/test/src/objects/response/parse_response_utils_test.dart index 759ca6826..a01247589 100644 --- a/packages/dart/test/src/objects/response/parse_response_utils_test.dart +++ b/packages/dart/test/src/objects/response/parse_response_utils_test.dart @@ -13,152 +13,148 @@ void main() { group('handleResponse()', () { group('when batch', () { test( - 'should return a ParseResponse holds a list of created/updated ParseObjects', - () { - // arrange - final object1 = ParseObject("object1"); + 'should return a ParseResponse holds a list of created/updated ParseObjects', + () { + // arrange + final object1 = ParseObject("object1"); - final object2 = ParseObject("object2")..objectId = "GYfSRRRXbL"; + final object2 = ParseObject("object2")..objectId = "GYfSRRRXbL"; - final objectsToBatch = [object1, object2]; + final objectsToBatch = [object1, object2]; - const object1ResultFromServerObjectId = "YAfSAWwXbL"; - const object1ResultFromServerCreatedAt = "2023-03-19T00:20:37.187Z"; + const object1ResultFromServerObjectId = "YAfSAWwXbL"; + const object1ResultFromServerCreatedAt = "2023-03-19T00:20:37.187Z"; - const object2ResultFromServerUpdatedAt = "2023-03-19T00:20:37.187Z"; + const object2ResultFromServerUpdatedAt = "2023-03-19T00:20:37.187Z"; - final resultFromServerForBatch = json.encode( - [ + final resultFromServerForBatch = json.encode([ { "success": { keyVarObjectId: object1ResultFromServerObjectId, keyVarCreatedAt: object1ResultFromServerCreatedAt, - } + }, }, { - "success": { - keyVarUpdatedAt: object2ResultFromServerUpdatedAt, - } - } - ], - ); + "success": {keyVarUpdatedAt: object2ResultFromServerUpdatedAt}, + }, + ]); - final result = ParseNetworkResponse( - data: resultFromServerForBatch, - statusCode: 200, - ); + final result = ParseNetworkResponse( + data: resultFromServerForBatch, + statusCode: 200, + ); - // act - final response = handleResponse( - objectsToBatch, - result, - ParseApiRQ.batch, - true, - 'test_batch', - ); + // act + final response = handleResponse( + objectsToBatch, + result, + ParseApiRQ.batch, + true, + 'test_batch', + ); - // assert - expect(response.success, isTrue); + // assert + expect(response.success, isTrue); - expect(response.count, equals(2)); + expect(response.count, equals(2)); - expect(response.error, isNull); + expect(response.error, isNull); - final resultsObjectsList = List.from(response.results!); + final resultsObjectsList = List.from(response.results!); - expect(resultsObjectsList.length, equals(2)); + expect(resultsObjectsList.length, equals(2)); - final firstObject = resultsObjectsList[0]; - final secondObject = resultsObjectsList[1]; + final firstObject = resultsObjectsList[0]; + final secondObject = resultsObjectsList[1]; - expect(firstObject.objectId, equals(object1ResultFromServerObjectId)); + expect(firstObject.objectId, equals(object1ResultFromServerObjectId)); - expect( - firstObject.createdAt!.toIso8601String(), - equals(object1ResultFromServerCreatedAt), - ); + expect( + firstObject.createdAt!.toIso8601String(), + equals(object1ResultFromServerCreatedAt), + ); - expect( - secondObject.updatedAt!.toIso8601String(), - equals(object2ResultFromServerUpdatedAt), - ); - }); + expect( + secondObject.updatedAt!.toIso8601String(), + equals(object2ResultFromServerUpdatedAt), + ); + }, + ); test( - 'should return a ParseResponse holds a list of ParseObject and ParseError', - () { - // arrange - final object1 = ParseObject("object1"); - final object2 = ParseObject("object2"); + 'should return a ParseResponse holds a list of ParseObject and ParseError', + () { + // arrange + final object1 = ParseObject("object1"); + final object2 = ParseObject("object2"); - final objectsToBatch = [object1, object2]; + final objectsToBatch = [object1, object2]; - const object1ResultFromServerObjectId = "YAfSAWwXbL"; - const object1ResultFromServerCreatedAt = "2023-03-19T00:20:37.187Z"; + const object1ResultFromServerObjectId = "YAfSAWwXbL"; + const object1ResultFromServerCreatedAt = "2023-03-19T00:20:37.187Z"; - final resultFromServerForBatch = json.encode( - [ + final resultFromServerForBatch = json.encode([ { "success": { keyVarObjectId: object1ResultFromServerObjectId, keyVarCreatedAt: object1ResultFromServerCreatedAt, - } + }, }, // error while saving the second object { "error": { "code": ParseError.internalServerError, "error": "internal server error", - } - } - ], - ); + }, + }, + ]); - final result = ParseNetworkResponse( - data: resultFromServerForBatch, - statusCode: 200, - ); + final result = ParseNetworkResponse( + data: resultFromServerForBatch, + statusCode: 200, + ); - // act - final response = handleResponse( - objectsToBatch, - result, - ParseApiRQ.batch, - true, - 'test_batch', - ); + // act + final response = handleResponse( + objectsToBatch, + result, + ParseApiRQ.batch, + true, + 'test_batch', + ); - // assert - expect(response.success, isTrue); + // assert + expect(response.success, isTrue); - expect(response.count, equals(2)); + expect(response.count, equals(2)); - expect(response.error, isNull); + expect(response.error, isNull); - expect(response.results, isNotNull); + expect(response.results, isNotNull); - final resultsList = response.results!; + final resultsList = response.results!; - expect(resultsList.length, equals(2)); + expect(resultsList.length, equals(2)); - final ParseObject firstObject = resultsList[0]; + final ParseObject firstObject = resultsList[0]; - final ParseError secondObjectError = resultsList[1]; + final ParseError secondObjectError = resultsList[1]; - expect(firstObject.objectId, equals(object1ResultFromServerObjectId)); + expect(firstObject.objectId, equals(object1ResultFromServerObjectId)); - expect( - firstObject.createdAt!.toIso8601String(), - equals(object1ResultFromServerCreatedAt), - ); + expect( + firstObject.createdAt!.toIso8601String(), + equals(object1ResultFromServerCreatedAt), + ); - expect( - secondObjectError.code, - equals(ParseError.internalServerError), - ); + expect( + secondObjectError.code, + equals(ParseError.internalServerError), + ); - expect(object2.objectId, isNull); - }); + expect(object2.objectId, isNull); + }, + ); }); }); } diff --git a/packages/dart/test/src/utils/parse_encoder_test.dart b/packages/dart/test/src/utils/parse_encoder_test.dart index afc442d48..eaf4b0328 100644 --- a/packages/dart/test/src/utils/parse_encoder_test.dart +++ b/packages/dart/test/src/utils/parse_encoder_test.dart @@ -47,8 +47,8 @@ void main() { "lastPreformedOperation": { "__op": "Increment", "amount": 4.0, - "estimatedValue": 4 - } + "estimatedValue": 4, + }, }, "string_val": "some String", "double_val": { @@ -59,44 +59,44 @@ void main() { "lastPreformedOperation": { "__op": "Increment", "amount": 0.5, - "estimatedValue": 0.5 - } + "estimatedValue": 0.5, + }, }, "array_1_val": { "className": "ParseArray", "estimatedArray": [1, 2, 3, 3], "savedArray": [], - "lastPreformedOperation": null + "lastPreformedOperation": null, }, "array_2_val": { "className": "ParseArray", "estimatedArray": [1, 2, 3, 4], "savedArray": [], - "lastPreformedOperation": null + "lastPreformedOperation": null, }, "array_3_val": { "className": "ParseArray", "estimatedArray": [1, 2], "savedArray": [], - "lastPreformedOperation": null + "lastPreformedOperation": null, }, "relation_val": { "className": "ParseRelation", "targetClass": "object_in_relation2", "key": "relation_val", "objects": [ - {"className": "object_in_relation2", "objectId": "GDIJPWW"} + {"className": "object_in_relation2", "objectId": "GDIJPWW"}, ], "lastPreformedOperation": { "__op": "AddRelation", "objects": [ - {"className": "object_in_relation2", "objectId": "GDIJPWW"} + {"className": "object_in_relation2", "objectId": "GDIJPWW"}, ], "valueForAPIRequest": [ - {"className": "object_in_relation2", "objectId": "GDIJPWW"} - ] - } - } + {"className": "object_in_relation2", "objectId": "GDIJPWW"}, + ], + }, + }, }; expect(jsonEncode(encodeResult), equals(jsonEncode(expectedValue))); diff --git a/packages/dart/test/test_utils.dart b/packages/dart/test/test_utils.dart index fbf7ee43b..1889872b6 100644 --- a/packages/dart/test/test_utils.dart +++ b/packages/dart/test/test_utils.dart @@ -62,14 +62,8 @@ void testUnmergeableOperationShouldThrow({ String testingOnKey = 'key'; final Map operationsFuncRefWithArgs = { - parseObject.setAdd: [ - testingOnKey, - 1, - ], - parseObject.setAddUnique: [ - testingOnKey, - 1, - ], + parseObject.setAdd: [testingOnKey, 1], + parseObject.setAddUnique: [testingOnKey, 1], parseObject.setAddAll: [ testingOnKey, [1, 2], @@ -78,29 +72,20 @@ void testUnmergeableOperationShouldThrow({ testingOnKey, [1, 2], ], - parseObject.setRemove: [ - testingOnKey, - 1, - ], + parseObject.setRemove: [testingOnKey, 1], parseObject.setRemoveAll: [ testingOnKey, - [1, 2] - ], - parseObject.setIncrement: [ - testingOnKey, - 1, - ], - parseObject.setDecrement: [ - testingOnKey, - 1, + [1, 2], ], + parseObject.setIncrement: [testingOnKey, 1], + parseObject.setDecrement: [testingOnKey, 1], parseObject.addRelation: [ testingOnKey, - [ParseObject('class')] + [ParseObject('class')], ], parseObject.removeRelation: [ testingOnKey, - [ParseObject('class')] + [ParseObject('class')], ], }; @@ -121,7 +106,8 @@ void testUnmergeableOperationShouldThrow({ expect( () => Function.apply(testingOn, testingOnValue), throwsA(isA()), - reason: 'Calling {{${functionRef.toString()}}} ' + reason: + 'Calling {{${functionRef.toString()}}} ' 'then {{${testingOn.toString()}}} should throw ParseOperationException', ); } From ae3e138e2da5d17afec0e4fc2b9ee96899fe8e96 Mon Sep 17 00:00:00 2001 From: Manuel Trezza <5673677+mtrezza@users.noreply.github.com> Date: Fri, 28 Nov 2025 21:51:16 +0100 Subject: [PATCH 06/14] Update .gitignore --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index 929c76d27..67a3f690b 100644 --- a/.gitignore +++ b/.gitignore @@ -123,3 +123,6 @@ app.*.symbols !/dev/ci/**/Gemfile.lock /node_modules + +# AI agents +/.claude From ca49b3a706a589c5df6d3108cbd3be814f66898c Mon Sep 17 00:00:00 2001 From: Manuel Trezza <5673677+mtrezza@users.noreply.github.com> Date: Fri, 28 Nov 2025 21:58:56 +0100 Subject: [PATCH 07/14] chore: Add .gitattributes to normalize line endings across platforms --- .gitattributes | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 .gitattributes diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 000000000..859513cb9 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,15 @@ +# Auto normalize line endings for all text files +* text=auto + +# Dart files +*.dart text eol=lf +*.yaml text eol=lf +*.yml text eol=lf +*.md text eol=lf +*.json text eol=lf + +# Scripts +*.sh text eol=lf + +# Generated mock files should also use LF +*.mocks.dart text eol=lf From 9a8a5c1798943959dd58e5fef58b295667bb79a5 Mon Sep 17 00:00:00 2001 From: Manuel Trezza <5673677+mtrezza@users.noreply.github.com> Date: Fri, 28 Nov 2025 22:05:39 +0100 Subject: [PATCH 08/14] Revert "chore: Add .gitattributes to normalize line endings across platforms" This reverts commit ca49b3a706a589c5df6d3108cbd3be814f66898c. --- .gitattributes | 15 --------------- 1 file changed, 15 deletions(-) delete mode 100644 .gitattributes diff --git a/.gitattributes b/.gitattributes deleted file mode 100644 index 859513cb9..000000000 --- a/.gitattributes +++ /dev/null @@ -1,15 +0,0 @@ -# Auto normalize line endings for all text files -* text=auto - -# Dart files -*.dart text eol=lf -*.yaml text eol=lf -*.yml text eol=lf -*.md text eol=lf -*.json text eol=lf - -# Scripts -*.sh text eol=lf - -# Generated mock files should also use LF -*.mocks.dart text eol=lf From 7fed11aa3e6d18ef6d0c7796a67777df704b8f5d Mon Sep 17 00:00:00 2001 From: Manuel Trezza <5673677+mtrezza@users.noreply.github.com> Date: Fri, 28 Nov 2025 22:12:08 +0100 Subject: [PATCH 09/14] fix windows --- .github/workflows/ci.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b02d002ac..847ad306a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -45,6 +45,12 @@ jobs: run: dart pub get --directory packages/dart - name: Run build_runner run: (cd packages/dart && dart run build_runner build --delete-conflicting-outputs) + - name: Normalize line endings in generated mock files (Windows) + if: matrix.os == 'windows-latest' + shell: bash + run: | + find packages/dart/test -name "*.mocks.dart" -type f -exec dos2unix {} \; 2>/dev/null || \ + find packages/dart/test -name "*.mocks.dart" -type f -exec sed -i 's/\r$//' {} \; - name: Analyze code run: dart analyze packages/dart --fatal-infos - name: Lint From 78ce520320cc17e1e82d6ef8b4ed893f331a8986 Mon Sep 17 00:00:00 2001 From: Manuel Trezza <5673677+mtrezza@users.noreply.github.com> Date: Fri, 28 Nov 2025 22:17:15 +0100 Subject: [PATCH 10/14] win2 --- .github/workflows/ci.yml | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 847ad306a..abd293a7b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -47,10 +47,13 @@ jobs: run: (cd packages/dart && dart run build_runner build --delete-conflicting-outputs) - name: Normalize line endings in generated mock files (Windows) if: matrix.os == 'windows-latest' - shell: bash + shell: pwsh run: | - find packages/dart/test -name "*.mocks.dart" -type f -exec dos2unix {} \; 2>/dev/null || \ - find packages/dart/test -name "*.mocks.dart" -type f -exec sed -i 's/\r$//' {} \; + Get-ChildItem -Path packages/dart/test -Filter *.mocks.dart -Recurse | ForEach-Object { + $content = Get-Content $_.FullName -Raw + $content = $content -replace "`r`n", "`n" + [System.IO.File]::WriteAllText($_.FullName, $content) + } - name: Analyze code run: dart analyze packages/dart --fatal-infos - name: Lint From 2aee2f009a8793f29431735ecf177e627880c1a5 Mon Sep 17 00:00:00 2001 From: Manuel Trezza <5673677+mtrezza@users.noreply.github.com> Date: Fri, 28 Nov 2025 22:23:54 +0100 Subject: [PATCH 11/14] win3 --- .github/workflows/ci.yml | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index abd293a7b..c789150df 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -37,6 +37,12 @@ jobs: steps: - name: Checkout code uses: actions/checkout@v4 + - name: Configure git for line endings (Windows) + if: matrix.os == 'windows-latest' + run: | + git config core.autocrlf false + git rm --cached -r . + git reset --hard - name: Setup dart uses: dart-lang/setup-dart@v1.5.0 with: @@ -45,15 +51,6 @@ jobs: run: dart pub get --directory packages/dart - name: Run build_runner run: (cd packages/dart && dart run build_runner build --delete-conflicting-outputs) - - name: Normalize line endings in generated mock files (Windows) - if: matrix.os == 'windows-latest' - shell: pwsh - run: | - Get-ChildItem -Path packages/dart/test -Filter *.mocks.dart -Recurse | ForEach-Object { - $content = Get-Content $_.FullName -Raw - $content = $content -replace "`r`n", "`n" - [System.IO.File]::WriteAllText($_.FullName, $content) - } - name: Analyze code run: dart analyze packages/dart --fatal-infos - name: Lint From 7aab0787b70acbf571c3cba6077c84a631601602 Mon Sep 17 00:00:00 2001 From: Manuel Trezza <5673677+mtrezza@users.noreply.github.com> Date: Fri, 28 Nov 2025 22:35:00 +0100 Subject: [PATCH 12/14] fix flutter --- .github/workflows/ci.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c789150df..e32103318 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -86,13 +86,13 @@ jobs: # The latest Flutter framework (below) is tested on all architectures (Ubuntu, macOS, Windows). - name: Flutter 3.38, Ubuntu os: ubuntu-latest - sdk: 3.38.0 + sdk: 3.38.1 - name: Flutter 3.38, macOS os: macos-latest - sdk: 3.38.0 + sdk: 3.38.1 - name: Flutter 3.38, Windows os: windows-latest - sdk: 3.38.0 + sdk: 3.38.1 # Beta channel helps identify breaking changes early - name: Flutter beta os: ubuntu-latest From 56e408743bba106c1dd280cc28552cc2032c9a05 Mon Sep 17 00:00:00 2001 From: Manuel Trezza <5673677+mtrezza@users.noreply.github.com> Date: Fri, 28 Nov 2025 22:56:41 +0100 Subject: [PATCH 13/14] lint --- .../flutter/lib/parse_server_sdk_flutter.dart | 80 ++++++----- packages/flutter/lib/src/push/parse_push.dart | 15 +- .../src/storage/core_store_directory_io.dart | 6 +- .../lib/src/storage/core_store_sembast.dart | 13 +- .../core_store_shared_preferences.dart | 5 +- .../lib/src/utils/parse_live_grid.dart | 81 +++++------ .../lib/src/utils/parse_live_list.dart | 125 +++++++++------- .../test/parse_client_configuration_test.dart | 23 +-- .../storage/core_store_directory_io_test.dart | 135 +++++++++--------- 9 files changed, 248 insertions(+), 235 deletions(-) diff --git a/packages/flutter/lib/parse_server_sdk_flutter.dart b/packages/flutter/lib/parse_server_sdk_flutter.dart index a7e14cf88..2f1f5a2f9 100644 --- a/packages/flutter/lib/parse_server_sdk_flutter.dart +++ b/packages/flutter/lib/parse_server_sdk_flutter.dart @@ -80,33 +80,37 @@ class Parse extends sdk.Parse } return await super.initialize( - appId, - serverUrl, - debug: debug, - appName: appName, - appVersion: appVersion, - appPackageName: appPackageName, - locale: locale ?? - (sdk.parseIsWeb - ? PlatformDispatcher.instance.locale.toString() - : Platform.localeName), - liveQueryUrl: liveQueryUrl, - clientKey: clientKey, - masterKey: masterKey, - sessionId: sessionId, - autoSendSessionId: autoSendSessionId, - securityContext: securityContext, - coreStore: coreStore ?? await CoreStoreSharedPreferences.getInstance(), - registeredSubClassMap: registeredSubClassMap, - parseUserConstructor: parseUserConstructor, - parseFileConstructor: parseFileConstructor, - liveListRetryIntervals: liveListRetryIntervals, - connectivityProvider: connectivityProvider ?? this, - fileDirectory: - fileDirectory ?? (await CoreStoreDirectory().getTempDirectory()), - appResumedStream: appResumedStream ?? _appResumedStreamController.stream, - clientCreator: clientCreator, - ) as Parse; + appId, + serverUrl, + debug: debug, + appName: appName, + appVersion: appVersion, + appPackageName: appPackageName, + locale: + locale ?? + (sdk.parseIsWeb + ? PlatformDispatcher.instance.locale.toString() + : Platform.localeName), + liveQueryUrl: liveQueryUrl, + clientKey: clientKey, + masterKey: masterKey, + sessionId: sessionId, + autoSendSessionId: autoSendSessionId, + securityContext: securityContext, + coreStore: + coreStore ?? await CoreStoreSharedPreferences.getInstance(), + registeredSubClassMap: registeredSubClassMap, + parseUserConstructor: parseUserConstructor, + parseFileConstructor: parseFileConstructor, + liveListRetryIntervals: liveListRetryIntervals, + connectivityProvider: connectivityProvider ?? this, + fileDirectory: + fileDirectory ?? (await CoreStoreDirectory().getTempDirectory()), + appResumedStream: + appResumedStream ?? _appResumedStreamController.stream, + clientCreator: clientCreator, + ) + as Parse; } final StreamController _appResumedStreamController = @@ -127,17 +131,17 @@ class Parse extends sdk.Parse @override Stream get connectivityStream { - return Connectivity().onConnectivityChanged.map( - (List event) { - if (event.contains(ConnectivityResult.wifi)) { - return sdk.ParseConnectivityResult.wifi; - } else if (event.contains(ConnectivityResult.mobile)) { - return sdk.ParseConnectivityResult.mobile; - } else { - return sdk.ParseConnectivityResult.none; - } - }, - ); + return Connectivity().onConnectivityChanged.map(( + List event, + ) { + if (event.contains(ConnectivityResult.wifi)) { + return sdk.ParseConnectivityResult.wifi; + } else if (event.contains(ConnectivityResult.mobile)) { + return sdk.ParseConnectivityResult.mobile; + } else { + return sdk.ParseConnectivityResult.none; + } + }); } @override diff --git a/packages/flutter/lib/src/push/parse_push.dart b/packages/flutter/lib/src/push/parse_push.dart index 7deaec45c..e931c59d4 100644 --- a/packages/flutter/lib/src/push/parse_push.dart +++ b/packages/flutter/lib/src/push/parse_push.dart @@ -24,10 +24,9 @@ class ParsePush { _parseNotification = parseNotification; // Get Google Cloud Messaging (GCM) token - firebaseMessaging - .getToken(vapidKey: vapidKey) - .asStream() - .listen((event) async { + firebaseMessaging.getToken(vapidKey: vapidKey).asStream().listen(( + event, + ) async { // Set token in installation sdk.ParseInstallation parseInstallation = await sdk.ParseInstallation.currentInstallation(); @@ -55,8 +54,12 @@ class ParsePush { } /// Processes the incoming push notification message. - void _handlePush(String pushId, String timestamp, String channel, - Map? data) { + void _handlePush( + String pushId, + String timestamp, + String channel, + Map? data, + ) { if (pushId.isEmpty || timestamp.isEmpty) { return; } diff --git a/packages/flutter/lib/src/storage/core_store_directory_io.dart b/packages/flutter/lib/src/storage/core_store_directory_io.dart index db531bc90..4535a465b 100644 --- a/packages/flutter/lib/src/storage/core_store_directory_io.dart +++ b/packages/flutter/lib/src/storage/core_store_directory_io.dart @@ -41,11 +41,7 @@ class CoreStoreDirectory { final appDocDirPath = (await path_provider.getApplicationDocumentsDirectory()).path; - final databaseFilePath = path.join( - appDocDirPath, - 'parse', - 'parse.db', - ); + final databaseFilePath = path.join(appDocDirPath, 'parse', 'parse.db'); final dbFile = File(databaseFilePath); diff --git a/packages/flutter/lib/src/storage/core_store_sembast.dart b/packages/flutter/lib/src/storage/core_store_sembast.dart index 5e00d0ee0..56d8de609 100644 --- a/packages/flutter/lib/src/storage/core_store_sembast.dart +++ b/packages/flutter/lib/src/storage/core_store_sembast.dart @@ -6,12 +6,15 @@ class CoreStoreSembast implements sdk.CoreStoreSembastImp { static sdk.CoreStore? _sembastImp; - static Future getInstance( - {DatabaseFactory? factory, String? password}) async { + static Future getInstance({ + DatabaseFactory? factory, + String? password, + }) async { _sembastImp ??= await sdk.CoreStoreSembastImp.getInstance( - await _dbDirectory(), - factory: factory, - password: password); + await _dbDirectory(), + factory: factory, + password: password, + ); return CoreStoreSembast._(); } diff --git a/packages/flutter/lib/src/storage/core_store_shared_preferences.dart b/packages/flutter/lib/src/storage/core_store_shared_preferences.dart index 30afc0a23..ac2ea73cb 100644 --- a/packages/flutter/lib/src/storage/core_store_shared_preferences.dart +++ b/packages/flutter/lib/src/storage/core_store_shared_preferences.dart @@ -11,8 +11,9 @@ class CoreStoreSharedPreferences implements sdk.CoreStore { /// If no instance exists, this function creates a new instance of /// `SharedPreferences` and passes it as a parameter to the constructor of /// `CoreStoreSharedPrefsImp`. - static Future getInstance( - {SharedPreferences? store}) async { + static Future getInstance({ + SharedPreferences? store, + }) async { if (_instance == null) { store ??= await SharedPreferences.getInstance(); _instance = CoreStoreSharedPreferences._internal(store); diff --git a/packages/flutter/lib/src/utils/parse_live_grid.dart b/packages/flutter/lib/src/utils/parse_live_grid.dart index 41407ffd4..b41843376 100644 --- a/packages/flutter/lib/src/utils/parse_live_grid.dart +++ b/packages/flutter/lib/src/utils/parse_live_grid.dart @@ -69,20 +69,18 @@ class ParseLiveGridWidget extends StatefulWidget { State> createState() => _ParseLiveGridWidgetState(); static Widget defaultChildBuilder( - BuildContext context, sdk.ParseLiveListElementSnapshot snapshot) { + BuildContext context, + sdk.ParseLiveListElementSnapshot snapshot, + ) { Widget child; if (snapshot.failed) { child = const Text('something went wrong!'); } else if (snapshot.hasData) { child = ListTile( - title: Text( - snapshot.loadedData!.get(sdk.keyVarObjectId)!, - ), + title: Text(snapshot.loadedData!.get(sdk.keyVarObjectId)!), ); } else { - child = const ListTile( - leading: CircularProgressIndicator(), - ); + child = const ListTile(leading: CircularProgressIndicator()); } return child; } @@ -113,8 +111,9 @@ class _ParseLiveGridWidgetState } setState(() { _liveGrid = value; - _liveGrid!.stream - .listen((sdk.ParseLiveListEvent event) { + _liveGrid!.stream.listen(( + sdk.ParseLiveListEvent event, + ) { if (mounted) { setState(() {}); } @@ -145,48 +144,40 @@ class _ParseLiveGridWidgetState Widget buildAnimatedGrid(sdk.ParseLiveList liveGrid) { Animation boxAnimation; - boxAnimation = Tween( - begin: 0.0, - end: 1.0, - ).animate( + boxAnimation = Tween(begin: 0.0, end: 1.0).animate( CurvedAnimation( // TODO: AnimationController is always null, so this breaks parent: widget.animationController!, - curve: const Interval( - 0, - 0.5, - curve: Curves.decelerate, - ), + curve: const Interval(0, 0.5, curve: Curves.decelerate), ), ); return GridView.builder( - reverse: widget.reverse, - padding: widget.padding, - physics: widget.scrollPhysics, - controller: widget.scrollController, - scrollDirection: widget.scrollDirection, - shrinkWrap: widget.shrinkWrap, - itemCount: liveGrid.size, - gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( - crossAxisCount: widget.crossAxisCount, - crossAxisSpacing: widget.crossAxisSpacing, - mainAxisSpacing: widget.mainAxisSpacing, - childAspectRatio: widget.childAspectRatio), - itemBuilder: ( - BuildContext context, - int index, - ) { - return ParseLiveListElementWidget( - key: ValueKey(liveGrid.getIdentifier(index)), - stream: () => liveGrid.getAt(index), - loadedData: () => liveGrid.getLoadedAt(index)!, - preLoadedData: () => liveGrid.getPreLoadedAt(index)!, - sizeFactor: boxAnimation, - duration: widget.duration, - childBuilder: - widget.childBuilder ?? ParseLiveGridWidget.defaultChildBuilder, - ); - }); + reverse: widget.reverse, + padding: widget.padding, + physics: widget.scrollPhysics, + controller: widget.scrollController, + scrollDirection: widget.scrollDirection, + shrinkWrap: widget.shrinkWrap, + itemCount: liveGrid.size, + gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: widget.crossAxisCount, + crossAxisSpacing: widget.crossAxisSpacing, + mainAxisSpacing: widget.mainAxisSpacing, + childAspectRatio: widget.childAspectRatio, + ), + itemBuilder: (BuildContext context, int index) { + return ParseLiveListElementWidget( + key: ValueKey(liveGrid.getIdentifier(index)), + stream: () => liveGrid.getAt(index), + loadedData: () => liveGrid.getLoadedAt(index)!, + preLoadedData: () => liveGrid.getPreLoadedAt(index)!, + sizeFactor: boxAnimation, + duration: widget.duration, + childBuilder: + widget.childBuilder ?? ParseLiveGridWidget.defaultChildBuilder, + ); + }, + ); } @override diff --git a/packages/flutter/lib/src/utils/parse_live_list.dart b/packages/flutter/lib/src/utils/parse_live_list.dart index da212eb07..ce335f89f 100644 --- a/packages/flutter/lib/src/utils/parse_live_list.dart +++ b/packages/flutter/lib/src/utils/parse_live_list.dart @@ -1,8 +1,11 @@ part of 'package:parse_server_sdk_flutter/parse_server_sdk_flutter.dart'; /// The type of function that builds a child widget for a ParseLiveList element. -typedef ChildBuilder = Widget Function( - BuildContext context, sdk.ParseLiveListElementSnapshot snapshot); +typedef ChildBuilder = + Widget Function( + BuildContext context, + sdk.ParseLiveListElementSnapshot snapshot, + ); /// The type of function that returns the stream to listen for updates from. typedef StreamGetter = Stream Function(); @@ -67,7 +70,9 @@ class ParseLiveListWidget extends StatefulWidget { /// The default child builder function used to display a ParseLiveList element. static Widget defaultChildBuilder( - BuildContext context, sdk.ParseLiveListElementSnapshot snapshot) { + BuildContext context, + sdk.ParseLiveListElementSnapshot snapshot, + ) { Widget child; if (snapshot.failed) { child = const Text('something went wrong!'); @@ -79,9 +84,7 @@ class ParseLiveListWidget extends StatefulWidget { ), ); } else { - child = const ListTile( - leading: CircularProgressIndicator(), - ); + child = const ListTile(leading: CircularProgressIndicator()); } return child; } @@ -106,28 +109,33 @@ class _ParseLiveListWidgetState _animatedListKey.currentState; if (animatedListState != null) { if (event is sdk.ParseLiveListAddEvent) { - animatedListState.insertItem(event.index, - duration: widget.duration); + animatedListState.insertItem( + event.index, + duration: widget.duration, + ); setState(() { _noData = liveList.size == 0; }); } else if (event is sdk.ParseLiveListDeleteEvent) { animatedListState.removeItem( - event.index, - (BuildContext context, Animation animation) => - ParseLiveListElementWidget( - key: ValueKey( - event.object.get(sdk.keyVarObjectId) ?? - 'removingItem'), - childBuilder: widget.childBuilder ?? - ParseLiveListWidget.defaultChildBuilder, - sizeFactor: animation, - duration: widget.duration, - loadedData: () => event.object as T, - preLoadedData: () => event.object as T, + event.index, + (BuildContext context, Animation animation) => + ParseLiveListElementWidget( + key: ValueKey( + event.object.get(sdk.keyVarObjectId) ?? + 'removingItem', ), - duration: widget.duration); + childBuilder: + widget.childBuilder ?? + ParseLiveListWidget.defaultChildBuilder, + sizeFactor: animation, + duration: widget.duration, + loadedData: () => event.object as T, + preLoadedData: () => event.object as T, + ), + duration: widget.duration, + ); setState(() { _noData = liveList.size == 0; }); @@ -174,28 +182,30 @@ class _ParseLiveListWidgetState Widget buildAnimatedList(sdk.ParseLiveList liveList) { return AnimatedList( - key: _animatedListKey, - physics: widget.scrollPhysics, - controller: widget.scrollController, - scrollDirection: widget.scrollDirection, - padding: widget.padding, - primary: widget.primary, - reverse: widget.reverse, - shrinkWrap: widget.shrinkWrap, - initialItemCount: liveList.size, - itemBuilder: - (BuildContext context, int index, Animation animation) { - return ParseLiveListElementWidget( - key: ValueKey(liveList.getIdentifier(index)), - stream: () => liveList.getAt(index), - loadedData: () => liveList.getLoadedAt(index), - preLoadedData: () => liveList.getPreLoadedAt(index), - sizeFactor: animation, - duration: widget.duration, - childBuilder: - widget.childBuilder ?? ParseLiveListWidget.defaultChildBuilder, - ); - }); + key: _animatedListKey, + physics: widget.scrollPhysics, + controller: widget.scrollController, + scrollDirection: widget.scrollDirection, + padding: widget.padding, + primary: widget.primary, + reverse: widget.reverse, + shrinkWrap: widget.shrinkWrap, + initialItemCount: liveList.size, + itemBuilder: + (BuildContext context, int index, Animation animation) { + return ParseLiveListElementWidget( + key: ValueKey(liveList.getIdentifier(index)), + stream: () => liveList.getAt(index), + loadedData: () => liveList.getLoadedAt(index), + preLoadedData: () => liveList.getPreLoadedAt(index), + sizeFactor: animation, + duration: widget.duration, + childBuilder: + widget.childBuilder ?? + ParseLiveListWidget.defaultChildBuilder, + ); + }, + ); } @override @@ -208,14 +218,15 @@ class _ParseLiveListWidgetState class ParseLiveListElementWidget extends StatefulWidget { - const ParseLiveListElementWidget( - {super.key, - this.stream, - this.loadedData, - this.preLoadedData, - required this.sizeFactor, - required this.duration, - required this.childBuilder}); + const ParseLiveListElementWidget({ + super.key, + this.stream, + this.loadedData, + this.preLoadedData, + required this.sizeFactor, + required this.duration, + required this.childBuilder, + }); final StreamGetter? stream; final DataGetter? loadedData; @@ -238,15 +249,19 @@ class _ParseLiveListElementWidgetState @override void initState() { _snapshot = sdk.ParseLiveListElementSnapshot( - loadedData: widget.loadedData != null ? widget.loadedData!() : null, - preLoadedData: - widget.preLoadedData != null ? widget.preLoadedData!() : null); + loadedData: widget.loadedData != null ? widget.loadedData!() : null, + preLoadedData: widget.preLoadedData != null + ? widget.preLoadedData!() + : null, + ); if (widget.stream != null) { _streamSubscription = widget.stream!().listen( (T data) { setState(() { _snapshot = sdk.ParseLiveListElementSnapshot( - loadedData: data, preLoadedData: data); + loadedData: data, + preLoadedData: data, + ); }); }, onError: (Object error) { diff --git a/packages/flutter/test/parse_client_configuration_test.dart b/packages/flutter/test/parse_client_configuration_test.dart index 549915bb1..60a384508 100644 --- a/packages/flutter/test/parse_client_configuration_test.dart +++ b/packages/flutter/test/parse_client_configuration_test.dart @@ -7,16 +7,19 @@ void main() { test('testBuilder', () async { // arrange - await Parse().initialize('appId', 'serverUrl', - clientKey: 'clientKey', - liveQueryUrl: 'liveQueryUrl', - appName: 'appName', - appPackageName: 'somePackageName', - appVersion: 'someAppVersion', - masterKey: 'masterKey', - sessionId: 'sessionId', - fileDirectory: 'someDirectory', - debug: true); + await Parse().initialize( + 'appId', + 'serverUrl', + clientKey: 'clientKey', + liveQueryUrl: 'liveQueryUrl', + appName: 'appName', + appPackageName: 'somePackageName', + appVersion: 'someAppVersion', + masterKey: 'masterKey', + sessionId: 'sessionId', + fileDirectory: 'someDirectory', + debug: true, + ); // assert expect(ParseCoreData().applicationId, 'appId'); diff --git a/packages/flutter/test/src/storage/core_store_directory_io_test.dart b/packages/flutter/test/src/storage/core_store_directory_io_test.dart index acb32a053..6408b278e 100644 --- a/packages/flutter/test/src/storage/core_store_directory_io_test.dart +++ b/packages/flutter/test/src/storage/core_store_directory_io_test.dart @@ -59,8 +59,7 @@ void main() { deleteLibraryDir(); }); - test( - 'on ios, should copy the db file if exists from the old dir path ' + test('on ios, should copy the db file if exists from the old dir path ' '(applicationDocumentDirectory) to the new dir path (LibraryDirectory)' ' and the old db file should be deleted from the old dir path ' 'then return the new dir path (LibraryDirectory)', () async { @@ -81,11 +80,7 @@ void main() { 'dbDirectory should be the new db dir path for iOS (LibraryDir)', ); - final newDBFilePath = path.join( - dbDirectory, - 'parse', - 'parse.db', - ); + final newDBFilePath = path.join(dbDirectory, 'parse', 'parse.db'); final newDBFile = File(newDBFilePath); expect(newDBFile.existsSync(), isTrue); expect( @@ -96,76 +91,80 @@ void main() { }); test( - 'on ios, if there is no db file in the old dir (applicationDocumentDirectory)' - ' and there is db file in the new dir (LibraryDirectory) ' - 'the (copy) migration should not work and so the getDatabaseDirectory()' - 'should return the new db dir path (LibraryDirectory)', () async { - debugDefaultTargetPlatformOverride = TargetPlatform.iOS; - - final dbFileInNewPath = create1MBParseDBFileInLibraryPath(); - final dbFileSizeBefore = dbFileInNewPath.lengthSync(); - final dbFileLastModifiedBefore = dbFileInNewPath.lastModifiedSync(); - final dbDirectory = await coreStoreDirectory.getDatabaseDirectory(); - expect(dbFileInNewPath.existsSync(), isTrue); - - final dbFileSizeAfter = dbFileInNewPath.lengthSync(); - final dbFileLastModifiedAfter = dbFileInNewPath.lastModifiedSync(); - expect( - dbFileSizeBefore, - equals(dbFileSizeAfter), - reason: 'the db file should be the same', - ); - expect( - dbFileLastModifiedBefore.compareTo(dbFileLastModifiedAfter), - equals(0), // 0 if this DateTime [isAtSameMomentAs] [other] - reason: 'last modified date should not change', - ); - expect( - dbDirectory, - equals(libraryPath), - reason: - 'dbDirectory should be the new db dir path for iOS (LibraryDir)', - ); - }); - - test( - 'on any platform other than iOS, the copy migration algorithm should ' - 'not run and the db file should and will remain in ' - '(applicationDocumentDirectory) and getDatabaseDirectory() should ' - 'return (applicationDocumentDirectory) as db directory', () async { - final targetPlatforms = TargetPlatform.values.toSet(); - targetPlatforms.remove(TargetPlatform.iOS); - - final dbFile = create1MBParseDBFileInAppDocDir(); - final dbFileSizeBefore = dbFile.lengthSync(); - final dbFileLastModifiedBefore = dbFile.lastModifiedSync(); - - for (final platform in targetPlatforms) { - debugDefaultTargetPlatformOverride = platform; - + 'on ios, if there is no db file in the old dir (applicationDocumentDirectory)' + ' and there is db file in the new dir (LibraryDirectory) ' + 'the (copy) migration should not work and so the getDatabaseDirectory()' + 'should return the new db dir path (LibraryDirectory)', + () async { + debugDefaultTargetPlatformOverride = TargetPlatform.iOS; + + final dbFileInNewPath = create1MBParseDBFileInLibraryPath(); + final dbFileSizeBefore = dbFileInNewPath.lengthSync(); + final dbFileLastModifiedBefore = dbFileInNewPath.lastModifiedSync(); final dbDirectory = await coreStoreDirectory.getDatabaseDirectory(); - expect(dbFile.existsSync(), isTrue); + expect(dbFileInNewPath.existsSync(), isTrue); - final dbFileSizeAfter = dbFile.lengthSync(); - final dbFileLastModifiedSyncAfter = dbFile.lastModifiedSync(); + final dbFileSizeAfter = dbFileInNewPath.lengthSync(); + final dbFileLastModifiedAfter = dbFileInNewPath.lastModifiedSync(); expect( dbFileSizeBefore, equals(dbFileSizeAfter), reason: 'the db file should be the same', ); expect( - dbFileLastModifiedBefore.compareTo(dbFileLastModifiedSyncAfter), + dbFileLastModifiedBefore.compareTo(dbFileLastModifiedAfter), equals(0), // 0 if this DateTime [isAtSameMomentAs] [other] reason: 'last modified date should not change', ); expect( dbDirectory, - equals(applicationDocumentsPath), + equals(libraryPath), reason: - 'dbDirectory should point to application Documents Directory', + 'dbDirectory should be the new db dir path for iOS (LibraryDir)', ); - } - }); + }, + ); + + test( + 'on any platform other than iOS, the copy migration algorithm should ' + 'not run and the db file should and will remain in ' + '(applicationDocumentDirectory) and getDatabaseDirectory() should ' + 'return (applicationDocumentDirectory) as db directory', + () async { + final targetPlatforms = TargetPlatform.values.toSet(); + targetPlatforms.remove(TargetPlatform.iOS); + + final dbFile = create1MBParseDBFileInAppDocDir(); + final dbFileSizeBefore = dbFile.lengthSync(); + final dbFileLastModifiedBefore = dbFile.lastModifiedSync(); + + for (final platform in targetPlatforms) { + debugDefaultTargetPlatformOverride = platform; + + final dbDirectory = await coreStoreDirectory.getDatabaseDirectory(); + expect(dbFile.existsSync(), isTrue); + + final dbFileSizeAfter = dbFile.lengthSync(); + final dbFileLastModifiedSyncAfter = dbFile.lastModifiedSync(); + expect( + dbFileSizeBefore, + equals(dbFileSizeAfter), + reason: 'the db file should be the same', + ); + expect( + dbFileLastModifiedBefore.compareTo(dbFileLastModifiedSyncAfter), + equals(0), // 0 if this DateTime [isAtSameMomentAs] [other] + reason: 'last modified date should not change', + ); + expect( + dbDirectory, + equals(applicationDocumentsPath), + reason: + 'dbDirectory should point to application Documents Directory', + ); + } + }, + ); }); }); } @@ -181,11 +180,7 @@ File create1MBParseDBFileInAppDocDir() { } File create1MBParseDBFileInLibraryPath() { - final databaseFilePath = path.join( - libraryPath, - 'parse', - 'parse.db', - ); + final databaseFilePath = path.join(libraryPath, 'parse', 'parse.db'); return generate1MBFile(databaseFilePath); } @@ -219,8 +214,10 @@ void deleteDirectory(String path) { const String kTemporaryPath = "temporaryPath"; final String libraryPath = path.join(path.current, 'library'); -final String applicationDocumentsPath = - path.join(path.current, 'applicationDocument'); +final String applicationDocumentsPath = path.join( + path.current, + 'applicationDocument', +); class FakePathProviderPlatform extends Fake with MockPlatformInterfaceMixin From 056e7aebe719cb5c0df126658f3741c61e0d3969 Mon Sep 17 00:00:00 2001 From: Manuel Trezza <5673677+mtrezza@users.noreply.github.com> Date: Fri, 28 Nov 2025 23:06:25 +0100 Subject: [PATCH 14/14] ignore plug files --- .gitignore | 3 +++ .../flutter/generated_plugin_registrant.cc | 11 --------- .../flutter/generated_plugin_registrant.h | 15 ------------ .../linux/flutter/generated_plugins.cmake | 23 ------------------ .../flutter/generated_plugin_registrant.cc | 14 ----------- .../flutter/generated_plugin_registrant.h | 15 ------------ .../windows/flutter/generated_plugins.cmake | 24 ------------------- 7 files changed, 3 insertions(+), 102 deletions(-) delete mode 100644 packages/flutter/example/linux/flutter/generated_plugin_registrant.cc delete mode 100644 packages/flutter/example/linux/flutter/generated_plugin_registrant.h delete mode 100644 packages/flutter/example/linux/flutter/generated_plugins.cmake delete mode 100644 packages/flutter/example/windows/flutter/generated_plugin_registrant.cc delete mode 100644 packages/flutter/example/windows/flutter/generated_plugin_registrant.h delete mode 100644 packages/flutter/example/windows/flutter/generated_plugins.cmake diff --git a/.gitignore b/.gitignore index 67a3f690b..b04ebc66c 100644 --- a/.gitignore +++ b/.gitignore @@ -50,6 +50,9 @@ analysis_benchmark.json .flutter-plugins .flutter-plugins-dependencies **/generated_plugin_registrant.dart +**/generated_plugin_registrant.cc +**/generated_plugin_registrant.h +**/generated_plugins.cmake .packages .pub-cache/ .pub/ diff --git a/packages/flutter/example/linux/flutter/generated_plugin_registrant.cc b/packages/flutter/example/linux/flutter/generated_plugin_registrant.cc deleted file mode 100644 index e71a16d23..000000000 --- a/packages/flutter/example/linux/flutter/generated_plugin_registrant.cc +++ /dev/null @@ -1,11 +0,0 @@ -// -// Generated file. Do not edit. -// - -// clang-format off - -#include "generated_plugin_registrant.h" - - -void fl_register_plugins(FlPluginRegistry* registry) { -} diff --git a/packages/flutter/example/linux/flutter/generated_plugin_registrant.h b/packages/flutter/example/linux/flutter/generated_plugin_registrant.h deleted file mode 100644 index e0f0a47bc..000000000 --- a/packages/flutter/example/linux/flutter/generated_plugin_registrant.h +++ /dev/null @@ -1,15 +0,0 @@ -// -// Generated file. Do not edit. -// - -// clang-format off - -#ifndef GENERATED_PLUGIN_REGISTRANT_ -#define GENERATED_PLUGIN_REGISTRANT_ - -#include - -// Registers Flutter plugins. -void fl_register_plugins(FlPluginRegistry* registry); - -#endif // GENERATED_PLUGIN_REGISTRANT_ diff --git a/packages/flutter/example/linux/flutter/generated_plugins.cmake b/packages/flutter/example/linux/flutter/generated_plugins.cmake deleted file mode 100644 index 2e1de87a7..000000000 --- a/packages/flutter/example/linux/flutter/generated_plugins.cmake +++ /dev/null @@ -1,23 +0,0 @@ -# -# Generated file, do not edit. -# - -list(APPEND FLUTTER_PLUGIN_LIST -) - -list(APPEND FLUTTER_FFI_PLUGIN_LIST -) - -set(PLUGIN_BUNDLED_LIBRARIES) - -foreach(plugin ${FLUTTER_PLUGIN_LIST}) - add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/linux plugins/${plugin}) - target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) - list(APPEND PLUGIN_BUNDLED_LIBRARIES $) - list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) -endforeach(plugin) - -foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST}) - add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/linux plugins/${ffi_plugin}) - list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) -endforeach(ffi_plugin) diff --git a/packages/flutter/example/windows/flutter/generated_plugin_registrant.cc b/packages/flutter/example/windows/flutter/generated_plugin_registrant.cc deleted file mode 100644 index 8777c93d9..000000000 --- a/packages/flutter/example/windows/flutter/generated_plugin_registrant.cc +++ /dev/null @@ -1,14 +0,0 @@ -// -// Generated file. Do not edit. -// - -// clang-format off - -#include "generated_plugin_registrant.h" - -#include - -void RegisterPlugins(flutter::PluginRegistry* registry) { - ConnectivityPlusWindowsPluginRegisterWithRegistrar( - registry->GetRegistrarForPlugin("ConnectivityPlusWindowsPlugin")); -} diff --git a/packages/flutter/example/windows/flutter/generated_plugin_registrant.h b/packages/flutter/example/windows/flutter/generated_plugin_registrant.h deleted file mode 100644 index dc139d85a..000000000 --- a/packages/flutter/example/windows/flutter/generated_plugin_registrant.h +++ /dev/null @@ -1,15 +0,0 @@ -// -// Generated file. Do not edit. -// - -// clang-format off - -#ifndef GENERATED_PLUGIN_REGISTRANT_ -#define GENERATED_PLUGIN_REGISTRANT_ - -#include - -// Registers Flutter plugins. -void RegisterPlugins(flutter::PluginRegistry* registry); - -#endif // GENERATED_PLUGIN_REGISTRANT_ diff --git a/packages/flutter/example/windows/flutter/generated_plugins.cmake b/packages/flutter/example/windows/flutter/generated_plugins.cmake deleted file mode 100644 index cc1361d8d..000000000 --- a/packages/flutter/example/windows/flutter/generated_plugins.cmake +++ /dev/null @@ -1,24 +0,0 @@ -# -# Generated file, do not edit. -# - -list(APPEND FLUTTER_PLUGIN_LIST - connectivity_plus -) - -list(APPEND FLUTTER_FFI_PLUGIN_LIST -) - -set(PLUGIN_BUNDLED_LIBRARIES) - -foreach(plugin ${FLUTTER_PLUGIN_LIST}) - add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/windows plugins/${plugin}) - target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) - list(APPEND PLUGIN_BUNDLED_LIBRARIES $) - list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) -endforeach(plugin) - -foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST}) - add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/windows plugins/${ffi_plugin}) - list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) -endforeach(ffi_plugin)