From 6e5dc0ea8eedd1fc7a2be08291d10757262b8d93 Mon Sep 17 00:00:00 2001 From: Govard Barkhatov Date: Thu, 12 Sep 2024 14:16:18 +0300 Subject: [PATCH] E2E unbond and withdraw --- e2e/helper/interceptRequest.ts | 18 +++++++ e2e/mock/tx/unbonding.ts | 46 ++++++++++++++++++ e2e/mock/tx/withdrawing.ts | 34 +++++++++++++ e2e/unbonding.spec.ts | 88 ++++++++++++++++++++++++++++++++++ e2e/withdrawing.spec.ts | 68 ++++++++++++++++++++++++++ 5 files changed, 254 insertions(+) create mode 100644 e2e/helper/interceptRequest.ts create mode 100644 e2e/mock/tx/unbonding.ts create mode 100644 e2e/mock/tx/withdrawing.ts create mode 100644 e2e/unbonding.spec.ts create mode 100644 e2e/withdrawing.spec.ts diff --git a/e2e/helper/interceptRequest.ts b/e2e/helper/interceptRequest.ts new file mode 100644 index 00000000..67453472 --- /dev/null +++ b/e2e/helper/interceptRequest.ts @@ -0,0 +1,18 @@ +import { Page } from "@playwright/test"; + +// Utility function to intercept requests and fulfill them with mock data +export const interceptRequest = async ( + page: Page, + urlPattern: string, + status: number, + body: Record = {}, + contentType: string = "application/json", +): Promise => { + await page.route(urlPattern, async (route) => { + await route.fulfill({ + status, + contentType, + body: JSON.stringify(body), + }); + }); +}; diff --git a/e2e/mock/tx/unbonding.ts b/e2e/mock/tx/unbonding.ts new file mode 100644 index 00000000..59537f71 --- /dev/null +++ b/e2e/mock/tx/unbonding.ts @@ -0,0 +1,46 @@ +import { DelegationState } from "@/app/types/delegations"; + +export const activeTX = { + data: [ + { + staking_tx_hash_hex: + "1547205e6e32aaeb4eba21788891f4f323ffee9fd824461300f71292fe33f67e", + staker_pk_hex: + "4c6e2954c75bcb53aa13b7cd5d8bcdb4c9a4dd0784d68b115bd4408813b45608", + finality_provider_pk_hex: + "094f5861be4128861d69ea4b66a5f974943f100f55400bf26f5cce124b4c9af7", + state: DelegationState.ACTIVE, + staking_value: 50000, + staking_tx: { + tx_hex: + "02000000000102b497f79965a97f792dc604791cb97d76567660fdcff8d519cf993175de2880c00000000000fdffffff092b154d5edd66be91057390140898468a48df393ea52e52f6fe82fc895141ec0000000000fdffffff0350c3000000000000225120cf7c40c6fb1395430816dbb5e1ba9f172ef25573a3b609efa1723559cd82d5590000000000000000496a4762626234004c6e2954c75bcb53aa13b7cd5d8bcdb4c9a4dd0784d68b115bd4408813b45608094f5861be4128861d69ea4b66a5f974943f100f55400bf26f5cce124b4c9af70096cdb80000000000002251203a24123d844b4115ac87811ec3e9acfe8a307307a4d480f04bffcae35cb80f470140aefb4107e1a224ec95f5ef3482e594371d575b869b6ea95f985fd39d02f821549acd28c7c4a80e4d2af66e6d24f06f3e4be4e8f1e6778559c25ed4eca145fa960140242411eccc7853685dd04e7b2eb0ad337e6e8c0039026114cb2500566aa1e6d4288121ccec9535aaee697d0b4f0fd2588838d4d3a0055165aeeb59883fdd1716340e0d00", + output_index: 0, + start_timestamp: "2024-09-12T16:09:01Z", + start_height: 861031, + timelock: 150, + }, + is_overflow: false, + }, + ], + pagination: { next_key: "" }, +}; + +export const unbondingPOST = { + staker_signed_signature_hex: + "c73ffb9088349cb94b4d5a5f293394627d190437f47dc2275a1c67afd8f1200d594f8d86f4dd6bf248bd2f71f337eac094feafebe78ea68279c3e22609cf5b58", + staking_tx_hash_hex: + "1547205e6e32aaeb4eba21788891f4f323ffee9fd824461300f71292fe33f67e", + unbonding_tx_hash_hex: + "6f62aaae98658e64c6ee91f408375feb06bbd33536b7a5ffc12cc88c43a82c93", + unbonding_tx_hex: + "020000000001017ef633fe9212f700134624d89feeff23f3f491887821ba4eebaa326e5e2047150000000000ffffffff019065000000000000225120f33fe22ce55a0d4f272d54d84ceb2d8b383784bd5bdad12cfc6e6ed38336148f0340c73ffb9088349cb94b4d5a5f293394627d190437f47dc2275a1c67afd8f1200d594f8d86f4dd6bf248bd2f71f337eac094feafebe78ea68279c3e22609cf5b588a204c6e2954c75bcb53aa13b7cd5d8bcdb4c9a4dd0784d68b115bd4408813b45608ad206f13a6d104446520d1757caec13eaf6fbcf29f488c31e0107e7351d4994cd068ac20a10a06bb3bae360db3aef0326413b55b9e46bf20b9a96fc8a806a99e644fe277ba20a5e21514682b87e37fb5d3c9862055041d1e6f4cc4f3034ceaf3d90f86b230a6ba529c61c050929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac08cbd88238189c54c871cf32592ee6a4168ace71be7fa421fe0d115170c55fd872e3fc575e9845aca1bad807c7c409836824e21b47fcdc5ba259c787c17ff5fca00000000", +}; + +export const unbondingTX = { + tx_hex: + "020000000001017ef633fe9212f700134624d89feeff23f3f491887821ba4eebaa326e5e2047150000000000ffffffff019065000000000000225120f33fe22ce55a0d4f272d54d84ceb2d8b383784bd5bdad12cfc6e6ed38336148f0600406e6c2d481d4c77bf841c98e32b2dfcc86bb3267564752e03a34e67fdf70a9c2a7eddc15943f938b915518405dc6c80807971883f5206026ca609254ae401310040f53aa9e92f4d93ec92da58c6c7a379f18435f7ac877eda33105300115e942a403ae384ccfade0b690acd3bc4b6f2d946f1ef8a370f0d74fca346915dd2090caf40c73ffb9088349cb94b4d5a5f293394627d190437f47dc2275a1c67afd8f1200d594f8d86f4dd6bf248bd2f71f337eac094feafebe78ea68279c3e22609cf5b588a204c6e2954c75bcb53aa13b7cd5d8bcdb4c9a4dd0784d68b115bd4408813b45608ad206f13a6d104446520d1757caec13eaf6fbcf29f488c31e0107e7351d4994cd068ac20a10a06bb3bae360db3aef0326413b55b9e46bf20b9a96fc8a806a99e644fe277ba20a5e21514682b87e37fb5d3c9862055041d1e6f4cc4f3034ceaf3d90f86b230a6ba529c61c050929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac08cbd88238189c54c871cf32592ee6a4168ace71be7fa421fe0d115170c55fd872e3fc575e9845aca1bad807c7c409836824e21b47fcdc5ba259c787c17ff5fca00000000", + output_index: 0, + start_timestamp: "2024-09-12T16:32:26Z", + start_height: 861033, + timelock: 5, +}; diff --git a/e2e/mock/tx/withdrawing.ts b/e2e/mock/tx/withdrawing.ts new file mode 100644 index 00000000..1fb61ef3 --- /dev/null +++ b/e2e/mock/tx/withdrawing.ts @@ -0,0 +1,34 @@ +import { DelegationState } from "@/app/types/delegations"; + +export const unbondedTX = { + data: [ + { + staking_tx_hash_hex: + "1547205e6e32aaeb4eba21788891f4f323ffee9fd824461300f71292fe33f67e", + staker_pk_hex: + "4c6e2954c75bcb53aa13b7cd5d8bcdb4c9a4dd0784d68b115bd4408813b45608", + finality_provider_pk_hex: + "094f5861be4128861d69ea4b66a5f974943f100f55400bf26f5cce124b4c9af7", + state: DelegationState.UNBONDED, + staking_value: 50000, + staking_tx: { + tx_hex: + "02000000000102b497f79965a97f792dc604791cb97d76567660fdcff8d519cf993175de2880c00000000000fdffffff092b154d5edd66be91057390140898468a48df393ea52e52f6fe82fc895141ec0000000000fdffffff0350c3000000000000225120cf7c40c6fb1395430816dbb5e1ba9f172ef25573a3b609efa1723559cd82d5590000000000000000496a4762626234004c6e2954c75bcb53aa13b7cd5d8bcdb4c9a4dd0784d68b115bd4408813b45608094f5861be4128861d69ea4b66a5f974943f100f55400bf26f5cce124b4c9af70096cdb80000000000002251203a24123d844b4115ac87811ec3e9acfe8a307307a4d480f04bffcae35cb80f470140aefb4107e1a224ec95f5ef3482e594371d575b869b6ea95f985fd39d02f821549acd28c7c4a80e4d2af66e6d24f06f3e4be4e8f1e6778559c25ed4eca145fa960140242411eccc7853685dd04e7b2eb0ad337e6e8c0039026114cb2500566aa1e6d4288121ccec9535aaee697d0b4f0fd2588838d4d3a0055165aeeb59883fdd1716340e0d00", + output_index: 0, + start_timestamp: "2024-09-12T16:09:01Z", + start_height: 861031, + timelock: 150, + }, + is_overflow: false, + unbonding_tx: { + tx_hex: + "020000000001017ef633fe9212f700134624d89feeff23f3f491887821ba4eebaa326e5e2047150000000000ffffffff019065000000000000225120f33fe22ce55a0d4f272d54d84ceb2d8b383784bd5bdad12cfc6e6ed38336148f0600406e6c2d481d4c77bf841c98e32b2dfcc86bb3267564752e03a34e67fdf70a9c2a7eddc15943f938b915518405dc6c80807971883f5206026ca609254ae401310040f53aa9e92f4d93ec92da58c6c7a379f18435f7ac877eda33105300115e942a403ae384ccfade0b690acd3bc4b6f2d946f1ef8a370f0d74fca346915dd2090caf40c73ffb9088349cb94b4d5a5f293394627d190437f47dc2275a1c67afd8f1200d594f8d86f4dd6bf248bd2f71f337eac094feafebe78ea68279c3e22609cf5b588a204c6e2954c75bcb53aa13b7cd5d8bcdb4c9a4dd0784d68b115bd4408813b45608ad206f13a6d104446520d1757caec13eaf6fbcf29f488c31e0107e7351d4994cd068ac20a10a06bb3bae360db3aef0326413b55b9e46bf20b9a96fc8a806a99e644fe277ba20a5e21514682b87e37fb5d3c9862055041d1e6f4cc4f3034ceaf3d90f86b230a6ba529c61c050929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac08cbd88238189c54c871cf32592ee6a4168ace71be7fa421fe0d115170c55fd872e3fc575e9845aca1bad807c7c409836824e21b47fcdc5ba259c787c17ff5fca00000000", + output_index: 0, + start_timestamp: "2024-09-12T16:32:26Z", + start_height: 861033, + timelock: 5, + }, + }, + ], + pagination: { next_key: "" }, +}; diff --git a/e2e/unbonding.spec.ts b/e2e/unbonding.spec.ts new file mode 100644 index 00000000..2a15200a --- /dev/null +++ b/e2e/unbonding.spec.ts @@ -0,0 +1,88 @@ +import { expect, test } from "@playwright/test"; + +import { DelegationState } from "@/app/types/delegations"; +import { getState } from "@/utils/getState"; + +import { setupWalletConnection } from "./helper/connect"; +import { interceptRequest } from "./helper/interceptRequest"; +import { activeTX, unbondingTX } from "./mock/tx/unbonding"; + +test.describe("Create unbonding transaction", () => { + test("prepare the unbonding", async ({ page }) => { + // Intercept the GET request for delegations + await interceptRequest(page, "**/v1/staker/delegations**", 200, activeTX); + // Intercept the GET request for unbonding eligibility + await interceptRequest(page, "**/v1/unbonding/eligibility**", 200); + // Intercept the POST request for unbonding + await interceptRequest(page, "**/v1/unbonding", 202, { + message: "Request accepted", + }); + await page.goto("/"); + await setupWalletConnection(page); + + // unbond -> proceed + await page.getByRole("button", { name: "Unbond" }).click(); + await page.getByRole("button", { name: "Proceed" }).click(); + + // expect the unbonding state text instead of a button + await expect( + page.getByText(getState(DelegationState.INTERMEDIATE_UNBONDING)), + ).toBeVisible(); + + // check for local storage + const item = await page.evaluate(() => + localStorage.getItem( + "bbn-staking-intermediate-delegations-4c6e2954c75bcb53aa13b7cd5d8bcdb4c9a4dd0784d68b115bd4408813b45608", + ), + ); + expect(item).not.toBeNull(); + const parsed = JSON.parse(item as string); + expect(parsed).toHaveLength(1); + expect(parsed[0].state).toBe(DelegationState.INTERMEDIATE_UNBONDING); + }); + + test("unbonding requested", async ({ page }) => { + // Modify the activeTX to reflect "unbonding requested" + const updatedTX = { + ...activeTX, + data: activeTX.data.map((tx) => ({ + ...tx, + state: DelegationState.UNBONDING_REQUESTED, + })), + }; + + // Intercept the GET request for updated delegation + await interceptRequest(page, "**/v1/staker/delegations**", 200, updatedTX); + await page.goto("/"); + await setupWalletConnection(page); + await expect(page.getByText("Unbonding Requested")).toBeVisible(); + + // check for local storage + const item = await page.evaluate(() => + localStorage.getItem( + "bbn-staking-intermediate-delegations-4c6e2954c75bcb53aa13b7cd5d8bcdb4c9a4dd0784d68b115bd4408813b45608", + ), + ); + expect(item).toBe("[]"); + const parsed = JSON.parse(item as string); + expect(parsed).toHaveLength(0); + }); + + test("unbonded", async ({ page }) => { + // Modify the activeTX to reflect "unbonding requested" + const updatedTX = { + ...activeTX, + data: activeTX.data.map((tx) => ({ + ...tx, + state: DelegationState.UNBONDED, + unbonding_tx: unbondingTX, + })), + }; + + // Intercept the GET request for updated delegation + await interceptRequest(page, "**/v1/staker/delegations**", 200, updatedTX); + await page.goto("/"); + await setupWalletConnection(page); + await expect(page.getByText("Unbonded", { exact: true })).toBeVisible(); + }); +}); diff --git a/e2e/withdrawing.spec.ts b/e2e/withdrawing.spec.ts new file mode 100644 index 00000000..93520c6a --- /dev/null +++ b/e2e/withdrawing.spec.ts @@ -0,0 +1,68 @@ +import { expect, test } from "@playwright/test"; + +import { DelegationState } from "@/app/types/delegations"; +import { getState } from "@/utils/getState"; + +import { setupWalletConnection } from "./helper/connect"; +import { interceptRequest } from "./helper/interceptRequest"; +import { unbondedTX } from "./mock/tx/withdrawing"; + +test.describe("Create withdrawing transaction", () => { + test("prepare the withdrawing", async ({ page }) => { + // Intercept the GET request for delegations + await interceptRequest(page, "**/v1/staker/delegations**", 200, unbondedTX); + // Intercept the Mempool POST request for withdrawing + await interceptRequest(page, "**/api/tx", 200, { + tx: "536af306eac15c7d87d852c601f52a23c2242f53c8c5f803c11befce090c3616", + }); + await page.goto("/"); + await setupWalletConnection(page); + + // withdraw -> proceed + await page.getByRole("button", { name: "Withdraw" }).click(); + await page.getByRole("button", { name: "Proceed" }).click(); + + // expect the withdrawal state text instead of a button + await expect( + page.getByText(getState(DelegationState.INTERMEDIATE_WITHDRAWAL)), + ).toBeVisible(); + + // check for local storage + const item = await page.evaluate(() => + localStorage.getItem( + "bbn-staking-intermediate-delegations-4c6e2954c75bcb53aa13b7cd5d8bcdb4c9a4dd0784d68b115bd4408813b45608", + ), + ); + expect(item).not.toBeNull(); + const parsed = JSON.parse(item as string); + expect(parsed).toHaveLength(1); + expect(parsed[0].state).toBe(DelegationState.INTERMEDIATE_WITHDRAWAL); + }); + + test("withdrawn", async ({ page }) => { + // Modify the activeTX to reflect "withdrawn" + const updatedTX = { + ...unbondedTX, + data: unbondedTX.data.map((tx) => ({ + ...tx, + state: DelegationState.WITHDRAWN, + })), + }; + + // Intercept the GET request for updated delegation + await interceptRequest(page, "**/v1/staker/delegations**", 200, updatedTX); + await page.goto("/"); + await setupWalletConnection(page); + await expect(page.getByText("Withdrawn")).toBeVisible(); + + // check for local storage + const item = await page.evaluate(() => + localStorage.getItem( + "bbn-staking-intermediate-delegations-4c6e2954c75bcb53aa13b7cd5d8bcdb4c9a4dd0784d68b115bd4408813b45608", + ), + ); + expect(item).toBe("[]"); + const parsed = JSON.parse(item as string); + expect(parsed).toHaveLength(0); + }); +});