From 85643688a48e7c12b270ccf6229c2b4abaee2a66 Mon Sep 17 00:00:00 2001 From: Bug Catcher / Milan <57331244+MilanVojnovic95@users.noreply.github.com> Date: Wed, 22 Nov 2023 16:52:48 +0100 Subject: [PATCH 1/5] feat: initial smoke test (#511) * feat: smoke test [WIP] * feat(qa): smoke test [WIP] * feat(qa): added more steps, selectors [WIP] * feat(qa): added additional assertions for smoke * feat(qa): fixing comments * feat(qa): edit script * fix(frontend): add yarn prefix to script --------- Co-authored-by: Paolo Guerra --- .gitignore | 8 +- .../frontend/e2e/tests/connect-wallet.test.ts | 2 +- packages/frontend/e2e/tests/home-page.test.ts | 23 - packages/frontend/e2e/tests/smoke.test.ts | 105 + packages/frontend/e2e/utils/data.ts | 46 + packages/frontend/e2e/{ => utils}/fixtures.ts | 9 + packages/frontend/e2e/utils/pages/basePage.ts | 71 + packages/frontend/e2e/utils/pages/homePage.ts | 295 +++ packages/frontend/package.json | 3 +- packages/frontend/playwright.config.ts | 24 +- .../src/components/ui/navbar/index.tsx | 1 + .../ui/navbar/popovers/preferences.tsx | 2 +- yarn.lock | 1961 ++++++++++++++++- 13 files changed, 2432 insertions(+), 118 deletions(-) delete mode 100644 packages/frontend/e2e/tests/home-page.test.ts create mode 100644 packages/frontend/e2e/tests/smoke.test.ts create mode 100644 packages/frontend/e2e/utils/data.ts rename packages/frontend/e2e/{ => utils}/fixtures.ts (86%) create mode 100644 packages/frontend/e2e/utils/pages/basePage.ts create mode 100644 packages/frontend/e2e/utils/pages/homePage.ts diff --git a/.gitignore b/.gitignore index 2f7241830..e2973c543 100644 --- a/.gitignore +++ b/.gitignore @@ -182,12 +182,12 @@ packages/ipfs-pinner/src/contracts packages/subgraph/src/gen -# Test results & reposts -test-results/ -playwright-report/ +# Test results & reports +packages/frontend/test-results +packages/frontend/playwright-report # Cache -playwright/.cache/ +packages/frontend/playwright/.cache/ # Metamask metamask-* \ No newline at end of file diff --git a/packages/frontend/e2e/tests/connect-wallet.test.ts b/packages/frontend/e2e/tests/connect-wallet.test.ts index 98f6af0d4..e6c7b3bb5 100644 --- a/packages/frontend/e2e/tests/connect-wallet.test.ts +++ b/packages/frontend/e2e/tests/connect-wallet.test.ts @@ -1,4 +1,4 @@ -import { test, expect } from "../fixtures"; +import { test, expect } from "../utils/fixtures"; import { acceptAccess } from "@synthetixio/synpress/commands/metamask"; test.beforeEach(async ({ page }) => { diff --git a/packages/frontend/e2e/tests/home-page.test.ts b/packages/frontend/e2e/tests/home-page.test.ts deleted file mode 100644 index 48ff64573..000000000 --- a/packages/frontend/e2e/tests/home-page.test.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { test } from "../fixtures"; - -test("home page", async ({ page }) => { - await test.step("navigate to home page", async () => { - await page.goto("/"); - }); - - await test.step("about visible", async () => { - await page.locator("div").getByText("About`").nth(1).isVisible(); - }); - - await test.step("campaigns visible", async () => { - await page.locator("div").getByText("Campaign").nth(1).isVisible(); - }); - - await test.step("connect wallet button visible", async () => { - await page - .locator("div") - .getByText("Connect wallet") - .nth(1) - .isVisible(); - }); -}); diff --git a/packages/frontend/e2e/tests/smoke.test.ts b/packages/frontend/e2e/tests/smoke.test.ts new file mode 100644 index 000000000..e50a30034 --- /dev/null +++ b/packages/frontend/e2e/tests/smoke.test.ts @@ -0,0 +1,105 @@ +import { test } from "../utils/fixtures"; +import { networks } from "../utils/data"; + +/** + *@description Smoke test for Carrot Home page + */ +test.beforeEach(async ({ homePage }) => { + await test.step("Navigate to Carrot Home page", async () => { + await homePage.goToHomePage(); + }); +}); +test.describe("Carrot Smoke test", () => { + test("Home page assertions and redirections", async ({ homePage }) => { + await test.step("Staging banner is visible", async () => { + await homePage.checkStagingBanner(); + }); + await test.step("About button redirects to Carrot community page", async () => { + await homePage.checkAboutButtonRedirection(); + }); + await test.step("Campaigns button redirects to All campaigns page", async () => { + await homePage.clickCampaignsHeader(); + await homePage.checkIfOnAllCampaignsPage(); + await homePage.goBack(); + }); + await test.step("Check default selected network", async () => { + await homePage.checkSelectedNetwork(networks.polygonMumbai); + }); + await test.step("Check Network dropdown options", async () => { + await homePage.checkNetworkOptions(); + }); + await test.step("Check connect wallet dropdown options", async () => { + await homePage.checkWalletOptions(); + }); + await test.step("Check Settings dropdown", async () => { + await homePage.checkSettingsDropdown(); + }); + // todo: network switching steps can be refactored to go in loop + await test.step("Switch to Scroll Sepolia network", async () => { + await homePage.selectNetwork(networks.scrollSepolia); + }); + await test.step("Scroll Sepolia network is selected", async () => { + await homePage.checkSelectedNetwork(networks.scrollSepolia); + }); + await test.step("Switch to Sepolia network", async () => { + await homePage.selectNetwork(networks.sepolia); + }); + await test.step("Sepolia network is selected", async () => { + await homePage.checkSelectedNetwork(networks.sepolia); + }); + await test.step("Switch to Polygon Mumbai network", async () => { + await homePage.selectNetwork(networks.polygonMumbai); + }); + await test.step("Polygon Mumbai network is selected", async () => { + await homePage.checkSelectedNetwork(networks.polygonMumbai); + }); + await test.step("Hero section text", async () => { + await homePage.checkTextOnHomePage(); + }); + // todo: next to steps depend on each other think of better solution + await test.step("Create campaign button redirects to Create campaign page", async () => { + await homePage.checkCreateCampaignButtonRedirection(); + }); + await test.step("No wallet: users sees Wallet disconnected notification", async () => { + await homePage.checkWalletDisconnectedText(); + await homePage.goBack(); + }); + await test.step("Check How it work video", async () => { + await homePage.checkHowItWorksPreview(); + }); + await test.step("Check first campaign title", async () => { + await homePage.checkFirstCampaign(); + }); + // todo: have some compiling troubles on local env after redirection + // await test.step("View campaign button redirects to campaigns page", async () => { + // await homePage.checkRedirectionToFirstCampaign(); + // }); + await test.step("View all campaigns button redirects to All campaigns page", async () => { + await homePage.clickViewAllCampaigns(); + await homePage.checkIfOnAllCampaignsPage(); + await homePage.goBack(); + }); + await test.step("Check first template title", async () => { + await homePage.checkFirstTemplate(); + }); + await test.step("Use template button redirects to Create campaign page for that template", async () => { + await homePage.checkRedirectionToFirstTemplate(); + }); + //---Footer + await test.step("Footer - Documentation redirects to docs Carrot community", async () => { + await homePage.checkFooterDocumentationButtonRedirection(); + }); + await test.step("Footer - Audits redirects to Carrot GitHub", async () => { + await homePage.checkFooterAuditsButtonRedirection(); + }); + await test.step("Footer - Discord redirects to Carrot Discord", async () => { + await homePage.checkFooterDiscordButtonRedirection(); + }); + await test.step("Footer - Twitter redirects to Carrot Twitter", async () => { + await homePage.checkFooterTwitterButtonRedirection(); + }); + await test.step("Footer - Carrot info page redirect to Carrot community", async () => { + await homePage.checkFooterCarrotInfoPageButtonRedirection(); + }); + }); +}); diff --git a/packages/frontend/e2e/utils/data.ts b/packages/frontend/e2e/utils/data.ts new file mode 100644 index 000000000..7d47c491a --- /dev/null +++ b/packages/frontend/e2e/utils/data.ts @@ -0,0 +1,46 @@ +/** + * @exports different data used in tests + */ +export const networks = { + polygonMumbai: "Polygon Mumbai", + scrollSepolia: "Scroll Sepolia", + sepolia: "Sepolia", +}; +export const wallets = { + injectedMetamask: "Injected (MetaMask)", + metamask: "MetaMask", + coinBase: "Coinbase Wallet", +}; +export const urls = { + carrotCommunity: "https://www.staging.carrot.community/", + allCampaigns: "/campaigns?chain=polygon+mumbai", + createCampaign: "/create/1?chain=polygon+mumbai", + documentation: "https://docs.staging.carrot.community/", + audits: "https://github.com/carrot-kpi/contracts/tree/main/audits", + discord: "https://discord.com/invite/uRer2D4Pdf", + twitter: "https://twitter.com/CarrotEth", + carrotInfoPage: "https://www.staging.carrot.community/", +}; +export const textData = { + stagingBannerText: + "You are using Carrot templates' staging versions. Reach out to the team in Discord for the LIVE released and audited version.", + interfaceSettings: "Interface settings", + darkTheme: "Dark theme", + decentralizationMode: "Decentralization mode", + stagingMode: "Staging mode", + heroSectionTitle: "Reach your goals with a Carrot", + heroSectionDescription: + "Easy and powerful tool to create conditional tokens allowing an effective method to achieve any goal desirable using permissionless, decentralized technologies.", + latestCampaigns: "Latest Campaigns", + templatest: "Templates", + footerAbout: "About", + footerCommunity: "Community", + walletDisconnected: "Wallet disconnected", + walletRequiredDescription: "A connected wallet is required to continue.", +}; +export const campaignData = { + firstCampaign: "TS01 NOV13", +}; +export const templateData = { + erc20Title: "ERC20 KPI token", +}; diff --git a/packages/frontend/e2e/fixtures.ts b/packages/frontend/e2e/utils/fixtures.ts similarity index 86% rename from packages/frontend/e2e/fixtures.ts rename to packages/frontend/e2e/utils/fixtures.ts index 3100e3221..417dffc20 100644 --- a/packages/frontend/e2e/fixtures.ts +++ b/packages/frontend/e2e/utils/fixtures.ts @@ -3,10 +3,19 @@ import { initialSetup } from "@synthetixio/synpress/commands/metamask"; import { setExpectInstance } from "@synthetixio/synpress/commands/playwright"; import { resetState } from "@synthetixio/synpress/commands/synpress"; import { prepareMetamask } from "@synthetixio/synpress/helpers"; +import { HomePage } from "../utils/pages/homePage"; +/** + * @exports context fixture which sets up Metamask extension before test start + * @exports page instances used in tests + */ export const test = base.extend<{ context: BrowserContext; + homePage: HomePage; }>({ + homePage: async ({ page }, use) => { + await use(new HomePage(page)); + }, context: async ({}, use) => { // required for synpress as it shares same expect instance as playwright await setExpectInstance(expect); diff --git a/packages/frontend/e2e/utils/pages/basePage.ts b/packages/frontend/e2e/utils/pages/basePage.ts new file mode 100644 index 000000000..30cfa1c84 --- /dev/null +++ b/packages/frontend/e2e/utils/pages/basePage.ts @@ -0,0 +1,71 @@ +import { expect, Page } from "@playwright/test"; +/** + * @exports default methods used in all tests + */ +export class BasePage { + readonly page: Page; + constructor(page: Page) { + this.page = page; + } + async pauseInSec(sec: number) { + await this.page.waitForTimeout(sec * 1000); + } + async open(url: string) { + await this.page.goto(url); + } + async checkUrl(page: Page, url: string) { + expect(page.url()).toContain(url); + } + async checkRedirectionToNewTab(selector: string, url: string) { + const newPagePromise = this.page.waitForEvent("popup"); + await this.click(selector); + const newPage = await newPagePromise; + expect(newPage.url()).toContain(url); + await newPage.close(); + } + async clickAnyWhereToClose() { + await this.page.mouse.click(1000, 1000); + } + async click(selector: string) { + await this.page.waitForLoadState(); + await this.page.getByTestId(selector).click(); + } + async clickFirst(selector: string) { + await this.page.waitForLoadState(); + await this.page.getByTestId(selector).first().click(); + } + async clickSecond(selector: string) { + await this.page.waitForLoadState(); + await this.page.getByTestId(selector).nth(1).click(); + } + async hoverElement(selector: string) { + await this.page.getByTestId(selector).hover(); + } + async isVisible(selector: string) { + await expect(this.page.getByTestId(selector)).toBeVisible(); + } + async isNotVisible(selector: string) { + await expect(this.page.getByTestId(selector)).not.toBeVisible(); + } + async enterText(selector: string, text: string) { + await this.page.getByTestId(selector).fill(text); + } + async compareText(selector: string, text: string, index: number) { + await expect(this.page.getByTestId(selector).nth(index)).toHaveText( + text, + ); + } + async containsText(selector: string, text: string) { + await expect(this.page.getByTestId(selector)).toContainText(text); + } + async compareValue(selector: string, text: string) { + await expect(this.page.getByTestId(selector)).toHaveValue(text); + } + async compareNotValue(selector: string, text: string) { + await expect(this.page.getByTestId(selector)).not.toHaveValue(text); + } + async getAllElements(selector: string) { + await this.page.waitForSelector(selector); + return this.page.getByTestId(selector).all(); + } +} diff --git a/packages/frontend/e2e/utils/pages/homePage.ts b/packages/frontend/e2e/utils/pages/homePage.ts new file mode 100644 index 000000000..3bbf15609 --- /dev/null +++ b/packages/frontend/e2e/utils/pages/homePage.ts @@ -0,0 +1,295 @@ +import { BasePage } from "../pages/basePage"; +import { + campaignData, + urls, + networks, + templateData, + textData, + wallets, +} from "../data"; + +/** + * @exports Selectors and Methods for Home page + */ +export class HomePage extends BasePage { + // ---Selectors + stagingBanner_Text = "staging-banner-text"; + about_Button = "header-About-button"; + campaignsHeader_Button = "header-Campaigns-button"; + networkDropdown_Button = "network-drop-down-button"; + selectedPolygonMumbain_Text = "Polygon Mumbai-button"; + selectedScrollSepoliaNetwork_Text = "Scroll Sepolia-button"; + selectedSepoliaNetwork_Text = "Sepolia-button"; + polygonMumbaiNetwork_Text = "Polygon Mumbai-network-button"; + scrollSepoliaNetwork_Text = "Scroll Sepolia-network-button"; + sepoliaNetwork_Text = "Sepolia-network-button"; + connectWallet_Button = "connect-wallet-button"; + injectedMetamask_Button = "injected-wallet-button"; + metamask_Button = "metaMask-wallet-button"; + coinBase_Button = "coinbaseWallet-wallet-button"; + settings_Button = "settings-button"; + interfaceSettingsTitle_Text = "interface-settings-title"; + darkTheme_Text = "theme-name-text"; + decentralizationMode_Text = "decentralization-mode-text"; + decentralizationMode_Switch = "decentralization-mode-switch"; + stagingMode_Text = "staging-mode-text"; + stagingMode_Switch = "staging-mode-switch"; + heroTitle_Text = "hero-section-title-text"; + heroDescription_Text = "hero-section-description-text"; + howItWorks_Button = "how-it-works-button"; + howItWorksVideo_Preview = "video-preview-overlay"; + createCampaign_Button = "create-campaign-button"; + latestCampaign_Text = "latest-campaigns-title-text"; + viewCampaign_Button = "view-campaign-button"; + viewAllCampaigns_Button = "view-all-campaigns-button"; + firstCampaignTitle_Text = "TS01 NOV13-campaign-title"; + templates_Text = "templates-title-text"; + templateErc20KPIToken_Text = "ERC20 KPI token-template-title"; + useTemplate_Button = "use-template-button"; + footerAbout_Text = "footer-About-text"; + footerCommunity_Text = "footer-Community-text"; + footerDocumentation_Link = "footer-Documentation-button"; + footerAudits_Link = "footer-Audits-button"; + footerDiscrod_Link = "footer-Discord-button"; + footerTwitter_Link = "footer-Twitter-button"; + footerCarrotInfoPage_Button = "footer-carrot-info-page-button"; + // selectors on campaign page + walletDisconnected_Text = "wallet-disconnected-text"; + walletRequiredDescription_Text = "connect-wallet-required-text"; + // ---Methods + async goToHomePage() { + await this.open("/"); + } + async goBack() { + await this.page.goBack(); + } + async checkStagingBanner() { + await this.compareText( + this.stagingBanner_Text, + textData.stagingBannerText, + 0, + ); + } + //---Clicks + async clickAbout() { + await this.click(this.about_Button); + } + async clickCampaignsHeader() { + await this.click(this.campaignsHeader_Button); + } + async clickNetwork() { + this.clickSecond(this.networkDropdown_Button); + } + async clickConnectWallet() { + await this.clickSecond(this.connectWallet_Button); + } + async clickSettings() { + await this.click(this.settings_Button); + } + async clickCreateCampaign() { + await this.click(this.createCampaign_Button); + } + async clickHowItWorks() { + await this.click(this.howItWorks_Button); + } + async clickViewAllCampaigns() { + await this.click(this.viewAllCampaigns_Button); + } + //--- Header assertions + async checkAboutButtonRedirection() { + await this.checkRedirectionToNewTab( + this.about_Button, + urls.carrotCommunity, + ); + } + async checkIfOnAllCampaignsPage() { + await this.checkUrl(this.page, urls.allCampaigns); + } + async checkNetworkOptions() { + await this.clickNetwork(); + await this.compareText( + this.scrollSepoliaNetwork_Text, + networks.scrollSepolia, + 1, + ); + await this.compareText(this.sepoliaNetwork_Text, networks.sepolia, 1); + } + // selects networks from network dropdown and checks if it's been selected + async selectNetwork(network: string) { + await this.clickNetwork(); + switch (network) { + case networks.polygonMumbai: + await this.clickSecond(this.polygonMumbaiNetwork_Text); + break; + case networks.scrollSepolia: + await this.clickSecond(this.scrollSepoliaNetwork_Text); + break; + case networks.sepolia: + await this.clickSecond(this.sepoliaNetwork_Text); + break; + } + } + async checkSelectedNetwork(network: string) { + switch (network) { + case networks.polygonMumbai: + await this.compareText( + this.selectedPolygonMumbain_Text, + networks.polygonMumbai, + 1, + ); + break; + case networks.scrollSepolia: + await this.compareText( + this.selectedScrollSepoliaNetwork_Text, + networks.scrollSepolia, + 1, + ); + break; + case networks.sepolia: + await this.compareText( + this.selectedSepoliaNetwork_Text, + networks.sepolia, + 1, + ); + break; + } + } + async checkWalletOptions() { + await this.clickConnectWallet(); + await this.compareText( + this.injectedMetamask_Button, + wallets.injectedMetamask, + 1, + ); + await this.compareText(this.metamask_Button, wallets.metamask, 1); + await this.compareText(this.coinBase_Button, wallets.coinBase, 1); + } + async selectWalletConnection(wallet: string) { + await this.clickConnectWallet(); + switch (wallet) { + case wallets.injectedMetamask: + await this.click(this.injectedMetamask_Button); + break; + case wallets.metamask: + await this.click(this.metamask_Button); + break; + case wallets.coinBase: + await this.click(this.coinBase_Button); + break; + } + } + async checkSettingsDropdown() { + await this.clickSettings(); + await this.compareText( + this.interfaceSettingsTitle_Text, + textData.interfaceSettings, + 0, + ); + await this.compareText(this.darkTheme_Text, textData.darkTheme, 0); + await this.compareText( + this.decentralizationMode_Text, + textData.decentralizationMode, + 0, + ); + await this.compareText(this.stagingMode_Text, textData.stagingMode, 0); + } + //---Page assertions + async checkTextOnHomePage() { + await this.compareText( + this.heroTitle_Text, + textData.heroSectionTitle, + 0, + ); + await this.compareText( + this.heroDescription_Text, + textData.heroSectionDescription, + 0, + ); + await this.compareText( + this.latestCampaign_Text, + textData.latestCampaigns, + 0, + ); + await this.compareText(this.templates_Text, textData.templatest, 0); + await this.compareText(this.footerAbout_Text, textData.footerAbout, 0); + await this.compareText( + this.footerCommunity_Text, + textData.footerCommunity, + 0, + ); + } + async checkCreateCampaignButtonRedirection() { + await this.clickCreateCampaign(); + await this.checkUrl(this.page, urls.createCampaign); + } + async checkHowItWorksPreview() { + await this.clickHowItWorks(); + await this.isVisible(this.howItWorksVideo_Preview); + await this.clickAnyWhereToClose(); + } + // todo: this method should be on create campaign page + async checkWalletDisconnectedText() { + await this.compareText( + this.walletDisconnected_Text, + textData.walletDisconnected, + 0, + ); + await this.compareText( + this.walletRequiredDescription_Text, + textData.walletRequiredDescription, + 0, + ); + } + async checkFirstCampaign() { + await this.compareText( + this.firstCampaignTitle_Text, + campaignData.firstCampaign, + 0, + ); + } + async checkRedirectionToFirstCampaign() { + await this.clickFirst(this.viewCampaign_Button); + await this.checkUrl(this.page, "campaigns/"); + } + async checkFirstTemplate() { + await this.compareText( + this.templateErc20KPIToken_Text, + templateData.erc20Title, + 0, + ); + } + async checkRedirectionToFirstTemplate() { + await this.click(this.useTemplate_Button); + await this.checkUrl(this.page, urls.createCampaign); + } + async checkFooterDocumentationButtonRedirection() { + await this.checkRedirectionToNewTab( + this.footerDocumentation_Link, + urls.documentation, + ); + } + async checkFooterAuditsButtonRedirection() { + await this.checkRedirectionToNewTab( + this.footerAudits_Link, + urls.audits, + ); + } + async checkFooterDiscordButtonRedirection() { + await this.checkRedirectionToNewTab( + this.footerDiscrod_Link, + urls.discord, + ); + } + async checkFooterTwitterButtonRedirection() { + await this.checkRedirectionToNewTab( + this.footerTwitter_Link, + urls.twitter, + ); + } + async checkFooterCarrotInfoPageButtonRedirection() { + await this.checkRedirectionToNewTab( + this.footerCarrotInfoPage_Button, + urls.carrotInfoPage, + ); + } +} diff --git a/packages/frontend/package.json b/packages/frontend/package.json index be29fbe76..5be180cbf 100644 --- a/packages/frontend/package.json +++ b/packages/frontend/package.json @@ -35,12 +35,13 @@ "config-react-env": "./scripts/config-react-env.js", "start:staging": "STAGING=true craco start", "start": "craco start", - "test:e2e": "yarn prepare-fathom && playwright test" + "test:e2e": "yarn prepare-fathom && yarn playwright test" }, "dependencies": { "@carrot-kpi/react": "*", "@carrot-kpi/sdk": "*", "@carrot-kpi/ui": "*", + "@synthetixio/synpress": "^3.7.2-beta.9", "class-variance-authority": "^0.7.0", "dayjs": "^1.11.10", "eth-provider": "^0.13.6", diff --git a/packages/frontend/playwright.config.ts b/packages/frontend/playwright.config.ts index 86bd7e70a..6648de9f1 100644 --- a/packages/frontend/playwright.config.ts +++ b/packages/frontend/playwright.config.ts @@ -1,16 +1,20 @@ import { defineConfig, devices } from "@playwright/test"; export default defineConfig({ - timeout: 60_000, + timeout: 60000, testDir: "./e2e", fullyParallel: true, forbidOnly: !!process.env.CI, retries: process.env.CI ? 2 : 0, workers: process.env.CI ? 1 : 1, - reporter: process.env.CI ? "dot" : "list", + reporter: [ + ["list", { printSteps: true }], + ["html", { open: "always" }], + ], use: { headless: false, baseURL: "http://localhost:3000/#/?chain=scroll+sepolia", + // baseURL: "https://app.staging.carrot.community/#/?chain=polygon+mumbai", trace: "on-first-retry", }, projects: [ @@ -21,14 +25,14 @@ export default defineConfig({ viewport: { width: 1920, height: 1080 }, }, }, - { - name: "firefox", - use: { ...devices["Desktop Firefox"] }, - }, - { - name: "webkit", - use: { ...devices["Desktop Safari"] }, - }, + // { + // name: "firefox", + // use: { ...devices["Desktop Firefox"] }, + // }, + // { + // name: "webkit", + // use: { ...devices["Desktop Safari"] }, + // }, ], webServer: { command: "yarn start:staging", diff --git a/packages/frontend/src/components/ui/navbar/index.tsx b/packages/frontend/src/components/ui/navbar/index.tsx index df20304da..5fb1c3263 100644 --- a/packages/frontend/src/components/ui/navbar/index.tsx +++ b/packages/frontend/src/components/ui/navbar/index.tsx @@ -141,6 +141,7 @@ export const Navbar = ({ />