Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 0 additions & 25 deletions .github/actions/cache-xcode-cas/action.yml

This file was deleted.

17 changes: 8 additions & 9 deletions .github/workflows/e2e_test.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: e2e
name: E2E Tests

on:
workflow_dispatch:
Expand Down Expand Up @@ -85,14 +85,8 @@ jobs:

- uses: ./.github/actions/setup-ruby

- uses: ./.github/actions/cache-cocoapods
with:
key-suffix: ${{ runner.os }}

- uses: ./.github/actions/cache-xcode-cas
with:
key-suffix: ${{ runner.os }}
xcode-version: ${{ env.IOS_SIMULATOR_VERSION }}
- name: Use Firebase stubs
run: cp sample_app/pubspec_overrides.e2e.yaml sample_app/pubspec_overrides.yaml

- name: Bootstrap
run: |
Expand All @@ -101,6 +95,11 @@ jobs:
flutter pub global activate melos
melos bootstrap

# After Bootstrap so the cache keys hash the resolved pubspec.lock
- uses: ./.github/actions/cache-cocoapods
with:
key-suffix: ${{ runner.os }}

- name: Launch Allure TestOps
working-directory: sample_app/ios
run: bundle exec fastlane allure_launch
Expand Down
15 changes: 7 additions & 8 deletions .github/workflows/e2e_test_cron.yml
Original file line number Diff line number Diff line change
Expand Up @@ -111,14 +111,8 @@ jobs:
version: ${{ matrix.ios }}
device: ${{ matrix.device }}

- uses: ./.github/actions/cache-cocoapods
with:
key-suffix: ${{ matrix.ios }}

- uses: ./.github/actions/cache-xcode-cas
with:
key-suffix: ${{ matrix.ios }}
xcode-version: ${{ env.XCODE_VERSION }}
- name: Use Firebase stubs
run: cp sample_app/pubspec_overrides.e2e.yaml sample_app/pubspec_overrides.yaml

- name: Bootstrap
run: |
Expand All @@ -127,6 +121,11 @@ jobs:
flutter pub global activate melos
melos bootstrap

# After Bootstrap so the cache keys hash the resolved pubspec.lock
- uses: ./.github/actions/cache-cocoapods
with:
key-suffix: ${{ matrix.ios }}

- name: Launch Allure TestOps
working-directory: sample_app/ios
run: bundle exec fastlane allure_launch cron:true
Expand Down
55 changes: 55 additions & 0 deletions sample_app/e2e_stubs/firebase_core/lib/firebase_core.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/// No-op stand-in for `firebase_core`, swapped in via `pubspec_overrides.e2e.yaml`.
///
/// Being a pure-Dart package (no `flutter.plugin` section), it removes the Firebase
/// pods / Gradle dependencies from e2e builds entirely: nothing is downloaded,
/// compiled, or initialized. Only the API surface used by `sample_app` is stubbed.
library;

/// No-op replacement for the Firebase entry point.
class Firebase {
/// Does nothing and returns a dummy [FirebaseApp].
static Future<FirebaseApp> initializeApp({String? name, FirebaseOptions? options}) async => const FirebaseApp();
}

/// Inert replacement for a configured Firebase app.
class FirebaseApp {
/// Creates the inert app handle.
const FirebaseApp();
}

/// Plain data holder matching the real `FirebaseOptions` constructor, so the
/// FlutterFire-generated `firebase_options.dart` compiles unchanged.
class FirebaseOptions {
/// Mirrors the real constructor's signature; values are never read.
const FirebaseOptions({
required this.apiKey,
required this.appId,
required this.messagingSenderId,
required this.projectId,
this.authDomain,
this.databaseURL,
this.storageBucket,
this.measurementId,
this.trackingId,
this.deepLinkURLScheme,
this.androidClientId,
this.iosClientId,
this.iosBundleId,
this.appGroupId,
});

/// See the real `firebase_core` for field semantics; all unused here.
final String apiKey, appId, messagingSenderId, projectId;

/// Optional platform-specific fields accepted for constructor compatibility.
final String? authDomain,
databaseURL,
storageBucket,
measurementId,
trackingId,
deepLinkURLScheme,
androidClientId,
iosClientId,
iosBundleId,
appGroupId;
}
7 changes: 7 additions & 0 deletions sample_app/e2e_stubs/firebase_core/pubspec.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
name: firebase_core
description: No-op pure-Dart stand-in for firebase_core, swapped in by e2e builds to keep the native Firebase SDK out of the app.
version: 0.0.1
publish_to: none

environment:
sdk: ^3.0.0
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/// No-op stand-in for `firebase_crashlytics`, swapped in via `pubspec_overrides.e2e.yaml`.
///
/// See the `firebase_core` stub for the rationale. Only the API surface used by
/// `sample_app` is stubbed; every call silently does nothing.
library;

