From bf4e14b3a1d2928b597fc0a938b3266b232b779e Mon Sep 17 00:00:00 2001
From: Bogdan Crisan <bogdan@crbl.studio>
Date: Tue, 22 Oct 2024 16:00:24 +0200
Subject: [PATCH] Add e2e search test

---
 cfg/cspell-frontend-dictionary.txt            |  1 +
 src/docker/compose.yaml                       | 10 ++++----
 src/docker/utils/prune.sh                     |  4 +--
 src/typescript/ci.env                         |  1 +
 src/typescript/frontend/.eslintrc.js          |  1 -
 src/typescript/frontend/package.json          |  1 +
 src/typescript/frontend/playwright.config.ts  |  5 ++++
 .../emoji-picker/EmojiPickerWithInput.tsx     |  1 +
 src/typescript/frontend/src/lib/server-env.ts |  5 +++-
 src/typescript/frontend/src/middleware.ts     |  5 ++++
 .../frontend/tests/e2e/search.spec.ts         | 25 +++++++++++++++++++
 src/typescript/frontend/tsconfig.json         |  3 ++-
 src/typescript/package.json                   |  3 +--
 src/typescript/pnpm-lock.yaml                 |  3 +++
 .../sdk/src/client/emojicoin-client.ts        |  3 ++-
 .../utils/test/docker/docker-test-harness.ts  |  6 +++--
 src/typescript/sdk/tests/post-test.ts         |  2 +-
 17 files changed, 63 insertions(+), 16 deletions(-)
 create mode 100644 src/typescript/frontend/tests/e2e/search.spec.ts

diff --git a/cfg/cspell-frontend-dictionary.txt b/cfg/cspell-frontend-dictionary.txt
index c814a2cc87..55b0795a2e 100644
--- a/cfg/cspell-frontend-dictionary.txt
+++ b/cfg/cspell-frontend-dictionary.txt
@@ -50,3 +50,4 @@ localstorage
 vpnapi
 ctrls
 dockerenv
+testid
diff --git a/src/docker/compose.yaml b/src/docker/compose.yaml
index fa7e7fe6d0..bd7cd36a3f 100644
--- a/src/docker/compose.yaml
+++ b/src/docker/compose.yaml
@@ -100,13 +100,13 @@ services:
       dockerfile: 'src/docker/frontend/Dockerfile'
       args:
         HASH_SEED: '${HASH_SEED}'
-        EMOJICOIN_INDEXER_URL: 'http://localhost:3000'
+        EMOJICOIN_INDEXER_URL: 'http://host.docker.internal:3000'
         NEXT_PUBLIC_APTOS_NETWORK: '${APTOS_NETWORK}'
         NEXT_PUBLIC_INTEGRATOR_ADDRESS: '${EMOJICOIN_INTEGRATOR_ADDRESS}'
         NEXT_PUBLIC_INTEGRATOR_FEE_RATE_BPS: '${FEE_RATE_BPS}'
         NEXT_PUBLIC_IS_ALLOWLIST_ENABLED: 'false'
         NEXT_PUBLIC_MODULE_ADDRESS: '${EMOJICOIN_MODULE_ADDRESS}'
-        NEXT_PUBLIC_BROKER_URL: 'ws://localhost:${BROKER_PORT}'
+        NEXT_PUBLIC_BROKER_URL: 'ws://host.docker.internal:${BROKER_PORT}'
         NEXT_PUBLIC_REWARDS_MODULE_ADDRESS: >-
           ${EMOJICOIN_REWARDS_MODULE_ADDRESS}
         REVALIDATION_TIME: '${REVALIDATION_TIME}'
@@ -116,18 +116,18 @@ services:
     - 'frontend'
     environment:
       HASH_SEED: '${HASH_SEED}'
-      EMOJICOIN_INDEXER_URL: 'http://localhost:3000'
+      EMOJICOIN_INDEXER_URL: 'http://host.docker.internal:3000'
       NEXT_PUBLIC_APTOS_NETWORK: '${APTOS_NETWORK}'
       NEXT_PUBLIC_INTEGRATOR_ADDRESS: '${EMOJICOIN_INTEGRATOR_ADDRESS}'
       NEXT_PUBLIC_INTEGRATOR_FEE_RATE_BPS: '${FEE_RATE_BPS}'
       NEXT_PUBLIC_IS_ALLOWLIST_ENABLED: 'false'
       NEXT_PUBLIC_MODULE_ADDRESS: '${EMOJICOIN_MODULE_ADDRESS}'
