From 98b1c29d25efaa87939a68be1279735982eaa59c Mon Sep 17 00:00:00 2001 From: Megha-Dev-19 <100185149+Megha-Dev-19@users.noreply.github.com> Date: Wed, 30 Oct 2024 17:05:37 +0530 Subject: [PATCH 1/2] added tests --- .../widget/components/DropDown.jsx | 1 + .../widget/pages/settings/Thresholds.jsx | 24 +- .../members/create-threshold-request.spec.js | 197 ++++++++++++++++ playwright-tests/util/rpcmock.js | 222 +++++++++--------- 4 files changed, 329 insertions(+), 115 deletions(-) create mode 100644 playwright-tests/tests/members/create-threshold-request.spec.js diff --git a/instances/treasury-devdao.near/widget/components/DropDown.jsx b/instances/treasury-devdao.near/widget/components/DropDown.jsx index 48fafee6..7ea4b27f 100644 --- a/instances/treasury-devdao.near/widget/components/DropDown.jsx +++ b/instances/treasury-devdao.near/widget/components/DropDown.jsx @@ -49,6 +49,7 @@ return ( type="button" data-bs-toggle="dropdown" aria-expanded="false" + data-testid="dropdown-btn" > {selected.label} diff --git a/instances/treasury-devdao.near/widget/pages/settings/Thresholds.jsx b/instances/treasury-devdao.near/widget/pages/settings/Thresholds.jsx index 521a1553..f4e4a834 100644 --- a/instances/treasury-devdao.near/widget/pages/settings/Thresholds.jsx +++ b/instances/treasury-devdao.near/widget/pages/settings/Thresholds.jsx @@ -10,6 +10,9 @@ const { href } = VM.require("${REPL_DEVHUB}/widget/core.lib.url") || { href: () => {}, }; +if (typeof getRoleWiseData !== "function") { + return <>; +} const [selectedGroup, setSelectedGroup] = useState(null); const [selectedVoteOption, setSelectedVoteOption] = useState(null); const [selectedVoteValue, setSelectedVoteValue] = useState(null); @@ -57,9 +60,9 @@ function getLastProposalId() { } useEffect(() => { - if (typeof getRoleWiseData === "function") { - getRoleWiseData(treasuryDaoID).then((resp) => setRolesData(resp)); - } + getRoleWiseData(treasuryDaoID).then((resp) => { + setRolesData(resp); + }); }, [refreshData]); useEffect(() => { @@ -230,7 +233,7 @@ function updateDaoPolicy() { weight_kind: "RoleWeight", quorum: "0", threshold: isPercentageSelected - ? [selectedVoteValue, 100] + ? [parseInt(selectedVoteValue), 100] : selectedVoteValue, }; return policy; @@ -350,7 +353,7 @@ return ( instance, heading: "Are you sure?", content: ( -
+
This action will result in significant changes to the system.
@@ -400,7 +403,10 @@ return (
{Array.isArray(selectedGroup.members) && selectedGroup.members.map((member) => ( -
+
{ - setSelectedVoteValue(e.target.value); - const number = parseInt(e.target.value); + const value = e.target.value.replace(/[^0-9]/g, ""); + setSelectedVoteValue(value); + const number = parseInt(value); setValueError(null); if (isPercentageSelected) { if (number > 100) diff --git a/playwright-tests/tests/members/create-threshold-request.spec.js b/playwright-tests/tests/members/create-threshold-request.spec.js new file mode 100644 index 00000000..83cea40c --- /dev/null +++ b/playwright-tests/tests/members/create-threshold-request.spec.js @@ -0,0 +1,197 @@ +import { expect } from "@playwright/test"; +import { test } from "../../util/test.js"; + +import { getTransactionModalObject } from "../../util/transaction.js"; +import { + getMockedPolicy, + mockRpcRequest, + updateDaoPolicyMembers, +} from "../../util/rpcmock.js"; + +const lastProposalId = 3; + +const votePolicy = { + weight_kind: "RoleWeight", + quorum: "0", + threshold: [0, 100], +}; + +async function updateLastProposalId(page) { + await mockRpcRequest({ + page, + filterParams: { + method_name: "get_last_proposal_id", + }, + modifyOriginalResultFunction: (originalResult) => { + originalResult = lastProposalId; + return originalResult; + }, + }); +} + +async function checkForVoteApproveTxn(page) { + const txnLocator = await page + .locator("div.modal-body code") + .nth(1) + .innerText(); + const dataReceived = JSON.parse(txnLocator); + expect(dataReceived).toEqual({ + id: lastProposalId, + action: "VoteApprove", + }); +} + +test.describe("without login", function () { + test.beforeEach(async ({ page, instanceAccount }) => { + await updateDaoPolicyMembers({ page }); + await page.goto(`/${instanceAccount}/widget/app?page=settings`); + await page.getByText("Voting Thresholds").click({ timeout: 20_000 }); + }); + + test("should show members of different roles", async ({ page }) => { + test.setTimeout(60_000); + await expect(page.getByText("Permission Groups")).toBeVisible({ + timeout: 20_000, + }); + await expect(page.getByText("Members 6")).toBeVisible(); + await expect(page.getByText("Voting Policy")).toBeVisible(); + await expect( + page.getByText( + "@2dada969f3743a4a41cfdb1a6e39581c2844ce8fbe25948700c85c598090b3e1", + { exact: true } + ) + ).toBeVisible(); + await page.getByText("Manage Members").click(); + await expect( + page.getByText("@megha19.near", { exact: true }) + ).toBeVisible(); + await page.getByText("Vote", { exact: true }).click(); + await expect(page.getByText("@test04.near", { exact: true })).toBeVisible(); + }); + + test("should disable input and hide submit button for non authorised people", async ({ + page, + }) => { + test.setTimeout(60_000); + await expect(page.getByText("Permission Groups")).toBeVisible({ + timeout: 20_000, + }); + await expect(page.getByTestId("dropdown-btn")).toBeDisabled(); + await expect(page.getByText("Submit")).toBeHidden(); + await expect(page.getByTestId("threshold-input")).toBeDisabled(); + }); +}); + +async function fillInput(page, value) { + const submitBtn = page.getByText("Submit"); + const thresholdInput = page.getByTestId("threshold-input"); + await thresholdInput.type(value); + expect(submitBtn).toBeVisible(); +} + +test.describe("admin connected", function () { + test.use({ + storageState: "playwright-tests/storage-states/wallet-connected-admin.json", + }); + + test.beforeEach(async ({ page, instanceAccount }) => { + await updateLastProposalId(page); + await updateDaoPolicyMembers({ page }); + await page.goto(`/${instanceAccount}/widget/app?page=settings`); + await page.getByText("Voting Thresholds").click({ timeout: 20_000 }); + await expect(page.getByText("Submit")).toBeVisible({ + timeout: 20_000, + }); + await page.getByTestId("dropdown-btn").click(); + }); + + test("should allow only valid input for threshold", async ({ page }) => { + test.setTimeout(20_000); + await page.getByRole("list").getByText("Number of votes").click(); + await fillInput(page, "20097292"); + await fillInput(page, "10.323"); + await fillInput(page, "werwr"); + await fillInput(page, "$&$^&%&"); + await page.getByTestId("dropdown-btn").click(); + await page.getByRole("list").getByText("Percentage of members").click(); + await fillInput(page, "3423"); + await fillInput(page, "13.123"); + await fillInput(page, "wqeqewq"); + await fillInput(page, "$&$^&%&"); + }); + + test("should be able to update policy by fixed vote count", async ({ + page, + }) => { + test.setTimeout(60_000); + const submitBtn = page.getByText("Submit"); + await page.getByRole("list").getByText("Number of votes").click(); + const thresholdInput = page.getByTestId("threshold-input"); + await thresholdInput.fill("20"); + await expect(page.getByText("Maximum members allowed is ")).toBeVisible(); + await expect(submitBtn).toBeDisabled(); + await thresholdInput.fill("2"); + await submitBtn.click(); + await expect( + page.getByText( + "Changing this setting will require 2 vote(s) to approve requests. You will no longer be able to approve requests with 1 vote(s)." + ) + ).toBeVisible(); + await page.getByRole("button", { name: "Confirm" }).click(); + const updatedPolicy = { + weight_kind: "RoleWeight", + quorum: "0", + threshold: "2", + }; + + expect(await getTransactionModalObject(page)).toEqual({ + proposal: { + description: "Update Policy", + kind: { + ChangePolicy: { + policy: getMockedPolicy(updatedPolicy, votePolicy, votePolicy), + }, + }, + }, + }); + await checkForVoteApproveTxn(page); + }); + + test("should be able to update policy by percentage", async ({ page }) => { + test.setTimeout(60_000); + const submitBtn = page.getByText("Submit"); + await page.getByRole("list").getByText("Percentage of members").click(); + const thresholdInput = page.getByTestId("threshold-input"); + await thresholdInput.fill("101"); + await expect( + page.getByText("Maximum percentage allowed is ") + ).toBeVisible(); + await expect(submitBtn).toBeDisabled(); + await thresholdInput.fill("20"); + await expect(page.getByText("Heads up, Bro!")).toBeVisible(); + await submitBtn.click(); + await expect( + page.getByText( + "Changing this setting will require 2 vote(s) to approve requests. You will no longer be able to approve requests with 1 vote(s)." + ) + ).toBeVisible(); + await page.getByRole("button", { name: "Confirm" }).click(); + const updatedPolicy = { + weight_kind: "RoleWeight", + quorum: "0", + threshold: [20, 100], + }; + + await expect(await getTransactionModalObject(page)).toEqual({ + proposal: { + description: "Update Policy", + kind: { + ChangePolicy: { + policy: getMockedPolicy(updatedPolicy, votePolicy, votePolicy), + }, + }, + }, + }); + await checkForVoteApproveTxn(page); + }); +}); diff --git a/playwright-tests/util/rpcmock.js b/playwright-tests/util/rpcmock.js index 58c41e7f..1af0c20b 100644 --- a/playwright-tests/util/rpcmock.js +++ b/playwright-tests/util/rpcmock.js @@ -45,6 +45,120 @@ export async function mockRpcRequest({ }); } +export function getMockedPolicy( + createRequestPolicy, + membersPolicy, + votePolicy +) { + return { + roles: [ + { + name: "Create Requests", + kind: { + Group: [ + "theori.near", + "2dada969f3743a4a41cfdb1a6e39581c2844ce8fbe25948700c85c598090b3e1", + "freski.near", + "megha19.near", + "thomasguntenaar.near", + "petersalomonsen.near", + ], + }, + permissions: [ + "call:AddProposal", + "transfer:AddProposal", + "config:Finalize", + ], + vote_policy: { + transfer: createRequestPolicy, + bounty_done: createRequestPolicy, + add_bounty: createRequestPolicy, + policy: createRequestPolicy, + call: createRequestPolicy, + upgrade_self: createRequestPolicy, + config: createRequestPolicy, + set_vote_token: createRequestPolicy, + upgrade_remote: createRequestPolicy, + vote: createRequestPolicy, + add_member_to_role: createRequestPolicy, + remove_member_from_role: createRequestPolicy, + }, + }, + { + name: "Manage Members", + kind: { + Group: [ + "petersalomonsen.near", + "thomasguntenaar.near", + "theori.near", + "megha19.near", + ], + }, + permissions: [ + "config:*", + "policy:*", + "add_member_to_role:*", + "remove_member_from_role:*", + ], + vote_policy: { + upgrade_remote: membersPolicy, + upgrade_self: membersPolicy, + call: membersPolicy, + bounty_done: membersPolicy, + policy: membersPolicy, + config: membersPolicy, + add_member_to_role: membersPolicy, + set_vote_token: membersPolicy, + vote: membersPolicy, + transfer: membersPolicy, + add_bounty: membersPolicy, + remove_member_from_role: membersPolicy, + }, + }, + { + name: "Vote", + kind: { + Group: [ + "megha19.near", + "petersalomonsen.near", + "treasurytestuserledger.near", + "tfdevhub.near", + "theori.near", + "thomasguntenaar.near", + "test04.near", + "test03.near", + "test05.near", + ], + }, + permissions: ["*:VoteReject", "*:VoteApprove", "*:VoteRemove"], + vote_policy: { + transfer: votePolicy, + config: votePolicy, + add_bounty: votePolicy, + set_vote_token: votePolicy, + upgrade_remote: votePolicy, + add_member_to_role: votePolicy, + upgrade_self: votePolicy, + call: votePolicy, + policy: votePolicy, + remove_member_from_role: votePolicy, + bounty_done: votePolicy, + vote: votePolicy, + }, + }, + ], + default_vote_policy: { + weight_kind: "RoleWeight", + quorum: "0", + threshold: [1, 2], + }, + proposal_bond: "0", + proposal_period: "604800000000000", + bounty_bond: "100000000000000000000000", + bounty_forgiveness_period: "604800000000000", + }; +} + export async function updateDaoPolicyMembers({ page, isMultiVote = false }) { await mockRpcRequest({ page, @@ -57,113 +171,7 @@ export async function updateDaoPolicyMembers({ page, isMultiVote = false }) { quorum: "0", threshold: isMultiVote ? [90, 100] : [0, 100], }; - originalResult = { - roles: [ - { - name: "Create Requests", - kind: { - Group: [ - "theori.near", - "2dada969f3743a4a41cfdb1a6e39581c2844ce8fbe25948700c85c598090b3e1", - "freski.near", - "megha19.near", - "thomasguntenaar.near", - "petersalomonsen.near", - ], - }, - permissions: [ - "call:AddProposal", - "transfer:AddProposal", - "config:Finalize", - ], - vote_policy: { - transfer: votePolicy, - bounty_done: votePolicy, - add_bounty: votePolicy, - policy: votePolicy, - call: votePolicy, - upgrade_self: votePolicy, - config: votePolicy, - set_vote_token: votePolicy, - upgrade_remote: votePolicy, - vote: votePolicy, - add_member_to_role: votePolicy, - remove_member_from_role: votePolicy, - }, - }, - { - name: "Manage Members", - kind: { - Group: [ - "petersalomonsen.near", - "thomasguntenaar.near", - "theori.near", - "megha19.near", - ], - }, - permissions: [ - "config:*", - "policy:*", - "add_member_to_role:*", - "remove_member_from_role:*", - ], - vote_policy: { - upgrade_remote: votePolicy, - upgrade_self: votePolicy, - call: votePolicy, - bounty_done: votePolicy, - policy: votePolicy, - config: votePolicy, - add_member_to_role: votePolicy, - set_vote_token: votePolicy, - vote: votePolicy, - transfer: votePolicy, - add_bounty: votePolicy, - remove_member_from_role: votePolicy, - }, - }, - { - name: "Vote", - kind: { - Group: [ - "megha19.near", - "petersalomonsen.near", - "treasurytestuserledger.near", - "tfdevhub.near", - "theori.near", - "thomasguntenaar.near", - "test04.near", - "test03.near", - "test05.near", - ], - }, - permissions: ["*:VoteReject", "*:VoteApprove", "*:VoteRemove"], - vote_policy: { - transfer: votePolicy, - config: votePolicy, - add_bounty: votePolicy, - set_vote_token: votePolicy, - upgrade_remote: votePolicy, - add_member_to_role: votePolicy, - upgrade_self: votePolicy, - call: votePolicy, - policy: votePolicy, - remove_member_from_role: votePolicy, - bounty_done: votePolicy, - vote: votePolicy, - }, - }, - ], - default_vote_policy: { - weight_kind: "RoleWeight", - quorum: "0", - threshold: [1, 2], - }, - proposal_bond: "0", - proposal_period: "604800000000000", - bounty_bond: "100000000000000000000000", - bounty_forgiveness_period: "604800000000000", - }; + originalResult = getMockedPolicy(votePolicy, votePolicy, votePolicy); return originalResult; }, }); From f6c84aa61ac4692ec11100b7b412d121d86be2b4 Mon Sep 17 00:00:00 2001 From: Megha-Dev-19 <100185149+Megha-Dev-19@users.noreply.github.com> Date: Wed, 30 Oct 2024 17:24:27 +0530 Subject: [PATCH 2/2] minor fixes --- .../widget/pages/settings/Thresholds.jsx | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/instances/treasury-devdao.near/widget/pages/settings/Thresholds.jsx b/instances/treasury-devdao.near/widget/pages/settings/Thresholds.jsx index f4e4a834..a6ed9009 100644 --- a/instances/treasury-devdao.near/widget/pages/settings/Thresholds.jsx +++ b/instances/treasury-devdao.near/widget/pages/settings/Thresholds.jsx @@ -34,7 +34,7 @@ const hasCreatePermission = hasPermission( ); useEffect(() => { - if (Array.isArray(rolesData) && rolesData.length && !selectedGroup) { + if (Array.isArray(rolesData) && rolesData.length) { setSelectedGroup(rolesData[0]); } }, [rolesData]); @@ -60,6 +60,7 @@ function getLastProposalId() { } useEffect(() => { + setRolesData([]); getRoleWiseData(treasuryDaoID).then((resp) => { setRolesData(resp); }); @@ -76,11 +77,16 @@ function checkProposalStatus(proposalId) { Near.asyncView(treasuryDaoID, "get_proposal", { id: proposalId, }).then((result) => { - if (result.status === "Approved") { - setRefreshData(!refreshData); + if (Object.keys(result.votes).length === 1) { + if (result.status === "Approved") { + setRefreshData(!refreshData); + } + setToastStatus(result.status); + setVoteProposalId(proposalId); + setTxnCreated(false); + } else { + setTimeout(() => checkProposalStatus(proposalId), 1000); } - setToastStatus(result.status); - setVoteProposalId(proposalId); }); }