import 'package:flutter/foundation.dart';

/// No-op replacement for the Crashlytics reporter.
class FirebaseCrashlytics {
FirebaseCrashlytics._();

/// The shared no-op instance.
static final FirebaseCrashlytics instance = FirebaseCrashlytics._();

/// Does nothing. The positional bool must match the real API's signature.
// ignore: avoid_positional_boolean_parameters
Future<void> setCrashlyticsCollectionEnabled(bool enabled) async {}

/// Does nothing.
Future<void> recordError(
dynamic exception,
StackTrace? stack, {
dynamic reason,
Iterable<Object> information = const [],
bool? printDetails,
bool fatal = false,
}) async {}

/// Does nothing.
Future<void> recordFlutterFatalError(FlutterErrorDetails flutterErrorDetails) async {}
}
11 changes: 11 additions & 0 deletions sample_app/e2e_stubs/firebase_crashlytics/pubspec.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
name: firebase_crashlytics
description: No-op pure-Dart stand-in for firebase_crashlytics, swapped in by e2e builds to keep the native Firebase SDK out of the app.
version: 0.0.1
publish_to: none

environment:
sdk: ^3.0.0

dependencies:
flutter:
sdk: flutter
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
/// No-op stand-in for `firebase_messaging`, swapped in via `pubspec_overrides.e2e.yaml`.
///
/// See the `firebase_core` stub for the rationale. Only the API surface used by
/// `sample_app` is stubbed: permission requests report denied, token getters
/// return null, and the message streams never emit.
library;

/// Signature of the top-level FCM background handler.
typedef BackgroundMessageHandler = Future<void> Function(RemoteMessage message);

/// No-op replacement for the FCM entry point.
class FirebaseMessaging {
FirebaseMessaging._();

/// The shared no-op instance.
static final FirebaseMessaging instance = FirebaseMessaging._();

/// Never emits.
static Stream<RemoteMessage> get onMessage => const Stream.empty();

/// Never emits.
static Stream<RemoteMessage> get onMessageOpenedApp => const Stream.empty();

/// Does nothing; [handler] is never invoked.
static void onBackgroundMessage(BackgroundMessageHandler handler) {}

/// Never emits.
Stream<String> get onTokenRefresh => const Stream.empty();

/// Always resolves to null.
Future<String?> getToken({String? vapidKey}) async => null;

/// Always resolves to null.
Future<String?> getAPNSToken() async => null;

/// Always resolves to null.
Future<RemoteMessage?> getInitialMessage() async => null;

/// Always reports [AuthorizationStatus.denied].
Future<NotificationSettings> requestPermission({
bool alert = true,
bool announcement = false,
bool badge = true,
bool carPlay = false,
bool criticalAlert = false,
bool provisional = false,
bool providesAppNotificationSettings = false,
bool sound = true,
}) async => const NotificationSettings(authorizationStatus: AuthorizationStatus.denied);

/// Does nothing.
Future<void> setForegroundNotificationPresentationOptions({
bool alert = false,
bool badge = false,
bool sound = false,
}) async {}
}

/// Mirrors the real enum values.
enum AuthorizationStatus {
/// Permission granted.
authorized,

/// Permission denied — the stub's fixed answer.
denied,

/// Permission not requested yet.
notDetermined,

/// Provisional (quiet) permission.
provisional,
}

/// Minimal result type for [FirebaseMessaging.requestPermission].
class NotificationSettings {
/// Creates settings with the given [authorizationStatus].
const NotificationSettings({required this.authorizationStatus});

/// The reported permission state.
final AuthorizationStatus authorizationStatus;
}

/// Minimal FCM message: only [data] is read by `sample_app`.
class RemoteMessage {
/// Creates a message carrying [data].
const RemoteMessage({this.data = const {}});

/// The message payload.
final Map<String, dynamic> data;
}
7 changes: 7 additions & 0 deletions sample_app/e2e_stubs/firebase_messaging/pubspec.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
name: firebase_messaging
description: No-op pure-Dart stand-in for firebase_messaging, swapped in by e2e builds to keep the native Firebase SDK out of the app.
version: 0.0.1
publish_to: none

environment:
sdk: ^3.0.0
12 changes: 12 additions & 0 deletions sample_app/pubspec_overrides.e2e.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Copied over pubspec_overrides.yaml by the e2e CI jobs BEFORE `melos bootstrap`
# (melos merges its own managed overrides into the existing file, preserving these
# entries). Swaps the Firebase plugins for the pure-Dart no-op stubs in
# e2e_stubs/ so no native Firebase SDK is downloaded, compiled, or
# initialized during e2e runs.
dependency_overrides:
firebase_core:
path: e2e_stubs/firebase_core
firebase_crashlytics:
path: e2e_stubs/firebase_crashlytics
firebase_messaging:
path: e2e_stubs/firebase_messaging
Loading