-      NEXT_PUBLIC_BROKER_URL: 'ws://localhost:${BROKER_PORT}'
+      NEXT_PUBLIC_BROKER_URL: 'ws://host.docker.internal:${BROKER_PORT}'
       NEXT_PUBLIC_REWARDS_MODULE_ADDRESS: '${EMOJICOIN_REWARDS_MODULE_ADDRESS}'
       REVALIDATION_TIME: '${REVALIDATION_TIME}'
     healthcheck:
       test: 'curl -f http://localhost:3001/ || exit 1'
-      interval: '2m'
+      interval: '30s'
       timeout: '1s'
       retries: '1'
       start_period: '20s'
diff --git a/src/docker/utils/prune.sh b/src/docker/utils/prune.sh
index 81f93b2845..133a10c59c 100755
--- a/src/docker/utils/prune.sh
+++ b/src/docker/utils/prune.sh
@@ -137,7 +137,7 @@ if [ -n "$reset_localnet" ]; then
 	# Bind-mounting the parent of `.aptos` gives the container the right to
 	# delete it.
 	docker run --rm -v "$docker_dir/localnet:/pwd" busybox rm -rf /pwd/.aptos
-	docker compose -f compose.local.yaml down --volumes
+	docker compose -f compose.local.yaml --profile frontend down --volumes
 else
-	docker compose -f compose.local.yaml down
+	docker compose -f compose.local.yaml --profile frontend down
 fi
diff --git a/src/typescript/ci.env b/src/typescript/ci.env
index 6fc7c9a204..3d3bff6687 100644
--- a/src/typescript/ci.env
+++ b/src/typescript/ci.env
@@ -1,3 +1,4 @@
+NODE_ENV="test"
 APTOS_NETWORK="local"
 NEXT_PUBLIC_APTOS_NETWORK="local"
 NEXT_PUBLIC_BROKER_URL="ws://localhost:3009"
