From cbf51bf16691b2e0ea8ef49a6e381198869aa29f Mon Sep 17 00:00:00 2001 From: Niraj Date: Wed, 16 Apr 2025 16:52:42 +0545 Subject: [PATCH 01/19] tests: budget proposal costing and further information validation --- .../pages/budgetDiscussionSubmissionPage.ts | 103 +++++++++++++++++- .../proposalBudgetSubmission.loggedin.spec.ts | 101 +++++++++++++---- 2 files changed, 179 insertions(+), 25 deletions(-) diff --git a/tests/govtool-frontend/playwright/lib/pages/budgetDiscussionSubmissionPage.ts b/tests/govtool-frontend/playwright/lib/pages/budgetDiscussionSubmissionPage.ts index 3376c2b7c..5844a7dd5 100644 --- a/tests/govtool-frontend/playwright/lib/pages/budgetDiscussionSubmissionPage.ts +++ b/tests/govtool-frontend/playwright/lib/pages/budgetDiscussionSubmissionPage.ts @@ -1,6 +1,7 @@ import environments from "@constants/environments"; import { faker } from "@faker-js/faker"; import { extractProposalIdFromUrl } from "@helpers/string"; +import { invalid, valid } from "@mock/index"; import { Page, expect } from "@playwright/test"; import { AdministrationAndAuditingProps, @@ -27,7 +28,9 @@ const formErrors = { motivation: "motivation-helper-error", rationale: "rationale-helper-error", receivingAddress: "receiving-address-0-text-error", - amount: "amount-0-text-error", + adaAmount: "ada-amount-error", + usdToAdaConversionError: "usd-to-ada-converson-error", + preferredCurrencyError: "preferred-currency-error", constitutionalUrl: "prop-constitution-url-text-error", guardrailsScriptUrl: "prop-guardrails-script-url-input-error", link: "link-0-url-input-error", @@ -131,7 +134,7 @@ export default class BudgetDiscussionSubmissionPage { readonly usaToAdaCnversionRateInput = this.page.getByLabel( "USD to ADA Conversion Rate *" ); //BUG missing test Ids - readonly preferredCurrencyAmountInput = this.page.getByLabel( + readonly preferredCurrencyInput = this.page.getByLabel( "Amount in preferred currency *" ); readonly costBreakdownInput = this.page.getByTestId("cost-breakdown-input"); @@ -418,7 +421,7 @@ export default class BudgetDiscussionSubmissionPage { await this.page .getByTestId(`${costing.preferredCurrency.toLowerCase()}-button`) .click(); - await this.preferredCurrencyAmountInput.fill( + await this.preferredCurrencyInput.fill( costing.AmountInPreferredCurrency.toString() ); await this.costBreakdownInput.fill(costing.costBreakdown); @@ -441,6 +444,18 @@ export default class BudgetDiscussionSubmissionPage { await this.continueBtn.click(); } + async fillCostingSectionExceptAmountInputs() { + const costing = this.generateValidCosting(); + await this.preferredCurrencySelect.click(); + await this.page + .getByTestId(`${costing.preferredCurrency.toLowerCase()}-button`) + .click(); + await this.preferredCurrencyInput.fill( + costing.AmountInPreferredCurrency.toString() + ); + await this.costBreakdownInput.fill(costing.costBreakdown.toString()); + } + async fillupForm( budgetProposal: BudgetProposalProps, stage: BudgetProposalStageEnum = BudgetProposalStageEnum.AdministrationAndAuditing @@ -790,4 +805,86 @@ export default class BudgetDiscussionSubmissionPage { : "No" ); } + + async validateCostingSection(isValid: boolean = true) { + const adaAmount = isValid + ? faker.number.int({ min: 100, max: 10000 }) + : faker.lorem.paragraph(2); + const usdToAdaCnversionRate = isValid + ? faker.number.int({ min: 1, max: 100 }) + : faker.lorem.paragraph(2); + const preferredCurrency = isValid + ? faker.number.int({ min: 100, max: 10000 }) + : faker.lorem.paragraph(2); + await this.adaAmountInput.fill(adaAmount.toString()); + await this.usaToAdaCnversionRateInput.fill( + usdToAdaCnversionRate.toString() + ); + await this.preferredCurrencyInput.fill(preferredCurrency.toString()); + const adaErrorElement = this.page.getByTestId(formErrors.adaAmount); + const usdToAdaConversionRateErrorElement = this.page.getByTestId( + formErrors.usdToAdaConversionError + ); + const preferredCurrencyErrorElement = this.page.getByTestId( + formErrors.preferredCurrencyError + ); + const isAdaAmountErrorVisible = await adaErrorElement.isVisible(); + const isUsdToAdaConversionRateErrorVisible = + await usdToAdaConversionRateErrorElement.isVisible(); + const isPreferredCurrencyErrorVisible = + await preferredCurrencyErrorElement.isVisible(); + + if (isValid) { + await expect(adaErrorElement, { + message: isAdaAmountErrorVisible && `${adaAmount} is an invalid amount`, + }).toBeHidden(); + await expect(usdToAdaConversionRateErrorElement, { + message: + isUsdToAdaConversionRateErrorVisible && + `${usdToAdaCnversionRate} is an invalid amount`, + }).toBeHidden(); + await expect(preferredCurrencyErrorElement, { + message: + isPreferredCurrencyErrorVisible && + `${preferredCurrency} is an invalid amount`, + }).toBeHidden(); + } else { + await expect(adaErrorElement, { + message: !isAdaAmountErrorVisible && `${adaAmount} is a valid amount`, + }).toBeVisible(); + await expect(usdToAdaConversionRateErrorElement, { + message: + !isUsdToAdaConversionRateErrorVisible && + `${usdToAdaCnversionRate} is a valid amount`, + }).toBeVisible(); + await expect(preferredCurrencyErrorElement, { + message: + !isPreferredCurrencyErrorVisible && + `${preferredCurrency} is a valid amount`, + }).toBeVisible(); + } + } + + async validateFurtherInformationSection(isValid: boolean = true) { + const url = isValid ? valid.url() : invalid.url(); + const linkText = isValid + ? faker.internet.displayName() + : faker.lorem.paragraph(40); + + await this.linkTextInput.fill(linkText); + await this.linkUrlInput.fill(url); + + const errorElement = this.page.getByTestId(formErrors.link); + const isErrorVisible = await errorElement.isVisible(); + + if (isValid) { + await expect(errorElement, { + message: isErrorVisible && `${url} is an invalid URL`, + }).toBeHidden(); + } else { + await expect(errorElement, { + message: !isErrorVisible && `${url} is a valid URL`, + }).toBeVisible(); + } + } } diff --git a/tests/govtool-frontend/playwright/tests/12-proposal-budget-submission/proposalBudgetSubmission.loggedin.spec.ts b/tests/govtool-frontend/playwright/tests/12-proposal-budget-submission/proposalBudgetSubmission.loggedin.spec.ts index 698106567..a7366cbc6 100644 --- a/tests/govtool-frontend/playwright/tests/12-proposal-budget-submission/proposalBudgetSubmission.loggedin.spec.ts +++ b/tests/govtool-frontend/playwright/tests/12-proposal-budget-submission/proposalBudgetSubmission.loggedin.spec.ts @@ -84,6 +84,8 @@ test.describe("Budget proposal 01 wallet", () => { await expect( budgetProposalSubmissionPage.keyInformationOfGroupInput ).toBeVisible(); + + await expect(budgetProposalSubmissionPage.continueBtn).toBeDisabled(); }); test("12D_2. Should verify all field of “problem statements and proposal benefits” section", async () => { @@ -111,6 +113,7 @@ test.describe("Budget proposal 01 wallet", () => { await expect( budgetProposalSubmissionPage.suplimentaryEndorsementInput ).toBeVisible(); + await expect(budgetProposalSubmissionPage.continueBtn).toBeDisabled(); }); test("12D_3. Should verify all field of “proposal details” section", async () => { @@ -142,6 +145,7 @@ test.describe("Budget proposal 01 wallet", () => { await expect( budgetProposalSubmissionPage.contractingTypeSelect ).toBeVisible(); + await expect(budgetProposalSubmissionPage.continueBtn).toBeDisabled(); }); test("12D_4. Should verify all field of “costing” section", async () => { @@ -160,11 +164,12 @@ test.describe("Budget proposal 01 wallet", () => { budgetProposalSubmissionPage.preferredCurrencySelect ).toBeVisible(); await expect( - budgetProposalSubmissionPage.preferredCurrencyAmountInput + budgetProposalSubmissionPage.preferredCurrencyInput ).toBeVisible(); await expect( budgetProposalSubmissionPage.costBreakdownInput ).toBeVisible(); + await expect(budgetProposalSubmissionPage.continueBtn).toBeDisabled(); }); test("12D_5. Should verify all field of “further information” section", async () => { @@ -191,6 +196,7 @@ test.describe("Budget proposal 01 wallet", () => { await expect( budgetProposalSubmissionPage.intersectNamedAdministratorSelect ).toBeVisible(); + await expect(budgetProposalSubmissionPage.continueBtn).toBeDisabled(); }); }); @@ -203,29 +209,80 @@ test.describe("Budget proposal 01 wallet", () => { proposalInformations ); }); - }); -}); + test.describe("Budget proposal field validation", () => { + test("12E_1. Should accept valid data in “Costing” section", async () => { + test.slow(); // Brute-force testing with 50 random data + const proposalInformation = + budgetProposalSubmissionPage.generateValidBudgetProposalInformation(); + await budgetProposalSubmissionPage.fillupForm( + proposalInformation, + BudgetProposalStageEnum.ProposalDetails + ); -test.describe("Budget proposal field validation", () => { - test.beforeEach(async () => { - await allure.description( - "Field validation tests are pending implementation." - ); - test.skip(); + for (let i = 0; i < 50; i++) { + await budgetProposalSubmissionPage.validateCostingSection(); + } + + await budgetProposalSubmissionPage.fillCostingSectionExceptAmountInputs(); + await expect(budgetProposalSubmissionPage.continueBtn).toBeEnabled(); + }); + + test("12E_2. Should accept valid data in “further information” section", async () => { + test.slow(); // Brute-force testing with 50 random data + const proposalInformation = + budgetProposalSubmissionPage.generateValidBudgetProposalInformation(); + await budgetProposalSubmissionPage.fillupForm( + proposalInformation, + BudgetProposalStageEnum.Costing + ); + + for (let i = 0; i < 50; i++) { + await budgetProposalSubmissionPage.validateFurtherInformationSection(); + } + + for (let i = 0; i < 18; i++) { + await expect(budgetProposalSubmissionPage.addLinkBtn).toBeVisible(); + await budgetProposalSubmissionPage.addLinkBtn.click(); + } + await expect(budgetProposalSubmissionPage.addLinkBtn).toBeHidden(); + await expect(budgetProposalSubmissionPage.continueBtn).toBeEnabled(); + }); + + test("12F_1. Should reject valid data in “Costing” section", async () => { + test.slow(); // Brute-force testing with 50 random data + const proposalInformation = + budgetProposalSubmissionPage.generateValidBudgetProposalInformation(); + await budgetProposalSubmissionPage.fillupForm( + proposalInformation, + BudgetProposalStageEnum.ProposalDetails + ); + + for (let i = 0; i < 50; i++) { + await budgetProposalSubmissionPage.validateCostingSection(false); + } + + await budgetProposalSubmissionPage.fillCostingSectionExceptAmountInputs(); + await expect(budgetProposalSubmissionPage.continueBtn).toBeDisabled(); + }); + + test("12F_2. Should reject invalid data in “further information” section", async () => { + test.slow(); // Brute-force testing with 50 random data + const proposalInformation = + budgetProposalSubmissionPage.generateValidBudgetProposalInformation(); + await budgetProposalSubmissionPage.fillupForm( + proposalInformation, + BudgetProposalStageEnum.Costing + ); + + for (let i = 0; i < 50; i++) { + await budgetProposalSubmissionPage.validateFurtherInformationSection( + false + ); + } + await expect(budgetProposalSubmissionPage.continueBtn).toBeDisabled(); + }); + }); }); - test("12E_1. Should accept valid data in “contact information” section", async ({}) => {}); - test("12E_2. Should accept valid data in “proposal ownership” section", async ({}) => {}); - test("12E_3. Should accept valid data in “problem statements and proposal benefits” section", async ({}) => {}); - test("12E_4. Should accept valid data in “proposal details” section", async ({}) => {}); - test("12E_5. Should accept valid data in “costing” section", async ({}) => {}); - test("12E_6. Should accept valid data in “further information” section", async ({}) => {}); - - test("12F_1. Should reject invalid data in “contact information” section", async ({}) => {}); - test("12F_2. Should reject invalid data in “proposal ownership” section", async ({}) => {}); - test("12F_3. Should reject invalid data in “problem statements and proposal benefits” section", async ({}) => {}); - test("12E_4. Should accept invalid data in “proposal details” section", async ({}) => {}); - test("12F_5. Should reject invalid data in “costing” section", async ({}) => {}); - test("12F_6. Should reject invalid data in “further information” section", async ({}) => {}); }); test("12C. Should save and view draft proposal", async ({ browser }) => { From a202dca0638d30319d616aaf12a35879f97029e3 Mon Sep 17 00:00:00 2001 From: Niraj Date: Thu, 17 Apr 2025 10:50:34 +0545 Subject: [PATCH 02/19] fix: proposal pillars ada format --- .../playwright/lib/helpers/adaFormat.ts | 9 +++++++++ .../lib/pages/budgetDiscussionDetailsPage.ts | 9 ++++++--- .../lib/pages/budgetDiscussionSubmissionPage.ts | 15 +++++++++------ tests/govtool-frontend/playwright/lib/types.ts | 2 +- .../proposalBudgetSubmission.loggedin.spec.ts | 1 - 5 files changed, 25 insertions(+), 11 deletions(-) diff --git a/tests/govtool-frontend/playwright/lib/helpers/adaFormat.ts b/tests/govtool-frontend/playwright/lib/helpers/adaFormat.ts index 3082c39af..398239140 100644 --- a/tests/govtool-frontend/playwright/lib/helpers/adaFormat.ts +++ b/tests/govtool-frontend/playwright/lib/helpers/adaFormat.ts @@ -36,3 +36,12 @@ export const correctDRepDirectoryFormat = (ada: number | undefined) => { } return "0"; }; + +export const correctProposalPillarsAdaFormat = (ada: number | undefined) => { + if (ada) { + return ada.toLocaleString("en-us", { + maximumFractionDigits: 3, + }); + } + return "0"; +}; diff --git a/tests/govtool-frontend/playwright/lib/pages/budgetDiscussionDetailsPage.ts b/tests/govtool-frontend/playwright/lib/pages/budgetDiscussionDetailsPage.ts index 44a50c317..aabb8b336 100644 --- a/tests/govtool-frontend/playwright/lib/pages/budgetDiscussionDetailsPage.ts +++ b/tests/govtool-frontend/playwright/lib/pages/budgetDiscussionDetailsPage.ts @@ -1,3 +1,4 @@ +import { correctProposalPillarsAdaFormat } from "@helpers/adaFormat"; import { expect, Page } from "@playwright/test"; import { BudgetProposalProps, CommentResponse } from "@types"; import environments from "lib/constants/environments"; @@ -193,16 +194,18 @@ export default class BudgetDiscussionDetailsPage { // costing validation await expect(this.costingAmountContent).toHaveText( - budgetProposal.costing.adaAmount.toString() + `₳ ${correctProposalPillarsAdaFormat(budgetProposal.costing.adaAmount)}` ); await expect(this.costingConversionRateContent).toHaveText( - budgetProposal.costing.adaToUsdConversionRate.toString() + budgetProposal.costing.usdToAdaConversionRate.toString() ); await expect(this.constingPreferedCurrencyContent).toHaveText( budgetProposal.costing.preferredCurrency ); await expect(this.costingPreferedCurrencyAmountContent).toHaveText( - budgetProposal.costing.AmountInPreferredCurrency.toString() + correctProposalPillarsAdaFormat( + budgetProposal.costing.AmountInPreferredCurrency + ) ); await expect(this.costBreakdownContent).toHaveText( budgetProposal.costing.costBreakdown diff --git a/tests/govtool-frontend/playwright/lib/pages/budgetDiscussionSubmissionPage.ts b/tests/govtool-frontend/playwright/lib/pages/budgetDiscussionSubmissionPage.ts index 5844a7dd5..61d5aa4f0 100644 --- a/tests/govtool-frontend/playwright/lib/pages/budgetDiscussionSubmissionPage.ts +++ b/tests/govtool-frontend/playwright/lib/pages/budgetDiscussionSubmissionPage.ts @@ -1,5 +1,6 @@ import environments from "@constants/environments"; import { faker } from "@faker-js/faker"; +import { correctProposalPillarsAdaFormat } from "@helpers/adaFormat"; import { extractProposalIdFromUrl } from "@helpers/string"; import { invalid, valid } from "@mock/index"; import { Page, expect } from "@playwright/test"; @@ -415,7 +416,7 @@ export default class BudgetDiscussionSubmissionPage { async fillupCostingForm(costing: BudgetCostingProps) { await this.adaAmountInput.fill(costing.adaAmount.toString()); await this.usaToAdaCnversionRateInput.fill( - costing.adaToUsdConversionRate.toString() + costing.usdToAdaConversionRate.toString() ); await this.preferredCurrencySelect.click(); await this.page @@ -598,11 +599,11 @@ export default class BudgetDiscussionSubmissionPage { generateValidCosting(): BudgetCostingProps { return { adaAmount: faker.number.int({ min: 100, max: 10000 }), - adaToUsdConversionRate: faker.number.int({ min: 1, max: 100 }), + usdToAdaConversionRate: faker.number.int({ min: 1, max: 10 }), preferredCurrency: faker.helpers.arrayElement( Object.values(PreferredCurrencyEnum) ), - AmountInPreferredCurrency: faker.number.int({ min: 1, max: 100 }), + AmountInPreferredCurrency: faker.number.int({ min: 100, max: 10000 }), costBreakdown: faker.lorem.paragraph(2), }; } @@ -764,10 +765,10 @@ export default class BudgetDiscussionSubmissionPage { // costing await expect(this.adaAmountContent).toHaveText( - proposalInformations.costing.adaAmount.toString() + `₳ ${correctProposalPillarsAdaFormat(proposalInformations.costing.adaAmount)}` ); await expect(this.adaToUsdConversionRateContent).toHaveText( - proposalInformations.costing.adaToUsdConversionRate.toString() + proposalInformations.costing.usdToAdaConversionRate.toString() ); const preferredCurrencyShortForm = Object.keys(PreferredCurrencyEnum).find( @@ -780,7 +781,9 @@ export default class BudgetDiscussionSubmissionPage { preferredCurrencyShortForm ); await expect(this.preferredCurrencyAmountContent).toHaveText( - proposalInformations.costing.AmountInPreferredCurrency.toString() + correctProposalPillarsAdaFormat( + proposalInformations.costing.AmountInPreferredCurrency + ) ); await expect(this.costBreakdownContent).toHaveText( proposalInformations.costing.costBreakdown diff --git a/tests/govtool-frontend/playwright/lib/types.ts b/tests/govtool-frontend/playwright/lib/types.ts index eafc59ca8..e861f3678 100644 --- a/tests/govtool-frontend/playwright/lib/types.ts +++ b/tests/govtool-frontend/playwright/lib/types.ts @@ -489,7 +489,7 @@ export enum PreferredCurrencyEnum { export interface BudgetCostingProps { adaAmount: number; - adaToUsdConversionRate: number; + usdToAdaConversionRate: number; preferredCurrency: preferredCurrencyType; AmountInPreferredCurrency: number; costBreakdown: string; diff --git a/tests/govtool-frontend/playwright/tests/12-proposal-budget-submission/proposalBudgetSubmission.loggedin.spec.ts b/tests/govtool-frontend/playwright/tests/12-proposal-budget-submission/proposalBudgetSubmission.loggedin.spec.ts index a7366cbc6..6ef9ed4ab 100644 --- a/tests/govtool-frontend/playwright/tests/12-proposal-budget-submission/proposalBudgetSubmission.loggedin.spec.ts +++ b/tests/govtool-frontend/playwright/tests/12-proposal-budget-submission/proposalBudgetSubmission.loggedin.spec.ts @@ -16,7 +16,6 @@ import { BudgetProposalStageEnum, CompanyEnum, } from "@types"; -import { allure } from "allure-playwright"; test.beforeEach(async () => { await setAllureEpic("12. Proposal Budget Submission"); From 8e888a27ed5fd6ebf32d174e5029e84410871eb9 Mon Sep 17 00:00:00 2001 From: Niraj Date: Thu, 17 Apr 2025 11:52:28 +0545 Subject: [PATCH 03/19] test: mark slow tests for proposal discussion scenarios --- .../proposalDiscussion.loggedin.spec.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tests/govtool-frontend/playwright/tests/8-proposal-discussion/proposalDiscussion.loggedin.spec.ts b/tests/govtool-frontend/playwright/tests/8-proposal-discussion/proposalDiscussion.loggedin.spec.ts index dddf552b1..cb4019cef 100644 --- a/tests/govtool-frontend/playwright/tests/8-proposal-discussion/proposalDiscussion.loggedin.spec.ts +++ b/tests/govtool-frontend/playwright/tests/8-proposal-discussion/proposalDiscussion.loggedin.spec.ts @@ -91,6 +91,7 @@ test.describe("Proposal created with poll enabled (user auth)", () => { }); test("8Q. Should vote on poll.", async ({ page }) => { + test.slow(); const pollVotes = ["Yes", "No"]; const choice = Math.floor(Math.random() * pollVotes.length); const vote = pollVotes[choice]; @@ -110,6 +111,8 @@ test.describe("Proposal created with poll enabled (user auth)", () => { }); test("8T. Should change vote on poll.", async ({ page }) => { + test.slow(); + const pollVotes = ["Yes", "No"]; const choice = Math.floor(Math.random() * pollVotes.length); const vote = pollVotes[choice]; @@ -157,6 +160,7 @@ test.describe("Proposal created with poll enabled (proposal auth)", () => { }); test("8P. Should add poll on own proposal", async ({}) => { + test.slow(); await expect( ownerProposalDiscussionDetailsPage.addPollBtn ).not.toBeVisible(); @@ -165,6 +169,7 @@ test.describe("Proposal created with poll enabled (proposal auth)", () => { test("8R. Should disable voting after cancelling the poll with the current poll result.", async ({ page, }) => { + test.slow(); await ownerProposalDiscussionDetailsPage.closePollBtn.click(); await ownerProposalDiscussionDetailsPage.closePollYesBtn.click(); await expect( @@ -178,6 +183,7 @@ test.describe("Proposal created with poll enabled (proposal auth)", () => { }); test("8U. Should navigate to the edit proposal page when 'goto data edit screen' is selected if data does not match the anchor URL", async () => { + test.slow(); const invalidMetadataAnchorUrl = "https://www.google.com"; await ownerProposalDiscussionDetailsPage.submitAsGABtn.click(); From d3f87156aecbbe5073ccee2424f0b6801b6f90db Mon Sep 17 00:00:00 2001 From: Niraj Date: Thu, 17 Apr 2025 11:56:57 +0545 Subject: [PATCH 04/19] fix: update outcome status handling for "Live" cases --- tests/govtool-frontend/playwright/lib/pages/outcomesPage.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/govtool-frontend/playwright/lib/pages/outcomesPage.ts b/tests/govtool-frontend/playwright/lib/pages/outcomesPage.ts index 48f333ef6..0d0d3e0f8 100644 --- a/tests/govtool-frontend/playwright/lib/pages/outcomesPage.ts +++ b/tests/govtool-frontend/playwright/lib/pages/outcomesPage.ts @@ -229,6 +229,9 @@ export default class OutComesPage { .locator('[data-testid$="-status"]') .textContent(); const outcomeStatus = outcomeStatusType.filter((statusType) => { + if (statusType === "Live") { + return "In Progress"; + } return status.includes(statusType); }); return outcomeStatus.some((status) => filters.includes(status)); From 0dc71486d7f8b41429a737c29c3589b9e9a24e95 Mon Sep 17 00:00:00 2001 From: Niraj Date: Thu, 17 Apr 2025 13:58:02 +0545 Subject: [PATCH 05/19] chore: make ada format seperator support for lovelace --- .../playwright/lib/helpers/adaFormat.ts | 10 +++++++--- .../lib/pages/budgetDiscussionDetailsPage.ts | 6 +++--- .../lib/pages/budgetDiscussionSubmissionPage.ts | 6 +++--- 3 files changed, 13 insertions(+), 9 deletions(-) diff --git a/tests/govtool-frontend/playwright/lib/helpers/adaFormat.ts b/tests/govtool-frontend/playwright/lib/helpers/adaFormat.ts index 398239140..0943d84a1 100644 --- a/tests/govtool-frontend/playwright/lib/helpers/adaFormat.ts +++ b/tests/govtool-frontend/playwright/lib/helpers/adaFormat.ts @@ -37,9 +37,13 @@ export const correctDRepDirectoryFormat = (ada: number | undefined) => { return "0"; }; -export const correctProposalPillarsAdaFormat = (ada: number | undefined) => { - if (ada) { - return ada.toLocaleString("en-us", { +export const formatWithThousandSeparator = ( + amount: number | undefined, + isAda: boolean = true +) => { + const updatedAmount = !isAda ? Math.ceil(amount / LOVELACE) : amount; + if (updatedAmount) { + return updatedAmount.toLocaleString("en-us", { maximumFractionDigits: 3, }); } diff --git a/tests/govtool-frontend/playwright/lib/pages/budgetDiscussionDetailsPage.ts b/tests/govtool-frontend/playwright/lib/pages/budgetDiscussionDetailsPage.ts index aabb8b336..cd781b1ec 100644 --- a/tests/govtool-frontend/playwright/lib/pages/budgetDiscussionDetailsPage.ts +++ b/tests/govtool-frontend/playwright/lib/pages/budgetDiscussionDetailsPage.ts @@ -1,4 +1,4 @@ -import { correctProposalPillarsAdaFormat } from "@helpers/adaFormat"; +import { formatWithThousandSeparator } from "@helpers/adaFormat"; import { expect, Page } from "@playwright/test"; import { BudgetProposalProps, CommentResponse } from "@types"; import environments from "lib/constants/environments"; @@ -194,7 +194,7 @@ export default class BudgetDiscussionDetailsPage { // costing validation await expect(this.costingAmountContent).toHaveText( - `₳ ${correctProposalPillarsAdaFormat(budgetProposal.costing.adaAmount)}` + `₳ ${formatWithThousandSeparator(budgetProposal.costing.adaAmount)}` ); await expect(this.costingConversionRateContent).toHaveText( budgetProposal.costing.usdToAdaConversionRate.toString() @@ -203,7 +203,7 @@ export default class BudgetDiscussionDetailsPage { budgetProposal.costing.preferredCurrency ); await expect(this.costingPreferedCurrencyAmountContent).toHaveText( - correctProposalPillarsAdaFormat( + formatWithThousandSeparator( budgetProposal.costing.AmountInPreferredCurrency ) ); diff --git a/tests/govtool-frontend/playwright/lib/pages/budgetDiscussionSubmissionPage.ts b/tests/govtool-frontend/playwright/lib/pages/budgetDiscussionSubmissionPage.ts index 61d5aa4f0..3623035fa 100644 --- a/tests/govtool-frontend/playwright/lib/pages/budgetDiscussionSubmissionPage.ts +++ b/tests/govtool-frontend/playwright/lib/pages/budgetDiscussionSubmissionPage.ts @@ -1,6 +1,6 @@ import environments from "@constants/environments"; import { faker } from "@faker-js/faker"; -import { correctProposalPillarsAdaFormat } from "@helpers/adaFormat"; +import { formatWithThousandSeparator } from "@helpers/adaFormat"; import { extractProposalIdFromUrl } from "@helpers/string"; import { invalid, valid } from "@mock/index"; import { Page, expect } from "@playwright/test"; @@ -765,7 +765,7 @@ export default class BudgetDiscussionSubmissionPage { // costing await expect(this.adaAmountContent).toHaveText( - `₳ ${correctProposalPillarsAdaFormat(proposalInformations.costing.adaAmount)}` + `₳ ${formatWithThousandSeparator(proposalInformations.costing.adaAmount)}` ); await expect(this.adaToUsdConversionRateContent).toHaveText( proposalInformations.costing.usdToAdaConversionRate.toString() @@ -781,7 +781,7 @@ export default class BudgetDiscussionSubmissionPage { preferredCurrencyShortForm ); await expect(this.preferredCurrencyAmountContent).toHaveText( - correctProposalPillarsAdaFormat( + formatWithThousandSeparator( proposalInformations.costing.AmountInPreferredCurrency ) ); From 8a02793e288dd4992ebfad8975aa2c290096d4c1 Mon Sep 17 00:00:00 2001 From: Niraj Date: Thu, 17 Apr 2025 14:07:52 +0545 Subject: [PATCH 06/19] chore: update the testIds and assertion of outcome voting power --- .../playwright/lib/helpers/index.ts | 16 +++ .../lib/pages/outcomeDetailsPage.ts | 72 ++++++---- .../govtool-frontend/playwright/lib/types.ts | 2 + .../tests/9-outcomes/outcomes.spec.ts | 133 ++++++++++++++---- 4 files changed, 171 insertions(+), 52 deletions(-) create mode 100644 tests/govtool-frontend/playwright/lib/helpers/index.ts diff --git a/tests/govtool-frontend/playwright/lib/helpers/index.ts b/tests/govtool-frontend/playwright/lib/helpers/index.ts new file mode 100644 index 000000000..1d78b1277 --- /dev/null +++ b/tests/govtool-frontend/playwright/lib/helpers/index.ts @@ -0,0 +1,16 @@ +export const parseVotingPowerAndPercentage = ( + combinedString: string +): { votingPower: string; percentage: string } => { + const splitString = combinedString.split("-"); + if (splitString.length !== 2) { + throw new Error("Invalid format: expected 'votingPower - percentage'"); + } + + const votingPower = splitString[0].trim(); + const percentage = splitString[1].trim(); + + return { + votingPower, + percentage, + }; +}; diff --git a/tests/govtool-frontend/playwright/lib/pages/outcomeDetailsPage.ts b/tests/govtool-frontend/playwright/lib/pages/outcomeDetailsPage.ts index e1e298704..878d71b67 100644 --- a/tests/govtool-frontend/playwright/lib/pages/outcomeDetailsPage.ts +++ b/tests/govtool-frontend/playwright/lib/pages/outcomeDetailsPage.ts @@ -1,33 +1,40 @@ import environments from "@constants/environments"; +import { formatWithThousandSeparator } from "@helpers/adaFormat"; import { Page, Response } from "@playwright/test"; -import { outcomeProposal } from "@types"; +import { outcomeProposal, VoterType } from "@types"; export default class OutcomeDetailsPage { - readonly dRepYesVotes = this.page.getByTestId("submitted-votes-dReps-yes"); - readonly dRepNoVotes = this.page.getByTestId("submitted-votes-dReps-no"); + readonly dRepYesVotes = this.page.getByTestId("DReps-yes-votes-submitted"); + readonly dRepNoVotes = this.page.getByTestId("DReps-no-votes-submitted"); readonly dRepNotVoted = this.page.getByTestId( "submitted-votes-dReps-notVoted" ); readonly dRepAbstainVotes = this.page.getByTestId( "submitted-votes-dReps-abstain" ); + readonly dRepExpandButton = this.page.getByTestId("DReps-expand-button"); - readonly sPosYesVotes = this.page.getByTestId("submitted-votes-sPos-yes"); - readonly sPosNoVotes = this.page.getByTestId("submitted-votes-sPos-no"); + readonly sPosYesVotes = this.page.getByTestId("SPOs-yes-votes-submitted"); + readonly sPosNoVotes = this.page.getByTestId("SPOs-no-votes-submitted"); readonly sPosAbstainVotes = this.page.getByTestId( "submitted-votes-sPos-abstain" ); + readonly sPosExpandButton = this.page.getByTestId("SPOs-expand-button"); readonly ccCommitteeYesVotes = this.page.getByTestId( - "submitted-votes-ccCommittee-yes" + "Constitutional Committee-yes-votes-submitted" ); readonly ccCommitteeNoVotes = this.page.getByTestId( - "submitted-votes-ccCommittee-no" + "Constitutional Committee-no-votes-submitted" ); - readonly ccCommitteeAbstainVotes = this.page.getByTestId( - "submitted-votes-ccCommittee-abstain" + readonly ccCommitteeAbstainVoteResult = this.page.getByTestId( + "CC-voting-results-data" ); + readonly dRepResultData = this.page.getByTestId("DReps-voting-results-data"); + readonly sPosResultData = this.page.getByTestId("SPOs-voting-results-data"); + readonly cCResultData = this.page.getByTestId("CC-voting-results-data"); + constructor(private readonly page: Page) {} get currentPage(): Page { @@ -40,21 +47,38 @@ export default class OutcomeDetailsPage { ); } - async getDRepTotalAbstainVoted( - proposal: outcomeProposal, - metricsResponses: Response - ): Promise { - const alwaysAbstainVotingPower = await metricsResponses - .json() - .then((res) => res.always_abstain_voting_power); - - if (alwaysAbstainVotingPower) { - const totalAbstainVoted = - parseInt(alwaysAbstainVotingPower) + parseInt(proposal.abstain_votes); - - return totalAbstainVoted; - } else { - return parseInt(proposal.abstain_votes); + async getSposAndDRepAbstainNoConfidence(metricsResponses: Response): Promise<{ + autoAbstain: string; + noConfidence: string; + sPosAutoAbstain: string; + sPosNoConfidence: string; + }> { + const response = await metricsResponses.json(); + const LOVELACE = 1000000; + let autoAbstain: string = "0"; + let noConfidence: string = "0"; + let sPosAutoAbstain: string = "0"; + let sPosNoConfidence: string = "0"; + if (response) { + autoAbstain = formatWithThousandSeparator( + Math.ceil(response.always_abstain_voting_power / LOVELACE) + ); + noConfidence = formatWithThousandSeparator( + Math.ceil(response.always_no_confidence_voting_power / LOVELACE) + ); + + sPosAutoAbstain = formatWithThousandSeparator( + Math.ceil(response.spos_abstain_voting_power / LOVELACE) + ); + sPosNoConfidence = formatWithThousandSeparator( + Math.ceil(response.spos_no_confidence_voting_power / LOVELACE) + ); } + return { + autoAbstain, + noConfidence, + sPosAutoAbstain, + sPosNoConfidence, + }; } } diff --git a/tests/govtool-frontend/playwright/lib/types.ts b/tests/govtool-frontend/playwright/lib/types.ts index e861f3678..07e1fe92b 100644 --- a/tests/govtool-frontend/playwright/lib/types.ts +++ b/tests/govtool-frontend/playwright/lib/types.ts @@ -518,3 +518,5 @@ export enum BudgetProposalStageEnum { AdministrationAndAuditing = 6, Review = 7, } + +export type VoterType = "DReps" | "SPOs" | "CC"; diff --git a/tests/govtool-frontend/playwright/tests/9-outcomes/outcomes.spec.ts b/tests/govtool-frontend/playwright/tests/9-outcomes/outcomes.spec.ts index 9af13f8f5..f9793088b 100644 --- a/tests/govtool-frontend/playwright/tests/9-outcomes/outcomes.spec.ts +++ b/tests/govtool-frontend/playwright/tests/9-outcomes/outcomes.spec.ts @@ -1,6 +1,9 @@ import { InvalidMetadata } from "@constants/index"; import { test } from "@fixtures/walletExtension"; -import { correctVoteAdaFormat } from "@helpers/adaFormat"; +import { + correctVoteAdaFormat, + formatWithThousandSeparator, +} from "@helpers/adaFormat"; import { setAllureEpic } from "@helpers/allure"; import { skipIfNotHardFork } from "@helpers/cardano"; import extractExpiryDateFromText from "@helpers/extractExpiryDateFromText"; @@ -9,6 +12,7 @@ import { areDRepVoteTotalsDisplayed, areSPOVoteTotalsDisplayed, } from "@helpers/featureFlag"; +import { parseVotingPowerAndPercentage } from "@helpers/index"; import { isMobile } from "@helpers/mobile"; import { injectLogger } from "@helpers/page"; import { functionWaitedAssert } from "@helpers/waitedLoop"; @@ -332,7 +336,7 @@ test("9G. Should display correct vote counts on outcome details page", async ({ ); const metricsResponsePromise = page.waitForResponse( - (response) => response.url().includes(`/misc/network/metrics`), + (response) => response.url().includes(`/misc/network/metrics?epoch`), { timeout: 60_000 } ); @@ -369,48 +373,121 @@ test("9G. Should display correct vote counts on outcome details page", async ({ const metricsResponse = await metricsResponsePromise; - const dRepTotalAbstainVote = - await govActionDetailsPage.getDRepTotalAbstainVoted( - proposalToCheck, + const { autoAbstain, noConfidence, sPosAutoAbstain, sPosNoConfidence } = + await govActionDetailsPage.getSposAndDRepAbstainNoConfidence( metricsResponse ); // check dRep votes if (await areDRepVoteTotalsDisplayed(proposalToCheck)) { - await expect(govActionDetailsPage.dRepYesVotes).toHaveText( - `₳ ${correctVoteAdaFormat(parseInt(proposalToCheck.yes_votes))}`, - { timeout: 60_000 } - ); - await expect(govActionDetailsPage.dRepAbstainVotes).toHaveText( - `₳ ${correctVoteAdaFormat(dRepTotalAbstainVote)}` - ); - await expect(govActionDetailsPage.dRepNoVotes).toHaveText( - `₳ ${correctVoteAdaFormat(parseInt(proposalToCheck.no_votes))}` + await govActionDetailsPage.dRepExpandButton.click(); + + await expect( + govActionDetailsPage.dRepResultData.getByRole("row", { + name: "Yes", + }) + ).toHaveText( + `Yes${formatWithThousandSeparator(proposalToCheck.yes_votes, false)}`, + { + timeout: 60_000, + } + ); //BUG missing testIds + + await expect( + govActionDetailsPage.dRepResultData.getByRole("row", { + name: "Auto-Abstain", + }) + ).toHaveText(`Auto-Abstain${autoAbstain}`); //BUG missing testIds + await expect( + govActionDetailsPage.dRepResultData.getByRole("row", { + name: "No Confidence", + }) + ).toHaveText(`No Confidence${noConfidence}`); //BUG missing testIds + await expect( + govActionDetailsPage.dRepResultData.getByRole("row", { + name: "Explicit", + }) + ).toHaveText( + `Explicit${formatWithThousandSeparator(proposalToCheck.abstain_votes, false)}` ); + + await expect( + govActionDetailsPage.dRepResultData + .getByRole("row", { + name: "No", + }) + .first() + ).toHaveText( + `No${formatWithThousandSeparator(proposalToCheck.no_votes, false)}` + ); //BUG missing testIds } + // check sPos votes if (await areSPOVoteTotalsDisplayed(proposalToCheck)) { - await expect(govActionDetailsPage.sPosYesVotes).toHaveText( - `₳ ${correctVoteAdaFormat(parseInt(proposalToCheck.pool_yes_votes))}` - ); - await expect(govActionDetailsPage.sPosAbstainVotes).toHaveText( - `₳ ${correctVoteAdaFormat(parseInt(proposalToCheck.pool_abstain_votes))}` - ); - await expect(govActionDetailsPage.sPosNoVotes).toHaveText( - `₳ ${correctVoteAdaFormat(parseInt(proposalToCheck.pool_no_votes))}` - ); + await govActionDetailsPage.sPosExpandButton.click(); + + await expect( + govActionDetailsPage.sPosResultData.getByRole("row", { + name: "Yes", + }) + ).toHaveText( + `Yes${formatWithThousandSeparator(proposalToCheck.pool_yes_votes, false)}`, + { + timeout: 60_000, + } + ); //BUG missing testIds + + await expect( + govActionDetailsPage.sPosResultData.getByRole("row", { + name: "Auto-Abstain", + }) + ).toHaveText(`Auto-Abstain${sPosAutoAbstain}`); //BUG missing testIds + await expect( + govActionDetailsPage.sPosResultData.getByRole("row", { + name: "No Confidence", + }) + ).toHaveText(`No Confidence${sPosNoConfidence}`); //BUG missing testIds + await expect( + govActionDetailsPage.sPosResultData.getByRole("row", { + name: "Explicit", + }) + ).toHaveText( + `Explicit${formatWithThousandSeparator(Math.ceil(proposalToCheck.pool_abstain_votes) / 1000000)}` + ); //BUG missing testIds + await expect( + govActionDetailsPage.sPosResultData + .getByRole("row", { + name: "No", + }) + .first() + ).toHaveText( + `No${formatWithThousandSeparator(proposalToCheck.pool_no_votes, false)}` + ); //BUG missing testIds } // check ccCommittee votes if (areCCVoteTotalsDisplayed(proposalToCheck)) { - await expect(govActionDetailsPage.ccCommitteeYesVotes).toHaveText( - `${proposalToCheck.cc_yes_votes}` + const ccYesVoteSubmittedText = + await govActionDetailsPage.ccCommitteeYesVotes.textContent(); + const ccNoVoteSubmittedText = + await govActionDetailsPage.ccCommitteeYesVotes.textContent(); + const { percentage: yesPercentage } = parseVotingPowerAndPercentage( + ccYesVoteSubmittedText ); - await expect(govActionDetailsPage.ccCommitteeAbstainVotes).toHaveText( - `${proposalToCheck.cc_abstain_votes}` + const { percentage: noPercentage } = parseVotingPowerAndPercentage( + ccNoVoteSubmittedText ); + await expect(govActionDetailsPage.ccCommitteeYesVotes).toHaveText( + `${proposalToCheck.cc_yes_votes} - ${yesPercentage}` + ); + await expect( + govActionDetailsPage.cCResultData.getByRole("row", { + name: "Abstain Votes", + }) + ).toHaveText(`Abstain Votes${proposalToCheck.pool_abstain_votes}`); //BUG missing testIds + await expect(govActionDetailsPage.ccCommitteeNoVotes).toHaveText( - `${proposalToCheck.cc_no_votes}` + `${proposalToCheck.cc_no_votes} - ${noPercentage}` ); } }) From 7b0531f2ec5657b954639ccdd4cb3c0ecdcb68a9 Mon Sep 17 00:00:00 2001 From: Niraj Date: Thu, 17 Apr 2025 14:08:25 +0545 Subject: [PATCH 07/19] feat: add support for DRep authentication in budget submission tests --- tests/govtool-frontend/playwright/lib/helpers/auth.ts | 5 +++++ .../tests/11-proposal-budget/proposalBudget.dRep.spec.ts | 2 ++ tests/govtool-frontend/playwright/tests/auth.setup.ts | 1 + 3 files changed, 8 insertions(+) diff --git a/tests/govtool-frontend/playwright/lib/helpers/auth.ts b/tests/govtool-frontend/playwright/lib/helpers/auth.ts index 12820ea34..665326ed9 100644 --- a/tests/govtool-frontend/playwright/lib/helpers/auth.ts +++ b/tests/govtool-frontend/playwright/lib/helpers/auth.ts @@ -20,6 +20,7 @@ interface CreateUserProps { context: BrowserContext; wallet: StaticWallet; auth: string; + isDrep?: boolean; } export async function createAuth({ @@ -42,6 +43,7 @@ export async function createAuthWithUserName({ context, wallet, auth, + isDrep = false, }: CreateUserProps) { await importWallet(page, wallet); @@ -52,6 +54,9 @@ export async function createAuthWithUserName({ const proposalDiscussionPage = new ProposalDiscussionPage(page); await proposalDiscussionPage.goto(); await proposalDiscussionPage.verifyIdentityBtn.click({ timeout: 15_000 }); + if (isDrep) { + await proposalDiscussionPage.verifyIdentityBtn.click({ timeout: 15_000 }); + } await proposalDiscussionPage.setUsername(mockValid.username()); diff --git a/tests/govtool-frontend/playwright/tests/11-proposal-budget/proposalBudget.dRep.spec.ts b/tests/govtool-frontend/playwright/tests/11-proposal-budget/proposalBudget.dRep.spec.ts index b1bb7dad2..44cb603c2 100644 --- a/tests/govtool-frontend/playwright/tests/11-proposal-budget/proposalBudget.dRep.spec.ts +++ b/tests/govtool-frontend/playwright/tests/11-proposal-budget/proposalBudget.dRep.spec.ts @@ -23,6 +23,7 @@ test.describe("Budget proposal dRep behaviour", () => { await budgetDiscussionDetailsPage.goto(proposalId); await budgetDiscussionDetailsPage.verifyIdentityBtn.click(); + await budgetDiscussionDetailsPage.verifyIdentityBtn.click(); }); test("11K. Should allow registered DRep to vote on a proposal", async () => { @@ -81,6 +82,7 @@ test.describe("Budget proposal dRep behaviour", () => { const budgetDiscussionPage = new BudgetDiscussionPage(page); await budgetDiscussionPage.goto(); await budgetDiscussionPage.verifyIdentityBtn.click(); + await budgetDiscussionPage.verifyIdentityBtn.click(); const budgetDiscussionDetailsPage = await budgetDiscussionPage.viewFirstProposal(); await budgetDiscussionDetailsPage.addComment(comment); diff --git a/tests/govtool-frontend/playwright/tests/auth.setup.ts b/tests/govtool-frontend/playwright/tests/auth.setup.ts index 2732ff065..c4a3ad553 100644 --- a/tests/govtool-frontend/playwright/tests/auth.setup.ts +++ b/tests/govtool-frontend/playwright/tests/auth.setup.ts @@ -92,6 +92,7 @@ setup("Create DRep 03 auth with username", async ({ page, context }) => { context, wallet: dRep03Wallet, auth: dRep03AuthFile, + isDrep: true, }); }); From c0abfc291df5d3c278fb475003af5d8b7d71cefe Mon Sep 17 00:00:00 2001 From: Niraj Date: Thu, 17 Apr 2025 14:31:06 +0545 Subject: [PATCH 08/19] refactor: remove unused contact information fields and related form logic --- .../pages/budgetDiscussionSubmissionPage.ts | 42 ------------------- 1 file changed, 42 deletions(-) diff --git a/tests/govtool-frontend/playwright/lib/pages/budgetDiscussionSubmissionPage.ts b/tests/govtool-frontend/playwright/lib/pages/budgetDiscussionSubmissionPage.ts index 3623035fa..fef1c0fb7 100644 --- a/tests/govtool-frontend/playwright/lib/pages/budgetDiscussionSubmissionPage.ts +++ b/tests/govtool-frontend/playwright/lib/pages/budgetDiscussionSubmissionPage.ts @@ -64,18 +64,6 @@ export default class BudgetDiscussionSubmissionPage { readonly linkTextInput = this.page.getByTestId("link-0-text-input"); readonly linkUrlInput = this.page.getByTestId("link-0-url-input"); - // contact-information - readonly beneficiaryFullNameInput = this.page.getByLabel( - "Beneficiary Full Name *" - ); //BUG missing test Ids - readonly beneficiaryEmailInput = this.page.getByLabel("Beneficiary e-mail *"); //BUG missing test Ids - readonly submissionLeadFullNameInput = this.page.getByLabel( - "Submission Lead Full Name *" - ); //BUG missing test Ids - readonly submissionLeadEmailInput = this.page.getByLabel( - "Submission Lead Email *" - ); //BUG missing test Ids - // proposal-ownership readonly companyNameInput = this.page.getByLabel("Company Name *"); //BUG missing test Ids readonly companyDomainNameInput = this.page.getByLabel( @@ -276,36 +264,6 @@ export default class BudgetDiscussionSubmissionPage { await this.continueBtn.click(); } - async fillupContactInformationForm( - contactInformation: BudgetProposalContactInformationProps - ) { - await this.beneficiaryFullNameInput.fill( - contactInformation.beneficiaryFullName - ); - await this.beneficiaryEmailInput.fill(contactInformation.beneficiaryEmail); - await this.beneficiaryCountrySelect.click(); - await this.page - .getByTestId( - `${contactInformation.beneficiaryCountry.toLowerCase().replace(/ /g, "-")}-button` - ) - .click(); - await this.beneficiaryNationalitySelect.click(); - await this.page - .getByTestId( - `${contactInformation.beneficiaryNationality.toLowerCase().replace(/ /g, "-")}-button` - ) - .click(); - - await this.submissionLeadFullNameInput.fill( - contactInformation.submissionLeadFullName - ); - await this.submissionLeadEmailInput.fill( - contactInformation.submissionLeadEmail - ); - - await this.continueBtn.click(); - } - async fillupProposalOwnershipForm( proposalOwnership: BudgetProposalOwnershipProps ) { From 0be05f05d24e437035d70efb9997077d094a4cae Mon Sep 17 00:00:00 2001 From: Niraj Date: Thu, 17 Apr 2025 14:56:50 +0545 Subject: [PATCH 09/19] refactor: remove isDrep parameter from authentication functions --- tests/govtool-frontend/playwright/lib/helpers/auth.ts | 5 ----- tests/govtool-frontend/playwright/tests/auth.setup.ts | 1 - 2 files changed, 6 deletions(-) diff --git a/tests/govtool-frontend/playwright/lib/helpers/auth.ts b/tests/govtool-frontend/playwright/lib/helpers/auth.ts index 665326ed9..12820ea34 100644 --- a/tests/govtool-frontend/playwright/lib/helpers/auth.ts +++ b/tests/govtool-frontend/playwright/lib/helpers/auth.ts @@ -20,7 +20,6 @@ interface CreateUserProps { context: BrowserContext; wallet: StaticWallet; auth: string; - isDrep?: boolean; } export async function createAuth({ @@ -43,7 +42,6 @@ export async function createAuthWithUserName({ context, wallet, auth, - isDrep = false, }: CreateUserProps) { await importWallet(page, wallet); @@ -54,9 +52,6 @@ export async function createAuthWithUserName({ const proposalDiscussionPage = new ProposalDiscussionPage(page); await proposalDiscussionPage.goto(); await proposalDiscussionPage.verifyIdentityBtn.click({ timeout: 15_000 }); - if (isDrep) { - await proposalDiscussionPage.verifyIdentityBtn.click({ timeout: 15_000 }); - } await proposalDiscussionPage.setUsername(mockValid.username()); diff --git a/tests/govtool-frontend/playwright/tests/auth.setup.ts b/tests/govtool-frontend/playwright/tests/auth.setup.ts index c4a3ad553..2732ff065 100644 --- a/tests/govtool-frontend/playwright/tests/auth.setup.ts +++ b/tests/govtool-frontend/playwright/tests/auth.setup.ts @@ -92,7 +92,6 @@ setup("Create DRep 03 auth with username", async ({ page, context }) => { context, wallet: dRep03Wallet, auth: dRep03AuthFile, - isDrep: true, }); }); From a39a5d5cfa9dfdf2b325212de2b33842e92225e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Sza=C5=82owski?= Date: Thu, 17 Apr 2025 16:14:37 +0200 Subject: [PATCH 10/19] fix: remove vp that is refreshing every 20s from pdf --- govtool/frontend/src/pages/ProposalDiscussion.tsx | 8 +------- govtool/frontend/src/types/@intersect.mbo.d.ts | 1 - 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/govtool/frontend/src/pages/ProposalDiscussion.tsx b/govtool/frontend/src/pages/ProposalDiscussion.tsx index 396b6eee8..bbc0e8efe 100644 --- a/govtool/frontend/src/pages/ProposalDiscussion.tsx +++ b/govtool/frontend/src/pages/ProposalDiscussion.tsx @@ -11,11 +11,7 @@ import { import { useValidateMutation } from "@/hooks/mutations"; import { useScreenDimension } from "@/hooks/useScreenDimension"; import { Footer, TopNav } from "@/components/organisms"; -import { - useGetAdaHolderVotingPowerQuery, - useGetDRepVotingPowerList, - useGetVoterInfo, -} from "@/hooks"; +import { useGetDRepVotingPowerList, useGetVoterInfo } from "@/hooks"; const ProposalDiscussion = React.lazy( () => import("@intersect.mbo/pdf-ui/cjs"), @@ -30,7 +26,6 @@ export const ProposalDiscussionPillar = () => { const { createGovernanceActionJsonLD, createHash } = useGovernanceActions(); const { fetchDRepVotingPowerList } = useGetDRepVotingPowerList(); const { username, setUsername } = useProposalDiscussion(); - const { votingPower } = useGetAdaHolderVotingPowerQuery(context.stakeKey); const snackbarContext = useSnackbar(); return ( @@ -84,7 +79,6 @@ export const ProposalDiscussionPillar = () => { username={username} setUsername={setUsername} epochParams={epochParams} - votingPower={votingPower} {...snackbarContext} /> diff --git a/govtool/frontend/src/types/@intersect.mbo.d.ts b/govtool/frontend/src/types/@intersect.mbo.d.ts index 140fcd55b..5db4fed7b 100644 --- a/govtool/frontend/src/types/@intersect.mbo.d.ts +++ b/govtool/frontend/src/types/@intersect.mbo.d.ts @@ -30,7 +30,6 @@ declare module "@intersect.mbo/pdf-ui/cjs" { identifiers: string[], ) => Promise; epochParams?: EpochParams; - votingPower: number; username: string; setUsername: (username: string) => void; }; From 2a8fbcb6dde6dbd259c8c10965e37d9dc8eec57f Mon Sep 17 00:00:00 2001 From: Niraj Date: Fri, 18 Apr 2025 09:35:50 +0545 Subject: [PATCH 11/19] fix: correct total SPos no votes calculation in outcome details test --- .../playwright/tests/9-outcomes/outcomes.spec.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/tests/govtool-frontend/playwright/tests/9-outcomes/outcomes.spec.ts b/tests/govtool-frontend/playwright/tests/9-outcomes/outcomes.spec.ts index f9793088b..ea304163e 100644 --- a/tests/govtool-frontend/playwright/tests/9-outcomes/outcomes.spec.ts +++ b/tests/govtool-frontend/playwright/tests/9-outcomes/outcomes.spec.ts @@ -425,6 +425,9 @@ test("9G. Should display correct vote counts on outcome details page", async ({ // check sPos votes if (await areSPOVoteTotalsDisplayed(proposalToCheck)) { await govActionDetailsPage.sPosExpandButton.click(); + const totalSposNoVotes = + parseInt(sPosNoConfidence.replace(/,/g, "")) + + proposalToCheck.pool_no_votes * 1000000; await expect( govActionDetailsPage.sPosResultData.getByRole("row", { @@ -460,9 +463,7 @@ test("9G. Should display correct vote counts on outcome details page", async ({ name: "No", }) .first() - ).toHaveText( - `No${formatWithThousandSeparator(proposalToCheck.pool_no_votes, false)}` - ); //BUG missing testIds + ).toHaveText(`No${formatWithThousandSeparator(totalSposNoVotes)}`); //BUG missing testIds } // check ccCommittee votes From e9a8b07c81606b08f4aaecb868d3be0ac900bd4a Mon Sep 17 00:00:00 2001 From: Niraj Date: Fri, 18 Apr 2025 13:03:18 +0545 Subject: [PATCH 12/19] chore: add test slow in before each --- .../proposalDiscussion.loggedin.spec.ts | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/tests/govtool-frontend/playwright/tests/8-proposal-discussion/proposalDiscussion.loggedin.spec.ts b/tests/govtool-frontend/playwright/tests/8-proposal-discussion/proposalDiscussion.loggedin.spec.ts index cb4019cef..5caee4fd3 100644 --- a/tests/govtool-frontend/playwright/tests/8-proposal-discussion/proposalDiscussion.loggedin.spec.ts +++ b/tests/govtool-frontend/playwright/tests/8-proposal-discussion/proposalDiscussion.loggedin.spec.ts @@ -26,6 +26,7 @@ test.describe("Proposal created logged in state", () => { let proposalDiscussionDetailsPage: ProposalDiscussionDetailsPage; test.beforeEach(async ({ page, proposalId }) => { + test.slow(); proposalDiscussionDetailsPage = new ProposalDiscussionDetailsPage(page); await proposalDiscussionDetailsPage.goto(proposalId); @@ -48,7 +49,7 @@ test.describe("Proposal created logged in state", () => { page, }) => { for (let i = 0; i < 4; i++) { - const comment = faker.lorem.paragraph(2); + const comment = faker.lorem.words(5); await proposalDiscussionDetailsPage.addComment(comment); await page.waitForTimeout(2_000); } @@ -60,9 +61,7 @@ test.describe("Proposal created logged in state", () => { }); test("8N. Should reply to comments", async ({ page }) => { - test.slow(); - - const randComment = faker.lorem.paragraph(2); + const randComment = faker.lorem.words(5); const randReply = faker.lorem.words(5); await proposalDiscussionDetailsPage.addComment(randComment); @@ -85,13 +84,13 @@ test.describe("Proposal created with poll enabled (user auth)", () => { let proposalDiscussionDetailsPage: ProposalDiscussionDetailsPage; test.beforeEach(async ({ page, proposalId }) => { + test.slow(); proposalDiscussionDetailsPage = new ProposalDiscussionDetailsPage(page); await proposalDiscussionDetailsPage.goto(proposalId); await proposalDiscussionDetailsPage.verifyIdentityBtn.click(); }); test("8Q. Should vote on poll.", async ({ page }) => { - test.slow(); const pollVotes = ["Yes", "No"]; const choice = Math.floor(Math.random() * pollVotes.length); const vote = pollVotes[choice]; @@ -111,8 +110,6 @@ test.describe("Proposal created with poll enabled (user auth)", () => { }); test("8T. Should change vote on poll.", async ({ page }) => { - test.slow(); - const pollVotes = ["Yes", "No"]; const choice = Math.floor(Math.random() * pollVotes.length); const vote = pollVotes[choice]; @@ -148,6 +145,7 @@ test.describe("Proposal created with poll enabled (proposal auth)", () => { let proposalPage: Page; test.beforeEach(async ({ browser, proposalId }) => { + test.slow(); proposalPage = await createNewPageWithWallet(browser, { storageState: ".auth/proposal01.json", wallet: proposal01Wallet, @@ -160,7 +158,6 @@ test.describe("Proposal created with poll enabled (proposal auth)", () => { }); test("8P. Should add poll on own proposal", async ({}) => { - test.slow(); await expect( ownerProposalDiscussionDetailsPage.addPollBtn ).not.toBeVisible(); @@ -169,7 +166,6 @@ test.describe("Proposal created with poll enabled (proposal auth)", () => { test("8R. Should disable voting after cancelling the poll with the current poll result.", async ({ page, }) => { - test.slow(); await ownerProposalDiscussionDetailsPage.closePollBtn.click(); await ownerProposalDiscussionDetailsPage.closePollYesBtn.click(); await expect( @@ -183,7 +179,6 @@ test.describe("Proposal created with poll enabled (proposal auth)", () => { }); test("8U. Should navigate to the edit proposal page when 'goto data edit screen' is selected if data does not match the anchor URL", async () => { - test.slow(); const invalidMetadataAnchorUrl = "https://www.google.com"; await ownerProposalDiscussionDetailsPage.submitAsGABtn.click(); From 38094dd7f2cbb34f2ca445c679a94d5c35c0f0d5 Mon Sep 17 00:00:00 2001 From: Niraj Date: Fri, 18 Apr 2025 13:03:49 +0545 Subject: [PATCH 13/19] chore: replace paragraph comments with word-based comments in budget proposal tests --- .../tests/11-proposal-budget/proposalBudget.dRep.spec.ts | 2 +- .../11-proposal-budget/proposalBudget.loggedin.spec.ts | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/govtool-frontend/playwright/tests/11-proposal-budget/proposalBudget.dRep.spec.ts b/tests/govtool-frontend/playwright/tests/11-proposal-budget/proposalBudget.dRep.spec.ts index 44cb603c2..0b50b3f88 100644 --- a/tests/govtool-frontend/playwright/tests/11-proposal-budget/proposalBudget.dRep.spec.ts +++ b/tests/govtool-frontend/playwright/tests/11-proposal-budget/proposalBudget.dRep.spec.ts @@ -78,7 +78,7 @@ test.describe("Budget proposal dRep behaviour", () => { test("11M. Should display DRep tag, name and ID when a registered DRep comments on a proposal", async ({ page, }) => { - const comment = faker.lorem.paragraph(2); + const comment = faker.lorem.words(5); const budgetDiscussionPage = new BudgetDiscussionPage(page); await budgetDiscussionPage.goto(); await budgetDiscussionPage.verifyIdentityBtn.click(); diff --git a/tests/govtool-frontend/playwright/tests/11-proposal-budget/proposalBudget.loggedin.spec.ts b/tests/govtool-frontend/playwright/tests/11-proposal-budget/proposalBudget.loggedin.spec.ts index e76bb8738..bf0912073 100644 --- a/tests/govtool-frontend/playwright/tests/11-proposal-budget/proposalBudget.loggedin.spec.ts +++ b/tests/govtool-frontend/playwright/tests/11-proposal-budget/proposalBudget.loggedin.spec.ts @@ -28,7 +28,7 @@ test.describe("Budget proposal logged in state", () => { test("11G. Should sort the budget proposal comments", async ({ page }) => { for (let i = 0; i < 4; i++) { - const comment = faker.lorem.paragraph(2); + const comment = faker.lorem.words(5); await budgetDiscussionDetailsPage.addComment(comment); await page.waitForTimeout(2_000); } @@ -49,7 +49,7 @@ test.describe("Budget proposal logged in state", () => { }); test("11I. Should comments on any proposal", async ({}) => { - const comment = faker.lorem.paragraph(1); + const comment = faker.lorem.words(5); await budgetDiscussionDetailsPage.addComment(comment); await expect( budgetDiscussionDetailsPage.currentPage @@ -59,7 +59,7 @@ test.describe("Budget proposal logged in state", () => { }); test("11J. Should reply to any comments", async ({}) => { - const randComment = faker.lorem.paragraph(1); + const randComment = faker.lorem.words(5); const randReply = faker.lorem.words(5); await budgetDiscussionDetailsPage.addComment(randComment); From 20d30152b7bef6df98b2b5b15bae97ad304ca865 Mon Sep 17 00:00:00 2001 From: Niraj Date: Fri, 18 Apr 2025 13:04:03 +0545 Subject: [PATCH 14/19] test: increase timeout for delegation card visibility check --- .../2-delegation/delegationFunctionality.delegation.spec.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/govtool-frontend/playwright/tests/2-delegation/delegationFunctionality.delegation.spec.ts b/tests/govtool-frontend/playwright/tests/2-delegation/delegationFunctionality.delegation.spec.ts index 9f71eefa2..52e5ba376 100644 --- a/tests/govtool-frontend/playwright/tests/2-delegation/delegationFunctionality.delegation.spec.ts +++ b/tests/govtool-frontend/playwright/tests/2-delegation/delegationFunctionality.delegation.spec.ts @@ -98,9 +98,9 @@ test.describe("Change delegation", () => { await dRepDirectoryPage.delegateToDRep(dRepIdFirst); // verify delegation - await expect( - page.getByTestId(`${dRepIdFirst}-delegated-card`) - ).toBeVisible(); + await expect(page.getByTestId(`${dRepIdFirst}-delegated-card`)).toBeVisible( + { timeout: 60_000 } + ); await expect( page From 1fc137a8f87a55eb45327fbdd07790877ebb91d7 Mon Sep 17 00:00:00 2001 From: Niraj Date: Fri, 18 Apr 2025 15:05:02 +0545 Subject: [PATCH 15/19] feat: add dRepKey support to ShelleyWallet --- .../playwright/lib/helpers/crypto.ts | 30 ++++++------------- 1 file changed, 9 insertions(+), 21 deletions(-) diff --git a/tests/govtool-frontend/playwright/lib/helpers/crypto.ts b/tests/govtool-frontend/playwright/lib/helpers/crypto.ts index dd38d2c51..e1b75f849 100644 --- a/tests/govtool-frontend/playwright/lib/helpers/crypto.ts +++ b/tests/govtool-frontend/playwright/lib/helpers/crypto.ts @@ -84,14 +84,17 @@ export class Ed25519Key { export class ShelleyWallet { paymentKey: Ed25519Key; stakeKey: Ed25519Key; + dRepKey: Ed25519Key; - public constructor(payment, stake) { + public constructor(payment, stake, dRep) { this.paymentKey = payment; this.stakeKey = stake; + this.dRepKey = dRep; } public static async generate() { const wallet = new ShelleyWallet( + await Ed25519Key.generate(), await Ed25519Key.generate(), await Ed25519Key.generate() ); @@ -145,6 +148,7 @@ export class ShelleyWallet { return { payment: this.paymentKey.json(), stake: this.stakeKey.json(), + dRep: this.dRepKey.json(), dRepId: this.dRepIdBech32(), address: this.addressBech32(environments.networkId), }; @@ -153,6 +157,7 @@ export class ShelleyWallet { public static fromJson(obj: { payment: object; stake: object; + dRep: object; }): ShelleyWallet { if (!obj || typeof obj !== "object") { throw new Error("ShelleyWallet.fromJson: The input must be an object."); @@ -160,6 +165,7 @@ export class ShelleyWallet { const paymentKey = obj.payment; const stakeKey = obj.stake; + const dRepKey = obj.dRep; if (!paymentKey || typeof paymentKey !== "object") { throw new Error( @@ -174,28 +180,10 @@ export class ShelleyWallet { } return new ShelleyWallet( Ed25519Key.fromJson(paymentKey), - Ed25519Key.fromJson(stakeKey) + Ed25519Key.fromJson(stakeKey), + Ed25519Key.fromJson(dRepKey) ); } - - public static dummy(): ShelleyWallet { - return ShelleyWallet.fromJson({ - payment: { - pkh: "595ac9bbf256bae584f56a4b671baa4b14a18c8098b8e571834bc12c", - private: - "5a1380cd79ecaee48d66c14f7d92ddfc866490a3b59d44520e60f16309c8a17d", - public: - "8d2f4d49118eb1156048b66dd6372cdb1f82da0f8e208d9f8ea4b388c79c09ad", - }, - stake: { - pkh: "6706efab75778c2f08b9a5321ead8bfc982a5c08b51a0b2a713cac52", - private: - "24e8c012c7bef2f5823baef1c06dac253da860a43f0d1f43fc3c8349a4f719a1", - public: - "f7a1eaea2691ee80b6c0d6f27482145d7037055829b1b26224a5d8f0c2243f16", - }, - }); - } } export interface Address { From 595534b91fdf9db03704955fced84285a87e252b Mon Sep 17 00:00:00 2001 From: Niraj Date: Fri, 18 Apr 2025 15:06:20 +0545 Subject: [PATCH 16/19] chore: extract dRepId and register dRep using dRep keys --- tests/govtool-frontend/playwright/lib/helpers/shellyWallet.ts | 4 ++-- .../govtool-frontend/playwright/lib/services/kuberService.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/govtool-frontend/playwright/lib/helpers/shellyWallet.ts b/tests/govtool-frontend/playwright/lib/helpers/shellyWallet.ts index 5f2ba9eb0..4bf66bd48 100644 --- a/tests/govtool-frontend/playwright/lib/helpers/shellyWallet.ts +++ b/tests/govtool-frontend/playwright/lib/helpers/shellyWallet.ts @@ -4,9 +4,9 @@ import convertBufferToHex from "./convertBufferToHex"; import { ShelleyWallet } from "./crypto"; export default function extractDRepFromWallet(wallet: ShelleyWallet) { - const stakePubKey = convertBufferToHex(wallet.stakeKey.public); + const dRepPubKey = convertBufferToHex(wallet.dRepKey.public); - const dRepKeyBytes = Buffer.from(stakePubKey, "hex"); + const dRepKeyBytes = Buffer.from(dRepPubKey, "hex"); const dRepId = blake2bHex(dRepKeyBytes, undefined, 28); const words = bech32.toWords(Buffer.from(dRepId, "hex")); const dRepIdBech32 = bech32.encode("drep", words); diff --git a/tests/govtool-frontend/playwright/lib/services/kuberService.ts b/tests/govtool-frontend/playwright/lib/services/kuberService.ts index 5192debc4..9047faecb 100644 --- a/tests/govtool-frontend/playwright/lib/services/kuberService.ts +++ b/tests/govtool-frontend/playwright/lib/services/kuberService.ts @@ -219,7 +219,7 @@ const kuberService = { certificates: metadataAndWallets.map((metadataAndWallet) => Kuber.generateCert( "registerdrep", - metadataAndWallet.wallet.stake.pkh, + metadataAndWallet.wallet.dRep.pkh, metadataAndWallet ) ), @@ -227,7 +227,7 @@ const kuberService = { return { type: "PaymentSigningKeyShelley_ed25519", description: "Stake Signing Key", - cborHex: `5820${metadata.wallet.stake.private}`, + cborHex: `5820${metadata.wallet.dRep.private}`, }; }), }; From 60191eda9e4d68a3707f5259a1ae351583ca5201 Mon Sep 17 00:00:00 2001 From: Niraj Date: Fri, 18 Apr 2025 15:33:16 +0545 Subject: [PATCH 17/19] chore: update @cardanoapi/cardano-test-wallet to version 3.3.0 --- tests/govtool-frontend/playwright/package-lock.json | 8 ++++---- tests/govtool-frontend/playwright/package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/govtool-frontend/playwright/package-lock.json b/tests/govtool-frontend/playwright/package-lock.json index a186f77ce..38e2cc8e7 100644 --- a/tests/govtool-frontend/playwright/package-lock.json +++ b/tests/govtool-frontend/playwright/package-lock.json @@ -9,7 +9,7 @@ "version": "1.0.0", "license": "MIT", "dependencies": { - "@cardanoapi/cardano-test-wallet": "^3.2.0", + "@cardanoapi/cardano-test-wallet": "^3.3.0", "@faker-js/faker": "^8.4.1", "@noble/curves": "^1.3.0", "@noble/ed25519": "^2.0.0", @@ -43,9 +43,9 @@ } }, "node_modules/@cardanoapi/cardano-test-wallet": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/@cardanoapi/cardano-test-wallet/-/cardano-test-wallet-3.2.0.tgz", - "integrity": "sha512-tstRrWlfxatJ12Ra4Y3u4sMLwTyGWl7AYw8ix8H8pdxWToU2of8PWNQrRQ2fE40j2RJ0Ul5XLlMFOSLFrkybxA==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/@cardanoapi/cardano-test-wallet/-/cardano-test-wallet-3.3.0.tgz", + "integrity": "sha512-tIrxGTE1XHNAYwsbY/DaJXSRm1b0gt2yabBxJjX//fL8zdHV8uIj4RMpjf+juX8xWivOTDA+heMBTtAKKLervQ==", "license": "MIT" }, "node_modules/@cbor-extract/cbor-extract-darwin-arm64": { diff --git a/tests/govtool-frontend/playwright/package.json b/tests/govtool-frontend/playwright/package.json index 03997d2e4..6fe35493c 100644 --- a/tests/govtool-frontend/playwright/package.json +++ b/tests/govtool-frontend/playwright/package.json @@ -29,7 +29,7 @@ "generate-wallets": "ts-node ./generate_wallets.ts 24" }, "dependencies": { - "@cardanoapi/cardano-test-wallet": "^3.2.0", + "@cardanoapi/cardano-test-wallet": "^3.3.0", "@faker-js/faker": "^8.4.1", "@noble/curves": "^1.3.0", "@noble/ed25519": "^2.0.0", From 8719e66cb34fe3a405dbd8a4cdae8be91b97ddab Mon Sep 17 00:00:00 2001 From: Niraj Date: Fri, 18 Apr 2025 15:36:28 +0545 Subject: [PATCH 18/19] test: mark proposal draft tests as slow --- .../7-proposal-submission/proposalSubmission.loggedin.spec.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/govtool-frontend/playwright/tests/7-proposal-submission/proposalSubmission.loggedin.spec.ts b/tests/govtool-frontend/playwright/tests/7-proposal-submission/proposalSubmission.loggedin.spec.ts index 41fd994dc..2c53bb141 100644 --- a/tests/govtool-frontend/playwright/tests/7-proposal-submission/proposalSubmission.loggedin.spec.ts +++ b/tests/govtool-frontend/playwright/tests/7-proposal-submission/proposalSubmission.loggedin.spec.ts @@ -459,6 +459,7 @@ test.describe("Proposal Draft", () => { test(`7M_${index + 1}. Should edit a ${proposalType.toLowerCase()} proposal draft`, async ({ browser, }) => { + test.slow(); const { storageState, wallet } = getDraftProposalWalletAndState(proposalType); @@ -537,6 +538,8 @@ test.describe("Proposal Draft", () => { wallet: proposal06Wallet, }); + test.slow(); + const proposalType = Object.values(ProposalType)[ Math.floor(Math.random() * Object.values(ProposalType).length) From 087fdb8b69960d04aed14f63b4221274661ba028 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Sza=C5=82owski?= Date: Fri, 18 Apr 2025 11:07:13 +0200 Subject: [PATCH 19/19] fix: missing or wrong validation status of Governance Action metadata --- govtool/frontend/package-lock.json | 57 +-- .../src/components/organisms/DRepCard.tsx | 24 +- .../components/organisms/DRepDetailsCard.tsx | 24 +- .../DashboardCards/DelegateDashboardCard.tsx | 18 +- .../DashboardGovernanceActionDetails.tsx | 28 +- .../ValidatedGovernanceActionCard.tsx | 17 +- .../ValidatedGovernanceVotedOnCard.tsx | 15 +- .../metadataValidation/useValidateMutation.ts | 17 +- .../queries/useGetDRepVotingPowerList.ts | 20 +- .../src/pages/GovernanceActionDetails.tsx | 28 +- govtool/frontend/yarn.lock | 383 ++++++++++++++++-- 11 files changed, 504 insertions(+), 127 deletions(-) diff --git a/govtool/frontend/package-lock.json b/govtool/frontend/package-lock.json index 8c3cb8244..78093dc32 100644 --- a/govtool/frontend/package-lock.json +++ b/govtool/frontend/package-lock.json @@ -504,25 +504,25 @@ } }, "node_modules/@babel/helpers": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.26.0.tgz", - "integrity": "sha512-tbhNuIxNcVb21pInl3ZSjksLCvgdZy9KwJ8brv993QtIVKJBBkYXz4q4ZbAv31GdnC+R90np23L5FbEBlthAEw==", + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.27.0.tgz", + "integrity": "sha512-U5eyP/CTFPuNE3qk+WZMxFkp/4zUzdceQlfzf7DdGdhp+Fezd7HD+i8Y24ZuTMKX3wQBld449jijbGq6OdGNQg==", "license": "MIT", "dependencies": { - "@babel/template": "^7.25.9", - "@babel/types": "^7.26.0" + "@babel/template": "^7.27.0", + "@babel/types": "^7.27.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/parser": { - "version": "7.26.2", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.2.tgz", - "integrity": "sha512-DWMCZH9WA4Maitz2q21SRKHo9QXZxkDsbNZoVD62gusNtNBBqDg9i7uOhASfTfIGNzW+O+r7+jAlM8dwphcJKQ==", + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.0.tgz", + "integrity": "sha512-iaepho73/2Pz7w2eMS0Q5f83+0RKI7i4xmiYeBmDzfRVbQtTOG7Ts0S4HzJVsTMGI9keU8rNfuZr8DKfSt7Yyg==", "license": "MIT", "dependencies": { - "@babel/types": "^7.26.0" + "@babel/types": "^7.27.0" }, "bin": { "parser": "bin/babel-parser.js" @@ -2082,9 +2082,9 @@ } }, "node_modules/@babel/runtime": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.0.tgz", - "integrity": "sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw==", + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.27.0.tgz", + "integrity": "sha512-VtPOkrdPHZsKc/clNqyi9WUA8TINkZ4cGk63UUE3u4pmB2k+ZMQRDuIOagv8UVd6j7k0T3+RRIb7beKTebNbcw==", "license": "MIT", "dependencies": { "regenerator-runtime": "^0.14.0" @@ -2094,14 +2094,14 @@ } }, "node_modules/@babel/template": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.9.tgz", - "integrity": "sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg==", + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.0.tgz", + "integrity": "sha512-2ncevenBqXI6qRMukPlXwHKHchC7RyMuu4xv5JBXRfOGVcTy1mXCD12qrp7Jsoxll1EV3+9sE4GugBVRjT2jFA==", "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.25.9", - "@babel/parser": "^7.25.9", - "@babel/types": "^7.25.9" + "@babel/code-frame": "^7.26.2", + "@babel/parser": "^7.27.0", + "@babel/types": "^7.27.0" }, "engines": { "node": ">=6.9.0" @@ -2126,9 +2126,9 @@ } }, "node_modules/@babel/types": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.0.tgz", - "integrity": "sha512-Z/yiTPj+lDVnF7lWeKCIJzaIkI0vYO87dMpZ4bg4TDrFe4XXLFWL1TbXU27gBP3QccxV9mZICCrnjnYlJjXHOA==", + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.0.tgz", + "integrity": "sha512-H45s8fVLYjbhFH62dIJ3WtmJ6RSPt/3DRO0ZcT2SUiYiQyz3BLVb9ADEnLl91m74aQPS3AzzeajZHYOalWe3bg==", "license": "MIT", "dependencies": { "@babel/helper-string-parser": "^7.25.9", @@ -15154,7 +15154,7 @@ "version": "6.4.3", "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.3.tgz", "integrity": "sha512-PMXmW2y1hDDfTSRc9gaXIuCCRpuoz3Kaz8cUelp3smouvfT632ozg2vrT6lJsHKKOF59YLbOGfAWGUcKEfRMQw==", - "dev": true, + "devOptional": true, "license": "MIT", "peerDependencies": { "picomatch": "^3 || ^4" @@ -29530,7 +29530,7 @@ "version": "0.2.12", "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.12.tgz", "integrity": "sha512-qkf4trmKSIiMTs/E63cxH+ojC2unam7rJ0WrauAzpT3ECNTxGRMlaXxVbfxMUC/w0LaYk6jQ4y/nGR9uBO3tww==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "fdir": "^6.4.3", @@ -30431,15 +30431,18 @@ } }, "node_modules/vite": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/vite/-/vite-6.2.0.tgz", - "integrity": "sha512-7dPxoo+WsT/64rDcwoOjk76XHj+TqNTIvHKcuMQ1k4/SeHDaQt5GFAeLYzrimZrMpn/O6DtdI03WUjdxuPM0oQ==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/vite/-/vite-6.3.1.tgz", + "integrity": "sha512-kkzzkqtMESYklo96HKKPE5KKLkC1amlsqt+RjFMlX2AvbRB/0wghap19NdBxxwGZ+h/C6DLCrcEphPIItlGrRQ==", "devOptional": true, "license": "MIT", "dependencies": { "esbuild": "^0.25.0", + "fdir": "^6.4.3", + "picomatch": "^4.0.2", "postcss": "^8.5.3", - "rollup": "^4.30.1" + "rollup": "^4.34.9", + "tinyglobby": "^0.2.12" }, "bin": { "vite": "bin/vite.js" diff --git a/govtool/frontend/src/components/organisms/DRepCard.tsx b/govtool/frontend/src/components/organisms/DRepCard.tsx index a5ac25f0b..93b0bcaef 100644 --- a/govtool/frontend/src/components/organisms/DRepCard.tsx +++ b/govtool/frontend/src/components/organisms/DRepCard.tsx @@ -1,4 +1,4 @@ -import { useState, useRef, useEffect } from "react"; +import { useState, useEffect } from "react"; import { useNavigate } from "react-router-dom"; import { Box, ButtonBase, Divider, Avatar, Skeleton } from "@mui/material"; @@ -70,31 +70,35 @@ export const DRepCard = ({ const base64Image = getBase64ImageDetails(image ?? ""); const [isValidating, setIsValidating] = useState(false); - const metadataStatus = useRef(); + const [metadataStatus, setMetadataStatus] = useState< + MetadataValidationStatus | undefined + >(); const { validateMetadata } = useValidateMutation(); useEffect(() => { + if (!url) return; + const validate = async () => { setIsValidating(true); const { status: validationStatus } = await validateMetadata({ standard: MetadataStandard.CIP119, - url: url ?? "", + url, hash: metadataHash ?? "", }); - metadataStatus.current = validationStatus; + setMetadataStatus(validationStatus); setIsValidating(false); }; validate(); - }, []); + }, [url]); return ( - {metadataStatus.current - ? getMetadataDataMissingStatusTranslation( - metadataStatus.current, - ) + {metadataStatus + ? getMetadataDataMissingStatusTranslation(metadataStatus) : ellipsizeText(givenName ?? "", 25)} )} diff --git a/govtool/frontend/src/components/organisms/DRepDetailsCard.tsx b/govtool/frontend/src/components/organisms/DRepDetailsCard.tsx index a220bbbb0..82b560c44 100644 --- a/govtool/frontend/src/components/organisms/DRepDetailsCard.tsx +++ b/govtool/frontend/src/components/organisms/DRepDetailsCard.tsx @@ -1,4 +1,4 @@ -import { PropsWithChildren, useEffect, useRef, useState } from "react"; +import { PropsWithChildren, useEffect, useState } from "react"; import { Box, ButtonBase, Link, Skeleton } from "@mui/material"; import { Button, ExternalModalButton, StatusPill, Typography } from "@atoms"; @@ -52,24 +52,28 @@ export const DRepDetailsCard = ({ } = dRepData; const [isValidating, setIsValidating] = useState(false); - const metadataStatus = useRef(); + const [metadataStatus, setMetadataStatus] = useState< + MetadataValidationStatus | undefined + >(); const { validateMetadata } = useValidateMutation(); useEffect(() => { + if (!url) return; + const validate = async () => { setIsValidating(true); const { status: metadataValidationStatus } = await validateMetadata({ standard: MetadataStandard.CIP119, - url: url ?? "", + url, hash: metadataHash ?? "", }); - metadataStatus.current = metadataValidationStatus; + setMetadataStatus(metadataValidationStatus); setIsValidating(false); }; validate(); - }, []); + }, [url]); const groupedReferences = references?.reduce>( (acc, reference) => { @@ -117,18 +121,18 @@ export const DRepDetailsCard = ({ isMe={isMe} isMyDrep={isMyDrep} isValidating={isValidating} - metadataStatus={metadataStatus.current} + metadataStatus={metadataStatus} /> {/* ERROR MESSAGES */} - {metadataStatus.current && ( + {metadataStatus && ( )} - {metadataStatus.current && !!url && ( + {metadataStatus && !!url && ( (); + const [metadataStatus, setMetadataStatus] = useState< + MetadataValidationStatus | undefined + >(); const { validateMetadata } = useValidateMutation(); useEffect(() => { + if (!myDRepDelegationData?.url) return; + const validate = async () => { const { status } = await validateMetadata({ standard: MetadataStandard.CIP119, - url: myDRepDelegationData?.url ?? "", + url: myDRepDelegationData.url!, hash: myDRepDelegationData?.metadataHash ?? "", }); - metadataStatus.current = status; + setMetadataStatus(status); }; validate(); - }, []); + }, [myDRepDelegationData?.url]); const learnMoreButton = { children: t("learnMore"), @@ -170,8 +174,8 @@ export const DelegateDashboardCard = ({ drepName={ isLoading ? "Loading..." - : metadataStatus.current - ? getMetadataDataMissingStatusTranslation(metadataStatus.current) + : metadataStatus + ? getMetadataDataMissingStatusTranslation(metadataStatus) : myDRepDelegationData?.givenName ?? "" } dRepId={displayedDelegationId} diff --git a/govtool/frontend/src/components/organisms/DashboardGovernanceActionDetails.tsx b/govtool/frontend/src/components/organisms/DashboardGovernanceActionDetails.tsx index 1c6bdd6c1..17aab0727 100644 --- a/govtool/frontend/src/components/organisms/DashboardGovernanceActionDetails.tsx +++ b/govtool/frontend/src/components/organisms/DashboardGovernanceActionDetails.tsx @@ -1,4 +1,4 @@ -import { useEffect, useRef, useState } from "react"; +import { useEffect, useState } from "react"; import { useNavigate, useLocation, @@ -49,22 +49,34 @@ export const DashboardGovernanceActionDetails = () => { fullProposalId ?? "", !state?.proposal || !state?.vote, ); + // TODO: Refactor this mess with proposals and metadata validation + // once authors are existing in all CIP-108 metadata const [extendedProposal, setExtendedProposal] = useState( (data ?? state)?.proposal as ProposalData, ); + + useEffect(() => { + if (data?.proposal) { + setExtendedProposal(data.proposal); + } + }, [data?.proposal]); const vote = (data ?? state)?.vote; - const [isValidating, setIsValidating] = useState(false); - const metadataStatus = useRef(); + const [isValidating, setIsValidating] = useState(true); + const [metadataStatus, setMetadataStatus] = useState< + MetadataValidationStatus | undefined + >(); const { validateMetadata } = useValidateMutation(); useEffect(() => { + if (!extendedProposal?.url) return; + const validate = async () => { setIsValidating(true); const { status, metadata } = await validateMetadata({ standard: MetadataStandard.CIP108, - url: extendedProposal?.url ?? "", + url: extendedProposal?.url, hash: extendedProposal?.metadataHash ?? "", }); @@ -78,11 +90,11 @@ export const DashboardGovernanceActionDetails = () => { })); } - metadataStatus.current = status; + setMetadataStatus(status); setIsValidating(false); }; validate(); - }, []); + }, [extendedProposal?.url]); useEffect(() => { const isProposalNotFound = @@ -108,7 +120,7 @@ export const DashboardGovernanceActionDetails = () => { elementOne={t("govActions.title")} elementOnePath={PATHS.dashboardGovernanceActions} elementTwo={extendedProposal?.title ?? ""} - isDataMissing={metadataStatus?.current ?? null} + isDataMissing={metadataStatus ?? null} /> { isVoter={ voter?.isRegisteredAsDRep || voter?.isRegisteredAsSoleVoter } - isDataMissing={metadataStatus?.current} + isDataMissing={metadataStatus} isInProgress={ pendingTransaction.vote?.resourceId === fullProposalId?.replace("#", "") diff --git a/govtool/frontend/src/components/organisms/ValidatedGovernanceActionCard.tsx b/govtool/frontend/src/components/organisms/ValidatedGovernanceActionCard.tsx index 88ac88d62..4dfe5ae0a 100644 --- a/govtool/frontend/src/components/organisms/ValidatedGovernanceActionCard.tsx +++ b/govtool/frontend/src/components/organisms/ValidatedGovernanceActionCard.tsx @@ -1,4 +1,4 @@ -import { useState, useRef, useEffect } from "react"; +import { useState, useEffect } from "react"; import { useValidateMutation } from "@/hooks/mutations"; import { MetadataStandard, ProposalData } from "@/models"; @@ -19,24 +19,26 @@ type ActionTypeProps = Omit< }; export const ValidatedGovernanceActionCard = (props: ActionTypeProps) => { const [isValidating, setIsValidating] = useState(false); - const metadataStatus = useRef(); + const [metadataStatus, setMetadataStatus] = useState< + MetadataValidationStatus | undefined + >(); const { validateMetadata } = useValidateMutation(); const [extendedProposal, setExtendedProposal] = useState( props as ProposalData, ); useEffect(() => { + if (!props?.url) return; + const validate = async () => { setIsValidating(true); const { status, metadata } = await validateMetadata({ standard: MetadataStandard.CIP108, - url: props?.url ?? "", + url: props?.url, hash: props?.metadataHash ?? "", }); - metadataStatus.current = status; - if (metadata) { setExtendedProposal( (prevProposal) => @@ -50,16 +52,17 @@ export const ValidatedGovernanceActionCard = (props: ActionTypeProps) => { ); } + setMetadataStatus(status); setIsValidating(false); }; validate(); - }, [props?.url, props?.metadataHash]); + }, [props?.url]); return ( ); }; diff --git a/govtool/frontend/src/components/organisms/ValidatedGovernanceVotedOnCard.tsx b/govtool/frontend/src/components/organisms/ValidatedGovernanceVotedOnCard.tsx index e5581fd34..db8eb3bce 100644 --- a/govtool/frontend/src/components/organisms/ValidatedGovernanceVotedOnCard.tsx +++ b/govtool/frontend/src/components/organisms/ValidatedGovernanceVotedOnCard.tsx @@ -1,4 +1,4 @@ -import { useState, useRef, useEffect } from "react"; +import { useState, useEffect } from "react"; import { useValidateMutation } from "@/hooks/mutations"; import { MetadataStandard, ProposalData, VotedProposal } from "@/models"; @@ -13,12 +13,16 @@ export const ValidatedGovernanceVotedOnCard = ({ inProgress, }: Props) => { const [isValidating, setIsValidating] = useState(false); - const metadataStatus = useRef(); + const [metadataStatus, setMetadataStatus] = useState< + MetadataValidationStatus | undefined + >(); const { validateMetadata } = useValidateMutation(); const [extendedVotedProposal, setExtendedVotedProposal] = useState(votedProposal); useEffect(() => { + if (!votedProposal.proposal.url) return; + const validate = async () => { setIsValidating(true); @@ -28,8 +32,6 @@ export const ValidatedGovernanceVotedOnCard = ({ hash: votedProposal.proposal.metadataHash, }); - metadataStatus.current = status; - if (metadata) { setExtendedVotedProposal((prevProposal) => ({ ...(prevProposal || {}), @@ -42,17 +44,18 @@ export const ValidatedGovernanceVotedOnCard = ({ }, })); } + setMetadataStatus(status); setIsValidating(false); }; validate(); - }, []); + }, [votedProposal.proposal.url]); return ( ); }; diff --git a/govtool/frontend/src/hooks/mutations/metadataValidation/useValidateMutation.ts b/govtool/frontend/src/hooks/mutations/metadataValidation/useValidateMutation.ts index 74f5f0b06..313522fc1 100644 --- a/govtool/frontend/src/hooks/mutations/metadataValidation/useValidateMutation.ts +++ b/govtool/frontend/src/hooks/mutations/metadataValidation/useValidateMutation.ts @@ -3,6 +3,7 @@ import { useMutation, useQueryClient } from "react-query"; import { postValidate } from "@services"; import { MUTATION_KEYS } from "@consts"; import { MetadataValidationDTO } from "@models"; +import { useMemo } from "react"; export const useValidateMutation = () => { const queryClient = useQueryClient(); @@ -17,12 +18,16 @@ export const useValidateMutation = () => { queryClient.fetchQuery({ queryKey: [MUTATION_KEYS.postValidateKey, body.hash, body.url], queryFn: () => postValidate(body), - cacheTime: 20 * 1000, // 20 seconds }); - return { - validateMetadata, - validationStatus: data, - isValidating: isLoading, - }; + const contextValue = useMemo( + () => ({ + validateMetadata, + validationStatus: data, + isValidating: isLoading, + }), + [data, isLoading], + ); + + return contextValue; }; diff --git a/govtool/frontend/src/hooks/queries/useGetDRepVotingPowerList.ts b/govtool/frontend/src/hooks/queries/useGetDRepVotingPowerList.ts index c1c180f8a..26fb475a3 100644 --- a/govtool/frontend/src/hooks/queries/useGetDRepVotingPowerList.ts +++ b/govtool/frontend/src/hooks/queries/useGetDRepVotingPowerList.ts @@ -1,3 +1,4 @@ +import { useMemo } from "react"; import { useQuery, useQueryClient } from "react-query"; import { getDRepVotingPowerList } from "@/services"; import { QUERY_KEYS } from "@/consts"; @@ -22,11 +23,16 @@ export const useGetDRepVotingPowerList = () => { queryFn: () => getDRepVotingPowerList(identifiers), }); - return { - dRepVotingPowerList, - fetchDRepVotingPowerList, - isError, - error, - isLoading, - }; + const contextValue = useMemo( + () => ({ + dRepVotingPowerList, + fetchDRepVotingPowerList, + isError, + error, + isLoading, + }), + [dRepVotingPowerList, fetchDRepVotingPowerList], + ); + + return contextValue; }; diff --git a/govtool/frontend/src/pages/GovernanceActionDetails.tsx b/govtool/frontend/src/pages/GovernanceActionDetails.tsx index c35bfa16d..c82e6a033 100644 --- a/govtool/frontend/src/pages/GovernanceActionDetails.tsx +++ b/govtool/frontend/src/pages/GovernanceActionDetails.tsx @@ -1,4 +1,4 @@ -import { useEffect, useRef, useState } from "react"; +import { useEffect, useState } from "react"; import { useNavigate, useLocation, @@ -51,22 +51,33 @@ export const GovernanceActionDetails = () => { fullProposalId ?? "", !state?.proposal, ); + // TODO: Refactor this mess with proposals and metadata validation + // once authors are existing in all CIP-108 metadata const [extendedProposal, setExtendedProposal] = useState( (data ?? state)?.proposal as ProposalData, ); - const metadataStatus = useRef(); + + useEffect(() => { + if (data?.proposal) { + setExtendedProposal(data.proposal); + } + }, [data?.proposal]); + + const [metadataStatus, setMetadataStatus] = useState< + MetadataValidationStatus | undefined + >(); const { validateMetadata } = useValidateMutation(); useEffect(() => { + if (!extendedProposal?.url) return; + const validate = async () => { const { status, metadata } = await validateMetadata({ standard: MetadataStandard.CIP108, - url: extendedProposal?.url ?? "", + url: extendedProposal?.url, hash: extendedProposal?.metadataHash ?? "", }); - metadataStatus.current = status; - if (metadata) { setExtendedProposal((prevProposal) => ({ ...(prevProposal || {}), @@ -76,9 +87,10 @@ export const GovernanceActionDetails = () => { >), })); } + setMetadataStatus(status); }; validate(); - }, [extendedProposal?.url, extendedProposal?.metadataHash]); + }, [extendedProposal?.url]); useEffect(() => { const isProposalNotFound = @@ -133,7 +145,7 @@ export const GovernanceActionDetails = () => { elementOne={t("govActions.title")} elementOnePath={PATHS.governanceActions} elementTwo={extendedProposal?.title ?? ""} - isDataMissing={metadataStatus?.current ?? null} + isDataMissing={metadataStatus ?? null} /> { ) : extendedProposal ? ( diff --git a/govtool/frontend/yarn.lock b/govtool/frontend/yarn.lock index dc1af3026..ea257d680 100644 --- a/govtool/frontend/yarn.lock +++ b/govtool/frontend/yarn.lock @@ -38,7 +38,7 @@ css-tree "^2.3.1" is-potential-custom-element-name "^1.0.1" -"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.4", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.16.0", "@babel/code-frame@^7.25.9", "@babel/code-frame@^7.26.0", "@babel/code-frame@^7.8.3": +"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.4", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.16.0", "@babel/code-frame@^7.25.9", "@babel/code-frame@^7.26.0", "@babel/code-frame@^7.26.2", "@babel/code-frame@^7.8.3": version "7.26.2" resolved "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz" integrity sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ== @@ -248,19 +248,19 @@ "@babel/types" "^7.25.9" "@babel/helpers@^7.26.0": - version "7.26.0" - resolved "https://registry.npmjs.org/@babel/helpers/-/helpers-7.26.0.tgz" - integrity sha512-tbhNuIxNcVb21pInl3ZSjksLCvgdZy9KwJ8brv993QtIVKJBBkYXz4q4ZbAv31GdnC+R90np23L5FbEBlthAEw== + version "7.27.0" + resolved "https://registry.npmjs.org/@babel/helpers/-/helpers-7.27.0.tgz" + integrity sha512-U5eyP/CTFPuNE3qk+WZMxFkp/4zUzdceQlfzf7DdGdhp+Fezd7HD+i8Y24ZuTMKX3wQBld449jijbGq6OdGNQg== dependencies: - "@babel/template" "^7.25.9" - "@babel/types" "^7.26.0" + "@babel/template" "^7.27.0" + "@babel/types" "^7.27.0" -"@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.20.7", "@babel/parser@^7.23.9", "@babel/parser@^7.25.4", "@babel/parser@^7.25.9", "@babel/parser@^7.26.0", "@babel/parser@^7.26.2": - version "7.26.2" - resolved "https://registry.npmjs.org/@babel/parser/-/parser-7.26.2.tgz" - integrity sha512-DWMCZH9WA4Maitz2q21SRKHo9QXZxkDsbNZoVD62gusNtNBBqDg9i7uOhASfTfIGNzW+O+r7+jAlM8dwphcJKQ== +"@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.20.7", "@babel/parser@^7.23.9", "@babel/parser@^7.25.4", "@babel/parser@^7.25.9", "@babel/parser@^7.26.0", "@babel/parser@^7.26.2", "@babel/parser@^7.27.0": + version "7.27.0" + resolved "https://registry.npmjs.org/@babel/parser/-/parser-7.27.0.tgz" + integrity sha512-iaepho73/2Pz7w2eMS0Q5f83+0RKI7i4xmiYeBmDzfRVbQtTOG7Ts0S4HzJVsTMGI9keU8rNfuZr8DKfSt7Yyg== dependencies: - "@babel/types" "^7.26.0" + "@babel/types" "^7.27.0" "@babel/plugin-bugfix-firefox-class-in-computed-class-key@^7.25.9": version "7.25.9" @@ -1072,20 +1072,20 @@ "@babel/plugin-transform-typescript" "^7.25.9" "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.5", "@babel/runtime@^7.16.3", "@babel/runtime@^7.17.8", "@babel/runtime@^7.18.3", "@babel/runtime@^7.21.0", "@babel/runtime@^7.23.2", "@babel/runtime@^7.23.8", "@babel/runtime@^7.23.9", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.2", "@babel/runtime@^7.7.2", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7": - version "7.26.0" - resolved "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.0.tgz" - integrity sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw== + version "7.27.0" + resolved "https://registry.npmjs.org/@babel/runtime/-/runtime-7.27.0.tgz" + integrity sha512-VtPOkrdPHZsKc/clNqyi9WUA8TINkZ4cGk63UUE3u4pmB2k+ZMQRDuIOagv8UVd6j7k0T3+RRIb7beKTebNbcw== dependencies: regenerator-runtime "^0.14.0" -"@babel/template@^7.22.5", "@babel/template@^7.25.9", "@babel/template@^7.3.3": - version "7.25.9" - resolved "https://registry.npmjs.org/@babel/template/-/template-7.25.9.tgz" - integrity sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg== +"@babel/template@^7.22.5", "@babel/template@^7.25.9", "@babel/template@^7.27.0", "@babel/template@^7.3.3": + version "7.27.0" + resolved "https://registry.npmjs.org/@babel/template/-/template-7.27.0.tgz" + integrity sha512-2ncevenBqXI6qRMukPlXwHKHchC7RyMuu4xv5JBXRfOGVcTy1mXCD12qrp7Jsoxll1EV3+9sE4GugBVRjT2jFA== dependencies: - "@babel/code-frame" "^7.25.9" - "@babel/parser" "^7.25.9" - "@babel/types" "^7.25.9" + "@babel/code-frame" "^7.26.2" + "@babel/parser" "^7.27.0" + "@babel/types" "^7.27.0" "@babel/traverse@^7.18.9", "@babel/traverse@^7.25.9", "@babel/traverse@^7.7.2": version "7.25.9" @@ -1100,10 +1100,10 @@ debug "^4.3.1" globals "^11.1.0" -"@babel/types@^7.0.0", "@babel/types@^7.12.6", "@babel/types@^7.18.9", "@babel/types@^7.20.7", "@babel/types@^7.22.5", "@babel/types@^7.25.4", "@babel/types@^7.25.9", "@babel/types@^7.26.0", "@babel/types@^7.3.3", "@babel/types@^7.4.4": - version "7.26.0" - resolved "https://registry.npmjs.org/@babel/types/-/types-7.26.0.tgz" - integrity sha512-Z/yiTPj+lDVnF7lWeKCIJzaIkI0vYO87dMpZ4bg4TDrFe4XXLFWL1TbXU27gBP3QccxV9mZICCrnjnYlJjXHOA== +"@babel/types@^7.0.0", "@babel/types@^7.12.6", "@babel/types@^7.18.9", "@babel/types@^7.20.7", "@babel/types@^7.22.5", "@babel/types@^7.25.4", "@babel/types@^7.25.9", "@babel/types@^7.26.0", "@babel/types@^7.27.0", "@babel/types@^7.3.3", "@babel/types@^7.4.4": + version "7.27.0" + resolved "https://registry.npmjs.org/@babel/types/-/types-7.27.0.tgz" + integrity sha512-H45s8fVLYjbhFH62dIJ3WtmJ6RSPt/3DRO0ZcT2SUiYiQyz3BLVb9ADEnLl91m74aQPS3AzzeajZHYOalWe3bg== dependencies: "@babel/helper-string-parser" "^7.25.9" "@babel/helper-validator-identifier" "^7.25.9" @@ -1377,11 +1377,131 @@ resolved "https://registry.npmjs.org/@emurgo/cardano-serialization-lib-asmjs/-/cardano-serialization-lib-asmjs-14.1.1.tgz" integrity sha512-Q2HVpPRt417Quxv3qagGWbkJQU8SiQCl1K/344ZtQMwsLoqTfRlCNzmSWMBN7jyBxbtKoh+vdbSiLqwG1NAjYg== +"@esbuild/aix-ppc64@0.25.0": + version "0.25.0" + resolved "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.0.tgz" + integrity sha512-O7vun9Sf8DFjH2UtqK8Ku3LkquL9SZL8OLY1T5NZkA34+wG3OQF7cl4Ql8vdNzM6fzBbYfLaiRLIOZ+2FOCgBQ== + +"@esbuild/android-arm@0.25.0": + version "0.25.0" + resolved "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.0.tgz" + integrity sha512-PTyWCYYiU0+1eJKmw21lWtC+d08JDZPQ5g+kFyxP0V+es6VPPSUhM6zk8iImp2jbV6GwjX4pap0JFbUQN65X1g== + +"@esbuild/android-arm64@0.25.0": + version "0.25.0" + resolved "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.0.tgz" + integrity sha512-grvv8WncGjDSyUBjN9yHXNt+cq0snxXbDxy5pJtzMKGmmpPxeAmAhWxXI+01lU5rwZomDgD3kJwulEnhTRUd6g== + +"@esbuild/android-x64@0.25.0": + version "0.25.0" + resolved "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.0.tgz" + integrity sha512-m/ix7SfKG5buCnxasr52+LI78SQ+wgdENi9CqyCXwjVR2X4Jkz+BpC3le3AoBPYTC9NHklwngVXvbJ9/Akhrfg== + "@esbuild/darwin-arm64@0.25.0": version "0.25.0" resolved "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.0.tgz" integrity sha512-mVwdUb5SRkPayVadIOI78K7aAnPamoeFR2bT5nszFUZ9P8UpK4ratOdYbZZXYSqPKMHfS1wdHCJk1P1EZpRdvw== +"@esbuild/darwin-x64@0.25.0": + version "0.25.0" + resolved "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.0.tgz" + integrity sha512-DgDaYsPWFTS4S3nWpFcMn/33ZZwAAeAFKNHNa1QN0rI4pUjgqf0f7ONmXf6d22tqTY+H9FNdgeaAa+YIFUn2Rg== + +"@esbuild/freebsd-arm64@0.25.0": + version "0.25.0" + resolved "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.0.tgz" + integrity sha512-VN4ocxy6dxefN1MepBx/iD1dH5K8qNtNe227I0mnTRjry8tj5MRk4zprLEdG8WPyAPb93/e4pSgi1SoHdgOa4w== + +"@esbuild/freebsd-x64@0.25.0": + version "0.25.0" + resolved "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.0.tgz" + integrity sha512-mrSgt7lCh07FY+hDD1TxiTyIHyttn6vnjesnPoVDNmDfOmggTLXRv8Id5fNZey1gl/V2dyVK1VXXqVsQIiAk+A== + +"@esbuild/linux-arm@0.25.0": + version "0.25.0" + resolved "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.0.tgz" + integrity sha512-vkB3IYj2IDo3g9xX7HqhPYxVkNQe8qTK55fraQyTzTX/fxaDtXiEnavv9geOsonh2Fd2RMB+i5cbhu2zMNWJwg== + +"@esbuild/linux-arm64@0.25.0": + version "0.25.0" + resolved "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.0.tgz" + integrity sha512-9QAQjTWNDM/Vk2bgBl17yWuZxZNQIF0OUUuPZRKoDtqF2k4EtYbpyiG5/Dk7nqeK6kIJWPYldkOcBqjXjrUlmg== + +"@esbuild/linux-ia32@0.25.0": + version "0.25.0" + resolved "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.0.tgz" + integrity sha512-43ET5bHbphBegyeqLb7I1eYn2P/JYGNmzzdidq/w0T8E2SsYL1U6un2NFROFRg1JZLTzdCoRomg8Rvf9M6W6Gg== + +"@esbuild/linux-loong64@0.25.0": + version "0.25.0" + resolved "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.0.tgz" + integrity sha512-fC95c/xyNFueMhClxJmeRIj2yrSMdDfmqJnyOY4ZqsALkDrrKJfIg5NTMSzVBr5YW1jf+l7/cndBfP3MSDpoHw== + +"@esbuild/linux-mips64el@0.25.0": + version "0.25.0" + resolved "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.0.tgz" + integrity sha512-nkAMFju7KDW73T1DdH7glcyIptm95a7Le8irTQNO/qtkoyypZAnjchQgooFUDQhNAy4iu08N79W4T4pMBwhPwQ== + +"@esbuild/linux-ppc64@0.25.0": + version "0.25.0" + resolved "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.0.tgz" + integrity sha512-NhyOejdhRGS8Iwv+KKR2zTq2PpysF9XqY+Zk77vQHqNbo/PwZCzB5/h7VGuREZm1fixhs4Q/qWRSi5zmAiO4Fw== + +"@esbuild/linux-riscv64@0.25.0": + version "0.25.0" + resolved "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.0.tgz" + integrity sha512-5S/rbP5OY+GHLC5qXp1y/Mx//e92L1YDqkiBbO9TQOvuFXM+iDqUNG5XopAnXoRH3FjIUDkeGcY1cgNvnXp/kA== + +"@esbuild/linux-s390x@0.25.0": + version "0.25.0" + resolved "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.0.tgz" + integrity sha512-XM2BFsEBz0Fw37V0zU4CXfcfuACMrppsMFKdYY2WuTS3yi8O1nFOhil/xhKTmE1nPmVyvQJjJivgDT+xh8pXJA== + +"@esbuild/linux-x64@0.25.0": + version "0.25.0" + resolved "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.0.tgz" + integrity sha512-9yl91rHw/cpwMCNytUDxwj2XjFpxML0y9HAOH9pNVQDpQrBxHy01Dx+vaMu0N1CKa/RzBD2hB4u//nfc+Sd3Cw== + +"@esbuild/netbsd-arm64@0.25.0": + version "0.25.0" + resolved "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.0.tgz" + integrity sha512-RuG4PSMPFfrkH6UwCAqBzauBWTygTvb1nxWasEJooGSJ/NwRw7b2HOwyRTQIU97Hq37l3npXoZGYMy3b3xYvPw== + +"@esbuild/netbsd-x64@0.25.0": + version "0.25.0" + resolved "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.0.tgz" + integrity sha512-jl+qisSB5jk01N5f7sPCsBENCOlPiS/xptD5yxOx2oqQfyourJwIKLRA2yqWdifj3owQZCL2sn6o08dBzZGQzA== + +"@esbuild/openbsd-arm64@0.25.0": + version "0.25.0" + resolved "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.0.tgz" + integrity sha512-21sUNbq2r84YE+SJDfaQRvdgznTD8Xc0oc3p3iW/a1EVWeNj/SdUCbm5U0itZPQYRuRTW20fPMWMpcrciH2EJw== + +"@esbuild/openbsd-x64@0.25.0": + version "0.25.0" + resolved "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.0.tgz" + integrity sha512-2gwwriSMPcCFRlPlKx3zLQhfN/2WjJ2NSlg5TKLQOJdV0mSxIcYNTMhk3H3ulL/cak+Xj0lY1Ym9ysDV1igceg== + +"@esbuild/sunos-x64@0.25.0": + version "0.25.0" + resolved "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.0.tgz" + integrity sha512-bxI7ThgLzPrPz484/S9jLlvUAHYMzy6I0XiU1ZMeAEOBcS0VePBFxh1JjTQt3Xiat5b6Oh4x7UC7IwKQKIJRIg== + +"@esbuild/win32-arm64@0.25.0": + version "0.25.0" + resolved "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.0.tgz" + integrity sha512-ZUAc2YK6JW89xTbXvftxdnYy3m4iHIkDtK3CLce8wg8M2L+YZhIvO1DKpxrd0Yr59AeNNkTiic9YLf6FTtXWMw== + +"@esbuild/win32-ia32@0.25.0": + version "0.25.0" + resolved "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.0.tgz" + integrity sha512-eSNxISBu8XweVEWG31/JzjkIGbGIJN/TrRoiSVZwZ6pkC6VX4Im/WV2cz559/TXLcYbcrDN8JtKgd9DJVIo8GA== + +"@esbuild/win32-x64@0.25.0": + version "0.25.0" + resolved "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.0.tgz" + integrity sha512-ZENoHJBxA20C2zFzh6AI4fT6RraMzjYw4xKWemRTRmRVtN9c5DcH9r/f2ihEkMjOW5eGgrwCslG/+Y/3bL+DHQ== + "@eslint-community/eslint-utils@^4.2.0", "@eslint-community/eslint-utils@^4.4.0": version "4.4.1" resolved "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.1.tgz" @@ -2173,11 +2293,71 @@ resolved "https://registry.npmjs.org/@open-draft/until/-/until-2.1.0.tgz" integrity sha512-U69T3ItWHvLwGg5eJ0n3I62nWuE6ilHlmz7zM0npLBRvPRd7e6NYmg54vvRtP5mZG7kZqZCFVdsTWo7BPtBujg== +"@parcel/watcher-android-arm64@2.5.0": + version "2.5.0" + resolved "https://registry.npmjs.org/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.5.0.tgz" + integrity sha512-qlX4eS28bUcQCdribHkg/herLe+0A9RyYC+mm2PXpncit8z5b3nSqGVzMNR3CmtAOgRutiZ02eIJJgP/b1iEFQ== + "@parcel/watcher-darwin-arm64@2.5.0": version "2.5.0" resolved "https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.5.0.tgz" integrity sha512-hyZ3TANnzGfLpRA2s/4U1kbw2ZI4qGxaRJbBH2DCSREFfubMswheh8TeiC1sGZ3z2jUf3s37P0BBlrD3sjVTUw== +"@parcel/watcher-darwin-x64@2.5.0": + version "2.5.0" + resolved "https://registry.npmjs.org/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.5.0.tgz" + integrity sha512-9rhlwd78saKf18fT869/poydQK8YqlU26TMiNg7AIu7eBp9adqbJZqmdFOsbZ5cnLp5XvRo9wcFmNHgHdWaGYA== + +"@parcel/watcher-freebsd-x64@2.5.0": + version "2.5.0" + resolved "https://registry.npmjs.org/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.5.0.tgz" + integrity sha512-syvfhZzyM8kErg3VF0xpV8dixJ+RzbUaaGaeb7uDuz0D3FK97/mZ5AJQ3XNnDsXX7KkFNtyQyFrXZzQIcN49Tw== + +"@parcel/watcher-linux-arm-glibc@2.5.0": + version "2.5.0" + resolved "https://registry.npmjs.org/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.5.0.tgz" + integrity sha512-0VQY1K35DQET3dVYWpOaPFecqOT9dbuCfzjxoQyif1Wc574t3kOSkKevULddcR9znz1TcklCE7Ht6NIxjvTqLA== + +"@parcel/watcher-linux-arm-musl@2.5.0": + version "2.5.0" + resolved "https://registry.npmjs.org/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.5.0.tgz" + integrity sha512-6uHywSIzz8+vi2lAzFeltnYbdHsDm3iIB57d4g5oaB9vKwjb6N6dRIgZMujw4nm5r6v9/BQH0noq6DzHrqr2pA== + +"@parcel/watcher-linux-arm64-glibc@2.5.0": + version "2.5.0" + resolved "https://registry.npmjs.org/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.5.0.tgz" + integrity sha512-BfNjXwZKxBy4WibDb/LDCriWSKLz+jJRL3cM/DllnHH5QUyoiUNEp3GmL80ZqxeumoADfCCP19+qiYiC8gUBjA== + +"@parcel/watcher-linux-arm64-musl@2.5.0": + version "2.5.0" + resolved "https://registry.npmjs.org/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.5.0.tgz" + integrity sha512-S1qARKOphxfiBEkwLUbHjCY9BWPdWnW9j7f7Hb2jPplu8UZ3nes7zpPOW9bkLbHRvWM0WDTsjdOTUgW0xLBN1Q== + +"@parcel/watcher-linux-x64-glibc@2.5.0": + version "2.5.0" + resolved "https://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.5.0.tgz" + integrity sha512-d9AOkusyXARkFD66S6zlGXyzx5RvY+chTP9Jp0ypSTC9d4lzyRs9ovGf/80VCxjKddcUvnsGwCHWuF2EoPgWjw== + +"@parcel/watcher-linux-x64-musl@2.5.0": + version "2.5.0" + resolved "https://registry.npmjs.org/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.5.0.tgz" + integrity sha512-iqOC+GoTDoFyk/VYSFHwjHhYrk8bljW6zOhPuhi5t9ulqiYq1togGJB5e3PwYVFFfeVgc6pbz3JdQyDoBszVaA== + +"@parcel/watcher-win32-arm64@2.5.0": + version "2.5.0" + resolved "https://registry.npmjs.org/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.5.0.tgz" + integrity sha512-twtft1d+JRNkM5YbmexfcH/N4znDtjgysFaV9zvZmmJezQsKpkfLYJ+JFV3uygugK6AtIM2oADPkB2AdhBrNig== + +"@parcel/watcher-win32-ia32@2.5.0": + version "2.5.0" + resolved "https://registry.npmjs.org/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.5.0.tgz" + integrity sha512-+rgpsNRKwo8A53elqbbHXdOMtY/tAtTzManTWShB5Kk54N8Q9mzNWV7tV+IbGueCbcj826MfWGU3mprWtuf1TA== + +"@parcel/watcher-win32-x64@2.5.0": + version "2.5.0" + resolved "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.0.tgz" + integrity sha512-lPrxve92zEHdgeff3aiu4gDOIt4u7sJYha6wbdEZDCDUhtjTsOMiaJzG5lMY4GkWH8p0fMmO2Ppq5G5XXG+DQw== + "@parcel/watcher@^2.4.1": version "2.5.0" resolved "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.5.0.tgz" @@ -2289,11 +2469,106 @@ estree-walker "^2.0.2" picomatch "^4.0.2" +"@rollup/rollup-android-arm-eabi@4.34.9": + version "4.34.9" + resolved "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.34.9.tgz" + integrity sha512-qZdlImWXur0CFakn2BJ2znJOdqYZKiedEPEVNTBrpfPjc/YuTGcaYZcdmNFTkUj3DU0ZM/AElcM8Ybww3xVLzA== + +"@rollup/rollup-android-arm64@4.34.9": + version "4.34.9" + resolved "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.34.9.tgz" + integrity sha512-4KW7P53h6HtJf5Y608T1ISKvNIYLWRKMvfnG0c44M6In4DQVU58HZFEVhWINDZKp7FZps98G3gxwC1sb0wXUUg== + "@rollup/rollup-darwin-arm64@4.34.9": version "4.34.9" resolved "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.34.9.tgz" integrity sha512-0CY3/K54slrzLDjOA7TOjN1NuLKERBgk9nY5V34mhmuu673YNb+7ghaDUs6N0ujXR7fz5XaS5Aa6d2TNxZd0OQ== +"@rollup/rollup-darwin-x64@4.34.9": + version "4.34.9" + resolved "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.34.9.tgz" + integrity sha512-eOojSEAi/acnsJVYRxnMkPFqcxSMFfrw7r2iD9Q32SGkb/Q9FpUY1UlAu1DH9T7j++gZ0lHjnm4OyH2vCI7l7Q== + +"@rollup/rollup-freebsd-arm64@4.34.9": + version "4.34.9" + resolved "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.34.9.tgz" + integrity sha512-2lzjQPJbN5UnHm7bHIUKFMulGTQwdvOkouJDpPysJS+QFBGDJqcfh+CxxtG23Ik/9tEvnebQiylYoazFMAgrYw== + +"@rollup/rollup-freebsd-x64@4.34.9": + version "4.34.9" + resolved "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.34.9.tgz" + integrity sha512-SLl0hi2Ah2H7xQYd6Qaiu01kFPzQ+hqvdYSoOtHYg/zCIFs6t8sV95kaoqjzjFwuYQLtOI0RZre/Ke0nPaQV+g== + +"@rollup/rollup-linux-arm-gnueabihf@4.34.9": + version "4.34.9" + resolved "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.34.9.tgz" + integrity sha512-88I+D3TeKItrw+Y/2ud4Tw0+3CxQ2kLgu3QvrogZ0OfkmX/DEppehus7L3TS2Q4lpB+hYyxhkQiYPJ6Mf5/dPg== + +"@rollup/rollup-linux-arm-musleabihf@4.34.9": + version "4.34.9" + resolved "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.34.9.tgz" + integrity sha512-3qyfWljSFHi9zH0KgtEPG4cBXHDFhwD8kwg6xLfHQ0IWuH9crp005GfoUUh/6w9/FWGBwEHg3lxK1iHRN1MFlA== + +"@rollup/rollup-linux-arm64-gnu@4.34.9": + version "4.34.9" + resolved "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.34.9.tgz" + integrity sha512-6TZjPHjKZUQKmVKMUowF3ewHxctrRR09eYyvT5eFv8w/fXarEra83A2mHTVJLA5xU91aCNOUnM+DWFMSbQ0Nxw== + +"@rollup/rollup-linux-arm64-musl@4.12.0": + version "4.12.0" + resolved "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.12.0.tgz" + integrity sha512-eTvzUS3hhhlgeAv6bfigekzWZjaEX9xP9HhxB0Dvrdbkk5w/b+1Sxct2ZuDxNJKzsRStSq1EaEkVSEe7A7ipgQ== + +"@rollup/rollup-linux-arm64-musl@4.34.9": + version "4.34.9" + resolved "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.34.9.tgz" + integrity sha512-LD2fytxZJZ6xzOKnMbIpgzFOuIKlxVOpiMAXawsAZ2mHBPEYOnLRK5TTEsID6z4eM23DuO88X0Tq1mErHMVq0A== + +"@rollup/rollup-linux-loongarch64-gnu@4.34.9": + version "4.34.9" + resolved "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.34.9.tgz" + integrity sha512-dRAgTfDsn0TE0HI6cmo13hemKpVHOEyeciGtvlBTkpx/F65kTvShtY/EVyZEIfxFkV5JJTuQ9tP5HGBS0hfxIg== + +"@rollup/rollup-linux-powerpc64le-gnu@4.34.9": + version "4.34.9" + resolved "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.34.9.tgz" + integrity sha512-PHcNOAEhkoMSQtMf+rJofwisZqaU8iQ8EaSps58f5HYll9EAY5BSErCZ8qBDMVbq88h4UxaNPlbrKqfWP8RfJA== + +"@rollup/rollup-linux-riscv64-gnu@4.34.9": + version "4.34.9" + resolved "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.34.9.tgz" + integrity sha512-Z2i0Uy5G96KBYKjeQFKbbsB54xFOL5/y1P5wNBsbXB8yE+At3oh0DVMjQVzCJRJSfReiB2tX8T6HUFZ2k8iaKg== + +"@rollup/rollup-linux-s390x-gnu@4.34.9": + version "4.34.9" + resolved "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.34.9.tgz" + integrity sha512-U+5SwTMoeYXoDzJX5dhDTxRltSrIax8KWwfaaYcynuJw8mT33W7oOgz0a+AaXtGuvhzTr2tVKh5UO8GVANTxyQ== + +"@rollup/rollup-linux-x64-gnu@4.34.9": + version "4.34.9" + resolved "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.34.9.tgz" + integrity sha512-FwBHNSOjUTQLP4MG7y6rR6qbGw4MFeQnIBrMe161QGaQoBQLqSUEKlHIiVgF3g/mb3lxlxzJOpIBhaP+C+KP2A== + +"@rollup/rollup-linux-x64-musl@4.34.9": + version "4.34.9" + resolved "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.34.9.tgz" + integrity sha512-cYRpV4650z2I3/s6+5/LONkjIz8MBeqrk+vPXV10ORBnshpn8S32bPqQ2Utv39jCiDcO2eJTuSlPXpnvmaIgRA== + +"@rollup/rollup-win32-arm64-msvc@4.34.9": + version "4.34.9" + resolved "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.34.9.tgz" + integrity sha512-z4mQK9dAN6byRA/vsSgQiPeuO63wdiDxZ9yg9iyX2QTzKuQM7T4xlBoeUP/J8uiFkqxkcWndWi+W7bXdPbt27Q== + +"@rollup/rollup-win32-ia32-msvc@4.34.9": + version "4.34.9" + resolved "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.34.9.tgz" + integrity sha512-KB48mPtaoHy1AwDNkAJfHXvHp24H0ryZog28spEs0V48l3H1fr4i37tiyHsgKZJnCmvxsbATdZGBpbmxTE3a9w== + +"@rollup/rollup-win32-x64-msvc@4.34.9": + version "4.34.9" + resolved "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.34.9.tgz" + integrity sha512-AyleYRPU7+rgkMWbEh71fQlrzRfeP6SyMnRf9XX4fCdDPAJumdSBqYEcWPMzVQ4ScAl7E4oFfK0GUVn77xSwbw== + "@rtsao/scc@^1.1.0": version "1.1.0" resolved "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz" @@ -2900,6 +3175,51 @@ resolved "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.9.3.tgz" integrity sha512-hGfl/KTic/QY4tB9DkTbNuxy5cV4IeejpPD4zo+Lzt4iLlDWIeANL4Fkg67FiVceNJboqg48CUX+APhDHO5G1w== +"@swc/core-darwin-x64@1.9.3": + version "1.9.3" + resolved "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.9.3.tgz" + integrity sha512-IaRq05ZLdtgF5h9CzlcgaNHyg4VXuiStnOFpfNEMuI5fm5afP2S0FHq8WdakUz5WppsbddTdplL+vpeApt/WCQ== + +"@swc/core-linux-arm-gnueabihf@1.9.3": + version "1.9.3" + resolved "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.9.3.tgz" + integrity sha512-Pbwe7xYprj/nEnZrNBvZfjnTxlBIcfApAGdz2EROhjpPj+FBqBa3wOogqbsuGGBdCphf8S+KPprL1z+oDWkmSQ== + +"@swc/core-linux-arm64-gnu@1.9.3": + version "1.9.3" + resolved "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.9.3.tgz" + integrity sha512-AQ5JZiwNGVV/2K2TVulg0mw/3LYfqpjZO6jDPtR2evNbk9Yt57YsVzS+3vHSlUBQDRV9/jqMuZYVU3P13xrk+g== + +"@swc/core-linux-arm64-musl@1.9.3": + version "1.9.3" + resolved "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.9.3.tgz" + integrity sha512-tzVH480RY6RbMl/QRgh5HK3zn1ZTFsThuxDGo6Iuk1MdwIbdFYUY034heWUTI4u3Db97ArKh0hNL0xhO3+PZdg== + +"@swc/core-linux-x64-gnu@1.9.3": + version "1.9.3" + resolved "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.9.3.tgz" + integrity sha512-ivXXBRDXDc9k4cdv10R21ccBmGebVOwKXT/UdH1PhxUn9m/h8erAWjz5pcELwjiMf27WokqPgaWVfaclDbgE+w== + +"@swc/core-linux-x64-musl@1.9.3": + version "1.9.3" + resolved "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.9.3.tgz" + integrity sha512-ILsGMgfnOz1HwdDz+ZgEuomIwkP1PHT6maigZxaCIuC6OPEhKE8uYna22uU63XvYcLQvZYDzpR3ms47WQPuNEg== + +"@swc/core-win32-arm64-msvc@1.9.3": + version "1.9.3" + resolved "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.9.3.tgz" + integrity sha512-e+XmltDVIHieUnNJHtspn6B+PCcFOMYXNJB1GqoCcyinkEIQNwC8KtWgMqUucUbEWJkPc35NHy9k8aCXRmw9Kg== + +"@swc/core-win32-ia32-msvc@1.9.3": + version "1.9.3" + resolved "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.9.3.tgz" + integrity sha512-rqpzNfpAooSL4UfQnHhkW8aL+oyjqJniDP0qwZfGnjDoJSbtPysHg2LpcOBEdSnEH+uIZq6J96qf0ZFD8AGfXA== + +"@swc/core-win32-x64-msvc@1.9.3": + version "1.9.3" + resolved "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.9.3.tgz" + integrity sha512-3YJJLQ5suIEHEKc1GHtqVq475guiyqisKSoUnoaRtxkDaW5g1yvPt9IoSLOe2mRs7+FFhGGU693RsBUSwOXSdQ== + "@swc/core@*", "@swc/core@^1.5.22", "@swc/core@^1.7.26": version "1.9.3" resolved "https://registry.npmjs.org/@swc/core/-/core-1.9.3.tgz" @@ -12842,7 +13162,7 @@ rollup-plugin-terser@^7.0.0: optionalDependencies: fsevents "~2.3.2" -rollup@^1.20.0||^2.0.0||^3.0.0||^4.0.0, rollup@^4.30.1: +rollup@^1.20.0||^2.0.0||^3.0.0||^4.0.0, rollup@^4.34.9: version "4.34.9" resolved "https://registry.npmjs.org/rollup/-/rollup-4.34.9.tgz" integrity sha512-nF5XYqWWp9hx/LrpC8sZvvvmq0TeTjQgaZHYmAgwysT9nh8sWnZhBnM8ZyVbbJFIQBLwHDNoMqsBZBbUo4U8sQ== @@ -14685,13 +15005,16 @@ vite-plugin-istanbul@^3.0.1: test-exclude "^6.0.0" vite@*, "vite@^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0", "vite@^4 || ^5 || ^6", "vite@^4.0.0 || ^5.0.0 || ^6.0.0", "vite@^5.0.0 || ^6.0.0", vite@^6.0.0, vite@^6.0.3, vite@>=2.0.0: - version "6.2.0" - resolved "https://registry.npmjs.org/vite/-/vite-6.2.0.tgz" - integrity sha512-7dPxoo+WsT/64rDcwoOjk76XHj+TqNTIvHKcuMQ1k4/SeHDaQt5GFAeLYzrimZrMpn/O6DtdI03WUjdxuPM0oQ== + version "6.3.1" + resolved "https://registry.npmjs.org/vite/-/vite-6.3.1.tgz" + integrity sha512-kkzzkqtMESYklo96HKKPE5KKLkC1amlsqt+RjFMlX2AvbRB/0wghap19NdBxxwGZ+h/C6DLCrcEphPIItlGrRQ== dependencies: esbuild "^0.25.0" + fdir "^6.4.3" + picomatch "^4.0.2" postcss "^8.5.3" - rollup "^4.30.1" + rollup "^4.34.9" + tinyglobby "^0.2.12" optionalDependencies: fsevents "~2.3.3"