Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
c26185c
chore(demo): use env vars for OneSignal app id
fadi-george Apr 9, 2026
dc0af72
ci: add e2e tests and accessibility labels
fadi-george Apr 9, 2026
c411667
ci: update E2E tests with new secrets and accessibility
fadi-george Apr 10, 2026
2d57f81
ci(e2e): migrate to shared Appium workflow
fadi-george Apr 10, 2026
8cc894b
refactor(demo): remove log level colors from UI
fadi-george Apr 10, 2026
b099447
test(demo): add semantic identifier to main scroll view
fadi-george Apr 10, 2026
0941cb8
feat(demo): improve info icon accessibility in section cards
fadi-george Apr 11, 2026
a90a5ce
test(demo): add section keys for Appium testing
fadi-george Apr 11, 2026
e6b9c62
refactor(demo): replace custom Row with SwitchListTile
fadi-george Apr 11, 2026
b4710c7
test(demo): add semantics and improve button labels
fadi-george Apr 13, 2026
bbdf209
refactor(demo): remove tag parameter from LogManager
fadi-george Apr 13, 2026
9aee160
test(demo): add semantics to delete button in PairItem
fadi-george Apr 13, 2026
f2be61a
test(demo): add semantics to SectionCard widget
fadi-george Apr 13, 2026
65981b5
test(demo): add semantics to PairItem text widgets
fadi-george Apr 13, 2026
f9daaa9
test(demo): add sectionKey to improve semantic identifiers
fadi-george Apr 13, 2026
b81921b
test(demo): add semantics to remove dialog checkboxes
fadi-george Apr 14, 2026
0bb596f
test(demo): add semantics and rename sections for appium
fadi-george Apr 14, 2026
166ee42
test(demo): add semantic labels to dialog inputs
fadi-george Apr 14, 2026
e88c07b
test(demo): clarify clear button label for triggers
fadi-george Apr 14, 2026
7be7dfc
refactor(demo): replace LogManager with debugPrint
fadi-george Apr 14, 2026
6749f3f
test(demo): add location shared check button
fadi-george Apr 14, 2026
2470653
test(demo): enhance UI elements for appium testing
fadi-george Apr 14, 2026
c212619
test(demo): add snackbar feedback and update theme
fadi-george Apr 14, 2026
af4f183
test(demo): fix tooltip key for custom events
fadi-george Apr 14, 2026
7e789a2
style(demo): update app bar to use primary color
fadi-george Apr 14, 2026
c069ed8
test(demo): rename "activity" to "screen" in UI text
fadi-george Apr 14, 2026
02495c2
Potential fix for pull request finding 'CodeQL / Workflow does not co…
fadi-george Apr 14, 2026
b7fd679
Potential fix for pull request finding 'CodeQL / Workflow does not co…
fadi-george Apr 14, 2026
9f7b8ad
ci(e2e): add E2E_MODE env var and update test setup
fadi-george Apr 14, 2026
0938d10
test(e2e): improve semantic IDs and app config
fadi-george Apr 14, 2026
6d5ea78
test(demo): add sectionKey props to CollapsibleList widgets
fadi-george Apr 14, 2026
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
124 changes: 124 additions & 0 deletions .github/workflows/e2e.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
name: E2E Tests

