Skip to content

Commit

Permalink
Merge branch 'feat/js-sdk-integration' into feat/js-sentry-api
Browse files Browse the repository at this point in the history
  • Loading branch information
buenaflor committed Dec 12, 2024
2 parents e4931b1 + 6f566c4 commit d51b5d0
Show file tree
Hide file tree
Showing 22 changed files with 338 additions and 163 deletions.
45 changes: 15 additions & 30 deletions .github/workflows/flutter_test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ on:
- "flutter/**"

env:
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
SENTRY_AUTH_TOKEN_E2E: ${{ secrets.SENTRY_AUTH_TOKEN_E2E }}

jobs:
cancel-previous-workflow:
Expand All @@ -25,7 +25,7 @@ jobs:
access_token: ${{ github.token }}

test-android:
runs-on: macos-13
runs-on: ubuntu-latest
timeout-minutes: 30
defaults:
run:
Expand All @@ -38,6 +38,12 @@ jobs:
- name: checkout
uses: actions/checkout@v4

- name: Enable KVM group perms
run: |
echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules
sudo udevadm control --reload-rules
sudo udevadm trigger --name-match=kvm
- uses: actions/setup-java@v4
with:
distribution: "adopt"
Expand All @@ -56,28 +62,7 @@ jobs:
- name: Gradle cache
uses: gradle/gradle-build-action@ac2d340dc04d9e1113182899e983b5400c17cda1 # [email protected]

