Skip to content

Commit

Permalink
test(explorer): add e2e tests for all routes
Browse files Browse the repository at this point in the history
  • Loading branch information
telestrial committed Nov 4, 2024
1 parent d37289d commit 868d062
Show file tree
Hide file tree
Showing 16 changed files with 449 additions and 23 deletions.
20 changes: 20 additions & 0 deletions apps/explorer-e2e/src/fixtures/ExplorerApp.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { Locator, Page } from 'playwright'
import { SEARCHBAR } from './constants'

export class ExplorerApp {
private readonly searchBar: Locator

constructor(public readonly page: Page) {
this.searchBar = this.page.locator(SEARCHBAR)
}

async navigateBySearchBar(searchTerm: string) {
await this.searchBar.click()
await this.searchBar.fill(searchTerm)
await this.page.keyboard.press('Enter')
}

async goTo(url: string) {
await this.page.goto(url)
}
}
92 changes: 92 additions & 0 deletions apps/explorer-e2e/src/fixtures/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
// General/Shared
export const SEARCHBAR = `input[name="query"]`
export const APP_NAME = 'explorer-identity-appName'
export const APP_NETWORK = 'explorer-identity-network'
export const DATUM_VALUE = 'explorer-datum-value'

// Home page
export const METRICS_ITEM = 'explorer-metrics-item'
export const LATEST_BLOCKS_ITEM = 'explorer-latestBlocks-item'
export const TOP_HOSTS_ITEMS = 'explorer-topHosts-item'

// Contract page
export const RENEWED_FROM_BUTTON = 'explorer-contract-renewedFrom'
export const RENEWED_TO_BUTTON = 'explorer-contract-renewedTo'

// Host page
export const HOST_PRICING = 'explorer-hostPricing'
export const HOST_SETTINGS_PATTERNS = [
'\\b\\d+(?:\\.\\d{1,2})?\\s?(TB|GB|MB|KB|B)\\b', // 3.00 TB
'(\\$\\d+(?:\\.\\d{1,3})?|\\d+(?:\\.\\d{1,3})?\\s?[A-Z]{2})\\/(TB|GB|MB|KB|B)\\/(month|year|day)', // $1.16/TB/month
'(\\$\\d+(?:\\.\\d{1,3})?|\\d+(?:\\.\\d{1,3})?\\s?[A-Z]{2})\\/(TB|GB|MB|KB|B)', // $1.12/TB
'\\b\\d+\\s?(days?|months?|years?)\\b', // 6 months
'^\\$\\d+(?:\\.\\d+)?$', // $4.50
'(\\$\\d+(?:\\.\\d+)?|\\d+(?:\\.\\d+)?\\s?[A-Z]{2})\\/million', // $0.0045 SC/million
'^\\d+(?:\\.\\d+)?\\s?(KS|mS)$', // 1.000 KS/mS
'\\b\\d+\\s?blocks\\b', // 25920 blocks
'^(6553[0-5]|(655[0-2][0-9]|64[0-9]{3}|6[0-3][0-9]{2}|[1-5]?[0-9]{0,4}))$', // 9883
'^\\d{1,16}$', // 2592000000000000
'\\b\\d+(?:\\.\\d{1,2})?\\s?(Mbps|Gbps|Kbps)\\b', // 998.64 Mbps
]

// Test data
export const TEST_BLOCK_1 = {
height: '25000',
id: '0000000016920b69bfbe192005cfed7c9c5369bb83ff8406320f77f74f7b40cb',
display: {
title: 'Block 25,000',
blockHash: '000000...7b40cb',
minerPayoutAddress: '13caad1b4eea...',
transactionHeader: '2 transactions',
lastTransactionID: '8a03682f2285...',
},
}