diff --git a/src/typescript/frontend/.eslintrc.js b/src/typescript/frontend/.eslintrc.js
index 335c5f7436..d7d8b4aeab 100644
--- a/src/typescript/frontend/.eslintrc.js
+++ b/src/typescript/frontend/.eslintrc.js
@@ -25,7 +25,6 @@ module.exports = {
     "playwright.config.ts",
     "postcss.config.js",
     "tailwind.config.js",
-    "example.spec.ts",
   ],
   parser: "@typescript-eslint/parser",
   parserOptions: {
diff --git a/src/typescript/frontend/package.json b/src/typescript/frontend/package.json
index afaef7fb9f..ab91ee419b 100644
--- a/src/typescript/frontend/package.json
+++ b/src/typescript/frontend/package.json
@@ -35,6 +35,7 @@
     "axios": ">=0.28.0",
     "big.js": "^6.2.2",
     "clsx": "^2.1.1",
+    "dotenv": "^16.4.5",
     "emoji-mart": "https://github.com/econia-labs/emoji-mart/raw/emojicoin-dot-fun/packages/emoji-mart/emoji-mart-v5.6.0.tgz",
     "emoji-regex": "^10.4.0",
     "framer-motion": "^11.11.4",
diff --git a/src/typescript/frontend/playwright.config.ts b/src/typescript/frontend/playwright.config.ts
index ccfcab6174..ce826012b1 100644
--- a/src/typescript/frontend/playwright.config.ts
+++ b/src/typescript/frontend/playwright.config.ts
@@ -1,4 +1,9 @@
 import { defineConfig, devices } from "@playwright/test";
+import dotenv from "dotenv";
+import path from "path";
+
+// Read from ".env" file.
+dotenv.config({ path: path.resolve(__dirname, "../ci.env") });
 
 /**
  * See https://playwright.dev/docs/test-configuration.
diff --git a/src/typescript/frontend/src/components/emoji-picker/EmojiPickerWithInput.tsx b/src/typescript/frontend/src/components/emoji-picker/EmojiPickerWithInput.tsx
index 725c329bd0..136c373aca 100644
--- a/src/typescript/frontend/src/components/emoji-picker/EmojiPickerWithInput.tsx
+++ b/src/typescript/frontend/src/components/emoji-picker/EmojiPickerWithInput.tsx
@@ -250,6 +250,7 @@ export const EmojiPickerWithInput = ({
                   onClick={() => {
                     setPickerInvisible(false);
                   }}
+                  data-testid="emoji-input"
                 />
                 {mode === "search" && close}
                 {mode === "chat" ? (
diff --git a/src/typescript/frontend/src/lib/server-env.ts b/src/typescript/frontend/src/lib/server-env.ts
index d4fc10c53e..eec40d9f10 100644
--- a/src/typescript/frontend/src/lib/server-env.ts
+++ b/src/typescript/frontend/src/lib/server-env.ts
@@ -43,7 +43,10 @@ export const GALXE_CAMPAIGN_ID: string | undefined = process.env.GALXE_CAMPAIGN_
 export const REVALIDATION_TIME: number = Number(process.env.REVALIDATION_TIME);
 export const VPNAPI_IO_API_KEY: string = process.env.VPNAPI_IO_API_KEY!;
 
-if (APTOS_NETWORK === Network.LOCAL && !EMOJICOIN_INDEXER_URL.includes("localhost")) {
+if (
+  APTOS_NETWORK === Network.LOCAL &&
+  (!EMOJICOIN_INDEXER_URL.includes("localhost") || !EMOJICOIN_INDEXER_URL.includes("docker"))
+) {
   throw new Error(
     `APTOS_NETWORK is ${APTOS_NETWORK} but the indexer processor url is set to ${EMOJICOIN_INDEXER_URL}`
   );
diff --git a/src/typescript/frontend/src/middleware.ts b/src/typescript/frontend/src/middleware.ts
index 3bc7b37c1b..55350fbb05 100644
--- a/src/typescript/frontend/src/middleware.ts
+++ b/src/typescript/frontend/src/middleware.ts
@@ -3,6 +3,7 @@ import {
   COOKIE_FOR_HASHED_ADDRESS,
 } from "components/pages/verify/session-info";
 import { authenticate } from "components/pages/verify/verify";
+import { IS_ALLOWLIST_ENABLED } from "lib/env";
 import { NextResponse, type NextRequest } from "next/server";
 import { ROUTES } from "router/routes";
 import { normalizePossibleMarketPath } from "utils/pathname-helpers";
@@ -24,6 +25,10 @@ export default async function middleware(request: NextRequest) {
     return NextResponse.redirect(possibleMarketPath);
   }
 
+  if (!IS_ALLOWLIST_ENABLED) {
+    return NextResponse.next();
+  }
+
   const hashed = request.cookies.get(COOKIE_FOR_HASHED_ADDRESS)?.value;
   const address = request.cookies.get(COOKIE_FOR_ACCOUNT_ADDRESS)?.value;
 
diff --git a/src/typescript/frontend/tests/e2e/search.spec.ts b/src/typescript/frontend/tests/e2e/search.spec.ts
new file mode 100644
index 0000000000..14a7398aef
--- /dev/null
+++ b/src/typescript/frontend/tests/e2e/search.spec.ts
@@ -0,0 +1,25 @@
+import { test, expect } from "@playwright/test";
+import { EmojicoinClient } from "../../../sdk/src/client/emojicoin-client";
+import { getFundedAccount } from "../../../sdk/src/utils/test/test-accounts";
+import { SYMBOL_EMOJI_DATA } from "../../../sdk/src";
+
+test("check search results", async ({ page }) => {
+  const user = getFundedAccount("666");
+  const symbols = [SYMBOL_EMOJI_DATA.byName("cat")!.emoji, SYMBOL_EMOJI_DATA.byName("cat")!.emoji];
+
+  const client = new EmojicoinClient();
+
+  await client.register(user, symbols);
+
+  await page.goto("/home");
+
+  const search = page.getByTestId("emoji-input");
+  expect(search).toBeVisible();
+  await search.fill(symbols.join(""));
+
+  const marketCard = page.getByText("cat,cat", { exact: true });
+  expect(marketCard).toBeVisible();
+  await marketCard.click();
+
+  await expect(page).toHaveURL(/.*cat;cat/);
+});
diff --git a/src/typescript/frontend/tsconfig.json b/src/typescript/frontend/tsconfig.json
index 8b30e81e83..204a045916 100644
--- a/src/typescript/frontend/tsconfig.json
+++ b/src/typescript/frontend/tsconfig.json
@@ -69,6 +69,7 @@
     "./src/**/*.ts",
     "./src/**/*.tsx",
     ".next/types/**/*.ts",
-    "./dist/types/**/*.ts"
+    "./dist/types/**/*.ts",
+    "./tests/**/*.ts"
   ]
 }
diff --git a/src/typescript/package.json b/src/typescript/package.json
index 105641293c..623b8681ec 100644
--- a/src/typescript/package.json
+++ b/src/typescript/package.json
@@ -25,14 +25,13 @@
     "dev:debug": "pnpm dotenv -v FETCH_DEBUG=true -- pnpm run dev",
     "dev:debug-verbose": "pnpm dotenv -v FETCH_DEBUG_VERBOSE=true -- pnpm run dev",
     "down": "pnpm run prune",
