From 2b697c05a7407d2d078bdf12b8d45dc53412e014 Mon Sep 17 00:00:00 2001 From: Thomas Date: Fri, 19 Jul 2024 00:00:15 +0200 Subject: [PATCH 1/6] sync proposal.spec between events and devhub --- .../tests/events/proposals.spec.js | 113 ++++-------------- .../tests/proposal/proposals.spec.js | 4 +- 2 files changed, 29 insertions(+), 88 deletions(-) diff --git a/playwright-tests/tests/events/proposals.spec.js b/playwright-tests/tests/events/proposals.spec.js index 7dcbb6729..0c67fdfab 100644 --- a/playwright-tests/tests/events/proposals.spec.js +++ b/playwright-tests/tests/events/proposals.spec.js @@ -1,9 +1,6 @@ import { test as base, expect } from "@playwright/test"; import { modifySocialNearGetRPCResponsesInsteadOfGettingWidgetsFromBOSLoader } from "../../util/bos-loader.js"; -import { - pauseIfVideoRecording, - waitForSelectorToBeVisible, -} from "../../testUtils.js"; +import { pauseIfVideoRecording } from "../../testUtils.js"; import { setCommitWritePermissionDontAskAgainCacheValues, setDontAskAgainCacheValues, @@ -14,7 +11,6 @@ import { encodeResultJSON, } from "../../util/transaction.js"; import { mockRpcRequest } from "../../util/rpcmock.js"; -import { mockSocialIndexResponses } from "../../util/socialapi.js"; const test = base.extend({ // Define an option and provide a default value. @@ -27,7 +23,27 @@ test.afterEach( async ({ page }) => await page.unrouteAll({ behavior: "ignoreErrors" }) ); -test.describe("Wallet is connected, but not KYC verified", () => { +let acceptedTermsVersion = 122927956; +async function getCurrentBlockHeight(page) { + // set current block height for accepted terms and conditions + await page.route(`http://localhost:20000/`, async (route) => { + const request = route.request(); + const requestPostData = request.postDataJSON(); + if ( + requestPostData?.method === "block" && + requestPostData?.params?.finality === "optimistic" + ) { + const response = await route.fetch(); + const json = await response.json(); + json.result.header.height = acceptedTermsVersion; + await route.fulfill({ response, json }); + } else { + await route.continue(); + } + }); +} + +test.describe.skip("Wallet is connected, but not KYC verified", () => { test.use({ storageState: "playwright-tests/storage-states/wallet-connected-not-kyc-verified-account.json", @@ -170,85 +186,6 @@ test.describe("Don't ask again enabled", () => { await page.waitForTimeout(1000); await pauseIfVideoRecording(page); }); - test("should add comment on a proposal", async ({ page, account }) => { - await page.goto(`/${account}/widget/app?page=proposal&id=5`); - const widgetSrc = `${account}/widget/devhub.entity.proposal.ComposeComment`; - - const delay_milliseconds_between_keypress_when_typing = 0; - const commentArea = await page - .frameLocator("iframe") - .locator(".CodeMirror textarea"); - await commentArea.focus(); - const text = "Comment testing"; - await commentArea.pressSequentially(text, { - delay: delay_milliseconds_between_keypress_when_typing, - }); - await commentArea.blur(); - await pauseIfVideoRecording(page); - - const accountId = "petersalomonsen.near"; - await setCommitWritePermissionDontAskAgainCacheValues({ - page, - widgetSrc, - accountId: accountId, - }); - - await mockTransactionSubmitRPCResponses( - page, - async ({ route, request, transaction_completed, last_receiver_id }) => { - const postData = request.postDataJSON(); - const args_base64 = postData.params?.args_base64; - if (transaction_completed && args_base64) { - const args = atob(args_base64); - if ( - postData.params.account_id === "social.near" && - postData.params.method_name === "get" && - args === `{"keys":["${accountId}/post/**"]}` - ) { - const response = await route.fetch(); - const json = await response.json(); - const resultObj = decodeResultJSON(json.result.result); - resultObj[accountId].post.main = JSON.stringify({ - text: text, - }); - json.result.result = encodeResultJSON(resultObj); - await route.fulfill({ response, json }); - return; - } else { - await route.continue(); - } - } else { - await route.continue(); - } - } - ); - const commentButton = await page.getByRole("button", { name: "Comment" }); - await expect(commentButton).toBeAttached(); - await commentButton.scrollIntoViewIfNeeded(); - await commentButton.click(); - await expect( - await page.frameLocator("iframe").locator(".CodeMirror") - ).toContainText(text); - - const loadingIndicator = await page.locator(".comment-btn-spinner"); - await expect(loadingIndicator).toBeAttached(); - await loadingIndicator.waitFor({ state: "detached", timeout: 30000 }); - await expect(loadingIndicator).not.toBeVisible(); - const transaction_successful_toast = await page.getByText( - "Comment Submitted Successfully", - { exact: true } - ); - await expect(transaction_successful_toast).toBeVisible(); - - await expect(transaction_successful_toast).not.toBeAttached(); - await expect( - await page.frameLocator("iframe").locator(".CodeMirror") - ).not.toContainText(text); - await expect( - await page.frameLocator("iframe").locator(".CodeMirror") - ).toContainText("Add your comment here..."); - await pauseIfVideoRecording(page); - }); }); test.describe.skip('Moderator with "Don\'t ask again" enabled', () => { @@ -401,6 +338,7 @@ test.describe("Wallet is connected", () => { account, }) => { test.setTimeout(120000); + await getCurrentBlockHeight(page); await page.goto(`/${account}/widget/app?page=create-proposal`); const delay_milliseconds_between_keypress_when_typing = 0; @@ -510,7 +448,7 @@ test.describe("Wallet is connected", () => { await pauseIfVideoRecording(page); }); - test.skip("should show relevant users in mention autocomplete", async ({ + test("should show relevant users in mention autocomplete", async ({ page, account, }) => { @@ -598,6 +536,7 @@ test.describe("Wallet is connected", () => { account, }) => { test.setTimeout(120000); + await getCurrentBlockHeight(page); await page.goto(`/${account}/widget/app?page=create-proposal`); await page.route( "https://near-queryapi.api.pagoda.co/v1/graphql", @@ -707,7 +646,7 @@ test.describe("Wallet is connected", () => { delay: delay_milliseconds_between_keypress_when_typing, } ); - + await descriptionArea.pressSequentially("2", { delay: 10 }); await pauseIfVideoRecording(page); await page diff --git a/playwright-tests/tests/proposal/proposals.spec.js b/playwright-tests/tests/proposal/proposals.spec.js index 558ddbaf0..79569dea5 100644 --- a/playwright-tests/tests/proposal/proposals.spec.js +++ b/playwright-tests/tests/proposal/proposals.spec.js @@ -87,7 +87,6 @@ test.describe("Don't ask again enabled", () => { await modifySocialNearGetRPCResponsesInsteadOfGettingWidgetsFromBOSLoader( page ); - console.log({ account }); await page.goto(`/${account}/widget/app?page=proposals`); const widgetSrc = `${account}/widget/devhub.entity.proposal.Editor`; @@ -211,6 +210,7 @@ test.describe('Moderator with "Don\'t ask again" enabled', () => { }, modifyOriginalResultFunction: (originalResult) => { originalResult.snapshot.timeline.status = "REVIEW"; + if (isTransactionCompleted) { const lastSnapshot = originalResult.snapshot_history[ @@ -246,6 +246,7 @@ test.describe('Moderator with "Don\'t ask again" enabled', () => { ); await page.goto(`/${account}/widget/app?page=proposal&id=17`); + await setDontAskAgainCacheValues({ page, contractId: account, @@ -340,6 +341,7 @@ test.describe("Wallet is connected", () => { test.setTimeout(120000); await getCurrentBlockHeight(page); await page.goto(`/${account}/widget/app?page=create-proposal`); + const delay_milliseconds_between_keypress_when_typing = 0; const titleArea = await page.getByRole("textbox").first(); await expect(titleArea).toBeEditable(); From 7c5ca4820854de41c355d2b90a12194a89e99cfa Mon Sep 17 00:00:00 2001 From: Thomas Date: Fri, 19 Jul 2024 00:00:43 +0200 Subject: [PATCH 2/6] feat: add deploy and signer account for events portal --- instances/events-committee.near/bos.config.json | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/instances/events-committee.near/bos.config.json b/instances/events-committee.near/bos.config.json index a382469fb..63c0e0e2b 100644 --- a/instances/events-committee.near/bos.config.json +++ b/instances/events-committee.near/bos.config.json @@ -1,5 +1,9 @@ { "account": "events-committee.near", + "accounts": { + "deploy": "events-committee.near", + "signer": "events-committee.near" + }, "aliasPrefix": "REPL", "aliasesContainsPrefix": true, "aliases": ["./aliases.mainnet.json"] From 6242f3239dfc956b9f28a55e0d7bf9a111ec8ad9 Mon Sep 17 00:00:00 2001 From: Thomas Date: Fri, 19 Jul 2024 01:00:21 +0200 Subject: [PATCH 3/6] edit playwright.config exclude proposal folder for events com but still test comments --- playwright-tests/tests/events/comment.spec.js | 293 ++++++++++++++++++ .../tests/events/proposals.spec.js | 1 - playwright.config.js | 4 +- 3 files changed, 295 insertions(+), 3 deletions(-) create mode 100644 playwright-tests/tests/events/comment.spec.js diff --git a/playwright-tests/tests/events/comment.spec.js b/playwright-tests/tests/events/comment.spec.js new file mode 100644 index 000000000..2c92614cd --- /dev/null +++ b/playwright-tests/tests/events/comment.spec.js @@ -0,0 +1,293 @@ +import { test as base, expect } from "@playwright/test"; +import { pauseIfVideoRecording } from "../../testUtils.js"; +import { setCommitWritePermissionDontAskAgainCacheValues } from "../../util/cache.js"; +import { + mockTransactionSubmitRPCResponses, + decodeResultJSON, + encodeResultJSON, +} from "../../util/transaction.js"; + +const test = base.extend({ + // Define an option and provide a default value. + // We can later override it in the config. + account: ["events-committee.near", { option: true }], + proposalAuthorAccountId: ["megha19.near", { option: true }], +}); + +test.describe("Don't ask again enabled", () => { + test.use({ + contextOptions: { + permissions: ["clipboard-read", "clipboard-write"], + }, + storageState: + "playwright-tests/storage-states/wallet-connected-with-devhub-access-key.json", + }); + test("should add comment on a proposal", async ({ + page, + account: instanceAccount, + }) => { + await page.goto(`/${instanceAccount}/widget/app?page=proposal&id=5`); + const widgetSrc = + instanceAccount === "infrastructure-committee.near" + ? "infrastructure-committee.near/widget/components.molecule.ComposeComment" + : `${instanceAccount}/widget/devhub.entity.proposal.ComposeComment`; + + const delay_milliseconds_between_keypress_when_typing = 0; + const commentArea = await page + .frameLocator("iframe") + .locator(".CodeMirror textarea"); + await commentArea.focus(); + const text = "Comment testing"; + await commentArea.pressSequentially(text, { + delay: delay_milliseconds_between_keypress_when_typing, + }); + await commentArea.blur(); + await pauseIfVideoRecording(page); + + const accountId = "petersalomonsen.near"; + await setCommitWritePermissionDontAskAgainCacheValues({ + page, + widgetSrc, + accountId: accountId, + }); + + await mockTransactionSubmitRPCResponses( + page, + async ({ route, request, transaction_completed, last_receiver_id }) => { + const postData = request.postDataJSON(); + const args_base64 = postData.params?.args_base64; + if (transaction_completed && args_base64) { + const args = atob(args_base64); + if ( + postData.params.account_id === "social.near" && + postData.params.method_name === "get" && + args === `{"keys":["${accountId}/post/**"]}` + ) { + const response = await route.fetch(); + const json = await response.json(); + const resultObj = decodeResultJSON(json.result.result); + resultObj[accountId].post.main = JSON.stringify({ + text: text, + }); + json.result.result = encodeResultJSON(resultObj); + await route.fulfill({ response, json }); + return; + } else { + await route.continue(); + } + } else { + await route.continue(); + } + } + ); + const commentButton = await page.getByRole("button", { name: "Comment" }); + await expect(commentButton).toBeAttached(); + await commentButton.scrollIntoViewIfNeeded(); + await commentButton.click(); + await expect( + await page.frameLocator("iframe").locator(".CodeMirror") + ).toContainText(text); + + const loadingIndicator = await page.locator(".comment-btn-spinner"); + await expect(loadingIndicator).toBeAttached(); + await loadingIndicator.waitFor({ state: "detached", timeout: 30000 }); + await expect(loadingIndicator).not.toBeVisible(); + const transaction_successful_toast = await page.getByText( + "Comment Submitted Successfully", + { exact: true } + ); + await expect(transaction_successful_toast).toBeVisible(); + + await expect(transaction_successful_toast).not.toBeAttached(); + await expect( + await page.frameLocator("iframe").locator(".CodeMirror") + ).not.toContainText(text); + await expect( + await page.frameLocator("iframe").locator(".CodeMirror") + ).toContainText("Add your comment here..."); + await pauseIfVideoRecording(page); + }); + test("should paste a long comment to a proposal, see that the comment appears after submission, and that the comment field is cleared, even after reloading the page", async ({ + page, + account: instanceAccount, + }) => { + test.setTimeout(2 * 60000); + await page.goto(`/${instanceAccount}/widget/app?page=proposal&id=5`); + const widgetSrc = + instanceAccount === "infrastructure-committee.near" + ? "infrastructure-committee.near/widget/components.molecule.ComposeComment" + : `${instanceAccount}/widget/devhub.entity.proposal.ComposeComment`; + + let commentButton = await page.getByRole("button", { name: "Comment" }); + await expect(commentButton).toBeAttached({ timeout: 10000 }); + await commentButton.scrollIntoViewIfNeeded(); + + const commentText = + "Hi @petersalomonsen.near – congratulations! This message confirms your funding request approval by @neardevdao.near. We're excited to sponsor your work! This approval follows our review process involving various Work Groups and DevDAO Moderators, as outlined in our [guidelines](/devhub.near/widget/app?page=community&handle=developer-dao&tab=Funding). Please note that the funding distribution is contingent on successfully passing our KYC/B and paperwork process.\n\nHere’s what to expect:\n\n**Funding Steps**\n\n1. **KYC/KYB Verification:** A DevDAO Moderator will move your proposal to the Payment Processing Stage and verify that you have completed verification to ensure compliance. If you are not verified, your DevHub Moderator will contact you on Telegram with instructions on how to proceed. To receive funding, you must get verified through Fractal, a trusted third-party identification verification solution. Your verification badge is valid for 365 days and needs renewal upon expiration OR if your personal information changes, such as your name, address, or ID expiration.\n2. **Information Collection:** Once verified, a DevDAO Moderator will contact you via Telegram and request that you complete the Funding Request Form using Airtable.\n3. **Processing:** Our legal team will verify your application details to ensure compliance. They will then send you an email requesting your signature for the underlying agreement via Ironclad.\n4. **Invoicing & Payment:** Once we receive your signed agreement, our finance team will email you instructions to submit the final invoice using Request Finance. Once we receive your invoice, our finance team will send a test transaction confirmation email. Once you confirm the test transaction, we will distribute the funds and post a payment link on your proposal.\n\n**Funding Conversion Notice**\n\nOnce you receive your funding, we urge you to exercise caution if attempting to convert your funds. Some third-party tools may impose significant swapping fees.\n\n**Visibility**\n\nWe track the funding process on each proposal using the timeline and comments. However, you are welcome to reach out to the DevDAO Moderator with any questions. \n\n**Timeline**\n\nTypically, funds are disbursed within 10 business days, but the timeline can vary depending on the project's complexity and paperwork. Your DevDAO Moderator will keep you updated."; + await page.evaluate(async (text) => { + await navigator.clipboard.writeText(text); + }, commentText); + + const commentArea = await page + .frameLocator("iframe") + .locator(".CodeMirror textarea"); + + await pauseIfVideoRecording(page); + await commentArea.focus(); + + const isMac = process.platform === "darwin"; + + if (isMac) { + await page.keyboard.down("Meta"); // Command key on macOS + await page.keyboard.press("a"); + await page.keyboard.press("v"); + await page.keyboard.up("Meta"); + } else { + await page.keyboard.down("Control"); // Control key on Windows/Linux + await page.keyboard.press("a"); + await page.keyboard.press("v"); + await page.keyboard.up("Control"); + } + + await commentArea.blur(); + await pauseIfVideoRecording(page); + + const userAccount = "petersalomonsen.near"; + await setCommitWritePermissionDontAskAgainCacheValues({ + page, + widgetSrc, + accountId: userAccount, + }); + + const transactionMockStatus = await mockTransactionSubmitRPCResponses( + page, + async ({ route, request, transaction_completed, last_transaction }) => { + const postData = request.postDataJSON(); + const args_base64 = postData.params?.args_base64; + + if (transaction_completed && args_base64) { + const args = atob(args_base64); + if ( + postData.params.account_id === "social.near" && + postData.params.method_name === "get" && + args === `{"keys":["${userAccount}/post/**"]}` + ) { + const response = await route.fetch(); + const json = await response.json(); + const resultObj = decodeResultJSON(json.result.result); + + resultObj[userAccount].post.main = JSON.stringify({ + text: commentText, + }); + json.result.result = encodeResultJSON(resultObj); + completedPromiseResolve(last_transaction); + await route.fulfill({ response, json }); + return; + } else { + await route.continue(); + } + } else { + await route.continue(); + } + } + ); + + let submittedTransactionJsonObjectPromiseResolve; + let submittedTransactionJsonObjectPromise = new Promise( + (r) => (submittedTransactionJsonObjectPromiseResolve = r) + ); + await page.route("https://api.near.social/index", async (route) => { + if (transactionMockStatus.transaction_completed) { + const lastTransactionParamBuffer = Buffer.from( + transactionMockStatus.last_transaction.params[0], + "base64" + ); + + const transactionDataJsonStartIndex = + lastTransactionParamBuffer.indexOf('{"data":'); + const transactionDataJsonEndIndex = + lastTransactionParamBuffer.indexOf('"}}}}') + '"}}}}'.length; + const transactionDataJsonString = lastTransactionParamBuffer.subarray( + transactionDataJsonStartIndex, + transactionDataJsonEndIndex + ); + + submittedTransactionJsonObjectPromiseResolve( + JSON.parse(transactionDataJsonString.toString()) + ); + + const response = await route.fetch(); + const json = await response.json(); + json.push({ + accountId: "theori.near", + blockHeight: 121684809, + value: { + type: "md", + }, + }); + + await route.fulfill({ json }); + } else { + await route.continue(); + } + }); + + commentButton = await page.getByRole("button", { name: "Comment" }); + await commentButton.click(); + + const loadingIndicator = await page.locator(".comment-btn-spinner"); + await expect(loadingIndicator).toBeAttached(); + await loadingIndicator.waitFor({ state: "detached", timeout: 30000 }); + await expect(loadingIndicator).not.toBeVisible(); + const transaction_successful_toast = await page.getByText( + "Comment Submitted Successfully", + { exact: true } + ); + await expect(transaction_successful_toast).toBeVisible(); + + await expect(transaction_successful_toast).not.toBeAttached({ + timeout: 10000, + }); + await expect( + await page.frameLocator("iframe").locator(".CodeMirror") + ).not.toContainText(commentText); + await expect( + await page.frameLocator("iframe").locator(".CodeMirror") + ).toContainText("Add your comment here..."); + + const submittedTransactionJsonObject = + await submittedTransactionJsonObjectPromise; + const submittedComment = JSON.parse( + submittedTransactionJsonObject.data["petersalomonsen.near"].post.comment + ); + expect(submittedComment.text).toEqual(commentText); + let commentElement = await page.locator("#theorinear121684809"); + await expect(commentElement).toBeVisible({ timeout: 10000 }); + await commentElement.scrollIntoViewIfNeeded(); + await expect(commentElement).toContainText( + "Typically, funds are disbursed within 10 business days, but the timeline can vary depending on the project's complexity and paperwork. Your DevDAO Moderator will keep you updated.", + { timeout: 10000 } + ); + + await page.reload(); + + commentElement = await page.locator("#theorinear121684809"); + await expect(commentElement).toBeVisible({ timeout: 20000 }); + await commentElement.scrollIntoViewIfNeeded(); + await expect(commentElement).toContainText( + "Typically, funds are disbursed within 10 business days, but the timeline can vary depending on the project's complexity and paperwork. Your DevDAO Moderator will keep you updated.", + { timeout: 10000 } + ); + + commentButton = await page.getByRole("button", { name: "Comment" }); + await expect(commentButton).toBeAttached({ timeout: 10000 }); + await commentButton.scrollIntoViewIfNeeded(); + + await page.waitForTimeout(5000); + await expect( + await page.frameLocator("iframe").locator(".CodeMirror") + ).toContainText("Add your comment here..."); + + await pauseIfVideoRecording(page); + }); +}); diff --git a/playwright-tests/tests/events/proposals.spec.js b/playwright-tests/tests/events/proposals.spec.js index 0c67fdfab..ac55af1d6 100644 --- a/playwright-tests/tests/events/proposals.spec.js +++ b/playwright-tests/tests/events/proposals.spec.js @@ -646,7 +646,6 @@ test.describe("Wallet is connected", () => { delay: delay_milliseconds_between_keypress_when_typing, } ); - await descriptionArea.pressSequentially("2", { delay: 10 }); await pauseIfVideoRecording(page); await page diff --git a/playwright.config.js b/playwright.config.js index bbc770de8..d66b0899f 100644 --- a/playwright.config.js +++ b/playwright.config.js @@ -69,7 +69,7 @@ export default defineConfig({ }, { name: "events", - testMatch: /(events|proposal)\/.*.spec.js/, // (proposal|events) + testMatch: /(events)\/.*.spec.js/, // (proposal|events) use: { ...devices["Desktop Chrome"], baseURL: "http://localhost:8080", @@ -79,7 +79,7 @@ export default defineConfig({ }, { name: "devhub", - testMatch: /(blog|community|other|proposal|sunset)\/.*.spec.js/, // (proposal|events) + testMatch: /(blog|community|other|proposal|sunset)\/.*.spec.js/, use: { ...devices["Desktop Chrome"], baseURL: "http://localhost:8080", From f3a73649d838576693dee4ddadc2e95c6a46e128 Mon Sep 17 00:00:00 2001 From: Thomas Date: Fri, 19 Jul 2024 12:23:04 +0200 Subject: [PATCH 4/6] add relevant mentions to events committee portal --- .../devhub/components/molecule/Compose.jsx | 2 ++ .../devhub/components/molecule/SimpleMDE.jsx | 17 +++++++++++------ .../devhub/entity/proposal/CommentsAndLogs.jsx | 2 +- .../widget/devhub/entity/proposal/Proposal.jsx | 13 +++++++++++++ playwright-tests/tests/events/proposals.spec.js | 14 +++++++------- 5 files changed, 34 insertions(+), 14 deletions(-) diff --git a/instances/events-committee.near/widget/devhub/components/molecule/Compose.jsx b/instances/events-committee.near/widget/devhub/components/molecule/Compose.jsx index ca118c7d6..2350d97cc 100644 --- a/instances/events-committee.near/widget/devhub/components/molecule/Compose.jsx +++ b/instances/events-committee.near/widget/devhub/components/molecule/Compose.jsx @@ -29,6 +29,7 @@ const Compose = ({ showProposalIdAutoComplete, onChangeKeyup, handler, + sortedRelevantUsers, }) => { State.init({ data: data, @@ -97,6 +98,7 @@ const Compose = ({ showProposalIdAutoComplete: showProposalIdAutoComplete, autoFocus: state.autoFocus, onChangeKeyup: onChangeKeyup, + sortedRelevantUsers, }} /> diff --git a/instances/events-committee.near/widget/devhub/components/molecule/SimpleMDE.jsx b/instances/events-committee.near/widget/devhub/components/molecule/SimpleMDE.jsx index 8c2283b43..b06b92526 100644 --- a/instances/events-committee.near/widget/devhub/components/molecule/SimpleMDE.jsx +++ b/instances/events-committee.near/widget/devhub/components/molecule/SimpleMDE.jsx @@ -11,17 +11,16 @@ const onChangeKeyup = props.onChangeKeyup ?? (() => {}); // in case where we wan const height = props.height ?? "390"; const className = props.className ?? "w-100"; const embeddCSS = props.embeddCSS; +const sortedRelevantUsers = props.sortedRelevantUsers || []; State.init({ iframeHeight: height, message: props.data, }); -const profilesData = Social.get("*/profile/name", "final"); -const followingData = Social.get( - `${context.accountId}/graph/follow/**`, - "final" -); +const profilesData = Social.get("*/profile/name", "final") ?? {}; +const followingData = + Social.get(`${context.accountId}/graph/follow/**`, "final") ?? {}; // SIMPLEMDE CONFIG // const fontFamily = props.fontFamily ?? "sans-serif"; @@ -139,7 +138,13 @@ function getSuggestedAccounts(term) { let results = []; term = (term || "").replace(/\W/g, "").toLowerCase(); - const limit = 5; + let limit = 5; + if (term.length < 2) { + results = [${sortedRelevantUsers + .map((u) => "{accountId:'" + u + "', score: 60}") + .join(",")}]; + limit = ${5 + sortedRelevantUsers.length}; + } const profiles = Object.entries(profilesData); diff --git a/instances/events-committee.near/widget/devhub/entity/proposal/CommentsAndLogs.jsx b/instances/events-committee.near/widget/devhub/entity/proposal/CommentsAndLogs.jsx index 386049776..b534383f5 100644 --- a/instances/events-committee.near/widget/devhub/entity/proposal/CommentsAndLogs.jsx +++ b/instances/events-committee.near/widget/devhub/entity/proposal/CommentsAndLogs.jsx @@ -317,7 +317,7 @@ const parseProposalKeyAndValue = (key, modifiedValue, originalValue) => { accepted diff --git a/instances/events-committee.near/widget/devhub/entity/proposal/Proposal.jsx b/instances/events-committee.near/widget/devhub/entity/proposal/Proposal.jsx index 573edbdc6..3e790cf62 100644 --- a/instances/events-committee.near/widget/devhub/entity/proposal/Proposal.jsx +++ b/instances/events-committee.near/widget/devhub/entity/proposal/Proposal.jsx @@ -294,6 +294,12 @@ const item = { path: `${REPL_EVENTS_CONTRACT}/post/main`, blockHeight, }; +const comments = Social.index("comment", item, { subscribe: true }) ?? []; + +const commentAuthors = [ + ...new Set(comments.map((comment) => comment.accountId)), +]; + const proposalURL = getLinkUsingCurrentGateway( `${REPL_EVENTS}/widget/app?page=proposal&id=${proposal.id}×tamp=${snapshot.timestamp}` ); @@ -451,6 +457,7 @@ const CheckBox = ({ value, isChecked, label, disabled, onClick }) => { return (
user !== accountId), }} />
diff --git a/playwright-tests/tests/events/proposals.spec.js b/playwright-tests/tests/events/proposals.spec.js index ac55af1d6..1520ec2ed 100644 --- a/playwright-tests/tests/events/proposals.spec.js +++ b/playwright-tests/tests/events/proposals.spec.js @@ -452,17 +452,17 @@ test.describe("Wallet is connected", () => { page, account, }) => { - await page.goto(`/${account}/widget/app?page=proposal&id=112`); + await page.goto(`/${account}/widget/app?page=proposal&id=2`); await page.waitForSelector(`iframe`, { state: "visible", }); - const comment = page.getByRole("link", { name: "geforcy.near" }); + const comment = page.getByRole("link", { name: "toronto-sc.near" }).first(); await comment.waitFor(); await comment.scrollIntoViewIfNeeded(); - const heading = page.getByRole("heading", { name: "Relevant Mentions" }); + const heading = page.getByText("Add a comment"); await heading.waitFor(); await heading.scrollIntoViewIfNeeded(); @@ -487,10 +487,10 @@ test.describe("Wallet is connected", () => { ); const liLocators = await liFrameLocators.owner().all(); const expected = [ - "thomasguntenaar.near", // author, - "theori.near", // supervisor, - "neardevdao.near", // requested_sponsor, - "geforcy.near", // comment author, + "toronto-sc.near", + "yarotska.near", + "events-committee.near", + "nneoma.near", ]; let mentionSuggestions = []; for (let i = 0; i < liLocators.length; i++) { From 96b4562583b3b5c38ec92b878d8fc27ed3f340a2 Mon Sep 17 00:00:00 2001 From: Thomas Date: Fri, 19 Jul 2024 13:16:02 +0200 Subject: [PATCH 5/6] fix: playwright config to run comments --- playwright-tests/tests/events/comment.spec.js | 293 ------------------ playwright.config.js | 2 +- 2 files changed, 1 insertion(+), 294 deletions(-) delete mode 100644 playwright-tests/tests/events/comment.spec.js diff --git a/playwright-tests/tests/events/comment.spec.js b/playwright-tests/tests/events/comment.spec.js deleted file mode 100644 index 2c92614cd..000000000 --- a/playwright-tests/tests/events/comment.spec.js +++ /dev/null @@ -1,293 +0,0 @@ -import { test as base, expect } from "@playwright/test"; -import { pauseIfVideoRecording } from "../../testUtils.js"; -import { setCommitWritePermissionDontAskAgainCacheValues } from "../../util/cache.js"; -import { - mockTransactionSubmitRPCResponses, - decodeResultJSON, - encodeResultJSON, -} from "../../util/transaction.js"; - -const test = base.extend({ - // Define an option and provide a default value. - // We can later override it in the config. - account: ["events-committee.near", { option: true }], - proposalAuthorAccountId: ["megha19.near", { option: true }], -}); - -test.describe("Don't ask again enabled", () => { - test.use({ - contextOptions: { - permissions: ["clipboard-read", "clipboard-write"], - }, - storageState: - "playwright-tests/storage-states/wallet-connected-with-devhub-access-key.json", - }); - test("should add comment on a proposal", async ({ - page, - account: instanceAccount, - }) => { - await page.goto(`/${instanceAccount}/widget/app?page=proposal&id=5`); - const widgetSrc = - instanceAccount === "infrastructure-committee.near" - ? "infrastructure-committee.near/widget/components.molecule.ComposeComment" - : `${instanceAccount}/widget/devhub.entity.proposal.ComposeComment`; - - const delay_milliseconds_between_keypress_when_typing = 0; - const commentArea = await page - .frameLocator("iframe") - .locator(".CodeMirror textarea"); - await commentArea.focus(); - const text = "Comment testing"; - await commentArea.pressSequentially(text, { - delay: delay_milliseconds_between_keypress_when_typing, - }); - await commentArea.blur(); - await pauseIfVideoRecording(page); - - const accountId = "petersalomonsen.near"; - await setCommitWritePermissionDontAskAgainCacheValues({ - page, - widgetSrc, - accountId: accountId, - }); - - await mockTransactionSubmitRPCResponses( - page, - async ({ route, request, transaction_completed, last_receiver_id }) => { - const postData = request.postDataJSON(); - const args_base64 = postData.params?.args_base64; - if (transaction_completed && args_base64) { - const args = atob(args_base64); - if ( - postData.params.account_id === "social.near" && - postData.params.method_name === "get" && - args === `{"keys":["${accountId}/post/**"]}` - ) { - const response = await route.fetch(); - const json = await response.json(); - const resultObj = decodeResultJSON(json.result.result); - resultObj[accountId].post.main = JSON.stringify({ - text: text, - }); - json.result.result = encodeResultJSON(resultObj); - await route.fulfill({ response, json }); - return; - } else { - await route.continue(); - } - } else { - await route.continue(); - } - } - ); - const commentButton = await page.getByRole("button", { name: "Comment" }); - await expect(commentButton).toBeAttached(); - await commentButton.scrollIntoViewIfNeeded(); - await commentButton.click(); - await expect( - await page.frameLocator("iframe").locator(".CodeMirror") - ).toContainText(text); - - const loadingIndicator = await page.locator(".comment-btn-spinner"); - await expect(loadingIndicator).toBeAttached(); - await loadingIndicator.waitFor({ state: "detached", timeout: 30000 }); - await expect(loadingIndicator).not.toBeVisible(); - const transaction_successful_toast = await page.getByText( - "Comment Submitted Successfully", - { exact: true } - ); - await expect(transaction_successful_toast).toBeVisible(); - - await expect(transaction_successful_toast).not.toBeAttached(); - await expect( - await page.frameLocator("iframe").locator(".CodeMirror") - ).not.toContainText(text); - await expect( - await page.frameLocator("iframe").locator(".CodeMirror") - ).toContainText("Add your comment here..."); - await pauseIfVideoRecording(page); - }); - test("should paste a long comment to a proposal, see that the comment appears after submission, and that the comment field is cleared, even after reloading the page", async ({ - page, - account: instanceAccount, - }) => { - test.setTimeout(2 * 60000); - await page.goto(`/${instanceAccount}/widget/app?page=proposal&id=5`); - const widgetSrc = - instanceAccount === "infrastructure-committee.near" - ? "infrastructure-committee.near/widget/components.molecule.ComposeComment" - : `${instanceAccount}/widget/devhub.entity.proposal.ComposeComment`; - - let commentButton = await page.getByRole("button", { name: "Comment" }); - await expect(commentButton).toBeAttached({ timeout: 10000 }); - await commentButton.scrollIntoViewIfNeeded(); - - const commentText = - "Hi @petersalomonsen.near – congratulations! This message confirms your funding request approval by @neardevdao.near. We're excited to sponsor your work! This approval follows our review process involving various Work Groups and DevDAO Moderators, as outlined in our [guidelines](/devhub.near/widget/app?page=community&handle=developer-dao&tab=Funding). Please note that the funding distribution is contingent on successfully passing our KYC/B and paperwork process.\n\nHere’s what to expect:\n\n**Funding Steps**\n\n1. **KYC/KYB Verification:** A DevDAO Moderator will move your proposal to the Payment Processing Stage and verify that you have completed verification to ensure compliance. If you are not verified, your DevHub Moderator will contact you on Telegram with instructions on how to proceed. To receive funding, you must get verified through Fractal, a trusted third-party identification verification solution. Your verification badge is valid for 365 days and needs renewal upon expiration OR if your personal information changes, such as your name, address, or ID expiration.\n2. **Information Collection:** Once verified, a DevDAO Moderator will contact you via Telegram and request that you complete the Funding Request Form using Airtable.\n3. **Processing:** Our legal team will verify your application details to ensure compliance. They will then send you an email requesting your signature for the underlying agreement via Ironclad.\n4. **Invoicing & Payment:** Once we receive your signed agreement, our finance team will email you instructions to submit the final invoice using Request Finance. Once we receive your invoice, our finance team will send a test transaction confirmation email. Once you confirm the test transaction, we will distribute the funds and post a payment link on your proposal.\n\n**Funding Conversion Notice**\n\nOnce you receive your funding, we urge you to exercise caution if attempting to convert your funds. Some third-party tools may impose significant swapping fees.\n\n**Visibility**\n\nWe track the funding process on each proposal using the timeline and comments. However, you are welcome to reach out to the DevDAO Moderator with any questions. \n\n**Timeline**\n\nTypically, funds are disbursed within 10 business days, but the timeline can vary depending on the project's complexity and paperwork. Your DevDAO Moderator will keep you updated."; - await page.evaluate(async (text) => { - await navigator.clipboard.writeText(text); - }, commentText); - - const commentArea = await page - .frameLocator("iframe") - .locator(".CodeMirror textarea"); - - await pauseIfVideoRecording(page); - await commentArea.focus(); - - const isMac = process.platform === "darwin"; - - if (isMac) { - await page.keyboard.down("Meta"); // Command key on macOS - await page.keyboard.press("a"); - await page.keyboard.press("v"); - await page.keyboard.up("Meta"); - } else { - await page.keyboard.down("Control"); // Control key on Windows/Linux - await page.keyboard.press("a"); - await page.keyboard.press("v"); - await page.keyboard.up("Control"); - } - - await commentArea.blur(); - await pauseIfVideoRecording(page); - - const userAccount = "petersalomonsen.near"; - await setCommitWritePermissionDontAskAgainCacheValues({ - page, - widgetSrc, - accountId: userAccount, - }); - - const transactionMockStatus = await mockTransactionSubmitRPCResponses( - page, - async ({ route, request, transaction_completed, last_transaction }) => { - const postData = request.postDataJSON(); - const args_base64 = postData.params?.args_base64; - - if (transaction_completed && args_base64) { - const args = atob(args_base64); - if ( - postData.params.account_id === "social.near" && - postData.params.method_name === "get" && - args === `{"keys":["${userAccount}/post/**"]}` - ) { - const response = await route.fetch(); - const json = await response.json(); - const resultObj = decodeResultJSON(json.result.result); - - resultObj[userAccount].post.main = JSON.stringify({ - text: commentText, - }); - json.result.result = encodeResultJSON(resultObj); - completedPromiseResolve(last_transaction); - await route.fulfill({ response, json }); - return; - } else { - await route.continue(); - } - } else { - await route.continue(); - } - } - ); - - let submittedTransactionJsonObjectPromiseResolve; - let submittedTransactionJsonObjectPromise = new Promise( - (r) => (submittedTransactionJsonObjectPromiseResolve = r) - ); - await page.route("https://api.near.social/index", async (route) => { - if (transactionMockStatus.transaction_completed) { - const lastTransactionParamBuffer = Buffer.from( - transactionMockStatus.last_transaction.params[0], - "base64" - ); - - const transactionDataJsonStartIndex = - lastTransactionParamBuffer.indexOf('{"data":'); - const transactionDataJsonEndIndex = - lastTransactionParamBuffer.indexOf('"}}}}') + '"}}}}'.length; - const transactionDataJsonString = lastTransactionParamBuffer.subarray( - transactionDataJsonStartIndex, - transactionDataJsonEndIndex - ); - - submittedTransactionJsonObjectPromiseResolve( - JSON.parse(transactionDataJsonString.toString()) - ); - - const response = await route.fetch(); - const json = await response.json(); - json.push({ - accountId: "theori.near", - blockHeight: 121684809, - value: { - type: "md", - }, - }); - - await route.fulfill({ json }); - } else { - await route.continue(); - } - }); - - commentButton = await page.getByRole("button", { name: "Comment" }); - await commentButton.click(); - - const loadingIndicator = await page.locator(".comment-btn-spinner"); - await expect(loadingIndicator).toBeAttached(); - await loadingIndicator.waitFor({ state: "detached", timeout: 30000 }); - await expect(loadingIndicator).not.toBeVisible(); - const transaction_successful_toast = await page.getByText( - "Comment Submitted Successfully", - { exact: true } - ); - await expect(transaction_successful_toast).toBeVisible(); - - await expect(transaction_successful_toast).not.toBeAttached({ - timeout: 10000, - }); - await expect( - await page.frameLocator("iframe").locator(".CodeMirror") - ).not.toContainText(commentText); - await expect( - await page.frameLocator("iframe").locator(".CodeMirror") - ).toContainText("Add your comment here..."); - - const submittedTransactionJsonObject = - await submittedTransactionJsonObjectPromise; - const submittedComment = JSON.parse( - submittedTransactionJsonObject.data["petersalomonsen.near"].post.comment - ); - expect(submittedComment.text).toEqual(commentText); - let commentElement = await page.locator("#theorinear121684809"); - await expect(commentElement).toBeVisible({ timeout: 10000 }); - await commentElement.scrollIntoViewIfNeeded(); - await expect(commentElement).toContainText( - "Typically, funds are disbursed within 10 business days, but the timeline can vary depending on the project's complexity and paperwork. Your DevDAO Moderator will keep you updated.", - { timeout: 10000 } - ); - - await page.reload(); - - commentElement = await page.locator("#theorinear121684809"); - await expect(commentElement).toBeVisible({ timeout: 20000 }); - await commentElement.scrollIntoViewIfNeeded(); - await expect(commentElement).toContainText( - "Typically, funds are disbursed within 10 business days, but the timeline can vary depending on the project's complexity and paperwork. Your DevDAO Moderator will keep you updated.", - { timeout: 10000 } - ); - - commentButton = await page.getByRole("button", { name: "Comment" }); - await expect(commentButton).toBeAttached({ timeout: 10000 }); - await commentButton.scrollIntoViewIfNeeded(); - - await page.waitForTimeout(5000); - await expect( - await page.frameLocator("iframe").locator(".CodeMirror") - ).toContainText("Add your comment here..."); - - await pauseIfVideoRecording(page); - }); -}); diff --git a/playwright.config.js b/playwright.config.js index d66b0899f..fef427f14 100644 --- a/playwright.config.js +++ b/playwright.config.js @@ -69,7 +69,7 @@ export default defineConfig({ }, { name: "events", - testMatch: /(events)\/.*.spec.js/, // (proposal|events) + testMatch: /(events|proposal)\/.*.spec.js/, // (proposal|events) use: { ...devices["Desktop Chrome"], baseURL: "http://localhost:8080", From 5e2098fa203c8a06a423f1e943ad57e077495417 Mon Sep 17 00:00:00 2001 From: Thomas Date: Tue, 23 Jul 2024 18:19:19 +0200 Subject: [PATCH 6/6] revert --- instances/events-committee.near/bos.config.json | 4 ---- 1 file changed, 4 deletions(-) diff --git a/instances/events-committee.near/bos.config.json b/instances/events-committee.near/bos.config.json index 63c0e0e2b..a382469fb 100644 --- a/instances/events-committee.near/bos.config.json +++ b/instances/events-committee.near/bos.config.json @@ -1,9 +1,5 @@ { "account": "events-committee.near", - "accounts": { - "deploy": "events-committee.near", - "signer": "events-committee.near" - }, "aliasPrefix": "REPL", "aliasesContainsPrefix": true, "aliases": ["./aliases.mainnet.json"]