export const TEST_TX_1 = {
id: '8a03682f22857f306a95f55a28fa9edf111e1d11803f8b4a91bd01d2803a4eb6',
display: {
title: 'Transaction 8a03682f22857f3...',
confirmationHeight: '25,000',
numberOfConfirmations: '72+ confirmations',
inputAddress: '83cc8e810db6...',
outputAddress: 'e3ed23ed389f...',
inputAmount: '+8.979 KS',
outputAmount: '+107.674 SC',
contractID: '25c94822bf7b...',
},
}

export const TEST_ADDRESS_1 = {
id: '68bf48e81536f2221f3809aa9d1c89c1c869a17c6f186a088e49fd2605e4bfaaa24f26e4c42c',
display: {
title: 'Address 68bf48e81536f22...',
transactionNumber: '500 transactions',
amount: '1.262 MS',
transactionID: '23e427949a63...',
},
}

export const TEST_CONTRACT_1 = {
id: '25c94822bf7bd86a92d28a148d9d30151949f3599bf93af0df7b4f1e1b3c990d',
renewedFromTitle: 'Contract 494d147a8028217...',
renewedToTitle: 'Contract 43a81d1a21ebf6f...',
display: {
title: 'Contract 25c94822bf7bd86...',
transactionID: 'a3a4a6808e33...',
unlockHash: 'c50b70e7dd79...',
revisionNumber: '18,446,744,0...',
status: 'obligation succeeded',
missedProofTitle: 'Missed proof outputs (2)',
validProofTitle: 'Valid proof outputs (2)',
},
}

export const TEST_HOST_1 = {
pubkey:
'ed25519:3926a0434232bba9eaca2041303a1039d4f65bf54d7bd4e2a9164ea2d778b714',
display: {
pubkey: 'ed25519:3926a0434232bba9eaca20...',
title: '51.81.242.140:9882',
location: 'US',
},
}
52 changes: 52 additions & 0 deletions apps/explorer-e2e/src/specs/address.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { test, expect } from '@playwright/test'
import { ExplorerApp } from '../fixtures/ExplorerApp'
import { TEST_ADDRESS_1 } from '../fixtures/constants'

let explorerApp: ExplorerApp

test.beforeEach(async ({ page }) => {
explorerApp = new ExplorerApp(page)
})

test('address can be searched by id', async ({ page }) => {
await explorerApp.goTo('/')
await explorerApp.navigateBySearchBar(TEST_ADDRESS_1.id)

await expect(page.getByText(TEST_ADDRESS_1.display.title)).toBeVisible()
})

test('address can be directly navigated to by id', async ({ page }) => {
await explorerApp.goTo('/address/' + TEST_ADDRESS_1.id)

await expect(page.getByText(TEST_ADDRESS_1.display.title)).toBeVisible()
})

test('address displays the intended data', async ({ page }) => {
const displayKeys = Object.keys(TEST_ADDRESS_1.display)

await explorerApp.goTo('/address/' + TEST_ADDRESS_1.id)

for (const key of displayKeys) {
const currentProperty = TEST_ADDRESS_1.display[key]
await expect(page.getByText(currentProperty)).toBeVisible()
}
})

test('address can navigate to the unspent outputs list', async ({ page }) => {
await explorerApp.goTo('/address/' + TEST_ADDRESS_1.id)
await page.getByRole('tab').getByText('Unspent outputs').click()

await expect(page.getByText('073b0cbbdd6f...')).toBeVisible()
})

test('address can navigate through to a transaction', async ({ page }) => {
await explorerApp.goTo('/address/' + TEST_ADDRESS_1.id)

await page
.getByTestId(
'entity-link-/tx/23e427949a6360014602cff7cfa9aa0c3b87765b94c6b37accfb661d3365336a'
)
.click()

await expect(page.getByText('Transaction 23e427949a63600...')).toBeVisible()
})
57 changes: 57 additions & 0 deletions apps/explorer-e2e/src/specs/block.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { test, expect } from '@playwright/test'
import { ExplorerApp } from '../fixtures/ExplorerApp'
import { TEST_BLOCK_1 } from '../fixtures/constants'