-    "e2e:frontend": "pnpm run load-env:e2e-frontend -- turbo run e2e:frontend --filter @econia-labs/emojicoin-frontend --log-prefix none",
+    "e2e:frontend": "turbo run e2e:frontend --filter @econia-labs/emojicoin-frontend --log-prefix none",
     "format": "turbo run format -- --write",
     "format:check": "turbo run format -- --check",
     "full-clean": "pnpm run clean && rm -rf node_modules && rm -rf sdk/node_modules && rm -rf frontend/node_modules",
     "lint": "turbo run lint",
     "lint:fix": "turbo run lint -- --fix",
     "load-env": "dotenv -e .env.local -e .env -e .env.example -e ../docker/example.local.env -e ../docker/.env",
-    "load-env:e2e-frontend": "dotenv -e .env.local -e .env -e .env.example -e ../docker/example.local.env -e ../docker/.env -e ci.env -v NODE_ENV=test",
     "load-env:test": "dotenv -e .env.local -e .env -e .env.example -e ../docker/example.local.env -e ../docker/.env -e ci.env",
     "load-env:test-debug": "dotenv -e .env.local -e .env -e .env.example -e ../docker/example.local.env -e ../docker/.env -e ci.env -v FETCH_DEBUG=true",
     "load-env:test-verbose": "dotenv -e .env.local -e .env -e .env.example -e ../docker/example.local.env -e ../docker/.env -e ci.env -v FETCH_DEBUG=true VERBOSE_TEST_LOGS=true",
diff --git a/src/typescript/pnpm-lock.yaml b/src/typescript/pnpm-lock.yaml
index c2917b1c74..4f5899249b 100644
--- a/src/typescript/pnpm-lock.yaml
+++ b/src/typescript/pnpm-lock.yaml
@@ -99,6 +99,9 @@ importers:
       clsx:
         specifier: ^2.1.1
         version: 2.1.1
+      dotenv:
+        specifier: ^16.4.5
+        version: 16.4.5
       emoji-mart:
         specifier: https://github.com/econia-labs/emoji-mart/raw/emojicoin-dot-fun/packages/emoji-mart/emoji-mart-v5.6.0.tgz
         version: https://github.com/econia-labs/emoji-mart/raw/emojicoin-dot-fun/packages/emoji-mart/emoji-mart-v5.6.0.tgz
diff --git a/src/typescript/sdk/src/client/emojicoin-client.ts b/src/typescript/sdk/src/client/emojicoin-client.ts
index 44f7d1d69a..912ffccf08 100644
--- a/src/typescript/sdk/src/client/emojicoin-client.ts
+++ b/src/typescript/sdk/src/client/emojicoin-client.ts
@@ -24,7 +24,7 @@ import { type EventsModels, getEventsAsProcessorModelsFromResponse } from "../mi
 import { getAptosClient } from "../utils/aptos-client";
 import { toChatMessageEntryFunctionArgs } from "../emoji_data";
 import customExpect from "./expect";
-import { INTEGRATOR_ADDRESS } from "../const";
+import { DEFAULT_REGISTER_MARKET_GAS_OPTIONS, INTEGRATOR_ADDRESS } from "../const";
 
 const { expect, Expect } = customExpect;
 
@@ -119,6 +119,7 @@ export class EmojicoinClient {
       emojis: this.emojisToHexStrings(symbolEmojis),
       integrator: this.integrator,
       ...options,
+      options: DEFAULT_REGISTER_MARKET_GAS_OPTIONS,
     });
     const res = this.getTransactionEventData(response);
     return {
diff --git a/src/typescript/sdk/src/utils/test/docker/docker-test-harness.ts b/src/typescript/sdk/src/utils/test/docker/docker-test-harness.ts
index d03c35d1fd..8c290c8acc 100644
--- a/src/typescript/sdk/src/utils/test/docker/docker-test-harness.ts
+++ b/src/typescript/sdk/src/utils/test/docker/docker-test-harness.ts
@@ -73,8 +73,10 @@ export class DockerTestHarness {
   /**
    * Stops the Docker containers.
    */
-  static async stop() {
-    await execPromise(`docker compose -f ${LOCAL_COMPOSE_PATH} stop`);
+  static async stop(frontend: boolean) {
+    await execPromise(
+      `docker compose -f ${LOCAL_COMPOSE_PATH} ${frontend && "--profile frontend"} stop`
+    );
     const process = Number(readFileSync(TMP_PID_FILE_PATH, { encoding: "utf-8" }));
     if (process) {
       kill(process);
diff --git a/src/typescript/sdk/tests/post-test.ts b/src/typescript/sdk/tests/post-test.ts
index 4e6c8ed73c..4704bb6758 100644
--- a/src/typescript/sdk/tests/post-test.ts
+++ b/src/typescript/sdk/tests/post-test.ts
@@ -2,5 +2,5 @@
 import { DockerTestHarness } from "../src/utils/test/docker/docker-test-harness";
 
 export default async function postTest() {
-  await DockerTestHarness.stop();
+  await DockerTestHarness.stop(false);
 }