From 1378c81dcc2df3c7eed68fab11ae99666fce28a4 Mon Sep 17 00:00:00 2001 From: Guillaume Bernos Date: Thu, 30 Apr 2026 11:15:14 +0100 Subject: [PATCH 1/5] ci(fdc): improve CI for FDC --- .github/workflows/e2e_tests_fdc.yaml | 15 ++++----- .../example/integration_test/e2e_test.dart | 33 +++++++++++++++++-- .../example/start-firebase-emulator.sh | 31 ++++++++++++++--- 3 files changed, 65 insertions(+), 14 deletions(-) diff --git a/.github/workflows/e2e_tests_fdc.yaml b/.github/workflows/e2e_tests_fdc.yaml index 4810a0304fb1..7bcc342c02c2 100644 --- a/.github/workflows/e2e_tests_fdc.yaml +++ b/.github/workflows/e2e_tests_fdc.yaml @@ -52,7 +52,7 @@ jobs: - name: 'Install Tools' run: | sudo npm i -g firebase-tools - echo "FIREBASE_TOOLS_VERSION=$(npm firebase --version)" >> $GITHUB_ENV + echo "FIREBASE_TOOLS_VERSION=$(firebase --version)" >> $GITHUB_ENV - name: Firebase Emulator Cache id: firebase-emulator-cache uses: actions/cache/restore@668228422ae6a00e4ad889ee87cd7109ec5666a7 @@ -179,7 +179,7 @@ jobs: - name: 'Install Tools' run: | sudo npm i -g firebase-tools - echo "FIREBASE_TOOLS_VERSION=$(npm firebase --version)" >> $GITHUB_ENV + echo "FIREBASE_TOOLS_VERSION=$(firebase --version)" >> $GITHUB_ENV - name: Firebase Emulator Cache id: firebase-emulator-cache uses: actions/cache/restore@668228422ae6a00e4ad889ee87cd7109ec5666a7 @@ -232,10 +232,9 @@ jobs: run: | # Uncomment following line to have simulator logs printed out for debugging purposes. # xcrun simctl spawn booted log stream --predicate 'eventMessage contains "flutter"' & - # The iOS simulator sometimes fails to connect the VM Service, hanging for - # 12 minutes before timing out. Use a 6-minute limit and retry once with - # a simulator reboot. Normal connection takes 30s-5min. - perl -e 'alarm 360; exec @ARGV' -- flutter test integration_test/e2e_test.dart -d "$SIMULATOR" --dart-define=CI=true --timeout 10x || { + # The iOS simulator sometimes fails to connect the VM Service. Keep a + # limit around the full test command and retry once with a simulator reboot. + perl -e 'alarm 900; exec @ARGV' -- flutter test integration_test/e2e_test.dart -d "$SIMULATOR" --dart-define=CI=true --timeout 10x || { echo "First attempt failed or timed out. Rebooting simulator and retrying..." xcrun simctl shutdown "$SIMULATOR" || true xcrun simctl boot "$SIMULATOR" @@ -295,7 +294,7 @@ jobs: - name: 'Install Tools' run: | sudo npm i -g firebase-tools - echo "FIREBASE_TOOLS_VERSION=$(npm firebase --version)" >> $GITHUB_ENV + echo "FIREBASE_TOOLS_VERSION=$(firebase --version)" >> $GITHUB_ENV - name: Firebase Emulator Cache id: firebase-emulator-cache uses: actions/cache/restore@668228422ae6a00e4ad889ee87cd7109ec5666a7 @@ -374,7 +373,7 @@ jobs: - name: 'Install Tools' run: | sudo npm i -g firebase-tools - echo "FIREBASE_TOOLS_VERSION=$(npm firebase --version)" >> $GITHUB_ENV + echo "FIREBASE_TOOLS_VERSION=$(firebase --version)" >> $GITHUB_ENV - name: Firebase Emulator Cache id: firebase-emulator-cache uses: actions/cache/restore@668228422ae6a00e4ad889ee87cd7109ec5666a7 diff --git a/packages/firebase_data_connect/firebase_data_connect/example/integration_test/e2e_test.dart b/packages/firebase_data_connect/firebase_data_connect/example/integration_test/e2e_test.dart index bae5432a6b96..78e604c432cf 100644 --- a/packages/firebase_data_connect/firebase_data_connect/example/integration_test/e2e_test.dart +++ b/packages/firebase_data_connect/firebase_data_connect/example/integration_test/e2e_test.dart @@ -16,6 +16,36 @@ import 'listen_e2e.dart'; import 'query_e2e.dart'; import 'websocket_e2e.dart'; +Future _signInTestUser() async { + final auth = FirebaseAuth.instance; + const password = 'password'; + final email = 'fdc-test-${DateTime.now().microsecondsSinceEpoch}@mail.com'; + + for (var attempt = 0; attempt < 5; attempt++) { + try { + await auth.createUserWithEmailAndPassword( + email: email, + password: password, + ); + return; + } on FirebaseAuthException catch (e) { + if (e.code == 'email-already-in-use') { + await auth.signInWithEmailAndPassword( + email: email, + password: password, + ); + return; + } + + if (attempt == 4) { + rethrow; + } + } + + await Future.delayed(Duration(seconds: attempt + 1)); + } +} + void main() { IntegrationTestWidgetsFlutterBinding.ensureInitialized(); @@ -31,8 +61,7 @@ void main() { .useDataConnectEmulator('127.0.0.1', 9399); await FirebaseAuth.instance.useAuthEmulator('127.0.0.1', 9099); - await FirebaseAuth.instance.createUserWithEmailAndPassword( - email: 'test@mail.com', password: 'password'); + await _signInTestUser(); }); runInstanceTests(); diff --git a/packages/firebase_data_connect/firebase_data_connect/example/start-firebase-emulator.sh b/packages/firebase_data_connect/firebase_data_connect/example/start-firebase-emulator.sh index 3821f46e402a..7f0d598c6530 100755 --- a/packages/firebase_data_connect/firebase_data_connect/example/start-firebase-emulator.sh +++ b/packages/firebase_data_connect/firebase_data_connect/example/start-firebase-emulator.sh @@ -1,5 +1,28 @@ #!/bin/bash -firebase emulators:start --project flutterfire-e2e-tests & -# Added below to fix the e2e tests -# npx "firebase/firebase-tools#mtewani/dart-bugbash" emulators:start --project flutterfire-e2e-tests & -sleep 30 \ No newline at end of file +set -euo pipefail + +LOG_FILE="${TMPDIR:-/tmp}/flutterfire-fdc-emulators.log" +rm -f "$LOG_FILE" + +firebase emulators:start --project flutterfire-e2e-tests >"$LOG_FILE" 2>&1 & +FIREBASE_PID=$! + +for _ in {1..90}; do + if ! kill -0 "$FIREBASE_PID" 2>/dev/null; then + echo "Firebase emulators exited before becoming ready." + cat "$LOG_FILE" + wait "$FIREBASE_PID" + exit 1 + fi + + if grep -q "All emulators ready" "$LOG_FILE"; then + cat "$LOG_FILE" + exit 0 + fi + + sleep 1 +done + +echo "Timed out waiting for Firebase emulators to become ready." +cat "$LOG_FILE" +exit 1 From 080184935e34e55d9fb2f27ef65b43ca53d8c8de Mon Sep 17 00:00:00 2001 From: Guillaume Bernos Date: Thu, 30 Apr 2026 11:26:20 +0100 Subject: [PATCH 2/5] try --- .github/workflows/e2e_tests_fdc.yaml | 8 -------- 1 file changed, 8 deletions(-) diff --git a/.github/workflows/e2e_tests_fdc.yaml b/.github/workflows/e2e_tests_fdc.yaml index 7bcc342c02c2..de4acc208555 100644 --- a/.github/workflows/e2e_tests_fdc.yaml +++ b/.github/workflows/e2e_tests_fdc.yaml @@ -70,8 +70,6 @@ jobs: cache: true cache-key: "flutter-:os:-:channel:-:version:-:arch:-:hash:" pub-cache-key: "flutter-pub-:os:-:channel:-:version:-:arch:-:hash:" - - name: Setup PostgreSQL for Linux/macOS/Windows - uses: ikalnytskyi/action-setup-postgres@v7 - uses: bluefireteam/melos-action@705015c3d2bc4ab94201ac24accb2bbe070cf533 with: run-bootstrap: false @@ -159,8 +157,6 @@ jobs: with: distribution: 'temurin' java-version: '21' - - name: Setup PostgreSQL for Linux/macOS/Windows - uses: ikalnytskyi/action-setup-postgres@v7 - uses: hendrikmuhs/ccache-action@5ebbd400eff9e74630f759d94ddd7b6c26299639 name: Xcode Compile Cache with: @@ -277,8 +273,6 @@ jobs: with: distribution: 'temurin' java-version: '21' - - name: Setup PostgreSQL for Linux/macOS/Windows - uses: ikalnytskyi/action-setup-postgres@v7 - uses: subosito/flutter-action@f2c4f6686ca8e8d6e6d0f28410eeef506ed66aff with: channel: 'stable' @@ -356,8 +350,6 @@ jobs: with: distribution: 'temurin' java-version: '21' - - name: Setup PostgreSQL for Linux/macOS/Windows - uses: ikalnytskyi/action-setup-postgres@v7 - uses: subosito/flutter-action@f2c4f6686ca8e8d6e6d0f28410eeef506ed66aff with: channel: 'stable' From e50eece87f7a2ca0525fc98572e06103bb5833b3 Mon Sep 17 00:00:00 2001 From: Guillaume Bernos Date: Thu, 30 Apr 2026 11:37:29 +0100 Subject: [PATCH 3/5] more debug logs --- .github/workflows/e2e_tests_fdc.yaml | 12 ++++++++++ .../example/start-firebase-emulator.sh | 22 ++++++++++++++++--- 2 files changed, 31 insertions(+), 3 deletions(-) diff --git a/.github/workflows/e2e_tests_fdc.yaml b/.github/workflows/e2e_tests_fdc.yaml index de4acc208555..841b21eec0d6 100644 --- a/.github/workflows/e2e_tests_fdc.yaml +++ b/.github/workflows/e2e_tests_fdc.yaml @@ -70,6 +70,8 @@ jobs: cache: true cache-key: "flutter-:os:-:channel:-:version:-:arch:-:hash:" pub-cache-key: "flutter-pub-:os:-:channel:-:version:-:arch:-:hash:" + - name: Setup PostgreSQL for Linux/macOS/Windows + uses: ikalnytskyi/action-setup-postgres@v7 - uses: bluefireteam/melos-action@705015c3d2bc4ab94201ac24accb2bbe070cf533 with: run-bootstrap: false @@ -79,6 +81,7 @@ jobs: - name: Start Firebase Emulator run: | cd ./packages/firebase_data_connect/firebase_data_connect/example + pkill -x postgres || true unset PGSERVICEFILE firebase experiments:enable dataconnect ./start-firebase-emulator.sh @@ -157,6 +160,8 @@ jobs: with: distribution: 'temurin' java-version: '21' + - name: Setup PostgreSQL for Linux/macOS/Windows + uses: ikalnytskyi/action-setup-postgres@v7 - uses: hendrikmuhs/ccache-action@5ebbd400eff9e74630f759d94ddd7b6c26299639 name: Xcode Compile Cache with: @@ -214,6 +219,7 @@ jobs: run: | sudo chown -R 501:20 "/Users/runner/.npm" cd ./packages/firebase_data_connect/firebase_data_connect/example + pkill -x postgres || true unset PGSERVICEFILE firebase experiments:enable dataconnect ./start-firebase-emulator.sh @@ -273,6 +279,8 @@ jobs: with: distribution: 'temurin' java-version: '21' + - name: Setup PostgreSQL for Linux/macOS/Windows + uses: ikalnytskyi/action-setup-postgres@v7 - uses: subosito/flutter-action@f2c4f6686ca8e8d6e6d0f28410eeef506ed66aff with: channel: 'stable' @@ -304,6 +312,7 @@ jobs: run: | sudo chown -R 501:20 "/Users/runner/.npm" cd ./packages/firebase_data_connect/firebase_data_connect/example + pkill -x postgres || true unset PGSERVICEFILE firebase experiments:enable dataconnect ./start-firebase-emulator.sh @@ -350,6 +359,8 @@ jobs: with: distribution: 'temurin' java-version: '21' + - name: Setup PostgreSQL for Linux/macOS/Windows + uses: ikalnytskyi/action-setup-postgres@v7 - uses: subosito/flutter-action@f2c4f6686ca8e8d6e6d0f28410eeef506ed66aff with: channel: 'stable' @@ -380,6 +391,7 @@ jobs: - name: Start Firebase Emulator run: | cd ./packages/firebase_data_connect/firebase_data_connect/example + pkill -x postgres || true unset PGSERVICEFILE firebase experiments:enable dataconnect ./start-firebase-emulator.sh diff --git a/packages/firebase_data_connect/firebase_data_connect/example/start-firebase-emulator.sh b/packages/firebase_data_connect/firebase_data_connect/example/start-firebase-emulator.sh index 7f0d598c6530..ddf9dde87cf0 100755 --- a/packages/firebase_data_connect/firebase_data_connect/example/start-firebase-emulator.sh +++ b/packages/firebase_data_connect/firebase_data_connect/example/start-firebase-emulator.sh @@ -4,19 +4,35 @@ set -euo pipefail LOG_FILE="${TMPDIR:-/tmp}/flutterfire-fdc-emulators.log" rm -f "$LOG_FILE" +print_emulator_logs() { + cat "$LOG_FILE" + + if [ -f firebase-debug.log ]; then + echo + echo "firebase-debug.log:" + cat firebase-debug.log + fi + + if [ -f dataconnect-debug.log ]; then + echo + echo "dataconnect-debug.log:" + cat dataconnect-debug.log + fi +} + firebase emulators:start --project flutterfire-e2e-tests >"$LOG_FILE" 2>&1 & FIREBASE_PID=$! for _ in {1..90}; do if ! kill -0 "$FIREBASE_PID" 2>/dev/null; then echo "Firebase emulators exited before becoming ready." - cat "$LOG_FILE" + print_emulator_logs wait "$FIREBASE_PID" exit 1 fi if grep -q "All emulators ready" "$LOG_FILE"; then - cat "$LOG_FILE" + print_emulator_logs exit 0 fi @@ -24,5 +40,5 @@ for _ in {1..90}; do done echo "Timed out waiting for Firebase emulators to become ready." -cat "$LOG_FILE" +print_emulator_logs exit 1 From 7fdb0e57b2ecff7ed58cf53fde795bd6ca8b8a97 Mon Sep 17 00:00:00 2001 From: Guillaume Bernos Date: Thu, 30 Apr 2026 11:46:12 +0100 Subject: [PATCH 4/5] fix that --- .github/workflows/e2e_tests_fdc.yaml | 28 ++++------------------------ 1 file changed, 4 insertions(+), 24 deletions(-) diff --git a/.github/workflows/e2e_tests_fdc.yaml b/.github/workflows/e2e_tests_fdc.yaml index 841b21eec0d6..3f402dc497db 100644 --- a/.github/workflows/e2e_tests_fdc.yaml +++ b/.github/workflows/e2e_tests_fdc.yaml @@ -58,12 +58,9 @@ jobs: uses: actions/cache/restore@668228422ae6a00e4ad889ee87cd7109ec5666a7 continue-on-error: true with: - # The firebase emulators are pure javascript and java, OS-independent - enableCrossOsArchive: true # Must match the save path exactly path: ~/.cache/firebase/emulators - key: firebase-emulators-v3-${{ env.FIREBASE_TOOLS_VERSION }} - restore-keys: firebase-emulators-v3 + key: firebase-emulators-v4-${{ runner.os }}-${{ env.FIREBASE_TOOLS_VERSION }} - uses: subosito/flutter-action@f2c4f6686ca8e8d6e6d0f28410eeef506ed66aff with: channel: 'stable' @@ -137,8 +134,6 @@ jobs: uses: actions/cache/save@668228422ae6a00e4ad889ee87cd7109ec5666a7 continue-on-error: true with: - # The firebase emulators are pure javascript and java, OS-independent - enableCrossOsArchive: true key: ${{ steps.firebase-emulator-cache.outputs.cache-primary-key }} # Must match the restore path exactly path: ~/.cache/firebase/emulators @@ -186,12 +181,9 @@ jobs: uses: actions/cache/restore@668228422ae6a00e4ad889ee87cd7109ec5666a7 continue-on-error: true with: - # The firebase emulators are pure javascript and java, OS-independent - enableCrossOsArchive: true # Must match the save path exactly path: ~/.cache/firebase/emulators - key: firebase-emulators-v3-${{ env.FIREBASE_TOOLS_VERSION }} - restore-keys: firebase-emulators-v3 + key: firebase-emulators-v4-${{ runner.os }}-${{ env.FIREBASE_TOOLS_VERSION }} - uses: subosito/flutter-action@f2c4f6686ca8e8d6e6d0f28410eeef506ed66aff with: channel: 'stable' @@ -249,8 +241,6 @@ jobs: uses: actions/cache/save@668228422ae6a00e4ad889ee87cd7109ec5666a7 continue-on-error: true with: - # The firebase emulators are pure javascript and java, OS-independent - enableCrossOsArchive: true key: ${{ steps.firebase-emulator-cache.outputs.cache-primary-key }} # Must match the restore path exactly path: ~/.cache/firebase/emulators @@ -302,12 +292,9 @@ jobs: uses: actions/cache/restore@668228422ae6a00e4ad889ee87cd7109ec5666a7 continue-on-error: true with: - # The firebase emulators are pure javascript and java, OS-independent - enableCrossOsArchive: true # Must match the save path exactly path: ~/.cache/firebase/emulators - key: firebase-emulators-v3-${{ env.FIREBASE_TOOLS_VERSION }} - restore-keys: firebase-emulators-v3 + key: firebase-emulators-v4-${{ runner.os }}-${{ env.FIREBASE_TOOLS_VERSION }} - name: Start Firebase Emulator run: | sudo chown -R 501:20 "/Users/runner/.npm" @@ -338,8 +325,6 @@ jobs: uses: actions/cache/save@668228422ae6a00e4ad889ee87cd7109ec5666a7 continue-on-error: true with: - # The firebase emulators are pure javascript and java, OS-independent - enableCrossOsArchive: true key: ${{ steps.firebase-emulator-cache.outputs.cache-primary-key }} # Must match the restore path exactly path: ~/.cache/firebase/emulators @@ -382,12 +367,9 @@ jobs: uses: actions/cache/restore@668228422ae6a00e4ad889ee87cd7109ec5666a7 continue-on-error: true with: - # The firebase emulators are pure javascript and java, OS-independent - enableCrossOsArchive: true # Must match the save path exactly path: ~/.cache/firebase/emulators - key: firebase-emulators-v3-${{ env.FIREBASE_TOOLS_VERSION }} - restore-keys: firebase-emulators-v3 + key: firebase-emulators-v4-${{ runner.os }}-${{ env.FIREBASE_TOOLS_VERSION }} - name: Start Firebase Emulator run: | cd ./packages/firebase_data_connect/firebase_data_connect/example @@ -418,8 +400,6 @@ jobs: uses: actions/cache/save@668228422ae6a00e4ad889ee87cd7109ec5666a7 continue-on-error: true with: - # The firebase emulators are pure javascript and java, OS-independent - enableCrossOsArchive: true key: ${{ steps.firebase-emulator-cache.outputs.cache-primary-key }} # Must match the restore path exactly path: ~/.cache/firebase/emulators From f4c1225f8b3c53a13468726464ea2b33877bd5b4 Mon Sep 17 00:00:00 2001 From: Guillaume Bernos Date: Thu, 30 Apr 2026 12:41:44 +0100 Subject: [PATCH 5/5] improve tests --- .../integration_test/websocket_e2e.dart | 159 ++++++++++-------- 1 file changed, 93 insertions(+), 66 deletions(-) diff --git a/packages/firebase_data_connect/firebase_data_connect/example/integration_test/websocket_e2e.dart b/packages/firebase_data_connect/firebase_data_connect/example/integration_test/websocket_e2e.dart index 930c7d4da0c2..a105b4783184 100644 --- a/packages/firebase_data_connect/firebase_data_connect/example/integration_test/websocket_e2e.dart +++ b/packages/firebase_data_connect/firebase_data_connect/example/integration_test/websocket_e2e.dart @@ -20,6 +20,18 @@ import 'package:flutter_test/flutter_test.dart'; import 'query_e2e.dart'; // For deleteAllMovies +const _streamTimeout = Duration(seconds: 30); + +Future _waitForStreamEvent(Future future, String description) { + return future.timeout( + _streamTimeout, + onTimeout: () => throw TimeoutException( + 'Timed out waiting for $description', + _streamTimeout, + ), + ); +} + void runWebSocketTests() { group( '$FirebaseDataConnect WebSocketTransport', @@ -44,9 +56,9 @@ void runWebSocketTests() { .subscribe() .listen((value) { if (count1 == 0) { - ready1.complete(); + if (!ready1.isCompleted) ready1.complete(); } else { - update1.complete(); + if (!update1.isCompleted) update1.complete(); } count1++; }); @@ -57,44 +69,57 @@ void runWebSocketTests() { .subscribe() .listen((value) { if (count2 == 0) { - ready2.complete(); + if (!ready2.isCompleted) ready2.complete(); } else { - update2.complete(); + if (!update2.isCompleted) update2.complete(); } count2++; }); - // Wait for both to be ready - await ready1.future; - await ready2.future; - - // Create movies - await MoviesConnector.instance - .createMovie( - genre: 'Action', - title: 'The Matrix', - releaseYear: 1999, - ) - .rating(4.5) - .ref() - .execute(); - - await MoviesConnector.instance - .createMovie( - genre: 'Drama', - title: 'Titanic', - releaseYear: 1997, - ) - .rating(4.8) - .ref() - .execute(); - - // Wait for updates - await update1.future; - await update2.future; - - await sub1.cancel(); - await sub2.cancel(); + try { + // Wait for both to be ready + await _waitForStreamEvent(ready1.future, 'Matrix subscription'); + await _waitForStreamEvent(ready2.future, 'Titan subscription'); + + // Create movies + await MoviesConnector.instance + .createMovie( + genre: 'Action', + title: 'The Matrix', + releaseYear: 1999, + ) + .rating(4.5) + .ref() + .execute(); + + await MoviesConnector.instance + .createMovie( + genre: 'Drama', + title: 'Titanic', + releaseYear: 1997, + ) + .rating(4.8) + .ref() + .execute(); + + // Explicitly resume each active query so this test does not depend on + // emulator-side push timing. + await MoviesConnector.instance + .listMoviesByPartialTitle(input: 'Matrix') + .ref() + .execute(fetchPolicy: QueryFetchPolicy.serverOnly); + await MoviesConnector.instance + .listMoviesByPartialTitle(input: 'Titan') + .ref() + .execute(fetchPolicy: QueryFetchPolicy.serverOnly); + + // Wait for updates + await _waitForStreamEvent(update1.future, 'Matrix update'); + await _waitForStreamEvent(update2.future, 'Titan update'); + } finally { + await sub1.cancel(); + await sub2.cancel(); + } }); testWidgets( @@ -110,36 +135,38 @@ void runWebSocketTests() { .subscribe() .listen((value) { if (count == 0) { - isReady.complete(); + if (!isReady.isCompleted) isReady.complete(); } count++; }); - await isReady.future; - - // Now perform a query, which should go over WebSocket if connected - final result = - await MoviesConnector.instance.listMovies().ref().execute(); - expect(result.data.movies.length, 0); - - // Perform a mutation - await MoviesConnector.instance - .createMovie( - genre: 'Action', - title: 'Inception', - releaseYear: 2010, - ) - .rating(4.9) - .ref() - .execute(); - - // Verify update via query - final result2 = - await MoviesConnector.instance.listMovies().ref().execute(); - expect(result2.data.movies.length, 1); - expect(result2.data.movies[0].title, 'Inception'); - - await sub.cancel(); + try { + await _waitForStreamEvent(isReady.future, 'listMovies subscription'); + + // Now perform a query, which should go over WebSocket if connected + final result = + await MoviesConnector.instance.listMovies().ref().execute(); + expect(result.data.movies.length, 0); + + // Perform a mutation + await MoviesConnector.instance + .createMovie( + genre: 'Action', + title: 'Inception', + releaseYear: 2010, + ) + .rating(4.9) + .ref() + .execute(); + + // Verify update via query + final result2 = + await MoviesConnector.instance.listMovies().ref().execute(); + expect(result2.data.movies.length, 1); + expect(result2.data.movies[0].title, 'Inception'); + } finally { + await sub.cancel(); + } }); testWidgets('should stop receiving events after cancel', @@ -154,14 +181,14 @@ void runWebSocketTests() { .subscribe() .listen((value) { if (count == 0) { - isReady.complete(); + if (!isReady.isCompleted) isReady.complete(); } else { - receivedUpdate.complete(); + if (!receivedUpdate.isCompleted) receivedUpdate.complete(); } count++; }); - await isReady.future; + await _waitForStreamEvent(isReady.future, 'listMovies subscription'); // Cancel the subscription await sub.cancel(); @@ -200,12 +227,12 @@ void runWebSocketTests() { .subscribe() .listen((value) { if (count == 0) { - isReady.complete(); + if (!isReady.isCompleted) isReady.complete(); } count++; }); - await isReady.future; + await _waitForStreamEvent(isReady.future, 'listMovies subscription'); final dataConnect = MoviesConnector.instance.dataConnect; final transport = (dataConnect as dynamic).transport;