From e8070a27e8175293bf478dd7f3f87e97703ec7c8 Mon Sep 17 00:00:00 2001
From: Elliot Braem <16282460+elliotBraem@users.noreply.github.com>
Date: Tue, 9 Jul 2024 07:46:04 -0400
Subject: [PATCH] Adds tests for Near.call (#25)
* init tests
* adds tests for vm custom elements
* fix structure for video, update pause method
* wip
* wip
* passing unhappy paths
* renames
* happy path tests
* validates more transaction details
---
.../code/near-call/object-params.js | 7 +
.../code/near-call/positional-params.js | 13 ++
.../storage-states/wallet-connected.json | 31 +++
.../storage-states/wallet-not-connected.json | 10 +
playwright-tests/testUtils.js | 27 +++
playwright-tests/tests/vm/near-call.spec.js | 216 ++++++++++++++++++
6 files changed, 304 insertions(+)
create mode 100644 playwright-tests/code/near-call/object-params.js
create mode 100644 playwright-tests/code/near-call/positional-params.js
create mode 100644 playwright-tests/storage-states/wallet-connected.json
create mode 100644 playwright-tests/storage-states/wallet-not-connected.json
create mode 100644 playwright-tests/tests/vm/near-call.spec.js
diff --git a/playwright-tests/code/near-call/object-params.js b/playwright-tests/code/near-call/object-params.js
new file mode 100644
index 0000000..bddd608
--- /dev/null
+++ b/playwright-tests/code/near-call/object-params.js
@@ -0,0 +1,7 @@
+const { tx } = props;
+
+function handleClick() {
+ Near.call(tx);
+}
+
+return ;
diff --git a/playwright-tests/code/near-call/positional-params.js b/playwright-tests/code/near-call/positional-params.js
new file mode 100644
index 0000000..c52fde7
--- /dev/null
+++ b/playwright-tests/code/near-call/positional-params.js
@@ -0,0 +1,13 @@
+const { contractName, methodName, args, gas, deposit, extra } = props;
+
+function handleClick() {
+ if (extra) {
+ Near.call(contractName, methodName, args, gas, deposit, extra);
+ } else if (contractName && !methodName && !args && !gas && !deposit) {
+ Near.call(contractName);
+ } else {
+ Near.call(contractName, methodName, args, gas, deposit);
+ }
+}
+
+return ;
diff --git a/playwright-tests/storage-states/wallet-connected.json b/playwright-tests/storage-states/wallet-connected.json
new file mode 100644
index 0000000..4e8bde5
--- /dev/null
+++ b/playwright-tests/storage-states/wallet-connected.json
@@ -0,0 +1,31 @@
+{
+ "cookies": [],
+ "origins": [
+ {
+ "origin": "http://localhost:3000",
+ "localStorage": [
+ {
+ "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"
+ },
+ {
+ "name": "near-api-js:keystore:anybody.near:mainnet",
+ "value": "ed25519:67p9ygtfVNZz5AzMkeN4bqstCck8RWxWDthcTa7JaBvxkrBRTc6A43SsuPy9LdtiR6XtSRD1HiS4KQTWCZw83FKS"
+ },
+ {
+ "name": "near-wallet-selector:contract",
+ "value": "{\"contractId\":\"social.near\",\"methodNames\":[]}"
+ }
+ ]
+ }
+ ],
+ "sessionStorage": []
+}
\ No newline at end of file
diff --git a/playwright-tests/storage-states/wallet-not-connected.json b/playwright-tests/storage-states/wallet-not-connected.json
new file mode 100644
index 0000000..2aaa5a3
--- /dev/null
+++ b/playwright-tests/storage-states/wallet-not-connected.json
@@ -0,0 +1,10 @@
+{
+ "cookies": [],
+ "origins": [
+ {
+ "origin": "http://localhost:3000",
+ "localStorage": []
+ }
+ ],
+ "sessionStorage": []
+}
diff --git a/playwright-tests/testUtils.js b/playwright-tests/testUtils.js
index 7c89430..dd04514 100644
--- a/playwright-tests/testUtils.js
+++ b/playwright-tests/testUtils.js
@@ -1,4 +1,6 @@
import { expect } from "@playwright/test";
+import path from "path";
+import fs from "fs";
export const pauseIfVideoRecording = async (page) => {
let isVideoRecorded = (await page.video()) ? true : false;
@@ -40,3 +42,28 @@ export const escapeHtml = (html) => {
.replace(/"/g, """)
.replace(/'/g, "'");
};
+
+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) : "";
+
+ // 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);
+ },
+ { code, initialProps }
+ );
+
+ // Verify the viewer is visible
+ await waitForSelectorToBeVisible(page, "near-social-viewer");
+
+ await pauseIfVideoRecording(page);
+ } catch (err) {
+ throw new Error(`Error loading file: ${err.message}`);
+ }
+};
diff --git a/playwright-tests/tests/vm/near-call.spec.js b/playwright-tests/tests/vm/near-call.spec.js
new file mode 100644
index 0000000..9553794
--- /dev/null
+++ b/playwright-tests/tests/vm/near-call.spec.js
@@ -0,0 +1,216 @@
+import { describe, expect, test } from "@playwright/test";
+import { pauseIfVideoRecording, useCode } from "../../testUtils";
+
+describe("Near.call", () => {
+ 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("should throw 'No wallet selected' error", async ({ page }) => {
+ const expectedErrorMessage = "No wallet selected";
+
+ await useCode(page, "near-call/positional-params.js", {
+ contractName: "hello.near-examples.near",
+ methodName: "set_greeting",
+ });
+
+ // Expect error message to be displayed
+ const [error] = await Promise.all([
+ page.waitForEvent("pageerror"),
+ await page.getByRole("button", { name: "click" }).click(),
+ ]);
+
+ await pauseIfVideoRecording(page);
+
+ // Verify that the expected error message was logged
+ expect(error).toBeTruthy();
+ expect(error.message).toContain(expectedErrorMessage);
+ });
+ });
+
+ describe("User is logged in", () => {
+ test.use({
+ storageState: "playwright-tests/storage-states/wallet-connected.json",
+ });
+
+ describe("arguments: (contractName, methodName, args?, gas?, deposit?)", () => {
+ test("should throw error if appropriate arguments are not provide (over 5 args)", async ({
+ page,
+ }) => {
+ const expectedErrorMessage =
+ "Method: Near.call. Required argument: 'contractName'. If the first argument is a string: 'methodName'. Optional: 'args', 'gas' (defaults to 300Tg), 'deposit' (defaults to 0)";
+
+ await useCode(page, "near-call/positional-params.js", {
+ contractName: "hello.near-examples.near",
+ methodName: "set_greeting",
+ args: { message: "Hello, World!" },
+ gas: "300000000000000",
+ deposit: "1000000000000000000000000",
+ extra: "extra argument",
+ });
+
+ // Expect error message to be displayed
+ const [error] = await Promise.all([
+ page.waitForEvent("pageerror"),
+ await page.getByRole("button", { name: "click" }).click(),
+ ]);
+
+ await pauseIfVideoRecording(page);
+
+ // Verify that the expected error message was logged
+ expect(error).toBeTruthy();
+ expect(error.message).toContain(expectedErrorMessage);
+ });
+ test("should open confirmation modal with appropriate details", async ({
+ page,
+ }) => {
+ const expectedTransactionData = { message: "Hello, World!" };
+
+ await useCode(page, "near-call/positional-params.js", {
+ contractName: "hello.near-examples.near",
+ methodName: "set_greeting",
+ args: { message: "Hello, World!" },
+ gas: "300000000000000",
+ deposit: "1000000000000000000000000",
+ });
+
+ await page.getByRole("button", { name: "click" }).click();
+
+ const transactionObj = JSON.parse(
+ await page.locator("div.modal-body code").innerText()
+ );
+
+ await pauseIfVideoRecording(page);
+
+ // do something with transactionObj
+ expect(transactionObj).toMatchObject(expectedTransactionData);
+ });
+ });
+
+ describe("arguments: ({ tx })", () => {
+ test("should throw error if transaction object argument is invalid (single string provided)", async ({
+ page,
+ }) => {
+ const expectedErrorMessage =
+ "Method: Near.call. Required argument: 'tx/txs'. A single argument call requires an TX object or an array of TX objects.";
+
+ await useCode(page, "near-call/positional-params.js", {
+ contractName: "hello.near-examples.near",
+ });
+
+ // Expect error message to be displayed
+ const [error] = await Promise.all([
+ page.waitForEvent("pageerror"),
+ await page.getByRole("button", { name: "click" }).click(),
+ ]);
+
+ await pauseIfVideoRecording(page);
+
+ // Verify that the expected error message was logged
+ expect(error).toBeTruthy();
+ expect(error.message).toContain(expectedErrorMessage);
+ });
+ test("should open confirmation modal with appropriate details", async ({
+ page,
+ }) => {
+ const expectedTransactionData = { message: "Hello, World!" };
+
+ await useCode(page, "near-call/object-params.js", {
+ tx: {
+ contractName: "hello.near-examples.near",
+ methodName: "set_greeting",
+ args: { message: "Hello, World!" },
+ gas: "300000000000000",
+ deposit: "1000000000000000000000000",
+ },
+ });
+
+ await page.getByRole("button", { name: "click" }).click();
+
+ const transactionObj = JSON.parse(
+ await page.locator("div.modal-body code").innerText()
+ );
+ const modalBody = await page.locator(".modal-body");
+ const transactionNumber = await modalBody.locator("h4").textContent();
+ const values = await modalBody
+ .locator(".font-monospace")
+ .allInnerTexts();
+ const [contractId, methodName, deposit, gas] = values;
+
+ await pauseIfVideoRecording(page);
+
+ // do something with transactionObj
+ expect(transactionObj).toMatchObject(expectedTransactionData);
+ expect(transactionNumber).toBe("Transaction #1");
+ expect(contractId).toBe("hello.near-examples.near");
+ expect(methodName).toBe("set_greeting");
+ expect(deposit).toBe("1 NEAR");
+ expect(gas).toBe("300 TGas");
+ });
+ });
+
+ describe("arguments: [{ tx }, ...]", () => {
+ test("should open confirmation modal with appropriate details, multiple transactions", async ({
+ page,
+ }) => {
+ await useCode(page, "near-call/object-params.js", {
+ tx: [
+ {
+ contractName: "hello.near-examples.near",
+ methodName: "set_greeting",
+ args: { message: "Hello, World!" },
+ gas: "300000000000000",
+ deposit: "1000000000000000000000000",
+ },
+ {
+ contractName: "goodbye.near-examples.near",
+ methodName: "set_goobye",
+ args: { message: "Goodbye, World!" },
+ gas: "600000000000000",
+ deposit: "2000000000000000000000000",
+ },
+ ],
+ });
+
+ await page.getByRole("button", { name: "click" }).click();
+
+ const blocks = await page
+ .locator("div.modal-body code")
+ .allInnerTexts();
+ const modalBody = await page.locator(".modal-body");
+ const transactionNumbers = await modalBody.locator("h4").allInnerTexts();
+ const values = await modalBody
+ .locator(".font-monospace")
+ .allInnerTexts();
+
+ const [firstBlock, secondBlock] = blocks;
+
+ await pauseIfVideoRecording(page);
+
+ expect(transactionNumbers).toEqual(["Transaction #1", "Transaction #2"]);
+ expect(values).toEqual([
+ "hello.near-examples.near",
+ "set_greeting",
+ "1 NEAR",
+ "300 TGas",
+ "goodbye.near-examples.near",
+ "set_goobye",
+ "2 NEAR",
+ "600 TGas",
+ ]);
+
+ expect(JSON.parse(firstBlock)).toMatchObject({
+ message: "Hello, World!",
+ });
+ expect(JSON.parse(secondBlock)).toMatchObject({
+ message: "Goodbye, World!",
+ });
+ });
+ });
+ });
+});