From 0ea6431ac6aa360b5088d0bbfdf17aef40df74f6 Mon Sep 17 00:00:00 2001
From: Elliot Braem <16282460+elliotBraem@users.noreply.github.com>
Date: Wed, 10 Jul 2024 20:00:44 -0400
Subject: [PATCH] Adds "Wallet" custom element and signOut functionality within
the app (#29)
* init
* tests
* adds signIn and fixes tests
* formatting
* formatting
* revert open-walletselector-button
---
package.json | 1 +
playwright-tests/code/auth/wallet.js | 14 ++++
.../storage-states/wallet-connected.json | 14 ++--
playwright-tests/testUtils.js | 4 +-
playwright-tests/tests/auth.spec.js | 77 +++++++++++++++++++
src/App.js | 4 +
src/auth/Wallet.js | 31 ++++++++
yarn.lock | 49 +++++++++---
8 files changed, 178 insertions(+), 16 deletions(-)
create mode 100644 playwright-tests/code/auth/wallet.js
create mode 100644 playwright-tests/tests/auth.spec.js
create mode 100644 src/auth/Wallet.js
diff --git a/package.json b/package.json
index 0112576..10f461d 100644
--- a/package.json
+++ b/package.json
@@ -12,6 +12,7 @@
"LICENSE"
],
"dependencies": {
+ "@near-wallet-selector/modal-ui-js": "^8.9.10",
"@playwright/test": "^1.38.1",
"@web3-onboard/injected-wallets": "^2.11.1",
"@web3-onboard/ledger": "^2.7.1",
diff --git a/playwright-tests/code/auth/wallet.js b/playwright-tests/code/auth/wallet.js
new file mode 100644
index 0000000..a79821e
--- /dev/null
+++ b/playwright-tests/code/auth/wallet.js
@@ -0,0 +1,14 @@
+return (
+ <>
+
{context.accountId}
+ {
+ return context.accountId ? (
+
+ ) : (
+
+ );
+ }}
+ />
+ >
+);
diff --git a/playwright-tests/storage-states/wallet-connected.json b/playwright-tests/storage-states/wallet-connected.json
index 685dd20..e3593d7 100644
--- a/playwright-tests/storage-states/wallet-connected.json
+++ b/playwright-tests/storage-states/wallet-connected.json
@@ -4,17 +4,17 @@
{
"origin": "http://localhost:3000",
"localStorage": [
+ {
+ "name": "near-wallet-selector:recentlySignedInWallets",
+ "value": "[\"my-near-wallet\"]"
+ },
{
"name": "near-wallet-selector:selectedWalletId",
"value": "\"my-near-wallet\""
},
{
"name": "near_app_wallet_auth_key",
- "value": "{\"accountId\":\"anybody.near\",\"allKeys\":[\"ed25519:CziSGowWUKiP5N5pqGUgXCJXtqpySAk29YAU6zEs5RAi\"]}}"
- },
- {
- "name": "near-social-vm:v01::accountId:",
- "value": "anybody.near"
+ "value": "{\"accountId\":\"anybody.near\",\"allKeys\":[\"ed25519:CziSGowWUKiP5N5pqGUgXCJXtqpySAk29YAU6zEs5RAi\"]}"
},
{
"name": "near-api-js:keystore:anybody.near:mainnet",
@@ -23,6 +23,10 @@
{
"name": "near-wallet-selector:contract",
"value": "{\"contractId\":\"social.near\",\"methodNames\":[]}"
+ },
+ {
+ "name": "near-social-vm:v01::accountId:",
+ "value": "anybody.near"
}
]
}
diff --git a/playwright-tests/testUtils.js b/playwright-tests/testUtils.js
index dd04514..e02bc05 100644
--- a/playwright-tests/testUtils.js
+++ b/playwright-tests/testUtils.js
@@ -47,14 +47,14 @@ export const useCode = async (page, filePath, props) => {
const fullPath = path.join(__dirname, "code", filePath);
try {
const code = fs.readFileSync(fullPath, "utf8");
- const initialProps = props ? JSON.stringify(props) : "";
+ const initialProps = props ? JSON.stringify(props) : null;
// Set code and initialProps attribute
await page.evaluate(
({ code, initialProps }) => {
const viewer = document.querySelector("near-social-viewer");
viewer.setAttribute("code", code);
- viewer.setAttribute("initialprops", initialProps);
+ initialProps && viewer.setAttribute("initialprops", initialProps);
},
{ code, initialProps }
);
diff --git a/playwright-tests/tests/auth.spec.js b/playwright-tests/tests/auth.spec.js
new file mode 100644
index 0000000..6603e11
--- /dev/null
+++ b/playwright-tests/tests/auth.spec.js
@@ -0,0 +1,77 @@
+import { describe, expect, test } from "@playwright/test";
+import { useCode } from "../testUtils";
+
+describe("auth", () => {
+ test.beforeEach(async ({ page }) => {
+ await page.goto("/");
+ });
+
+ describe("User is not logged in", () => {
+ test.use({
+ storageState: "playwright-tests/storage-states/wallet-not-connected.json",
+ });
+
+ test("'context.accountId' should be null and show login button", async ({
+ page,
+ }) => {
+ await useCode(page, "auth/wallet.js");
+
+ const accountId = await page.textContent('[data-testid="accountId"]');
+ expect(accountId).toBe("");
+ });
+
+ test("should show wallet modal after clicking login button", async ({
+ page,
+ }) => {
+ await useCode(page, "auth/wallet.js");
+
+ await page.click("#open-walletselector-button");
+
+ const modal = await page.getByText("Connect Your Wallet");
+ expect(modal).not.toBeNull();
+ });
+ });
+
+ describe("User is logged in", () => {
+ test.use({
+ storageState: "playwright-tests/storage-states/wallet-connected.json",
+ });
+
+ test("should have 'context.accountId' be non null and show logout button", async ({
+ page,
+ }) => {
+ await useCode(page, "auth/wallet.js");
+
+ const accountId = page.getByTestId("accountId");
+ expect(accountId).toHaveText("anybody.near");
+ });
+
+ test("should prompt to disconnect wallet after clicking logout button", async ({
+ page,
+ }) => {
+ await useCode(page, "auth/wallet.js");
+
+ // Verify auth keys exist
+ const authKey = await page.evaluate(() => ({
+ near_app_wallet_auth_key: localStorage.getItem(
+ "near_app_wallet_auth_key"
+ ),
+ }));
+
+ expect(authKey).not.toBeNull();
+
+ await page.getByRole("button", { name: "Log out" }).click();
+
+ // Verify auth keys are removed
+ await page.waitForFunction(
+ () => {
+ return localStorage.getItem("near_app_wallet_auth_key") === null;
+ },
+ { timeout: 1000 }
+ );
+
+ const accountId = await page.textContent('[data-testid="accountId"]');
+ expect(accountId).toBe("");
+ });
+ });
+});
diff --git a/src/App.js b/src/App.js
index 9af4f93..3feb9a5 100644
--- a/src/App.js
+++ b/src/App.js
@@ -12,6 +12,7 @@ import {
RouterProvider,
useLocation,
} from "react-router-dom";
+import Wallet from "./auth/Wallet";
import { BosWorkspaceProvider, useRedirectMap } from "./utils/bos-workspace";
import { EthersProvider } from "./utils/web3/ethers";
@@ -76,6 +77,9 @@ function App(props) {
}
return ;
},
+ Wallet: (props) => {
+ return ;
+ },
},
features: {
enableComponentSrcDataKey: true,
diff --git a/src/auth/Wallet.js b/src/auth/Wallet.js
new file mode 100644
index 0000000..ac473fe
--- /dev/null
+++ b/src/auth/Wallet.js
@@ -0,0 +1,31 @@
+import { setupModal } from "@near-wallet-selector/modal-ui-js";
+import { useNear } from "near-social-vm";
+import { useCallback } from "react";
+
+function Wallet({ provides, config }) {
+ const near = useNear();
+
+ const signOut = useCallback(async () => {
+ const wallet = await (await near.selector).wallet();
+ wallet.signOut();
+ }, [near]);
+
+ const signIn = useCallback(async () => {
+ const modal = setupModal(await near.selector, config);
+ modal.show();
+ }, [near]);
+
+ return provides({ signIn, signOut });
+}
+
+Wallet.defaultProps = {
+ provides: {
+ signIn: () => console.log("signIn"),
+ signOut: () => console.log("signOut"),
+ },
+ config: {
+ contractId: "social.near",
+ },
+};
+
+export default Wallet;
diff --git a/yarn.lock b/yarn.lock
index 4ba30db..c74d965 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2670,6 +2670,25 @@
bn.js "5.2.1"
borsh "^0.7.0"
+"@near-wallet-selector/core@8.9.10":
+ version "8.9.10"
+ resolved "https://registry.npmjs.org/@near-wallet-selector/core/-/core-8.9.10.tgz#0e19b22bbe69208c9e82b149682be37f6f9b76c5"
+ integrity sha512-do+DDahRHPzr5VKiFS7NWKyNbspXu64/w7CuSBi8IUDsDsclmV7Os6Hp5HcVAq+X3Whi//NxKGX6mPMb+SRPqw==
+ dependencies:
+ borsh "0.7.0"
+ events "3.3.0"
+ js-sha256 "0.9.0"
+ rxjs "7.8.1"
+
+"@near-wallet-selector/modal-ui-js@^8.9.10":
+ version "8.9.10"
+ resolved "https://registry.npmjs.org/@near-wallet-selector/modal-ui-js/-/modal-ui-js-8.9.10.tgz#9caa6e06e8c9a3b41227d8a018f022e28c7b40f2"
+ integrity sha512-NrtwRZwMotQ/XeULRe3tCfSyMwtVlnwVqUaHi0vJ1CKijSc5xR9fmVv1Bl9+xjP1bykhniWchb4O1wE1lTm3MQ==
+ dependencies:
+ "@near-wallet-selector/core" "8.9.10"
+ copy-to-clipboard "3.3.3"
+ qrcode "1.5.3"
+
"@nicolo-ribaudo/chokidar-2@2.1.8-no-fsevents.3":
version "2.1.8-no-fsevents.3"
resolved "https://registry.npmjs.org/@nicolo-ribaudo/chokidar-2/-/chokidar-2-2.1.8-no-fsevents.3.tgz#323d72dd25103d0c4fbdce89dadf574a787b1f9b"
@@ -5924,7 +5943,7 @@ bootstrap@^5.2.1, bootstrap@^5.3.1:
resolved "https://registry.npmjs.org/bootstrap/-/bootstrap-5.3.3.tgz#de35e1a765c897ac940021900fcbb831602bac38"
integrity sha512-8HLCdWgyoMguSO9o+aH+iuZ+aht+mzW0u3HIMzVu7Srrpv7EBBxTnrFlSCskwdY1+EOFQSm7uMJhNQHkdPcmjg==
-borsh@^0.7.0:
+borsh@0.7.0, borsh@^0.7.0:
version "0.7.0"
resolved "https://registry.npmjs.org/borsh/-/borsh-0.7.0.tgz#6e9560d719d86d90dc589bca60ffc8a6c51fec2a"
integrity sha512-CLCsZGIBCFnPtkNnieW/a8wmreDmfUtjU2m9yHrzPXIlNbqVs0AQrSatSG6vdNYUqdc83tkQi2eHfF98ubzQLA==
@@ -6538,6 +6557,13 @@ cookies@~0.9.0:
depd "~2.0.0"
keygrip "~1.1.0"
+copy-to-clipboard@3.3.3:
+ version "3.3.3"
+ resolved "https://registry.npmjs.org/copy-to-clipboard/-/copy-to-clipboard-3.3.3.tgz#55ac43a1db8ae639a4bd99511c148cdd1b83a1b0"
+ integrity sha512-2KV8NhB5JqC3ky0r9PMCAZKbUHSwtEo4CwCs0KXgruG43gX5PMqDEBbVU4OUzw2MuAWUfsuFmWvEKG5QRfSnJA==
+ dependencies:
+ toggle-selection "^1.0.6"
+
copy-webpack-plugin@^9.0.1:
version "9.1.0"
resolved "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-9.1.0.tgz#2d2c460c4c4695ec0a58afb2801a1205256c4e6b"
@@ -8979,7 +9005,7 @@ joi@17.9.1:
"@sideway/formula" "^3.0.1"
"@sideway/pinpoint" "^2.0.0"
-js-sha256@^0.9.0:
+js-sha256@0.9.0, js-sha256@^0.9.0:
version "0.9.0"
resolved "https://registry.npmjs.org/js-sha256/-/js-sha256-0.9.0.tgz#0b89ac166583e91ef9123644bd3c5334ce9d0966"
integrity sha512-sga3MHh9sgQN2+pJ9VYZ+1LPwXOxuBJBA5nrR5/ofPfuiJBE2hnjsaN8se8JznOmGLN2p49Pe5U/ttafcs/apA==
@@ -11906,6 +11932,13 @@ run-parallel@^1.1.9:
dependencies:
queue-microtask "^1.2.2"
+rxjs@7.8.1, rxjs@^7.5.2, rxjs@^7.5.5:
+ version "7.8.1"
+ resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.8.1.tgz#6f6f3d99ea8044291efd92e7c7fcf562c4057543"
+ integrity sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==
+ dependencies:
+ tslib "^2.1.0"
+
rxjs@^6.6.3:
version "6.6.7"
resolved "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz#90ac018acabf491bf65044235d5863c4dab804c9"
@@ -11913,13 +11946,6 @@ rxjs@^6.6.3:
dependencies:
tslib "^1.9.0"
-rxjs@^7.5.2, rxjs@^7.5.5:
- version "7.8.1"
- resolved "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz#6f6f3d99ea8044291efd92e7c7fcf562c4057543"
- integrity sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==
- dependencies:
- tslib "^2.1.0"
-
sade@^1.7.3, sade@^1.8.1:
version "1.8.1"
resolved "https://registry.npmjs.org/sade/-/sade-1.8.1.tgz#0a78e81d658d394887be57d2a409bf703a3b2701"
@@ -12746,6 +12772,11 @@ to-regex-range@^5.0.1:
dependencies:
is-number "^7.0.0"
+toggle-selection@^1.0.6:
+ version "1.0.6"
+ resolved "https://registry.npmjs.org/toggle-selection/-/toggle-selection-1.0.6.tgz#6e45b1263f2017fa0acc7d89d78b15b8bf77da32"
+ integrity sha512-BiZS+C1OS8g/q2RRbJmy59xpyghNBqrr6k5L/uKBGRsTfxmu3ffiRnd8mlGPUVayg8pvfi5urfnu8TU7DVOkLQ==
+
toidentifier@1.0.1:
version "1.0.1"
resolved "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35"