diff --git a/.env.example b/.env.example index 9e2515f1d9..2d080aaa47 100644 --- a/.env.example +++ b/.env.example @@ -2,7 +2,6 @@ ALCHEMY_KEY="" ARBITRUM_ALCHEMY_KEY="" BALANCY_TOKEN="" BASE_ALCHEMY_KEY="" -CYPRESS_RECORD_KEY="" EDGE_CONFIG="" GOERLI_ALCHEMY_KEY="" GUILD_API_KEY="" diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml new file mode 100644 index 0000000000..d4c315e440 --- /dev/null +++ b/.github/workflows/playwright.yml @@ -0,0 +1,32 @@ +name: Playwright Tests +on: + deployment_status: + workflow_dispatch: + +concurrency: + group: "playwright" + +jobs: + test: + if: github.event_name == 'deployment_status' && github.event.deployment_status.state == 'success' + timeout-minutes: 60 + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: lts/* + - name: Install dependencies + run: npm ci + - name: Install Playwright Browsers + run: npx playwright install --with-deps + - name: Run Playwright tests + env: + DEPLOYMENT_URL: ${{ github.event.deployment_status.target_url }} + run: npm run test + - uses: actions/upload-artifact@v4 + if: ${{ failure() }} + with: + name: playwright-report + path: playwright-report/ + retention-days: 30 \ No newline at end of file diff --git a/.gitignore b/.gitignore index 2f93b01b8a..5284569a4c 100644 --- a/.gitignore +++ b/.gitignore @@ -29,9 +29,6 @@ yarn-error.log* .vercel -cypress/screenshots -cypress/videos - .idea *.tsbuildinfo @@ -39,3 +36,9 @@ cypress/videos storybook-static bun.lockb + +/playwright-report/ +/blob-report/ +/playwright/.cache/ +/playwright/.auth/ +/playwright/results diff --git a/biome.json b/biome.json index f3769b5e7d..63f837aa5b 100644 --- a/biome.json +++ b/biome.json @@ -10,7 +10,7 @@ "attributePosition": "auto" }, "files": { - "include": ["src", "cypress"], + "include": ["src", "playwright", "playwright.config.ts"], "ignore": ["node_modules", ".next", "public", ".out", "package-lock.json"] }, "organizeImports": { diff --git a/cypress.config.ts b/cypress.config.ts deleted file mode 100644 index 5bdd6e32f5..0000000000 --- a/cypress.config.ts +++ /dev/null @@ -1,53 +0,0 @@ -import fs from "fs" -// eslint-disable-next-line import/no-extraneous-dependencies -import { defineConfig } from "cypress" - -export default defineConfig({ - e2e: { - retries: 1, - experimentalMemoryManagement: true, - video: true, - baseUrl: "http://localhost:3000", - userAgent: "cypress", - specPattern: "./cypress/e2e/**/*.spec.ts", - defaultCommandTimeout: 15_000, - requestTimeout: 15_000, - env: { - guildApiV1Url: "https://api.guild.xyz/v1", - guildApiUrl: "https://api.guild.xyz/v2", - userAddress: "0x304Def656Babc745c53782639D3CaB00aCe8C843", - platformlessGuildName: "Platformless Cypress Gang", - platformlessGuildUrlName: "platformless-cypress-gang", - guildName: "Cypress Gang", - guildUrlName: "cypress-gang", - dcClientId: "868172385000509460", - dcServerId: "1096417797292171365", // We'll delete the created roles in this Discord server - tgId: "-1001653099938", - RUN_ID: "localhost", - TEST_GUILD_URL_NAME: "guild-e2e-cypress", - GUILD_CHECKOUT_TEST_GUILD_URL_NAME: "guild-checkout-e2e-cypress", - PINATA_PIN_FILE_API_URL: "https://api.pinata.cloud/pinning/pinFileToIPFS", - }, - setupNodeEvents(on, config) { - on( - "after:spec", - (spec: Cypress.Spec, results: CypressCommandLine.RunResult) => { - if (results && results.video) { - // Do we have failures for any retry attempts? - const failures = results.tests.some((test) => - test.attempts.some((attempt) => attempt.state === "failed") - ) - if (!failures) { - // delete the video if the spec passed and no tests retried - try { - fs.unlinkSync(results.video) - } catch (unlinkSyncError) { - console.log("[WARNING] fs.unlinkSync error", unlinkSyncError) - } - } - } - } - ) - }, - }, -}) diff --git a/cypress/e2e/1-roles-requirements-rewards/0-manage-roles.spec.ts b/cypress/e2e/1-roles-requirements-rewards/0-manage-roles.spec.ts deleted file mode 100644 index f28519766a..0000000000 --- a/cypress/e2e/1-roles-requirements-rewards/0-manage-roles.spec.ts +++ /dev/null @@ -1,167 +0,0 @@ -const CONTEXT = { - createdRoleId: undefined, - guild: undefined, - createdRequirement: undefined, -} - -describe("roles", () => { - beforeEach(() => { - cy.clearIndexedDB() - cy.visit(`/${Cypress.env("TEST_GUILD_URL_NAME")}`) - cy.connectWallet() - - cy.intercept( - "GET", - `${Cypress.env("guildApiUrl")}/guilds/guild-page/${Cypress.env( - "TEST_GUILD_URL_NAME" - )}` - ).as("fetchGuild") - }) - - it("can fetch guild id", () => { - cy.wait("@fetchGuild") - .then((intercept) => { - CONTEXT.guild = intercept.response.body - return intercept - }) - .its("response.statusCode") - .should("eq", 200) - }) - - it("can create a role without rewards", () => { - cy.intercept( - "POST", - `${Cypress.env("guildApiUrl")}/guilds/${CONTEXT.guild.id}/roles` - ).as("createRoleApiCall") - - cy.getByDataTest("add-role-button").click({ force: true }) - - cy.get("div[role='dialog'].chakra-slide").within(() => { - cy.get("input[name='name']").focus().blur() - cy.get( - "input[name='name'] ~ .chakra-collapse .chakra-form__error-message" - ).should("exist") - cy.get("input[name='name']").type("Cypress Test Role") - cy.get( - "input[name='name'] ~ .chakra-collapse .chakra-form__error-message" - ).should("not.exist") - - cy.contains("Open access").should("exist") - - cy.getByDataTest("save-role-button").click() - - cy.wait("@createRoleApiCall") - .then((intercept) => { - CONTEXT.createdRoleId = intercept.response?.body.id - return intercept - }) - .its("response.statusCode") - .should("eq", 201) - }) - }) - - it("can edit general role data", () => { - if (!CONTEXT.createdRoleId) - throw new Error("Can't run test, because couldn't create a role.") - - cy.intercept( - "PUT", - `${Cypress.env("guildApiUrl")}/guilds/${CONTEXT.guild.id}/roles/${ - CONTEXT.createdRoleId - }` - ).as("editRoleApiCall") - - cy.get(`#role-${CONTEXT.createdRoleId}`).should("exist") - cy.get(`#role-${CONTEXT.createdRoleId} button[aria-label='Edit role']`).click() - - cy.get("div[role='dialog'].chakra-slide").within(() => { - cy.get("input[name='name']").type(" (edited)") - cy.get("textarea[name='description']").type("wagmi") - - cy.getByDataTest("save-role-button").click() - cy.wait("@editRoleApiCall").its("response.statusCode").should("eq", 200) - }) - }) - - it("can add requirements", () => { - if (!CONTEXT.createdRoleId) - throw new Error("Can't run test, because couldn't create a role.") - - cy.intercept( - "POST", - `${Cypress.env("guildApiUrl")}/guilds/${CONTEXT.guild.id}/roles/${ - CONTEXT.createdRoleId - }/requirements` - ).as("createRequirementApiCall") - - cy.get(`#role-${CONTEXT.createdRoleId}`).should("exist") - cy.get(`#role-${CONTEXT.createdRoleId} button[aria-label='Edit role']`).click() - - cy.get("div[role='dialog'].chakra-slide").should("exist") - - cy.getByDataTest("add-requirement-button").click() - cy.getByDataTest("add-requirement-modal").within(() => { - cy.contains("Allowlist").click() - cy.get("textarea").type(Cypress.env("userAddress")) - cy.contains("Add requirement").click() - cy.wait("@createRequirementApiCall") - .its("response.statusCode") - .should("eq", 201) - }) - - cy.getByDataTest("add-requirement-button").click() - cy.getByDataTest("add-requirement-modal").within(() => { - cy.contains("Captcha").click() - cy.get("input[name='.data.maxAmount']").type("1") - cy.contains("Add requirement").click() - cy.wait("@createRequirementApiCall") - .its("response.statusCode") - .should("eq", 201) - }) - - cy.contains("Open access").should("not.exist") - }) - - it("can edit requirements list", () => { - if (!CONTEXT.createdRoleId) - throw new Error("Can't run test, because couldn't create a role.") - - cy.get(`#role-${CONTEXT.createdRoleId}`).should("exist") - cy.get(`#role-${CONTEXT.createdRoleId} button[aria-label='Edit role']`).click() - - cy.get("div[role='dialog'].chakra-slide").within(() => { - cy.get("button[aria-label='Remove requirement']").first().click() - }) - - cy.intercept( - "DELETE", - `${Cypress.env("guildApiUrl")}/guilds/*/roles/*/requirements/*` - ).as("deleteRequirement") - - cy.getByDataTest("delete-confirmation-button").click() - cy.wait("@deleteRequirement") - cy.contains("Requirement deleted!") - }) - - it("can delete a role", () => { - if (!CONTEXT.createdRoleId) - throw new Error("Can't run test, because couldn't create a role.") - - cy.get(`#role-${CONTEXT.createdRoleId}`).should("exist") - cy.get(`#role-${CONTEXT.createdRoleId} button[aria-label='Edit role']`).click() - - cy.get( - "div[role='dialog'].chakra-slide button[aria-label='Delete role']" - ).click() - - cy.intercept("DELETE", `${Cypress.env("guildApiUrl")}/guilds/*/roles/*`).as( - "deleteRole" - ) - - cy.getByDataTest("delete-confirmation-button").click() - cy.wait("@deleteRole") - cy.contains("Role deleted!") - }) -}) - -export {} diff --git a/cypress/e2e/2-guild-checkout/0-payment-requirement.spec.ts b/cypress/e2e/2-guild-checkout/0-payment-requirement.spec.ts deleted file mode 100644 index 0e040c795c..0000000000 --- a/cypress/e2e/2-guild-checkout/0-payment-requirement.spec.ts +++ /dev/null @@ -1,98 +0,0 @@ -const MUMBAI_USDC_ADDRESS = "0xe9dce89b076ba6107bb64ef30678efec11939234" -const UNHAPPY_PATH_ROLE_CARD_ID = "#role-90904" -const HAPPY_PATH_ROLE_CARD_ID = "#role-90671" - -describe("payment requirement", () => { - beforeEach(() => { - cy.clearIndexedDB() - cy.visit(`/${Cypress.env("GUILD_CHECKOUT_TEST_GUILD_URL_NAME")}`) - cy.connectWallet() - }) - - it("can create a payment requirement", () => { - cy.getByDataTest("add-role-button").click() - cy.getByDataTest("add-requirement-button").click() - cy.get("button") - .contains(/^Payment$/) - .click() - - cy.get("label").contains("Chain").click().type("Mumbai") - cy.getByDataTest("custom-select-option").contains("Polygon Mumbai").click() - cy.getByDataTest("payment-form-switch-network-button").should("exist") - - cy.get("label").contains("Token").click().type(`${MUMBAI_USDC_ADDRESS}{esc}`) - - cy.get("label").contains("Price").click().type("1") - - cy.getByDataTest("payment-form-switch-network-button").click() - cy.getByDataTest("payment-form-register-vault-button").click() - - cy.getByDataTest("add-requirement-modal").should("not.exist") - cy.get(".chakra-modal__body") - .contains(/^Pay(.)*on Polygon Mumbai/) - .should("exist") - }) - - it("can't buy a pass without allowance", () => { - cy.get(UNHAPPY_PATH_ROLE_CARD_ID).within(() => { - cy.getByDataTest("payment-requirement-buy-button").click() - }) - - cy.getByDataTest("token-info-fee-currency").should("contain", "1 USDT") - cy.getByDataTest("token-info-balance").should("contain", "0 USDT") - cy.getByDataTest("fees-table").get("span").should("contain", "1 USDT") - - cy.getByDataTest("tos-checkbox").should("not.be.visible") - cy.getByDataTest("buy-button").should("be.disabled") - - cy.get(".chakra-modal__footer").get("button").contains("Switch network").click() - - cy.getByDataTest("tos-checkbox").should("be.visible") - cy.getByDataTest("buy-allowance-button") - .should("be.visible") - .should("contain", "Allow Guild to use your USDT") - cy.getByDataTest("buy-button") - .should("be.disabled") - .should("contain", "Insufficient balance") - }) - - it("can buy a pass", () => { - cy.get(HAPPY_PATH_ROLE_CARD_ID).within(() => { - cy.getByDataTest("payment-requirement-buy-button").click() - }) - - cy.getByDataTest("token-info-fee-currency").should("contain", "1 USDC") - cy.getByDataTest("token-info-balance").should("contain", "10 USDC") - cy.getByDataTest("fees-table").get("span").should("contain", "1 USDC") - - cy.getByDataTest("tos-checkbox").should("not.be.visible") - cy.getByDataTest("buy-button").should("be.disabled") - - cy.get(".chakra-modal__footer").get("button").contains("Switch network").click() - - cy.getByDataTest("tos-checkbox").should("be.visible").click() - cy.getByDataTest("buy-button").should("be.enabled") - - cy.getByDataTest("buy-button") - .click() - .get(".chakra-alert") - .contains("Successful payment") - .should("be.visible") - }) - - it("can withdraw from a vault", () => { - cy.get(HAPPY_PATH_ROLE_CARD_ID).within(() => { - cy.getByDataTest("withdraw-button").should("be.enabled") - cy.getByDataTest("withdraw-button").should( - "contain", - "Switch to Polygon Mumbai to withdraw" - ) - cy.getByDataTest("withdraw-button").click() - - cy.getByDataTest("withdraw-button").should("be.enabled") - cy.getByDataTest("withdraw-button").click() - }) - - cy.get(".chakra-alert").contains("Successful withdraw").should("be.visible") - }) -}) diff --git a/cypress/e2e/2-guild-checkout/1-guild-pins.spec.ts b/cypress/e2e/2-guild-checkout/1-guild-pins.spec.ts deleted file mode 100644 index cb2c9ee864..0000000000 --- a/cypress/e2e/2-guild-checkout/1-guild-pins.spec.ts +++ /dev/null @@ -1,56 +0,0 @@ -describe("guild pins", () => { - beforeEach(() => { - cy.clearIndexedDB() - }) - - it("can't see the guild pin reward card (unauthenticated)", () => { - cy.visit(Cypress.env("TEST_GUILD_URL_NAME")) - cy.getByDataTest("guild-pin-reward-card").should("not.exist") - }) - - it("can see the guild pin reward card (authenticated)", () => { - cy.visit(Cypress.env("TEST_GUILD_URL_NAME")) - cy.connectWallet() - - cy.getByDataTest("guild-pin-reward-card").should("exist") - }) - - it("can see the pin setup modal", () => { - cy.visit(Cypress.env("TEST_GUILD_URL_NAME")) - cy.connectWallet() - - cy.getByDataTest("guild-pin-reward-card") - .get("button") - .contains("Setup Guild Pin") - .click() - - cy.get(".chakra-modal__header").contains("Setup Guild Pin").should("be.visible") - }) - - it("can mint a guild pin", () => { - cy.visit(Cypress.env("GUILD_CHECKOUT_TEST_GUILD_URL_NAME")) - cy.connectWallet() - - cy.intercept("POST", `${Cypress.env("guildApiUrl")}/guilds/*/pin`).as("claim") - - cy.getByDataTest("guild-pin-reward-card") - .get("button") - .contains("Mint Guild Pin") - .click() - - cy.get(".chakra-modal__footer").get("button").contains("Switch network").click() - - cy.get(".chakra-modal__footer") - .getByDataTest("fees-table") - .get("span") - .should("contain", "0.001 MATIC") - - cy.get(".chakra-modal__footer").get("button").contains("Mint NFT").click() - - cy.wait("@claim") - - cy.get(".chakra-alert") - .contains("GUILD_PIN_E2E_TEST_SUCCESS") - .should("be.visible") - }) -}) diff --git a/cypress/e2e/2-guild-checkout/2-nft-reward.spec.ts b/cypress/e2e/2-guild-checkout/2-nft-reward.spec.ts deleted file mode 100644 index b9de174c93..0000000000 --- a/cypress/e2e/2-guild-checkout/2-nft-reward.spec.ts +++ /dev/null @@ -1,110 +0,0 @@ -describe("nft reward", () => { - beforeEach(() => { - cy.clearIndexedDB() - cy.visit(Cypress.env("GUILD_CHECKOUT_TEST_GUILD_URL_NAME")) - cy.connectWallet() - }) - - it("can deploy an nft contract", () => { - // Scrolling to the top of the page to avoid a rare edge case where the add reward button isn't clickable - cy.scrollTo("top") - - cy.getByDataTest("add-reward-button").click() - cy.get("div[role='group']").contains("Create a gated NFT").click({ force: true }) - - cy.get("header.chakra-modal__header").within(() => { - cy.get("p").contains("Add NFT reward").should("exist") - }) - - cy.fixture("cypress-gang-nft.png", null).as("imageInput") - // cy.fixture("cypress-gang-nft.png").as("imageInput") - cy.get("input[type=file]").selectFile("@imageInput", { force: true }) - - cy.get("input[name='name']").type("E2E Test NFT") - cy.get("input[name='symbol']").type("E2E") - cy.get("textarea[name='description']").type("This is the description...") - - cy.get("input[name='attributes.0.name']").should("not.exist") - cy.get("input[name='attributes.0.value']").should("not.exist") - - cy.get("button").contains("Add attribute").click() - - cy.get("input[name='attributes.0.name']").should("exist") - cy.get("input[name='attributes.0.value']").should("exist") - - cy.get("input[name='attributes.0.name']").type("something") - cy.get("input[name='attributes.0.value']").type("anything") - - cy.get("label").contains("Chain").click().type("Mumbai{enter}") - - cy.get("label").contains("Price").click().type(".12345") - - cy.getByDataTest("create-nft-switch-network-button").should("exist") - cy.getByDataTest("create-nft-button").should("be.disabled") - cy.getByDataTest("create-nft-switch-network-button").click() - cy.getByDataTest("create-nft-button").should("be.enabled") - - cy.getByDataTest("create-nft-button").click() - - cy.intercept("GET", "/api/pinata-key", { - body: { - jwt: "mockJWT", - key: "mockKey", - }, - }) - cy.intercept("POST", Cypress.env("PINATA_PIN_FILE_API_URL"), (req) => { - req.reply({ - body: { - IpfsHash: "QmcTynAiKGnwnF77xMEdNAPkjpmpWiWUwcnNdXKU84uJPt", - }, - }) - }) - - // You'll see both an error and a success toast in the tests, that's expected, since we don't actually call the contract & just return an empty object - cy.get(".chakra-alert") - .contains("Successfully deployed NFT contract", { timeout: 40_000 }) - .should("be.visible") - }) - - it("can't collect the nft if requirements aren't satisfied", () => { - cy.get("p") - .contains("Collect: Cypress Gang #1") - .within(() => { - cy.get("a").click() - }) - - cy.url().should("contain", "/collect/") - - cy.getByDataTest("collect-nft-button").should("be.disabled") - cy.getByDataTest("switch-network-button").click() - - cy.getByDataTest("collect-nft-button").should("be.disabled") - }) - - it("can collect an nft if requirements are satisfied", () => { - cy.intercept( - "POST", - `${Cypress.env("guildApiUrl")}/guilds/*/roles/*/role-platforms/*/claim` - ).as("claim") - - cy.get("p") - .contains("Collect: Cypress Gang #2") - .within(() => { - cy.get("a").click() - }) - - cy.url().should("contain", "/collect/") - - cy.getByDataTest("collect-nft-button").should("be.disabled") - cy.getByDataTest("switch-network-button").click() - - cy.getByDataTest("collect-nft-button").should("be.enabled") - cy.getByDataTest("collect-nft-button").click() - - cy.wait("@claim", { - responseTimeout: 40_000, - }) - - cy.get(".chakra-alert").contains("Successfully collected NFT!").should("exist") - }) -}) diff --git a/cypress/e2e/readme.md b/cypress/e2e/readme.md deleted file mode 100644 index f7323f2b4d..0000000000 --- a/cypress/e2e/readme.md +++ /dev/null @@ -1,59 +0,0 @@ -# E2E testing with Cypress - -We use [Cypress](https://www.cypress.io/) for testing. The setup is quite simple, anyone can start writing tests easily. - -### Writing tests - -You should put the `*.spec.ts` files in the `/cypress/e2e` folder. The folder structure is not well defined here, so feel free to change it. Currently there is a `0-guild` folder with the (platformless) guild related tests, a `1-roles-requirements-rewards` folder with the role/requirement related tests, and a `2-guild-checkout` folder with Payment, Guild Pin, and NFT reward related ones. - -It is recommended to start each test by removing data from indexedDB & connecting the mock wallet to the app, like so: - -```ts -before(() => { - cy.clearIndexedDB() - cy.connectWallet() -}) -``` - -Then, you can write the actual test. The official [Cypress guide](https://docs.cypress.io/guides/end-to-end-testing/writing-your-first-end-to-end-test) is a great starting point, it contains most of the resources you'll need to write E2E tests. - -### Custom commands - -Sometimes you can find yourself in a situation where you start repeating yourself. In this case, you can create a custom command within the `/cypress/support/commands.ts` file. We already have a couple of custom commands, like `getByDataTest`, `connectWallet`, and `clearIndexedDB`. - -### How to use selectors - -You should try to be as specific as possible when using the `cy.get()` command. In some cases, you might not be able to select the specific element you want just by using an ID or class - in that case, feel free to add a `data-test` HTML attribute to the element and use the custom `getByDataTest` command in your tests. Here's an example: - -```html - -``` - -```ts -cy.getByDataTest("my-awesome-button").click() -``` - -### Mocking data - -Although we (should) try to use real data in our tests, sometimes we can't avoid mocking API responses. You can define the mocked data in JSON files inside the `/cypress/fixtures` directory, and then use it in your tests using `cy.intercept` a: - -```ts -// Intercept an API call -cy.intercept(`${Cypress.env("guildApiUrl")}/some/endpoint`, { - statusCode: 200, - fixture: "someData.json", -}).as("mockedData") - -// Wait for the intercepted response -cy.wait("@mockedData") -``` - -You can find more details about this topic in the official Cypress docs: [fixture](https://docs.cypress.io/api/commands/fixture#docusaurus_skipToContent_fallback), [intercept](https://docs.cypress.io/api/commands/intercept#docusaurus_skipToContent_fallback) - -### Running Cypress locally - -You can open Cypress UI using the `npm run cypress:open` command. Make sure to set the `NEXT_PUBLIC_MOCK_CONNECTOR` env var to `true` & also define the `NEXT_PUBLIC_E2E_WALLET_MNEMONIC` env var (you can find its value in our 1Password). If you'd just like to run tests in headless mode, you can use the `npm run test` command. - -### GitHub Action - -There's a GitHub Action for running the tests. It'll run when we push a commit to `main`, or when we open a pull request. diff --git a/cypress/fixtures/cypress-gang-nft.png b/cypress/fixtures/cypress-gang-nft.png deleted file mode 100644 index 11aa220855..0000000000 Binary files a/cypress/fixtures/cypress-gang-nft.png and /dev/null differ diff --git a/cypress/fixtures/testUserDiscordGateables.json b/cypress/fixtures/testUserDiscordGateables.json deleted file mode 100644 index 6ce8167d7e..0000000000 --- a/cypress/fixtures/testUserDiscordGateables.json +++ /dev/null @@ -1,8 +0,0 @@ -[ - { - "id": "1096417797292171365", - "name": "Cypress Gang", - "img": "/default_discord_icon.png", - "owner": true - } -] diff --git a/cypress/support/commands.ts b/cypress/support/commands.ts deleted file mode 100644 index 61d389e206..0000000000 --- a/cypress/support/commands.ts +++ /dev/null @@ -1,32 +0,0 @@ -Cypress.Commands.add("getByDataTest", (selector: string) => - cy.get(`[data-test='${selector}']`) -) - -Cypress.Commands.add("connectWallet", () => { - cy.intercept("POST", `${Cypress.env("guildApiUrl")}/users/*/public-key`).as( - "setPubKey" - ) - - cy.getByDataTest("connect-wallet-button").click() - cy.get("[data-wagmi-connector-id='mock']").click() - - cy.getByDataTest("verify-address-button").should("be.visible") - cy.getByDataTest("verify-address-button").click() - - cy.wait("@setPubKey", { requestTimeout: 30_000, responseTimeout: 30_000 }) - .its("response.statusCode") - .should("eq", 200) -}) - -Cypress.Commands.add("clearIndexedDB", () => { - indexedDB.deleteDatabase("guild.xyz") -}) - -// biome-ignore lint/style/noNamespace: -declare namespace Cypress { - interface Chainable { - getByDataTest(selector: string): Chainable> - connectWallet(): Chainable> - clearIndexedDB(): Chainable> - } -} diff --git a/cypress/support/e2e.ts b/cypress/support/e2e.ts deleted file mode 100644 index b7cb303d80..0000000000 --- a/cypress/support/e2e.ts +++ /dev/null @@ -1 +0,0 @@ -import "./commands" diff --git a/cypress/tsconfig.json b/cypress/tsconfig.json deleted file mode 100644 index b6c74e8347..0000000000 --- a/cypress/tsconfig.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "extends": "../tsconfig.json", - "compilerOptions": { - "target": "es5", - "lib": ["es5", "dom"], - "types": ["cypress", "node"], - "sourceMap": false - }, - "include": ["**/*.ts"] -} diff --git a/package-lock.json b/package-lock.json index 8d9db5b567..bcb5e80077 100644 --- a/package-lock.json +++ b/package-lock.json @@ -114,6 +114,7 @@ "@biomejs/biome": "1.8.3", "@chromatic-com/storybook": "^1.6.0", "@hookform/devtools": "^4.3.0", + "@playwright/test": "^1.45.3", "@storybook/addon-essentials": "^8.1.11", "@storybook/addon-interactions": "^8.1.11", "@storybook/addon-links": "^8.1.11", @@ -131,7 +132,6 @@ "abitype": "^1.0.2", "autoprefixer": "^10.4.19", "circular-dependency-plugin": "^5.2.2", - "cypress": "^13.3.2", "dotenv-cli": "^7.4.1", "dpdm": "^3.14.0", "event-stream": "^4.0.1", @@ -3807,78 +3807,6 @@ "node": ">=6" } }, - "node_modules/@colors/colors": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", - "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==", - "dev": true, - "optional": true, - "engines": { - "node": ">=0.1.90" - } - }, - "node_modules/@cypress/request": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@cypress/request/-/request-3.0.1.tgz", - "integrity": "sha512-TWivJlJi8ZDx2wGOw1dbLuHJKUYX7bWySw377nlnGOW3hP9/MUKIsEdXT/YngWxVdgNCHRBmFlBipE+5/2ZZlQ==", - "dev": true, - "dependencies": { - "aws-sign2": "~0.7.0", - "aws4": "^1.8.0", - "caseless": "~0.12.0", - "combined-stream": "~1.0.6", - "extend": "~3.0.2", - "forever-agent": "~0.6.1", - "form-data": "~2.3.2", - "http-signature": "~1.3.6", - "is-typedarray": "~1.0.0", - "isstream": "~0.1.2", - "json-stringify-safe": "~5.0.1", - "mime-types": "~2.1.19", - "performance-now": "^2.1.0", - "qs": "6.10.4", - "safe-buffer": "^5.1.2", - "tough-cookie": "^4.1.3", - "tunnel-agent": "^0.6.0", - "uuid": "^8.3.2" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/@cypress/request/node_modules/form-data": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", - "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", - "dev": true, - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.6", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 0.12" - } - }, - "node_modules/@cypress/xvfb": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@cypress/xvfb/-/xvfb-1.2.4.tgz", - "integrity": "sha512-skbBzPggOVYCbnGgV+0dmBdW/s77ZkAOXIC1knS8NagwDjBrNC1LuXtQJeiN6l+m7lzmHtaoUw/ctJKdqkG57Q==", - "dev": true, - "dependencies": { - "debug": "^3.1.0", - "lodash.once": "^4.1.1" - } - }, - "node_modules/@cypress/xvfb/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "dependencies": { - "ms": "^2.1.1" - } - }, "node_modules/@dotlottie/common": { "version": "0.7.11", "resolved": "https://registry.npmjs.org/@dotlottie/common/-/common-0.7.11.tgz", @@ -7771,6 +7699,21 @@ "node": ">=14" } }, + "node_modules/@playwright/test": { + "version": "1.45.3", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.45.3.tgz", + "integrity": "sha512-UKF4XsBfy+u3MFWEH44hva1Q8Da28G6RFtR2+5saw+jgAFQV5yYnB1fu68Mz7fO+5GJF3wgwAIs0UelU8TxFrA==", + "devOptional": true, + "dependencies": { + "playwright": "1.45.3" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/@pmmmwh/react-refresh-webpack-plugin": { "version": "0.5.15", "resolved": "https://registry.npmjs.org/@pmmmwh/react-refresh-webpack-plugin/-/react-refresh-webpack-plugin-0.5.15.tgz", @@ -13199,18 +13142,6 @@ "@types/send": "*" } }, - "node_modules/@types/sinonjs__fake-timers": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-8.1.1.tgz", - "integrity": "sha512-0kSuKjAS0TrGLJ0M/+8MaFkGsQhZpB6pxOmvS3K8FYI72K//YmdfoW9X2qPsAKh1mkwxGD5zib9s1FIFed6E8g==", - "dev": true - }, - "node_modules/@types/sizzle": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/@types/sizzle/-/sizzle-2.3.8.tgz", - "integrity": "sha512-0vWLNK2D5MT9dg0iOo8GlKguPAU02QjmZitPEsXRuJXU/OGIOt9vT9Fc26wtYuavLxtO45v9PGleoL9Z0k1LHg==", - "dev": true - }, "node_modules/@types/stack-utils": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", @@ -13270,16 +13201,6 @@ "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", "peer": true }, - "node_modules/@types/yauzl": { - "version": "2.10.3", - "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.3.tgz", - "integrity": "sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==", - "dev": true, - "optional": true, - "dependencies": { - "@types/node": "*" - } - }, "node_modules/@ungap/structured-clone": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", @@ -15591,19 +15512,6 @@ "node": ">=8.9.0" } }, - "node_modules/aggregate-error": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", - "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", - "dev": true, - "dependencies": { - "clean-stack": "^2.0.0", - "indent-string": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/ajv": { "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", @@ -15678,37 +15586,12 @@ "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", "dev": true, + "optional": true, + "peer": true, "engines": { "node": ">=6" } }, - "node_modules/ansi-escapes": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", - "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", - "dev": true, - "dependencies": { - "type-fest": "^0.21.3" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ansi-escapes/node_modules/type-fest": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", - "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/ansi-fragments": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/ansi-fragments/-/ansi-fragments-0.2.1.tgz", @@ -15858,26 +15741,6 @@ "integrity": "sha512-Quji6+8kLBC3NnBeo14nPDq0+2jUs5s3/xEye+udFHumHhRk4M7aAMXp/PBJqkKYGuuyR9M/6Dq7d2AViiGmhw==", "peer": true }, - "node_modules/arch": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/arch/-/arch-2.2.0.tgz", - "integrity": "sha512-Of/R0wqp83cgHozfIYLbBMnej79U/SVGOOyuB3VVFv1NRM/PSFMK12x9KVtiYzJqmnU5WR2qp0Z5rHb7sWGnFQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, "node_modules/arg": { "version": "5.0.2", "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", @@ -16009,21 +15872,6 @@ "node": ">=4" } }, - "node_modules/astral-regex": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", - "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/async": { - "version": "3.2.5", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.5.tgz", - "integrity": "sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==", - "dev": true - }, "node_modules/async-limiter": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz", @@ -16043,15 +15891,6 @@ "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" }, - "node_modules/at-least-node": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", - "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", - "dev": true, - "engines": { - "node": ">= 4.0.0" - } - }, "node_modules/atomic-sleep": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/atomic-sleep/-/atomic-sleep-1.0.0.tgz", @@ -16465,18 +16304,6 @@ "ieee754": "^1.1.13" } }, - "node_modules/blob-util": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/blob-util/-/blob-util-2.0.2.tgz", - "integrity": "sha512-T7JQa+zsXXEa6/8ZhHcQEW1UFfVM49Ts65uBkFL6fz2QmrElqmbajIDJvuA0tEhRe5eIjpV9ZF+0RfZR9voJFQ==", - "dev": true - }, - "node_modules/bluebird": { - "version": "3.7.2", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", - "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", - "dev": true - }, "node_modules/bn.js": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz", @@ -16793,15 +16620,6 @@ "ieee754": "^1.2.1" } }, - "node_modules/buffer-crc32": { - "version": "0.2.13", - "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", - "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", - "dev": true, - "engines": { - "node": "*" - } - }, "node_modules/buffer-from": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", @@ -16985,15 +16803,6 @@ "node": ">= 0.8" } }, - "node_modules/cachedir": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/cachedir/-/cachedir-2.4.0.tgz", - "integrity": "sha512-9EtFOZR8g22CL7BWjJ9BUx1+A/djkofnyW3aOXZORNW2kxoUpx2h+uN2cOqwPmFhnpVmxg+KW2OjOSgChTEvsQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, "node_modules/call-bind": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", @@ -17263,15 +17072,6 @@ "node": "*" } }, - "node_modules/check-more-types": { - "version": "2.24.0", - "resolved": "https://registry.npmjs.org/check-more-types/-/check-more-types-2.24.0.tgz", - "integrity": "sha512-Pj779qHxV2tuapviy1bSZNEL1maXr13bPYpsvSDB68HlYcYuhlDrmGd63i0JHMCLKzc7rUSNIrpdJlhVlNwrxA==", - "dev": true, - "engines": { - "node": ">= 0.8.0" - } - }, "node_modules/chokidar": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", @@ -17364,6 +17164,7 @@ "url": "https://github.com/sponsors/sibiraj-s" } ], + "peer": true, "engines": { "node": ">=8" } @@ -17449,15 +17250,6 @@ "node": ">=0.10.0" } }, - "node_modules/clean-stack": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", - "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", - "dev": true, - "engines": { - "node": ">=6" - } - }, "node_modules/cli-cursor": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", @@ -17491,37 +17283,6 @@ "node": ">= 0.2.0" } }, - "node_modules/cli-table3": { - "version": "0.6.5", - "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.5.tgz", - "integrity": "sha512-+W/5efTR7y5HRD7gACw9yQjqMVvEMLBHmboM/kPWam+H+Hmyrgjh6YncVKK122YZkXrLudzTuAukUw9FnMf7IQ==", - "dev": true, - "dependencies": { - "string-width": "^4.2.0" - }, - "engines": { - "node": "10.* || >= 12.*" - }, - "optionalDependencies": { - "@colors/colors": "1.5.0" - } - }, - "node_modules/cli-truncate": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-2.1.0.tgz", - "integrity": "sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==", - "dev": true, - "dependencies": { - "slice-ansi": "^3.0.0", - "string-width": "^4.2.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/client-only": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", @@ -17938,15 +17699,6 @@ "integrity": "sha512-QE33hToZseCH3jS0qN96O/bSh3kaw/h+Tq7ngyY9eWDUnTlTNUyqfqvCXioLe5Na5jFsL78ra/wuBU4iuEgd4w==", "dev": true }, - "node_modules/common-tags": { - "version": "1.8.2", - "resolved": "https://registry.npmjs.org/common-tags/-/common-tags-1.8.2.tgz", - "integrity": "sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA==", - "dev": true, - "engines": { - "node": ">=4.0.0" - } - }, "node_modules/commondir": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", @@ -18534,193 +18286,6 @@ "uniq": "^1.0.0" } }, - "node_modules/cypress": { - "version": "13.13.0", - "resolved": "https://registry.npmjs.org/cypress/-/cypress-13.13.0.tgz", - "integrity": "sha512-ou/MQUDq4tcDJI2FsPaod2FZpex4kpIK43JJlcBgWrX8WX7R/05ZxGTuxedOuZBfxjZxja+fbijZGyxiLP6CFA==", - "dev": true, - "hasInstallScript": true, - "dependencies": { - "@cypress/request": "^3.0.0", - "@cypress/xvfb": "^1.2.4", - "@types/sinonjs__fake-timers": "8.1.1", - "@types/sizzle": "^2.3.2", - "arch": "^2.2.0", - "blob-util": "^2.0.2", - "bluebird": "^3.7.2", - "buffer": "^5.7.1", - "cachedir": "^2.3.0", - "chalk": "^4.1.0", - "check-more-types": "^2.24.0", - "cli-cursor": "^3.1.0", - "cli-table3": "~0.6.1", - "commander": "^6.2.1", - "common-tags": "^1.8.0", - "dayjs": "^1.10.4", - "debug": "^4.3.4", - "enquirer": "^2.3.6", - "eventemitter2": "6.4.7", - "execa": "4.1.0", - "executable": "^4.1.1", - "extract-zip": "2.0.1", - "figures": "^3.2.0", - "fs-extra": "^9.1.0", - "getos": "^3.2.1", - "is-ci": "^3.0.1", - "is-installed-globally": "~0.4.0", - "lazy-ass": "^1.6.0", - "listr2": "^3.8.3", - "lodash": "^4.17.21", - "log-symbols": "^4.0.0", - "minimist": "^1.2.8", - "ospath": "^1.2.2", - "pretty-bytes": "^5.6.0", - "process": "^0.11.10", - "proxy-from-env": "1.0.0", - "request-progress": "^3.0.0", - "semver": "^7.5.3", - "supports-color": "^8.1.1", - "tmp": "~0.2.3", - "untildify": "^4.0.0", - "yauzl": "^2.10.0" - }, - "bin": { - "cypress": "bin/cypress" - }, - "engines": { - "node": "^16.0.0 || ^18.0.0 || >=20.0.0" - } - }, - "node_modules/cypress/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/cypress/node_modules/buffer": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", - "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.1.13" - } - }, - "node_modules/cypress/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/cypress/node_modules/chalk/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/cypress/node_modules/eventemitter2": { - "version": "6.4.7", - "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-6.4.7.tgz", - "integrity": "sha512-tYUSVOGeQPKt/eC1ABfhHy5Xd96N3oIijJvN3O9+TsC28T5V9yX9oEfEK5faP0EFSNVOG97qtAS68GBrQB2hDg==", - "dev": true - }, - "node_modules/cypress/node_modules/fs-extra": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", - "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", - "dev": true, - "dependencies": { - "at-least-node": "^1.0.0", - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/cypress/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/cypress/node_modules/proxy-from-env": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.0.0.tgz", - "integrity": "sha512-F2JHgJQ1iqwnHDcQjVBsq3n/uoaFL+iPW/eAeL7kVxy/2RrWaN4WroKjjvbsoRtv0ftelNyC01bjRhn/bhcf4A==", - "dev": true - }, - "node_modules/cypress/node_modules/semver": { - "version": "7.6.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", - "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/cypress/node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, "node_modules/d3-array": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.1.tgz", @@ -19646,6 +19211,8 @@ "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.4.1.tgz", "integrity": "sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==", "dev": true, + "optional": true, + "peer": true, "dependencies": { "ansi-colors": "^4.1.1", "strip-ansi": "^6.0.1" @@ -19659,6 +19226,8 @@ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, + "optional": true, + "peer": true, "dependencies": { "ansi-regex": "^5.0.1" }, @@ -20088,50 +19657,6 @@ "safe-buffer": "^5.1.1" } }, - "node_modules/execa": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-4.1.0.tgz", - "integrity": "sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==", - "dev": true, - "dependencies": { - "cross-spawn": "^7.0.0", - "get-stream": "^5.0.0", - "human-signals": "^1.1.1", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.0", - "onetime": "^5.1.0", - "signal-exit": "^3.0.2", - "strip-final-newline": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" - } - }, - "node_modules/executable": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/executable/-/executable-4.1.1.tgz", - "integrity": "sha512-8iA79xD3uAch729dUG8xaaBBFGaEa0wdD2VkYLFHwlqosEj/jT66AzcreRDSgV7ehnNLBW2WR5jIXwGKjVdTLg==", - "dev": true, - "dependencies": { - "pify": "^2.2.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/executable/node_modules/pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/express": { "version": "4.19.2", "resolved": "https://registry.npmjs.org/express/-/express-4.19.2.tgz", @@ -20232,26 +19757,6 @@ "url": "https://github.com/sponsors/jaydenseric" } }, - "node_modules/extract-zip": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz", - "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==", - "dev": true, - "dependencies": { - "debug": "^4.1.1", - "get-stream": "^5.1.0", - "yauzl": "^2.10.0" - }, - "bin": { - "extract-zip": "cli.js" - }, - "engines": { - "node": ">= 10.17.0" - }, - "optionalDependencies": { - "@types/yauzl": "^2.9.1" - } - }, "node_modules/extsprintf": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", @@ -20358,44 +19863,11 @@ "walk-up-path": "^3.0.1" } }, - "node_modules/fd-slicer": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", - "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==", - "dev": true, - "dependencies": { - "pend": "~1.2.0" - } - }, "node_modules/fflate": { "version": "0.8.2", "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz", "integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==" }, - "node_modules/figures": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", - "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", - "dev": true, - "dependencies": { - "escape-string-regexp": "^1.0.5" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/figures/node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true, - "engines": { - "node": ">=0.8.0" - } - }, "node_modules/file-selector": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/file-selector/-/file-selector-0.6.0.tgz", @@ -21115,30 +20587,6 @@ "resolved": "https://registry.npmjs.org/get-port-please/-/get-port-please-3.1.2.tgz", "integrity": "sha512-Gxc29eLs1fbn6LQ4jSU4vXjlwyZhF5HsGuMAa7gqBP4Rw4yxxltyDUuF5MBclFzDTXO+ACchGQoeela4DSfzdQ==" }, - "node_modules/get-stream": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", - "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", - "dev": true, - "dependencies": { - "pump": "^3.0.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/getos": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/getos/-/getos-3.2.1.tgz", - "integrity": "sha512-U56CfOK17OKgTVqozZjUKNdkfEv6jk5WISBJ8SHoagjE6L69zOwl3Z+O8myjY9MEW3i2HPWQBt/LTbCgcC973Q==", - "dev": true, - "dependencies": { - "async": "^3.2.0" - } - }, "node_modules/getpass": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", @@ -21230,21 +20678,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/global-dirs": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-3.0.1.tgz", - "integrity": "sha512-NBcGGFbBA9s1VzD41QXDG+3++t9Mn5t1FpLdhESY6oKY4gYTFpX4wO3sqGUa0Srjtbfj3szX0RnemmrVRUdULA==", - "dev": true, - "dependencies": { - "ini": "2.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/globals": { "version": "11.12.0", "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", @@ -21827,35 +21260,12 @@ "node": ">= 0.12.0" } }, - "node_modules/http-signature": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.3.6.tgz", - "integrity": "sha512-3adrsD6zqo4GsTqtO7FyrejHNv+NgiIfAfv68+jVlFmSr9OGy7zrxONceFRLKvnnZA5jbxQBX1u9PpB6Wi32Gw==", - "dev": true, - "dependencies": { - "assert-plus": "^1.0.0", - "jsprim": "^2.0.2", - "sshpk": "^1.14.1" - }, - "engines": { - "node": ">=0.10" - } - }, "node_modules/https-browserify": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz", "integrity": "sha512-J+FkSdyD+0mA0N+81tMotaRMfSL9SGi+xpD3T6YApKsc3bGSXJlfXri3VyFOeYkfLRQisDk1W+jIFFKBeUBbBg==", "dev": true }, - "node_modules/human-signals": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz", - "integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==", - "dev": true, - "engines": { - "node": ">=8.12.0" - } - }, "node_modules/husky": { "version": "8.0.3", "resolved": "https://registry.npmjs.org/husky/-/husky-8.0.3.tgz", @@ -22025,15 +21435,6 @@ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, - "node_modules/ini": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ini/-/ini-2.0.0.tgz", - "integrity": "sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==", - "dev": true, - "engines": { - "node": ">=10" - } - }, "node_modules/inline-style-parser": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.1.1.tgz", @@ -22145,18 +21546,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-ci": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-3.0.1.tgz", - "integrity": "sha512-ZYvCgrefwqoQ6yTyYUbQu64HsITZ3NfKX1lzaEYdkTDcfKzzCI/wthRRYKkdjHKFVgNiXKAKm65Zo1pk2as/QQ==", - "dev": true, - "dependencies": { - "ci-info": "^3.2.0" - }, - "bin": { - "is-ci": "bin.js" - } - }, "node_modules/is-core-module": { "version": "2.14.0", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.14.0.tgz", @@ -22266,22 +21655,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/is-installed-globally": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.4.0.tgz", - "integrity": "sha512-iwGqO3J21aaSkC7jWnHP/difazwS7SFeIqxv6wEtLU8Y5KlzFTjyqcSIT0d8s4+dDhKytsk9PJZ2BkS5eZwQRQ==", - "dev": true, - "dependencies": { - "global-dirs": "^3.0.0", - "is-path-inside": "^3.0.2" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/is-interactive": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", @@ -22314,15 +21687,6 @@ "node": ">=0.12.0" } }, - "node_modules/is-path-inside": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", - "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/is-plain-obj": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", @@ -23147,21 +22511,6 @@ "graceful-fs": "^4.1.6" } }, - "node_modules/jsprim": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-2.0.2.tgz", - "integrity": "sha512-gqXddjPqQ6G40VdnI6T6yObEC+pDNvyP95wdQhkWkg7crHH3km5qP1FsOXEkzEQwnz6gz5qGTn1c2Y52wP3OyQ==", - "dev": true, - "engines": [ - "node >=0.6.0" - ], - "dependencies": { - "assert-plus": "1.0.0", - "extsprintf": "1.3.0", - "json-schema": "0.4.0", - "verror": "1.10.0" - } - }, "node_modules/jwt-decode": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/jwt-decode/-/jwt-decode-3.1.2.tgz", @@ -23219,15 +22568,6 @@ "node": ">= 8" } }, - "node_modules/lazy-ass": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/lazy-ass/-/lazy-ass-1.6.0.tgz", - "integrity": "sha512-cc8oEVoctTvsFZ/Oje/kGnHbpWHYBe8IAJe4C0QNc3t8uM/0Y8+erSz/7Y1ALuXTEZTMvxXwO6YbX1ey3ujiZw==", - "dev": true, - "engines": { - "node": "> 0.8" - } - }, "node_modules/leven": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", @@ -23751,77 +23091,6 @@ "listhen": "bin/listhen.mjs" } }, - "node_modules/listr2": { - "version": "3.14.0", - "resolved": "https://registry.npmjs.org/listr2/-/listr2-3.14.0.tgz", - "integrity": "sha512-TyWI8G99GX9GjE54cJ+RrNMcIFBfwMPxc3XTFiAYGN4s10hWROGtOg7+O6u6LE3mNkyld7RSLE6nrKBvTfcs3g==", - "dev": true, - "dependencies": { - "cli-truncate": "^2.1.0", - "colorette": "^2.0.16", - "log-update": "^4.0.0", - "p-map": "^4.0.0", - "rfdc": "^1.3.0", - "rxjs": "^7.5.1", - "through": "^2.3.8", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "enquirer": ">= 2.3.0 < 3" - }, - "peerDependenciesMeta": { - "enquirer": { - "optional": true - } - } - }, - "node_modules/listr2/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/listr2/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/listr2/node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, "node_modules/lit": { "version": "2.8.0", "resolved": "https://registry.npmjs.org/lit/-/lit-2.8.0.tgz", @@ -23929,12 +23198,6 @@ "resolved": "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.2.tgz", "integrity": "sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==" }, - "node_modules/lodash.once": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", - "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==", - "dev": true - }, "node_modules/lodash.throttle": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/lodash.throttle/-/lodash.throttle-4.1.1.tgz", @@ -24004,82 +23267,6 @@ "node": ">=8" } }, - "node_modules/log-update": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/log-update/-/log-update-4.0.0.tgz", - "integrity": "sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg==", - "dev": true, - "dependencies": { - "ansi-escapes": "^4.3.0", - "cli-cursor": "^3.1.0", - "slice-ansi": "^4.0.0", - "wrap-ansi": "^6.2.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/log-update/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/log-update/node_modules/slice-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", - "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "astral-regex": "^2.0.0", - "is-fullwidth-code-point": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/slice-ansi?sponsor=1" - } - }, - "node_modules/log-update/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/log-update/node_modules/wrap-ansi": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", - "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/logkitty": { "version": "0.7.1", "resolved": "https://registry.npmjs.org/logkitty/-/logkitty-0.7.1.tgz", @@ -27068,12 +26255,6 @@ "integrity": "sha512-gjcpUc3clBf9+210TRaDWbf+rZZZEshZ+DlXMRCeAjp0xhTrnQsKHypIy1J3d5hKdUzj69t708EHtU8P6bUn0A==", "dev": true }, - "node_modules/ospath": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/ospath/-/ospath-1.2.2.tgz", - "integrity": "sha512-o6E5qJV5zkAbIDNhGSIlyOhScKXgQrSRMilfph0clDfM0nEnBOlKlH4sWDmG95BW/CvwNz0vmm7dJVtU2KlMiA==", - "dev": true - }, "node_modules/p-limit": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", @@ -27102,21 +26283,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/p-map": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", - "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", - "dev": true, - "dependencies": { - "aggregate-error": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/p-try": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", @@ -27330,12 +26496,6 @@ "node": ">=0.12" } }, - "node_modules/pend": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", - "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", - "dev": true - }, "node_modules/performance-now": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", @@ -27522,6 +26682,49 @@ "pathe": "^1.1.2" } }, + "node_modules/playwright": { + "version": "1.45.3", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.45.3.tgz", + "integrity": "sha512-QhVaS+lpluxCaioejDZ95l4Y4jSFCsBvl2UZkpeXlzxmqS+aABr5c82YmfMHrL6x27nvrvykJAFpkzT2eWdJww==", + "devOptional": true, + "dependencies": { + "playwright-core": "1.45.3" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "fsevents": "2.3.2" + } + }, + "node_modules/playwright-core": { + "version": "1.45.3", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.45.3.tgz", + "integrity": "sha512-+ym0jNbcjikaOwwSZycFbwkWgfruWvYlJfThKYAlImbxUgdWFO2oW70ojPm4OpE4t6TAo2FY/smM+hpVTtkhDA==", + "devOptional": true, + "bin": { + "playwright-core": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/playwright/node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, "node_modules/pngjs": { "version": "3.4.0", "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-3.4.0.tgz", @@ -28078,18 +27281,6 @@ } } }, - "node_modules/pretty-bytes": { - "version": "5.6.0", - "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz", - "integrity": "sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==", - "dev": true, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/pretty-error": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/pretty-error/-/pretty-error-4.0.0.tgz", @@ -28454,21 +27645,6 @@ "node": ">=8" } }, - "node_modules/qs": { - "version": "6.10.4", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.4.tgz", - "integrity": "sha512-OQiU+C+Ds5qiH91qh/mg0w+8nwQuLjM4F4M/PbmhDOoYehPh+Fb0bDjtR1sOvy7YKxvj28Y/M0PhP5uVX0kB+g==", - "dev": true, - "dependencies": { - "side-channel": "^1.0.4" - }, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/query-string": { "version": "7.1.3", "resolved": "https://registry.npmjs.org/query-string/-/query-string-7.1.3.tgz", @@ -28513,12 +27689,6 @@ "node": ">=0.4.x" } }, - "node_modules/querystringify": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", - "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==", - "dev": true - }, "node_modules/queue": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/queue/-/queue-6.0.2.tgz", @@ -30255,15 +29425,6 @@ "node": ">= 6" } }, - "node_modules/request-progress": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/request-progress/-/request-progress-3.0.0.tgz", - "integrity": "sha512-MnWzEHHaxHO2iWiQuHrUPBi/1WeBf5PkxQqNyNvLl9VAYSdXkP8tQ3pBSeCPD+yw0v0Aq1zosWLz0BdeXpWwZg==", - "dev": true, - "dependencies": { - "throttleit": "^1.0.0" - } - }, "node_modules/request/node_modules/form-data": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", @@ -30355,12 +29516,6 @@ "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==" }, - "node_modules/requires-port": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", - "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", - "dev": true - }, "node_modules/resolve": { "version": "1.22.8", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", @@ -30579,15 +29734,6 @@ "queue-microtask": "^1.2.2" } }, - "node_modules/rxjs": { - "version": "7.8.1", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", - "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", - "dev": true, - "dependencies": { - "tslib": "^2.1.0" - } - }, "node_modules/sade": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/sade/-/sade-1.8.1.tgz", @@ -31040,35 +30186,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/slice-ansi": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-3.0.0.tgz", - "integrity": "sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "astral-regex": "^2.0.0", - "is-fullwidth-code-point": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/slice-ansi/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, "node_modules/slugify": { "version": "1.6.6", "resolved": "https://registry.npmjs.org/slugify/-/slugify-1.6.6.tgz", @@ -32293,15 +31410,6 @@ "integrity": "sha512-fcwX4mndzpLQKBS1DVYhGAcYaYt7vsHNIvQV+WXMvnow5cgjPphq5CaayLaGsjRdSCKZFNGt7/GYAuXaNOiYCA==", "peer": true }, - "node_modules/throttleit": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/throttleit/-/throttleit-1.0.1.tgz", - "integrity": "sha512-vDZpf9Chs9mAdfY046mcPt8fg5QSZr37hEH4TXYBnDF+izxgrbRGUAAaBvIk/fJm9aOFCGFd1EsNg5AZCbnQCQ==", - "dev": true, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/through": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", @@ -32369,15 +31477,6 @@ "node": ">=14.0.0" } }, - "node_modules/tmp": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.3.tgz", - "integrity": "sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w==", - "dev": true, - "engines": { - "node": ">=14.14" - } - }, "node_modules/tmpl": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", @@ -32426,30 +31525,6 @@ "resolved": "https://registry.npmjs.org/toposort/-/toposort-2.0.2.tgz", "integrity": "sha512-0a5EOkAUp8D4moMi2W8ZF8jcga7BgZd91O/yabJCFY8az+XSzeGyTKs0Aoo897iV1Nj6guFq8orWDS96z91oGg==" }, - "node_modules/tough-cookie": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.4.tgz", - "integrity": "sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==", - "dev": true, - "dependencies": { - "psl": "^1.1.33", - "punycode": "^2.1.1", - "universalify": "^0.2.0", - "url-parse": "^1.5.3" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/tough-cookie/node_modules/universalify": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz", - "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==", - "dev": true, - "engines": { - "node": ">= 4.0.0" - } - }, "node_modules/tr46": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", @@ -33076,15 +32151,6 @@ "node": ">=0.4.0" } }, - "node_modules/untildify": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/untildify/-/untildify-4.0.0.tgz", - "integrity": "sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/untun": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/untun/-/untun-0.1.3.tgz", @@ -33150,16 +32216,6 @@ "qs": "^6.11.2" } }, - "node_modules/url-parse": { - "version": "1.5.10", - "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", - "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", - "dev": true, - "dependencies": { - "querystringify": "^2.1.1", - "requires-port": "^1.0.0" - } - }, "node_modules/url/node_modules/punycode": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", @@ -34172,16 +33228,6 @@ "node": ">=12" } }, - "node_modules/yauzl": { - "version": "2.10.0", - "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", - "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", - "dev": true, - "dependencies": { - "buffer-crc32": "~0.2.3", - "fd-slicer": "~1.1.0" - } - }, "node_modules/yjs": { "version": "13.6.18", "resolved": "https://registry.npmjs.org/yjs/-/yjs-13.6.18.tgz", diff --git a/package.json b/package.json index af5abe9437..d60f4a5006 100644 --- a/package.json +++ b/package.json @@ -9,16 +9,15 @@ "start": "next start", "type-check": "tsc --pretty --noEmit --incremental false --project './tsconfig.build.json'", "write-check": "npx @biomejs/biome check --write --unsafe .", - "cypress:open": "cypress open", - "cypress:open:mobile": "cypress open --config viewportWidth=375,viewportHeight=667", - "test": "cypress run", - "test:mobile": "cypress run --config viewportWidth=375,viewportHeight=667", "snyk-protect": "snyk-protect", "postinstall": "npx dotenv-cli -e .env.local -- bash -c 'npm i --no-save --ignore-scripts $WAAS_WEB_URL $WAAS_VIEM_URL'", "storybook": "storybook dev -p 6006", "build-storybook": "storybook build", "dcd": "npx tsx scripts/detectCircularDependencies.ts", - "analyze-bundle": "node scripts/bundleAnalyzer.mjs" + "analyze-bundle": "node scripts/bundleAnalyzer.mjs", + "test": "npx playwright test", + "test:ui": "npx playwright test --ui", + "test:debug": "npx playwright test --debug" }, "dependencies": { "@bugsnag/js": "^7.22.4", @@ -126,6 +125,7 @@ "@biomejs/biome": "1.8.3", "@chromatic-com/storybook": "^1.6.0", "@hookform/devtools": "^4.3.0", + "@playwright/test": "^1.45.3", "@storybook/addon-essentials": "^8.1.11", "@storybook/addon-interactions": "^8.1.11", "@storybook/addon-links": "^8.1.11", @@ -143,7 +143,6 @@ "abitype": "^1.0.2", "autoprefixer": "^10.4.19", "circular-dependency-plugin": "^5.2.2", - "cypress": "^13.3.2", "dotenv-cli": "^7.4.1", "dpdm": "^3.14.0", "event-stream": "^4.0.1", diff --git a/playwright.config.ts b/playwright.config.ts new file mode 100644 index 0000000000..fa9dc2d5fc --- /dev/null +++ b/playwright.config.ts @@ -0,0 +1,71 @@ +import { defineConfig, devices } from "@playwright/test" + +const baseURL = process.env.DEPLOYMENT_URL || "http://localhost:3000" + +export default defineConfig({ + testDir: "./playwright", + fullyParallel: true, + forbidOnly: !!process.env.CI, + retries: process.env.CI ? 2 : 0, + workers: process.env.CI ? 1 : undefined, + reporter: "html", + outputDir: "playwright/results/", + + use: { + baseURL, + trace: "on-first-retry", + }, + + projects: [ + { + name: "auth-setup", + testMatch: /auth\.setup\.ts/, + use: { + ...devices["Desktop Chrome"], + userAgent: `${devices["Desktop Chrome"].userAgent} GUILD_E2E`, + }, + }, + { + name: "chromium", + use: { + ...devices["Desktop Chrome"], + userAgent: `${devices["Desktop Chrome"].userAgent} GUILD_E2E`, + }, + dependencies: ["auth-setup"], + }, + // { + // name: "firefox", + // use: { + // ...devices["Desktop Firefox"], + // }, + // dependencies: ["auth-setup"], + // }, + // { + // name: "webkit", + // use: { + // ...devices["Desktop Safari"], + // }, + // dependencies: ["auth-setup"], + // }, + // { + // name: "Mobile Chrome", + // use: { + // ...devices["Pixel 5"], + // }, + // dependencies: ["auth-setup"], + // }, + // { + // name: "Mobile Safari", + // use: { + // ...devices["iPhone 12"], + // }, + // dependencies: ["auth-setup"], + // }, + ], + + webServer: { + command: process.env.CI ? "" : "npm run start", + url: baseURL, + reuseExistingServer: true, + }, +}) diff --git a/playwright/auth.setup.ts b/playwright/auth.setup.ts new file mode 100644 index 0000000000..659a055409 --- /dev/null +++ b/playwright/auth.setup.ts @@ -0,0 +1,89 @@ +import fs from "fs" +import path from "path" +import { expect, test as setup } from "@playwright/test" +import { StoredKeyPair } from "utils/keyPair" +import { TEST_USER } from "./constants" + +const authFile = "playwright/.auth/user.json" + +setup("authenticate", async ({ page }) => { + await page.addInitScript(() => { + /** + * Always calling the `generateKey` method with `extractable: true`, so we can save & re-use the generated keys in our E2E tests + */ + const originalGenerateKey = window.crypto.subtle.generateKey + + window.crypto.subtle.generateKey = async function ( + algorithm, + _extractable, + keyUsages + ) { + return originalGenerateKey.call(this, algorithm, true, keyUsages) + } + }) + + await page.goto("/explorer") + await page.getByTestId("sign-in-button").click() + + const signInDialog = await page.getByRole("dialog", { + name: "Connect to Guild", + }) + expect(signInDialog).toBeVisible() + + await page.getByTestId("mock-connector-button").click() + await page.getByTestId("verify-address-button").click() + + const publicKeyResponse = await page + .waitForResponse(`**/v2/users/${TEST_USER.id}/public-key`) + .then((res) => res.json()) + + const storedKeyPairToSave = await page.evaluate( + async ({ userId }) => { + const idb = await new Promise((resolve, reject) => { + const request = indexedDB.open("guild.xyz") + + request.onsuccess = () => resolve(request.result) + request.onerror = () => reject(request.error) + }) + + const keyPair = await new Promise((resolve, reject) => { + const transaction = idb.transaction("signingKeyPairs", "readonly") + const store = transaction.objectStore("signingKeyPairs") + const request = store.get(userId) + + request.onsuccess = () => resolve(request.result) + request.onerror = () => reject(request.error) + }) + + const exportedPrivateKey = await window.crypto.subtle.exportKey( + "jwk", + keyPair.keyPair.privateKey + ) + const exportedPublicKey = await window.crypto.subtle.exportKey( + "jwk", + keyPair.keyPair.publicKey + ) + + return { + pubKey: keyPair.pubKey, + keyPair: { privateKey: exportedPrivateKey, publicKey: exportedPublicKey }, + } + }, + { userId: publicKeyResponse.id } + ) + + try { + fs.mkdirSync(path.join(__dirname, "/.auth")) + // biome-ignore lint/suspicious/noEmptyBlockStatements: we don't need to re-create the directory if it already exists + } catch {} + + fs.writeFileSync( + path.join(__dirname, "/.auth/keyPair.json"), + JSON.stringify(storedKeyPairToSave), + { + flag: "w+", + } + ) + + await page.context().storageState({ path: authFile }) +}) diff --git a/playwright/constants.ts b/playwright/constants.ts new file mode 100644 index 0000000000..40162d10fc --- /dev/null +++ b/playwright/constants.ts @@ -0,0 +1,4 @@ +export const TEST_USER = { + id: 953897, + address: "0x304def656babc745c53782639d3cab00ace8c843", +} as const diff --git a/playwright/dummy.spec.ts b/playwright/dummy.spec.ts new file mode 100644 index 0000000000..780b34b02b --- /dev/null +++ b/playwright/dummy.spec.ts @@ -0,0 +1,12 @@ +import { expect } from "@playwright/test" +import { TEST_USER } from "./constants" +import { test } from "./fixtures" + +test("dummy", async ({ pageWithKeyPair: { page } }) => { + await page.goto("/explorer") + + const accountCard = await page.getByTestId("account-card") + expect(accountCard).toBeVisible() + + await page.waitForResponse(`**/v2/users/${TEST_USER.id}/profile`) +}) diff --git a/playwright/fixtures.ts b/playwright/fixtures.ts new file mode 100644 index 0000000000..0ce74c8bd2 --- /dev/null +++ b/playwright/fixtures.ts @@ -0,0 +1,95 @@ +import { JsonWebKey } from "crypto" +import fs from "fs" +import path from "path" +import { type Page, test as base } from "@playwright/test" +import { StoredKeyPair } from "utils/keyPair" +import { TEST_USER } from "./constants" + +class PageWithKeyPair { + page: Page + + constructor(page: Page) { + this.page = page + } +} + +type Fixtures = { + pageWithKeyPair: PageWithKeyPair +} + +export const test = base.extend({ + pageWithKeyPair: async ({ browser }, use) => { + const context = await browser.newContext({ + storageState: "playwright/.auth/user.json", + }) + const pageWithKeyPair = new PageWithKeyPair(await context.newPage()) + + const keyPairPath = path.join(__dirname, "/.auth/keyPair.json") + const importedKeyPairData: { + pubKey: StoredKeyPair["pubKey"] + keyPair: { + publicKey: JsonWebKey + privateKey: JsonWebKey + } + } = JSON.parse(fs.readFileSync(keyPairPath, "utf8")) + + await pageWithKeyPair.page.addInitScript( + async ({ importedKeyPairData, userId }) => { + const importedPublicKey = await crypto.subtle.importKey( + "jwk", + importedKeyPairData.keyPair.publicKey, + { + name: "ECDSA", + namedCurve: "P-256", + }, + false, + ["verify"] + ) + + const importedPrivateKey = await crypto.subtle.importKey( + "jwk", + importedKeyPairData.keyPair.privateKey, + { + name: "ECDSA", + namedCurve: "P-256", + }, + false, + ["sign"] + ) + + const idb = await new Promise((resolve, reject) => { + const request = indexedDB.open("guild.xyz") + + request.onupgradeneeded = () => { + request.result.createObjectStore("signingKeyPairs") + } + + request.onsuccess = () => resolve(request.result) + request.onerror = () => reject(request.error) + }) + + await new Promise((resolve, reject) => { + const transaction = idb.transaction("signingKeyPairs", "readwrite") + const store = transaction.objectStore("signingKeyPairs") + + const keyPairToSave = { + pubKey: importedKeyPairData.pubKey, + keyPair: { + privateKey: importedPrivateKey, + publicKey: importedPublicKey, + }, + } satisfies StoredKeyPair + + const request = store.put(keyPairToSave, userId) + + request.onsuccess = () => resolve() + request.onerror = () => reject(request.error) + }) + }, + { importedKeyPairData, userId: TEST_USER.id } + ) + + await use(pageWithKeyPair) + await context.close() + }, +}) diff --git a/src/app/explorer/page.tsx b/src/app/explorer/page.tsx index d89bfca089..f05f0ebde5 100644 --- a/src/app/explorer/page.tsx +++ b/src/app/explorer/page.tsx @@ -29,14 +29,14 @@ const Page = async ({ searchParams }: { searchParams: SearchParams }) => { const [ssrFeaturedGuilds, ssrNewestGuilds] = await Promise.all([ fetch(`${env.NEXT_PUBLIC_API.replace("/v1", "")}${featuredPath}`, { next: { - revalidate: 300, + revalidate: 600, }, }) .then((res) => res.json()) .catch((_) => []), fetch(`${env.NEXT_PUBLIC_API.replace("/v1", "")}${newestPath}`, { next: { - revalidate: 300, + revalidate: 600, }, }) .then((res) => res.json()) diff --git a/src/components/[guild]/Requirements/components/GuildCheckout/hooks/useMintGuildPin.tsx b/src/components/[guild]/Requirements/components/GuildCheckout/hooks/useMintGuildPin.tsx index 9166b6fc26..651cb52639 100644 --- a/src/components/[guild]/Requirements/components/GuildCheckout/hooks/useMintGuildPin.tsx +++ b/src/components/[guild]/Requirements/components/GuildCheckout/hooks/useMintGuildPin.tsx @@ -121,14 +121,6 @@ const useMintGuildPin = () => { account: walletClient.account, }) - if (process.env.NEXT_PUBLIC_MOCK_CONNECTOR) { - toastWithTweetButton({ - title: "GUILD_PIN_E2E_TEST_SUCCESS", - tweetText: "", - }) - return Promise.resolve() - } - const hash = await walletClient.writeContract({ ...request, account: walletClient.account, diff --git a/src/components/[guild]/RolePlatforms/components/AddRoleRewardModal/components/AddContractCallPanel/components/CreateNftForm/hooks/useCreateNft.ts b/src/components/[guild]/RolePlatforms/components/AddRoleRewardModal/components/AddContractCallPanel/components/CreateNftForm/hooks/useCreateNft.ts index 833d17408b..c19c66b4c3 100644 --- a/src/components/[guild]/RolePlatforms/components/AddRoleRewardModal/components/AddContractCallPanel/components/CreateNftForm/hooks/useCreateNft.ts +++ b/src/components/[guild]/RolePlatforms/components/AddRoleRewardModal/components/AddContractCallPanel/components/CreateNftForm/hooks/useCreateNft.ts @@ -149,10 +149,6 @@ const useCreateNft = ( args: contractCallParams, }) - if (process.env.NEXT_PUBLIC_MOCK_CONNECTOR) { - return Promise.resolve({} as CreateNFTResponse) - } - const hash = await walletClient.writeContract({ ...request, }) diff --git a/src/components/[guild]/collect/hooks/useCollectNft.ts b/src/components/[guild]/collect/hooks/useCollectNft.ts index 33cb4fb111..75ceb5daa3 100644 --- a/src/components/[guild]/collect/hooks/useCollectNft.ts +++ b/src/components/[guild]/collect/hooks/useCollectNft.ts @@ -155,10 +155,6 @@ const useCollectNft = () => { request = newClaimRequest } - if (process.env.NEXT_PUBLIC_MOCK_CONNECTOR) { - return Promise.resolve({} as TransactionReceipt) - } - const hash = await walletClient.writeContract({ ...request, account: walletClient.account, diff --git a/src/hooks/useSubmitTransaction.ts b/src/hooks/useSubmitTransaction.ts index a43ad3e46e..b68f31ee36 100644 --- a/src/hooks/useSubmitTransaction.ts +++ b/src/hooks/useSubmitTransaction.ts @@ -182,15 +182,6 @@ const useSubmitTransaction = ( return } - if (process.env.NEXT_PUBLIC_MOCK_CONNECTOR) { - setTxHash( - "0x0000000000000000000000000000000000000000000000000000000000000000" - ) - setTxSuccess(true) - onSuccess({} as TransactionReceipt, []) - return - } - // TODO: properly type this writeContract(contractCallConfig as WriteContractParameters) }, diff --git a/src/requirements/Payment/components/RegisterVaultForm/hooks/useRegisterVault.ts b/src/requirements/Payment/components/RegisterVaultForm/hooks/useRegisterVault.ts index dc2935ed6f..11f7a626ca 100644 --- a/src/requirements/Payment/components/RegisterVaultForm/hooks/useRegisterVault.ts +++ b/src/requirements/Payment/components/RegisterVaultForm/hooks/useRegisterVault.ts @@ -65,11 +65,6 @@ const useRegisterVault = ({ }) }, onSuccess: (_, events) => { - if (process.env.NEXT_PUBLIC_MOCK_CONNECTOR) { - onSuccess("0") - return - } - const vaultRegisteredEvent = findEvent< typeof feeCollectorAbi, "VaultRegistered" diff --git a/src/rewards/Token/hooks/useCollectToken.tsx b/src/rewards/Token/hooks/useCollectToken.tsx index e134276bf1..f3873f8ff9 100644 --- a/src/rewards/Token/hooks/useCollectToken.tsx +++ b/src/rewards/Token/hooks/useCollectToken.tsx @@ -103,10 +103,6 @@ const useCollectToken = ( account: walletClient.account, }) - if (process.env.NEXT_PUBLIC_MOCK_CONNECTOR) { - return Promise.resolve({} as TransactionReceipt) - } - const hash = await walletClient.writeContract({ ...request, account: walletClient.account, diff --git a/src/rewards/Token/hooks/useRegisterPool.tsx b/src/rewards/Token/hooks/useRegisterPool.tsx index 5425cc93ee..a505f9859b 100644 --- a/src/rewards/Token/hooks/useRegisterPool.tsx +++ b/src/rewards/Token/hooks/useRegisterPool.tsx @@ -36,10 +36,6 @@ const useRegisterPool = ( console.error(error) }, onSuccess: (_, events) => { - if (process.env.NEXT_PUBLIC_MOCK_CONNECTOR) { - return - } - const poolRegisteredEvent = findEvent< typeof tokenRewardPoolAbi, "PoolRegistered" diff --git a/src/rewards/Token/hooks/useWithdrawPool.tsx b/src/rewards/Token/hooks/useWithdrawPool.tsx index b43dce4c5d..c92b446c9c 100644 --- a/src/rewards/Token/hooks/useWithdrawPool.tsx +++ b/src/rewards/Token/hooks/useWithdrawPool.tsx @@ -29,9 +29,6 @@ const useWithdrawPool = (chain: Chain, poolId: bigint, onSuccess: () => void) => console.error(error) }, onSuccess: () => { - if (process.env.NEXT_PUBLIC_MOCK_CONNECTOR) { - return - } captureEvent("Funds withdrawn from pool", { ...postHogOptions }) onSuccess() }, diff --git a/src/v2/components/Account/Account.tsx b/src/v2/components/Account/Account.tsx index ba32ec5929..26712937d6 100644 --- a/src/v2/components/Account/Account.tsx +++ b/src/v2/components/Account/Account.tsx @@ -31,7 +31,11 @@ export const Account = () => { if (!address) return ( - @@ -39,7 +43,7 @@ export const Account = () => { ) return ( - + diff --git a/src/v2/components/Web3ConnectionManager/WalletSelectorModal/components/ConnectorButton.tsx b/src/v2/components/Web3ConnectionManager/WalletSelectorModal/components/ConnectorButton.tsx index 78e004b47c..cea2495f09 100644 --- a/src/v2/components/Web3ConnectionManager/WalletSelectorModal/components/ConnectorButton.tsx +++ b/src/v2/components/Web3ConnectionManager/WalletSelectorModal/components/ConnectorButton.tsx @@ -52,6 +52,7 @@ const ConnectorButton = ({ connector, pendingConnector, connect, error }: Props) !error } loadingText={`${connectorName} - connecting...`} + data-testid={`${connector.id}-connector-button`} > {connectorIcon ? (
diff --git a/src/v2/hooks/useAutoReconnect.ts b/src/v2/hooks/useAutoReconnect.ts index d0b51116ce..491831e950 100644 --- a/src/v2/hooks/useAutoReconnect.ts +++ b/src/v2/hooks/useAutoReconnect.ts @@ -13,11 +13,14 @@ const useAutoReconnect = () => { let connected = false + let canConnectToSafe = false const safeConnector = connectors.find((connector) => connector.id === "safe") - const canConnectToSafe = await safeConnector - .getProvider() - .then((provider) => !!provider) - .catch(() => false) + if (safeConnector) { + canConnectToSafe = await safeConnector + .getProvider() + .then((provider) => !!provider) + .catch(() => false) + } const recentConnectorId = await config.storage.getItem("recentConnectorId") if (!recentConnectorId && !canConnectToSafe) return diff --git a/src/wagmiConfig/index.ts b/src/wagmiConfig/index.ts index a822c09535..e37f75f992 100644 --- a/src/wagmiConfig/index.ts +++ b/src/wagmiConfig/index.ts @@ -179,7 +179,11 @@ export const wagmiConfig = createConfig({ [blast.id]: http(), [blastSepolia.id]: http(), [oasisSapphire.id]: http(), - [sepolia.id]: http("https://ethereum-sepolia-rpc.publicnode.com"), + [sepolia.id]: http( + process.env.NEXT_PUBLIC_E2E_WALLET_MNEMONIC + ? "http://localhost:8545" + : "https://ethereum-sepolia-rpc.publicnode.com" + ), [astarZkEVM.id]: http(), [coreDao.id]: http(), [liskSepolia.id]: http(), @@ -191,40 +195,48 @@ export const wagmiConfig = createConfig({ [mint.id]: http(), }, ssr: true, - connectors: process.env.NEXT_PUBLIC_MOCK_CONNECTOR - ? [ - mock({ - accounts: [mnemonicToAccount(process.env.NEXT_PUBLIC_E2E_WALLET_MNEMONIC)], - }), - ] - : [ - injected(), - coinbaseWallet({ - appName: "Guild.xyz", - appLogoUrl: "https://guild.xyz/guild-icon.png", - version: "4", - }), - walletConnect({ - projectId: env.NEXT_PUBLIC_WALLETCONNECT_PROJECT_ID, - showQrModal: true, - qrModalOptions: { - explorerRecommendedWalletIds: [ - "971e689d0a5be527bac79629b4ee9b925e82208e5168b733496a09c0faed0709", // OKX - "107bb20463699c4e614d3a2fb7b961e66f48774cb8f6d6c1aee789853280972c", // Bitcoin.com - "541d5dcd4ede02f3afaf75bf8e3e4c4f1fb09edb5fa6c4377ebf31c2785d9adf", // Ronin - "4622a2b2d6af1c9844944291e5e7351a6aa24cd7b23099efac1b2fd875da31a0", // Trust + connectors: + typeof navigator !== "undefined" && + navigator.userAgent.includes("GUILD_E2E") && + process.env.NEXT_PUBLIC_E2E_WALLET_MNEMONIC + ? [ + mock({ + accounts: [ + mnemonicToAccount(process.env.NEXT_PUBLIC_E2E_WALLET_MNEMONIC), ], - themeVariables: { - "--wcm-z-index": "10001", - "--w3m-z-index": "10001", - } as any, // casting it, so `--wcm-z-index` is accepted - }, - }), - safe({ - allowedDomains: [/gnosis-safe\.io$/, /app\.safe\.global$/], - debug: false, - }), - ], + features: { + reconnect: true, + }, + }), + ] + : [ + injected(), + coinbaseWallet({ + appName: "Guild.xyz", + appLogoUrl: "https://guild.xyz/guild-icon.png", + version: "4", + }), + walletConnect({ + projectId: env.NEXT_PUBLIC_WALLETCONNECT_PROJECT_ID, + showQrModal: true, + qrModalOptions: { + explorerRecommendedWalletIds: [ + "971e689d0a5be527bac79629b4ee9b925e82208e5168b733496a09c0faed0709", // OKX + "107bb20463699c4e614d3a2fb7b961e66f48774cb8f6d6c1aee789853280972c", // Bitcoin.com + "541d5dcd4ede02f3afaf75bf8e3e4c4f1fb09edb5fa6c4377ebf31c2785d9adf", // Ronin + "4622a2b2d6af1c9844944291e5e7351a6aa24cd7b23099efac1b2fd875da31a0", // Trust + ], + themeVariables: { + "--wcm-z-index": "10001", + "--w3m-z-index": "10001", + } as any, // casting it, so `--wcm-z-index` is accepted + }, + }), + safe({ + allowedDomains: [/gnosis-safe\.io$/, /app\.safe\.global$/], + debug: false, + }), + ], }) export const COINBASE_INJECTED_WALLET_ID = "com.coinbase.wallet" diff --git a/src/wagmiConfig/mockConnector.ts b/src/wagmiConfig/mockConnector.ts index 171d5b0f97..c6f2080473 100644 --- a/src/wagmiConfig/mockConnector.ts +++ b/src/wagmiConfig/mockConnector.ts @@ -64,19 +64,21 @@ export function mock( let currentChainId = await this.getChainId() if (chainId && currentChainId !== chainId) { - const chain = await this.switchChain({ chainId }) - currentChainId = chain.id + const chain = await this.switchChain?.({ chainId }) + + if (chain) currentChainId = chain.id } connected = true + await config.storage?.setItem("mock.connected", true) return { accounts, chainId: currentChainId } }, async disconnect() { + await config.storage?.removeItem("mock.connected") connected = false }, async getAccounts() { - if (!connected) throw new Error("Connector is not connected") const provider = await this.getProvider() const accounts = await provider.request({ method: "eth_accounts" }) return accounts.map((x) => getAddress(x)) @@ -88,7 +90,10 @@ export function mock( }, async isAuthorized() { if (!features.reconnect) return false - if (!connected) return false + + const connectedLocal = await config.storage?.getItem("mock.connected") + if (!connectedLocal) return false + const accounts = await this.getAccounts() return !!accounts.length }, @@ -180,10 +185,10 @@ export function mock( return custom({ request })({ retryCount: 0 }) }, - async getClient({ chainId }) { + async getClient({ chainId } = {}) { const client = createWalletClient({ transport: http( - config.chains.find((c) => c.id === chainId).rpcUrls.default.http[0] + config.chains.find((c) => c.id === chainId)?.rpcUrls.default.http[0] ), account: parameters.accounts[0], }) as Client diff --git a/tsconfig.json b/tsconfig.json index d247ed23f5..9a5bd0c77f 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -32,11 +32,11 @@ "./src/**/*.ts", "./src/**/*.tsx", "api/**/*.ts", - "cypress.config.ts", - "cypress/**/*.ts", ".next/types/**/*.ts", "tailwind.config.ts", - "scripts/detectCircularDependencies.ts" + "scripts/detectCircularDependencies.ts", + "playwright.config.ts", + "playwright/**/*.ts" ], "exclude": ["node_modules", ".next", "out"] }