Writing MetaMask Tests with Synpress
A detailed guide on how to write MetaMask tests with Synpress, the challenges I encountered, and the solutions I found to overcome them.
When you want to write tests for your Web3 application, Synpress can be one of the most logical options. I want to share my experiences and the problems I encountered with this library, which was developed as a Cypress plugin. This article is written for readers who have basic knowledge of Web3 testing and want to learn more about this topic.
Installation
You can use the following commands to use Synpress in your project:
npm install --save-dev [email protected]
npm install --save-dev [email protected]
Note: The reason I use version 3.7.1 of Synpress is that newer versions are in beta and haven't been working stably. The reason I use Cypress version 12.17.3 is related to this issue.
An example run script for package.json is as follows:
"scripts": {
"synpress:run": "env-cmd -f .env.testing synpress run --config='fixturesFolder=tests/e2e/fixtures'"
}
Here we define a custom env file and config.
Project structure:
project_dir
└── src
└── tests
└── e2e
└── .eslintrc.js
└── support.js
└── tsconfig.json
└── specs
└── example-spec.js
└── pages
└── example-page.js
- Create the
.eslintrc.jsfile under the (/project_dir/tests/e2e) folder:
const path = require("path");
const synpressPath = path.join(
process.cwd(),
"/node_modules/@synthetixio/synpress"
);
module.exports = {
extends: `${synpressPath}/.eslintrc.js`,
};
- Create the
support.jsfile under the (/project_dir/tests/e2e) folder:
import "@synthetixio/synpress/support/index";
- Create the
tsconfig.jsonfile under the (/project_dir/tests/e2e) folder:
{
"compilerOptions": {
"allowJs": true,
"baseUrl": "../../node_modules",
"types": [
"cypress",
"@synthetixio/synpress/support",
"cypress-wait-until",
"@testing-library/cypress"
],
"outDir": "./output"
},
"include": ["**/*.*"]
}
After completing the above steps, you'll need an example env file:
NETWORK_NAME="HardhatNetwork"
RPC_URL=http://127.0.0.1:8545/
CHAIN_ID=31337
SYMBOL=ETH
PRIVATE_KEY="0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80"
PASSWORD="your_password"
In this example using .env.testing, I'm setting up a blockchain network with hardhat. Therefore, you need to change the NETWORK_NAME, RPC_URL, CHAIN_ID, SYMBOL, PASSWORD, and (PRIVATE_KEY or SECRET_WORDS) values according to your network values. We use this .env.testing file with env-cmd in the example script under package.json.
Usage
You can write your tests under the specs folder. An example test file is as follows:
describe("Wallet", () => {
it("connect wallet", () => {
cy.visit("/wallet");
cy.get("h1").should("contain", "Wallet");
cy.get("button").contains("Connect").click();
cy.acceptMetamaskAccess();
cy.contains("p.text-body", "Connected address");
cy.contains("p.address", cy.getMetamaskAddress());
});
});
Let's go through this example test. Before this test starts, you can think of it as having a before section where the cy.setupMetamask() function runs with the information defined in env, and definitions are made by opening the metamask wallet before the test starts. If you get an error at this stage, you can assume there's a configuration-specific error.
In the example, we go to the wallet path and check that "Wallet" is written in the h1 tag. Then we click the Connect button. At this stage, a metamask access request comes and we accept it with the cy.acceptMetamaskAccess() function. Then we check that "Connected address" is written in p.text-body. Finally, we check that our metamask address is written in p.address.
In these tests, you can use synpress APIs in addition to the cypress API.
Contrary to the Synpress documentation, you may experience problems with the given examples not working due to env issues. I recommend paying attention to these. Especially, I recommend not using METAMASK_VERSION=latest because this plugin is written for a specific metamask version and won't work with later versions.
I couldn't run synpress in --headless mode and got wallet-specific errors. Also in synpress open, the metamask wallet gets stuck and doesn't open during tests. Then it gives a message asking if I want to restart the wallet. I couldn't overcome these either.
I managed ENV management through the env-cmd package as I wrote above; I wasn't successful with others.
I had to add cy.wait(5000) in cy.confirmMetamaskPermissionToSpend() functions. Otherwise, there were cases where the time wasn't sufficient. This was the case even with my local hardhat network; I don't know the situation with the testnets you'll use, but I recommend looking into these.
When you want to import a second account, you may encounter an autoconnect problem. I found a hacky solution like the following to solve this.
describe("projects", () => {
it("change account", () => {
cy.visit("/wallet");
cy.disconnectMetamaskWalletFromAllDapps();
cy.fixture("user2.json").then((user) => {
cy.importMetamaskAccount(user.privateKey);
cy.switchMetamaskAccount(3);
});
cy.resetMetamaskAccount();
cy.get("button").contains("Connect").click();
cy.acceptMetamaskAccess({ allAccounts: true });
cy.contains("p.text-body1", "Connected address");
});
});
Conclusion
It's possible to write metamask tests with Synpress. However, as far as I've experienced, I encountered many problems. While solving these, I spent more time than I initially planned. By reading this article, you can solve problems you might encounter at the beginning to avoid experiencing similar issues.