diff --git a/.github/workflows/app-deploy-feature-branch.yml b/.github/workflows/app-deploy-feature-branch.yml index 953638a41e..5b862b4edd 100644 --- a/.github/workflows/app-deploy-feature-branch.yml +++ b/.github/workflows/app-deploy-feature-branch.yml @@ -63,3 +63,16 @@ jobs: expires: 7d projectId: staging-scan-v2 entryPoint: ./packages/app + + mainnet: + needs: build + name: Feature Env, Mainnet+ + uses: ./.github/workflows/app-e2e.yml + secrets: inherit + permissions: + contents: read + with: + targetUrl: ${{ needs.build.outputs.dappUrl }} + default_network_value_for_e2e: "/?network=mainnet" + publish_to_allure: true + environmentTags: "and not @stagingEnv" diff --git a/.github/workflows/app-deploy-preview.yml b/.github/workflows/app-deploy-preview.yml index ad8916f9a9..de279372f8 100644 --- a/.github/workflows/app-deploy-preview.yml +++ b/.github/workflows/app-deploy-preview.yml @@ -60,3 +60,17 @@ jobs: run: | run: | echo "Block explorer app has been deployed to: ${{ steps.deploy.outputs.details_url }}" + + mainnet: + needs: deploy + name: Staging Env, Mainnet+ + uses: ./.github/workflows/app-e2e.yml + secrets: inherit + permissions: + contents: read + with: + targetUrl: ${{ needs.deploy.outputs.dappUrl }} + default_network_value_for_e2e: "/?network=mainnet" + publish_to_allure: true + environmentTags: "and not @featureEnv" + \ No newline at end of file diff --git a/.github/workflows/app-e2e.yml b/.github/workflows/app-e2e.yml new file mode 100644 index 0000000000..a152ced03e --- /dev/null +++ b/.github/workflows/app-e2e.yml @@ -0,0 +1,150 @@ +name: BE App E2E tests + +on: + workflow_call: + secrets: + ALLURE_TOKEN: + description: 'A token passed from the caller workflow' + required: true + inputs: + environmentTags: + type: string + default: '' + required: false + targetUrl: + type: string + default: 'https://staging-scan-v2.zksync.dev/' + required: true + default_network_value_for_e2e: + type: string + default: '/?network=mainnet' + required: true + publish_to_allure: #Here we define the variable that can be overwritten by caller workflow + type: boolean + description: "Publish test results to allure" + default: true + required: false + +env: + ALLURE_TOKEN: ${{ secrets.ALLURE_TOKEN }} + ALLURE_SEARCH_REQUEST: '[{"id":"name","type":"string","value":"#${{ github.run_number }}"}]' + ALLURE_BASIC_TAGS: "${{ github.head_ref }}, #${{ github.run_number }}, ${{ github.event.pull_request.title }}" + ALLURE_PROJECT_ID: ${{ vars.ALLURE_PROJECT_ID }} + ALLURE_ENDPOINT: ${{ vars.ALLURE_ENDPOINT }} + +jobs: + e2e: + runs-on: [self-hosted, ci-runner] + permissions: + contents: read + defaults: + run: + working-directory: ./packages/app + strategy: + fail-fast: false + matrix: + tags: [ + "@artifactsSet1", + "@artifactsSet2", + "@artifactsSet3", + "@artifactsSet4", + "@redirectionSet1", + "@redirectionSet2", + "@redirectionSet3", + "@copying", + "@search", + "@testnetSmokeSuite" + ] + + name: '${{ matrix.tags }}' + container: + image: mcr.microsoft.com/playwright:v1.27.0-focal + options: --user root + steps: + - uses: actions/checkout@v3 + with: + fetch-depth: 0 + + - name: Cache node modules + id: cache-nodemodules + uses: actions/cache@v3 + env: + cache-name: cache-node-modules + with: + path: node_modules + key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }} + restore-keys: | + ${{ runner.os }}-build-${{ env.cache-name }}- + ${{ runner.os }}-build- + ${{ runner.os }}- + + - name: Install dependencies + working-directory: ./ + if: steps.cache-nodemodules.outputs.cache-hit != 'true' + run: | + npm pkg delete scripts.prepare + npm ci + + - name: Download allurectl + run: wget https://github.com/allure-framework/allurectl/releases/latest/download/allurectl_linux_386 -O ./allurectl + + - name: Change permission for allurectl + run: chmod +x ./allurectl + + - name: Launch tests + env: + CI: 'true' + TARGET_ENV: ${{ inputs.targetUrl }} + run: | + echo "Run tests" + if [ "${{ matrix.tags }}" = "@testnetSmokeSuite" ]; then + echo "Run in testnetSmokeSuite only" + E2ENETWORK='/?network=goerli' npx cucumber-js --tags "${{ matrix.tags }} ${{ inputs.environmentTags }} and not @mainnet" + else + echo "Run in mainnet" + E2ENETWORK='${{ inputs.default_network_value_for_e2e }}' npx cucumber-js --tags "${{ matrix.tags }} ${{ inputs.environmentTags }} and not @testnet" + fi + + - name: Reset tags quotes + if: always() + run: | + echo "MATRIX_TAG_WITHOUT_QUOTES=$(echo ${{ matrix.tags }} | sed -e 's/@//g' )" >> $GITHUB_ENV + + - name: Create launch ID + if: always() + env: + ALLURE_LAUNCH_NAME: "#${{ github.run_number }} ${{ env.MATRIX_TAG_WITHOUT_QUOTES }}" + ALLURE_LAUNCH_TAGS: "${{ env.ALLURE_BASIC_TAGS }}, ${{ env.MATRIX_TAG_WITHOUT_QUOTES }}" + run: | + echo "ALLURE_LAUNCH_ID=$(./allurectl launch create --launch-name '${{ env.ALLURE_LAUNCH_NAME }}' --no-header --format ID | tail -n1)" >> $GITHUB_ENV + + - name: Upload tests to the Allure proj + if: always() && inputs.publish_to_allure == true + run: | + ./allurectl upload allure-results --launch-id ${{ env.ALLURE_LAUNCH_ID }} + ./allurectl launch close ${{ env.ALLURE_LAUNCH_ID }} + + - if: failure() + name: Save artifacts + uses: actions/upload-artifact@v3 + with: + name: portal_e2e_${{ github.run_number }}_artifacts + path: packages/app/tests/e2e/artifacts/* + + publish: + name: Publish Allure link to GIT + runs-on: ubuntu-latest + permissions: + contents: read + needs: e2e + if: always() + steps: + - name: Prepare a link + run: | + echo "BASE64_SEARCH_REQUEST=$(echo '${{ env.ALLURE_SEARCH_REQUEST }}' | base64)" >> $GITHUB_ENV + + - name: Publish Allure link to GIT Summary + run: | + LINK="${{ vars.ALLURE_ENDPOINT }}project/${{ vars.ALLURE_PROJECT_ID }}/launches?search=${{ env.BASE64_SEARCH_REQUEST }}" + echo "Allure [e2e tests]($LINK) :rocket: in git run #${{ github.run_number }}" >> $GITHUB_STEP_SUMMARY + \ No newline at end of file diff --git a/packages/app/README.md b/packages/app/README.md index 5d5035eb97..27c421eff1 100644 --- a/packages/app/README.md +++ b/packages/app/README.md @@ -93,6 +93,12 @@ npm run build npm run test ``` +### Run End-to-End Tests with [Playwright](https://www.playwright.io/) + +```sh +npm run test:e2e +``` + ### Lint with [ESLint](https://eslint.org/) ```sh diff --git a/packages/app/cucumber.mjs b/packages/app/cucumber.mjs new file mode 100644 index 0000000000..92b66724c3 --- /dev/null +++ b/packages/app/cucumber.mjs @@ -0,0 +1,25 @@ +const getWorldParams = () => { + const params = { + foo: "bar", + }; + + return params; +}; + +export default { + requireModule: ["ts-node/register"], + paths: ["tests/e2e/features/**/*.feature"], + require: ["tests/e2e/src/**/*.ts"], + format: [ + //"json:tests/e2e/reports/cucumber-report.json", + //"html:tests/e2e/reports/report.html", + "summary", + "progress-bar", + "@cucumber/pretty-formatter", + "./tests/e2e/src/support/reporters/allure-reporter.js", + ], + formatOptions: { snippetInterface: "async-await" }, + worldParameters: getWorldParams(), + publishQuiet: true, + retry: 1, +}; diff --git a/packages/app/package.json b/packages/app/package.json index a32d195303..f969896f5a 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -13,6 +13,7 @@ "start": "vite preview --port 3010", "test": "TZ=UTC-3 vitest --environment jsdom", "test:ci": "TZ=UTC-3 vitest --environment jsdom --run --reporter=verbose --reporter=junit --outputFile.junit=./junit.xml", + "test:e2e": "cucumber-js -t \"not @testnet\"", "typecheck": "vue-tsc --noEmit -p tsconfig.vitest.json --composite false", "lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix", "storybook": "start-storybook -s ./public -p 6006", diff --git a/packages/app/tests/e2e/README.md b/packages/app/tests/e2e/README.md new file mode 100644 index 0000000000..38470a2ba9 --- /dev/null +++ b/packages/app/tests/e2e/README.md @@ -0,0 +1,61 @@ +# The test solution for BlockExplorer v2 E2E testing + +Based on Playwright.io/TypeScript/BDD + +## Recommended E2E pre-test setup + +Before the execution of the end-2-end tests there needs to install dependencies: + +```bash +npm install +``` + +## How to run E2E tests + +-- +all tests: +```bash +npx cucumber-js +``` +or + +```bash +npm run test:e2e +``` + +-- +tests by specified tags (eg, @title): +```bash +npx cucumber-js --tags @title +``` +## Variables + +process.env.TARGET_ENV: + set up the target URL environment for the test run. Default value is stage https://staging-scan-v2.zksync.dev + +## Reports +Reports are configured in Cucumber.mjs file and results after test runs might be gotten by the next one approaches: + +Cucumber reports: + +- uncomment lines in cucumber.mjs file + // "json:tests/e2e/reports/cucumber-report.json", + // "html:tests/e2e/reports/report.html", + +- run the script below +```bash +open tests/e2e/reports/report.html +``` +Allure reports: + +It's working on CI/CD. The results are collected in allure-results folder. Every test run generate appropriate test result data in JSON format. + +## Tags +Tags allow to separate the current feature scope to appropriate suits, eg: + +@smoke +@sanity +@regression:small +@regression:all + +Tags should be pasted above the "Feature" or "Scenario" key-word \ No newline at end of file diff --git a/packages/app/tests/e2e/features/artifacts/artifactsSet1.feature b/packages/app/tests/e2e/features/artifacts/artifactsSet1.feature new file mode 100644 index 0000000000..4217c90f80 --- /dev/null +++ b/packages/app/tests/e2e/features/artifacts/artifactsSet1.feature @@ -0,0 +1,230 @@ +@artifactsSet1 +Feature: Main Page + + Background: + Given I am on main page + + @id253 @featureEnv @testnet + Scenario Outline: Check the element "" in Tools section is available, clickable and have correct href + Given I click by text "Tools" + Given Element with "text" "" should be "visible" + When Element with "text" "" should be "clickable" + Then Element with "text" "" should have "" value + + Examples: + | Sub-Section | url | + | Smart Contract Verification | /contracts/verify | + | Portal | https://goerli.staging-portal.zksync.dev/ | + + @id253 @featureEnv @mainnet + Scenario Outline: Check the element "" in Tools section is available, clickable and have correct href + Given I click by text "Tools" + Given Element with "text" "" should be "visible" + When Element with "text" "" should be "clickable" + Then Element with "text" "" should have "" value + + Examples: + | Sub-Section | url | + | Smart Contract Verification | /contracts/verify | + | Portal | https://staging-portal.zksync.dev/ | + + @id253:I @stagingEnv @testnet + Scenario Outline: Check the element "" in Tools section is available, clickable and have correct href + Given I click by text "Tools" + Given Element with "text" "" should be "visible" + When Element with "text" "" should be "clickable" + Then Element with "text" "" should have "" value + + Examples: + | Sub-Section | url | + | Smart Contract Verification | /contracts/verify | + | Portal | https://goerli.portal.zksync.io/ | + + @id253:I @stagingEnv @mainnet + Scenario Outline: Check the element "" in Tools section is available, clickable and have correct href + Given I click by text "Tools" + Given Element with "text" "" should be "visible" + When Element with "text" "" should be "clickable" + Then Element with "text" "" should have "" value + + Examples: + | Sub-Section | url | + | Smart Contract Verification | /contracts/verify | + | Portal | https://portal.zksync.io/ | + + @id231 + Scenario Outline: Check social networks icon "" is available, clickable and have correct href + Given Element with "partial href" "" should be "visible" + When Element with "partial href" "" should be "clickable" + Then Element with "partial href" "" should have "" value + + Examples: + | Value | url | + # discord renamed to "join" + | join | https://join.zksync.dev/ | + | twitter | https://twitter.com/zksync | + + @id254:I + Scenario Outline: Check dropdown "" for "" and verify + Given Set the "" value for "" switcher + Then Check the "" value is actual for "" switcher + + Examples: + | Value | Dropdown | + | zkSync Era Testnet | network | + | zkSync Era Mainnet | network | + | EN | language | + | UA | language | + + @id254:II @featureEnv + Scenario Outline: Check dropdown "" for "" and verify + Given Set the "" value for "" switcher + Then Check the "" value is actual for "" switcher + + Examples: + | Value | Dropdown | + | Goerli (Stage2) | network | + + Scenario: Network stats is displayed + Then Element with "text" "Network Stats" should be "visible" + + @id101:I @id274:I @testnetSmokeSuite + Scenario Outline: Count rows in "" table + Then Table "
" should have "" rows + + Examples: + | Table | Number of rows | + | Latest Batches | 10 | + | Latest Transactions | 10 | + + @id101:II @id274:II @testnetSmokeSuite + Scenario Outline: Verify "" column in "
" table + Then Table "
" on Main page contains "" name + + Examples: + | Table | Column | + | Latest Transactions | Status | + | Latest Transactions | Transaction Hash | + | Latest Transactions | Age | + | Latest Batches | Status | + | Latest Batches | Batch | + | Latest Batches | Size | + | Latest Batches | Age | + + # @id127 #should be fixed on the ci + # Scenario Outline: Verify "" Tab title + # Given I go to page "<Page>" + # Then Tab title on "<Page>" contains "<Title name>" + # + # Examples: + # | Page | Title name | + # | /accounts/0x0bda7f2dc633b66b2cff59718fddb25cf6b2e760 | Account 0x0bda7f...e760 | + # | /block/635161 | Block #635161 | + # | /contracts/0x92131f10c54f9b251a5deaf3c05815f7659bbe02 | Contract 0x92131f...be02 | + # | /ts/0x2134e1f86ac15d7fadb0b7ea68698103ac23eebe6f95aed0fa4f5712eaff22a5 | Transaction 0x2134e1...22a5 | + + @id103 @testnetSmokeSuite + Scenario: Check Search field is visible + Given I go to page "/address/0x8f0F33583a56908F7F933cd6F0AaE382aC3fd8f6" + Then Element with "id" "search" should be "visible" + + @id209:I @testnet + Scenario Outline: Verify Transaction table contains "<Row>" row + Given I go to page "/tx/0xe7a91cc9b270d062328ef995e0ef67195a3703d43ce4e1d375f87d5c64e51981" + When Table contains row with "<Row>" + Then Element with "text" "<Value>" should be "visible" + Examples: + | Row | Value | + | Transaction Hash | 0xe7a91cc9b270d062328ef995e0ef67195a3703d43ce4e1d375f87d5c64e51981 | + | Status | Processed | + | Status | Executed | + | From | 0x8f0F33583a56908F7F933cd6F0AaE382aC3fd8f6 | + | To | 0x0faF6df7054946141266420b43783387A78d82A9 | + | Fee | ETH | + | Nonce | 0 | + + + + @id209:II @testnet + Scenario Outline: Verify Transaction table contains "<Row>" row + Given I go to page "/tx/0xe7a91cc9b270d062328ef995e0ef67195a3703d43ce4e1d375f87d5c64e51981" + When Table contains row with "<Row>" + Then Element with "partial text" "<Value>" should be "visible" + Examples: + | Row | Value | + | Input data | 0xa9059cbb00000000000000000000000 | + | Block | 45751 | + | Batch | #661 | + | Created | 2023-02-10 | + | Tokens Transferred | 0x8f0F33583a5 | + | Tokens Transferred | From | + | Tokens Transferred | 0x8f0F33583a5...d8f6 | + | Tokens Transferred | To | + | Tokens Transferred | 0x0faF6df7054...82A9 | + + @id209:I @mainnet + Scenario Outline: Verify Transaction table contains "<Row>" row + Given I go to page "/tx/0xc598db886275ce5d7900fbfb6797afb3231a117446d91eb0dd8cf69addf90779" + When Table contains row with "<Row>" + Then Element with "text" "<Value>" should be "visible" + Examples: + | Row | Value | + | Transaction Hash | 0xc598db886275ce5d7900fbfb6797afb3231a117446d91eb0dd8cf69addf90779 | + | Status | Processed | + | Status | Executed | + | From | 0xc2C6aE3B10c741A3FD291a1C1Baadc41fD2d84d5 | + | To | 0x6Ef9598Bf85bc651780e17D6848f2E1Ff1Ce5Aae | + | Fee | ETH | + | Nonce | 60 | + + @id209:II @mainnet + Scenario Outline: Verify Transaction table contains "<Row>" row + Given I go to page "/tx/0xc598db886275ce5d7900fbfb6797afb3231a117446d91eb0dd8cf69addf90779" + When Table contains row with "<Row>" + Then Element with "partial text" "<Value>" should be "visible" + Examples: + | Row | Value | + | Input data | Function: transfer | + | Block | 3491940 | + | Batch | #28739 | + | Created | 2023-05-14 | + + + @id211 @testnet + Scenario Outline: Verify Contract info table contains "<Row>" row + Given I go to page "/address/0x3e7676937A7E96CFB7616f255b9AD9FF47363D4b" + Then Element with "text" "<Row>" should be "visible" + + Examples: + | Row | + | Address | + | Creator | + | Transactions | + + @id211 @mainnet + Scenario Outline: Verify Contract info table contains "<Row>" row + Given I go to page "/address/0x6Ef9598Bf85bc651780e17D6848f2E1Ff1Ce5Aae" + Then Element with "text" "<Row>" should be "visible" + + Examples: + | Row | + | Address | + | Creator | + | Transactions | + + @id212 + Scenario Outline: Verify Smart contract verification Page + Given I go to page "/contracts/verify" + Then Element with "<Selector type>" "<Element>" should be "<Assertion>" + + Examples: + | Selector type | Element | Assertion | + | id | contractAddress | visible | + | id | contractName | visible | + | testId | radio-buttons | visible | + | id | compilerVersion | visible | + | id | zkCompilerVersion | visible | + | id | sourceCode | visible | + | id | constructorArguments | visible | + | text | Verify Smart Contract | clickable | + | text | Clear | clickable | diff --git a/packages/app/tests/e2e/features/artifacts/artifactsSet2.feature b/packages/app/tests/e2e/features/artifacts/artifactsSet2.feature new file mode 100644 index 0000000000..05043dc146 --- /dev/null +++ b/packages/app/tests/e2e/features/artifacts/artifactsSet2.feature @@ -0,0 +1,241 @@ +@artifactsSet2 +Feature: Main Page + + Background: + Given I am on main page + + @id236:I @testnet + Scenario Outline: Verify left table contains "<Row>" row and "<Value>" value on Block page + Given I go to page "/block/13781" + When Table contains cell with "<Row>" + Then Element with "text" "<Value>" should be "visible" + + Examples: + | Row | Value | + | Block Number | 13781 | + | Block Size | 1 | + | Status | Verified | + + @id236:I @mainnet + Scenario Outline: Verify left table contains "<Row>" row and "<Value>" value on Block page + Given I go to page "/block/13781" + When Table contains cell with "<Row>" + Then Element with "text" "<Value>" should be "visible" + + Examples: + | Row | Value | + | Block Number | 13781 | + | Block Size | 6 | + | Status | Verified | + + @id236:II @testnet + Scenario Outline: Verify left table contains "<Row>" row and "<Value>" value on Block page + Given I go to page "/block/13781" + When Table contains cell with "<Row>" + Then Element with "partial text" "<Value>" should be "visible" + + Examples: + | Row | Value | + | Root hash | 0xfa8fadc06c46dc8a3c52 | + | Timestamp | 2023-02-09 | + | Commit tx hash | 0xc3211d8bc51163f923ff | + + @id236:II @mainnet + Scenario Outline: Verify left table contains "<Row>" row and "<Value>" value on Block page + Given I go to page "/block/13781" + When Table contains cell with "<Row>" + Then Element with "partial text" "<Value>" should be "visible" + + Examples: + | Row | Value | + | Root hash | 0xfa8fadc06c46dc8a3c52 | + | Timestamp | 2023-03-24 | + | Commit tx hash | 0xeb94693555bd2ef92c82 | + + @id264 @testnet + Scenario Outline: Verify right table contains "<Row>" row and "<Value>" value on Block page + Given I go to page "/block/13781" + When Table contains cell with "<Row>" + Then Element with "partial text" "<Value>" should be "visible" + + Examples: + | Row | Value | + | Committed | 2023-02-09 | + | Prove tx hash | 0xc30de91b87be5939ac1c4 | + | Proven | 2023-02-09 | + | Execute tx hash | 0x898385316483aef82e6d | + | Executed | 2023-02-09 | + + @id264 @mainnet + Scenario Outline: Verify right table contains "<Row>" row and "<Value>" value on Block page + Given I go to page "/block/13781" + When Table contains cell with "<Row>" + Then Element with "partial text" "<Value>" should be "visible" + + Examples: + | Row | Value | + | Committed | 2023-03-24 | + | Prove tx hash | 0xbda7a65f5de30c866639a | + | Proven | 2023-03-24 | + | Execute tx hash | 0x1ddae5de222936cb7f5c | + | Executed | 2023-03-25 | + + @id265 + Scenario Outline: Verify block transactions table contains "<Column name>" + Given I go to page "/block/1" + Then Column with "<Column name>" name is visible + + Examples: + | Column name | + | Status | + | Transaction Hash | + | Age | + | From | + | To | + | Method | + | Value | + | Fee | + + @id245 + Scenario Outline: Verify table contains "<Column name>" column name on Blocks page + Given I go to page "/blocks" + And Pagination form should be visible + And Table "Blocks" should have "10" rows + Then Element with "text" "<Column name>" should be "visible" + + Examples: + | Column name | + | Block | + | Status | + | Age | + + @id247 + Scenario Outline: Verify table contains "<Column name>" column name on Transactions page + Given I go to page "/transactions" + And Pagination form should be visible + And Table "Transactions" should have "10" rows + Then Column with "<Column name>" name is visible + + Examples: + | Column name | + | Status | + | Transaction Hash | + | Age | + | From | + | To | + | Method | + | Value | + | Fee | + + @id273 @testnet + Scenario Outline: Verify Network stats component contains "<Block>" block + Then Network stats "<Block>" block should be visible + + Examples: + | Block | + | Network Stats | + | Stats are occasionally reset on testnet. | + | Committed Blocks | + | Verified Blocks | + | Transactions | + + @id273 @mainnet + Scenario Outline: Verify Network stats component contains "<Block>" block + Then Network stats "<Block>" block should be visible + + Examples: + | Block | + | Network Stats | + | zkSync Era Mainnet is open to everyone. | + | Committed Blocks | + | Verified Blocks | + | Transactions | + + @id271:I @testnet + Scenario Outline: Verify Contract tab for "<Element>" visibility on Contracts page + Given I go to page "/address/0x3e7676937A7E96CFB7616f255b9AD9FF47363D4b" + When I select "Contract" tab on "Contract" page + Then Element with "<Selector type>" "<Element>" should be "<Assertion>" + + Examples: + | Selector type | Element | Assertion | + | text | Are you owner of this smart-contract? | visible | + | partial text | Verify and Publish your contract source code today! | visible | + | partial text | This contract was created by the contract code at | visible | + | partial text | 0x3e7676937A7E96CFB7616f255b9AD9FF47363D4b | visible | + | text | Verify Smart Contract | clickable | + | text | Verify Smart Contract | visible | + | partial text | 0x000400000000000200030000000000020000000003010019000000600 | clickable | + | partial text | 0x000400000000000200030000000000020000000003010019000000600 | visible | + + @id271:I @mainnet + Scenario Outline: Verify Contract tab for "<Element>" visibility on Contracts page + Given I go to page "/address/0x8f9e8Fd0b8442a1dc72d2D9506e99cBDA134B382" + When I select "Contract" tab on "Contract" page + Then Element with "<Selector type>" "<Element>" should be "<Assertion>" + + Examples: + | Selector type | Element | Assertion | + | text | Are you owner of this smart-contract? | visible | + | partial text | Verify and Publish your contract source code today! | visible | + | partial text | This contract was created by the contract code at | visible | + | partial text | 0x8f9e8Fd0b8442a1dc72d2D9506e99cBDA134B382 | visible | + | text | Verify Smart Contract | clickable | + | text | Verify Smart Contract | visible | + | partial text | 0x000400000000000200050000000 | clickable | + | partial text | 0x000400000000000200050000000 | visible | + + @id271:II @testnet + Scenario: Verify Contract tab dropdown on Contracts page + Given I go to page "/address/0x3e7676937A7E96CFB7616f255b9AD9FF47363D4b" + When I select "Contract" tab on "Contract" page + When I click on bytecode dropdown + Then Element with "partial text" "0x000400000000000200030000000000020000000003010019000000600" should be "visible" + + @id271:II @mainnet + Scenario: Verify Contract tab dropdown on Contracts page + Given I go to page "/address/0x8f9e8Fd0b8442a1dc72d2D9506e99cBDA134B382" + When I select "Contract" tab on "Contract" page + When I click on bytecode dropdown + Then Element with "partial text" "0x000400000000000200050000000" should be "visible" + + @id270 @testnet + Scenario Outline: Verify column "<Column name>" tab on Contract page + Given I go to page "/address/0x3e7676937A7E96CFB7616f255b9AD9FF47363D4b" + # And Element with "text" "Show more transactions ->" should be "visible" + # And Element with "text" "Show more transactions ->" should be "clickable" + Then Column with "<Column name>" name is visible + + Examples: + | Column name | + | Status | + | Transaction Hash | + | From | + | Method | + | Age | + + @id270 @mainnet + Scenario Outline: Verify column "<Column name>" tab on Contract page + Given I go to page "/address/0x8f9e8Fd0b8442a1dc72d2D9506e99cBDA134B382" + # And Element with "text" "Show more transactions ->" should be "visible" + # And Element with "text" "Show more transactions ->" should be "clickable" + Then Column with "<Column name>" name is visible + + Examples: + | Column name | + | Status | + | Transaction Hash | + | From | + | Method | + | Age | + + @id261 @testnet + Scenario Outline: Verify column "<Column name>" for Balances on Account page + When I go to page "/address/0x8f0F33583a56908F7F933cd6F0AaE382aC3fd8f6" + Then Column with "<Column name>" name is visible + + Examples: + | Column name | + | Asset | + | Balance | + | Token Address | diff --git a/packages/app/tests/e2e/features/artifacts/artifactsSet3.feature b/packages/app/tests/e2e/features/artifacts/artifactsSet3.feature new file mode 100644 index 0000000000..2520f2ec52 --- /dev/null +++ b/packages/app/tests/e2e/features/artifacts/artifactsSet3.feature @@ -0,0 +1,219 @@ +@artifactsSet3 +Feature: Main Page + + Background: + Given I am on main page + + @id230 @testnet + Scenario Outline: Verify Logs tab artifact for "<Element>" on Transaction page + Given I go to page "/tx/0x4f7406f5565d875ce1a2ebb7c83f582e9795294ad57276eae3909b59537ab051" + When I select "Logs" tab on "Transaction" page + Then Element with "<Selector type>" "<Element>" should be "<Assertion>" + + Examples: + | Selector type | Element | Assertion | + | log tab | Address | visible | + | log tab | Name | visible | + | log tab | Topics | visible | + | log tab | Data | visible | + | log tab | 180817500000000 | visible | + | log tab | 180817500000000 | clickable | + | log tab | 0x0000000000000000000000000000000000000000000000000de0b6b3a7640000 | visible | + | log tab | 0x0000000000000000000000000000000000000000000000000de0b6b3a7640000 | clickable | + + @id230 @mainnet + Scenario Outline: Verify Logs tab artifact for "<Element>" on Transaction page + Given I go to page "/tx/0xc598db886275ce5d7900fbfb6797afb3231a117446d91eb0dd8cf69addf90779" + When I select "Logs" tab on "Transaction" page + Then Element with "<Selector type>" "<Element>" should be "<Assertion>" + + Examples: + | Selector type | Element | Assertion | + | log tab | Address | visible | + | log tab | Name | visible | + | log tab | Topics | visible | + | log tab | Data | visible | + | log tab | 345359500000000 | visible | + | log tab | 345359500000000 | clickable | + + @id209:III @id230:II @testnet + Scenario Outline: Check copy button action on "<Page>" page + Given I go to page "<Page>" + When I click on the first copy button + Then Element with "text" "Copied!" should be "visible" + + Examples: + | Page | + | /tx/0x4f7406f5565d875ce1a2ebb7c83f582e9795294ad57276eae3909b59537ab051 | + | /address/0xed7175341f123f7718aBaCF1702d6980CFc08784 | + | /block/1 | + | /address/0x574343B3d1544477f2C4dF38c2Ef720Ab33e782b | + + @id209:III @id230:II @mainnet + Scenario Outline: Check copy button action on "<Page>" page + Given I go to page "<Page>" + When I click on the first copy button + Then Element with "text" "Copied!" should be "visible" + + Examples: + | Page | + | /tx/0xc598db886275ce5d7900fbfb6797afb3231a117446d91eb0dd8cf69addf90779 | + | /address/0x6Ef9598Bf85bc651780e17D6848f2E1Ff1Ce5Aae | + | /block/1 | + | /address/0xc2C6aE3B10c741A3FD291a1C1Baadc41fD2d84d5 | + + @id249 @testnet @testnetSmokeSuite + Scenario Outline: Verify table contains "<Column name>" column name on Tokens page + Given I go to page "/tokenlist" + And Table "Tokens" should have "5" rows + Then Column with "<Column name>" name is visible + + Examples: + | Column name | + | Token Name | + | Price | + | Token Address | + + @id249 @mainnet + Scenario Outline: Verify table contains "<Column name>" column name on Tokens page + Given I go to page "/tokenlist" + # And Table "Tokens" should have "5" rows + Then Column with "<Column name>" name is visible + + Examples: + | Column name | + | Token Name | + | Price | + | Token Address | + + @id381 @testnet @testnetSmokeSuite + Scenario Outline: Verify label "out" for Account info on Account page + When I go to page "/address/0xed7175341f123f7718aBaCF1702d6980CFc08784" + Then Element with "text" "out" should be "visible" + + @id380 @testnet @testnetSmokeSuite + Scenario: Verify label "in" for Account info on Account page + Given I go to page "/address/0xed7175341f123f7718aBaCF1702d6980CFc08784" + # When I click by text "Show more transactions ->" + Then Element with "text" "in" should be "visible" + + @id382 @testnet @testnetSmokeSuite + Scenario: Verify label "self" for Account info on Account page + Given I go to page "/address/0xed7175341f123f7718aBaCF1702d6980CFc08784" + # When I click by text "Show more transactions ->" + Then Element with "text" "self" should be "visible" + + @id381 @mainnet + Scenario Outline: Verify label "out" for Account info on Account page + When I go to page "/address/0x9CE4Feb41669BbD3FAA50EbbB6ffF85FC5Dec16C" + Then Element with "text" "out" should be "visible" + + @id380 @mainnet + Scenario: Verify label "in" for Account info on Account page + Given I go to page "/address/0x0000000000000000000000000000000000000000" + # When I click by text "Show more transactions ->" + Then Element with "text" "in" should be "visible" + + @id382 @mainnet + Scenario: Verify label "self" for Account info on Account page + Given I go to page "/address/0x94124252B5D343AB6E950A15982599ee1AADE660" + # When I click by text "Show more transactions ->" + Then Element with "text" "self" should be "visible" + + @id580 @id578 @id619 @testnet @testnetSmokeSuite + Scenario Outline: Verify label "<label name>" for method column on Contract page + Given I go to page "<Page>" + Then Column with "Method" name includes "<label name>" cell + + Examples: + | Page | label name | + | /address/0x8128948710d00d2698BAB65B5DBed7e839C71434 | multiTransfer | + # | /address/0x574343B3d1544477f2C4dF38c2Ef720Ab33e782b | 0xcd72250d | + # | /address/0x4A80888F58D004c5ef2013d2Cf974f00f42DD934 | mintNFT | + + @id580 @id578 @id619 @mainnet + Scenario Outline: Verify label "<label name>" for method column on Contract page + Given I go to page "<Page>" + Then Column with "Method" name includes "<label name>" cell + + Examples: + | Page | label name | + | /address/0x0000000000000000000000000000000000008006 | create | + + @id258 @testnet @testnetSmokeSuite + Scenario Outline: Check data type dropdown for "<Row>" and select "<Value>" + Given I go to page "/tx/0x4f7406f5565d875ce1a2ebb7c83f582e9795294ad57276eae3909b59537ab051" + When I select "Logs" tab on "Transaction" page + When I click on datatype dropdown of "<Row>" row + When I click by text "<Value>" + Then Check the element contains text "<Value>" + + Examples: + | Row | Value | + | Topics | Hex | + | Topics | Number | + | Topics | Text | + | Topics | Address | + | Data | Hex | + | Data | Number | + | Data | Text | + | Data | Address | + + @id258 @mainnet + Scenario Outline: Check data type dropdown for "<Row>" and select "<Value>" + Given I go to page "/tx/0xc598db886275ce5d7900fbfb6797afb3231a117446d91eb0dd8cf69addf90779" + When I select "Logs" tab on "Transaction" page + When I click on datatype dropdown of "<Row>" row + When I click by text "<Value>" + Then Check the element contains text "<Value>" + + Examples: + | Row | Value | + | Topics | Hex | + | Topics | Number | + | Topics | Text | + | Topics | Address | + | Data | Hex | + | Data | Number | + | Data | Text | + | Data | Address | + + @id587 @testnetSmokeSuite + Scenario: Check Processed status component for Transaction page + Given I go to the first Processed transaction link + Then Verify the badge with "Processed" status is visible + Then Verify the badge with "Sending" status is visible + Then Sending spinner should be visible + Then Element with "text" "Sending" should be "visible" + # Then Status component color with "Sending" status should be "grey" + + @id588 @testnet @testnetSmokeSuite + Scenario: Check Processed status component for Transaction page + Given I go to page "/tx/0x51cef3cdc8237635c6151b664c1925281766fabe2fa3d60e63f1369829c2f881" + Then Verify the badge with "Processed" status is visible + Then Verify the badge with "Executed" status is visible + Then Element with "text" "Executed" should be "visible" + # Then Status component color with "Executed" status should be "green" + + @id589 @testnet @testnetSmokeSuite + Scenario: Check Failed status component for Transaction page + Given I go to page "/tx/0xb556d6cd77cf37002c668156b0ea7a1c18050decc1c99a67d6aa3b214647d2ae" + Then Verify the badge with "Failed" status is visible + Then Element with "text" "Failed" should be "visible" + # Then Status component color with "failed" status should be "red" + + @id588 @mainnet + Scenario: Check Verified status component for Transaction page + Given I go to page "/tx/0xec06ab90e8cbada2b205874567504ceed9e005df452a997472823a8b59cb30ec" + Then Verify the badge with "Processed" status is visible + Then Verify the badge with "Executed" status is visible + Then Element with "text" "Executed" should be "visible" + # Then Status component color with "Executed" status should be "green" + + @id589 @mainnet + Scenario: Check Failed status component for Transaction page + Given I go to page "/tx/0x2eb8b2afc76783e09fbebfc2db085e4ebd4d0944ddd241b4a35bc2e03de23fb5" + Then Verify the badge with "Failed" status is visible + Then Element with "text" "Failed" should be "visible" + # Then Status component color with "failed" status should be "red" + \ No newline at end of file diff --git a/packages/app/tests/e2e/features/artifacts/artifactsSet4.feature b/packages/app/tests/e2e/features/artifacts/artifactsSet4.feature new file mode 100644 index 0000000000..72f025485f --- /dev/null +++ b/packages/app/tests/e2e/features/artifacts/artifactsSet4.feature @@ -0,0 +1,84 @@ +@artifactsSet4 +Feature: Main Page + + Background: + Given I am on main page + + @id261 @mainnet + Scenario Outline: Verify column "<Column name>" for Balances on Account page + When I go to page "/address/0x73f49Abb4f830270866B93cf26753ea8074CFE19" + Then Column with "<Column name>" name is visible + + Examples: + | Column name | + | Asset | + | Balance | + | Token Address | + + # @id267 #Temporary deprecated + # Scenario Outline: Verify column "<Column name>" for Balances on Сontraсt page + # When I go to page "/address/0x0000000000000000000000000000000000008006" + # Then Column with "<Column name>" name is visible + + # Examples: + # | Column name | + # | Asset | + # | Balance | + # | Token Address | + + @id260 @testnet + Scenario Outline: Verify row "<Row>" for Account info on Account page + When I go to page "/address/0x8f0F33583a56908F7F933cd6F0AaE382aC3fd8f6" + Then Element with "text" "<Row>" should be "visible" + + Examples: + | Row | + | Address | + | Committed nonce | + | Verified nonce | + + @id260 @mainnet + Scenario Outline: Verify row "<Row>" for Account info on Account page + When I go to page "/address/0x9671075872d0b9f797cfF2b05586710Ea34e88E0" + Then Element with "text" "<Row>" should be "visible" + + Examples: + | Row | + | Address | + | Committed nonce | + | Verified nonce | + + @id262 @testnet + Scenario Outline: Verify Latest transaction "<Column name>" on Account page + Given I go to page "/address/0x8f0F33583a56908F7F933cd6F0AaE382aC3fd8f6" + # And Element with "text" "Show more transactions ->" should be "clickable" + Then Column with "<Column name>" name is visible + + Examples: + | Column name | + | Status | + | Transaction Hash | + | Age | + | From | + | To | + | Method | + | Value | + | Fee | + + @id262 @mainnet + Scenario Outline: Verify Latest transaction "<Column name>" on Account page + Given I go to page "/address/0x9671075872d0b9f797cfF2b05586710Ea34e88E0" + # And Element with "text" "Show more transactions ->" should be "clickable" + Then Column with "<Column name>" name is visible + + Examples: + | Column name | + | Status | + | Transaction Hash | + | Age | + | From | + | To | + | Method | + | Value | + | Fee | + diff --git a/packages/app/tests/e2e/features/copying.feature b/packages/app/tests/e2e/features/copying.feature new file mode 100644 index 0000000000..332a60ee29 --- /dev/null +++ b/packages/app/tests/e2e/features/copying.feature @@ -0,0 +1,188 @@ +@copying @regression + +Feature: Copying + + Background: + Given I am on main page + + @id209:III @id230:II + Scenario Outline: Check copy button action on "<Page>" page + Given I go to page "<Page>" + When I click on the first copy button + Then Element with "text" "Copied!" should be "visible" + + Examples: + | Page | + | /tx/0xe84dcdaa4e40c9899763c8f55255376fd77f3a588be0fe0afa69f153a0ae3f10 | + | /address/0xed7175341f123f7718aBaCF1702d6980CFc08784 | + | /tx/0xe239f4cc4ddbaad475d0ef3e23114a89387864bcde5da5b5ca4d2c140bfc4bc4 | + | /block/1 | + | /address/0x574343B3d1544477f2C4dF38c2Ef720Ab33e782b | + + @id239 @testnet + Scenario Outline: Check "<Row>" hashes copying for Transaction page + Given I go to page "/tx/0xe7a91cc9b270d062328ef995e0ef67195a3703d43ce4e1d375f87d5c64e51981" + When I click on the copy button with "<Row>" row on "Transaction" page + And Element with "text" "Copied!" should be "visible" + Then Clipboard contains "<Text>" value + + Examples: + | Row | Text | + | Transaction Hash | 0xe7a91cc9b270d062328ef995e0ef67195a3703d43ce4e1d375f87d5c64e51981 | + | From | 0x8f0F33583a56908F7F933cd6F0AaE382aC3fd8f6 | + | To | 0x0faF6df7054946141266420b43783387A78d82A9 | + | Input data | 0xa9059cbb0000000000000000000000000faf6df7054946141266420b43783387a78d82a90000000000000000000000000000000000000000000000000000000000989680 | + + @id239 @mainnet + Scenario Outline: Check "<Row>" hashes copying for Transaction page + Given I go to page "/tx/0x97e3d593e03e764df14e352e73ba6af487faf8d04bd65a1fd3d55208b6e0d972" + When I click on the copy button with "<Row>" row on "Transaction" page + And Element with "text" "Copied!" should be "visible" + Then Clipboard contains "<Text>" value + + Examples: + | Row | Text | + | Transaction Hash | 0x97e3d593e03e764df14e352e73ba6af487faf8d04bd65a1fd3d55208b6e0d972 | + | From | 0xE1D6FA366B480Ea437419333333b31366C8a158F | + | To | 0xA0924Cc8792836F48D94759c76D32986BC74B54c | + | Input data | 0x2e4dbe8f000000000000000000000000000000000000000000000000000000000001bc45000000000000000000000000d613effb65b11e301f1338f71013b390985380300000000000000000000000000000000000000000000000000000000008116e2b000000000000000000000000000000000000000000000000000000000001bc4500000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000004123131c5b6401f42c98f7e84ae00ef577fa5e580f313a83a135c8fc893103ec884bc0bbd4b5d3ef32ec774eb20529ffebb7aae5d250d2568cff65e5ac412f99f21c00000000000000000000000000000000000000000000000000000000000000 | + + @id257 @testnet + Scenario Outline: Check "<Row>" hashes copying for Logs tab on Transaction page + Given I go to page "/tx/0xe7a91cc9b270d062328ef995e0ef67195a3703d43ce4e1d375f87d5c64e51981" + And I select "Logs" tab on "Transaction" page + When I click on the copy button with "<Row>" row on "Transaction" page + And Element with "text" "Copied!" should be "visible" + Then Clipboard contains "<Text>" value + + Examples: + | Row | Text | + | Address | 0x000000000000000000000000000000000000800A | + | Topics | 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef | + | Data | 855606582320872 | + + @id257:I @mainnet + Scenario Outline: Check "<Row>" hashes copying for Logs tab on Transaction page + Given I go to page "/tx/0x97e3d593e03e764df14e352e73ba6af487faf8d04bd65a1fd3d55208b6e0d972" + And I select "Logs" tab on "Transaction" page + When I click on the copy button with "<Row>" row on "Transaction" page + And Element with "text" "Copied!" should be "visible" + Then Clipboard contains "<Text>" value + + Examples: + | Row | Text | + | Address | 0x000000000000000000000000000000000000800A | + | Topics | 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef | + | Data | 490189750000000 | + + @id269:I + Scenario Outline: Check Address hashes copying for Contract page + Given I go to page "/address/0x000000000000000000000000000000000000800A" + When I click on the copy button with "<Row>" row on "Contract" page + And Element with "text" "Copied!" should be "visible" + Then Clipboard contains "<Text>" value + + Examples: + | Row | Text | + | Address | 0x000000000000000000000000000000000000800A | + + @id269:II @id266:II @id244:II + Scenario Outline: Check hashes copying from title on Contract/Block/Accounts page + Given I go to page "<Address>" + When I click on the copy button for page title + And Element with "text" "Copied!" should be "visible" + Then Clipboard contains "<Text>" value + + Examples: + | Address | Text | + | /address/0x0faF6df7054946141266420b43783387A78d82A9 | 0x0faF6df7054946141266420b43783387A78d82A9 | + | /block/1 | 1 | + | /address/0x8f0F33583a56908F7F933cd6F0AaE382aC3fd8f6 | 0x8f0F33583a56908F7F933cd6F0AaE382aC3fd8f6 | + + + @id272 + Scenario Outline: Check hashes copying for Contracts tab on Contracts page + Given I go to page "/address/0x000000000000000000000000000000000000800A" + When I select "Contract" tab on "Contract" page + And I click on the copy button for deployed bytecode + Then Clipboard contains "<Text>" value + + Examples: + | Text | + | 0x00030000000000020005000000000002000200000001035500000060011002700000010f0010019d000100000000001f0000008001000039000000400010043f00000001012001900000003c0000c13d0000000002000031000000040120008c0000029c0000413d0000000201000367000000000301043b000000e003300270000001110430009c000000440000a13d000001120430009c000000650000a13d000001130430009c000000e80000613d000001140130009c0000028b0000613d000001150130009c0000029c0000c13d0000000001000416000000000110004c0000029c0000c13d000000040100008a00000000011000310000011d02000041000000200310008c000000000300001900000000030240190000011d01100197000000000410004c000000000200a0190000011d0110009c00000000010300190000000001026019000000000110004c0000029c0000c13d00000004010000390000000201100367000000000101043b0000011e011001970000000000100435000000200000043f0000000001000019043803fe0000040f000000000101041a000000400200043d00000000001204350000010f010000410000010f0320009c000000000102401900000040011002100000011f011001c7000004390001042e0000000001000416000000000110004c0000029c0000c13d0000002001000039000001000010044300000120000004430000011001000041000004390001042e000001180130009c000000a20000213d0000011b0130009c0000020b0000613d0000011c0130009c0000029c0000c13d0000000001000416000000000110004c0000029c0000c13d000000040100008a00000000011000310000011d02000041000000000310004c000000000300001900000000030240190000011d01100197000000000410004c000000000200a0190000011d0110009c00000000010300190000000001026019000000000110004c0000029c0000c13d0000000101000039000000000101041a000000400200043d00000000001204350000010f010000410000010f0320009c000000000102401900000040011002100000011f011001c7000004390001042e000001160430009c000002330000613d000001170130009c0000029c0000c13d0000000001000416000000000110004c0000029c0000c13d000000040100008a00000000011000310000011d02000041000000600310008c000000000300001900000000030240190000011d01100197000000000410004c000000000200a0190000011d0110009c00000000010300190000000001026019000000000110004c0000029c0000c13d00000002010003670000000402100370000000000402043b0000011e0240009c0000029c0000213d0000002402100370000000000202043b0000011e052001970000011e0220009c0000029c0000213d00000000020004110000004401100370000000000301043b000080060120008c0000033e0000613d000000090100008a000000000112016f000080010110008c0000033e0000613d000000400100043d00000064021000390000012703000041000000000032043500000044021000390000012803000041000000000032043500000024021000390000003e030000390000000000320435000001290200004100000000002104350000000402100039000000200300003900000000003204350000010f020000410000010f0310009c000000000102801900000040011002100000012a011001c70000043a00010430000001190130009c000002710000613d0000011a0130009c0000029c0000c13d0000000001000416000000000110004c0000029c0000c13d000000040100008a00000000011000310000011d02000041000000400310008c000000000300001900000000030240190000011d01100197000000000410004c000000000200a0190000011d0110009c00000000010300190000000001026019000000000110004c0000029c0000c13d00000002010003670000000402100370000000000402043b0000011e0240009c0000029c0000213d0000002401100370000000000501043b0000000001000411000080010110008c000002b80000c13d0000000101000039000000000301041a0000000002530019000000000332004b000000000300001900000001030040390000000103300190000000e20000c13d000400000005001d000000000021041b0000000000400435000000200000043f0000010f0100004100000000020004140000010f0320009c0000000001024019000000c00110021000000122011001c70000801002000039000500000004001d043804330000040f000000050500002900000001022001900000029c0000613d000000000101043b000000000301041a00000004040000290000000002430019000000000332004b0000000003000019000000010300403900000001033001900000038b0000613d000001310100004100000000001004350000001101000039000000040010043f00000132010000410000043a00010430000000040320008a0000011d04000041000000400530008c000000000500001900000000050440190000011d03300197000000000630004c000000000400a0190000011d0330009c00000000030500190000000003046019000000000330004c0000029c0000c13d0000000403100370000000000803043b0000011e0380009c0000029c0000213d0000002403100370000000000303043b000001210430009c0000029c0000213d00000023043000390000011d05000041000000000624004b000000000600001900000000060580190000011d022001970000011d04400197000000000724004b0000000005008019000000000224013f0000011d0220009c00000000020600190000000002056019000000000220004c0000029c0000c13d0000000402300039000000000121034f000000000101043b000001210210009c0000026b0000213d000000bf02100039000000200500008a000000000252016f000001210420009c0000026b0000213d000000400020043f000000800010043f000000240330003900000000023100190000000004000031000000000242004b0000029c0000213d000400000005001d0000001f0210018f000000020330036700000005041002720000012b0000613d00000000050000190000000506500210000000000763034f000000000707043b000000a00660003900000000007604350000000105500039000000000645004b000001230000413d000500000008001d000000000520004c0000013b0000613d0000000504400210000000000343034f0000000302200210000000a004400039000000000504043300000000052501cf000000000525022f000000000303043b0000010002200089000000000323022f00000000022301cf000000000252019f0000000000240435000000a00110003900000000000104350000000001000416000300000001001d00000000010004100000000000100435000000200000043f0000010f0100004100000000020004140000010f0320009c0000000001024019000000c00110021000000122011001c70000801002000039043804330000040f0000000102200190000000050400002900000004070000290000029c0000613d000000000101043b000000000201041a00000003090000290000000002920049000000000021041b0000000101000039000000000201041a0000000002920049000000000021041b0000012302000041000000400100043d000000200310003900000000002304350000006002400210000000240310003900000000002304350000000008000411000000600280021000000058031000390000000000230435000000380210003900000000009204350000006c03100039000000800200043d000000000420004c000001700000613d00000000040000190000000005340019000000a006400039000000000606043300000000006504350000002004400039000000000524004b000001690000413d000000000332001900000000000304350000004c0320003900000000003104350000008b02200039000000000272016f000000000a12001900000000022a004b000000000200001900000001020040390000012103a0009c0000026b0000213d00000001022001900000026b0000c13d000100000008001d0000004000a0043f000001240200004100000000002a04350000000402a000390000002003000039000000000032043500000000020104330000002403a0003900000000002304350000004403a00039000000000420004c000001930000613d000000000400001900000000053400190000002004400039000000000614001900000000060604330000000000650435000000000524004b0000018c0000413d000000000132001900000000000104350000001f01200039000000000171016f0000010f020000410000010f03a0009c000000000302001900000000030a4019000000400330021000000044011000390000010f0410009c00000000010280190000006001100210000000000131019f00000000030004140000010f0430009c0000000002034019000000c002200210000000000112019f000080080200003900020000000a001d0438042e0000040f000000020a000029000000000301001900000060033002700000010f03300197000000200430008c000000200400003900000000040340190000001f0540018f0000000506400272000001bc0000613d0000000007000019000000050870021000000000098a0019000000000881034f000000000808043b00000000008904350000000107700039000000000867004b000001b40000413d000000000750004c000001cb0000613d0000000506600210000000000761034f00000000066a00190000000305500210000000000806043300000000085801cf000000000858022f000000000707043b0000010005500089000000000757022f00000000055701cf000000000585019f0000000000560435000100000003001f0000000102200190000003d80000613d0000001f01400039000000600210018f0000000001a20019000000000221004b00000000020000190000000102004039000001210410009c000000050400002900000003050000290000026b0000213d00000001022001900000026b0000c13d000000400010043f000000200230008c0000029c0000413d00000020021000390000004003000039000000000032043500000000005104350000004003100039000000800200043d000000000023043500000060031000390000011e06400197000000000420004c000001f00000613d00000000040000190000000005340019000000a007400039000000000707043300000000007504350000002004400039000000000524004b000001e90000413d000000000332001900000000000304350000007f022000390000000403000029000000000232016f0000010f030000410000010f0410009c000000000103801900000040011002100000010f0420009c00000000020380190000006002200210000000000112019f00000000020004140000010f0420009c0000000002038019000000c002200210000000000112019f00000125011001c70000800d020000390000000303000039000001260400004100000001050000290438042e0000040f0000000101200190000003d60000c13d0000029c0000013d0000000001000416000000000110004c0000029c0000c13d000000040100008a00000000011000310000011d02000041000000000310004c000000000300001900000000030240190000011d01100197000000000410004c000000000200a0190000011d0110009c00000000010300190000000001026019000000000110004c0000029c0000c13d000000c001000039000000400010043f0000000501000039000000800010043f0000013501000041000000a00010043f0000002001000039000000400200043d000500000002001d00000000021204360000008001000039043804100000040f000000050400002900000000014100490000010f020000410000010f0310009c00000000010280190000010f0340009c000000000204401900000040022002100000006001100210000000000121019f000004390001042e000000040220008a0000011d03000041000000200420008c000000000400001900000000040340190000011d02200197000000000520004c000000000300a0190000011d0220009c00000000020400190000000002036019000000000220004c0000029c0000c13d0000000401100370000000000401043b0000011e0140009c0000029c0000213d0000000001000416000400000001001d00000000010004100000000000100435000000200000043f0000010f0100004100000000020004140000010f0320009c0000000001024019000000c00110021000000122011001c70000801002000039000500000004001d043804330000040f000000050400002900000001022001900000029c0000613d000000000101043b000000000201041a00000004050000290000000002520049000000000021041b0000000101000039000000000201041a0000000002520049000000000021041b0000012302000041000000400100043d0000002003100039000000000023043500000060024002100000002403100039000000000023043500000038021000390000000000520435000000380200003900000000002104350000012f0210009c000002ca0000413d000001310100004100000000001004350000004101000039000000040010043f00000132010000410000043a000104300000000001000416000000000110004c0000029c0000c13d000000040100008a00000000011000310000011d02000041000000000310004c000000000300001900000000030240190000011d01100197000000000410004c000000000200a0190000011d0110009c00000000010300190000000001026019000000000110004c0000029c0000c13d000000400100043d000000120200003900000000002104350000010f020000410000010f0310009c000000000102801900000040011002100000011f011001c7000004390001042e0000000001000416000000000110004c0000029c0000c13d000000040100008a00000000011000310000011d02000041000000000310004c000000000300001900000000030240190000011d01100197000000000410004c000000000200a0190000011d0110009c00000000010300190000000001026019000000000110004c0000029e0000613d00000000010000190000043a00010430000000400100043d000400000001001d043804230000040f00000004030000290000002001300039000001200200004100000000002104350000000301000039000000000013043500000000010300190000002002000039000000400300043d000500000003001d0000000002230436043804100000040f000000050400002900000000014100490000010f020000410000010f0310009c00000000010280190000010f0340009c000000000204401900000040022002100000006001100210000000000121019f000004390001042e000000400100043d00000044021000390000013303000041000000000032043500000024021000390000001f030000390000000000320435000001290200004100000000002104350000000402100039000000200300003900000000003204350000010f020000410000010f0310009c000000000102801900000040011002100000012e011001c70000043a000104300000006007100039000000400070043f00000124020000410000000000270435000000640210003900000020030000390000000000320435000000840310003900000000020104330000000000230435000000a403100039000000000420004c000002df0000613d000000000400001900000000053400190000002004400039000000000614001900000000060604330000000000650435000000000524004b000002d80000413d000000000132001900000000000104350000001f01200039000000200200008a000000000121016f0000010f020000410000010f0370009c00000000030200190000000003074019000000400330021000000044011000390000010f0410009c00000000010280190000006001100210000000000131019f00000000030004140000010f0430009c0000000002034019000000c002200210000000000112019f0000800802000039000300000007001d0438042e0000040f000000030a000029000000000301001900000060033002700000010f03300197000000200430008c000000200400003900000000040340190000001f0540018f0000000506400272000003090000613d0000000007000019000000050870021000000000098a0019000000000881034f000000000808043b00000000008904350000000107700039000000000867004b000003010000413d000000000750004c000003180000613d0000000506600210000000000761034f00000000066a00190000000305500210000000000806043300000000085801cf000000000858022f000000000707043b0000010005500089000000000757022f00000000055701cf000000000585019f0000000000560435000100000003001f0000000102200190000003650000613d0000001f01400039000000600210018f0000000001a20019000000000221004b00000000020000190000000102004039000001210410009c000000050500002900000004040000290000026b0000213d00000001022001900000026b0000c13d000000400010043f000000200230008c0000029c0000413d00000000004104350000010f0200004100000000030004140000010f0430009c00000000030280190000010f0410009c00000000010280190000004001100210000000c002300210000000000112019f0000012b011001c70000011e065001970000800d020000390000000303000039000001300400004100000000050004110438042e0000040f0000000101200190000003d60000c13d0000029c0000013d000400000003001d0000000000400435000000200000043f0000010f0100004100000000020004140000010f0320009c0000000001024019000000c00110021000000122011001c70000801002000039000500000004001d000300000005001d043804330000040f000000050300002900000001022001900000029c0000613d000000000101043b000000000201041a0000000401000029000000000112004b0000039f0000813d000000400100043d00000044021000390000012d03000041000000000032043500000024021000390000001f030000390000000000320435000001290200004100000000002104350000000402100039000000200300003900000000003204350000010f020000410000010f0310009c000000000102801900000040011002100000012e011001c70000043a00010430000000400200043d0000001f0430018f0000000503300272000003720000613d000000000500001900000005065002100000000007620019000000000661034f000000000606043b00000000006704350000000105500039000000000635004b0000036a0000413d000000000540004c000003810000613d0000000503300210000000000131034f00000000033200190000000304400210000000000503043300000000054501cf000000000545022f000000000101043b0000010004400089000000000141022f00000000014101cf000000000151019f00000000001304350000010f0100004100000001030000310000010f0430009c00000000030180190000010f0420009c000000000102401900000040011002100000006002300210000000000112019f0000043a00010430000000000021041b000000400100043d00000000004104350000010f0200004100000000030004140000010f0430009c00000000030280190000010f0410009c00000000010280190000004001100210000000c002300210000000000112019f0000012b011001c70000800d02000039000000020300003900000134040000410438042e0000040f00000001012001900000029c0000613d000003d60000013d000200000002001d0000000000300435000000200000043f0000010f0100004100000000020004140000010f0320009c0000000001024019000000c00110021000000122011001c70000801002000039043804330000040f000000030300002900000001022001900000029c0000613d000000040200002900000002040000290000000002240049000000000101043b000000000021041b00000000003004350000010f0100004100000000020004140000010f0320009c0000000001024019000000c00110021000000122011001c70000801002000039043804330000040f0000000306000029000000050500002900000001022001900000029c0000613d000000000101043b000000000201041a00000004030000290000000002320019000000000021041b000000400100043d00000000003104350000010f0200004100000000030004140000010f0430009c00000000030280190000010f0410009c00000000010280190000004001100210000000c002300210000000000112019f0000012b011001c70000800d0200003900000003030000390000012c040000410438042e0000040f00000001012001900000029c0000613d0000000001000019000004390001042e000000400200043d0000001f0430018f0000000503300272000003e50000613d000000000500001900000005065002100000000007620019000000000661034f000000000606043b00000000006704350000000105500039000000000635004b000003dd0000413d000000000540004c000003f40000613d0000000503300210000000000131034f00000000033200190000000304400210000000000503043300000000054501cf000000000545022f000000000101043b0000010004400089000000000141022f00000000014101cf000000000151019f00000000001304350000010f0100004100000001030000310000010f0430009c00000000030180190000010f0420009c000000000102401900000040011002100000006002300210000000000112019f0000043a000104300000010f0200004100000000030004140000010f0430009c00000000030280190000010f0410009c00000000010280190000004001100210000000c002300210000000000112019f00000122011001c70000801002000039043804330000040f00000001022001900000040e0000613d000000000101043b000000000001042d00000000010000190000043a0001043000000000030104330000000002320436000000000430004c0000041c0000613d000000000400001900000000052400190000002004400039000000000614001900000000060604330000000000650435000000000534004b000004150000413d000000000123001900000000000104350000001f01300039000000200300008a000000000131016f0000000001120019000000000001042d000001360210009c000004280000813d0000004001100039000000400010043f000000000001042d000001310100004100000000001004350000004101000039000000040010043f00000132010000410000043a0001043000000431002104210000000102000039000000000001042d0000000002000019000000000001042d00000436002104230000000102000039000000000001042d0000000002000019000000000001042d0000043800000432000004390001042e0000043a00010430000000000000000000000000000000000000000000000000000000000000000000000000ffffffff00000002000000000000000000000000000000400000010000000000000000000000000000000000000000000000000000000000000000000000000051cff8d80000000000000000000000000000000000000000000000000000000084bc3eaf0000000000000000000000000000000000000000000000000000000084bc3eb00000000000000000000000000000000000000000000000000000000095d89b41000000000000000000000000000000000000000000000000000000009cc7f7080000000000000000000000000000000000000000000000000000000051cff8d900000000000000000000000000000000000000000000000000000000579952fc00000000000000000000000000000000000000000000000000000000313ce56600000000000000000000000000000000000000000000000000000000313ce5670000000000000000000000000000000000000000000000000000000040c10f190000000000000000000000000000000000000000000000000000000006fdde030000000000000000000000000000000000000000000000000000000018160ddd8000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffffffffffffffffffff00000000000000000000000000000000000000200000000000000000000000004554480000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffff02000000000000000000000000000000000000400000000000000000000000006c0960f90000000000000000000000000000000000000000000000000000000062f84b24000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000c405fe8958410bbaf0c73b7a0c3e20859e86ca168a4c9b0def9c54d2555a306b616c206163636573732063616e2063616c6c2074686973206d6574686f6400004f6e6c792073797374656d20636f6e747261637473207769746820737065636908c379a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000840000000000000000000000000200000000000000000000000000000000000020000000000000000000000000ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef5472616e7366657220616d6f756e7420657863656564732062616c616e6365000000000000000000000000000000000000000064000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffa02717ead6b9200dd235aad468c9809ea400fe33ac69b5bfaa6d3e90fc922b63984e487b7100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002400000000000000000000000043616c6c61626c65206f6e6c792062792074686520626f6f746c6f61646572000f6798a560793a54c3bcfe86a93cde1e73087d944c0ea20544137d41213968854574686572000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffc000000000000000000000000000000000000000000000000000000000000000006487144452ecab754e690468f49e4e530cd29f0c84820730e6499b5510870110 | + + @id266:I @testnet + Scenario Outline: Check "<Row>" hashes copying on Block page + Given I go to page "/block/1" + When I click on the copy button with "<Row>" row on "Block" page + And Element with "text" "Copied!" should be "visible" + Then Clipboard contains "<Text>" value + + Examples: + | Row | Text | + | Root hash | 0x51f81bcdfc324a0dff2b5bec9d92e21cbebc4d5e29d3a3d30de3e03fbeab8d7f | + | Commit tx hash | 0x6ad6a118e09a27e39ee57c63e812953788de4974987c76bc954c14a8c32688e8 | + | Prove tx hash | 0xfbd3a89cee83e4f28999bc8fd5e96d133b7ebc367d5c7026f173d21687998379 | + | Execute tx hash | 0x5131c1bb47dca3d42ccdfd12d1ab7224cbb88fb9ad91b94e2da26631602f6fab | + + @id266:III @mainnet + Scenario Outline: Check "<Row>" hashes copying on Block page + Given I go to page "/block/1" + When I click on the copy button with "<Row>" row on "Block" page + And Element with "text" "Copied!" should be "visible" + Then Clipboard contains "<Text>" value + + Examples: + | Row | Text | + | Root hash | 0x51f81bcdfc324a0dff2b5bec9d92e21cbebc4d5e29d3a3d30de3e03fbeab8d7f | + | Commit tx hash | 0x33143afba6c91f77d18b0d7a50248e6255461ec0e0cd80a06d3bd86f2686768c | + | Prove tx hash | 0x424cdbfb877178a909fbbe6dca6ef131a752e6c91c8b24470d919e30c06e3692 | + | Execute tx hash | 0x51425089db3b2ce38b1893ec2f1dc23e3f5db8e9f48f06bb624e99d77fe76aca | + + @id244:I + Scenario Outline: Check account address hashes copying for Account page + Given I go to page "/address/0x000000000000000000000000000000000000800A" + When I click on the copy button with "<Row>" row on "Account" page + And Element with "text" "Copied!" should be "visible" + Then Clipboard contains "<Text>" value + + Examples: + | Row | Text | + | Address | 0x000000000000000000000000000000000000800A | +# related to ZKF-2551. It needs to be uncommented after the fix of the main issue. +# @id244:II +# Scenario: Check token address hashes copying for Account page +# Given I go to page "/address/0xE60EB4Bbd3F8EA522CBFA8025b8763442FaDe55e" +# When I click on the copy button for the first token asset +# And Element with "text" "Copied!" should be "visible" +# Then Clipboard value is not empty + + @id275 @testnet @testnetSmokeSuite + Scenario Outline: Check "<Row>" hashes copying for Tokens page + Given I go to page "/tokenlist" + When I click on the copy button with "<Row>" row on "Tokens" page + And Element with "text" "Copied!" should be "visible" + Then Clipboard contains "<Text>" value + + Examples: + | Row | Text | + | ETH | 0x000000000000000000000000000000000000800A | + | USDC | 0x0faF6df7054946141266420b43783387A78d82A9 | + | DAI | 0x3e7676937A7E96CFB7616f255b9AD9FF47363D4b | + | wBTC | 0x0BfcE1D53451B4a8175DD94e6e029F7d8a701e9c | + | LINK | 0x40609141Db628BeEE3BfAB8034Fc2D8278D0Cc78 | + + @id275:I @mainnet + Scenario Outline: Check "<Row>" hashes copying for Tokens page + Given I go to page "/tokenlist" + When I click on the copy button with "<Row>" row on "Tokens" page + And Element with "text" "Copied!" should be "visible" + Then Clipboard contains "<Text>" value + + Examples: + | Row | Text | + | ETH | 0x000000000000000000000000000000000000800A | + | USDC | 0x3355df6D4c9C3035724Fd0e3914dE96A5a83aaf4 | + | MUTE | 0x0e97C7a0F8B2C9885C8ac9fC6136e829CbC21d42 | + | COMBO | 0xc2B13Bb90E33F1E191b8aA8F44Ce11534D5698E3 | + | PERP | 0x42c1c56be243c250AB24D2ecdcC77F9cCAa59601 | diff --git a/packages/app/tests/e2e/features/redirection/redirectionSet1.feature b/packages/app/tests/e2e/features/redirection/redirectionSet1.feature new file mode 100644 index 0000000000..5d5883cfc4 --- /dev/null +++ b/packages/app/tests/e2e/features/redirection/redirectionSet1.feature @@ -0,0 +1,175 @@ +@redirectionSet1 @regression + +Feature: Redirection + + Background: + Given I am on main page + + #Footer + @id232 + Scenario Outline: Verify redirection for "<Extra button name>" on Footer for internal links + Then I click by text "<Extra button name>" + Given New page have "<url>" address + + Examples: + | Extra button name | url | + | Docs | https://era.zksync.io/docs/dev/ | + | Terms | https://zksync.io/terms | + | Contact | https://zksync.io/contact | + + #Header + @id231 + Scenario Outline: Verify redirection for "<Icon>" social network icon on Header + When I click by element with partial href "<Icon>" + Then New page have "<url>" address + + Examples: + | Icon | url | + # discord renamed to "join" + | join | https://join.zksync.dev/ | + | twitter | https://twitter.com/zksync | + + @id251 + Scenario: Verify redirection for Documentation link + Given I click by text "Documentation" + Then New page have "https://era.zksync.io/docs/dev/" address + + @id252 + Scenario Outline: Verify redirection for "<Sub-Section>" in BE menu + Given I click by text "Block Explorer " + When I click by element with partial href "<url>" and text "<Sub-Section>" + Then Current page have "<url>" address + + Examples: + | Sub-Section | url | + | Blocks | /blocks/ | + | Transactions | /transactions/ | + + @id253:II + Scenario Outline: Verify redirection for "<Sub-Section>" in Tools menu + Given I click by text "Tools " + When I click by element with partial href "<url>" and text "<Sub-Section>" + Then Current page have "<url>" address + + Examples: + | Sub-Section | url | + | Smart Contract Verification | /contracts/verify | + # | zkEVM Debugger | /tools/debugger | + + @id253:III @featureEnv @testnet + Scenario Outline: Verify redirection for "<Sub-Section>" in Tools menu + Given I click by text "Tools " + When I click by element with partial href "<url>" and text "<Sub-Section>" + Then New page have "<url>" address + + Examples: + | Sub-Section | url | + | Portal | https://goerli.staging-portal.zksync.dev/ | + + + @id253:IIII @stagingEnv @testnet + Scenario Outline: Verify redirection for "<Sub-Section>" in Tools menu + Given I click by text "Tools " + When I click by element with partial href "<url>" and text "<Sub-Section>" + Then New page have "<url>" address + + Examples: + | Sub-Section | url | + | Portal | https://goerli.portal.zksync.io/ | + + @id253:IV @featureEnv @mainnet + Scenario Outline: Verify redirection for "<Sub-Section>" in Tools menu + Given I click by text "Tools " + When I click by element with partial href "<url>" and text "<Sub-Section>" + Then New page have "<url>" address + + Examples: + | Sub-Section | url | + | Portal | https://staging-portal.zksync.dev/ | + + + @id253:IV @stagingEnv @mainnet + Scenario Outline: Verify redirection for "<Sub-Section>" in Tools menu + Given I click by text "Tools " + When I click by element with partial href "<url>" and text "<Sub-Section>" + Then New page have "<url>" address + + Examples: + | Sub-Section | url | + | Portal | https://portal.zksync.io/ | + + #Account page + @id259 @testnet + Scenario Outline: Verify redirection from Account page after "<Artifact type>" click + Given I go to page "/address/0x851df0eDcc4109C7E620d0AAdFDB99348821EB79" + When I click on the first "<Artifact type>" link + Given Page with part address "<url>" includes ID result + + Examples: + | Artifact type | url | + | tx hash | /tx/ | + | from address | /address/ | + | to address | /address/ | + | Value | /address/ | + | Fee | /address/ | + + #Account page + @id367 @testnet + Scenario Outline: Verify L1 redirection from Account page after "<Artifact type>" click + Given I go to page "/address/0x851df0eDcc4109C7E620d0AAdFDB99348821EB79" + When I click on the first "<Artifact type>" link + Given Page with part address "<url>" includes ID result + + Examples: + | Artifact type | url | + | from address | /address/ | + | to address | /address/ | + + #Account page + @id259:I @mainnet + Scenario Outline: Verify redirection from Account page after "<Artifact type>" click + Given I go to page "/address/0xa9A17Ce49Bf3d0a7b2B633994729f057A90c8130" + When I click on the first "<Artifact type>" link + Given Page with part address "<url>" includes ID result + + Examples: + | Artifact type | url | + | tx hash | /tx/ | + | from address | /address/ | + | to address | /address/ | + | Value | /address/ | + | Fee | /address/ | + + #Account page + @id367:I @mainnet + Scenario Outline: Verify L1 redirection from Account page after "<Artifact type>" click + Given I go to page "/address/0xa9A17Ce49Bf3d0a7b2B633994729f057A90c8130" + When I click on the first "<Artifact type>" link + Given Page with part address "<url>" includes ID result + + Examples: + | Artifact type | url | + | from address | /address/ | + | to address | /address/ | + + #Block page + @id235 + Scenario Outline: Verify redirection from Block page after "<Artifact type>" click + Given I go to page "/block/13780" + When I click on the first "<Artifact type>" link + Given Page with part address "<url>" includes ID result + + Examples: + | Artifact type | url | + | tx hash | /tx/ | + | from address | /address/ | + | to address | /address/ | + | Value | /address/ | + | Fee | /address/ | + + #Blocks page + @id246 + Scenario: Verify redirection from Main page after block number click + Given I go to page "/blocks" + Given I click on the first "block number" link + Given Page with part address "/block/" includes ID result diff --git a/packages/app/tests/e2e/features/redirection/redirectionSet2.feature b/packages/app/tests/e2e/features/redirection/redirectionSet2.feature new file mode 100644 index 0000000000..cb0e600ae7 --- /dev/null +++ b/packages/app/tests/e2e/features/redirection/redirectionSet2.feature @@ -0,0 +1,145 @@ +@redirectionSet2 @regression + +Feature: Redirection + + Background: + Given I am on main page + + #Common pages part + @id276 + Scenario: Check Dashboard redirection from Account page to Main page + Given I go to page "/address/0x0000000000000000000000000000000000000000" + When I click by text "Dashboard" + Given I am on main page + + @id238 + Scenario: Check zkSync logo redirection from Account page to Main page + Given I go to page "/address/0x0000000000000000000000000000000000000000" + When I click on main logo + Given I am on main page + + #Contract page + @id268 @testnet + Scenario: Verify redirection for Contract button from Contract Page to verification interface + Given I go to page "/address/0x0faF6df7054946141266420b43783387A78d82A9" + When I select "Contract" tab on "Contract" page + And I press Verify Smart Contract button + Then Current page have "/contracts/verify?address=0x0faF6df7054946141266420b43783387A78d82A9" address + + + @id242:I @testnet @testnetSmokeSuite + Scenario Outline: Verify redirection from Contracts page after "<Artifact type>" click + Given I go to page "/address/0xAB2e93b557ef5FD04ABc6f57De6a1D1DE18924c4" + When I click on the first "<Artifact type>" link + Given Page with part address "<url>" includes ID result + + Examples: + | Artifact type | url | + | tx hash | /tx/ | + + @id242:I @mainnet + Scenario Outline: Verify redirection from Contracts page after "<Artifact type>" click + Given I go to page "/address/0x0000000000000000000000000000000000000000" + When I click on the first "<Artifact type>" link + Given Page with part address "<url>" includes ID result + + Examples: + | Artifact type | url | + | tx hash | /tx/ | + + @id242:II @testnet + Scenario: Verify redirection from Contracts page after Creator click + Given I go to page "/address/0x3e7676937A7E96CFB7616f255b9AD9FF47363D4b" + When I click by text "0xA38E...1df8" + Given Current page have "/address/0xA38EDFcc55164a59e0f33918D13a2d559BC11df8" address + + @id242:III @mainnet + Scenario: Verify redirection from Contracts page after Creator click + Given I go to page "/address/0x70B86390133d4875933bE54AE2083AAEbe18F2dA" + When I click by text "0xE593...C670" + Given Current page have "/address/0xE5939999825340E0Fec8e8a091cd695c93b4C670" address + + #Main page - Dashboard + @id234 @testnetSmokeSuite + Scenario Outline: Verify redirection for "<Artifact type>" from Main Page + Given I click on the first "<Artifact type>" link + Then Page with part address "<url>" includes ID result + + Examples: + | Artifact type | url | + | batch number | /batch/ | + | batch size | /batch/ | + | tx hash | /tx/ | + | committed blocks | /blocks/ | + | verified blocks | /blocks/ | + | transactions | /transactions/ | + + #Transaction Logs tab + @id256 @testnet + Scenario: Verify redirection for Address from Logs tab in transaction Page + Given I go to page "/tx/0x6fc015405255af17fb38f5a1408557f5f00d094e07a2f8f6af933a889d9a3330" + When I click on the first contract address + Then Page with part address "/address/" includes ID result + + #Transaction Logs tab + @id256 @mainnet + Scenario: Verify redirection for Address from Logs tab in transaction Page + Given I go to page "/tx/0xcbeff0db1300cbb5c1e5fc18a57ae46c94962eeebed7e82bc81e553ba27d0e9a" + When I click on the first contract address + Then Page with part address "/address/" includes ID result + + #Transaction page 241 - navigation, 553 - redirection + @id241 @id553 @id339 @id340 @testnet @testnetSmokeSuite + Scenario Outline: Verify redirection from Transaction page after "<Artifact type>" click + Given I go to page "/tx/0x6fc015405255af17fb38f5a1408557f5f00d094e07a2f8f6af933a889d9a3330" + When I click on the first "<Artifact type>" link + Given Page with part address "<url>" includes ID result + + Examples: + | Artifact type | url | + | block number | /block/ | + | To | /address/ | + | Fee | /address/ | + | Tokens Transferred | /address/ | + + @id241 @id553 @id339 @id340 @mainnet + Scenario Outline: Verify redirection from Transaction page after "<Artifact type>" click + Given I go to page "/tx/0xcbeff0db1300cbb5c1e5fc18a57ae46c94962eeebed7e82bc81e553ba27d0e9a" + When I click on the first "<Artifact type>" link + Given Page with part address "<url>" includes ID result + + Examples: + | Artifact type | url | + | block number | /block/ | + | To | /address/ | + | Fee | /address/ | + | Tokens Transferred | /address/ | + + #Transactions page + @id248 + Scenario Outline: Verify redirection from Transactions page after "<Artifact type>" click + Given I go to page "/transactions/" + When I click on the first "<Artifact type>" link + Given Page with part address "<url>" includes ID result + + Examples: + | Artifact type | url | + | tx hash | /tx/ | + # | initiator address | /address/ | Removed + | from address | /address/ | + | to address | /address/ | + | Value | /address/ | + | Fee | /address/ | + + + #Tokens page + @id250 @testnetSmokeSuite + Scenario Outline: Verify redirection from Tokens page after "<Artifact type>" click + Given I go to page "/tokenlist/" + When I click on the first "<Artifact type>" link + Given Page with part address "<url>" includes ID result + + Examples: + | Artifact type | url | + | token icon | /address/ | + | token address | /address/ | diff --git a/packages/app/tests/e2e/features/redirection/redirectionSet3.feature b/packages/app/tests/e2e/features/redirection/redirectionSet3.feature new file mode 100644 index 0000000000..86b97d052a --- /dev/null +++ b/packages/app/tests/e2e/features/redirection/redirectionSet3.feature @@ -0,0 +1,78 @@ +@redirectionSet3 @regression + +Feature: Redirection + + Background: + Given I am on main page + + #Block page - Pages + @id368 @testnet @testnetSmokeSuite + Scenario: Verify redirection from Block page L1 addresses to etherscan after address click + Given I go to page "/block/<Block Number>" + When I click on the first "<Artifact type>" link + Then Page with part address "<url>" includes ID result + + Examples: + | Block Number | Artifact type | url | + | 1003 | from address | /address/ | + | 1003 | to address | /address/ | + + @id368:I @mainnet + Scenario: Verify redirection from Block page L1 addresses to etherscan after address click + Given I go to page "/block/<Block Number>" + When I click on the first "<Artifact type>" link + Then Page with part address "<url>" includes ID result + + Examples: + | Block Number | Artifact type | url | + | 1003 | from address | /address/ | + | 1003 | to address | /address/ | + + #Main Page - Transaction page + @id338 @testnetSmokeSuite + Scenario: Verify redirection from Main page after latest transactions click + Given I click on the first "transaction hash" link + Given Page with part address "/tx/" includes ID result + + #Block page - Pages - Block's URL +# @id337 +# Scenario: Verify block's link from "Latest Blocks" section leads to correct URL +# Given I click on the first "block number" link +# Given Page with part address "/block/" includes ID result + + #369 Contract page - Pages - Redirection + @id369 @testnet @testnetSmokeSuite + Scenario: Verify contract link on the Contacts page + Given I go to page "/address/0x0BfcE1D53451B4a8175DD94e6e029F7d8a701e9c" + When I click on the first "from address" link + Given Page with part address "/address/" includes ID result + + @id369:I @mainnet + Scenario: Verify contract link on the Contacts page + Given I go to page "/address/0x2da10A1e27bF85cEdD8FFb1AbBe97e53391C0295" + When I click on the first "from address" link + Given Page with part address "/address/" includes ID result + + #Common - Network navigation (feature branch) - Redirection + @id561 @id562 @id563 @featureEnv @testnetSmokeSuite + Scenario Outline: Verify redirection to "<Network>" network + Given I go to page "<Initial page>" + When Set the "<Network>" value for "network" switcher + Then Current page have "<url>" address + + Examples: + | Initial page | Network | url | + | /address/0x000000000000000000000000000000000000800A | zkSync Era Testnet | /address/0x000000000000000000000000000000000000800A/?network=goerli | + | /address/0x000000000000000000000000000000000000800A | Goerli (Stage2) | /address/0x000000000000000000000000000000000000800A/?network=goerli-beta | + | /address/0x000000000000000000000000000000000000800A | zkSync Era Mainnet | /address/0x000000000000000000000000000000000000800A/?network=mainnet | + + @id561:I @id562:I @id563:I @stagingEnv + Scenario Outline: Verify redirection to "<Network>" network + Given I go to page "<Initial page>" + When Set the "<Network>" value for "network" switcher + Then Current page have "<url>" address + + Examples: + | Initial page | Network | url | + | /address/0x000000000000000000000000000000000000800A | zkSync Era Mainnet | /address/0x000000000000000000000000000000000000800A/?network=mainnet | + | /address/0x000000000000000000000000000000000000800A | zkSync Era Testnet | /address/0x000000000000000000000000000000000000800A/?network=goerli | diff --git a/packages/app/tests/e2e/features/search.feature b/packages/app/tests/e2e/features/search.feature new file mode 100644 index 0000000000..2d7c26bf67 --- /dev/null +++ b/packages/app/tests/e2e/features/search.feature @@ -0,0 +1,118 @@ +@search @regression + +Feature: Search field + + Background: + Given I am on main page + + #Addresses/Accounts + @id213 + Scenario: Search correct address / account + Given I fill the search field by "0xc24215226336d22238a20a72f8e489c005b44c4a" + When I press search button + And Title contains text "Account 0xc242...4C4A" + Then Current page have "/address/0xc24215226336d22238a20a72f8e489c005b44c4a" address + + @id214 + Scenario: Search incorrect address / account + Given I fill the search field by "0xAbebcd41ceabbc85da9bb67527f58d69ad4dfff51" + When I press search button + Then I am on main page + Then Search field color should be red + + @id226 + Scenario: Search random address / account + Given I fill the search field by "0xc24215226336d22238a20a72f8e489c005b44c41" + When I press search button + And Title contains text "Account 0xC242...4c41" + And Current page have "/address/0xc24215226336d22238a20a72f8e489c005b44c41" address + Then Element with "text" "We can’t find any balances related to this account." should be "visible" + + #Block Numbers + @id218 + Scenario: Search correct batch number + Given I fill the search field by "1" + When I press search button + Then Current page have "/batch/1" address + Then Title contains text "Batch # 1 " + + @id217 + Scenario Outline: Search incorrect batch number with "<Value>" + Given I fill the search field by "<Value>" + When I press search button + Then Search field color should be red + + Examples: + | Value | + | 341 844 | + | 341а44 | + | 341$44 | + + @id227 + Scenario: Search random batch number/inexistent + Given I fill the search field by "341844341844" + When I press search button + Then Element with "text" "Oops, we can’t find anything" should be "visible" + + #Contract hash + @id219 + Scenario: Search correct contract hash + Given I fill the search field by "0x0000000000000000000000000000000000000000" + When I press search button + Then Current page have "/address/0x0000000000000000000000000000000000000000" address + + @id220 + Scenario: Search incorrect contract hash + Given I fill the search field by "0x00000123532234232100000000000000000000" + When I press search button + Then Search field color should be red + + @id229 + Scenario: Search random contract hash / inexistent + Given I fill the search field by "0x98ddd69b2443fc67755f0901aeb9828a8a62cc61" + When I press search button + Then Element with "text" "We can’t find any balances related to this account." should be "visible" + + #TX Hash From/To + @id224:I @testnet + Scenario: Search correct tx hash + Given I fill the search field by "0x74a780a26da5751178eef9918e1383348f2281df9e259cc10e200974a2bce679" + When I press search button + Then Current page have "/tx/0x74a780a26da5751178eef9918e1383348f2281df9e259cc10e200974a2bce679" address + + @id224:II @mainnet + Scenario: Search correct tx hash + Given I fill the search field by "0x97e3d593e03e764df14e352e73ba6af487faf8d04bd65a1fd3d55208b6e0d972" + When I press search button + Then Current page have "/tx/0x97e3d593e03e764df14e352e73ba6af487faf8d04bd65a1fd3d55208b6e0d972" address + + @id223 + Scenario Outline: Search incorrect tx hash with "<Value>" + Given I fill the search field by "<Value>" + When I press search button + Then Search field color should be red + + Examples: + | Value | + | 0x!6adbc2511be767d03be088a4242d07829433e0bbb9d8a27489b971f1113ecff | + | 0x16adbc2511be767d03be088a4242d07829433e0bbb9d8a27489b971f1113ec | + | 0x16adbc2511be767d03be088a4242d07829433e0bbb9d8a27489b971f1113ec f | + | 0xW6adbc2511be767d03be088a4242d07829433e0bbb9d8a27489b971f1113ecff | + + @id228 + Scenario Outline: Search random tx hash / inexistent with "<Value>" + Given I fill the search field by "<Value>" + When I press search button + Then Element with "text" "Oops, we can’t find anything" should be "visible" + + Examples: + | Value | + | 0x16adbc2511be767d03be088a4242d07829433e0bbb9d8a27489b971f1113ecfa | + | 0x16adbc2511be767d03be088a4242d07829433e0bbb9d8a27489b971f1114ecff | + + #Empty Search field + @id225 + Scenario: Fill the search field by empty value and try to search + Given I fill the search field by "" + When I press search button + Given Search field color should be red diff --git a/packages/app/tests/e2e/src/data/data.ts b/packages/app/tests/e2e/src/data/data.ts new file mode 100644 index 0000000000..1fe9fa5a43 --- /dev/null +++ b/packages/app/tests/e2e/src/data/data.ts @@ -0,0 +1,5 @@ +export enum NetworkSwitcher { + zkSyncEraGoerli = "/?network=goerli", + goerliStage2 = "/?network=goerli-beta", + zkSyncEraMainnet = "/?network=mainnet", +} diff --git a/packages/app/tests/e2e/src/helpers/helper.ts b/packages/app/tests/e2e/src/helpers/helper.ts new file mode 100644 index 0000000000..aa4223410c --- /dev/null +++ b/packages/app/tests/e2e/src/helpers/helper.ts @@ -0,0 +1,123 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +import { Status } from "@cucumber/cucumber"; + +import { config } from "../support/config"; + +import type { ICustomWorld } from "../support/custom-world"; + +const tracesDir = config.artifactsFolder; +let element: any; +let result: any; + +export class Helper { + world: ICustomWorld; + + constructor(world: ICustomWorld) { + this.world = world; + } + + async checkElementVisible(selector: string, waitTime = config.increasedTimeout.timeout): Promise<boolean> { + let result = true; + try { + await this.world.page?.locator(selector).first().waitFor({ + state: "visible", + timeout: waitTime, + }); + } catch (e) { + console.error(e); + result = false; + } + return result; + } + + async checkElementClickable(selector: any, waitTime = config.increasedTimeout.timeout): Promise<boolean> { + let result = true; + try { + if (selector == "string") { + await this.world.page?.locator(selector).click({ trial: true, timeout: waitTime }); + } else { + await selector.click({ trial: true, timeout: waitTime }); + } + } catch (e) { + console.error(e); + result = false; + } + return result; + } + + async getScreenshotOnFail(result: any): Promise<void> { + console.log("======== " + result.status + ": " + this.world.testName); + if (result.status !== Status.PASSED) { + const image: any = await this.world.page?.screenshot({ path: tracesDir + this.world.testName + ".png" }); + return image; + } + } + + async getColorOfSelector(selector: string) { + element = await this.world.page?.locator(selector); + const color: any = await element.evaluate((el: any) => { + return window.getComputedStyle(el).getPropertyValue("background-color"); + }); + return await color; + } + + async extractTextFromElement(selector: string) { + element = await this.world.page?.locator(selector).first(); + result = await element.textContent(); + + return result; + } + + async extractHrefFromElement(selector: string) { + await this.world.page?.waitForTimeout(config.defaultTimeout.timeout); + element = await this.world.page?.locator(selector).first(); + result = await element.getAttribute("href"); + + return result; + } + + async extractIdFromElementHref(selector: string) { + element = await this.world.page?.locator(selector).first(); + await this.extractHrefFromElement(selector); + result = result.match(/[^s/]*$/g)[0]; + + return result; + } + + async getClipboardValue() { + result = await this.world.page?.evaluate(async () => { + return await navigator.clipboard.readText(); + }); + + return result; + } + + async clearClipboard() { + result = await this.world.page?.evaluate(async () => { + return await navigator.clipboard.writeText(""); + }); + + return result; + } + + async compareColors(elementColor: string, expectedColor: string) { + if (elementColor === expectedColor) { + return true; + } + } + + async checkComponentColor(color: string, selector: string) { + const colorRed = "rgb(239, 68, 68)"; + const colorGreen = "rgb(187, 247, 208)"; + const colorGrey = "rgb(229, 231, 235)"; + result = await this.getColorOfSelector(selector); + if (color === "red") { + element = await this.compareColors(result, colorRed); + } else if (color === "green") { + element = await this.compareColors(result, colorGreen); + } else if (color === "grey") { + element = await this.compareColors(result, colorGrey); + } + return element; + } +} diff --git a/packages/app/tests/e2e/src/pages/account.page.ts b/packages/app/tests/e2e/src/pages/account.page.ts new file mode 100644 index 0000000000..a6620c7ed7 --- /dev/null +++ b/packages/app/tests/e2e/src/pages/account.page.ts @@ -0,0 +1,62 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +import { BasePage } from "./base.page"; + +import { tokensIcon } from "../../testId.json"; + +import type { ICustomWorld } from "../support/custom-world"; + +let element: any; + +export class AccountPage extends BasePage { + constructor(world: ICustomWorld) { + super(world); + } + + get copyBtn() { + return "//td//*[@class='copy-button']"; + } + + get tokenIcon() { + return `${this.byTestId}${tokensIcon}`; + } + + get firstTokenAsset() { + return "//*[@class='token-symbol'][1]"; + } + + async configureTokenIcon() { + const address: any = await this.world.page?.url(); + const id: object = await address.match(/[^s/]*$/g)[0]; + const element = tokensIcon; + const configuredElement = `//td[@data-heading='Fee']//a[@data-testid='${element}']`; + if (await address.includes("blocks")) { + return configuredElement; + } else if ((await address.includes("transactions")) && id) { + return this.tokenIcon; + } else if (!id) { + return configuredElement; + } + } + + async getRowName(row: string) { + element = `//td[text()='${row}']`; + + return element; + } + + async getValueInRow(row: string, copying: boolean) { + element = copying + ? (await this.getRowName(row)) + `//..//*[@title]` + : (await this.getRowName(row)) + `/../..//*[@class='block-info-field-value']`; + + return element; + } + + async clickCopyBtnByFirstToken() { + const rowName = this.firstTokenAsset; + const copyBtn = this.copyBtn; + element = rowName + "/..//..//..//..//.." + copyBtn; + + await this.world.page?.locator(element).first().click(); + } +} diff --git a/packages/app/tests/e2e/src/pages/base.page.ts b/packages/app/tests/e2e/src/pages/base.page.ts new file mode 100644 index 0000000000..f973a529be --- /dev/null +++ b/packages/app/tests/e2e/src/pages/base.page.ts @@ -0,0 +1,226 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +import { Helper } from "../helpers/helper"; +import { config } from "../support/config"; + +import type { ICustomWorld } from "../support/custom-world"; + +let selector: any; +let element: any; +let helper: any; +let result: any; + +export class BasePage { + world: ICustomWorld; + + constructor(world: ICustomWorld) { + this.world = world; + } + + get paginationForm() { + return "//*[@aria-label='Pagination']"; + } + + async getColumnByText(text: string) { + element = `//th[text()="${text}"]`; + result = await this.world.page?.locator(element).first(); + + return result; + } + + get byTestId() { + return "data-testid="; + } + + get byDataHeading() { + return "data-heading="; + } + + async click(element: any, testId?: boolean) { + helper = new Helper(this.world); + selector = element; + if (testId) { + selector = this.byTestId + element; + } + await helper.checkElementVisible(selector); + await this.world.page?.locator(selector).first().click({ force: true, timeout: config.increasedTimeout.timeout }); + } + + async dblClick(element: any) { + await this.world.page?.locator(element).first().dblclick({ force: true, timeout: config.increasedTimeout.timeout }); + } + + async fill(element: any, text: string, testId?: boolean) { + helper = new Helper(this.world); + selector = element; + if (testId) { + selector = this.byTestId + element; + } + await helper.checkElementVisible(selector); + await this.world.page?.fill(selector, text); + } + + async clickByText(text: string) { + selector = `//*[contains(text(),'${text}')] | //*[text()='${text}'][1]`; + await this.world.page?.locator(selector).first().click(); + } + + async clickByExactText(text: string) { + selector = `text=${text}`; + await this.world.page?.locator(selector).first().click(); + } + + async getElementByText(text: string) { + element = await this.world.page?.locator(`//*[text()='${text}'][1]`).first(); + return await element; + } + + async getElementByTextInsideALogTab(text: string) { + element = await this.world.page?.locator(`//*[not(self::th)][text()='${text}'][1]`).first(); + return await element; + } + + async getElementByPartialText(text: string) { + element = await this.world.page?.locator(`//*[contains(text(),'${text}')][1]`).first(); + return await element; + } + + async getElementByPartialHref(partialHref: string) { + element = await this.world.page?.locator(`//*[contains(@href,'${partialHref}')]`); + await element.scrollIntoViewIfNeeded(); + return await element; + } + + async getElementByHrefAndText(partialHref: string, text: string) { + element = await this.world.page?.locator(`//*[text()='${text}' and contains(@href,'${partialHref}')]`).first(); + await element.scrollIntoViewIfNeeded(); + return await element; + } + + async getElementById(id: string) { + element = await this.world.page?.locator(`#${id}`); + await element.scrollIntoViewIfNeeded(); + return await element; + } + + async getElementByTestId(testid: string) { + element = await this.world.page?.locator(`${this.byTestId}${testid}`); + await element.scrollIntoViewIfNeeded(); + return await element; + } + + async getElementByTitle(title: string) { + element = await this.world.page?.locator(`//*[@title='${title}']`); + await element.scrollIntoViewIfNeeded(); + return await element; + } + + async getElementByTestIdAndText(testid: string, text: string) { + element = await this.world.page?.locator(`//*[@${this.byTestId}'${testid}' and contains(text(), '${text}')]`); + await element.scrollIntoViewIfNeeded(); + return await element; + } + + async getNetworkLayer(testid: string, selectorType: string) { + helper = new Helper(this.world); + const dataLinkId = `//*[@class='transactions-data-link-network']`; + + if (selectorType === "byTestID") { + selector = `//*[@${this.byTestId}'${testid}']/..${dataLinkId}`; + } else if (selectorType === "byText") { + selector = `//*[text()='${testid}']/../..${dataLinkId}`; + } + + await helper.checkElementVisible(selector); + const result = await helper.extractTextFromElement(selector); + + return result; + } + + async getCustomNetworkLayer(element: string) { + // This is the universal filter for any tet cases. + // It uses 'I click on the first "<Artifact type>" link' element + // in 'Page with part address "<url>" includes ID result' to set the layer. + + const slug_element = element.split("=").pop(); + //hint: just to filter L1/L2 layers in depend of tx/address + //Debug on @id367 + + if (slug_element === "from-address" || slug_element === "to-address") { + return await this.getNetworkLayer(slug_element, "byTestID"); //changed in uexp-3170-transactions + } else if ( + //hint: for these types we should hardcoded L1 layer + //Debug on @id241 + + element.includes("Commit Tx hash") || + element.includes("Execute Tx hash") || + element.includes("Prove Tx hash") + ) { + return "L1"; + } else if (element.includes("Tokens Transferred")) { + //hint: just to filter L1/L2 layers in depend of input + //Debug on @id241 + + return await this.getNetworkLayer("Tokens Transferred", "byText"); + } + + //hint: should be L2 in any other cases to use pageURL + return "L2"; + } + + async getEtherscanDomain(networkType: string) { + if (networkType.includes("goerli")) { + return "https://goerli.etherscan.io"; // Testnet + } else { + return "https://etherscan.io"; // Mainnet + } + } + + async getUrlOfTheCurrentPage( + networkLayer: string, + pageURL: string, + partRoute: string, + result: string, + networkType: string + ) { + const etherscanDomain = await this.getEtherscanDomain(networkType); + + if (networkLayer === "L1") { + await this.world.page?.waitForTimeout(config.defaultTimeout.timeout); + + return [await this.world.context?.pages()[1].url(), etherscanDomain]; + } else { + await this.world.page?.waitForURL(pageURL + partRoute + result); + + return [await this.world.page?.url(), pageURL]; + } + } + + async getElement(selector: string) { + element = await this.world.page?.locator(selector); + await element.scrollIntoViewIfNeeded(); + return await element; + } + + async returnElementByType(elementType: string, value: string) { + if (elementType === "text") { + element = await this.getElementByText(value); + } else if (elementType === "partial text") { + element = await this.getElementByPartialText(value); + } else if (elementType === "partial href") { + element = await this.getElementByPartialHref(value); + } else if (elementType === "id") { + element = await this.getElementById(value); + } else if (elementType === "testId") { + element = await this.getElementByTestId(value); + } else if (elementType === "title") { + element = await this.getElementByTitle(value); + } else if (elementType === "log tab") { + element = await this.getElementByTextInsideALogTab(value); + } + return element; + } + + public async goTo(address: string) { + await this.world.page?.goto(address); + } +} diff --git a/packages/app/tests/e2e/src/pages/block.page.ts b/packages/app/tests/e2e/src/pages/block.page.ts new file mode 100644 index 0000000000..5f7c28b7c7 --- /dev/null +++ b/packages/app/tests/e2e/src/pages/block.page.ts @@ -0,0 +1,33 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +import { BasePage } from "./base.page"; + +import { pageTitle } from "../../testId.json"; + +import type { ICustomWorld } from "../support/custom-world"; + +let element: any; + +export class BlockPage extends BasePage { + constructor(world: ICustomWorld) { + super(world); + } + get title() { + return `${this.byTestId}${pageTitle}`; + } + + get copyBtn() { + return "//td//*[@class='copy-button']"; + } + + async getRowByText(rowName: string) { + element = `//*[text()='${rowName}'][1]`; + return element; + } + + async clickCopyBtnByRow(rowName: string) { + const copyBtn = this.copyBtn; + element = (await this.getRowByText(rowName)) + "/..//.." + copyBtn; + + await this.world.page?.locator(element).first().click(); + } +} diff --git a/packages/app/tests/e2e/src/pages/contract.page.ts b/packages/app/tests/e2e/src/pages/contract.page.ts new file mode 100644 index 0000000000..4ed032d106 --- /dev/null +++ b/packages/app/tests/e2e/src/pages/contract.page.ts @@ -0,0 +1,95 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +import { BasePage } from "./base.page"; +import { MainPage } from "./main.page"; + +import { + byteCodeDropDown, + contractVerificationButton, + optimizationRadioButtons, + transactionsTable, +} from "../../testId.json"; + +import type { ICustomWorld } from "../support/custom-world"; + +let element: any; +let result: any; + +export class ContractPage extends BasePage { + constructor(world: ICustomWorld) { + super(world); + } + get transactionsTab() { + return "//button[text()='Transactions']/.."; + } + + get contractTab() { + return "//button[text()='Contract']/.."; + } + + get contractVerificationBtn() { + return contractVerificationButton; + } + + get radioButtons() { + return optimizationRadioButtons; + } + + get byteCode() { + return byteCodeDropDown; + } + + get transactionsTable() { + return transactionsTable; + } + + get title() { + return "//*[@data-testid='page-title']"; + } + + async selectTab(tabName: string) { + if (tabName === "Transactions") { + await this.click(this.transactionsTab, false); + } else if (tabName === "Contract") { + await this.click(this.contractTab, false); + } else { + console.error("Incorrect Tab name. The value should be only Contract or Transactions"); + } + } + + private async getColumnValue(testid: string, columnName: string) { + element = `//*[@data-testid='${testid}']//td[@data-heading="${columnName}"]`; + return element; + } + + async getCellValueByColumn(columnName: string, cellName: string) { + element = `//td[@data-heading="${columnName}"]//*[text()='${cellName}']`; + result = await this.world.page?.locator(element).first(); + return result; + } + + async pressContractVerificationBtn() { + await this.click(this.contractVerificationBtn, true); + } + + async clickOnByteCodeDropDown() { + await this.click(this.byteCode, true); + } + + async clickCopyBtnOnTitle() { + const mainPage = new MainPage(this.world); + const copyBtn = mainPage.copyBtn; + const title = this.title; + element = title + copyBtn; + + await this.world.page?.locator(element).first().click(); + } + + async clickCopyBtnOnByteCode() { + const mainPage = new MainPage(this.world); + const copyBtn = mainPage.copyBtn; + const row = `//*[@data-testid='${this.byteCode}']`; + element = row + copyBtn; + + await this.world.page?.locator(element).first().click(); + } +} diff --git a/packages/app/tests/e2e/src/pages/main.page.ts b/packages/app/tests/e2e/src/pages/main.page.ts new file mode 100644 index 0000000000..8ac141ba0d --- /dev/null +++ b/packages/app/tests/e2e/src/pages/main.page.ts @@ -0,0 +1,334 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +import { BasePage } from "./base.page"; +import { BlockPage } from "./block.page"; +import { ContractPage } from "./contract.page"; +import { TokensPage } from "./tokens.page"; +import { TransactionPage } from "./transaction.page"; + +import { + blocksNumber, + blocksTable, + fromAddress, + initiatorsAddress, + latestBatchesTable, + latestTransactionsTable, + toAddress, + tokenAddress, + tokensIcon, + tokensTable, + transactionsHash, + transactionsTable, +} from "../../testId.json"; + +import type { ICustomWorld } from "../support/custom-world"; + +let blockPage: BlockPage; +let tokensPage: TokensPage; +let transactionPage: TransactionPage; +let element: any; +let result: any; + +export class MainPage extends BasePage { + constructor(world: ICustomWorld) { + super(world); + } + + get title() { + return ".title"; + } + + get logo() { + return "//*[text()='zkSync']/.."; + } + + get searchField() { + return "id=search"; + } + + get searchSubmitBtn() { + return "//*[@type='submit']"; + } + + get searchFieldInvalidInput() { + return ".search-input.has-error"; + } + + get selectedNetwork() { + return "//*[@class='network-item-label']"; + } + + get selectedLanguage() { + return "//*[@class='selected-language-label']"; + } + + async setSwitcherLanguage(languageOption: string) { + return `//*[@class='language-list']//*[text()='${languageOption}']`; + } + + async setSwitcherNetwork(networkOption: string) { + return `//*[@class='network-list']//*[contains(text(), '${networkOption}')]`; + } + + get txHash() { + return "//span[@class='transactions-data-link'][1]/a"; + } + + get batchNumber() { + return "//*[@data-heading='Batch']/a"; + } + + get batchSize() { + return "//*[@data-heading='Size']/a"; + } + + get committedBlocks() { + return "//*[text()='Committed Blocks']"; + } + + get verifiedBlocks() { + return "//*[text()='Verified Blocks']"; + } + + get transactions() { + return "//*[text()='Transactions']"; + } + + get fromAddress() { + return `${this.byTestId}${fromAddress}`; + } + + get toAddress() { + return `${this.byTestId}${toAddress}`; + } + + get initiatorAddress() { + return `${this.byTestId}${initiatorsAddress}`; + } + + get tokenIcon() { + return `${this.byTestId}${tokensIcon}`; + } + + get transactionsHash() { + return `${this.byTestId}${transactionsHash}`; + } + + get tokenAddress() { + return `${this.byTestId}${tokenAddress}`; + } + + get blockNumber() { + return `${this.byTestId}${blocksNumber}`; + } + + get blocksTable() { + return blocksTable; + } + + get latestBatchesTable() { + return latestBatchesTable; + } + + get latestTransactionTable() { + return latestTransactionsTable; + } + + get tokensTable() { + return tokensTable; + } + + get transactionsTable() { + return transactionsTable; + } + + get networkStats() { + return "//*[@class='card network-stats']"; + } + + get processedTxLink() { + return `//*[td[contains(., 'Processed')]]//*[@data-testid='transactions-hash'][1]`; + } + + get copyBtn() { + return "//*[@class='copy-button'][1]"; + } + + private async getRowTable(testid: string) { + element = `//*[@data-testid='${testid}']//tbody/tr`; + return element; + } + + private async getColumnValue(testid: string, columnName: string) { + element = `//*[contains(@data-testid,'${testid}')]//*[text()='${columnName}']`; + return element; + } + + async getDropDownValue(value: string) { + element = `//label[@href]//*[contains(text(),'${value}')] | + //label[contains(text(),'${value}')]`; + return await element; + } + + async countRowsInTable(table: string) { + if (table === "Latest Batches") { + element = this.latestBatchesTable; + } else if (table === "Latest Transactions") { + element = this.latestTransactionTable; + } else if (table === "Blocks") { + element = this.blocksTable; + } else if (table === "Transactions") { + element = this.transactionsTable; + } else if (table === "Tokens") { + element = this.tokensTable; + } + + const row: string = await this.getRowTable(element); + const rows: any = await this.world.page?.locator(row); + await this.world.page?.waitForSelector(row); + const countRows: any = await rows.count(); + result = countRows.toString(); + + return result; + } + + async getColumnValueInTable(table: string, column: string) { + let tableDataId: any; + if (table === "Latest Batches") { + tableDataId = this.latestBatchesTable; + } else if (table === "Latest Transactions") { + tableDataId = this.latestTransactionTable; + } else if (table === "Blocks") { + tableDataId = this.blocksTable; + } else if (table === "Transactions") { + tableDataId = this.transactionsTable; + } + + element = await this.getColumnValue(tableDataId, column); + result = await this.world.page?.locator(element).first(); + + return result; + } + + async getNetworkStatsBlock(text: string) { + element = this.networkStats + `//*[text()='${text}']`; + result = await this.world.page?.locator(element).first(); + + return result; + } + + async selectTabOnPage(tabName: string, pageName: string) { + const transactionPage = new TransactionPage(this.world); + const contractPage = new ContractPage(this.world); + + if (pageName === "Contract") { + await contractPage.selectTab(tabName); + } else if (pageName === "Transaction") { + await transactionPage.selectTab(tabName); + } else { + console.error("Incorrect Page name. The value should be only Contract or Transaction"); + } + } + + async clickOnCopyBtn() { + element = await this.world.page?.locator(this.copyBtn).first(); + await element.click(); + } + + async clickOnCopyBtnByRowPage(rowName: string, pageName: string) { + blockPage = new BlockPage(this.world); + transactionPage = new TransactionPage(this.world); + tokensPage = new TokensPage(this.world); + + if (pageName === "Transaction" || pageName === "Contract") { + result = await transactionPage.clickCopyBtnByRow(rowName); + } else if (pageName === "Account" || pageName === "Block") { + result = await blockPage.clickCopyBtnByRow(rowName); + } else if (pageName === "Tokens") { + result = await tokensPage.clickCopyBtnByRow(rowName); + } + return result; + } + + async setSwitcherValue(switcherType: string, value: string) { + const switcherDropdown = async (selector: string) => { + return await this.click(selector); + }; + if (switcherType === "language") { + await switcherDropdown(this.selectedLanguage); + result = await switcherDropdown(await this.setSwitcherLanguage(value)); + } else if (switcherType === "network") { + await switcherDropdown(this.selectedNetwork); + result = await switcherDropdown(await this.setSwitcherNetwork(value)); + } else { + console.error("Incorrect value for switcher type"); + } + } + + async verifyActualSwitcherValue(switcherType: string) { + const actualValue = async (selector: string) => { + return await this.world.page?.locator(selector); + }; + if (switcherType === "language") { + result = await actualValue(this.selectedLanguage); + } else if (switcherType === "network") { + result = await actualValue(this.selectedNetwork); + } else { + console.error("Incorrect value for switcher type"); + } + return result; + } + + async getTokenFromColumn(columnName: string) { + const tokenIconId = `//*[@${this.byTestId}'${tokensIcon}']`; + const dataHeadingID = `//*[@${this.byDataHeading}'${columnName}']`; + const textHeadingID = `//*[text()='${columnName}']`; + element = `${dataHeadingID}${tokenIconId} | ${textHeadingID}/../..${tokenIconId}`; + return element; + } + + async getLinkForElement(elementType: string) { + if (elementType === "tx hash") { + element = this.txHash; + } else if (elementType === "batch number") { + element = this.batchNumber; + } else if (elementType === "batch size") { + element = this.batchSize; + } else if (elementType === "committed blocks") { + element = this.committedBlocks; + } else if (elementType === "verified blocks") { + element = this.verifiedBlocks; + } else if (elementType === "transactions") { + element = this.transactions; + } else if (elementType === "initiator address") { + element = this.initiatorAddress; + } else if (elementType === "token icon") { + element = this.tokenIcon; + } else if (elementType === "transaction hash") { + element = this.transactionsHash; + } else if (elementType === "token address") { + element = this.tokenAddress; + } else if (elementType === "block number") { + element = this.blockNumber; + } else if (elementType === "from address") { + element = this.fromAddress; + } else if (elementType === "to address") { + element = this.toAddress; + } else if (elementType === "Value" || elementType === "Fee") { + element = await this.getTokenFromColumn(elementType); + } else if ( + elementType === "To" || + elementType === "Commit Tx hash" || + elementType === "Execute Tx hash" || + elementType === "Prove Tx hash" || + elementType === "Tokens Transferred" + ) { + element = await this.getRandomToken(elementType); + } + return await element; + } + + async getRandomToken(columnName: string) { + element = `//*[text()='${columnName}']/../..//*[@href]`; + return element; + } +} diff --git a/packages/app/tests/e2e/src/pages/tokens.page.ts b/packages/app/tests/e2e/src/pages/tokens.page.ts new file mode 100644 index 0000000000..e4eb6484c6 --- /dev/null +++ b/packages/app/tests/e2e/src/pages/tokens.page.ts @@ -0,0 +1,33 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +import { BasePage } from "./base.page"; + +import { pageTitle } from "../../testId.json"; + +import type { ICustomWorld } from "../support/custom-world"; + +let element: any; + +export class TokensPage extends BasePage { + constructor(world: ICustomWorld) { + super(world); + } + get title() { + return `${this.byTestId}${pageTitle}`; + } + + get copyBtn() { + return "//td//*[@class='copy-button']"; + } + + async getRowByText(rowName: string) { + element = `//*[text()='${rowName}'][1]`; + return element; + } + + async clickCopyBtnByRow(rowName: string) { + const copyBtn = this.copyBtn; + element = (await this.getRowByText(rowName)) + "/..//..//..//.." + copyBtn; + + await this.world.page?.locator(element).first().click(); + } +} diff --git a/packages/app/tests/e2e/src/pages/transaction.page.ts b/packages/app/tests/e2e/src/pages/transaction.page.ts new file mode 100644 index 0000000000..bfec33015b --- /dev/null +++ b/packages/app/tests/e2e/src/pages/transaction.page.ts @@ -0,0 +1,100 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +import { BasePage } from "./base.page"; +import { MainPage } from "./main.page"; +import { Helper } from "../helpers/helper"; + +import type { ICustomWorld } from "../support/custom-world"; + +let element: any; +let result: any; +let helper: Helper; + +export class TransactionPage extends BasePage { + constructor(world: ICustomWorld) { + super(world); + } + + get actualString() { + return "//span[@class='actual-string']"; + } + + get contractAddress() { + return "//*[text()='To']/../..//a[contains(@href,'/address/')]"; + } + + get generalInfoTab() { + return "//button[contains(text(),'General Info')]/.."; + } + + get logsTab() { + return "//button[contains(text(),'Logs')]/.."; + } + + get badgeContent() { + return "//*[@class='badge-content']"; + } + + get dataType() { + return "//*[@class='convert-dropdown-container']"; + } + + get sendingSpinner() { + return "(//*[@class='badge-icon']//*[@class])[1]"; + } + + async getRowByText(rowName: string) { + element = `//tr//*[text()='${rowName}'][1]`; + return element; + } + + async getCellByText(rowName: string) { + element = `//tr//td//*[text()='${rowName}'][1]`; + return element; + } + + async getValueInRow(row: string, copying: boolean) { + element = copying + ? (await this.getRowByText(row)) + `//..//*[@title]` + : (await this.getRowByText(row)) + `//..//td[text()][2]`; + + return element; + } + + async selectTab(tabName: string) { + if (tabName === "General Info") { + await this.click(this.generalInfoTab, false); + } else if (tabName === "Logs") { + await this.click(this.logsTab, false); + } else { + console.error("Incorrect Tab name. The value should be only General Info or Logs"); + } + } + + async clickCopyBtnByRow(rowName: string) { + const mainPage = new MainPage(this.world); + const copyBtn = mainPage.copyBtn; + + if (rowName === "Data" || rowName === "Topics") { + element = (await this.getRowByText(rowName)) + "/.." + copyBtn; + } else { + element = (await this.getRowByText(rowName)) + "/../.." + copyBtn; + } + await this.world.page?.locator(element).first().click(); + } + + async clickOnDataTypeDropDownByRow(rowName: string) { + element = (await this.getRowByText(rowName)) + "/.." + this.dataType; + await this.world.page?.locator(element).first().click(); + } + + async verifyStatusComponentColor(status: string, color: string) { + helper = new Helper(this.world); + result = await helper.checkComponentColor(color, `//*[@${this.byTestId}'${status}']/*[@${this.byTestId}'badge']`); // //*[@class='status-badge' and contains(. ,'${status}')] + return result; + } + + async getBadgeByStatus(text: string) { + result = await this.world.page?.locator(`(${this.badgeContent}//*[text()='${text}'])[1]`); + return result; + } +} diff --git a/packages/app/tests/e2e/src/steps/blockexplorer.steps.ts b/packages/app/tests/e2e/src/steps/blockexplorer.steps.ts new file mode 100644 index 0000000000..507e977eab --- /dev/null +++ b/packages/app/tests/e2e/src/steps/blockexplorer.steps.ts @@ -0,0 +1,426 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +import { Given, Then, When } from "@cucumber/cucumber"; +import { expect } from "@playwright/test"; + +import { Helper } from "../helpers/helper"; +import { AccountPage } from "../pages/account.page"; +import { BasePage } from "../pages/base.page"; +import { BlockPage } from "../pages/block.page"; +import { ContractPage } from "../pages/contract.page"; +import { MainPage } from "../pages/main.page"; +import { TransactionPage } from "../pages/transaction.page"; +import { config } from "../support/config"; + +import type { ICustomWorld } from "../support/custom-world"; + +let accountPage: AccountPage; +let basePage: BasePage; +let blockPage: BlockPage; +let mainPage: MainPage; +let transactionPage: TransactionPage; +let contractPage: ContractPage; +let helper: Helper; + +let result: any; +let element: any; + +Given("I am on main page", async function (this: ICustomWorld) { + await expect(this.page?.url()).toContain(config.BASE_URL); +}); + +Given("I fill the search field by {string}", async function (this: ICustomWorld, text: string) { + mainPage = new MainPage(this); + basePage = new BasePage(this); + element = mainPage.searchField; + + await basePage.fill(element, text); +}); + +Given("I press search button", async function (this: ICustomWorld) { + mainPage = new MainPage(this); + basePage = new BasePage(this); + element = mainPage.searchSubmitBtn; + + await basePage.click(element); +}); + +Then("Current page have {string} address", async function (this: ICustomWorld, route: string) { + mainPage = new MainPage(this); + helper = new Helper(this); + await this.page?.waitForURL("**" + route); + result = await this.page?.url(); + + await expect(result).toBe(config.BASE_URL + route); +}); + +Then("Current page includes {string} part address", async function (this: ICustomWorld, partRoute: string) { + mainPage = new MainPage(this); + helper = new Helper(this); + await this.page?.waitForURL("**" + partRoute + "**"); + result = await this.page?.url(); + + await expect(result).toContain(config.BASE_URL + partRoute); +}); + +Then("Page with part address {string} includes ID result", async function (this: ICustomWorld, partRoute: string) { + mainPage = new MainPage(this); + helper = new Helper(this); + + const networkLayer = await mainPage.getCustomNetworkLayer(element); + const [currentUrl, pageURL] = await mainPage.getUrlOfTheCurrentPage( + networkLayer, + config.BASE_URL, + partRoute, + result, + config.DAPP_NETWORK + ); + + await expect(currentUrl).toBe(pageURL + partRoute + result); +}); + +Then("New page have {string} address", async function (this: ICustomWorld, url: string) { + mainPage = new MainPage(this); + helper = new Helper(this); + await this.page?.waitForTimeout(config.increasedTimeout.timeout); + const pages: any = this.context?.pages(); + + result = await pages[1].url(); + await expect(result).toBe(url); +}); + +Given("I go to page {string}", async function (this: ICustomWorld, route: string) { + await this.page?.goto(config.BASE_URL + route + config.DAPP_NETWORK); + await this.page?.waitForLoadState(); +}); + +When("I click by text {string}", async function (this: ICustomWorld, text: string) { + basePage = new BasePage(this); + + await basePage.clickByText(text); +}); + +When("I click on the first {string} link", async function (this: ICustomWorld, elementType: string) { + mainPage = new MainPage(this); + helper = new Helper(this); + element = await mainPage.getLinkForElement(elementType); + result = await helper.extractIdFromElementHref(element); + + await mainPage.click(element, false); +}); + +When("I click on the first contract address", async function (this: ICustomWorld) { + transactionPage = new TransactionPage(this); + basePage = new BasePage(this); + helper = new Helper(this); + element = transactionPage.contractAddress; + result = await helper.extractTextFromElement(element); + + await basePage.click(element, false); +}); + +When("I go to the first Processed transaction link", async function (this: ICustomWorld) { + mainPage = new MainPage(this); + basePage = new BasePage(this); + helper = new Helper(this); + element = await helper.extractHrefFromElement(mainPage.processedTxLink); + + await this.page?.goto(config.BASE_URL + element); + await this.page?.waitForLoadState(); +}); + +When("I click by element with partial href {string}", async function (this: ICustomWorld, partialHref: string) { + mainPage = new MainPage(this); + element = await mainPage.getElementByPartialHref(partialHref); + + await element.click(); +}); + +When( + "I click by element with partial href {string} and text {string}", + async function (this: ICustomWorld, partialHref: string, text: string) { + mainPage = new MainPage(this); + element = await mainPage.getElementByHrefAndText(partialHref, text); + + await element.click(); + } +); + +When("I click on bytecode dropdown", async function (this: ICustomWorld) { + contractPage = new ContractPage(this); + + await contractPage.clickOnByteCodeDropDown(); +}); + +When("I click on datatype dropdown of {string} row", async function (this: ICustomWorld, rowName: string) { + transactionPage = new TransactionPage(this); + + await transactionPage.clickOnDataTypeDropDownByRow(rowName); +}); + +When("I select {string} as dropdown value", async function (this: ICustomWorld, value: string) { + mainPage = new MainPage(this); + basePage = new BasePage(this); + element = await mainPage.getDropDownValue(value); + + await basePage.click(element); +}); + +When("I click on main logo", async function (this: ICustomWorld) { + mainPage = new MainPage(this); + basePage = new BasePage(this); + + await basePage.click(mainPage.logo, false); +}); + +When("Message {string} should be visible", async function (this: ICustomWorld, successMessage: string) { + result = await this.page?.locator(`text=${successMessage}`); + + await expect(result).toBeVisible(config.increasedTimeout); +}); + +Then( + "Element with {string} {string} should be {string}", + async function (this: ICustomWorld, elementType: string, value: string, checkType: string) { + basePage = new BasePage(this); + helper = new Helper(this); + element = await basePage.returnElementByType(elementType, value); + + if (checkType === "visible") { + await expect(element).toBeVisible(config.increasedTimeout); + } else if (checkType === "clickable") { + result = await helper.checkElementClickable(element); + await expect(result).toBe(true); + } + } +); + +Then( + "Element with {string} {string} should have {string} value", + async function (this: ICustomWorld, elementType: string, text: string, value: string) { + mainPage = new MainPage(this); + + if (elementType === "text") { + element = await mainPage.getElementByPartialText(text); + } else if (elementType === "partial href") { + element = await mainPage.getElementByPartialHref(text); + } + + await expect(element).toHaveAttribute("href", value); + } +); + +Then("Title contains text {string}", async function (this: ICustomWorld, text: string) { + blockPage = new BlockPage(this); + basePage = new BasePage(this); + + element = await basePage.getElement(blockPage.title); + await expect(element).toContainText(text); +}); + +Then("Check the element contains text {string}", async function (this: ICustomWorld, text: string) { + mainPage = new MainPage(this); + element = await mainPage.getElementByPartialText(text); + + await expect(element).toContainText(text); +}); + +Then("Check the element have the exact text {string}", async function (this: ICustomWorld, text: string) { + mainPage = new MainPage(this); + element = await mainPage.getElementByText(text); + + await expect(element).toHaveText(text); +}); + +Then( + "Table {string} should have {string} rows", + async function (this: ICustomWorld, tableName: string, rowsNumber: string) { + mainPage = new MainPage(this); + result = await mainPage.countRowsInTable(tableName); + + await expect(result).toBe(rowsNumber); + } +); + +Then( + "Table {string} on Main page contains {string} name", + async function (this: ICustomWorld, tableName: string, columnName: string) { + mainPage = new MainPage(this); + result = await mainPage.getColumnValueInTable(tableName, columnName); + + await expect(result).toBeVisible(config.increasedTimeout); + } +); + +Then("Table contains row with {string}", async function (this: ICustomWorld, rowName: string) { + transactionPage = new TransactionPage(this); + result = await this.page?.locator(await transactionPage.getRowByText(rowName)).first(); + + await expect(result).toBeVisible(config.increasedTimeout); +}); + +Then("Table contains cell with {string}", async function (this: ICustomWorld, rowName: string) { + transactionPage = new TransactionPage(this); + result = await this.page?.locator(await transactionPage.getCellByText(rowName)); + + await expect(result).toBeVisible(config.increasedTimeout); +}); + +Then( + "Table on Transaction page contains element with value for {string} row and copying {string}", + async function (this: ICustomWorld, rowName: string, copying: boolean) { + transactionPage = new TransactionPage(this); + result = await this.page?.locator(await transactionPage.getValueInRow(rowName, copying)); + + await expect(result).toBeVisible(config.increasedTimeout); + } +); + +Then( + "Column with {string} name includes {string} cell", + async function (this: ICustomWorld, columnName: string, cellName: string) { + contractPage = new ContractPage(this); + result = await contractPage.getCellValueByColumn(columnName, cellName); + await expect(result).toBeVisible(config.increasedTimeout); + } +); + +Then("Search field color should be red", async function (this: ICustomWorld) { + mainPage = new MainPage(this); + helper = new Helper(this); + element = mainPage.searchFieldInvalidInput; + result = await helper.getColorOfSelector(element); + const colorRed = "rgb(255, 255, 255)"; + + await expect(result).toBe(colorRed); +}); + +Then("I select {string} tab on {string} page", async function (this: ICustomWorld, tabName: string, pageName: string) { + mainPage = new MainPage(this); + + await mainPage.selectTabOnPage(tabName, pageName); +}); + +Then("I press Verify Smart Contract button", async function (this: ICustomWorld) { + contractPage = new ContractPage(this); + + await contractPage.pressContractVerificationBtn(); +}); + +Then("Tab title on {string} contains {string}", async function (this: ICustomWorld, route: string, tabTitle: string) { + await this.page?.waitForNavigation(); + result = await this.page?.title(); + + await expect(result).toBe(tabTitle + " | zkSync 2.0 Block Explorer"); +}); + +Then("Pagination form should be visible", async function (this: ICustomWorld) { + basePage = new BasePage(this); + element = basePage.paginationForm; + result = await this.page?.locator(element); + + await expect(result).toBeVisible(config.increasedTimeout); +}); + +Then("Column with {string} name is visible", async function (this: ICustomWorld, columnName: string) { + basePage = new BasePage(this); + result = await basePage.getColumnByText(columnName); + + await expect(result).toBeVisible(config.increasedTimeout); +}); + +Then("Network stats {string} block should be visible", async function (this: ICustomWorld, blockName: string) { + mainPage = new MainPage(this); + result = await mainPage.getNetworkStatsBlock(blockName); + + await expect(result).toBeVisible(config.increasedTimeout); +}); + +Then("I click on the first copy button", async function (this: ICustomWorld) { + mainPage = new MainPage(this); + + await mainPage.clickOnCopyBtn(); +}); + +Then( + "I click on the copy button with {string} row on {string} page", + async function (this: ICustomWorld, rowName: string, pageName: string) { + mainPage = new MainPage(this); + + await mainPage.clickOnCopyBtnByRowPage(rowName, pageName); + } +); + +Then("I click on the copy button for page title", async function (this: ICustomWorld) { + contractPage = new ContractPage(this); + + await contractPage.clickCopyBtnOnTitle(); +}); + +Then("I click on the copy button for deployed bytecode", async function (this: ICustomWorld) { + contractPage = new ContractPage(this); + + await contractPage.clickCopyBtnOnByteCode(); +}); + +Then("I click on the copy button for the first token asset", async function (this: ICustomWorld) { + accountPage = new AccountPage(this); + helper = new Helper(this); + + await helper.clearClipboard(); + await accountPage.clickCopyBtnByFirstToken(); +}); + +Then("Clipboard contains {string} value", async function (this: ICustomWorld, text: string) { + helper = new Helper(this); + result = await helper.getClipboardValue(); + + await expect(result).toBe(text); +}); + +Then("Clipboard value is not empty", async function (this: ICustomWorld) { + helper = new Helper(this); + result = await helper.getClipboardValue(); + + await expect(typeof result).toBe("string"); + await expect(result.length).toBeGreaterThan(1); +}); + +When( + "Set the {string} value for {string} switcher", + async function (this: ICustomWorld, value: string, switcherType: string) { + mainPage = new MainPage(this); + await mainPage.setSwitcherValue(switcherType, value); + } +); + +When( + "Check the {string} value is actual for {string} switcher", + async function (this: ICustomWorld, value: string, switcherType: string) { + mainPage = new MainPage(this); + result = await mainPage.verifyActualSwitcherValue(switcherType); + + await expect(result).toBeVisible(config.extraTimeout); + await expect(result).toHaveText(value); + } +); + +Then("Sending spinner should be visible", async function (this: ICustomWorld) { + transactionPage = new TransactionPage(this); + result = await this.page?.locator(transactionPage.sendingSpinner); + await expect(result).toBeVisible(config.increasedTimeout); +}); + +Then( + "Status component color with {string} status should be {string}", + async function (this: ICustomWorld, status: string, color: string) { + transactionPage = new TransactionPage(this); + result = await transactionPage.verifyStatusComponentColor(status, color); + await expect(result).toBe(true); + } +); + +Then("Verify the badge with {string} status is visible", async function (this: ICustomWorld, status: string) { + transactionPage = new TransactionPage(this); + result = await transactionPage.getBadgeByStatus(status); + await expect(result).toBeVisible(config.increasedTimeout); +}); diff --git a/packages/app/tests/e2e/src/support/common-hooks.ts b/packages/app/tests/e2e/src/support/common-hooks.ts new file mode 100644 index 0000000000..cfb7be26c0 --- /dev/null +++ b/packages/app/tests/e2e/src/support/common-hooks.ts @@ -0,0 +1,48 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +import { After, AfterAll, Before, BeforeAll, setDefaultTimeout } from "@cucumber/cucumber"; +import { chromium } from "@playwright/test"; + +import { Helper } from "../helpers/helper"; +import { config } from "../support/config"; + +import type { ICustomWorld } from "./custom-world"; +import type { ITestCaseHookParameter } from "@cucumber/cucumber/lib/support_code_library_builder/types"; + +let browser: any; +setDefaultTimeout(process.env.PWDEBUG ? -1 : 60 * 1000); + +BeforeAll(async function (this: ICustomWorld) { + browser = await chromium.launch({ + slowMo: config.slowMo, + headless: config.headless, + }); + console.log("-------- Base Url: ", config.BASE_URL + config.DAPP_NETWORK); + await browser; +}); + +Before({ tags: "@ignore" }, async function () { + return "skipped" as any; +}); + +Before(async function (this: ICustomWorld, { pickle }: ITestCaseHookParameter) { + this.testName = pickle.name.replace(/\W/g, "-"); + this.context = await browser.newContext({ + viewport: config.mainWindowSize, + recordVideo: { dir: config.artifactsFolder }, + }); + await this.context?.grantPermissions(["clipboard-read", "clipboard-write"]); + this.page = await this.context?.newPage(); + this.feature = pickle; + + await this.page?.goto(config.BASE_URL + config.DAPP_NETWORK); + await this.page?.waitForLoadState(); +}); + +After(async function (this: ICustomWorld, { result }: ITestCaseHookParameter) { + await new Helper(this).getScreenshotOnFail(result); + await this.page?.close(); +}); + +AfterAll(async function (this: ICustomWorld) { + await browser.close(); +}); diff --git a/packages/app/tests/e2e/src/support/config.ts b/packages/app/tests/e2e/src/support/config.ts new file mode 100644 index 0000000000..a87fee6aa1 --- /dev/null +++ b/packages/app/tests/e2e/src/support/config.ts @@ -0,0 +1,24 @@ +import { NetworkSwitcher } from "../data/data"; + +import type { LaunchOptions } from "@playwright/test"; + +const browserOptions: LaunchOptions = { + slowMo: 10, + devtools: true, + headless: false, + args: ["--use-fake-ui-for-media-stream", "--use-fake-device-for-media-stream", "--disable-web-security"], +}; + +export const config = { + browser: process.env.BROWSER || "chromium", + browserOptions, + BASE_URL: process.env.TARGET_ENV || "http://localhost:3010", + mainWindowSize: { width: 1920, height: 1080 }, + artifactsFolder: "tests/e2e/artifacts/", + DAPP_NETWORK: process.env.E2ENETWORK || NetworkSwitcher.zkSyncEraMainnet, + headless: true, + slowMo: 10, + defaultTimeout: { timeout: 5000 }, + increasedTimeout: { timeout: 17000 }, + extraTimeout: { timeout: 60000 }, +}; diff --git a/packages/app/tests/e2e/src/support/custom-world.ts b/packages/app/tests/e2e/src/support/custom-world.ts new file mode 100644 index 0000000000..112fdf634b --- /dev/null +++ b/packages/app/tests/e2e/src/support/custom-world.ts @@ -0,0 +1,27 @@ +import { setWorldConstructor, World } from "@cucumber/cucumber"; + +import type { IWorldOptions } from "@cucumber/cucumber"; +import type * as messages from "@cucumber/messages"; +import type { BrowserContext, Page, PlaywrightTestOptions } from "@playwright/test"; +import type { AxiosInstance } from "axios"; + +export interface ICustomWorld extends World { + debug: boolean; + feature?: messages.Pickle; + context?: BrowserContext; + persistentContext?: BrowserContext; + page?: Page; + testName?: string; + startTime?: Date; + server?: AxiosInstance; + playwrightOptions?: PlaywrightTestOptions; +} + +export class CustomWorld extends World implements ICustomWorld { + constructor(options: IWorldOptions) { + super(options); + } + debug = false; +} + +setWorldConstructor(CustomWorld); diff --git a/packages/app/tests/e2e/src/support/reporters/allure-reporter.js b/packages/app/tests/e2e/src/support/reporters/allure-reporter.js new file mode 100644 index 0000000000..7422787620 --- /dev/null +++ b/packages/app/tests/e2e/src/support/reporters/allure-reporter.js @@ -0,0 +1,9 @@ +const { CucumberJSAllureFormatter } = require("allure-cucumberjs8"); +const { AllureRuntime } = require("allure-cucumberjs8"); +function Reporter(options) { + return new CucumberJSAllureFormatter(options, new AllureRuntime({ resultsDir: "./allure-results" }), {}); +} +Reporter.prototype = Object.create(CucumberJSAllureFormatter.prototype); +Reporter.prototype.constructor = Reporter; + +exports.default = Reporter;