let explorerApp: ExplorerApp

test.beforeEach(async ({ page }) => {
explorerApp = new ExplorerApp(page)
})

test('block can be searched by height', async ({ page }) => {
await explorerApp.goTo('/')
await explorerApp.navigateBySearchBar(TEST_BLOCK_1.height)

await expect(page.getByText(TEST_BLOCK_1.display.title).nth(0)).toBeVisible()
})

test('block can be searched by id', async ({ page }) => {
await explorerApp.goTo('/')
await explorerApp.navigateBySearchBar(TEST_BLOCK_1.id)

await expect(page.getByText(TEST_BLOCK_1.display.title).nth(0)).toBeVisible()
})

test('block can be directly navigated to by height', async ({ page }) => {
await explorerApp.goTo('/block/' + TEST_BLOCK_1.height)

await expect(page.getByText(TEST_BLOCK_1.display.title).nth(0)).toBeVisible()
})

test('block can be directly navigated to by id', async ({ page }) => {
await explorerApp.goTo('/block/' + TEST_BLOCK_1.id)

await expect(page.getByText(TEST_BLOCK_1.display.title).nth(0)).toBeVisible()
})

test('block can click through to a transaction', async ({ page }) => {
await explorerApp.goTo('/block/' + TEST_BLOCK_1.id)
await page
.getByTestId(
'entity-link-/tx/8a03682f22857f306a95f55a28fa9edf111e1d11803f8b4a91bd01d2803a4eb6'
)
.click()

await expect(page.getByText('Transaction 8a03682f22857f3...')).toBeVisible()
})

test('block displays the intended data', async ({ page }) => {
const displayKeys = Object.keys(TEST_BLOCK_1.display)

await explorerApp.goTo('/block/' + TEST_BLOCK_1.height)

for (const key of displayKeys) {
const currentProperty = TEST_BLOCK_1.display[key]
await expect(page.getByText(currentProperty)).toBeVisible()
}
})
51 changes: 51 additions & 0 deletions apps/explorer-e2e/src/specs/contract.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { test, expect } from '@playwright/test'
import { ExplorerApp } from '../fixtures/ExplorerApp'
import {
RENEWED_FROM_BUTTON,
RENEWED_TO_BUTTON,
TEST_CONTRACT_1,
} from '../fixtures/constants'

let explorerApp: ExplorerApp

test.beforeEach(async ({ page }) => {
explorerApp = new ExplorerApp(page)
})

test('contract can be searched by id', async ({ page }) => {
await explorerApp.goTo('/')
await explorerApp.navigateBySearchBar(TEST_CONTRACT_1.id)

await expect(page.getByText(TEST_CONTRACT_1.display.title)).toBeVisible()
})

test('contract can be directly navigated to', async ({ page }) => {
await explorerApp.goTo('/contract/' + TEST_CONTRACT_1.id)

await expect(page.getByText(TEST_CONTRACT_1.display.title)).toBeVisible()
})

test('contract displays the intended data', async ({ page }) => {
const displayKeys = Object.keys(TEST_CONTRACT_1.display)

await explorerApp.goTo('/contract/' + TEST_CONTRACT_1.id)

for (const key of displayKeys) {
const currentProperty = TEST_CONTRACT_1.display[key]
await expect(page.getByText(currentProperty)).toBeVisible()
}
})

test('contract can navigate to renewed from contract', async ({ page }) => {
await explorerApp.goTo('/contract/' + TEST_CONTRACT_1.id)
await page.getByTestId(RENEWED_FROM_BUTTON).click()

await expect(page.getByText(TEST_CONTRACT_1.renewedFromTitle)).toBeVisible()
})

