From 158a437080c210222bd86a46b65119de14f9f418 Mon Sep 17 00:00:00 2001
From: Elliot Braem <16282460+elliotBraem@users.noreply.github.com>
Date: Mon, 18 Dec 2023 23:18:02 -0800
Subject: [PATCH] fix create page
---
playwright-tests/tests/create.spec.js | 110 ++++-
src/devhub/page/create.jsx | 591 ++++++++++++--------------
2 files changed, 356 insertions(+), 345 deletions(-)
diff --git a/playwright-tests/tests/create.spec.js b/playwright-tests/tests/create.spec.js
index 22dafad3f..3ba16554f 100644
--- a/playwright-tests/tests/create.spec.js
+++ b/playwright-tests/tests/create.spec.js
@@ -1,24 +1,98 @@
-import { test } from "@playwright/test";
-import { setInputAndAssert, selectAndAssert } from "../testUtils";
+import { expect, test } from "@playwright/test";
+import { selectAndAssert, setInputAndAssert } from "../testUtils";
-test("should be able to submit a solution with USDC as currency", async ({
- page,
-}) => {
- await page.goto("/devgovgigs.near/widget/app?page=create");
+test.describe("Wallet is not connected", () => {
+ // sign in to wallet
+ test.use({
+ storageState: "playwright-tests/storage-states/wallet-not-connected.json",
+ });
- await page.click('button:has-text("Solution")');
+ test("should not be able to create if not logged in", async ({ page }) => {
+ await page.goto("/devhub.near/widget/app?page=create");
- await setInputAndAssert(
- page,
- 'p:has-text("Title") + input',
- "The test title"
- );
+ const createPostButton = 'button[data-testid="submit-create-post"]';
+
+ await page.waitForSelector(createPostButton, {
+ state: "detached",
+ });
+
+ const isCreatePostButtonVisible = await page.isVisible(createPostButton);
+
+ expect(isCreatePostButtonVisible).toBeFalsy();
+ });
+});
+
+test.describe("Wallet is connected", () => {
+ // sign in to wallet
+ test.use({
+ storageState: "playwright-tests/storage-states/wallet-connected.json",
+ });
- await page.click('label:has-text("Yes") button');
- await selectAndAssert(page, 'div:has-text("Currency") select', "USDT");
- await setInputAndAssert(
+ test("should be able to submit a solution with USDC as currency", async ({
page,
- 'div:has-text("Requested amount") input',
- "300"
- );
+ }) => {
+ await page.goto("/devhub.near/widget/app?page=create");
+
+ await page.click('button:has-text("Solution")');
+
+ await setInputAndAssert(
+ page,
+ 'p:has-text("Title") + input',
+ "The test title"
+ );
+
+ await page.click('label:has-text("Yes") button');
+ await selectAndAssert(page, 'div:has-text("Currency") select', "USDT");
+ await setInputAndAssert(
+ page,
+ 'div:has-text("Requested amount") input',
+ "300"
+ );
+ });
+
+ test("should init create post with labels from params", async ({ page }) => {
+ await page.goto("/devhub.near/widget/app?page=create&labels=devhub-test");
+
+ const selector = 'div:has-text("Labels") input';
+
+ const singleValue = await page.inputValue(selector);
+ expect(singleValue).toBe("devhub-test");
+
+ await page.goto("/devhub.near/widget/app?page=create&labels=devhub-test");
+
+ const multiValue = await page.inputValue(selector);
+ expect(multiValue).toBe("devhub-test");
+ });
+
+ test("should allow user to select multiple labels", async ({ page }) => {
+ await page.goto("/devhub.near/widget/app?page=create");
+
+ const selector = 'div:has-text("Labels") input';
+
+ await page.fill(selector, "devhub-test, security");
+ const actualValue = await page.inputValue(selector);
+ expect(actualValue).toBe(["devhub-test", "security"]);
+ });
+
+ test("should not allow user to use the blog label", async ({ page }) => {
+ await page.goto("/devhub.near/widget/app?page=create");
+
+ const selector = 'div:has-text("Labels") input';
+
+ await page.fill(selector, "blog");
+ const actualValue = await page.inputValue(selector);
+ expect(actualValue).toBe("");
+ });
+
+ test("should allow the user to create new labels", async ({ page }) => {
+ await page.goto("/devhub.near/widget/app?page=create");
+
+ await page.goto("/devhub.near/widget/app?page=create");
+
+ const selector = 'div:has-text("Labels") input';
+
+ await page.fill(selector, "random-crazy-label-lol");
+ const actualValue = await page.inputValue(selector);
+ expect(actualValue).toBe("random-crazy-label-lol");
+ });
});
diff --git a/src/devhub/page/create.jsx b/src/devhub/page/create.jsx
index 54ecdb65e..cfb645115 100644
--- a/src/devhub/page/create.jsx
+++ b/src/devhub/page/create.jsx
@@ -1,26 +1,45 @@
+const CenteredMessage = styled.div`
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ align-items: center;
+ width: 100%;
+ height: 384px;
+`;
+
+if (!context.accountId) {
+ return (
+ Please sign in to create a post.
+
Loading modules...
; } -const parentId = props.parentId ?? null; -const postId = props.postId ?? null; -const mode = props.mode ?? "Create"; - -const referralLabels = props.referral ? [`referral:${props.referral}`] : []; -const labelStrings = (props.labels ? props.labels.split(",") : []).concat( - referralLabels -); -const labels = labelStrings.map((s) => { - return { name: s }; -}); - -State.update({ - labels, -}); - -if (state.waitForDraftStateRestore) { - const draftstatestring = Storage.privateGet(DRAFT_STATE_STORAGE_KEY); - if (draftstatestring != null) { - if (props.transactionHashes) { - State.update({ waitForDraftStateRestore: false }); - Storage.privateSet(DRAFT_STATE_STORAGE_KEY, undefined); - } else { - try { - const draftstate = JSON.parse(draftstatestring); - State.update(draftstate); - } catch (e) { - console.error("error restoring draft", draftstatestring); - } - } - State.update({ waitForDraftStateRestore: false }); - } -} - // This must be outside onClick, because Near.view returns null at first, and when the view call finished, it returns true/false. // If checking this inside onClick, it will give `null` and we cannot tell the result is true or false. let grantNotify = Near.view("social.near", "is_write_permission_granted", { @@ -129,10 +107,6 @@ if (grantNotify === null) { } const onSubmit = () => { - Storage.privateSet(DRAFT_STATE_STORAGE_KEY, JSON.stringify(state)); - - let labels = state.labelStrings; - let body = { name: state.name, description: generateDescription( @@ -159,47 +133,33 @@ const onSubmit = () => { }; } - if (!context.accountId) return; - let txn = []; - if (mode == "Create") { - txn.push({ - contractName: "${REPL_DEVHUB_CONTRACT}", - methodName: "add_post", - args: { - parent_id: parentId, - labels, - body: body, - }, - gas: Big(10).pow(14), - }); - } else if (mode == "Edit") { - txn.push({ - contractName: "${REPL_DEVHUB_CONTRACT}", - methodName: "edit_post", + + txn.push({ + contractName: "${REPL_DEVHUB_CONTRACT}", + methodName: "add_post", + args: { + parent_id: null, + labels: state.labels, + body: body, + }, + gas: Big(10).pow(14), + }); + + if (grantNotify === false) { + txn.unshift({ + contractName: "social.near", + methodName: "grant_write_permission", args: { - id: postId, - labels, - body: body, + predecessor_id: "${REPL_DEVHUB_CONTRACT}", + keys: [context.accountId + "/index/notify"], }, gas: Big(10).pow(14), + deposit: Big(10).pow(22), }); } - if (mode == "Create" || mode == "Edit") { - if (grantNotify === false) { - txn.unshift({ - contractName: "social.near", - methodName: "grant_write_permission", - args: { - predecessor_id: "${REPL_DEVHUB_CONTRACT}", - keys: [context.accountId + "/index/notify"], - }, - gas: Big(10).pow(14), - deposit: Big(10).pow(22), - }); - } - Near.call(txn); - } + + Near.call(txn); }; const onIdeaClick = () => { @@ -240,218 +200,222 @@ const checkLabel = (label) => { }; const setLabels = (labels) => { - labels = labels.map((o) => { - o.name = normalizeLabel(o.name); - return o; - }); - if (labels.length < state.labels.length) { - let oldLabels = new Set(state.labels.map((label) => label.name)); - for (let label of labels) { - oldLabels.delete(label.name); - } - let removed = oldLabels.values().next().value; - Near.asyncView("${REPL_DEVHUB_CONTRACT}", "is_allowed_to_use_labels", { - editor: context.accountId, - labels: [removed], - }).then((allowed) => { - if (allowed) { - let labelStrings = labels.map(({ name }) => name); - State.update({ labels, labelStrings }); - } else { - State.update({ - warning: - 'The label "' + - removed + - '" is protected and can only be updated by moderators', - }); - return; + const normalizedLabels = labels.map((o) => + o.customOption ? normalizeLabel(o.label) : normalizeLabel(o) + ); + const uniqueLabels = [...new Set(normalizedLabels)]; + + if (uniqueLabels.length < state.labels.length) { + const removedLabel = state.labels.find( + (label) => !uniqueLabels.includes(label) + ); + + const allowed = Near.asyncView( + "${REPL_DEVHUB_CONTRACT}", + "is_allowed_to_use_labels", + { + editor: context.accountId, + labels: [removedLabel], } - }); + ); + + if (allowed) { + State.update({ labels: uniqueLabels }); + } else { + State.update({ + warning: `The label "${removedLabel}" is protected and can only be updated by moderators`, + }); + } } else { - let labelStrings = labels.map((o) => { - return o.name; - }); - State.update({ labels, labelStrings }); + State.update({ labels: uniqueLabels }); } }; -const existingLabelStrings = + +const existingLabels = Near.view("${REPL_DEVHUB_CONTRACT}", "get_all_allowed_labels", { editor: context.accountId, }) ?? []; -const existingLabelSet = new Set(existingLabelStrings); -const existingLabels = existingLabelStrings - .filter((it) => it !== "blog") // remove blog label so users cannot publish blogs from feed - .map((s) => { - return { name: s }; - }); +const allowedLabels = existingLabels.filter((it) => it !== "blog"); // remove blog label so users cannot publish blogs from feed -const labelEditor = ( -Labels
-Title
- State.update({ name: event.target.value })} - /> -Description
-Title
+ State.update({ name: event.target.value })} + /> +- Are you seeking funding for your solution? - (Optional) -
-Labels
+- Requested sponsor (Optional) -
-- If you are requesting funding from a specific sponsor, please enter - their username. -
-+ Are you seeking funding for your solution? + (Optional) +
++ Requested sponsor (Optional) +
++ If you are requesting funding from a specific sponsor, please enter + their username. +
+{state.seekingFunding}