diff --git a/tests/e2e/package.json b/tests/e2e/package.json index 8bb66923..a0d6534a 100644 --- a/tests/e2e/package.json +++ b/tests/e2e/package.json @@ -43,7 +43,7 @@ "test:prod:smoke:native_accounts": "npx playwright test --project=Chrome --grep=@NativeAccounts --retries=3 --timeout=60000", "test:prod:smoke:userops": "npx playwright test --project=Chrome --grep=@UserOps --retries=3 --timeout=60000", "test:prod:smoke:ens": "npx playwright test --project=Chrome --grep=@ENS --retries=3 --timeout=60000", - "test:ondemand": "npx playwright test --project=Chrome --grep=@OnDemandSmoke --retries=0 --timeout=30000", + "test:ondemand": "npx playwright test --project=Chrome --grep=@OnDemandSmoke --retries=0 --timeout=60000", "test:prod:smoke:marketplace": "npx playwright test --project=Chrome --grep=@Marketplace --retries=3 --timeout=30000", "test:rollup": "npx playwright test --project=Chrome --grep=@Rollup", "test:authorized": "npx playwright test --project=Chrome --grep=@Authorized", diff --git a/tests/e2e/pages/NewHome.ts b/tests/e2e/pages/NewHome.ts index 6283b9e3..b2b08b31 100644 --- a/tests/e2e/pages/NewHome.ts +++ b/tests/e2e/pages/NewHome.ts @@ -1,6 +1,6 @@ import { WebActions } from "@lib/WebActions" import type { BrowserContext, Page } from 'playwright' -import chalk from "chalk" +import { expect } from "@playwright/test" import { CommonPage } from "./Common" export class NewHomePage extends CommonPage { @@ -80,6 +80,10 @@ export class NewHomePage extends CommonPage { return this.actions.page.isVisible(`text=/Blob txns/`) } + async hasWriteContractTab(): Promise { + return this.page.getByRole(`tab`, { name: `Write contract` }).isVisible() + } + async checkBlobTransactions(): Promise { await this.page.click(`text=/Blob txns/`) const firstRow = `text=/Blob txn$/ >> ../../../.. >> nth=0` @@ -129,6 +133,115 @@ export class NewHomePage extends CommonPage { await this.page.getByText(`Contract source code`, { exact: true }).isVisible() } + async checkERC721Inventory(data: any): Promise { + await expect(this.page.getByText(`ERC-721`)).toBeVisible() + await expect(this.page.getByText(`Max total supply`)).toBeVisible() + await expect(this.page.locator(`p`).filter({ hasText: `Holders` })).toBeVisible() + await expect(this.page.getByText(`Transfers`, { exact: true })).toBeVisible() + await expect(this.page.getByText(`Sponsored`)).toBeVisible() + await expect(this.page.getByRole(`tab`, { name: `Inventory` })).toBeVisible() + await expect(this.page.getByRole(`tab`, { name: `Token transfers` })).toBeVisible() + await expect(this.page.getByRole(`tab`, { name: `Holders` })).toBeVisible() + await expect(this.page.getByRole(`tab`, { name: `Contract` })).toBeVisible() + await expect(this.page.getByRole(`tab`, { name: `Contract` })).toBeVisible() + + await this.actions.verifyElementIsDisplayed(this.tokenInventoryElement(0, 0)) + await this.actions.verifyElementIsDisplayed(`${this.tokenInventoryElement(0, 2)} >> text=/\\d+/`) + await this.actions.verifyElementIsDisplayed(`${this.tokenInventoryElement(0, 4)} >> text=/0x/`) + } + + async checkInventoryERC721Element(data: any): Promise { + await expect(this.page.getByText(`ERC-721`)).toBeVisible() + await expect(this.page.getByText(`Owner`)).toBeVisible() + await expect(this.page.getByText(`Creator`)).toBeVisible() + await expect(this.page.locator(`p`).filter({ hasText: `Token ID` })).toBeVisible() + await expect(this.page.getByText(`Transfers`, { exact: true })).toBeVisible() + await expect(this.page.getByText(`Name`)).toBeVisible() + await expect(this.page.getByText(`Description`)).toBeVisible() + await expect(this.page.getByText(`Attributes`)).toBeVisible() + await expect(this.page.getByText(`Sponsored`)).toBeVisible() + } + + async checkInventoryERC721MetadataTab(data: any): Promise { + await this.page.getByRole(`tab`, { name: `Metadata` }).click() + await this.page.getByRole(`combobox`).selectOption(`JSON`) + const textContent = await this.page.locator(`body >> section >> div >> nth=26`).textContent() + const minified = JSON.stringify(JSON.parse(textContent)) + await expect(minified).toContain( + data.metadata, + ) + } + + async checkERC404Inventory(data: any): Promise { + await expect(this.page.getByText(`ERC-404`, { exact: true })).toBeVisible() + await expect(this.page.getByText(`Max total supply`)).toBeVisible() + await expect(this.page.locator(`p`).filter({ hasText: `Holders` })).toBeVisible() + await expect(this.page.getByText(`Transfers`, { exact: true })).toBeVisible() + await expect(this.page.getByText(`Decimals`)).toBeVisible() + await expect(this.page.getByText(`Sponsored`)).toBeVisible() + await expect(this.page.getByRole(`tab`, { name: `Inventory` })).toBeVisible() + await expect(this.page.getByRole(`tab`, { name: `Token transfers` })).toBeVisible() + await expect(this.page.getByRole(`tab`, { name: `Holders` })).toBeVisible() + await expect(this.page.getByRole(`tab`, { name: `Contract` })).toBeVisible() + } + + async checkInventoryERC404Element(data: any): Promise { + await expect(this.page.getByText(`ERC-404`, { exact: true })).toBeVisible() + await expect(this.page.getByText(`Owner`)).toBeVisible() + await expect(this.page.getByText(`Creator`)).toBeVisible() + await expect(this.page.locator(`p`).filter({ hasText: `Token ID` })).toBeVisible() + await expect(this.page.getByText(`Transfers`, { exact: true })).toBeVisible() + await expect(this.page.getByText(`Name`)).toBeVisible() + await expect(this.page.getByText(`Description`)).toBeVisible() + await expect(this.page.getByText(`Attributes`)).toBeVisible() + await expect(this.page.getByText(`Sponsored`)).toBeVisible() + } + + async checkInventoryERC404MetadataTab(data: any): Promise { + await this.page.getByRole(`tab`, { name: `Metadata` }).click() + await this.page.getByRole(`combobox`).selectOption(`JSON`) + const textContent = await this.page.locator(`body >> section >> div >> nth=26`).textContent() + const minified = JSON.stringify(JSON.parse(textContent)) + await expect(minified).toContain( + data.metadata, + ) + } + + async checkERC1155Inventory(data: any): Promise { + await expect(this.page.getByText(`ERC-1155`)).toBeVisible() + await expect(this.page.getByText(`Max total supply`)).toBeVisible() + await expect(this.page.locator(`p`).filter({ hasText: `Holders` })).toBeVisible() + await expect(this.page.getByText(`Transfers`, { exact: true })).toBeVisible() + await expect(this.page.getByText(`Sponsored`)).toBeVisible() + await expect(this.page.getByRole(`tab`, { name: `Inventory` })).toBeVisible() + await expect(this.page.getByRole(`tab`, { name: `Token transfers` })).toBeVisible() + await expect(this.page.getByRole(`tab`, { name: `Holders` })).toBeVisible() + await expect(this.page.getByRole(`tab`, { name: `Contract` })).toBeVisible() + } + + async checkInventoryERC1155Element(data: any): Promise { + await expect(this.page.getByText(`ERC-1155`)).toBeVisible() + await expect(this.page.getByText(`Owner`, { exact: true })).toBeVisible() + await expect(this.page.getByText(`Creator`)).toBeVisible() + await expect(this.page.locator(`p`).filter({ hasText: `Token ID` })).toBeVisible() + await expect(this.page.getByText(`Transfers`, { exact: true })).toBeVisible() + await expect(this.page.getByText(`Sponsored`)).toBeVisible() + } + + async checkInventoryERC1155MetadataTab(data: any): Promise { + await this.page.getByRole(`tab`, { name: `Metadata` }).click() + await this.page.getByRole(`combobox`).selectOption(`JSON`) + const textContent = await this.page.locator(`body >> section >> div >> nth=26`).textContent() + const minified = JSON.stringify(JSON.parse(textContent)) + await expect(minified).toContain( + data.metadata, + ) + } + + tokenInventoryElement(row: number, el: number): string { + return `div[role="tabpanel"] >> nth=${row} >> div >> div >> div >> nth=0 >> div >> nth=${el}` + } + async checkContractUMLDiagram(): Promise { await this.page.getByRole(`link`, { name: `View UML diagram` }).click() await this.page.getByText(`/For contract0x.*/`).isVisible() diff --git a/tests/e2e/playwright.config.ts b/tests/e2e/playwright.config.ts index 082a5b1a..2bbf981f 100644 --- a/tests/e2e/playwright.config.ts +++ b/tests/e2e/playwright.config.ts @@ -16,6 +16,10 @@ const config: PlaywrightTestConfig = { // sets timeout for each test case timeout: 60000, + expect: { + timeout: 15000, + }, + // number of retries if test case fails retries: 0, diff --git a/tests/e2e/static/eth-sepolia.json b/tests/e2e/static/eth-sepolia.json new file mode 100644 index 00000000..964b0532 --- /dev/null +++ b/tests/e2e/static/eth-sepolia.json @@ -0,0 +1,17 @@ +{ + "erc721": { + "address": "0xB5E7041cA1B7024C77cEdCcB3da205a660643697", + "instance": "199", + "metadata": "{\"attributes\":[{\"trait_type\":\"Multi-sequencer\",\"value\":\"True\"}],\"description\":\"AltLayer Multi-sequencer Testnet NFT\",\"image\":\"https://altlayer-image-store.alt.technology/msnft.png\",\"name\":\"Multi-sequencer Test NFT\"}" + }, + "erc404": { + "address": "0x897F2cB1cAaAef45C08133F43aB80bd093dE6e46", + "instance": "199", + "metadata": "{\"attributes\":[{\"trait_type\":\"BG\",\"value\":\"Shrubbery\"},{\"trait_type\":\"WALL\",\"value\":\"Crimson\"},{\"trait_type\":\"LOBBY WINDOWS\",\"value\":\"Cats\"},{\"trait_type\":\"DOOR\",\"value\":\"European Retro\"},{\"trait_type\":\"UPPER WINDOWS\",\"value\":\"Upper Window 8\"},{\"trait_type\":\"SIGN\",\"value\":\"Fireworks\"},{\"trait_type\":\"FACTION\",\"value\":\"Sou\"},{\"trait_type\":\"ROOF\",\"value\":\"Normal Roof\"},{\"trait_type\":\"LEFT\",\"value\":\"Refrigerator x2\"},{\"trait_type\":\"RIGHT\",\"value\":\"Road Sign 14\"},{\"trait_type\":\"STREET\",\"value\":\"Exploration Robot\"}],\"description\":\"By Mahjong404Studio\",\"external_url\":\"\",\"image\":\"https://mj404.com/metapng/nft_mj404_455252.png\",\"name\":\"0xMJ404 #199\"}" + }, + "erc1155": { + "address": "0x0635513f179D50A207757E05759CbD106d7dFcE8", + "instance": "115790815234821798635716340045006141246626976709646673706192145106181710196931", + "metadata": "{\"error\":\"ens-metadata-service.appspot.com/name/0xffff47689f01958efdc638c3b2b9360c7ef0ad129e7b0083b237862420ccb0c3\"}" + } +} \ No newline at end of file diff --git a/tests/e2e/static/eth-sepolia.k8s-dev.json b/tests/e2e/static/eth-sepolia.k8s-dev.json new file mode 100644 index 00000000..964b0532 --- /dev/null +++ b/tests/e2e/static/eth-sepolia.k8s-dev.json @@ -0,0 +1,17 @@ +{ + "erc721": { + "address": "0xB5E7041cA1B7024C77cEdCcB3da205a660643697", + "instance": "199", + "metadata": "{\"attributes\":[{\"trait_type\":\"Multi-sequencer\",\"value\":\"True\"}],\"description\":\"AltLayer Multi-sequencer Testnet NFT\",\"image\":\"https://altlayer-image-store.alt.technology/msnft.png\",\"name\":\"Multi-sequencer Test NFT\"}" + }, + "erc404": { + "address": "0x897F2cB1cAaAef45C08133F43aB80bd093dE6e46", + "instance": "199", + "metadata": "{\"attributes\":[{\"trait_type\":\"BG\",\"value\":\"Shrubbery\"},{\"trait_type\":\"WALL\",\"value\":\"Crimson\"},{\"trait_type\":\"LOBBY WINDOWS\",\"value\":\"Cats\"},{\"trait_type\":\"DOOR\",\"value\":\"European Retro\"},{\"trait_type\":\"UPPER WINDOWS\",\"value\":\"Upper Window 8\"},{\"trait_type\":\"SIGN\",\"value\":\"Fireworks\"},{\"trait_type\":\"FACTION\",\"value\":\"Sou\"},{\"trait_type\":\"ROOF\",\"value\":\"Normal Roof\"},{\"trait_type\":\"LEFT\",\"value\":\"Refrigerator x2\"},{\"trait_type\":\"RIGHT\",\"value\":\"Road Sign 14\"},{\"trait_type\":\"STREET\",\"value\":\"Exploration Robot\"}],\"description\":\"By Mahjong404Studio\",\"external_url\":\"\",\"image\":\"https://mj404.com/metapng/nft_mj404_455252.png\",\"name\":\"0xMJ404 #199\"}" + }, + "erc1155": { + "address": "0x0635513f179D50A207757E05759CbD106d7dFcE8", + "instance": "115790815234821798635716340045006141246626976709646673706192145106181710196931", + "metadata": "{\"error\":\"ens-metadata-service.appspot.com/name/0xffff47689f01958efdc638c3b2b9360c7ef0ad129e7b0083b237862420ccb0c3\"}" + } +} \ No newline at end of file diff --git a/tests/e2e/static/eth.json b/tests/e2e/static/eth.json new file mode 100644 index 00000000..1c3338d5 --- /dev/null +++ b/tests/e2e/static/eth.json @@ -0,0 +1,17 @@ +{ + "erc721": { + "address": "0x382EDfe4c6168858C81893fE00fCB7b68914d929", + "instance": "100003764", + "metadata": "{\"attributes\":[{\"trait_type\":\"Accent Color\",\"value\":\"#6bffd3\"},{\"trait_type\":\"Primary Color\",\"value\":\"#6efdff\"},{\"trait_type\":\"Number\",\"value\":\"3764\"}],\"description\":\"ERC20721 Is ERC20 + ERC721 and functions both in Uniswap, and Opensea.\",\"image\":\"https://raw.githubusercontent.com/SerecThunderson/assets/main/emeralds/assets/3764_6bffd3_6efdff.png\",\"name\":\"Uniswap Emerald 3764\"}" + }, + "erc404": { + "address": "0x954a75564Cb355EA2D6FcCc6c1212FD01FdcB06f", + "instance": "1960", + "metadata": "{\"attributes\":[{\"trait_type\":\"Color\",\"value\":\"Blue\"}],\"description\":\"Plumpy Dragons is a ERC-404 collection of 888 NFTs. For every 1 million $LOONG you claim a lucky dragon.\",\"image\":\"https://bafybeicsmfawinz6xsw2xkeqd3sd64gsxwm3u5lcx6ctfipm5o3zu4mqwa.ipfs.nftstorage.link/3.gif\",\"name\":\"Plumpy Dragon #1960\"}" + }, + "erc1155": { + "address": "0x495f947276749Ce646f68AC8c248420045cb7b5e", + "instance": "115791876765286112095983152656382440851705833563249709443730295126148562026497", + "metadata": "{\"animation_url\":null,\"description\":\"The CyberPunk Comic is a collection of three comic book editions. The second edition consists of 4001 NFTs.\\n\\nThe comic is drawn in the classic way on paper and colored by hand in a combined technique.\\n\\nThe world was tormented by many wars and pandemics. It was horrible. Then the great nuclear war happened and the world was at the edge of extinction. Millions of sick and homeless are hiding without basic life necessities. People are left to fend for themselves...\\n\\nVisit https://comic.cyberpunkfighters.com/issue-no-2/191 to download the full digital version free of charge by the token owner. \",\"external_link\":\"https://comic.cyberpunkfighters.com/issue-no-2/191\",\"image\":\"https://i.seadn.io/gae/9yma_w49sUkMKJkky6SzgWAk2XNslR-ullz1CYGhhKjw0IaF8BIPOvmWhlJfvr015l_0xMX0vuNcmoizW0MkGfmAmVfMrzPgaB6W4g?w=500&auto=format\",\"name\":\"CyberPunk Comic Issue 2 #00191\",\"traits\":[{\"display_type\":null,\"max_value\":null,\"trait_type\":\"Issue Number\",\"value\":\"Issue 2\"},{\"display_type\":null,\"max_value\":null,\"trait_type\":\"Rarity\",\"value\":\"Gold\"}]}" + } +} \ No newline at end of file diff --git a/tests/e2e/tests/functional/Notifications.test.ts b/tests/e2e/tests/functional/Notifications.test.ts index fd9bb628..6f316aff 100644 --- a/tests/e2e/tests/functional/Notifications.test.ts +++ b/tests/e2e/tests/functional/Notifications.test.ts @@ -7,9 +7,11 @@ import { expect } from "@playwright/test" import { TestToken } from '../../../contracts/typechain/contracts/TestToken' import { TestNFT } from '../../../contracts/typechain/contracts/TestNFT' -const emailTimeout = 30000 +const emailTimeout = 120000 +const emailWait = 40000 test(`@AccountImage @Notifications Check notification received on Ether transfer`, async ({ authorized }) => { + test.setTimeout(emailWait + emailTimeout) await authorized.openWatchlist() const recipient = authorized.contracts.newWallet() console.log(`created recipient address: ${recipient.address}`) @@ -24,7 +26,7 @@ test(`@AccountImage @Notifications Check notification received on Ether transfer console.log(`sending Ether to recipient`) await authorized.contracts.sendEther(recipient.address, `0.01`) console.log(`awaiting blockscout indexing`) - await authorized.delay(30000) + await authorized.delay(emailWait) const emailResult = await authorized.ms.waitForMatchingEmails( { matches: [{ field: MatchOptionFieldEnum.SUBJECT, should: MatchOptionShouldEnum.CONTAIN, value: watchName }] }, @@ -39,6 +41,7 @@ test(`@AccountImage @Notifications Check notification received on Ether transfer }) test(`@AccountImage @Notifications Check notification received on ERC20 transfer`, async ({ authorized }) => { + test.setTimeout(emailWait + emailTimeout) const { TestTokenAddress } = process.env await authorized.openWatchlist() @@ -57,7 +60,7 @@ test(`@AccountImage @Notifications Check notification received on ERC20 transfer const receipt = await (await token.transfer(recipient.address, 1)).wait() console.log(`receipt: ${JSON.stringify(receipt)}`) console.log(`awaiting blockscout indexing`) - await authorized.delay(30000) + await authorized.delay(emailWait) const emailResult = await authorized.ms.waitForMatchingEmails( { matches: [{ field: MatchOptionFieldEnum.SUBJECT, should: MatchOptionShouldEnum.CONTAIN, value: watchName }] }, @@ -86,7 +89,7 @@ test.skip(`@AccountImage @Notifications Check notification received on NFT trans name: watchName, } as WatchListSpec) await authorized.checkNotificationItem() - await authorized.delay(5000) + await authorized.delay(emailWait) // const receipt = await (await nft.transferOwnership(recipient.address)).wait() // console.log(`transferred ownership: ${JSON.stringify(receipt)}`) const receipt = await (await nft.mintNFT(recipient.address, watchName)).wait() diff --git a/tests/e2e/tests/functional/SmokeOnDemand.test.ts b/tests/e2e/tests/functional/SmokeOnDemand.test.ts index 199a4c57..05df1580 100644 --- a/tests/e2e/tests/functional/SmokeOnDemand.test.ts +++ b/tests/e2e/tests/functional/SmokeOnDemand.test.ts @@ -1,5 +1,6 @@ import test from '@lib/BaseTest' import chalk from "chalk" +import { readFileSync } from "fs" test.describe.configure({ mode: `parallel` }) /* @@ -12,6 +13,13 @@ const url = process.env.BLOCKSCOUT_URL const COMMON_TOKEN_NAME = `USDT` const COMMON_TOKEN_FULL_NAME = `Tether USD \\(USDT\\)` +let staticData + +test.beforeAll(async () => { + const fileName = url.split(`//`)[1].split(`.`).slice(0, -2).join(`.`) + staticData = JSON.parse(readFileSync(`static/${fileName}.json`).toString()) +}) + test(`@OnDemandSmoke Main page components`, async ({ newHomePage }) => { await newHomePage.open_custom(url) await newHomePage.checkIndexing() @@ -113,7 +121,11 @@ test(`@OnDemandSmoke Check read contract tabs`, async ({ newHomePage }) => { test(`@OnDemandSmoke Check write contract tabs`, async ({ newHomePage }) => { await newHomePage.openFirstVerifiedContract(url) - await newHomePage.checkContractsWriteTabs() + if (await newHomePage.hasWriteContractTab()) { + await newHomePage.checkContractsWriteTabs() + } else { + console.log(chalk.yellow(`Contract doesn't have any write methods!`)) + } }) test(`@OnDemandSmoke Check contracts code tabs`, async ({ newHomePage }) => { @@ -121,3 +133,27 @@ test(`@OnDemandSmoke Check contracts code tabs`, async ({ newHomePage }) => { await newHomePage.checkContractsCodeTab() await newHomePage.checkContractUMLDiagram() }) + +test(`@OnDemandSmoke Check ERC-721 inventory tab`, async ({ newHomePage }) => { + await newHomePage.open_custom(`${url}/token/${staticData.erc721.address}`) + await newHomePage.checkERC721Inventory(staticData.erc721) + await newHomePage.open_custom(`${url}/token/${staticData.erc721.address}/instance/${staticData.erc721.instance}`) + await newHomePage.checkInventoryERC721Element(staticData.erc721) + await newHomePage.checkInventoryERC721MetadataTab(staticData.erc721) +}) + +test(`@OnDemandSmoke Check ERC-404 inventory tab`, async ({ newHomePage }) => { + await newHomePage.open_custom(`${url}/token/${staticData.erc404.address}`) + await newHomePage.checkERC404Inventory(staticData.erc404) + await newHomePage.open_custom(`${url}/token/${staticData.erc404.address}/instance/${staticData.erc404.instance}`) + await newHomePage.checkInventoryERC404Element(staticData.erc404) + await newHomePage.checkInventoryERC404MetadataTab(staticData.erc404) +}) + +test(`@OnDemandSmoke Check ERC-1155 inventory tab`, async ({ newHomePage }) => { + await newHomePage.open_custom(`${url}/token/${staticData.erc1155.address}`) + await newHomePage.checkERC1155Inventory(staticData.erc1155) + await newHomePage.open_custom(`${url}/token/${staticData.erc1155.address}/instance/${staticData.erc1155.instance}`) + await newHomePage.checkInventoryERC1155Element(staticData.erc1155) + await newHomePage.checkInventoryERC1155MetadataTab(staticData.erc1155) +})