diff --git a/web-wallet/CHANGELOG.md b/web-wallet/CHANGELOG.md index 19a5ceb94a..f7f7b5cf00 100644 --- a/web-wallet/CHANGELOG.md +++ b/web-wallet/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased +### Added + +- Added gas settings validation on Unstake / Widthdraw Rewards flows [#2000] + ### Changed - Update font-display to swap for custom fonts to improve performance [#2026] @@ -227,6 +231,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 [#1901]: https://github.com/dusk-network/rusk/issues/1901 [#1922]: https://github.com/dusk-network/rusk/issues/1922 [#2026]: https://github.com/dusk-network/rusk/issues/2026 +[#2000]: https://github.com/dusk-network/rusk/issues/2000 diff --git a/web-wallet/src/lib/components/Stake/Stake.svelte b/web-wallet/src/lib/components/Stake/Stake.svelte index 0fdb8922e7..ff665cc767 100644 --- a/web-wallet/src/lib/components/Stake/Stake.svelte +++ b/web-wallet/src/lib/components/Stake/Stake.svelte @@ -263,7 +263,7 @@ ? { action: resetOperation, disabled: false } : undefined} nextButton={{ - disabled: stakeAmount === 0, + disabled: flow === "stake" ? stakeAmount === 0 : !isGasValid, icon: { path: flow === "stake" ? mdiDatabaseOutline : mdiDatabaseArrowDownOutline, diff --git a/web-wallet/src/lib/components/__tests__/Stake.spec.js b/web-wallet/src/lib/components/__tests__/Stake.spec.js index bac2f42555..f8a4b95545 100644 --- a/web-wallet/src/lib/components/__tests__/Stake.spec.js +++ b/web-wallet/src/lib/components/__tests__/Stake.spec.js @@ -1,4 +1,12 @@ -import { afterAll, afterEach, describe, expect, it, vi } from "vitest"; +import { + afterAll, + afterEach, + beforeAll, + describe, + expect, + it, + vi, +} from "vitest"; import { cleanup, fireEvent, render } from "@testing-library/svelte"; import { deductLuxFeeFrom } from "$lib/contracts"; import { createCurrencyFormatter } from "$lib/dusk/currency"; @@ -157,14 +165,16 @@ describe("Stake", () => { }); describe("Stake operation", () => { - vi.useFakeTimers(); - - const expectedExplorerLink = `https://explorer.dusk.network/transactions/transaction?id=${lastTxId}`; + beforeAll(() => { + vi.useFakeTimers(); + }); afterAll(() => { vi.useRealTimers(); }); + const expectedExplorerLink = `https://explorer.dusk.network/transactions/transaction?id=${lastTxId}`; + it("should perform a stake for the desired amount, give a success message and supply a link to see the transaction in the explorer", async () => { const { getByRole, getByText } = render(Stake, baseProps); const amountInput = getByRole("spinbutton"); @@ -235,4 +245,108 @@ describe("Stake", () => { expect(() => getByRole("link", { name: /explorer/i })).toThrow(); }); }); + + describe("Unstake operation", () => { + const expectedExplorerLink = `https://explorer.dusk.network/transactions/transaction?id=${lastTxId}`; + + beforeAll(() => { + vi.useFakeTimers(); + }); + + afterAll(() => { + vi.useRealTimers(); + }); + + it("should perform an ustake, give a success message and supply a link to see the transaction in the explorer", async () => { + /** @type {import("svelte").ComponentProps} */ + const props = { ...baseProps, flow: "unstake" }; + + const { getByRole, getByText } = render(Stake, props); + + await vi.advanceTimersToNextTimerAsync(); + + await fireEvent.click(getByRole("button", { name: "Unstake" })); + + expect(baseProps.execute).toHaveBeenCalledTimes(1); + expect(baseProps.execute).toHaveBeenCalledWith( + baseProps.gasSettings.gasPrice, + baseProps.gasSettings.gasLimit + ); + + const explorerLink = getByRole("link", { name: /explorer/i }); + + expect(getByText("Transaction completed")).toBeInTheDocument(); + expect(explorerLink).toHaveAttribute("target", "_blank"); + expect(explorerLink).toHaveAttribute("href", expectedExplorerLink); + }); + + it("should not allow to unstake, if wrong gas settings are provided", async () => { + /** @type {import("svelte").ComponentProps} */ + const props = { + ...baseProps, + flow: "unstake", + gasSettings: { gasLimit: 29000000090, gasPrice: 1 }, + }; + + const { getByRole } = render(Stake, props); + + await vi.advanceTimersToNextTimerAsync(); + + const unstakeButton = getByRole("button", { name: "Unstake" }); + + expect(unstakeButton).toBeDisabled(); + }); + }); + + describe("Withdraw Rewards operation", () => { + const expectedExplorerLink = `https://explorer.dusk.network/transactions/transaction?id=${lastTxId}`; + + beforeAll(() => { + vi.useFakeTimers(); + }); + + afterAll(() => { + vi.useRealTimers(); + }); + + it("should perform a withdraw rewards, give a success message and supply a link to see the transaction in the explorer", async () => { + /** @type {import("svelte").ComponentProps} */ + const props = { ...baseProps, flow: "withdraw-rewards" }; + + const { getByRole, getByText } = render(Stake, props); + + await vi.advanceTimersToNextTimerAsync(); + + await fireEvent.click(getByRole("button", { name: "Withdraw" })); + + expect(baseProps.execute).toHaveBeenCalledTimes(1); + expect(baseProps.execute).toHaveBeenCalledWith( + baseProps.gasSettings.gasPrice, + baseProps.gasSettings.gasLimit + ); + + const explorerLink = getByRole("link", { name: /explorer/i }); + + expect(getByText("Transaction completed")).toBeInTheDocument(); + expect(explorerLink).toHaveAttribute("target", "_blank"); + expect(explorerLink).toHaveAttribute("href", expectedExplorerLink); + }); + + it("should not allow to unstake, if wrong gas settings are provided", async () => { + /** @type {import("svelte").ComponentProps} */ + const props = { + ...baseProps, + flow: "unstake", + gasSettings: { gasLimit: 29000000090, gasPrice: 1 }, + }; + + const { getByRole } = render(Stake, props); + + await vi.advanceTimersToNextTimerAsync(); + + const unstakeButton = getByRole("button", { name: "Unstake" }); + + expect(unstakeButton).toBeDisabled(); + }); + }); });