From a95473306076ecdcad702437c62d0bb667f97895 Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Thu, 14 Nov 2024 22:26:06 +0100 Subject: [PATCH 1/9] test: move e2e test assertions to maestro runScript --- .../e2e-tests/maestro/captureException.yml | 3 +- .../maestro/utils/assertEventIdVisible.yml | 15 ++++- .../e2e-tests/maestro/utils/sentryApi.js | 66 +++++++++++++++++++ dev-packages/e2e-tests/src/EndToEndTests.tsx | 26 +------- .../e2e-tests/src/utils/fetchEvent.ts | 44 ------------- 5 files changed, 81 insertions(+), 73 deletions(-) create mode 100644 dev-packages/e2e-tests/maestro/utils/sentryApi.js delete mode 100644 dev-packages/e2e-tests/src/utils/fetchEvent.ts diff --git a/dev-packages/e2e-tests/maestro/captureException.yml b/dev-packages/e2e-tests/maestro/captureException.yml index d768aae04a..e8a268ef70 100644 --- a/dev-packages/e2e-tests/maestro/captureException.yml +++ b/dev-packages/e2e-tests/maestro/captureException.yml @@ -1,5 +1,6 @@ appId: ${APP_ID} +jsEngine: graaljs --- - runFlow: utils/launchTestAppClear.yml -- tapOn: "Capture Exception" +- tapOn: 'Capture Exception' - runFlow: utils/assertEventIdVisible.yml diff --git a/dev-packages/e2e-tests/maestro/utils/assertEventIdVisible.yml b/dev-packages/e2e-tests/maestro/utils/assertEventIdVisible.yml index f7995a5f6b..761747c754 100644 --- a/dev-packages/e2e-tests/maestro/utils/assertEventIdVisible.yml +++ b/dev-packages/e2e-tests/maestro/utils/assertEventIdVisible.yml @@ -2,9 +2,18 @@ appId: ${APP_ID} --- - extendedWaitUntil: visible: - id: "eventId" - timeout: 600_000 # 10 minutes + id: 'eventId' + timeout: 60_000 # 60 seconds - copyTextFrom: - id: "eventId" + id: 'eventId' - assertTrue: ${maestro.copiedText} + +- runScript: + file: sentryApi.js + env: + fetch: event + id: ${maestro.copiedText} + sentryAuthToken: ${SENTRY_AUTH_TOKEN} + +- assertTrue: ${output.event_id == maestro.copiedText} diff --git a/dev-packages/e2e-tests/maestro/utils/sentryApi.js b/dev-packages/e2e-tests/maestro/utils/sentryApi.js new file mode 100644 index 0000000000..aced60401b --- /dev/null +++ b/dev-packages/e2e-tests/maestro/utils/sentryApi.js @@ -0,0 +1,66 @@ +const baseUrl = 'https://sentry.io/api/0/projects/sentry-sdks/sentry-react-native'; + +const RETRY_COUNT = 600; +const RETRY_INTERVAL = 1000; +const requestHeaders = { 'Authorization': `Bearer ${sentryAuthToken}` } + +// Top-level async doesn't seem to work so we can't sleep. +// Seems to work fine without it though, from the logs it seems to be rather slow anyway. +// TODO reach out on to Maestro & GrallJS GitHub issues +// function sleep(ms) { +// return new Promise(resolve => setTimeout(resolve, ms)); +// } + +function fetchFromSentry(url) { + console.log(`Fetching ${url}`); + let retries = 0; + const shouldRetry = (response) => { + switch (response.status) { + case 200: + return false; + case 403: + throw new Error(`Could not fetch ${url}: ${response.status} | ${response.body}`); + default: + if (retries++ < RETRY_COUNT) { + console.log(`Request failed, retrying: ${retries}/${RETRY_COUNT}`); + return true; + } + throw new Error(`Could not fetch ${url} within retry limit: ${response.status} | ${response.body}`); + } + } + + while (true) { + const response = http.get(url, { headers: requestHeaders }) + if (!shouldRetry(response)) { + console.log('Received data:'); + console.log(response.body); + return json(response.body); + } + // await sleep(RETRY_INTERVAL); + } +}; + +function setOutput(data) { + for (const [key, value] of Object.entries(data)) { + output[key] = value; + } +} + +// Note: "fetch" and "id" are script inputs, see for example assertEventIdIVisible.yml +switch (fetch) { + case 'event': { + const data = fetchFromSentry(`${baseUrl}/events/${id}/json/`); + setOutput(data); + break; + } + case 'replay': { + const data = fetchFromSentry(`${baseUrl}/replays/${id}/`); + setOutput(data); + break; + } + case 'replaySegment': { + const data = fetchFromSentry(`${baseUrl}/replays/${replayId}/videos/${segment}/`); + setOutput(data); + break; + } +} diff --git a/dev-packages/e2e-tests/src/EndToEndTests.tsx b/dev-packages/e2e-tests/src/EndToEndTests.tsx index d6c38d9961..173286e57b 100644 --- a/dev-packages/e2e-tests/src/EndToEndTests.tsx +++ b/dev-packages/e2e-tests/src/EndToEndTests.tsx @@ -3,8 +3,6 @@ import * as React from 'react'; import { Text, View } from 'react-native'; import { LaunchArguments } from "react-native-launch-arguments"; -import { fetchEvent } from './utils/fetchEvent'; - const E2E_TESTS_READY_TEXT = 'E2E Tests Ready'; const getSentryAuthToken = (): @@ -30,28 +28,6 @@ const EndToEndTestsScreen = (): JSX.Element => { const [eventId, setEventId] = React.useState(null); const [error, setError] = React.useState('No error'); - async function assertEventReceived(eventId: string | undefined) { - if (!eventId) { - setError('Event ID is required'); - return; - } - - const value = getSentryAuthToken(); - if ('error' in value) { - setError(value.error); - return; - } - - const event = await fetchEvent(eventId, value.token); - - if (event.event_id !== eventId) { - setError('Event ID mismatch'); - return; - } - - setEventId(eventId); - } - React.useEffect(() => { const client: Sentry.ReactNativeClient | undefined = Sentry.getClient(); @@ -63,7 +39,7 @@ const EndToEndTestsScreen = (): JSX.Element => { // WARNING: This is only for testing purposes. // We only do this to render the eventId onto the UI for end to end tests. client.getOptions().beforeSend = (e) => { - assertEventReceived(e.event_id); + setEventId(e.event_id); return e; }; diff --git a/dev-packages/e2e-tests/src/utils/fetchEvent.ts b/dev-packages/e2e-tests/src/utils/fetchEvent.ts deleted file mode 100644 index d792f9748e..0000000000 --- a/dev-packages/e2e-tests/src/utils/fetchEvent.ts +++ /dev/null @@ -1,44 +0,0 @@ -import pRetry from 'p-retry'; -import type { Event } from '@sentry/types'; - -const domain = 'sentry.io'; -const eventEndpoint = 'api/0/projects/sentry-sdks/sentry-react-native/events'; - -const RETRY_COUNT = 600; -const FIRST_RETRY_MS = 1_000; -const MAX_RETRY_TIMEOUT = 5_000; - -const fetchEvent = async (eventId: string, authToken: string): Promise => { - const url = `https://${domain}/${eventEndpoint}/${eventId}/json/`; - - const toRetry = async () => { - const response = await fetch(url, { - headers: { - Authorization: `Bearer ${authToken}`, - 'Content-Type': 'application/json', - }, - method: 'GET', - }); - - const json = (await response.json()) as Event; - if (!json.event_id) { - throw new Error('No event ID found in the response'); - } - - return json; - }; - - const response = await pRetry(toRetry, { - retries: RETRY_COUNT, - minTimeout: FIRST_RETRY_MS, - maxTimeout: MAX_RETRY_TIMEOUT, - factor: 2, - onFailedAttempt: e => { - console.log(`Failed attempt ${e.attemptNumber} of ${RETRY_COUNT}: ${e.message}`); - }, - }); - - return response; -}; - -export { fetchEvent }; From e650070a8a72df948b7c55b4ceafc8b6ce221239 Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Thu, 14 Nov 2024 22:58:18 +0100 Subject: [PATCH 2/9] replay tests --- .../e2e-tests/maestro/captureException.yml | 1 + .../maestro/utils/assertEventIdVisible.yml | 2 +- .../e2e-tests/maestro/utils/assertReplay.yml | 22 ++++++++ .../e2e-tests/maestro/utils/sentryApi.js | 53 ++++++++++++------- .../e2e-tests/patch-scripts/rn.patch.app.js | 6 +++ 5 files changed, 64 insertions(+), 20 deletions(-) create mode 100644 dev-packages/e2e-tests/maestro/utils/assertReplay.yml diff --git a/dev-packages/e2e-tests/maestro/captureException.yml b/dev-packages/e2e-tests/maestro/captureException.yml index e8a268ef70..51b9c81cec 100644 --- a/dev-packages/e2e-tests/maestro/captureException.yml +++ b/dev-packages/e2e-tests/maestro/captureException.yml @@ -4,3 +4,4 @@ jsEngine: graaljs - runFlow: utils/launchTestAppClear.yml - tapOn: 'Capture Exception' - runFlow: utils/assertEventIdVisible.yml +- runFlow: utils/assertReplay.yml diff --git a/dev-packages/e2e-tests/maestro/utils/assertEventIdVisible.yml b/dev-packages/e2e-tests/maestro/utils/assertEventIdVisible.yml index 761747c754..bd099eeb02 100644 --- a/dev-packages/e2e-tests/maestro/utils/assertEventIdVisible.yml +++ b/dev-packages/e2e-tests/maestro/utils/assertEventIdVisible.yml @@ -16,4 +16,4 @@ appId: ${APP_ID} id: ${maestro.copiedText} sentryAuthToken: ${SENTRY_AUTH_TOKEN} -- assertTrue: ${output.event_id == maestro.copiedText} +- assertTrue: ${output.eventId == maestro.copiedText} diff --git a/dev-packages/e2e-tests/maestro/utils/assertReplay.yml b/dev-packages/e2e-tests/maestro/utils/assertReplay.yml new file mode 100644 index 0000000000..0b7a35cdd4 --- /dev/null +++ b/dev-packages/e2e-tests/maestro/utils/assertReplay.yml @@ -0,0 +1,22 @@ +appId: ${APP_ID} +--- +- extendedWaitUntil: + visible: + id: 'eventId' + timeout: 60_000 # 60 seconds + +- copyTextFrom: + id: 'eventId' +- assertTrue: ${maestro.copiedText} + +- runScript: + file: sentryApi.js + env: + fetch: replay + id: ${maestro.copiedText} + sentryAuthToken: ${SENTRY_AUTH_TOKEN} + +- assertTrue: ${output.replayId} +- assertTrue: ${output.replayDuration} +- assertTrue: ${output.replaySegments} +- assertTrue: ${output.replayCodec == 'ftypmp42'} diff --git a/dev-packages/e2e-tests/maestro/utils/sentryApi.js b/dev-packages/e2e-tests/maestro/utils/sentryApi.js index aced60401b..1c1cd29710 100644 --- a/dev-packages/e2e-tests/maestro/utils/sentryApi.js +++ b/dev-packages/e2e-tests/maestro/utils/sentryApi.js @@ -4,12 +4,21 @@ const RETRY_COUNT = 600; const RETRY_INTERVAL = 1000; const requestHeaders = { 'Authorization': `Bearer ${sentryAuthToken}` } -// Top-level async doesn't seem to work so we can't sleep. -// Seems to work fine without it though, from the logs it seems to be rather slow anyway. -// TODO reach out on to Maestro & GrallJS GitHub issues -// function sleep(ms) { -// return new Promise(resolve => setTimeout(resolve, ms)); -// } +function sleep(ms) { + // Top-level async doesn't seem to work. + // TODO reach out on to Maestro & GrallJS GitHub issues + // return new Promise(resolve => setTimeout(resolve, ms)); + // Instead, we need to do a busy wait. + const until = Date.now() + ms; + while (Date.now() < until) { + // console.log(`Sleeping for ${until - Date.now()} ms`); + try { + http.get('http://127.0.0.1:1'); + } catch (e) { + // Ignore + } + } +} function fetchFromSentry(url) { console.log(`Fetching ${url}`); @@ -22,7 +31,7 @@ function fetchFromSentry(url) { throw new Error(`Could not fetch ${url}: ${response.status} | ${response.body}`); default: if (retries++ < RETRY_COUNT) { - console.log(`Request failed, retrying: ${retries}/${RETRY_COUNT}`); + console.log(`Request failed (HTTP ${response.status}), retrying: ${retries}/${RETRY_COUNT}`); return true; } throw new Error(`Could not fetch ${url} within retry limit: ${response.status} | ${response.body}`); @@ -32,35 +41,41 @@ function fetchFromSentry(url) { while (true) { const response = http.get(url, { headers: requestHeaders }) if (!shouldRetry(response)) { - console.log('Received data:'); - console.log(response.body); + console.log(`Received HTTP ${response.status}: body length ${response.body.length}`); return json(response.body); } - // await sleep(RETRY_INTERVAL); + sleep(RETRY_INTERVAL); } }; function setOutput(data) { for (const [key, value] of Object.entries(data)) { + console.log(`Setting output.${key} = '${value}'`); output[key] = value; } } -// Note: "fetch" and "id" are script inputs, see for example assertEventIdIVisible.yml +// Note: "fetch", "id", "eventId", etc. are script inputs, see for example assertEventIdIVisible.yml switch (fetch) { case 'event': { const data = fetchFromSentry(`${baseUrl}/events/${id}/json/`); - setOutput(data); + setOutput({ eventId: data.event_id }); break; } case 'replay': { - const data = fetchFromSentry(`${baseUrl}/replays/${id}/`); - setOutput(data); - break; - } - case 'replaySegment': { - const data = fetchFromSentry(`${baseUrl}/replays/${replayId}/videos/${segment}/`); - setOutput(data); + const event = fetchFromSentry(`${baseUrl}/events/${eventId}/json/`); + const replayId = event._dsc.replay_id; + const replay = fetchFromSentry(`${baseUrl}/replays/${replayId}/`); + const segment = fetchFromSentry(`${baseUrl}/replays/${replayId}/videos/0/`); + + setOutput({ + replayId: replayId, + replayDuration: replay.data.duration, + replaySegments: replay.data.count_segments, + replayCodec: segment.slice(4, 12) + }); break; } + default: + throw new Error(`Unknown "fetch" value: '${fetch}'`); } diff --git a/dev-packages/e2e-tests/patch-scripts/rn.patch.app.js b/dev-packages/e2e-tests/patch-scripts/rn.patch.app.js index c32274bfa9..a78c49847e 100755 --- a/dev-packages/e2e-tests/patch-scripts/rn.patch.app.js +++ b/dev-packages/e2e-tests/patch-scripts/rn.patch.app.js @@ -26,6 +26,12 @@ Sentry.init({ release: '${SENTRY_RELEASE}', dist: '${SENTRY_DIST}', dsn: 'https://1df17bd4e543fdb31351dee1768bb679@o447951.ingest.sentry.io/5428561', + _experiments: { + replaysOnErrorSampleRate: 1.0, + }, + integrations: [ + Sentry.mobileReplayIntegration(), + ], }); `; const e2eComponentPatch = ''; From 5a8d40bb4682a8c7025f2ca0f07d45f3c79e185b Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Fri, 15 Nov 2024 18:51:46 +0100 Subject: [PATCH 3/9] fix tests --- .../maestro/utils/assertEventIdVisible.yml | 2 +- .../e2e-tests/maestro/utils/assertReplay.yml | 2 +- dev-packages/e2e-tests/maestro/utils/sentryApi.js | 15 +++++++-------- 3 files changed, 9 insertions(+), 10 deletions(-) diff --git a/dev-packages/e2e-tests/maestro/utils/assertEventIdVisible.yml b/dev-packages/e2e-tests/maestro/utils/assertEventIdVisible.yml index bd099eeb02..73883c46db 100644 --- a/dev-packages/e2e-tests/maestro/utils/assertEventIdVisible.yml +++ b/dev-packages/e2e-tests/maestro/utils/assertEventIdVisible.yml @@ -3,7 +3,7 @@ appId: ${APP_ID} - extendedWaitUntil: visible: id: 'eventId' - timeout: 60_000 # 60 seconds + timeout: 600_000 # 10 minutes - copyTextFrom: id: 'eventId' diff --git a/dev-packages/e2e-tests/maestro/utils/assertReplay.yml b/dev-packages/e2e-tests/maestro/utils/assertReplay.yml index 0b7a35cdd4..60688d1779 100644 --- a/dev-packages/e2e-tests/maestro/utils/assertReplay.yml +++ b/dev-packages/e2e-tests/maestro/utils/assertReplay.yml @@ -13,7 +13,7 @@ appId: ${APP_ID} file: sentryApi.js env: fetch: replay - id: ${maestro.copiedText} + eventId: ${maestro.copiedText} sentryAuthToken: ${SENTRY_AUTH_TOKEN} - assertTrue: ${output.replayId} diff --git a/dev-packages/e2e-tests/maestro/utils/sentryApi.js b/dev-packages/e2e-tests/maestro/utils/sentryApi.js index 1c1cd29710..39f10ed298 100644 --- a/dev-packages/e2e-tests/maestro/utils/sentryApi.js +++ b/dev-packages/e2e-tests/maestro/utils/sentryApi.js @@ -5,8 +5,7 @@ const RETRY_INTERVAL = 1000; const requestHeaders = { 'Authorization': `Bearer ${sentryAuthToken}` } function sleep(ms) { - // Top-level async doesn't seem to work. - // TODO reach out on to Maestro & GrallJS GitHub issues + // TODO reach out to Maestro & GrallJS via GitHub issues. // return new Promise(resolve => setTimeout(resolve, ms)); // Instead, we need to do a busy wait. const until = Date.now() + ms; @@ -42,7 +41,7 @@ function fetchFromSentry(url) { const response = http.get(url, { headers: requestHeaders }) if (!shouldRetry(response)) { console.log(`Received HTTP ${response.status}: body length ${response.body.length}`); - return json(response.body); + return response.body; } sleep(RETRY_INTERVAL); } @@ -58,18 +57,18 @@ function setOutput(data) { // Note: "fetch", "id", "eventId", etc. are script inputs, see for example assertEventIdIVisible.yml switch (fetch) { case 'event': { - const data = fetchFromSentry(`${baseUrl}/events/${id}/json/`); + const data = json(fetchFromSentry(`${baseUrl}/events/${id}/json/`)); setOutput({ eventId: data.event_id }); break; } case 'replay': { - const event = fetchFromSentry(`${baseUrl}/events/${eventId}/json/`); - const replayId = event._dsc.replay_id; - const replay = fetchFromSentry(`${baseUrl}/replays/${replayId}/`); + const event = json(fetchFromSentry(`${baseUrl}/events/${eventId}/json/`)); + const replayId = event._dsc.replay_id.replace(/\-/g, ''); + const replay = json(fetchFromSentry(`${baseUrl}/replays/${replayId}/`)); const segment = fetchFromSentry(`${baseUrl}/replays/${replayId}/videos/0/`); setOutput({ - replayId: replayId, + replayId: replay.data.id, replayDuration: replay.data.duration, replaySegments: replay.data.count_segments, replayCodec: segment.slice(4, 12) From 8fb5c3a22ccd256f86815f388eee52c3fb6866ff Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Fri, 15 Nov 2024 21:36:07 +0100 Subject: [PATCH 4/9] update maestro jsengine everywhere --- dev-packages/e2e-tests/maestro/captureMessage.yml | 1 + .../e2e-tests/maestro/captureUnhandledPromiseRejection.yml | 3 ++- dev-packages/e2e-tests/maestro/close.yml | 1 + dev-packages/e2e-tests/maestro/crash.yml | 1 + dev-packages/e2e-tests/maestro/utils/assertEventIdVisible.yml | 1 + dev-packages/e2e-tests/maestro/utils/assertReplay.yml | 1 + dev-packages/e2e-tests/maestro/utils/launchTestAppClear.yml | 1 + 7 files changed, 8 insertions(+), 1 deletion(-) diff --git a/dev-packages/e2e-tests/maestro/captureMessage.yml b/dev-packages/e2e-tests/maestro/captureMessage.yml index 40df1a88cd..9c109e95a4 100644 --- a/dev-packages/e2e-tests/maestro/captureMessage.yml +++ b/dev-packages/e2e-tests/maestro/captureMessage.yml @@ -1,4 +1,5 @@ appId: ${APP_ID} +jsEngine: graaljs --- - runFlow: utils/launchTestAppClear.yml - tapOn: "Capture Message" diff --git a/dev-packages/e2e-tests/maestro/captureUnhandledPromiseRejection.yml b/dev-packages/e2e-tests/maestro/captureUnhandledPromiseRejection.yml index 292f2593a6..dacf399454 100644 --- a/dev-packages/e2e-tests/maestro/captureUnhandledPromiseRejection.yml +++ b/dev-packages/e2e-tests/maestro/captureUnhandledPromiseRejection.yml @@ -1,5 +1,6 @@ appId: ${APP_ID} +jsEngine: graaljs --- - runFlow: utils/launchTestAppClear.yml -- tapOn: "Unhandled Promise Rejection" +- tapOn: 'Unhandled Promise Rejection' - runFlow: utils/assertEventIdVisible.yml diff --git a/dev-packages/e2e-tests/maestro/close.yml b/dev-packages/e2e-tests/maestro/close.yml index e233373426..582396af2e 100644 --- a/dev-packages/e2e-tests/maestro/close.yml +++ b/dev-packages/e2e-tests/maestro/close.yml @@ -1,4 +1,5 @@ appId: ${APP_ID} +jsEngine: graaljs --- - runFlow: utils/launchTestAppClear.yml - tapOn: "Close" diff --git a/dev-packages/e2e-tests/maestro/crash.yml b/dev-packages/e2e-tests/maestro/crash.yml index fb76f3529b..c9256949e7 100644 --- a/dev-packages/e2e-tests/maestro/crash.yml +++ b/dev-packages/e2e-tests/maestro/crash.yml @@ -1,4 +1,5 @@ appId: ${APP_ID} +jsEngine: graaljs --- - runFlow: utils/launchTestAppClear.yml - tapOn: "Crash" diff --git a/dev-packages/e2e-tests/maestro/utils/assertEventIdVisible.yml b/dev-packages/e2e-tests/maestro/utils/assertEventIdVisible.yml index 73883c46db..c30a66738a 100644 --- a/dev-packages/e2e-tests/maestro/utils/assertEventIdVisible.yml +++ b/dev-packages/e2e-tests/maestro/utils/assertEventIdVisible.yml @@ -1,4 +1,5 @@ appId: ${APP_ID} +jsEngine: graaljs --- - extendedWaitUntil: visible: diff --git a/dev-packages/e2e-tests/maestro/utils/assertReplay.yml b/dev-packages/e2e-tests/maestro/utils/assertReplay.yml index 60688d1779..ed6e44d1fd 100644 --- a/dev-packages/e2e-tests/maestro/utils/assertReplay.yml +++ b/dev-packages/e2e-tests/maestro/utils/assertReplay.yml @@ -1,4 +1,5 @@ appId: ${APP_ID} +jsEngine: graaljs --- - extendedWaitUntil: visible: diff --git a/dev-packages/e2e-tests/maestro/utils/launchTestAppClear.yml b/dev-packages/e2e-tests/maestro/utils/launchTestAppClear.yml index 3216e1ed89..b0c5badfa8 100644 --- a/dev-packages/e2e-tests/maestro/utils/launchTestAppClear.yml +++ b/dev-packages/e2e-tests/maestro/utils/launchTestAppClear.yml @@ -1,4 +1,5 @@ appId: ${APP_ID} +jsEngine: graaljs --- - launchApp: clearState: true From 00eaec76beefd958b11341bd489cfa14088fc927 Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Sun, 17 Nov 2024 13:58:33 +0100 Subject: [PATCH 5/9] kill app between test cases --- dev-packages/e2e-tests/maestro/utils/launchTestAppClear.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/dev-packages/e2e-tests/maestro/utils/launchTestAppClear.yml b/dev-packages/e2e-tests/maestro/utils/launchTestAppClear.yml index b0c5badfa8..85eff29767 100644 --- a/dev-packages/e2e-tests/maestro/utils/launchTestAppClear.yml +++ b/dev-packages/e2e-tests/maestro/utils/launchTestAppClear.yml @@ -1,11 +1,15 @@ appId: ${APP_ID} jsEngine: graaljs --- +# Ensure the app is killed, otherwise we may see "INTERNAL: UiAutomation not connected" errors on Android. +# They seem to be casued by a previous test case running for a long time without UI interactions (e.g. runScript). +- killApp + - launchApp: clearState: true arguments: sentryAuthToken: ${SENTRY_AUTH_TOKEN} - extendedWaitUntil: - visible: "E2E Tests Ready" + visible: 'E2E Tests Ready' timeout: 120_000 # 2 minutes From 425b974769ad76e92eb7c82e0c44a0786d70d1fa Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Sun, 17 Nov 2024 16:42:35 +0100 Subject: [PATCH 6/9] replay tests only on iOS --- dev-packages/e2e-tests/maestro/captureException.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/dev-packages/e2e-tests/maestro/captureException.yml b/dev-packages/e2e-tests/maestro/captureException.yml index 51b9c81cec..2dfa39a18e 100644 --- a/dev-packages/e2e-tests/maestro/captureException.yml +++ b/dev-packages/e2e-tests/maestro/captureException.yml @@ -4,4 +4,7 @@ jsEngine: graaljs - runFlow: utils/launchTestAppClear.yml - tapOn: 'Capture Exception' - runFlow: utils/assertEventIdVisible.yml -- runFlow: utils/assertReplay.yml +- runFlow: + file: utils/assertReplay.yml + when: + platform: iOS From 8038867a08a4e27222e48fc955dec556d9f57496 Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Tue, 19 Nov 2024 11:07:56 +0100 Subject: [PATCH 7/9] review suggestions --- dev-packages/e2e-tests/maestro/captureException.yml | 6 +----- dev-packages/e2e-tests/maestro/captureReplay.yml | 10 ++++++++++ .../maestro/captureUnhandledPromiseRejection.yml | 2 +- 3 files changed, 12 insertions(+), 6 deletions(-) create mode 100644 dev-packages/e2e-tests/maestro/captureReplay.yml diff --git a/dev-packages/e2e-tests/maestro/captureException.yml b/dev-packages/e2e-tests/maestro/captureException.yml index 2dfa39a18e..f746c6fc83 100644 --- a/dev-packages/e2e-tests/maestro/captureException.yml +++ b/dev-packages/e2e-tests/maestro/captureException.yml @@ -2,9 +2,5 @@ appId: ${APP_ID} jsEngine: graaljs --- - runFlow: utils/launchTestAppClear.yml -- tapOn: 'Capture Exception' +- tapOn: "Capture Exception" - runFlow: utils/assertEventIdVisible.yml -- runFlow: - file: utils/assertReplay.yml - when: - platform: iOS diff --git a/dev-packages/e2e-tests/maestro/captureReplay.yml b/dev-packages/e2e-tests/maestro/captureReplay.yml new file mode 100644 index 0000000000..53e1b72be1 --- /dev/null +++ b/dev-packages/e2e-tests/maestro/captureReplay.yml @@ -0,0 +1,10 @@ +appId: ${APP_ID} +jsEngine: graaljs +--- +- runFlow: utils/launchTestAppClear.yml +- tapOn: "Capture Exception" +- runFlow: utils/assertEventIdVisible.yml +- runFlow: + file: utils/assertReplay.yml + when: + platform: iOS diff --git a/dev-packages/e2e-tests/maestro/captureUnhandledPromiseRejection.yml b/dev-packages/e2e-tests/maestro/captureUnhandledPromiseRejection.yml index dacf399454..4f24921129 100644 --- a/dev-packages/e2e-tests/maestro/captureUnhandledPromiseRejection.yml +++ b/dev-packages/e2e-tests/maestro/captureUnhandledPromiseRejection.yml @@ -2,5 +2,5 @@ appId: ${APP_ID} jsEngine: graaljs --- - runFlow: utils/launchTestAppClear.yml -- tapOn: 'Unhandled Promise Rejection' +- tapOn: "Unhandled Promise Rejection" - runFlow: utils/assertEventIdVisible.yml From de8ac546d3c92a13ea49b74dd9b3f91f44fea865 Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Tue, 19 Nov 2024 11:08:39 +0100 Subject: [PATCH 8/9] quote style --- .../e2e-tests/maestro/utils/assertEventIdVisible.yml | 4 ++-- dev-packages/e2e-tests/maestro/utils/assertReplay.yml | 6 +++--- dev-packages/e2e-tests/maestro/utils/launchTestAppClear.yml | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/dev-packages/e2e-tests/maestro/utils/assertEventIdVisible.yml b/dev-packages/e2e-tests/maestro/utils/assertEventIdVisible.yml index c30a66738a..f31a206d66 100644 --- a/dev-packages/e2e-tests/maestro/utils/assertEventIdVisible.yml +++ b/dev-packages/e2e-tests/maestro/utils/assertEventIdVisible.yml @@ -3,11 +3,11 @@ jsEngine: graaljs --- - extendedWaitUntil: visible: - id: 'eventId' + id: "eventId" timeout: 600_000 # 10 minutes - copyTextFrom: - id: 'eventId' + id: "eventId" - assertTrue: ${maestro.copiedText} - runScript: diff --git a/dev-packages/e2e-tests/maestro/utils/assertReplay.yml b/dev-packages/e2e-tests/maestro/utils/assertReplay.yml index ed6e44d1fd..b9885b8227 100644 --- a/dev-packages/e2e-tests/maestro/utils/assertReplay.yml +++ b/dev-packages/e2e-tests/maestro/utils/assertReplay.yml @@ -3,11 +3,11 @@ jsEngine: graaljs --- - extendedWaitUntil: visible: - id: 'eventId' + id: "eventId" timeout: 60_000 # 60 seconds - copyTextFrom: - id: 'eventId' + id: "eventId" - assertTrue: ${maestro.copiedText} - runScript: @@ -20,4 +20,4 @@ jsEngine: graaljs - assertTrue: ${output.replayId} - assertTrue: ${output.replayDuration} - assertTrue: ${output.replaySegments} -- assertTrue: ${output.replayCodec == 'ftypmp42'} +- assertTrue: ${output.replayCodec == "ftypmp42"} diff --git a/dev-packages/e2e-tests/maestro/utils/launchTestAppClear.yml b/dev-packages/e2e-tests/maestro/utils/launchTestAppClear.yml index 85eff29767..6769ff47aa 100644 --- a/dev-packages/e2e-tests/maestro/utils/launchTestAppClear.yml +++ b/dev-packages/e2e-tests/maestro/utils/launchTestAppClear.yml @@ -11,5 +11,5 @@ jsEngine: graaljs sentryAuthToken: ${SENTRY_AUTH_TOKEN} - extendedWaitUntil: - visible: 'E2E Tests Ready' + visible: "E2E Tests Ready" timeout: 120_000 # 2 minutes From bd6dc14fd31058432bd8ec794d8b0e76ec0a7dc6 Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Tue, 19 Nov 2024 22:18:20 +0100 Subject: [PATCH 9/9] only capture replay in replay test --- dev-packages/e2e-tests/maestro/captureReplay.yml | 5 ++++- dev-packages/e2e-tests/maestro/utils/launchTestAppClear.yml | 1 + dev-packages/e2e-tests/patch-scripts/rn.patch.app.js | 3 ++- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/dev-packages/e2e-tests/maestro/captureReplay.yml b/dev-packages/e2e-tests/maestro/captureReplay.yml index 53e1b72be1..a7b65612b4 100644 --- a/dev-packages/e2e-tests/maestro/captureReplay.yml +++ b/dev-packages/e2e-tests/maestro/captureReplay.yml @@ -1,7 +1,10 @@ appId: ${APP_ID} jsEngine: graaljs --- -- runFlow: utils/launchTestAppClear.yml +- runFlow: + file: utils/launchTestAppClear.yml + env: + replaysOnErrorSampleRate: 1.0 - tapOn: "Capture Exception" - runFlow: utils/assertEventIdVisible.yml - runFlow: diff --git a/dev-packages/e2e-tests/maestro/utils/launchTestAppClear.yml b/dev-packages/e2e-tests/maestro/utils/launchTestAppClear.yml index 6769ff47aa..1ea1c765e7 100644 --- a/dev-packages/e2e-tests/maestro/utils/launchTestAppClear.yml +++ b/dev-packages/e2e-tests/maestro/utils/launchTestAppClear.yml @@ -9,6 +9,7 @@ jsEngine: graaljs clearState: true arguments: sentryAuthToken: ${SENTRY_AUTH_TOKEN} + replaysOnErrorSampleRate: ${replaysOnErrorSampleRate} - extendedWaitUntil: visible: "E2E Tests Ready" diff --git a/dev-packages/e2e-tests/patch-scripts/rn.patch.app.js b/dev-packages/e2e-tests/patch-scripts/rn.patch.app.js index a78c49847e..fcee381601 100755 --- a/dev-packages/e2e-tests/patch-scripts/rn.patch.app.js +++ b/dev-packages/e2e-tests/patch-scripts/rn.patch.app.js @@ -21,13 +21,14 @@ logger.info('Patching RN App.(js|tsx)', args.app); const initPatch = ` import * as Sentry from '@sentry/react-native'; import { EndToEndTestsScreen } from 'sentry-react-native-e2e-tests'; +import { LaunchArguments } from "react-native-launch-arguments"; Sentry.init({ release: '${SENTRY_RELEASE}', dist: '${SENTRY_DIST}', dsn: 'https://1df17bd4e543fdb31351dee1768bb679@o447951.ingest.sentry.io/5428561', _experiments: { - replaysOnErrorSampleRate: 1.0, + replaysOnErrorSampleRate: LaunchArguments.value().replaysOnErrorSampleRate, }, integrations: [ Sentry.mobileReplayIntegration(),