synpress icon indicating copy to clipboard operation
synpress copied to clipboard

[πŸ’‘ Feature]: Synpress with fully parallel run with metamask windows

Open itev4n7 opened this issue 2 years ago β€’ 4 comments

Is your feature request related to a problem?

πŸ“¦ The problem Due to hardcoded line await browser.contexts()[0].pages(); in assignWindows function that placed in commands/playwright.js you not able to run tests with metamask in parallel mode, because context always assigned to the first context during tests run, and all operations with metamask using this first instance.

Describe the solution you'd like.

πŸ’‘ Idea already implemented and in personal use @noahlitvin @maxhoheiser @r3kt-eth @drptbl @neuodev

πŸ“ƒ Solution (Game changer) The main idea to set through initialSetup and init functions the context you want to run each matamask separately.

The code follow backward compatibility (e.g previous usage of updated methods is not ruin a code) πŸ“ Implementation:

πŸ“‹ commands/metamask.js

...
  //"context" parameter added by Oleksii_Kvasov
  async initialSetup(
    playwrightInstance,
    context,
    {
      secretWordsOrPrivateKey,
      network,
      password,
      enableAdvancedSettings,
      enableExperimentalSettings,
    },
  ) {
    //"context" as a parameter added into "init" by Oleksii_Kvasov
    if (playwrightInstance) {
      await playwright.init(playwrightInstance, context);
    } else {
      await playwright.init(undefined, context);
    }
...

πŸ“‹commands/playwright.js

...
  //"playwrightContext" (game changer for parralel run) added by Oleksii_Kvasov
  let playwrightContext;
  let retries = 0;
  module.exports = {
...
  //"context" parameter added by Oleksii_Kvasov
  async init(playwrightInstance, context) {
    ...
    //context initializing added by Oleksii_Kvasov
    if(context) {
      playwrightContext = context;
    } else {
      playwrightContext = browser.contexts()[0] //default first context
    }
    return browser.isConnected();
  },
...
  async assignWindows( ) {
    //context pages changed added by Oleksii_Kvasov
    let pages = playwrightContext.pages();
...
  async switchToMetamaskNotification() {
    //context pages changed added by Oleksii_Kvasov
    let pages = playwrightContext.pages();
...

In case if new functions will be added - and another functions that use context for navigation.

Also need to update cypress functions in synpress to use it with cypress commands

Example of use the implemented idea:

  • You need to run 5 different tests with different metamask wallets in parallel
  • After updating commands that described above
  • Add fullyParallel: true into playwright config file
  • Run your tests with parallel logic
  • You will have 5 different contexts(browser instances) and 5 different metamask wallets that will run parallel
  • Green checkmark for passed tests

❗Note: if you adding new network into metamask make sure that for each parallel wallet you have unique rpc url, either you will get 429 error in metamask.

Please make this post more popular, then I will create PR with this updates Thanks in advance!

Just to simplify search in google: Parallelisation for synpress using playwright Parallel run of metamask in synpress

Describe alternatives you've considered.

No response

Additional context

No response

itev4n7 avatar Aug 15 '23 19:08 itev4n7

Please guys make this post more popular, then I will create PR with this updates Thanks in advance!

itev4n7 avatar Aug 15 '23 19:08 itev4n7

I've created simple test example for public demonstration:

import { test } from '../src/common/fixtures'
import { initialSetup, acceptAccess } from '@synthetixio/synpress/commands/metamask'
import { Browser, BrowserContext } from '@playwright/test'
import * as playwright from '@synthetixio/synpress/commands/playwright'
import { generateMnemonic } from '@e2eUtils'

test.describe('Connect wallet opensea', function () {
  async function metamaskSetupWithContext(context: BrowserContext, browser: Browser, rpc: string) {
    await context.pages()[0].waitForTimeout(3000)
    const network = {
      networkName: process.env.NETWORK_NAME,
      rpcUrl: rpc,
      chainId: process.env.CHAIN_ID,
      symbol: process.env.SYMBOL,
      isTestnet: process.env.IS_TESTNET,
    }
    const playwrightInstance = browser.browserType()
    const mnemonic = generateMnemonic()
    await initialSetup(playwrightInstance, context, {
      secretWordsOrPrivateKey: mnemonic,
      network,
      password: process.env.METAMASK_PASSWORD,
      enableAdvancedSettings: true,
      enableExperimentalSettings: false,
    })
    await playwright.waitUntilStable()
  }

  test.beforeEach(async function({ context, browser }, testInfo) {
    if (testInfo.title.includes("1")) {
      const rpc = "<rpc_url1>"
      await metamaskSetupWithContext(context, browser, rpc)
    } else {
      const rpc = "<rpc_url2>"
      await metamaskSetupWithContext(context, browser, rpc)
    }
  })

  test('test 1', async function({ page, accountMenuPage, context, browser }) {
    await page.goto('https://testnets.opensea.io/')
    await page.locator('//*[@type="button" and contains(.,"Connect wallet")]').click()
    await page.locator('//li/button[contains(.,"MetaMask")]').click()
    //await page.locator('//button[contains(.,"Accept and sign")]').click()
    await acceptAccess(
      {
        signInSignature: true,
      },
    )
  })
  test('test 2', async function({ page, accountMenuPage, context, browser }) {
    await page.goto('https://testnets.opensea.io/')
    await page.locator('//*[@type="button" and contains(.,"Connect wallet")]').click()
    await page.locator('//li/button[contains(.,"MetaMask")]').click()
    //await page.locator('//button[contains(.,"Accept and sign")]').click()
    await acceptAccess(
      {
        signInSignature: true,
      },
    )
  })
})

each test will fail on "Access denied" error from opensea after acceptAccess, but in parallel mode :)

itev4n7 avatar Aug 15 '23 19:08 itev4n7

Great work @itev4n7! Would love to see this as a pull request. Cheers!

drptbl avatar Aug 15 '23 23:08 drptbl

@drptbl I will create it soon :)

itev4n7 avatar Aug 16 '23 15:08 itev4n7