Skip to content
Merged
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
114 changes: 63 additions & 51 deletions docs/design/plans/2026-09-16-implementation-plan.md

Large diffs are not rendered by default.

48 changes: 46 additions & 2 deletions examples/bare/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,9 @@
* `channel` is scenarios/channel.ts, `data` is scenarios/data.ts, `secure`
* is scenarios/secure.tsx, `attributes`/`attributes-persist` are
* scenarios/attributes.ts, `e2e-native-smoke` / `native-crash-*` are
* scenarios/native.ts, and `blackout`, `blackout-prelaunch`,
* `secure-component` and `view-tree` are scenarios/privacy.tsx.
* scenarios/native.ts, `blackout`, `blackout-prelaunch`,
* `secure-component` and `view-tree` are scenarios/privacy.tsx, and
* `exc-*` are scenarios/exceptions.tsx.
*/
import { useEffect, useState } from 'react';
import {
Expand Down Expand Up @@ -70,6 +71,14 @@ import {
preLaunchPrivacyProbe,
runPrivacyScenario,
} from './scenarios/privacy';
import {
ExceptionStage,
installExceptionAppHandler,
isExceptionRenderScenario,
isExceptionScenario,
preLaunchExceptionProbe,
runExceptionScenario,
} from './scenarios/exceptions';

const STATUS_NAMES: Record<number, string> = {
[Status.Stopped]: 'Stopped',
Expand Down Expand Up @@ -204,6 +213,13 @@ export default function App() {
const [privacy, setPrivacy] = useState<
{ scenario: PrivacyScenario; nonce: string; launched: boolean } | undefined
>();
/**
* Set once Launched for `exc-boundary` / `exc-root`: mounts the thrower
* (with or without an app ErrorBoundary).
*/
const [exceptionRender, setExceptionRender] = useState<
{ scenario: 'exc-boundary' | 'exc-root'; nonce: string } | undefined
>();

useEffect(() => {
let cancelled = false;
Expand Down Expand Up @@ -308,6 +324,14 @@ export default function App() {
if (isNativeScenario(choice.scenario)) {
installNativeHandler(choice.scenario);
}
// Before launch(): app ErrorUtils handler (so Bugsee chains to it), and
// the pre-launch exception probe (must not reach a bundle).
if (isExceptionScenario(choice.scenario)) {
installExceptionAppHandler();
if (choice.scenario === 'exc-prelaunch') {
preLaunchExceptionProbe(choice.nonce);
}
}
console.log(`BUGSEE_E2E launching on ${Platform.OS}`);
// Polling starts before launch() is awaited, not after. The SDK brings
// capture up off the main thread, so by the time the promise resolves the
Expand Down Expand Up @@ -366,6 +390,20 @@ export default function App() {
return;
}

if (isExceptionScenario(choice.scenario)) {
if (isExceptionRenderScenario(choice.scenario)) {
if (!cancelled) {
setExceptionRender({
scenario: choice.scenario,
nonce: choice.nonce,
});
}
return;
}
runExceptionScenario(choice.scenario, choice.nonce);
return;
}

// Task 2.6: the option must have reached the SDK, not merely been
// accepted by launch(). Reading it back through getLaunchOptions is
// the only thing that shows the difference.
Expand Down Expand Up @@ -468,6 +506,12 @@ export default function App() {
launched={privacy.launched}
/>
)}
{exceptionRender !== undefined && (
<ExceptionStage
scenario={exceptionRender.scenario}
nonce={exceptionRender.nonce}
/>
)}
</View>
);
}
Expand Down
3 changes: 2 additions & 1 deletion examples/bare/android/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,9 @@ react {

/**
* Set this to true to Run Proguard on Release builds to minify the Java bytecode.
* Task 7.5a (and 7.1c's keep-rule check) turn it on with -PbugseeE2eMinify=true.
*/
def enableProguardInReleaseBuilds = false
def enableProguardInReleaseBuilds = findProperty('bugseeE2eMinify') == 'true'

/**
* The preferred build flavor of JavaScriptCore (JSC)
Expand Down
13 changes: 13 additions & 0 deletions examples/bare/e2e/bundles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,19 @@ export function parseBundle(file: string, dir: string): PulledBundle {
return { file, dir, request, manifest, log, captures, binaries };
}

/**
* The parsed `crash` capture (`crash.json`), or `undefined` when the
* manifest has none. Task 7.5a: exception payloads live in
* `exception.reason` as JSON text with no prefix.
*/
export function crashOf(bundle: PulledBundle): Record<string, unknown> | undefined {
const text = bundle.captures.get('crash');
if (text === undefined) {
return undefined;
}
return JSON.parse(text) as Record<string, unknown>;
}

/**
* The `events` array of the capture file of manifest `type` (`log`,
* `events.user`, `traces.user`), or `[]` when the bundle has none.
Expand Down
Loading
Loading