Skip to content

Commit 728c089

Browse files
authored
test: Shield E2E - Add more Claim scenarios (#38389)
<!-- Please submit this PR as a draft initially. Do not mark it as "Ready for review" until the template has been completely filled out, and PR status checks have passed at least once. --> ## **Description** This PR adds comprehensive E2E test coverage for Shield Claims functionality, including successful claim submission with file attachments and error handling scenarios. The changes include: 1. **Added E2E tests for Shield Claims**: - Successfully submit shield claim form with file attachment and verify claim details - Display error when transaction is not eligible (field error validation) - Display error toast when duplicate transaction hash is submitted 2. **Enhanced Page Object Model**: - Added `verifyFieldError()` method for inline field error verification - Added `verifyToastError()` method for toast error message verification - Added `uploadTestFile()` method for file upload functionality - Added `verifyFileUploaded()` method for file upload verification - Organized selectors alphabetically for better maintainability 3. **Improved Mock Service**: - Extended `ShieldMockttpService` to support error scenarios (E102: TRANSACTION_NOT_ELIGIBLE, E203: DUPLICATE_CLAIM_EXISTS) - Added `claimErrorCode` override parameter for flexible error testing 4. **UI Enhancement**: - Added `data-testid="shield-claim-impacted-tx-hash-error"` to transaction hash error field for better testability 5. **Test Data**: - Created reusable test PDF file (`test/e2e/test-data/test-document.pdf`) for file upload scenarios **Motivation**: These tests ensure critical Shield Claims functionality works correctly, including form validation, error handling, and file upload capabilities. This improves test coverage and helps prevent regressions in the claims submission flow. [![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/MetaMask/metamask-extension/pull/38389?quickstart=1) ## **Changelog** CHANGELOG entry: null *(This is a test-only change and does not affect end-user functionality)* ## **Related issues** Fixes: ## **Manual testing steps** 1. Navigate to Settings > Security & Privacy > Transaction Shield 2. Click "Submit Case" button 3. Fill in the claim form with valid data: - Email address - Reimbursement wallet address - Select impacted wallet (Account 1) - Select network (Mainnet) - Enter impacted transaction hash - Enter description - Upload a PDF file (test-document.pdf) 4. Click "Submit" button 5. Verify success toast message appears 6. Verify claim appears in claims list with "Created" status 7. Click on the claim to view details 8. Verify all claim data is displayed correctly including uploaded file **Error Scenarios Testing:** 1. **Transaction Not Eligible Error**: - Submit a claim with a transaction hash that is not eligible - Verify error message appears on the transaction hash field: "This transaction is not done within MetaMask, hence it is not eligible for claims" 2. **Duplicate Claim Error**: - Submit a claim with a transaction hash that already exists - Verify error toast appears: "A claim has already been submitted for this transaction hash." ## **Screenshots/Recordings** <!-- If applicable, add screenshots and/or recordings to visualize the before and after of your change. --> ### **Before** *No E2E tests existed for Shield Claims error scenarios* ### **After** *E2E tests now cover:* - ✅ Successful claim submission with file upload - ✅ Transaction not eligible error (field validation) - ✅ Duplicate transaction hash error (toast notification) - ✅ Claim details verification in view mode ## **Pre-merge author checklist** - [x] I've followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Extension Coding Standards](https://github.com/MetaMask/metamask-extension/blob/main/.github/guidelines/CODING_GUIDELINES.md). - [x] I've completed the PR template to the best of my ability - [x] I've included tests if applicable - Added 3 new E2E test cases for Shield Claims - Enhanced page object methods for error verification - Extended mock service for error scenario support - [x] I've documented my code using [JSDoc](https://jsdoc.app/) format if applicable - All new page object methods include JSDoc comments - Test methods are self-documenting with descriptive names - [x] I've applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-extension/blob/main/.github/guidelines/LABELING_GUIDELINES.md)). Not required for external contributors. - Suggested labels: `test`, `e2e`, `shield`, `enhancement` ## **Pre-merge reviewer checklist** - [ ] I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed). - [ ] I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots. <!-- CURSOR_SUMMARY --> --- > [!NOTE] > Adds E2E tests for Shield claim submission (with file upload) and error cases, updates page objects and mocks, and tags the impacted tx hash error for testing. > > - **E2E Tests (Shield Claims)**: > - Add scenarios: successful claim with file upload and detail verification; inline error for ineligible tx; toast error for duplicate tx. > - **Page Objects** (`test/e2e/page-objects/pages/settings/shield/shield-claim-page.ts`): > - New helpers: `uploadTestFile`, `verifyFileUploaded`, `verifyFieldError`, `verifyToastError`; extend `fillForm` and `verifyClaimData` to handle uploads. > - **Mocks** (`test/e2e/helpers/shield/mocks.ts`): > - Support submit-claim error injection via `claimErrorCode` (E102, E203, default); return attachment in success path; propagate overrides in setup. > - **Test Data**: Add `test/e2e/test-data/test-document.pdf`. > - **Constants** (`test/e2e/helpers/shield/constants.ts`): include mock attachment in `MOCK_CLAIM_2`. > - **UI** (`ui/pages/settings/transaction-shield-tab/claims-form/claims-form.tsx`): > - Add `data-testid="shield-claim-impacted-tx-hash-error"` to impacted tx hash error help text. > > <sup>Written by [Cursor Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit b3c8a41. This will update automatically on new commits. Configure [here](https://cursor.com/dashboard?tab=bugbot).</sup> <!-- /CURSOR_SUMMARY -->
1 parent e6d39aa commit 728c089

File tree

6 files changed

+297
-60
lines changed

6 files changed

+297
-60
lines changed

test/e2e/helpers/shield/constants.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -219,7 +219,13 @@ export const MOCK_CLAIM_2 = {
219219
'0x55da3eaee9bbefd762a33413b764ee2c025ff4a2cc0a49a05896ceb24c95712f',
220220
reimbursementWalletAddress: '0x88069b650422308bf8b472beaf790189f3f28309',
221221
description: 'I got scammed. Please help me get my money back. T_T @_@',
222-
attachments: [],
222+
attachments: [
223+
{
224+
originalname: 'test-document.pdf',
225+
publicUrl: 'https://mock-storage-url.com/claims/test-document.pdf',
226+
contentType: 'application/pdf',
227+
},
228+
],
223229
intercomId: `intercom_${MOCK_CLAIM_ID_2}`,
224230
status: 'created',
225231
};

test/e2e/helpers/shield/mocks.ts

Lines changed: 54 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ export class ShieldMockttpService {
4141
isActiveUser?: boolean;
4242
subscriptionId?: string;
4343
coverageStatus?: 'covered' | 'not_covered' | 'malicious';
44+
claimErrorCode?: string;
4445
},
4546
) {
4647
// Mock Identity Services first as shield/subscription APIs depend on it (Auth Token)
@@ -76,7 +77,7 @@ export class ShieldMockttpService {
7677
await this.#handleGetClaimsConfigurations(server);
7778
await this.#handleGetClaims(server);
7879
await this.#handleClaimGenerateMessage(server);
79-
await this.#handleSubmitClaim(server);
80+
await this.#handleSubmitClaim(server, overrides);
8081

8182
// Ruleset Engine APIs
8283
await this.#handleRulesetEngine(server);
@@ -310,8 +311,59 @@ export class ShieldMockttpService {
310311
.thenJson(200, MOCK_CLAIM_GENERATE_MESSAGE_RESPONSE);
311312
}
312313

313-
async #handleSubmitClaim(server: Mockttp) {
314+
async #handleSubmitClaim(
315+
server: Mockttp,
316+
overrides?: {
317+
claimErrorCode?: string;
318+
},
319+
) {
314320
await server.forPost(CLAIMS_API.CLAIMS).thenCallback(() => {
321+
// Return error response if error code is specified
322+
if (overrides?.claimErrorCode) {
323+
const errorCode = overrides.claimErrorCode;
324+
let errorResponse: {
325+
statusCode: number;
326+
json: {
327+
errorCode: string;
328+
message: string;
329+
};
330+
};
331+
332+
if (errorCode === 'E102') {
333+
// TRANSACTION_NOT_ELIGIBLE
334+
errorResponse = {
335+
statusCode: 400,
336+
json: {
337+
errorCode: 'E102',
338+
message:
339+
'This transaction is not done within MetaMask, hence it is not eligible for claims',
340+
},
341+
};
342+
} else if (errorCode === 'E203') {
343+
// DUPLICATE_CLAIM_EXISTS
344+
errorResponse = {
345+
statusCode: 400,
346+
json: {
347+
errorCode: 'E203',
348+
message:
349+
'A claim has already been submitted for this transaction hash.',
350+
},
351+
};
352+
} else {
353+
// Default error
354+
errorResponse = {
355+
statusCode: 400,
356+
json: {
357+
errorCode,
358+
message: 'Claim submission failed',
359+
},
360+
};
361+
}
362+
363+
return errorResponse;
364+
}
365+
366+
// Success response
315367
this.#newClaimSubmitted = true;
316368
return {
317369
statusCode: 200,

test/e2e/page-objects/pages/settings/shield/shield-claim-page.ts

Lines changed: 74 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
// ShieldClaimPage class for interacting with the Shield Claim form page
2+
import * as path from 'path';
23
import { Driver } from '../../../../webdriver/driver';
34

45
export default class ShieldClaimPage {
@@ -14,6 +15,8 @@ export default class ShieldClaimPage {
1415
text: accountName,
1516
});
1617

18+
private readonly claimErrorToast = '[data-testid="claim-submit-toast-error"]';
19+
1720
private readonly claimSuccessToast = {
1821
text: 'Claim submission received',
1922
tag: 'p',
@@ -25,13 +28,12 @@ export default class ShieldClaimPage {
2528
private readonly descriptionTextarea =
2629
'[data-testid="shield-claim-description-textarea"]';
2730

28-
private readonly emailHelpText = '[data-testid="shield-claim-help-text"]';
29-
3031
private readonly emailInput = '[data-testid="shield-claim-email-input"]';
3132

32-
private readonly fileUploader = '[data-testid="upload-images-file-uploader"]';
33+
private readonly fileUploaderInput = '[data-testid="file-uploader-input"]';
3334

34-
private readonly hereLink = 'a[href="#"]';
35+
private readonly impactedTxHashError =
36+
'[data-testid="shield-claim-impacted-tx-hash-error"]';
3537

3638
private readonly impactedTxHashInput =
3739
'[data-testid="shield-claim-impacted-tx-hash-input"]';
@@ -44,9 +46,6 @@ export default class ShieldClaimPage {
4446

4547
private readonly pageContainer = '[data-testid="submit-claim-page"]';
4648

47-
private readonly reimbursementWalletAddressHelpText =
48-
'[data-testid="shield-claim-reimbursement-wallet-address-help-text"]';
49-
5049
private readonly reimbursementWalletAddressInput =
5150
'[data-testid="shield-claim-reimbursement-wallet-address-input"]';
5251

@@ -126,6 +125,32 @@ export default class ShieldClaimPage {
126125
await this.driver.fill(this.descriptionTextarea, description);
127126
}
128127

128+
/**
129+
* Upload a test file to the claim form
130+
* Uses the test-document.pdf file from the test-data folder
131+
*/
132+
async uploadTestFile(): Promise<void> {
133+
console.log('Uploading test file to claim form');
134+
const testDataPath = path.resolve(
135+
process.cwd(),
136+
'test/e2e/test-data/test-document.pdf',
137+
);
138+
const inputField = await this.driver.findElement(this.fileUploaderInput);
139+
await inputField.sendKeys(testDataPath);
140+
}
141+
142+
/**
143+
* Verify that a file has been uploaded successfully
144+
*
145+
* @param fileName - The name of the file to verify
146+
*/
147+
async verifyFileUploaded(fileName: string): Promise<void> {
148+
console.log(`Verifying file uploaded: ${fileName}`);
149+
await this.driver.waitForSelector({
150+
text: fileName,
151+
});
152+
}
153+
129154
/**
130155
* Click the submit button
131156
*/
@@ -138,11 +163,38 @@ export default class ShieldClaimPage {
138163
await this.driver.waitForSelector(this.claimSuccessToast);
139164
}
140165

166+
/**
167+
* Verify inline field error message is displayed
168+
*
169+
* @param errorMessage - The error message text to verify
170+
*/
171+
async verifyFieldError(errorMessage: string): Promise<void> {
172+
console.log(`Verifying field error message: ${errorMessage}`);
173+
await this.driver.waitForSelector({
174+
css: this.impactedTxHashError,
175+
text: errorMessage,
176+
});
177+
}
178+
179+
/**
180+
* Verify error toast message is displayed
181+
*
182+
* @param errorMessage - The error message text to verify
183+
*/
184+
async verifyToastError(errorMessage: string): Promise<void> {
185+
console.log(`Verifying toast error message: ${errorMessage}`);
186+
await this.driver.waitForSelector({
187+
css: this.claimErrorToast,
188+
text: errorMessage,
189+
});
190+
}
191+
141192
async verifyClaimData(claimData: {
142193
email: string;
143194
reimbursementWalletAddress: string;
144195
impactedTxHash: string;
145196
description: string;
197+
uploadedFileName?: string;
146198
}): Promise<void> {
147199
console.log('Verifying claim data is displayed correctly');
148200

@@ -178,6 +230,14 @@ export default class ShieldClaimPage {
178230
});
179231
console.log(`Description verified: ${claimData.description}`);
180232

233+
// Verify uploaded file if provided
234+
if (claimData.uploadedFileName) {
235+
await this.driver.waitForSelector({
236+
text: claimData.uploadedFileName,
237+
});
238+
console.log(`Uploaded file verified: ${claimData.uploadedFileName}`);
239+
}
240+
181241
console.log('All claim data verified successfully');
182242
}
183243

@@ -191,6 +251,7 @@ export default class ShieldClaimPage {
191251
* @param formData.impactedTxnHash - The impacted transaction hash
192252
* @param formData.reimbursementWalletAddress - The reimbursement wallet address
193253
* @param formData.description - The case description
254+
* @param formData.uploadTestFile - Optional flag to upload and verify test file
194255
* @param formData.files - Optional array of file paths to upload
195256
*/
196257
async fillForm(formData: {
@@ -200,6 +261,7 @@ export default class ShieldClaimPage {
200261
chainId: string;
201262
impactedTxnHash: string;
202263
description: string;
264+
uploadTestFile?: boolean;
203265
files?: string[];
204266
}): Promise<void> {
205267
console.log('Filling entire claim form');
@@ -213,6 +275,11 @@ export default class ShieldClaimPage {
213275
await this.fillImpactedTransactionHash(formData.impactedTxnHash);
214276
await this.fillDescription(formData.description);
215277

278+
if (formData.uploadTestFile) {
279+
await this.uploadTestFile();
280+
await this.verifyFileUploaded('test-document.pdf');
281+
}
282+
216283
console.log('Claim form filled successfully');
217284
}
218285

250 Bytes
Binary file not shown.

0 commit comments

Comments
 (0)