-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
test: Government action proposal submissions
- Loading branch information
Showing
12 changed files
with
547 additions
and
17 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,7 +7,6 @@ node_modules/ | |
tests-out/ | ||
.auth/ | ||
.download/ | ||
lib/_mock/ | ||
allure-results/ | ||
allure-report/ | ||
.secrets | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
import { faker } from "@faker-js/faker"; | ||
|
||
export const invalid = { | ||
url: () => { | ||
const invalidSchemes = ["ftp", "unsupported", "unknown-scheme"]; | ||
const invalidCharacters = "<>@!#$%^&*()"; | ||
const invalidTlds = [".invalid", ".example", ".test"]; | ||
|
||
const scheme = | ||
invalidSchemes[Math.floor(Math.random() * invalidSchemes.length)]; | ||
const invalidChar = | ||
invalidCharacters[Math.floor(Math.random() * invalidCharacters.length)]; | ||
const invalidTld = | ||
invalidTlds[Math.floor(Math.random() * invalidTlds.length)]; | ||
|
||
const randomDomain = `example${invalidChar}domain${invalidTld}`; | ||
return `${scheme}://${randomDomain}`; | ||
}, | ||
|
||
proposalTitle: () => { | ||
const choice = faker.number.int({ min: 1, max: 2 }); | ||
if (choice === 1) { | ||
// maximum 80 words invalid | ||
return faker.lorem.paragraphs(4).replace(/\s+/g, ""); | ||
} | ||
// empty invalid | ||
return " "; | ||
}, | ||
|
||
paragraph: () => { | ||
const choice = faker.number.int({ min: 1, max: 2 }); | ||
if (choice === 1) { | ||
// maximum 500 words | ||
return faker.lorem.paragraphs(40); | ||
} | ||
// empty invalid | ||
return " "; | ||
}, | ||
|
||
amount: () => { | ||
const choice = faker.number.int({ min: 1, max: 2 }); | ||
if (choice === 1) { | ||
// only number is allowed | ||
return faker.lorem.word(); | ||
} | ||
// empty invalid | ||
return " "; | ||
}, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
71 changes: 71 additions & 0 deletions
71
tests/govtool-frontend/playwright/lib/helpers/proposalSubmission.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
import environments from "@constants/environments"; | ||
import { faker } from "@faker-js/faker"; | ||
import { invalid } from "@mock/index"; | ||
import ProposalSubmission from "@pages/proposalSubmissionPage"; | ||
import { IGovernanceProposal, ProposalType } from "@types"; | ||
import { ShelleyWallet } from "./crypto"; | ||
|
||
export function generateValidProposalFormField( | ||
proposalType: ProposalType, | ||
receivingAddress?: string | ||
) { | ||
const proposal: IGovernanceProposal = { | ||
title: faker.lorem.sentence(6), | ||
abstract: faker.lorem.paragraph(2), | ||
motivation: faker.lorem.paragraphs(2), | ||
rationale: faker.lorem.paragraphs(2), | ||
|
||
extraContentLinks: [faker.internet.url()], | ||
type: proposalType, | ||
}; | ||
if (proposalType === "Treasury") { | ||
(proposal.receivingAddress = receivingAddress), | ||
(proposal.amount = faker.number.int({ min: 100, max: 1000 }).toString()); | ||
} | ||
return proposal; | ||
} | ||
|
||
export function generateInValidProposalFormField(proposalType: ProposalType) { | ||
const proposal: IGovernanceProposal = { | ||
title: invalid.proposalTitle(), | ||
abstract: invalid.paragraph(), | ||
motivation: invalid.paragraph(), | ||
rationale: invalid.paragraph(), | ||
|
||
extraContentLinks: [invalid.url()], | ||
type: proposalType, | ||
}; | ||
if (proposalType === "Treasury") { | ||
(proposal.receivingAddress = faker.location.streetAddress()), | ||
(proposal.amount = invalid.amount()); | ||
} | ||
return proposal; | ||
} | ||
|
||
export async function submitInfoProposal( | ||
proposalSubmissionPage: ProposalSubmission, | ||
proposalType: ProposalType | ||
) { | ||
await proposalSubmissionPage.infoRadioButton.click(); | ||
await proposalSubmissionPage.continueBtn.click(); | ||
|
||
const infoProposal: IGovernanceProposal = | ||
generateValidProposalFormField(proposalType); | ||
await proposalSubmissionPage.register({ ...infoProposal }); | ||
} | ||
|
||
export async function submitTreasuryProposal( | ||
proposalSubmissionPage: ProposalSubmission, | ||
proposalType: ProposalType, | ||
wallet: ShelleyWallet | ||
) { | ||
await proposalSubmissionPage.treasuryRadioButton.click(); | ||
await proposalSubmissionPage.continueBtn.click(); | ||
|
||
const treasuryProposal: IGovernanceProposal = generateValidProposalFormField( | ||
proposalType, | ||
wallet.rewardAddressBech32(environments.networkId) | ||
); | ||
|
||
await proposalSubmissionPage.register({ ...treasuryProposal }); | ||
} |
207 changes: 207 additions & 0 deletions
207
tests/govtool-frontend/playwright/lib/pages/proposalSubmissionPage.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,207 @@ | ||
import { downloadMetadata } from "@helpers/metadata"; | ||
import { Download, Page, expect } from "@playwright/test"; | ||
import metadataBucketService from "@services/metadataBucketService"; | ||
import { IGovernanceProposal, ProposalType } from "@types"; | ||
import environments from "lib/constants/environments"; | ||
import { withTxConfirmation } from "lib/transaction.decorator"; | ||
const formErrors = { | ||
proposalTitle: ["max-80-characters-error", "this-field-is-required-error"], | ||
abstract: "this-field-is-required-error", | ||
motivation: "this-field-is-required-error", | ||
Rationale: "this-field-is-required-error", | ||
receivingAddress: "invalid-bech32-address-error", | ||
amount: ["only-number-is-allowed-error", "this-field-is-required-error"], | ||
link: "invalid-url-error", | ||
}; | ||
|
||
export default class ProposalSubmission { | ||
// modals | ||
readonly registrationSuccessModal = this.page.getByTestId( | ||
"create-governance-action-submitted-modal" | ||
); | ||
|
||
// buttons | ||
readonly registerBtn = this.page.getByTestId("register-button"); | ||
readonly skipBtn = this.page.getByTestId("skip-button"); | ||
readonly confirmBtn = this.page.getByTestId("confirm-modal-button"); | ||
|
||
readonly continueBtn = this.page.getByTestId("continue-button"); | ||
readonly addLinkBtn = this.page.getByRole("button", { name: "+ Add link" }); // BUG testid= add-link-button | ||
readonly infoRadioButton = this.page.getByTestId("Info-radio"); | ||
readonly treasuryRadioButton = this.page.getByTestId("Treasury-radio"); | ||
|
||
// input fields | ||
readonly titleInput = this.page.getByPlaceholder("A name for this Action"); // BUG testid = title-input | ||
readonly abstractInput = this.page.getByPlaceholder("Summary"); // BUG testid = abstract-input | ||
readonly motivationInput = this.page.getByPlaceholder( | ||
"Problem this GA will solve" | ||
); // BUG testid = motivation-input | ||
readonly rationaleInput = this.page.getByPlaceholder( | ||
"Content of Governance Action" | ||
); // BUG testid = rationale-input | ||
readonly linkInput = this.page.getByPlaceholder("https://website.com/"); // BUG testid = link-input | ||
readonly receivingAddressInput = this.page.getByPlaceholder( | ||
"The address to receive funds" | ||
); | ||
readonly amountInput = this.page.getByPlaceholder("e.g."); | ||
|
||
constructor(private readonly page: Page) {} | ||
|
||
async goto() { | ||
await this.page.goto( | ||
`${environments.frontendUrl}/create_governance_action` | ||
); | ||
await this.continueBtn.click(); | ||
} | ||
|
||
@withTxConfirmation | ||
async register(governanceProposal: IGovernanceProposal) { | ||
await this.fillupForm(governanceProposal); | ||
|
||
await this.continueBtn.click(); | ||
await this.continueBtn.click(); | ||
await this.page.getByRole("checkbox").click(); | ||
await this.continueBtn.click(); | ||
|
||
this.page | ||
.getByRole("button", { name: `${governanceProposal.type}.jsonld` }) | ||
.click(); // BUG test id = metadata-download-button | ||
|
||
const dRepMetadata = await this.downloadVoteMetadata(); | ||
const url = await metadataBucketService.uploadMetadata( | ||
dRepMetadata.name, | ||
dRepMetadata.data | ||
); | ||
await this.page.getByPlaceholder("URL").fill(url); | ||
await this.continueBtn.click(); | ||
} | ||
|
||
async downloadVoteMetadata() { | ||
const download: Download = await this.page.waitForEvent("download"); | ||
return downloadMetadata(download); | ||
} | ||
|
||
async fillupForm(governanceProposal: IGovernanceProposal) { | ||
await this.titleInput.fill(governanceProposal.title); | ||
await this.abstractInput.fill(governanceProposal.abstract); | ||
await this.motivationInput.fill(governanceProposal.motivation); | ||
await this.rationaleInput.fill(governanceProposal.rationale); | ||
|
||
if (governanceProposal.type === "Treasury") { | ||
await this.receivingAddressInput.fill( | ||
governanceProposal.receivingAddress | ||
); | ||
await this.amountInput.fill(governanceProposal.amount); | ||
} | ||
|
||
if (governanceProposal.extraContentLinks != null) { | ||
for (let i = 0; i < governanceProposal.extraContentLinks.length; i++) { | ||
if (i > 0) { | ||
this.page | ||
.getByRole("button", { | ||
name: "+ Add link", | ||
}) | ||
.click(); | ||
} | ||
await this.linkInput | ||
.nth(i) | ||
.fill(governanceProposal.extraContentLinks[i]); | ||
} | ||
} | ||
} | ||
|
||
async validateForm(governanceProposal: IGovernanceProposal) { | ||
await this.fillupForm(governanceProposal); | ||
|
||
for (const err of formErrors.proposalTitle) { | ||
await expect( | ||
this.page.getByTestId(err), | ||
`Invalid title: ${governanceProposal.title}` | ||
).toBeHidden(); | ||
} | ||
|
||
expect(await this.abstractInput.textContent()).toEqual( | ||
governanceProposal.abstract | ||
); | ||
|
||
expect(await this.rationaleInput.textContent()).toEqual( | ||
governanceProposal.rationale | ||
); | ||
|
||
expect(await this.motivationInput.textContent()).toEqual( | ||
governanceProposal.motivation | ||
); | ||
|
||
if (governanceProposal.type === "Treasury") { | ||
await expect( | ||
this.page.getByTestId(formErrors.receivingAddress) | ||
).toBeHidden(); | ||
|
||
for (const err of formErrors.amount) { | ||
await expect(this.page.getByTestId(err)).toBeHidden(); | ||
} | ||
} | ||
|
||
await expect(this.page.getByTestId(formErrors.link)).toBeHidden(); | ||
|
||
await expect(this.continueBtn).toBeEnabled(); | ||
} | ||
|
||
async inValidateForm(governanceProposal: IGovernanceProposal) { | ||
await this.fillupForm(governanceProposal); | ||
|
||
function convertTestIdToText(testId: string) { | ||
let text = testId.replace("-error", ""); | ||
text = text.replace(/-/g, " "); | ||
return text[0].toUpperCase() + text.substring(1); | ||
} | ||
|
||
// Helper function to generate regex pattern from form errors | ||
function generateRegexPattern(errors: string[]) { | ||
return new RegExp(errors.map(convertTestIdToText).join("|")); | ||
} | ||
|
||
// Helper function to get errors based on regex pattern | ||
async function getErrorsByPattern(page: Page, regexPattern: RegExp) { | ||
return await page | ||
.locator('[data-testid$="-error"]') | ||
.filter({ hasText: regexPattern }) | ||
.all(); | ||
} | ||
|
||
const proposalTitlePattern = generateRegexPattern(formErrors.proposalTitle); | ||
const proposalTitleErrors = await getErrorsByPattern( | ||
this.page, | ||
proposalTitlePattern | ||
); | ||
expect(proposalTitleErrors.length).toEqual(1); | ||
|
||
if (governanceProposal.type === "Treasury") { | ||
const receiverAddressErrors = await getErrorsByPattern( | ||
this.page, | ||
new RegExp(convertTestIdToText(formErrors.receivingAddress)) | ||
); | ||
expect(receiverAddressErrors.length).toEqual(1); | ||
|
||
const amountPattern = generateRegexPattern(formErrors.amount); | ||
const amountErrors = await getErrorsByPattern(this.page, amountPattern); | ||
expect(amountErrors.length).toEqual(1); | ||
} | ||
|
||
expect(await this.abstractInput.textContent()).not.toEqual( | ||
governanceProposal.abstract | ||
); | ||
|
||
expect(await this.motivationInput.textContent()).not.toEqual( | ||
governanceProposal.motivation | ||
); | ||
|
||
expect(await this.rationaleInput.textContent()).not.toEqual( | ||
governanceProposal.rationale | ||
); | ||
|
||
await expect(this.page.getByTestId(formErrors.link)).toBeVisible(); | ||
|
||
await expect(this.continueBtn).toBeDisabled(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.