diff --git a/.gitignore b/.gitignore index 722f819f1a..00f642ea82 100644 --- a/.gitignore +++ b/.gitignore @@ -15,4 +15,6 @@ ios/ build/ .cxx/ + .test_coverage.dart +dart/coverage/* diff --git a/CHANGELOG.md b/CHANGELOG.md index be2abfe689..55f26e7c95 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -35,6 +35,7 @@ - Ref: execute before send callback - Feat: add lastEventId to the Sentry static API - Feat: addBreadcrumb on Static API +- Add a Dart web example - Fix: Integrations are executed on Hub creation - Fix: NoOp encode for Web - Fix: Breadcrumb data should accept serializable types and not only String values diff --git a/dart/example/event_example.dart b/dart/example/event_example.dart index 8848782ed3..d4559014ff 100644 --- a/dart/example/event_example.dart +++ b/dart/example/event_example.dart @@ -6,43 +6,45 @@ final event = SentryEvent( release: '1.4.0-preview.1', environment: 'Test', message: Message('This is an example Dart event.'), - transaction: '/example/app', - level: SentryLevel.warning, tags: const {'project-id': '7371'}, - extra: const {'company-name': 'Dart Inc'}, + extra: const {'section': '1'}, fingerprint: const ['example-dart'], userContext: const User( - id: '800', - username: 'first-user', - email: 'first@user.lan', - ipAddress: '127.0.0.1', - extras: {'first-sign-in': '2020-01-01'}), + id: '800', + username: 'first-user', + email: 'first@user.lan', + ipAddress: '127.0.0.1', + extras: {'first-sign-in': '2020-01-01'}, + ), breadcrumbs: [ Breadcrumb( - message: 'UI Lifecycle', - timestamp: DateTime.now().toUtc(), - category: 'ui.lifecycle', - type: 'navigation', - data: {'screen': 'MainActivity', 'state': 'created'}, - level: SentryLevel.info) + message: 'UI Lifecycle', + timestamp: DateTime.now().toUtc(), + category: 'ui.lifecycle', + type: 'navigation', + data: {'screen': 'MainActivity', 'state': 'created'}, + level: SentryLevel.info, + ) ], contexts: Contexts( operatingSystem: const OperatingSystem( - name: 'Android', - version: '5.0.2', - build: 'LRX22G.P900XXS0BPL2', - kernelVersion: - 'Linux version 3.4.39-5726670 (dpi@SWHC3807) (gcc version 4.8 (GCC) ) #1 SMP PREEMPT Thu Dec 1 19:42:39 KST 2016', - rooted: false), + name: 'Android', + version: '5.0.2', + build: 'LRX22G.P900XXS0BPL2', + kernelVersion: + 'Linux version 3.4.39-5726670 (dpi@SWHC3807) (gcc version 4.8 (GCC) ) #1 SMP PREEMPT Thu Dec 1 19:42:39 KST 2016', + rooted: false, + ), runtimes: [const Runtime(name: 'ART', version: '5')], app: App( - name: 'Example Dart App', - version: '1.42.0', - identifier: 'HGT-App-13', - build: '93785', - buildType: 'release', - deviceAppHash: '5afd3a6', - startTime: DateTime.now().toUtc()), + name: 'Example Dart App', + version: '1.42.0', + identifier: 'HGT-App-13', + build: '93785', + buildType: 'release', + deviceAppHash: '5afd3a6', + startTime: DateTime.now().toUtc(), + ), browser: const Browser(name: 'Firefox', version: '42.0.1'), device: Device( name: 'SM-P900', diff --git a/dart/example/main.dart b/dart/example/main.dart index 777407967e..9559110536 100644 --- a/dart/example/main.dart +++ b/dart/example/main.dart @@ -3,32 +3,72 @@ // found in the LICENSE file. import 'dart:async'; -import 'dart:io'; import 'package:sentry/sentry.dart'; import 'event_example.dart'; /// Sends a test exception report to Sentry.io using this Dart client. -Future main(List rawArgs) async { - if (rawArgs.length != 1) { - stderr.writeln( - 'Expected exactly one argument, which is the DSN issued by Sentry.io to your project.'); - exit(1); - } +Future main() async { + const dsn = + 'https://cb0fad6f5d4e42ebb9c956cb0463edc9@o447951.ingest.sentry.io/5428562'; + + SentryEvent processTagEvent(SentryEvent event, Object hint) => + event..tags.addAll({'page-locale': 'en-us'}); + + Sentry.init((options) => options + ..dsn = dsn + ..addEventProcessor(processTagEvent)); + + Sentry.addBreadcrumb( + Breadcrumb( + message: 'Authenticated user', + category: 'auth', + type: 'debug', + data: { + 'admin': true, + 'permissions': [1, 2, 3] + }, + ), + ); - final dsn = rawArgs.single; - Sentry.init((options) => options.dsn = dsn); + Sentry.configureScope((scope) { + scope + ..user = User( + id: '800', + username: 'first-user', + email: 'first@user.lan', + ipAddress: '127.0.0.1', + extras: {'first-sign-in': '2020-01-01'}, + ) + ..fingerprint = ['example-dart'] + ..transaction = '/example/app' + ..level = SentryLevel.warning + ..setTag('build', '579') + ..setExtra('company-name', 'Dart Inc'); + }); print('\nReporting a complete event example: '); // Sends a full Sentry event payload to show the different parts of the UI. final sentryId = await Sentry.captureEvent(event); - print('SentryId : ${sentryId}'); + print('Capture event result : SentryId : ${sentryId}'); + + print('\nCapture message: '); + + // Sends a full Sentry event payload to show the different parts of the UI. + final messageSentryId = await Sentry.captureMessage( + 'Message 1', + level: SentryLevel.warning, + template: 'Message %s', + params: ['1'], + ); + + print('Capture message result : SentryId : ${messageSentryId}'); try { - await foo(); + await loadConfig(); } catch (error, stackTrace) { print('\nReporting the following stack trace: '); print(stackTrace); @@ -37,7 +77,7 @@ Future main(List rawArgs) async { stackTrace: stackTrace, ); - print('SentryId : ${sentryId}'); + print('Capture exception result : SentryId : ${sentryId}'); } finally { await Sentry.close(); } @@ -45,14 +85,14 @@ Future main(List rawArgs) async { /* TODO(rxlabz) Sentry CaptureMessage(message, level) */ } -Future foo() async { - await bar(); +Future loadConfig() async { + await parseConfig(); } -Future bar() async { - await baz(); +Future parseConfig() async { + await decode(); } -Future baz() async { +Future decode() async { throw StateError('This is a test error'); } diff --git a/dart/example_web/.gitignore b/dart/example_web/.gitignore new file mode 100644 index 0000000000..3d64647b50 --- /dev/null +++ b/dart/example_web/.gitignore @@ -0,0 +1,9 @@ +# Files and directories created by pub +.dart_tool/ +.packages + +# Conventional directory for build outputs +build/ + +# Directory created by dartdoc +doc/api/ diff --git a/dart/example_web/README.md b/dart/example_web/README.md new file mode 100644 index 0000000000..73e0886e79 --- /dev/null +++ b/dart/example_web/README.md @@ -0,0 +1,9 @@ +# Sentry Dart : web example + +```dart +pub get + +# run the project ( see https://dart.dev/tools/webdev#serve ) +webdev serve --release + +``` diff --git a/dart/example_web/pubspec.yaml b/dart/example_web/pubspec.yaml new file mode 100644 index 0000000000..2b2d21817f --- /dev/null +++ b/dart/example_web/pubspec.yaml @@ -0,0 +1,16 @@ +name: sentry_dart_web_example +description: An absolute bare-bones web app. +# version: 1.0.0 +#homepage: https://www.example.com + +environment: + sdk: ^2.0.0 + +dependencies: + sentry: + path: ../.. + +dev_dependencies: + build_runner: ^1.10.0 + build_web_compilers: ^2.13.0 + pedantic: ^1.9.0 diff --git a/dart/example_web/web/event.dart b/dart/example_web/web/event.dart new file mode 100644 index 0000000000..d4559014ff --- /dev/null +++ b/dart/example_web/web/event.dart @@ -0,0 +1,77 @@ +import 'package:sentry/src/protocol.dart'; + +final event = SentryEvent( + logger: 'main', + serverName: 'server.dart', + release: '1.4.0-preview.1', + environment: 'Test', + message: Message('This is an example Dart event.'), + tags: const {'project-id': '7371'}, + extra: const {'section': '1'}, + fingerprint: const ['example-dart'], + userContext: const User( + id: '800', + username: 'first-user', + email: 'first@user.lan', + ipAddress: '127.0.0.1', + extras: {'first-sign-in': '2020-01-01'}, + ), + breadcrumbs: [ + Breadcrumb( + message: 'UI Lifecycle', + timestamp: DateTime.now().toUtc(), + category: 'ui.lifecycle', + type: 'navigation', + data: {'screen': 'MainActivity', 'state': 'created'}, + level: SentryLevel.info, + ) + ], + contexts: Contexts( + operatingSystem: const OperatingSystem( + name: 'Android', + version: '5.0.2', + build: 'LRX22G.P900XXS0BPL2', + kernelVersion: + 'Linux version 3.4.39-5726670 (dpi@SWHC3807) (gcc version 4.8 (GCC) ) #1 SMP PREEMPT Thu Dec 1 19:42:39 KST 2016', + rooted: false, + ), + runtimes: [const Runtime(name: 'ART', version: '5')], + app: App( + name: 'Example Dart App', + version: '1.42.0', + identifier: 'HGT-App-13', + build: '93785', + buildType: 'release', + deviceAppHash: '5afd3a6', + startTime: DateTime.now().toUtc(), + ), + browser: const Browser(name: 'Firefox', version: '42.0.1'), + device: Device( + name: 'SM-P900', + family: 'SM-P900', + model: 'SM-P900 (LRX22G)', + modelId: 'LRX22G', + arch: 'armeabi-v7a', + batteryLevel: 99, + orientation: Orientation.landscape, + manufacturer: 'samsung', + brand: 'samsung', + screenResolution: '2560x1600', + screenDensity: 2.1, + screenDpi: 320, + online: true, + charging: true, + lowMemory: true, + simulator: false, + memorySize: 1500, + freeMemory: 200, + usableMemory: 4294967296, + storageSize: 4294967296, + freeStorage: 2147483648, + externalStorageSize: 8589934592, + externalFreeStorage: 2863311530, + bootTime: DateTime.now().toUtc(), + timezone: 'America/Toronto', + ), + ), +); diff --git a/dart/example_web/web/favicon.ico b/dart/example_web/web/favicon.ico new file mode 100644 index 0000000000..7ba349b3e6 Binary files /dev/null and b/dart/example_web/web/favicon.ico differ diff --git a/dart/example_web/web/index.html b/dart/example_web/web/index.html new file mode 100644 index 0000000000..287720b3f4 --- /dev/null +++ b/dart/example_web/web/index.html @@ -0,0 +1,49 @@ + + + + + + + + + dart_web + + + + + + + + + + +
+ +
+ +
Captured
+
+ +
+ +
Captured
+
+ +
+ +
Captured
+
+ + + diff --git a/dart/example_web/web/main.dart b/dart/example_web/web/main.dart new file mode 100644 index 0000000000..edbf86c9c3 --- /dev/null +++ b/dart/example_web/web/main.dart @@ -0,0 +1,114 @@ +import 'dart:async'; +import 'dart:html'; + +import 'package:sentry/sentry.dart'; + +import 'event.dart'; + +const dsn = + 'https://cb0fad6f5d4e42ebb9c956cb0463edc9@o447951.ingest.sentry.io/5428562'; + +void main() { + querySelector('#output').text = 'Your Dart app is running.'; + + querySelector('#btEvent') + .onClick + .listen((event) => captureCompleteExampleEvent()); + querySelector('#btMessage').onClick.listen((event) => captureMessage()); + querySelector('#btException').onClick.listen((event) => captureException()); + + initSentry(); +} + +void initSentry() { + SentryEvent processTagEvent(SentryEvent event, Object hint) => + event..tags.addAll({'page-locale': 'en-us'}); + + Sentry.init((options) => options + ..dsn = dsn + ..addEventProcessor(processTagEvent)); + + Sentry.addBreadcrumb( + Breadcrumb( + message: 'Authenticated user', + category: 'auth', + type: 'debug', + data: { + 'admin': true, + 'permissions': [1, 2, 3] + }, + ), + ); + + Sentry.configureScope((scope) { + scope + ..user = User( + id: '800', + username: 'first-user', + email: 'first@user.lan', + ipAddress: '127.0.0.1', + extras: {'first-sign-in': '2020-01-01'}, + ) + ..fingerprint = ['example-dart'] + ..transaction = '/example/app' + ..level = SentryLevel.warning + ..setTag('build', '579') + ..setExtra('company-name', 'Dart Inc'); + }); +} + +void captureMessage() async { + print('Capturing Message : '); + final sentryId = await Sentry.captureMessage( + 'Message 2', + template: 'Message %s', + params: ['2'], + ); + print('capture message result : $sentryId'); + if (sentryId != SentryId.empty()) { + querySelector('#messageResult').style.display = 'block'; + } + await Sentry.close(); +} + +void captureException() async { + try { + await buildCard(); + } catch (error, stackTrace) { + print('\nReporting the following stack trace: '); + print(stackTrace); + final sentryId = await Sentry.captureException( + error, + stackTrace: stackTrace, + ); + + print('Capture exception : SentryId: ${sentryId}'); + + if (sentryId != SentryId.empty()) { + querySelector('#exceptionResult').style.display = 'block'; + } + } +} + +Future captureCompleteExampleEvent() async { + final sentryId = await Sentry.captureEvent(event); + + print('\nReporting a complete event example: ${sdkName}'); + print('Response SentryId: ${sentryId}'); + + if (sentryId != SentryId.empty()) { + querySelector('#eventResult').style.display = 'block'; + } +} + +Future buildCard() async { + await loadData(); +} + +Future loadData() async { + await parseData(); +} + +Future parseData() async { + throw StateError('This is a test error'); +} diff --git a/dart/example_web/web/styles.css b/dart/example_web/web/styles.css new file mode 100644 index 0000000000..cc035c95c9 --- /dev/null +++ b/dart/example_web/web/styles.css @@ -0,0 +1,14 @@ +@import url(https://fonts.googleapis.com/css?family=Roboto); + +html, body { + width: 100%; + height: 100%; + margin: 0; + padding: 0; + font-family: 'Roboto', sans-serif; +} + +#output { + padding: 20px; + text-align: center; +} diff --git a/dart/lib/src/browser_client.dart b/dart/lib/src/browser_client.dart index 26a8240d50..321d723ef1 100644 --- a/dart/lib/src/browser_client.dart +++ b/dart/lib/src/browser_client.dart @@ -5,8 +5,6 @@ /// A pure Dart client for Sentry.io crash reporting. import 'dart:html' show window; -import 'package:http/browser_client.dart'; - import 'client.dart'; import 'protocol.dart'; import 'sentry_options.dart'; @@ -28,8 +26,6 @@ class SentryBrowserClient extends SentryClient { /// If [httpClient] is provided, it is used instead of the default client to /// make HTTP calls to Sentry.io. This is useful in tests. factory SentryBrowserClient(SentryOptions options) { - options.httpClient ??= BrowserClient(); - options.sdk ??= Sdk(name: sdkName, version: sdkVersion); // origin is necessary for sentry to resolve stacktrace diff --git a/dart/lib/src/client.dart b/dart/lib/src/client.dart index c10b666928..f861a07c4e 100644 --- a/dart/lib/src/client.dart +++ b/dart/lib/src/client.dart @@ -167,7 +167,6 @@ abstract class SentryClient { event = event.copyWith(breadcrumbs: scope.breadcrumbs); } - // TODO add tests // Merge the scope tags. event = event.copyWith( tags: scope.tags.map((key, value) => MapEntry(key, value)) diff --git a/dart/lib/src/sentry.dart b/dart/lib/src/sentry.dart index d44d80fbc1..ff3c699a48 100644 --- a/dart/lib/src/sentry.dart +++ b/dart/lib/src/sentry.dart @@ -4,9 +4,9 @@ import 'package:meta/meta.dart'; import 'client.dart'; import 'hub.dart'; +import 'noop_hub.dart'; import 'protocol.dart'; import 'sentry_options.dart'; -import 'noop_hub.dart'; /// Configuration options callback typedef OptionsConfiguration = void Function(SentryOptions); diff --git a/dart/lib/src/sentry_options.dart b/dart/lib/src/sentry_options.dart index 8adadbe9d7..1a38bfc126 100644 --- a/dart/lib/src/sentry_options.dart +++ b/dart/lib/src/sentry_options.dart @@ -16,21 +16,32 @@ class SentryOptions { /// just not send any events. String dsn; + bool _compressPayload = true; + /// If [compressPayload] is `true` the outgoing HTTP payloads are compressed /// using gzip. Otherwise, the payloads are sent in plain UTF8-encoded JSON /// text. If not specified, the compression is enabled by default. - bool compressPayload = false; + bool get compressPayload => _compressPayload; + + set compressPayload(bool compressPayload) => + _compressPayload = compressPayload ?? _compressPayload; + + Client _httpClient = Client(); /// If [httpClient] is provided, it is used instead of the default client to /// make HTTP calls to Sentry.io. This is useful in tests. - Client httpClient; + Client get httpClient => _httpClient; + + set httpClient(Client httpClient) => _httpClient = httpClient ?? _httpClient; /// If [clock] is provided, it is used to get time instead of the system /// clock. This is useful in tests. Should be an implementation of [ClockProvider]. - ClockProvider _clock; + ClockProvider _clock = getUtcDateTime; ClockProvider get clock => _clock; + set clock(ClockProvider clock) => _clock = clock ?? _clock; + /// This variable controls the total amount of breadcrumbs that should be captured Default is 100 int maxBreadcrumbs = 100; @@ -134,14 +145,7 @@ class SentryOptions { // TODO: sendDefaultPii // TODO: those ctor params could be set on Sentry._setDefaultConfiguration or instantiate by default here - SentryOptions({ - this.dsn, - this.compressPayload, - this.httpClient, - ClockProvider clock = getUtcDateTime, - }) { - _clock = clock; - } + SentryOptions({this.dsn}); /// Adds an event processor void addEventProcessor(EventProcessor eventProcessor) { diff --git a/dart/test/hub_test.dart b/dart/test/hub_test.dart index 409e69a584..e253929c49 100644 --- a/dart/test/hub_test.dart +++ b/dart/test/hub_test.dart @@ -1,9 +1,10 @@ +import 'dart:async'; + import 'package:collection/collection.dart'; import 'package:mockito/mockito.dart'; import 'package:sentry/sentry.dart'; import 'package:sentry/src/hub.dart'; import 'package:test/test.dart'; -import 'dart:async'; import 'mocks.dart'; diff --git a/dart/test/scope_test.dart b/dart/test/scope_test.dart index f48152d07d..afb2b57700 100644 --- a/dart/test/scope_test.dart +++ b/dart/test/scope_test.dart @@ -231,8 +231,10 @@ void main() { expect(sut.tags, clone.tags); expect(sut.breadcrumbs, clone.breadcrumbs); expect(ListEquality().equals(sut.fingerprint, clone.fingerprint), true); - expect(ListEquality().equals(sut.eventProcessors, clone.eventProcessors), - true); + expect( + ListEquality().equals(sut.eventProcessors, clone.eventProcessors), + true, + ); }); } diff --git a/dart/test/sentry_client_test.dart b/dart/test/sentry_client_test.dart index d6801cc753..ec317afb29 100644 --- a/dart/test/sentry_client_test.dart +++ b/dart/test/sentry_client_test.dart @@ -5,6 +5,173 @@ import 'package:test/test.dart'; import 'mocks.dart'; void main() { + group('SentryClient captures message', () { + SentryOptions options; + + setUp(() { + options = SentryOptions(dsn: fakeDsn); + options.transport = MockTransport(); + }); + + test('should capture message', () async { + final client = SentryClient(options); + await client.captureMessage( + 'simple message 1', + template: 'simple message %d', + params: [1], + level: SentryLevel.error, + ); + + final capturedEvent = (verify( + options.transport.send(captureAny), + ).captured.first) as SentryEvent; + + expect(capturedEvent.message.formatted, 'simple message 1'); + expect(capturedEvent.message.template, 'simple message %d'); + expect(capturedEvent.message.params, [1]); + }); + }); + + group('SentryClient captures exception', () { + SentryOptions options; + + Error error; + StackTrace stackTrace; + + setUp(() { + options = SentryOptions(dsn: fakeDsn); + options.transport = MockTransport(); + }); + + test('should capture exception', () async { + try { + throw StateError('Error'); + } on Error catch (err, stack) { + error = err; + stackTrace = stack; + } + + final client = SentryClient(options); + await client.captureException(error, stackTrace: stackTrace); + + final capturedEvent = (verify( + options.transport.send(captureAny), + ).captured.first) as SentryEvent; + + expect(capturedEvent.exception, error); + expect(capturedEvent.stackTrace, stackTrace); + }); + }); + + group('SentryClient : apply scope to the captured event', () { + SentryOptions options; + Scope scope; + + final level = SentryLevel.error; + final transaction = '/test/scope'; + final fingerprint = ['foo', 'bar', 'baz']; + final user = User(id: '123', username: 'test'); + final crumb = Breadcrumb(message: 'bread'); + final scopeTagKey = 'scope-tag'; + final scopeTagValue = 'scope-tag-value'; + final eventTagKey = 'event-tag'; + final eventTagValue = 'event-tag-value'; + final scopeExtraKey = 'scope-extra'; + final scopeExtraValue = 'scope-extra-value'; + final eventExtraKey = 'event-extra'; + final eventExtraValue = 'event-extra-value'; + + final event = SentryEvent( + tags: {eventTagKey: eventTagValue}, + extra: {eventExtraKey: eventExtraValue}, + level: SentryLevel.warning, + ); + + setUp(() { + options = SentryOptions(dsn: fakeDsn); + options.transport = MockTransport(); + + scope = Scope(options) + ..user = user + ..level = level + ..transaction = transaction + ..fingerprint = fingerprint + ..addBreadcrumb(crumb) + ..setTag(scopeTagKey, scopeTagValue) + ..setExtra(scopeExtraKey, scopeExtraValue); + }); + + test('should apply the scope', () async { + final client = SentryClient(options); + await client.captureEvent(event, scope: scope); + + final capturedEvent = (verify( + options.transport.send(captureAny), + ).captured.first) as SentryEvent; + + expect(capturedEvent.userContext?.id, user.id); + expect(capturedEvent.level.name, SentryLevel.error.name); + expect(capturedEvent.transaction, transaction); + expect(capturedEvent.fingerprint, fingerprint); + expect(capturedEvent.breadcrumbs.first, crumb); + expect(capturedEvent.tags, { + scopeTagKey: scopeTagValue, + eventTagKey: eventTagValue, + }); + expect(capturedEvent.extra, { + scopeExtraKey: scopeExtraValue, + eventExtraKey: eventExtraValue, + }); + }); + }); + + group('SentryClient : apply partial scope to the captured event', () { + SentryOptions options; + Scope scope; + + final transaction = '/test/scope'; + final eventTransaction = '/event/transaction'; + final fingerprint = ['foo', 'bar', 'baz']; + final eventFingerprint = ['123', '456', '798']; + final user = User(id: '123'); + final eventUser = User(id: '987'); + final crumb = Breadcrumb(message: 'bread'); + final eventCrumbs = [Breadcrumb(message: 'bread')]; + + final event = SentryEvent( + level: SentryLevel.warning, + transaction: eventTransaction, + userContext: eventUser, + fingerprint: eventFingerprint, + breadcrumbs: eventCrumbs, + ); + + setUp(() { + options = SentryOptions(dsn: fakeDsn); + options.transport = MockTransport(); + scope = Scope(options) + ..user = user + ..transaction = transaction + ..fingerprint = fingerprint + ..addBreadcrumb(crumb); + }); + + test('should not apply the scope to non null event fields ', () async { + final client = SentryClient(options); + await client.captureEvent(event, scope: scope); + + final capturedEvent = (verify( + options.transport.send(captureAny), + ).captured.first) as SentryEvent; + + expect(capturedEvent.userContext.id, eventUser.id); + expect(capturedEvent.level.name, SentryLevel.warning.name); + expect(capturedEvent.transaction, eventTransaction); + expect(capturedEvent.fingerprint, eventFingerprint); + expect(capturedEvent.breadcrumbs, eventCrumbs); + }); + }); + group('SentryClient sampling', () { SentryOptions options; diff --git a/dart/test/test_utils.dart b/dart/test/test_utils.dart index f438ca1a8e..d540b39fbc 100644 --- a/dart/test/test_utils.dart +++ b/dart/test/test_utils.dart @@ -70,12 +70,10 @@ Future testCaptureException( fail('Unexpected request on ${request.method} ${request.url} in HttpMock'); }); - final options = SentryOptions( - dsn: testDsn, - httpClient: httpMock, - clock: fakeClockProvider, - compressPayload: compressPayload, - ) + final options = SentryOptions(dsn: testDsn) + ..compressPayload = compressPayload + ..clock = fakeClockProvider + ..httpClient = httpMock ..serverName = 'test.server.com' ..release = '1.2.3' ..environment = 'staging'; @@ -181,8 +179,10 @@ void runTest({Codec, List> gzip, bool isWeb = false}) { final options = SentryOptions(dsn: testDsn); final client = SentryClient(options); expect(options.transport.dsn.uri, Uri.parse(testDsn)); - expect(options.transport.dsn.postUri, - 'https://sentry.example.com/api/1/store/'); + expect( + options.transport.dsn.postUri, + 'https://sentry.example.com/api/1/store/', + ); expect(options.transport.dsn.publicKey, 'public'); expect(options.transport.dsn.secretKey, 'secret'); expect(options.transport.dsn.projectId, '1'); @@ -193,8 +193,10 @@ void runTest({Codec, List> gzip, bool isWeb = false}) { final options = SentryOptions(dsn: _testDsnWithoutSecret); final client = SentryClient(options); expect(options.transport.dsn.uri, Uri.parse(_testDsnWithoutSecret)); - expect(options.transport.dsn.postUri, - 'https://sentry.example.com/api/1/store/'); + expect( + options.transport.dsn.postUri, + 'https://sentry.example.com/api/1/store/', + ); expect(options.transport.dsn.publicKey, 'public'); expect(options.transport.dsn.secretKey, null); expect(options.transport.dsn.projectId, '1'); @@ -242,12 +244,10 @@ void runTest({Codec, List> gzip, bool isWeb = false}) { }); final client = SentryClient( - SentryOptions( - dsn: _testDsnWithoutSecret, - httpClient: httpMock, - clock: fakeClockProvider, - compressPayload: false, - ) + SentryOptions(dsn: _testDsnWithoutSecret) + ..httpClient = httpMock + ..clock = fakeClockProvider + ..compressPayload = false ..serverName = 'test.server.com' ..release = '1.2.3' ..environment = 'staging', @@ -299,10 +299,10 @@ void runTest({Codec, List> gzip, bool isWeb = false}) { final client = SentryClient( SentryOptions( dsn: testDsn, - httpClient: httpMock, - clock: fakeClockProvider, - compressPayload: false, ) + ..httpClient = httpMock + ..clock = fakeClockProvider + ..compressPayload = false ..serverName = 'test.server.com' ..release = '1.2.3' ..environment = 'staging', @@ -353,10 +353,10 @@ void runTest({Codec, List> gzip, bool isWeb = false}) { final options = SentryOptions( dsn: testDsn, - httpClient: httpMock, - clock: fakeClockProvider, - compressPayload: false, ) + ..httpClient = httpMock + ..clock = fakeClockProvider + ..compressPayload = false ..serverName = 'test.server.com' ..release = '1.2.3' ..environment = 'staging';