on:
push:
branches:
- rel/**
workflow_dispatch:
inputs:
platform:
description: 'Platform to test'
required: true
default: 'android'
type: choice
options:
- android
- ios
- both

permissions:
contents: read

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
build-android:
Comment thread
claude[bot] marked this conversation as resolved.
if: >-
github.event_name == 'push' ||
github.event.inputs.platform == 'android' ||
github.event.inputs.platform == 'both'
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v5

- name: Set up Flutter
uses: ./.github/actions/setup-flutter

- name: Set up Java
uses: actions/setup-java@v4
with:
distribution: temurin
java-version: '17'

- name: Create demo .env
working-directory: examples/demo
run: |
echo "ONESIGNAL_APP_ID=${{ vars.APPIUM_ONESIGNAL_APP_ID }}" > .env
echo "ONESIGNAL_API_KEY=${{ secrets.APPIUM_ONESIGNAL_API_KEY }}" >> .env
Comment thread
fadi-george marked this conversation as resolved.
echo "E2E_MODE=true" >> .env

- name: Build release APK
working-directory: examples/demo
run: flutter build apk --release

- name: Upload APK
uses: actions/upload-artifact@v4
with:
name: demo-apk
path: examples/demo/build/app/outputs/flutter-apk/app-release.apk
retention-days: 1

build-ios:
Comment thread
github-advanced-security[bot] marked this conversation as resolved.
Fixed
if: >-
github.event_name == 'push' ||
github.event.inputs.platform == 'ios' ||
Comment thread
claude[bot] marked this conversation as resolved.
github.event.inputs.platform == 'both'
runs-on: macos-latest
steps:
- name: Checkout
uses: actions/checkout@v5

- name: Set up Flutter
uses: ./.github/actions/setup-flutter

- name: Create demo .env
working-directory: examples/demo
run: |
echo "ONESIGNAL_APP_ID=${{ vars.APPIUM_ONESIGNAL_APP_ID }}" > .env
echo "ONESIGNAL_API_KEY=${{ secrets.APPIUM_ONESIGNAL_API_KEY }}" >> .env
echo "E2E_MODE=true" >> .env

- name: Install CocoaPods dependencies
working-directory: examples/demo/ios
run: pod install

- name: Build iOS app
working-directory: examples/demo
run: |
flutter build ios --release --no-codesign
cd build/ios/iphoneos
mkdir Payload
cp -r Runner.app Payload/
zip -r Runner.ipa Payload

- name: Upload IPA
uses: actions/upload-artifact@v4
with:
name: demo-ipa
path: examples/demo/build/ios/iphoneos/Runner.ipa
retention-days: 1

e2e-android:
Comment thread
github-advanced-security[bot] marked this conversation as resolved.
Fixed
needs: build-android
uses: OneSignal/sdk-shared/.github/workflows/appium-e2e.yml@main
secrets: inherit
with:
platform: android
app-artifact: demo-apk
app-filename: app-release.apk
sdk-type: flutter
build-name: flutter-android-${{ github.ref_name }}-${{ github.run_number }}

e2e-ios:
Comment thread
github-advanced-security[bot] marked this conversation as resolved.
Fixed
needs: build-ios
uses: OneSignal/sdk-shared/.github/workflows/appium-e2e.yml@main
secrets: inherit
with:
platform: ios
app-artifact: demo-ipa
app-filename: Runner.ipa
sdk-type: flutter
build-name: flutter-ios-${{ github.ref_name }}-${{ github.run_number }}
Comment thread
github-advanced-security[bot] marked this conversation as resolved.
Fixed
25 changes: 7 additions & 18 deletions examples/build.md
Original file line number Diff line number Diff line change
Expand Up @@ -119,9 +119,9 @@ OneSignal.User.addObserver(...)
- Use `await Future.delayed(const Duration(milliseconds: 100))` after setting state for render delay

### SnackBar Messages
- `AppViewModel` exposes a `snackBarMessage` stream or `ValueNotifier<String?>`
- `HomeScreen` shows via `ScaffoldMessenger.of(context).showSnackBar()`
- Clear previous SnackBar with `ScaffoldMessenger.of(context).clearSnackBars()`
- `AppSnackBar` extension on `BuildContext` defined in `theme.dart`
- Call `context.showSnackBar(message)` directly from widget callbacks
- Automatically hides the current SnackBar before showing the new one

### Send In-App Message Icons
- TOP BANNER: `Icons.vertical_align_top`
Expand All @@ -135,17 +135,6 @@ OneSignal.User.addObserver(...)
- `TextEditingController`s are properly disposed in `StatefulWidget`s
- JSON parsing via `jsonDecode` returns `Map<String, dynamic>` for Track Event

### Accessibility (Appium)
- Use `Semantics` widget with `label` property:
```dart
Semantics(label: 'log_entry_${index}_message', child: Text(entry.message))
```

### Log Manager
- Singleton with `ChangeNotifier` for reactive UI updates
- `LogManager().d(tag, message)`, `.i()`, `.w()`, `.e()`
- Also prints via `debugPrint` for development

---

## File Structure
Expand All @@ -162,8 +151,7 @@ examples/demo/
│ ├── services/
│ │ ├── onesignal_api_service.dart
│ │ ├── preferences_service.dart
│ │ ├── tooltip_helper.dart
│ │ └── log_manager.dart
│ │ └── tooltip_helper.dart
│ ├── repositories/
│ │ └── onesignal_repository.dart
│ ├── viewmodels/
Comment thread
fadi-george marked this conversation as resolved.
Expand All @@ -175,9 +163,9 @@ examples/demo/
│ ├── section_card.dart
│ ├── toggle_row.dart
│ ├── action_button.dart
│ ├── app_text_field.dart
│ ├── list_widgets.dart
│ ├── loading_overlay.dart
│ ├── log_view.dart
│ ├── dialogs.dart
│ └── sections/
│ ├── app_section.dart
Expand All @@ -192,7 +180,8 @@ examples/demo/
│ ├── tags_section.dart
│ ├── outcomes_section.dart
│ ├── triggers_section.dart
│ ├── track_event_section.dart
│ ├── custom_events_section.dart
│ ├── live_activities_section.dart
│ └── location_section.dart
├── android/
├── ios/
Expand Down
3 changes: 2 additions & 1 deletion examples/demo/.env.example
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
ONESIGNAL_API_KEY=your_rest_api_key
ONESIGNAL_APP_ID=your-onesignal-app-id # default app id: 77e32082-ea27-42e3-a898-c72e141824ef if empty
ONESIGNAL_API_KEY=your-onesignal-api-key
15 changes: 14 additions & 1 deletion examples/demo/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,20 @@ flutter run -d <device_id>

## Configuration

The app uses a default OneSignal App ID for testing. To use your own, update the `oneSignalAppId` constant in `lib/main.dart`.
Copy the example environment file and fill in your values:

```bash
cp .env.example .env
```

Set your OneSignal credentials in `.env`:

```
ONESIGNAL_APP_ID=your-onesignal-app-id
ONESIGNAL_API_KEY=your-onesignal-api-key
```

If no `.env` is provided, the app falls back to a built-in default App ID.

## Build Guide

Expand Down
31 changes: 14 additions & 17 deletions examples/demo/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,30 +6,28 @@ import 'package:provider/provider.dart';

import 'repositories/onesignal_repository.dart';
import 'screens/home_screen.dart';
import 'services/log_manager.dart';
import 'services/onesignal_api_service.dart';
import 'services/preferences_service.dart';
import 'services/tooltip_helper.dart';
import 'theme.dart';
import 'viewmodels/app_viewmodel.dart';

const String oneSignalAppId = '77e32082-ea27-42e3-a898-c72e141824ef';
const String _defaultAppId = '77e32082-ea27-42e3-a898-c72e141824ef';

Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();

// Load environment variables
try {
await dotenv.load(fileName: '.env');
} catch (_) {
LogManager().w('App', '.env file not found, continuing without API key');
debugPrint('.env file not found, using defaults');
}

// Initialize preferences
final prefs = PreferencesService();
await prefs.init();

final appId = prefs.appId ?? oneSignalAppId;
final envAppId = dotenv.env['ONESIGNAL_APP_ID'];
final appId = (envAppId != null && envAppId.isNotEmpty) ? envAppId : _defaultAppId;

// Initialize OneSignal SDK
OneSignal.Debug.setLogLevel(OSLogLevel.verbose);
Expand All @@ -50,29 +48,28 @@ Future<void> main() async {

// Register IAM listeners
OneSignal.InAppMessages.addWillDisplayListener((event) {
LogManager().i('IAM', 'Will display: ${event.message.messageId}');
debugPrint('IAM will display: ${event.message.messageId}');
});
OneSignal.InAppMessages.addDidDisplayListener((event) {
LogManager().i('IAM', 'Did display: ${event.message.messageId}');
debugPrint('IAM did display: ${event.message.messageId}');
});
OneSignal.InAppMessages.addWillDismissListener((event) {
LogManager().i('IAM', 'Will dismiss: ${event.message.messageId}');
debugPrint('IAM will dismiss: ${event.message.messageId}');
});
OneSignal.InAppMessages.addDidDismissListener((event) {
LogManager().i('IAM', 'Did dismiss: ${event.message.messageId}');
debugPrint('IAM did dismiss: ${event.message.messageId}');
});
OneSignal.InAppMessages.addClickListener((event) {
LogManager().i('IAM', 'Clicked: ${event.result.actionId}');
debugPrint('IAM clicked: ${event.result.actionId}');
});

// Register notification listeners
OneSignal.Notifications.addClickListener((event) {
LogManager().i('Notification', 'Clicked: ${event.notification.title}');
debugPrint('Notification clicked: ${event.notification.title}');
});
OneSignal.Notifications.addForegroundWillDisplayListener((event) {
LogManager().i(
'Notification',
'Foreground will display: ${event.notification.title}',
debugPrint(
'Notification foreground will display: ${event.notification.title}',
);
event.notification.display();
});
Expand All @@ -82,7 +79,7 @@ Future<void> main() async {
try {
apiKey = dotenv.env['ONESIGNAL_API_KEY'] ?? '';
} catch (_) {
LogManager().w('App', 'API key not found, continuing without it');
debugPrint('API key not found, continuing without it');
}
final apiService = OneSignalApiService()
..setAppId(appId)
Expand All @@ -92,7 +89,7 @@ Future<void> main() async {
// Fetch tooltips in background
TooltipHelper().init();

LogManager().i('App', 'OneSignal initialized with app ID: $appId');
debugPrint('OneSignal initialized with app ID: $appId');

runApp(
ChangeNotifierProvider(
Expand Down
Loading
Loading