You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
feat(replay): Add ReplayFrameObserver for snapshot testing (#5386)
* feat(replay): Add beforeStoreFrame callback (JAVA-504)
Add an experimental callback that fires right before a replay frame is
stored to disk. The callback receives the masked bitmap (via Hint),
timestamp, and current screen name. This enables snapshot testing of
replay masking without needing to decode stored video segments.
Includes a Kotlin extension for ergonomic usage:
options.sessionReplay.beforeStoreFrame { bitmap, ts, screen -> ... }
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(replay): Add replay snapshot UI test with Sauce Labs collection (JAVA-504)
Add ReplaySnapshotTest that uses the beforeStoreFrame callback to
capture masked replay frames during a Compose UI test. Frames are
written to the Downloads/sauce_labs_custom_screenshots/ directory,
which is the standard path Sauce Labs collects screenshots from.
CI changes:
- Add *.png to Sauce Labs artifact match patterns
- Upload collected replay snapshots via sentry-cli build snapshots
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(replay): Use Java API in snapshot test to avoid extension dep (JAVA-504)
The Kotlin extension `beforeStoreFrame` comes from `sentry-android-replay`
which may not resolve in the UI test module. Use the Java callback API
directly instead.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(replay): Skip snapshot test on GH emulators and add changelog (JAVA-504)
GH Actions emulators don't support screenshot capture for replay,
so the ReplaySnapshotTest needs the same assumeThat guard used by
ReplayTest. Also adds a changelog entry for the beforeStoreFrame
callback.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Apply suggestion from @markushi
Co-authored-by: Markus Hintersteiner <markus.hintersteiner@sentry.io>
* refactor(replay): Replace beforeStoreFrame with ReplaySnapshotObserver (JAVA-504)
Move the frame observer API from the core sentry module to
sentry-android-replay so it can use Bitmap directly instead of
the Hint indirection. The new ReplaySnapshotObserver fun interface
lives in the replay module and is set on ReplayIntegration.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(replay): Mark ReplaySnapshotObserver as experimental and use Set in test (JAVA-504)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(replay): Add @ApiStatus.Experimental to ReplaySnapshotObserver (JAVA-504)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(replay): Make snapshotObserver public for cross-module access (JAVA-504)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(replay): Exclude ReplaySnapshotTest when integrations disabled (JAVA-504)
Move ReplaySnapshotTest to a conditional androidTestReplay source set
so it's only compiled when APPLY_SENTRY_INTEGRATIONS is true. The test
imports replay classes that aren't on the classpath otherwise.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(replay): Copy bitmap before passing to ReplaySnapshotObserver (JAVA-504)
Consumers of the observer API receive a copy of the bitmap instead of
the replay system's shared instance. This eliminates race conditions
and crashes when consumers store or use the bitmap asynchronously.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor(replay): Move ReplaySnapshotObserver to SentryReplayOptions with Hint API (JAVA-504)
Move ReplaySnapshotObserver from the replay module to SentryReplayOptions
in the core module and change the callback signature to use Hint instead
of Bitmap. The bitmap is now accessible via TypeCheckHint.REPLAY_FRAME_BITMAP.
This allows configuring the observer during Sentry.init{} alongside other
replay options, removing the need to cast replayController to
ReplayIntegration.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(replay): Remove unnecessary jetbrains-annotations dependency (JAVA-504)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor(replay): Rename ReplaySnapshotObserver to ReplayFrameObserver (JAVA-504)
Rename the interface to ReplayFrameObserver and the callback method
to onMaskedFrameCaptured to clarify that frames have masking applied.
Also update the changelog with a usage snippet.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Format code
* fix(replay): Call onMaskedFrameCaptured in File-based onScreenshotRecorded (JAVA-504)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(changelog): Move replay entry to Unreleased section (JAVA-504)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: Markus Hintersteiner <markus.hintersteiner@sentry.io>
Co-authored-by: Sentry Github Bot <bot+github-bot@sentry.io>
0 commit comments