test('contract can navigate to renewed to contract', async ({ page }) => {
await explorerApp.goTo('/contract/' + TEST_CONTRACT_1.id)
await page.getByTestId(RENEWED_TO_BUTTON).click()

await expect(page.getByText(TEST_CONTRACT_1.renewedToTitle)).toBeVisible()
})
37 changes: 37 additions & 0 deletions apps/explorer-e2e/src/specs/home.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { test, expect } from '@playwright/test'
import { ExplorerApp } from '../fixtures/ExplorerApp'
import {
APP_NAME,
APP_NETWORK,
LATEST_BLOCKS_ITEM,
METRICS_ITEM,
TOP_HOSTS_ITEMS,
} from '../fixtures/constants'

let explorerApp: ExplorerApp

test.beforeEach(async ({ page }) => {
explorerApp = new ExplorerApp(page)

await explorerApp.goTo('/')
})

test('home displays app identity', async ({ page }) => {
await expect(page.getByTestId(APP_NAME)).toContainText('siascan')
await expect(page.getByTestId(APP_NETWORK)).toContainText('zen') // Can we get this via environmental variables? Will we always run these on zen?
})

// The expectations below have their boundary around the display of a sucessful network call.
// If the call fails, we fail, but if the call's contents change, we do not fail, as long as
// we display something.
test('home displays metrics', async ({ page }) => {
expect((await page.getByTestId(METRICS_ITEM).count()) > 0).toBeTruthy()
})

test('home displays latest blocks', async ({ page }) => {
expect((await page.getByTestId(LATEST_BLOCKS_ITEM).count()) > 0).toBeTruthy()
})

test('home displays top hosts', async ({ page }) => {
expect((await page.getByTestId(TOP_HOSTS_ITEMS).count()) > 0).toBeTruthy()
})
59 changes: 59 additions & 0 deletions apps/explorer-e2e/src/specs/host.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import { test, expect } from '@playwright/test'
import { ExplorerApp } from '../fixtures/ExplorerApp'
import {
DATUM_VALUE,
TEST_HOST_1,
HOST_SETTINGS_PATTERNS,
HOST_PRICING,
} from '../fixtures/constants'

// Hosts can not be guaranteed like other test slices. Therefore, we'll grab

let explorerApp: ExplorerApp

test.beforeEach(async ({ page }) => {
explorerApp = new ExplorerApp(page)
})

test('host can be searched by id', async ({ page }) => {
await explorerApp.goTo('/')
await explorerApp.navigateBySearchBar(TEST_HOST_1.pubkey)

await expect(page.getByText(TEST_HOST_1.display.title)).toBeVisible()
})

test('host can be directly navigated to', async ({ page }) => {
await explorerApp.goTo('/host/' + TEST_HOST_1.pubkey)

await expect(page.getByText(TEST_HOST_1.display.title)).toBeVisible()
})

test('host displays properly formatted host pricing', async ({ page }) => {
await explorerApp.goTo('/host/' + TEST_HOST_1.pubkey)

const hostSettings = await page.getByTestId(HOST_PRICING).allInnerTexts()

for (const text of hostSettings) {
const matched = HOST_SETTINGS_PATTERNS.some((pattern) => {
return text.match(new RegExp(pattern))
})
// If we're failing, uncomment this and check console.
// console.log(matched || text)
expect(matched).toBe(true)
}
})

test('host displays properly formatted host settings', async ({ page }) => {
await explorerApp.goTo('/host/' + TEST_HOST_1.pubkey)

const hostSettings = await page.getByTestId(DATUM_VALUE).allInnerTexts()

for (const text of hostSettings) {
const matched = HOST_SETTINGS_PATTERNS.some((pattern) => {
return text.match(new RegExp(pattern))
})
// If we're failing, uncomment this and check console.
// console.log(matched || text)
expect(matched).toBe(true)
}
})
17 changes: 0 additions & 17 deletions apps/explorer-e2e/src/specs/index.spec.ts

This file was deleted.

Loading

0 comments on commit 868d062

Please sign in to comment.