diff --git a/.github/workflows/playwright.yaml b/.github/workflows/playwright.yaml index b0f110a6..cda3d60e 100644 --- a/.github/workflows/playwright.yaml +++ b/.github/workflows/playwright.yaml @@ -10,7 +10,8 @@ jobs: strategy: matrix: tutorial: - - "tests/how-to-test-contracts.spec.ts" + - "tests/erc20-paymaster.spec.ts" + - "tests/how-to-test-contracts.spec.ts" steps: - uses: actions/checkout@v4 @@ -21,4 +22,9 @@ jobs: - name: Install Playwright Browsers run: bun playwright install chromium --with-deps - name: Run test for ${{ matrix.tutorial }} - run: bun test:github ${{ matrix.tutorial }} + run: | + export TERM=xterm-256color + export COLUMNS=80 + export LINES=24 + script -q -c "bun test:github ${{ matrix.tutorial }}" + diff --git a/bun.lockb b/bun.lockb index 192d1627..c16d1f9b 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/components/TestAction.vue b/components/TestAction.vue new file mode 100644 index 00000000..46e610aa --- /dev/null +++ b/components/TestAction.vue @@ -0,0 +1,12 @@ + + + diff --git a/content/tutorials/erc20-paymaster/10.index.md b/content/tutorials/erc20-paymaster/10.index.md index 5b2aa52c..11ba1af4 100644 --- a/content/tutorials/erc20-paymaster/10.index.md +++ b/content/tutorials/erc20-paymaster/10.index.md @@ -40,19 +40,26 @@ Atlas is a smart contract IDE that lets you write, deploy, and interact with con ## Setup the Project -1. Initiate a new project by running the command: +First, initialize a new project by running the command: - ```sh - npx zksync-cli create custom-paymaster-tutorial --template hardhat_solidity - ``` +:test-action{actionId="initialize-hardhat-project"} - This creates a new ZKsync Era project called `custom-paymaster-tutorial` with a basic `Greeter` contract. +```sh +npx zksync-cli create custom-paymaster-tutorial --template hardhat_solidity +``` -1. Navigate into the project directory: +This creates a new ZKsync Era project called `custom-paymaster-tutorial` with a basic `Greeter` contract. - ```sh - cd custom-paymaster-tutorial - ``` +Next, navigate into the project directory and install the dependencies: + +:test-action{actionId="wait-for-init"} +:test-action{actionId="npm-install"} + +```sh +cd custom-paymaster-tutorial && npm install --force +``` + +:test-action{actionId="wait-for-install"} ::callout{icon="i-heroicons-exclamation-circle"} The template project includes multiple example contracts. Feel free to delete them. @@ -213,7 +220,17 @@ _first_ check that the user provided enough allowance before calling `transferFr ## Paymaster Contract Full Code -Create the `contracts/MyPaymaster.sol` file and copy/paste the following: +Create the `contracts/MyPaymaster.sol` file with + +:test-action{actionId="create-contract-file"} + +```sh +touch contracts/MyPaymaster.sol +``` + +and copy/paste the following: + +:test-action{actionId="add-paymaster-contract"} ```solidity // SPDX-License-Identifier: MIT @@ -338,10 +355,18 @@ contract MyPaymaster is IPaymaster { ## Create ERC20 Contract -For the sake of simplicity we will use a modified OpenZeppelin ERC20 implementation: +For the sake of simplicity we will use a modified OpenZeppelin ERC20 implementation. + +:test-action{actionId="create-erc20-contract-file"} + +```sh +touch contracts/MyERC20.sol +``` Create the `contracts/MyERC20.sol` file and copy/paste the following: +:test-action{actionId="add-erc20-contract"} + ```solidity [contracts/MyERC20.sol] // SPDX-License-Identifier: UNLICENSED @@ -378,7 +403,17 @@ It also mints some `MyERC20` tokens into the account we use to deploy the contra In addition, the script sends `0.06ETH` to the paymaster contract so it can pay the transaction fees we send later on. 1. In the `deploy` folder, create the file `deploy-paymaster.ts` and copy/paste the following. - Make sure the private key of the account used to deploy the contracts is configured in the `.env` file of the project.: + + :test-action{actionId="create-deploy-file"} + + ```sh + touch deploy/deploy-paymaster.ts + ``` + + Make sure the private key of the account used to deploy the contracts is configured in the `.env` file of the project. + + :test-action{actionId="add-testing-private-key"} + :test-action{actionId="add-deploy-script"} ```ts [deploy-paymaster.ts] import { deployContract, getWallet, getProvider } from "./utils"; @@ -416,16 +451,41 @@ In addition, the script sends `0.06ETH` to the paymaster contract so it can pay 1. Compile and the contracts from the project root: - ```sh + :test-action{actionId="create-ts-config"} + :test-action{actionId="compile-contracts"} + + ::code-group + + ```bash [npx] + npx hardhat compile + ``` + + ```bash [yarn] yarn hardhat compile ``` + :: + 1. Execute the deployment script: - ```sh + :test-action{actionId="use-local-network"} + :test-action{actionId="start-local-network"} + :test-action{actionId="wait-for-hh-node"} + :test-action{actionId="temp-fix-import"} + :test-action{actionId="deploy-contracts"} + + ::code-group + + ```bash [npx] + npx hardhat deploy-zksync --script deploy-paymaster.ts + ``` + + ```bash [yarn] yarn hardhat deploy-zksync --script deploy-paymaster.ts ``` + :: + The output should be roughly the following: ```sh @@ -461,10 +521,18 @@ Make sure you delete the `artifacts-zk` and `cache-zk` folders before recompilin 1. Create the `use-paymaster.ts` script in the `deploy` folder, replacing the parameter placeholders with the details from the previous deploy step. + :test-action{actionId="create-deploy-paymaster-file"} + + ```sh + touch deploy/use-paymaster.ts + ``` + ::callout{icon="i-heroicons-exclamation-triangle"} Make sure you use the private key of the wallet created by the previous script as that wallet contains the ERC20 tokens. :: + :test-action{actionId="add-use-paymaster"} + ```ts [deploy/use-paymaster.ts] import { utils, Wallet } from "zksync-ethers"; import { getWallet, getProvider } from "./utils"; @@ -534,12 +602,25 @@ Make sure you delete the `artifacts-zk` and `cache-zk` folders before recompilin } ``` + :test-action{actionId="paymaster-address"} + :test-action{actionId="token-address"} + 1. Run the script: - ```sh + :test-action{actionId="run-use-paymaster"} + + ::code-group + + ```bash [npx] + npx hardhat deploy-zksync --script use-paymaster.ts + ``` + + ```bash [yarn] yarn hardhat deploy-zksync --script use-paymaster.ts ``` + :: + The output should look something like this: ```txt diff --git a/content/tutorials/how-to-test-contracts/10.index.md b/content/tutorials/how-to-test-contracts/10.index.md index 85f8c8a0..b415a6db 100644 --- a/content/tutorials/how-to-test-contracts/10.index.md +++ b/content/tutorials/how-to-test-contracts/10.index.md @@ -26,7 +26,7 @@ During the alpha phase, ZKsync Era Test Nodes are currently undergoing developme First, initialize a new Hardhat TypeScript project: - +:test-action{actionId="initialize-hardhat-project"} ```bash npx hardhat init @@ -36,7 +36,7 @@ Select the `Create a TypeScript project` option and install the sample project's To install the `hardhat-zksync` plugin, execute the following command: - +:test-action{actionId="install-hh-zksync"} ::code-group @@ -52,8 +52,7 @@ yarn add -D @matterlabs/hardhat-zksync Once installed, add the plugin at the top of your `hardhat.config.ts` file. - +:test-action{actionId="import-zksync-config"} ```ts [hardhat.config.ts] import "@matterlabs/hardhat-zksync"; @@ -63,7 +62,7 @@ import "@matterlabs/hardhat-zksync"; You can now safely run the **ZKsync Era Test Node** with the following command: - +:test-action{actionId="run-hh-node"} ::code-group @@ -77,9 +76,8 @@ yarn hardhat node-zksync :: - - - +:test-action{actionId="wait-for-hh-node"} +:test-action{actionId="test-hh-node"} ::callout{icon="i-heroicons-exclamation-circle"} We'll want to verify the correctness of our installations and test if we can run a **ZKsync Era Test Node**, @@ -107,8 +105,7 @@ To enable the usage of ZKsync Era Test Node in Hardhat, add the `zksync:true` option to the hardhat network in the `hardhat.config.ts` file and the `latest` version of `zksolc`: - +:test-action{actionId="zksync-hh-network"} ```ts zksolc: { @@ -128,7 +125,7 @@ it's necessary to use the `hardhat-chai-matchers` plugin. In the root directory of your project, execute this command: - +:test-action{actionId="install-chai-ethers"} ::code-group @@ -144,8 +141,7 @@ yarn add -D @nomicfoundation/hardhat-chai-matchers chai@4.3.6 After installing it, add the plugin at the top of your `hardhat.config.ts` file: - +:test-action{actionId="import-chai-matchers"} ```ts [hardhat.config.ts] import "@nomicfoundation/hardhat-chai-matchers"; @@ -153,7 +149,7 @@ import "@nomicfoundation/hardhat-chai-matchers"; With the previous steps completed, your `hardhat.config.ts` file should now be properly configured to include settings for local testing. - +:test-action{actionId="compare-config"} ```ts [hardhat.config.ts] import { HardhatUserConfig } from "hardhat/config"; @@ -182,7 +178,7 @@ To set up the environment for using chai matchers and writing tests, you'll need Inside the **contracts** folder, rename the example contract file to **Greeter.sol**. - +:test-action{actionId="rename-greeter-file"} ```bash mv contracts/Lock.sol contracts/Greeter.sol @@ -190,7 +186,7 @@ mv contracts/Lock.sol contracts/Greeter.sol Now replace the example contract in **Greeter.sol** with the new `Greeter` contract below: - +:test-action{actionId="create-greeter-contract"} ```solidity [Greeter.sol] // SPDX-License-Identifier: MIT @@ -223,7 +219,7 @@ Now you can create a test with the `hardhat-chai-matchers` plugin: Inside the `/test` folder, rename the example test file to `test.ts`. - +:test-action{actionId="rename-test-file"} ```bash mv test/Lock.ts test/test.ts @@ -231,7 +227,7 @@ mv test/Lock.ts test/test.ts Replace the old test with this example showcasing the functionalities of the contract: - +:test-action{actionId="create-test"} ```typescript import * as hre from "hardhat"; @@ -284,7 +280,7 @@ describe("Greeter", function () { Execute the following command in your terminal to run the tests: - +:test-action{actionId="run-test"} ::code-group diff --git a/nuxt.config.ts b/nuxt.config.ts index 90a91af5..61746ad1 100644 --- a/nuxt.config.ts +++ b/nuxt.config.ts @@ -11,6 +11,12 @@ export default defineNuxtConfig({ app: 'code', }, }, + components: [ + { + path: '~/components', + global: true, + }, + ], content: { navigation: { fields: [ diff --git a/package.json b/package.json index fe0d0fa2..0d1a40d7 100644 --- a/package.json +++ b/package.json @@ -50,6 +50,7 @@ "lint-staged": "^15.2.4", "markdownlint": "^0.34.0", "markdownlint-cli2": "^0.13.0", + "node-pty": "^1.0.0", "pm2": "^5.4.2", "prettier": "^3.2.5", "prettier-eslint": "^16.3.0", diff --git a/tests/configs/config.ts b/tests/configs/config.ts new file mode 100644 index 00000000..420c6973 --- /dev/null +++ b/tests/configs/config.ts @@ -0,0 +1,18 @@ +import { steps as erc20PaymasterSteps } from './erc20-paymaster'; +import { steps as howToTestContractsSteps } from './how-to-test-contracts'; + +export function getConfig(tutorialName: string) { + let steps; + switch (tutorialName) { + case 'erc20-paymaster': + steps = erc20PaymasterSteps; + break; + case 'how-to-test-contracts': + steps = howToTestContractsSteps; + break; + default: + break; + } + + return steps; +} diff --git a/tests/configs/erc20-paymaster.ts b/tests/configs/erc20-paymaster.ts new file mode 100644 index 00000000..f070a873 --- /dev/null +++ b/tests/configs/erc20-paymaster.ts @@ -0,0 +1,116 @@ +import type { IStepConfig } from '../utils/types'; + +export const steps: IStepConfig = { + 'initialize-hardhat-project': { + action: 'runCommand', + prompts: 'Private key of the wallet:0x7726827caac94a7f9e1b160f7ea819f172f7b6f9d2a97f992c38edeab82d4110|npm: ', + }, + 'wait-for-init': { + action: 'wait', + timeout: 5000, + }, + 'npm-install': { + action: 'runCommand', + }, + 'wait-for-install': { + action: 'wait', + timeout: 5000, + }, + 'create-contract-file': { + action: 'runCommand', + commandFolder: 'tests-output/custom-paymaster-tutorial', + }, + 'add-paymaster-contract': { + action: 'writeToFile', + filepath: 'tests-output/custom-paymaster-tutorial/contracts/MyPaymaster.sol', + }, + 'create-erc20-contract-file': { + action: 'runCommand', + commandFolder: 'tests-output/custom-paymaster-tutorial', + }, + 'add-erc20-contract': { + action: 'writeToFile', + filepath: 'tests-output/custom-paymaster-tutorial/contracts/MyERC20.sol', + }, + 'create-deploy-file': { + action: 'runCommand', + commandFolder: 'tests-output/custom-paymaster-tutorial', + }, + 'add-testing-private-key': { + action: 'modifyFile', + filepath: 'tests-output/custom-paymaster-tutorial/.env', + atLine: 1, + removeLines: [1], + useSetData: 'WALLET_PRIVATE_KEY=0x7726827caac94a7f9e1b160f7ea819f172f7b6f9d2a97f992c38edeab82d4110', + }, + 'add-deploy-script': { + action: 'writeToFile', + filepath: 'tests-output/custom-paymaster-tutorial/deploy/deploy-paymaster.ts', + }, + 'create-ts-config': { + action: 'runCommand', + commandFolder: 'tests-output/custom-paymaster-tutorial', + useSetCommand: 'touch tsconfig.json', + }, + 'compile-contracts': { + action: 'runCommand', + commandFolder: 'tests-output/custom-paymaster-tutorial', + }, + 'use-local-network': { + action: 'modifyFile', + filepath: 'tests-output/custom-paymaster-tutorial/hardhat.config.ts', + atLine: 6, + removeLines: [6], + useSetData: ' defaultNetwork: "inMemoryNode",', + }, + 'start-local-network': { + action: 'runCommand', + commandFolder: 'tests-output/custom-paymaster-tutorial', + useSetCommand: "bun pm2 start 'npx hardhat node-zksync' --name hh-zknode", + }, + 'wait-for-hh-node': { + action: 'wait', + timeout: 7000, + }, + 'temp-fix-import': { + action: 'modifyFile', + filepath: 'tests-output/custom-paymaster-tutorial/deploy/utils.ts', + atLine: 4, + removeLines: [4], + useSetData: "import * as dotenv from 'dotenv';", + }, + 'deploy-contracts': { + action: 'runCommand', + commandFolder: 'tests-output/custom-paymaster-tutorial', + }, + 'create-deploy-paymaster-file': { + action: 'runCommand', + commandFolder: 'tests-output/custom-paymaster-tutorial', + }, + 'add-use-paymaster': { + action: 'writeToFile', + filepath: 'tests-output/custom-paymaster-tutorial/deploy/use-paymaster.ts', + }, + 'paymaster-address': { + action: 'modifyFile', + filepath: 'tests-output/custom-paymaster-tutorial/deploy/use-paymaster.ts', + atLine: 7, + removeLines: [7], + getContractId: + 'tests-output/custom-paymaster-tutorial/deployments-zk/inMemoryNode/contracts/MyPaymaster.sol/MyPaymaster.json', + useSetData: "const PAYMASTER_ADDRESS = '<*GET_CONTRACT_ID*>';", + }, + 'token-address': { + action: 'modifyFile', + filepath: 'tests-output/custom-paymaster-tutorial/deploy/use-paymaster.ts', + atLine: 10, + removeLines: [10], + getContractId: + 'tests-output/custom-paymaster-tutorial/deployments-zk/inMemoryNode/contracts/MyERC20.sol/MyERC20.json', + useSetData: "const TOKEN_ADDRESS = '<*GET_CONTRACT_ID*>';", + }, + 'run-use-paymaster': { + action: 'runCommand', + commandFolder: 'tests-output/custom-paymaster-tutorial', + }, +}; diff --git a/tests/configs/how-to-test-contracts.ts b/tests/configs/how-to-test-contracts.ts new file mode 100644 index 00000000..d36c1c28 --- /dev/null +++ b/tests/configs/how-to-test-contracts.ts @@ -0,0 +1,68 @@ +import type { IStepConfig } from '../utils/types'; + +export const steps: IStepConfig = { + 'initialize-hardhat-project': { + action: 'runCommand', + }, + 'install-hh-zksync': { + action: 'runCommand', + commandFolder: 'tests-output/hardhat-project', + }, + 'import-zksync-config': { + action: 'modifyFile', + filepath: 'tests-output/hardhat-project/hardhat.config.ts', + atLine: 3, + }, + 'run-hh-node': { + action: 'runCommand', + commandFolder: 'tests-output/hardhat-project', + preCommand: "bun pm2 start '' --name hh-zknode", + }, + 'wait-for-hh-node': { + action: 'wait', + timeout: 7000, + }, + 'test-hh-node': { + action: 'checkIfBalanceIsZero', + networkUrl: 'http://127.0.0.1:8011', + address: '0xe2b8Cb53a43a56d4d2AB6131C81Bd76B86D3AFe5', + }, + 'zksync-hh-network': { + action: 'modifyFile', + filepath: 'tests-output/hardhat-project/hardhat.config.ts', + atLine: 7, + }, + 'install-chai-ethers': { + action: 'runCommand', + commandFolder: 'tests-output/hardhat-project', + }, + 'import-chai-matchers': { + action: 'modifyFile', + filepath: 'tests-output/hardhat-project/hardhat.config.ts', + atLine: 4, + }, + 'compare-config': { + action: 'compareToFile', + filepath: 'tests-output/hardhat-project/hardhat.config.ts', + }, + 'rename-greeter-file': { + action: 'runCommand', + commandFolder: 'tests-output/hardhat-project', + }, + 'create-greeter-contract': { + action: 'writeToFile', + filepath: 'tests-output/hardhat-project/contracts/Greeter.sol', + }, + 'rename-test-file': { + action: 'runCommand', + commandFolder: 'tests-output/hardhat-project', + }, + 'create-test': { + action: 'writeToFile', + filepath: 'tests-output/hardhat-project/test/test.ts', + }, + 'run-test': { + action: 'runCommand', + commandFolder: 'tests-output/hardhat-project', + }, +}; diff --git a/tests/erc20-paymaster.spec.ts b/tests/erc20-paymaster.spec.ts new file mode 100644 index 00000000..358c2d79 --- /dev/null +++ b/tests/erc20-paymaster.spec.ts @@ -0,0 +1,12 @@ +import { test } from '@playwright/test'; +import { setupAndRunTest } from './utils/runTest'; + +test('Build an ERC20 custom paymaster', async ({ page, context }) => { + await setupAndRunTest( + page, + context, + 'custom-paymaster-tutorial', + ['http://localhost:3000/tutorials/erc20-paymaster'], + 'erc20-paymaster' + ); +}); diff --git a/tests/how-to-test-contracts.spec.ts b/tests/how-to-test-contracts.spec.ts index 94e29447..262b0086 100644 --- a/tests/how-to-test-contracts.spec.ts +++ b/tests/how-to-test-contracts.spec.ts @@ -1,16 +1,12 @@ import { test } from '@playwright/test'; -import { setupFolders, stopServers, startLocalServer } from './utils/setup'; -import { runTest } from './utils/runTest'; +import { setupAndRunTest } from './utils/runTest'; -test('how-to-test-contracts-with-hardhat', async ({ page, context }) => { - // SETUP - await startLocalServer(page); - await context.grantPermissions(['clipboard-read', 'clipboard-write']); - await setupFolders('hardhat-test-example'); - - // TEST - await runTest(page, 'http://localhost:3000/tutorials/how-to-test-contracts'); - - // SHUT DOWN ANY RUNNING PROJECTS - stopServers(); +test('How to test smart contracts with Hardhat', async ({ page, context }) => { + await setupAndRunTest( + page, + context, + 'hardhat-project', + ['http://localhost:3000/tutorials/how-to-test-contracts'], + 'how-to-test-contracts' + ); }); diff --git a/tests/utils/files.ts b/tests/utils/files.ts index c4be9197..c22b96aa 100644 --- a/tests/utils/files.ts +++ b/tests/utils/files.ts @@ -15,10 +15,21 @@ export async function modifyFile( addSpacesBefore?: number, addSpacesAfter?: number, atLine?: number, - removeLines?: string, - useSetData?: string + removeLines?: number[], + useSetData?: string, + deploymentFilePath?: string ) { let contentText = useSetData; + + if (deploymentFilePath) { + const contractId = getContractId(deploymentFilePath); + if (contentText?.includes('<*GET_CONTRACT_ID*>')) { + contentText = contentText.replace('<*GET_CONTRACT_ID*>', contractId); + } else { + contentText = contractId; + } + } + if (!contentText) { contentText = await clickCopyButton(page, buttonName); } @@ -31,9 +42,8 @@ export async function modifyFile( } else { const lines = readFileSync(filePath, 'utf8').split('\n'); if (removeLines) { - const removeLinesArray = JSON.parse(removeLines); - removeLinesArray.forEach((lineNumber: string) => { - lines[Number.parseInt(lineNumber) - 1] = '~~~REMOVE~~~'; + removeLines.forEach((lineNumber: number) => { + lines[lineNumber - 1] = '~~~REMOVE~~~'; }); } if (atLine) { @@ -66,3 +76,9 @@ export function compareOutputs(expected: string, actual: string) { expect(trimmedLineA).toEqual(trimmedLineB); }); } + +function getContractId(deploymentFilePath: string) { + const deploymentFile = readFileSync(deploymentFilePath, { encoding: 'utf8' }); + const json = JSON.parse(deploymentFile); + return json.entries[0].address; +} diff --git a/tests/utils/getTestActions.ts b/tests/utils/getTestActions.ts index aba344ba..1d303e44 100644 --- a/tests/utils/getTestActions.ts +++ b/tests/utils/getTestActions.ts @@ -1,7 +1,7 @@ import { type Page, expect } from '@playwright/test'; export async function getTestActions(page: Page) { - const testActions = await page.$$eval('span[data-name]', (elements: Element[]) => { + const testActions = await page.$$eval('span[data-test-action]', (elements: Element[]) => { return elements.map((el) => { const dataAttributes: { [key: string]: string; diff --git a/tests/utils/runCommand.ts b/tests/utils/runCommand.ts index 0f70ea57..32e342b2 100644 --- a/tests/utils/runCommand.ts +++ b/tests/utils/runCommand.ts @@ -3,17 +3,21 @@ import { execSync } from 'node:child_process'; import { clickCopyButton } from './button'; import fs from 'fs'; import { join } from 'path'; +import os from 'os'; +import pty from 'node-pty'; export async function runCommand( page: Page, buttonName: string, goToFolder: string = 'tests-output', projectFolder: string = 'hardhat-project', - preCommand?: string + preCommand?: string, + useSetCommand?: string, + prompts?: string ) { const copied = await clickCopyButton(page, buttonName); console.log('COPIED', copied); - let command = copied; + let command = useSetCommand ?? copied; const newHardhatProject = command.includes('npx hardhat init'); if (newHardhatProject) { @@ -31,7 +35,11 @@ export async function runCommand( command = `cd ${goToFolder} && ${command}`; } - run(command); + if (prompts) { + await runWithPrompts(page, command, prompts); + } else { + run(command); + } } } @@ -77,3 +85,37 @@ function copyFolder(source: string, destination: string) { copyRecursive(source, destination); } + +export async function runWithPrompts(page: Page, command: string, prompts: string) { + const shell = os.platform() === 'win32' ? 'powershell.exe' : 'bash'; + + const ptyProcess = pty.spawn(shell, [], { + name: 'xterm-color', + cols: 80, + rows: 30, + cwd: process.cwd(), + env: process.env, + }); + + const promptsArray = prompts.split('|').map((pair) => { + const [prompt, answer] = pair.split(':'); + return { prompt, answer }; + }); + + ptyProcess.onData((data) => { + console.log('DATA:', data); + + for (let index = 0; index < promptsArray.length; index++) { + const promptObject = promptsArray[index]; + if (data.includes(promptObject.prompt)) { + console.log('FOUND PROMPT:', promptObject.prompt); + ptyProcess.write(promptObject.answer + '\r'); + } + } + }); + + ptyProcess.write(command + '\r'); + + await page.waitForTimeout(35000); + console.log('waited 35 seconds'); +} diff --git a/tests/utils/runTest.ts b/tests/utils/runTest.ts index 90ef725e..ba3c81c5 100644 --- a/tests/utils/runTest.ts +++ b/tests/utils/runTest.ts @@ -1,12 +1,38 @@ -import type { Page } from '@playwright/test'; +import type { BrowserContext, Page } from '@playwright/test'; import { runCommand } from './runCommand'; import { getTestActions } from './getTestActions'; import { visit } from './visit'; import { compareToFile, modifyFile, writeToFile } from './files'; import { checkIfBalanceIsZero } from './queries'; +import { setupFolders, startLocalServer, stopServers } from './setup'; +import { getConfig } from '../configs/config'; +import type { IStepConfig } from './types'; -export async function runTest(page: Page, url: string) { +export async function setupAndRunTest( + page: Page, + context: BrowserContext, + folderName: string, + pageUrls: string[], + tutorialName: string +) { + // SETUP + await startLocalServer(page); + await context.grantPermissions(['clipboard-read', 'clipboard-write']); + await setupFolders(folderName); + + const config = getConfig(tutorialName); + + // TEST + for (const pageUrl of pageUrls) { + await runTest(page, pageUrl, config!); + } + + // SHUT DOWN ANY RUNNING PROJECTS + stopServers(); +} + +export async function runTest(page: Page, url: string, config: IStepConfig) { await visit(page, url); console.log('GETTING TEST ACTIONS'); // eslint-disable-next-line @typescript-eslint/no-explicit-any @@ -16,42 +42,47 @@ export async function runTest(page: Page, url: string) { for (const step of steps) { console.log('STEP:', step); await page.waitForTimeout(1000); - switch (step['data-name']) { + const stepID = step['id']; + const stepData = config[stepID]; + switch (stepData.action) { case 'runCommand': await runCommand( page, - step.id, - step['data-command-folder'], - step['data-project-folder'], - step['data-pre-command'] + stepID, + stepData.commandFolder, + stepData.projectFolder, + stepData.preCommand, + stepData.useSetCommand, + stepData.prompts ); break; case 'wait': - await page.waitForTimeout(Number.parseInt(step['data-timeout'])); + await page.waitForTimeout(stepData.timeout); break; case 'writeToFile': - await writeToFile(page, step.id, step['data-filepath']); + await writeToFile(page, stepID, stepData.filepath); break; case 'modifyFile': await modifyFile( page, - step.id, - step['data-filepath'], - Number.parseInt(step['data-add-spaces-before']), - step['data-add-spaces-after'], - Number.parseInt(step['data-at-line']), - step['data-remove-lines'], - step['data-use-set-data'] + stepID, + stepData.filepath, + stepData.addSpacesBefore, + stepData.addSpacesAfter, + stepData.atLine, + stepData.removeLines, + stepData.useSetData, + stepData.getContractId ); break; case 'compareToFile': - await compareToFile(page, step.id, step['data-filepath']); + await compareToFile(page, stepID, stepData.filepath); break; case 'checkIfBalanceIsZero': - await checkIfBalanceIsZero(step['data-network-url'], step['data-address']); + await checkIfBalanceIsZero(stepData.networkUrl, stepData.address); break; default: - console.log('STEP NOT FOUND:', step); + console.log('STEP NOT FOUND:', stepData); } } } diff --git a/tests/utils/types.ts b/tests/utils/types.ts new file mode 100644 index 00000000..3fa7f347 --- /dev/null +++ b/tests/utils/types.ts @@ -0,0 +1,46 @@ +export interface IStepConfig { + [key: string]: IStep; +} + +export type IStep = IRunCommand | IWait | IWriteToFile | IModifyFile | ICompareToFile | ICheckIfBalanceIsZero; + +export interface IRunCommand { + action: 'runCommand'; + commandFolder?: string; + projectFolder?: string; + preCommand?: string; + useSetCommand?: string; + prompts?: string; +} + +export interface IWait { + action: 'wait'; + timeout: number; +} + +export interface IWriteToFile { + action: 'writeToFile'; + filepath: string; +} + +export interface IModifyFile { + action: 'modifyFile'; + filepath: string; + addSpacesBefore?: number; + addSpacesAfter?: number; + atLine?: number; + removeLines?: number[]; + useSetData?: string; + getContractId?: string; +} + +export interface ICompareToFile { + action: 'compareToFile'; + filepath: string; +} + +export interface ICheckIfBalanceIsZero { + action: 'checkIfBalanceIsZero'; + networkUrl: string; + address: string; +}