- name: AVD cache
uses: actions/cache@v4
id: avd-cache
with:
path: |
~/.android/avd/*
~/.android/adb*
key: avd-31

- name: create AVD and generate snapshot for caching
if: steps.avd-cache.outputs.cache-hit != 'true'
uses: reactivecircus/android-emulator-runner@62dbb605bba737720e10b196cb4220d374026a6d #[email protected]
with:
working-directory: ./flutter/example
api-level: 31
profile: Nexus 6
arch: x86_64
force-avd-creation: false
avd-name: macOS-avd-x86_64-31
emulator-options: -no-snapshot-save -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none
disable-animations: true
script: echo 'Generated AVD snapshot for caching.'
# TODO: fix emulator caching, in ubuntu-latest emulator won't boot: https://github.com/ReactiveCircus/android-emulator-runner/issues/278

- name: build apk
working-directory: ./flutter/example/android
Expand All @@ -91,8 +76,8 @@ jobs:
profile: Nexus 6
arch: x86_64
force-avd-creation: false
avd-name: macOS-avd-x86_64-31
emulator-options: -no-snapshot-save -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none
avd-name: avd-x86_64-31
emulator-options: -no-snapshot-save -no-window -accel on -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none
disable-animations: true
script: ./gradlew testDebugUnitTest

Expand All @@ -104,10 +89,10 @@ jobs:
profile: Nexus 6
arch: x86_64
force-avd-creation: false
avd-name: macOS-avd-x86_64-31
emulator-options: -no-snapshot-save -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none
avd-name: avd-x86_64-31
emulator-options: -no-snapshot-save -no-window -accel on -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none
disable-animations: true
script: flutter test integration_test/all.dart --verbose
script: flutter test integration_test/all.dart --dart-define SENTRY_AUTH_TOKEN_E2E=$SENTRY_AUTH_TOKEN_E2E --verbose

cocoa:
name: "${{ matrix.target }} | ${{ matrix.sdk }}"
Expand Down Expand Up @@ -158,7 +143,7 @@ jobs:
- name: run integration test
# Disable flutter integration tests for iOS for now (https://github.com/getsentry/sentry-dart/issues/1605#issuecomment-1695809346)
if: ${{ matrix.target != 'ios' }}
run: flutter test -d "${{ steps.device.outputs.name }}" integration_test/all.dart --verbose
run: flutter test -d "${{ steps.device.outputs.name }}" integration_test/all.dart --dart-define SENTRY_AUTH_TOKEN_E2E=$SENTRY_AUTH_TOKEN_E2E --verbose

- name: run native test
# We only have the native unit test package in the iOS xcodeproj at the moment.
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/testflight.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ jobs:
- uses: actions/checkout@v4
- uses: subosito/flutter-action@f2c4f6686ca8e8d6e6d0f28410eeef506ed66aff # [email protected]
- run: xcodes select 15.0.1
- uses: ruby/setup-ruby@a2bbe5b1b236842c1cb7dd11e8e3b51e0a616acc # pin@v1.202.0
- uses: ruby/setup-ruby@2a18b06812b0e15bb916e1df298d3e740422c47e # pin@v1.203.0
with:
ruby-version: '2.7.5'
bundler-cache: true
Expand Down
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,22 @@

## Unreleased

### Deprecate

- Manual TTID ([#2477](https://github.com/getsentry/sentry-dart/pull/2477))

### Enhancements

- Warning (in a debug build) if a potentially sensitive widget is not masked or unmasked explicitly ([#2375](https://github.com/getsentry/sentry-dart/pull/2375))

### Dependencies

- Bump Native SDK from v0.7.15 to v0.7.16 ([#2465](https://github.com/getsentry/sentry-dart/pull/2465))
- [changelog](https://github.com/getsentry/sentry-native/blob/master/CHANGELOG.md#0716)
- [diff](https://github.com/getsentry/sentry-native/compare/0.7.15...0.7.16)
- Bump Android SDK from v7.18.1 to v7.19.0 ([#2488](https://github.com/getsentry/sentry-dart/pull/2488))
- [changelog](https://github.com/getsentry/sentry-java/blob/main/CHANGELOG.md#7190)
- [diff](https://github.com/getsentry/sentry-java/compare/7.18.1...7.19.0)

## 8.12.0-beta.1

Expand Down
2 changes: 1 addition & 1 deletion flutter/android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ android {
}

dependencies {
api 'io.sentry:sentry-android:7.18.1'
api 'io.sentry:sentry-android:7.19.0'
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"

// Required -- JUnit 4 framework
Expand Down
139 changes: 76 additions & 63 deletions flutter/example/integration_test/integration_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,19 @@ import 'dart:convert';
import 'package:flutter/widgets.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:http/http.dart';
import 'package:integration_test/integration_test.dart';
import 'package:sentry_flutter/sentry_flutter.dart';
import 'package:sentry_flutter_example/main.dart';

import 'utils.dart';

void main() {
// const org = 'sentry-sdks';
// const slug = 'sentry-flutter';
// const authToken = String.fromEnvironment('SENTRY_AUTH_TOKEN');
const org = 'sentry-sdks';
const slug = 'sentry-flutter';
const authToken = String.fromEnvironment('SENTRY_AUTH_TOKEN_E2E');
const fakeDsn = 'https://[email protected]/1234567';

TestWidgetsFlutterBinding.ensureInitialized();
IntegrationTestWidgetsFlutterBinding.ensureInitialized();

tearDown(() async {
await Sentry.close();
Expand Down Expand Up @@ -98,7 +101,7 @@ void main() {

// ignore: deprecated_member_use_from_same_package
// ignore: deprecated_member_use
final associatedEventId = await Sentry.captureMessage("Associated");
final associatedEventId = await Sentry.captureMessage('Associated');
final feedback = SentryFeedback(
message: 'message',
contactEmail: '[email protected]',
Expand Down Expand Up @@ -162,66 +165,76 @@ void main() {
await transaction.finish();
});

// group('e2e', () {
// var output = find.byKey(const Key('output'));
// late Fixture fixture;
//
// setUp(() {
// fixture = Fixture();
// });
//
// testWidgets('captureException', (tester) async {
// await setupSentryAndApp(tester,
// dsn: exampleDsn, beforeSendCallback: fixture.beforeSend);
//
// await tester.tap(find.text('captureException'));
// await tester.pumpAndSettle();
//
// final text = output.evaluate().single.widget as Text;
// final id = text.data!;
//
// final uri = Uri.parse(
// 'https://sentry.io/api/0/projects/$org/$slug/events/$id/',
// );
// expect(authToken, isNotEmpty);
//
// final event = await fixture.poll(uri, authToken);
// expect(event, isNotNull);
//
// final sentEvent = fixture.sentEvent;
// expect(sentEvent, isNotNull);
//
// final tags = event!["tags"] as List<dynamic>;
//
// expect(sentEvent!.eventId.toString(), event["id"]);
// expect("_Exception: Exception: captureException", event["title"]);
// expect(sentEvent.release, event["release"]["version"]);
// expect(
// 2,
// (tags.firstWhere((e) => e["value"] == sentEvent.environment) as Map)
// .length);
// expect(sentEvent.fingerprint, event["fingerprint"] ?? []);
// expect(
// 2,
// (tags.firstWhere((e) => e["value"] == SentryLevel.error.name) as Map)
// .length);
// expect(sentEvent.logger, event["logger"]);
//
// final dist = tags.firstWhere((element) => element['key'] == 'dist');
// expect('1', dist['value']);
//
// final environment =
// tags.firstWhere((element) => element['key'] == 'environment');
// expect('integration', environment['value']);
// });
// });
group('e2e', () {
var output = find.byKey(const Key('output'));
late Fixture fixture;

setUp(() {
fixture = Fixture();
});

testWidgets('captureException', (tester) async {
late Uri uri;

await restoreFlutterOnErrorAfter(() async {
await setupSentryAndApp(tester,
dsn: exampleDsn, beforeSendCallback: fixture.beforeSend);

await tester.tap(find.text('captureException'));
await tester.pumpAndSettle();

final text = output.evaluate().single.widget as Text;
final id = text.data!;

uri = Uri.parse(
'https://sentry.io/api/0/projects/$org/$slug/events/$id/',
);
});

expect(authToken, isNotEmpty);

final event = await fixture.poll(uri, authToken);
expect(event, isNotNull);

final sentEvents = fixture.sentEvents
.where((el) => el!.eventId.toString() == event!['id']);
expect(
sentEvents.length, 1); // one button click should only send one error
final sentEvent = sentEvents.first;

final tags = event!['tags'] as List<dynamic>;

print('event id: ${event['id']}');
print('event title: ${event['title']}');
expect(sentEvent!.eventId.toString(), event['id']);
expect('_Exception: Exception: captureException', event['title']);
expect(sentEvent.release, event['release']['version']);
expect(
2,
(tags.firstWhere((e) => e['value'] == sentEvent.environment) as Map)
.length);
expect(sentEvent.fingerprint, event['fingerprint'] ?? []);
expect(
2,
(tags.firstWhere((e) => e['value'] == SentryLevel.error.name) as Map)
.length);
expect(sentEvent.logger, event['logger']);

final dist = tags.firstWhere((element) => element['key'] == 'dist');
expect('1', dist['value']);

final environment =
tags.firstWhere((element) => element['key'] == 'environment');
expect('integration', environment['value']);
});
});
}

class Fixture {
SentryEvent? sentEvent;
List<SentryEvent?> sentEvents = [];

FutureOr<SentryEvent?> beforeSend(SentryEvent event, Hint hint) async {
sentEvent = event;
sentEvents.add(event);
return event;
}

Expand All @@ -237,16 +250,16 @@ class Fixture {

while (retries < maxRetries) {
try {
print("Trying to fetch $url [try $retries/$maxRetries]");
print('Trying to fetch $url [try $retries/$maxRetries]');
final response = await client.get(
url,
headers: <String, String>{'Authorization': 'Bearer $authToken'},
);
print("Response status code: ${response.statusCode}");
print('Response status code: ${response.statusCode}');
if (response.statusCode == 200) {
return jsonDecode(utf8.decode(response.bodyBytes));
} else if (response.statusCode == 401) {
print("Cannot fetch $url - invalid auth token.");
print('Cannot fetch $url - invalid auth token.');
break;
}
} catch (e) {
Expand Down
19 changes: 10 additions & 9 deletions flutter/example/integration_test/utils.dart
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
import 'package:flutter/cupertino.dart';
import 'dart:async';

/// Restores the onError to it's original state.
/// This makes assertion errors readable.
import 'package:flutter/widgets.dart';

/// Restores Flutter's `FlutterError.onError` to its original state after executing a function.
///
/// testWidgets override Flutter.onError by default
/// If a fail happens during integration tests this would complain that
/// the FlutterError.onError was overwritten and wasn't reset to its
/// state before asserting.
/// `testWidgets` and `SentryFlutter.init` automatically override `FlutterError.onError`.
/// If `FlutterError.onError` is not restored to its original state and an assertion fails
/// Flutter will complain and throw an error.
///
/// This function needs to be executed before assertions.
Future<void> restoreFlutterOnErrorAfter(Future<void> Function() fn) async {
/// This function ensures `FlutterError.onError` is restored to its initial state after `fn` runs.
/// Assertions must only be executed after onError has been restored.
FutureOr<void> restoreFlutterOnErrorAfter(FutureOr<void> Function() fn) async {
final originalOnError = FlutterError.onError;
await fn();
final overriddenOnError = FlutterError.onError;
Expand Down
1 change: 1 addition & 0 deletions flutter/example/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -781,6 +781,7 @@ void navigateToAutoCloseScreen(BuildContext context) {
context,
MaterialPageRoute(
settings: const RouteSettings(name: 'AutoCloseScreen'),
// ignore: deprecated_member_use
builder: (context) => SentryDisplayWidget(child: const AutoCloseScreen()),
),
);
Expand Down
7 changes: 7 additions & 0 deletions flutter/lib/src/native/cocoa/sentry_native_cocoa.dart
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import 'dart:async';
import 'dart:ffi';
import 'dart:typed_data';
import 'dart:ui';

import 'package:meta/meta.dart';

import '../../../sentry_flutter.dart';
import '../../replay/replay_config.dart';
import '../../screenshot/recorder.dart';
import '../../screenshot/recorder_config.dart';
import '../sentry_native_channel.dart';
Expand Down Expand Up @@ -68,6 +70,11 @@ class SentryNativeCocoa extends SentryNativeChannel {
return super.init(hub);
}

@override
FutureOr<void> setReplayConfig(ReplayConfig config) {
// Note: unused on iOS.
}

@override
int? startProfiler(SentryId traceId) => tryCatchSync('startProfiler', () {
final cSentryId = cocoa.SentryId1.alloc(_lib)
Expand Down
2 changes: 1 addition & 1 deletion flutter/lib/src/native/sentry_native_channel.dart
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,7 @@ class SentryNativeChannel
bool get supportsReplay => false;

@override
Future<void> setReplayConfig(ReplayConfig config) =>
FutureOr<void> setReplayConfig(ReplayConfig config) =>
channel.invokeMethod('setReplayConfig', {
'width': config.width,
'height': config.height,
Expand Down
Loading

0 comments on commit d51b5d0

Please sign in to comment.