diff --git a/config/webpack.development.js b/config/webpack.development.js index 64db178..01307e5 100644 --- a/config/webpack.development.js +++ b/config/webpack.development.js @@ -2,7 +2,7 @@ const path = require("path"); const { HotModuleReplacementPlugin } = require("webpack"); module.exports = () => ({ - devtool: false, + devtool: 'inline-source-map', module: { rules: [ { diff --git a/package.json b/package.json index 5ec67cd..a3d44fe 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "near-bos-webcomponent", - "version": "0.0.6", + "version": "0.0.7", "homepage": "https://github.com/petersalomonsen/near-bos-webcomponent", "repository": { "type": "git", diff --git a/playwright-tests/tests/rpc.spec.js b/playwright-tests/tests/rpc.spec.js new file mode 100644 index 0000000..48f60de --- /dev/null +++ b/playwright-tests/tests/rpc.spec.js @@ -0,0 +1,85 @@ +import { expect, test } from "@playwright/test"; +import { waitForSelectorToBeVisible } from "../testUtils"; + +const DEFAULT_RPC_URL = "https://rpc.mainnet.near.org/"; +const CUSTOM_RPC_URL = "https://custom-rpc-url.com/"; + +test("Verify default RPC is called when value not provided", async ({ + page, +}) => { + // Set up a listener for the default rpc request + const defaultRpcRequestPromise = page.waitForRequest(DEFAULT_RPC_URL); + + // Navigate to the default route + await page.goto("/"); + + // Verify the viewer is visible + await waitForSelectorToBeVisible(page, "near-social-viewer"); + + // Get the value of the rpc attribute + const rpc = await page.evaluate(() => { + const viewer = document.querySelector("near-social-viewer"); + return viewer.getAttribute("rpc"); + }); + + // expect value to be undefined (aka use default value) + expect(rpc, undefined); + + // Wait for the widget to render + await waitForSelectorToBeVisible( + page, + 'div[data-component="devs.near/widget/default"]' + ); + + // Assert that the default RPC request has been made + const defaultRpcRequest = await defaultRpcRequestPromise; + expect(defaultRpcRequest).toBeTruthy(); +}); + +test("Verify custom RPC is called when provided", async ({ page }) => { + // Navigate to the default route + await page.goto("/"); + + // Verify the viewer is visible + await waitForSelectorToBeVisible(page, "near-social-viewer"); + + // Mock the custom rpc call so that the request doesn't hang + await page.route(CUSTOM_RPC_URL, (route) => { + route.fulfill({ + status: 200, + contentType: "application/json", + body: JSON.stringify({ result: "some valid response" }), + }); + }); + + // Set the rpc attribute to a custom rpc value + await page.evaluate((url) => { + const viewer = document.querySelector("near-social-viewer"); + viewer.setAttribute("rpc", url); + }, CUSTOM_RPC_URL); + + // Get the value of the rpc attribute + const actualRpc = await page.evaluate(() => { + const viewer = document.querySelector("near-social-viewer"); + return viewer.getAttribute("rpc"); + }); + + // Assert it equals custom value + expect(actualRpc).toBe(CUSTOM_RPC_URL); + + // Wait for the widget to render + await waitForSelectorToBeVisible( + page, + 'div[data-component="devs.near/widget/default"]' + ); + + // Set the src to new value (which should trigger the custom rpc call) + await page.evaluate(() => { + const viewer = document.querySelector("near-social-viewer"); + viewer.setAttribute("src", "efiz.near/widget/Tree"); + }); + + // Expect that the custom RPC is called + const customRpcRequest = await page.waitForRequest(CUSTOM_RPC_URL); + expect(customRpcRequest).toBeTruthy(); +}); diff --git a/public/index.html b/public/index.html index 02d9882..e0e4f21 100644 --- a/public/index.html +++ b/public/index.html @@ -1,107 +1,117 @@ + + + + + Near social + + + +
+ +
+ + - - - - - Near social - - - -
- - - - - + + - \ No newline at end of file + diff --git a/src/App.js b/src/App.js index 80b9551..933ae52 100644 --- a/src/App.js +++ b/src/App.js @@ -2,7 +2,7 @@ import "App.scss"; import "bootstrap-icons/font/bootstrap-icons.css"; import "bootstrap/dist/js/bootstrap.bundle"; import { Widget } from "near-social-vm"; -import React, { useEffect, useMemo, useState } from "react"; +import React, { useEffect, useMemo } from "react"; import "react-bootstrap-typeahead/css/Typeahead.css"; import { sanitizeUrl } from "@braintree/sanitize-url"; @@ -14,12 +14,9 @@ import { useLocation, } from "react-router-dom"; -const SESSION_STORAGE_REDIRECT_MAP_KEY = "nearSocialVMredirectMap"; - function Viewer({ widgetSrc, code, initialProps }) { const location = useLocation(); const searchParams = new URLSearchParams(location.search); - const [redirectMap, setRedirectMap] = useState({}); // create props from params const passProps = useMemo(() => { @@ -36,82 +33,60 @@ function Viewer({ widgetSrc, code, initialProps }) { return pathSrc; }, [widgetSrc, path]); - useEffect(() => { - const fetchRedirectMap = async () => { - try { - const localStorageFlags = JSON.parse( - localStorage.getItem("flags") || "{}" - ); - let redirectMapData; - - if (localStorageFlags.bosLoaderUrl) { - const response = await fetch(localStorageFlags.bosLoaderUrl); - const data = await response.json(); - redirectMapData = data.components; - } else { - redirectMapData = JSON.parse( - sessionStorage.getItem(SESSION_STORAGE_REDIRECT_MAP_KEY) || "{}" - ); - } - setRedirectMap(redirectMapData); - } catch (error) { - console.error("Error fetching redirect map:", error); - } - }; - fetchRedirectMap(); - }, []); - return ( <> ); } function App(props) { + const { src, code, initialProps, rpc, selectorPromise } = props; const { initNear } = useInitNear(); useAccount(); useEffect(() => { - initNear && - initNear({ - networkId: "mainnet", - selector: props.selectorPromise, - customElements: { - Link: (props) => { - if (!props.to && props.href) { - props.to = props.href; - delete props.href; - } - if (props.to) { - props.to = sanitizeUrl(props.to); - } - return ; - }, - }, - features: { - enableComponentSrcDataKey: true, - }, - config: { - defaultFinality: undefined, + const networkId = "mainnet"; + + const config = { + networkId: networkId, + selector: selectorPromise, + customElements: { + Link: (props) => { + if (!props.to && props.href) { + props.to = props.href; + delete props.href; + } + if (props.to) { + props.to = sanitizeUrl(props.to); + } + return ; }, - }); - }, [initNear]); + }, + features: { + enableComponentSrcDataKey: true, + }, + config: { + defaultFinality: undefined, + }, + }; + + if (rpc) { + config.config.nodeUrl = rpc; + } + + initNear && initNear(config); + }, [initNear, rpc]); const router = createBrowserRouter([ { path: "/*", element: ( - + ), }, //{ path: "/*", element: }, diff --git a/src/App.scss b/src/App.scss index 413020d..af6a7ca 100644 --- a/src/App.scss +++ b/src/App.scss @@ -32,18 +32,3 @@ a { } } -.sidebar-items { - &> div { - border-right: 1px solid #e5e5e5; - text-align: center; - min-height: 4rem; - max-height: 4rem; - display: flex; - } - - &> div:not(.apps) { - min-width: 4rem; - align-items: center !important; - justify-content: center !important; - } -} diff --git a/src/index.js b/src/index.js index 7fc9c5b..0d9d04d 100644 --- a/src/index.js +++ b/src/index.js @@ -9,6 +9,7 @@ class NearSocialViewerElement extends HTMLElement { this.attachShadow({ mode: "open" }); this.shadowRoot.innerHTML = ``; this.selectorPromise = new Promise(resolve => this.selectorPromiseResolve = resolve); + this.reactRoot = null; } set selector(selector) { @@ -24,15 +25,16 @@ class NearSocialViewerElement extends HTMLElement { } static get observedAttributes() { - return ['src', 'code', 'initialprops']; + return ['src', 'code', 'initialprops', 'rpc']; } renderRoot() { const src = this.getAttribute('src'); const code = this.getAttribute('code'); const initialProps = this.getAttribute('initialprops'); + const rpc = this.getAttribute('rpc'); - this.reactRoot.render(); + this.reactRoot.render(); } attributeChangedCallback(name, oldValue, newValue) {