Skip to content

Commit

Permalink
feat(1852): add eventId to sentry feedback report
Browse files Browse the repository at this point in the history
  • Loading branch information
DDDDDanica committed Nov 7, 2024
1 parent 7f706fc commit 32ea0a0
Show file tree
Hide file tree
Showing 9 changed files with 275 additions and 81 deletions.
24 changes: 0 additions & 24 deletions app/_locales/en/messages.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion app/scripts/lib/setupSentry.js
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ function getTracesSampleRate(sentryTarget) {
return 1.0;
}

return 0.02;
return 1.0;
}

/**
Expand Down
5 changes: 4 additions & 1 deletion test/e2e/helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ const convertETHToHexGwei = (eth) => convertToHexValue(eth * 10 ** 18);
/**
*
* @param {object} options
* @param {(fixtures: Fixtures) => Promise<void>} testSuite
* @param {({driver: Driver, mockedEndpoint: MockedEndpoint}: TestSuiteArguments) => Promise<void>} testSuite
*/
async function withFixtures(options, testSuite) {
const {
Expand Down Expand Up @@ -1305,6 +1305,8 @@ async function openMenuSafe(driver) {
}
}

const sentryRegEx = /^https:\/\/sentry\.io\/api\/\d+\/envelope/gu;

module.exports = {
DAPP_HOST_ADDRESS,
DAPP_URL,
Expand Down Expand Up @@ -1376,4 +1378,5 @@ module.exports = {
getSelectedAccountAddress,
tempToggleSettingRedesignedConfirmations,
openMenuSafe,
sentryRegEx,
};
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
import { strict as assert } from 'assert';
import { MockedEndpoint } from 'mockttp';
import { Driver } from '../../webdriver/driver';
import HeaderNavbar from './header-navbar';
import SettingsPage from './settings-page';
import DevelopOptionsPage from './developer-options-page';
import { getEventPayloads } from '../../helpers';

const WAIT_FOR_SENTRY_MS = 5000;
const FEEDBACK_MESSAGE =
'Message: Unable to find value of key "developerOptions" for locale "en"';

class ErrorPage {
private readonly driver: Driver;
Expand All @@ -17,11 +24,21 @@ class ErrorPage {
private readonly sendReportToSentryButton =
'[data-testid="error-page-describe-what-happened-button"]';

private readonly sentryReportForm = '#sentry-feedback';
private readonly sentryReportForm =
'[data-testid="error-page-sentry-feedback-modal"]';

private readonly contactSupportButton =
'[data-testid="error-page-contact-support-button"]';

private readonly sentryFeedbackTextarea =
'[data-testid="error-page-sentry-feedback-textarea"]';

private readonly sentryFeedbackSubmitButton =
'[data-testid="error-page-sentry-feedback-submit-button"]';

private readonly sentryFeedbackSuccessModal =
'[data-testid="error-page-sentry-feedback-success-modal"]';

constructor(driver: Driver) {
this.driver = driver;
}
Expand Down Expand Up @@ -59,6 +76,10 @@ class ErrorPage {
console.log(`Open sentry user feedback form in error page`);
await this.driver.clickElement(this.sendReportToSentryButton);
await this.driver.waitForSelector(this.sentryReportForm);
await this.driver.fill(this.sentryFeedbackTextarea, FEEDBACK_MESSAGE);
await this.driver.clickElementAndWaitToDisappear(
this.sentryFeedbackSubmitButton,
);
}

async contactAndValidateMetaMaskSupport(): Promise<void> {
Expand All @@ -68,6 +89,11 @@ class ErrorPage {
// metamask, help page
await this.driver.waitUntilXWindowHandles(2);
}

async waitForSentrySuccessModal(): Promise<void> {
await this.driver.waitForSelector(this.sentryFeedbackSuccessModal);
await this.driver.assertElementNotPresent(this.sentryFeedbackSuccessModal);
}
}

export default ErrorPage;
46 changes: 40 additions & 6 deletions test/e2e/tests/metrics/developer-options-sentry.spec.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import { Suite } from 'mocha';
import { withFixtures } from '../../helpers';
import { MockedEndpoint, MockttpServer } from 'mockttp';
import { withFixtures, sentryRegEx } from '../../helpers';
import FixtureBuilder from '../../fixture-builder';
import { Driver } from '../../webdriver/driver';
import { loginWithBalanceValidation } from '../../page-objects/flows/login.flow';
import SettingsPage from '../../page-objects/pages/settings-page';
import HeaderNavbar from '../../page-objects/pages/header-navbar';
import DevelopOptions from '../../page-objects/pages/developer-options-page';
import ErrorPage from '../../page-objects/pages/ErrorPage';
import ErrorPage from '../../page-objects/pages/error-page';

const triggerCrash = async (driver: Driver): Promise<void> => {
const headerNavbar = new HeaderNavbar(driver);
Expand All @@ -20,21 +21,51 @@ const triggerCrash = async (driver: Driver): Promise<void> => {
await developOptionsPage.clickGenerateCrashButton();
};

async function mockSentryError(mockServer: MockttpServer) {
return [
await mockServer
.forPost(sentryRegEx)
.withBodyIncluding('feedback')
.thenCallback(() => {
return {
statusCode: 200,
json: {},
};
}),
];
}

describe('Developer Options - Sentry', function (this: Suite) {
it('gives option to cause a page crash and provides sentry form to report', async function () {
await withFixtures(
{
fixtures: new FixtureBuilder().build(),
fixtures: new FixtureBuilder()
.withMetaMetricsController({
metaMetricsId: 'fake-metrics-id',
participateInMetaMetrics: true,
})
.build(),
title: this.test?.fullTitle(),
ignoredConsoleErrors: ['ignore-all'],
testSpecificMock: mockSentryError,
ignoredConsoleErrors: [
'Error#1: Unable to find value of key "developerOptions" for locale "en"',
'React will try to recreate this component tree from scratch using the error boundary you provided, Index.',
],
},
async ({ driver }: { driver: Driver }) => {
async ({
driver,
mockedEndpoint: mockedEndpoints,
}: {
driver: Driver;
mockedEndpoint: MockedEndpoint[];
}) => {
await loginWithBalanceValidation(driver);
await triggerCrash(driver);
const errorPage = new ErrorPage(driver);
await errorPage.check_pageIsLoaded();
await errorPage.validate_errorMessage();
await errorPage.submitToSentryUserFeedbackForm();
await errorPage.waitForSentrySuccessModal();
},
);
});
Expand All @@ -44,7 +75,10 @@ describe('Developer Options - Sentry', function (this: Suite) {
{
fixtures: new FixtureBuilder().build(),
title: this.test?.fullTitle(),
ignoredConsoleErrors: ['ignore-all'],
ignoredConsoleErrors: [
'Error#1: Unable to find value of key "developerOptions" for locale "en"',
'React will try to recreate this component tree from scratch using the error boundary you provided, Index.',
],
},
async ({ driver }: { driver: Driver }) => {
await loginWithBalanceValidation(driver);
Expand Down
3 changes: 1 addition & 2 deletions test/e2e/tests/metrics/errors.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ const {
convertToHexValue,
logInWithBalanceValidation,
withFixtures,
sentryRegEx,
} = require('../../helpers');
const { PAGES } = require('../../webdriver/driver');

Expand Down Expand Up @@ -181,8 +182,6 @@ function getMissingProperties(complete, object) {
}

describe('Sentry errors', function () {
const sentryRegEx = /^https:\/\/sentry\.io\/api\/\d+\/envelope/gu;

const migrationError =
process.env.SELENIUM_BROWSER === Browser.CHROME
? `"type":"TypeError","value":"Cannot read properties of undefined (reading 'version')`
Expand Down
70 changes: 65 additions & 5 deletions ui/pages/error-page/error-component.test.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import React from 'react';
import { useSelector } from 'react-redux';
import '@testing-library/jest-dom/extend-expect';
import browser from 'webextension-polyfill';
import { fireEvent } from '@testing-library/react';
import { renderWithProvider } from '../../../test/lib/render-helpers';
import { useI18nContext } from '../../hooks/useI18nContext';
import { MetaMetricsContext } from '../../contexts/metametrics';

import { getParticipateInMetaMetrics } from '../../selectors';
import { getMessage } from '../../helpers/utils/i18n-helper';
// eslint-disable-next-line import/no-restricted-paths
import messages from '../../../app/_locales/en/messages.json';
Expand All @@ -27,7 +28,13 @@ jest.mock('webextension-polyfill', () => ({
},
}));

jest.mock('react-redux', () => ({
...jest.requireActual('react-redux'),
useSelector: jest.fn(),
}));

describe('ErrorPage', () => {
const useSelectorMock = useSelector as jest.Mock;
const mockTrackEvent = jest.fn();
const MockError = new Error(
"Cannot read properties of undefined (reading 'message')",
Expand All @@ -41,8 +48,18 @@ describe('ErrorPage', () => {
);

beforeEach(() => {
jest.clearAllMocks();
useSelectorMock.mockImplementation((selector) => {
if (selector === getParticipateInMetaMetrics) {
return true;
}
return undefined;
});
(useI18nContext as jest.Mock).mockImplementation(mockI18nContext);
jest.useFakeTimers();
});

afterEach(() => {
jest.useRealTimers();
});

it('should render the error message, code, and name if provided', () => {
Expand Down Expand Up @@ -80,13 +97,56 @@ describe('ErrorPage', () => {
expect(queryByTestId('error-page-error-stack')).toBeNull();
});

it('should render sentry user feedback form', () => {
const { container } = renderWithProvider(
it('should render sentry user feedback form and submit sentry report successfully when metrics is opted in', () => {
const { getByTestId, queryByTestId } = renderWithProvider(
<MetaMetricsContext.Provider value={mockTrackEvent}>
<ErrorPage error={MockError} />
</MetaMetricsContext.Provider>,
);
const describeButton = getByTestId(
'error-page-describe-what-happened-button',
);
fireEvent.click(describeButton);
expect(
queryByTestId('error-page-sentry-feedback-modal'),
).toBeInTheDocument();
const textarea = getByTestId('error-page-sentry-feedback-textarea');
fireEvent.change(textarea, {
target: { value: 'Something went wrong on develop option page' },
});
const submitButton = getByTestId(
'error-page-sentry-feedback-submit-button',
);
fireEvent.click(submitButton);
expect(
queryByTestId('error-page-sentry-feedback-modal'),
).not.toBeInTheDocument();
expect(
queryByTestId('error-page-sentry-feedback-success-modal'),
).toBeInTheDocument();
jest.advanceTimersByTime(5000);
expect(
queryByTestId('error-page-sentry-feedback-modal'),
).not.toBeInTheDocument();
});

it('should render not sentry user feedback option when metrics is not opted in', () => {
useSelectorMock.mockImplementation((selector) => {
if (selector === getParticipateInMetaMetrics) {
return false;
}
return undefined;
});
const { queryByTestId } = renderWithProvider(
<MetaMetricsContext.Provider value={mockTrackEvent}>
<ErrorPage error={MockError} />
</MetaMetricsContext.Provider>,
);
expect(container.querySelector('#sentry-feedback')).toBeDefined();
const describeButton = queryByTestId(
'error-page-describe-what-happened-button',
);

expect(describeButton).toBeNull();
});

it('should reload the extension when the "Try Again" button is clicked', () => {
Expand Down
Loading

0 comments on commit 32ea0a0

Please sign in to comment.