Skip to content

Commit

Permalink
test(renterd): files and uploads pagination
Browse files Browse the repository at this point in the history
  • Loading branch information
alexfreska committed Dec 20, 2024
1 parent 2f3f9ab commit e983c15
Show file tree
Hide file tree
Showing 21 changed files with 588 additions and 93 deletions.
5 changes: 5 additions & 0 deletions .changeset/stupid-clocks-fail.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@siafoundation/design-system': minor
---

useTableState now supports a defaultSortDirection.
15 changes: 14 additions & 1 deletion apps/hostd-e2e/src/fixtures/contracts.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Page } from '@playwright/test'
import { expect, Page } from '@playwright/test'
import { maybeExpectAndReturn, step } from '@siafoundation/e2e'

export const getContractRowById = step(
Expand Down Expand Up @@ -33,6 +33,19 @@ export const getContractRowByIndex = step(
}
)

export const expectContractRowByIndex = step(
'expect contract row by index',
async (page: Page, index: number) => {
return expect(
page
.getByTestId('contractsTable')
.locator('tbody')
.getByRole('row')
.nth(index)
).toBeVisible()
}
)

export function getContractRows(page: Page) {
return page.getByTestId('contractsTable').locator('tbody').getByRole('row')
}
Expand Down
47 changes: 42 additions & 5 deletions apps/hostd-e2e/src/fixtures/volumes.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
import { Page, expect } from '@playwright/test'
import { navigateToVolumes } from './navigate'
import { fillTextInputByName, step } from '@siafoundation/e2e'
import {
clearToasts,
fillTextInputByName,
maybeExpectAndReturn,
step,
} from '@siafoundation/e2e'

export const createVolume = step(
'create volume',
Expand All @@ -20,9 +25,10 @@ export const createVolume = step(
await page.locator('input[name=size]').press('Enter')
await expect(page.getByRole('dialog')).toBeHidden()
const row = page.getByRole('row', { name: fullPath })
await expect(page.getByText('Volume created')).toBeVisible()
await expect(page.getByText('New volume created')).toBeVisible()
await expect(row.getByText('ready')).toBeVisible()
await expect(page.getByRole('cell', { name: fullPath })).toBeVisible()
await clearToasts({ page })
}
)

Expand All @@ -47,7 +53,7 @@ export const deleteVolumeIfExists = step(
'delete volume if exists',
async (page: Page, name: string, path: string) => {
const doesVolumeExist = await page
.getByRole('table')
.getByTestId('volumesTable')
.getByText(path + '/' + name)
.isVisible()
if (doesVolumeExist) {
Expand All @@ -67,16 +73,47 @@ export const openVolumeContextMenu = step(
}
)

export function getVolumeRows(page: Page) {
return page.getByTestId('volumesTable').locator('tbody').getByRole('row')
}

export const volumeInList = step(
'volume in list',
async (page: Page, name: string) => {
await expect(page.getByRole('table').getByText(name)).toBeVisible()
await expect(page.getByTestId('volumesTable').getByText(name)).toBeVisible()
}
)

export const volumeNotInList = step(
'volume not in list',
async (page: Page, name: string) => {
await expect(page.getByRole('table').getByText(name)).toBeHidden()
await expect(page.getByTestId('volumesTable').getByText(name)).toBeHidden()
}
)

export const getVolumeRowByIndex = step(
'get volume row by index',
async (page: Page, index: number, shouldExpect?: boolean) => {
return maybeExpectAndReturn(
page
.getByTestId('volumesTable')
.locator('tbody')
.getByRole('row')
.nth(index),
shouldExpect
)
}
)

export const expectVolumeRowByIndex = step(
'expect volume row by index',
async (page: Page, index: number) => {
return expect(
page
.getByTestId('volumesTable')
.locator('tbody')
.getByRole('row')
.nth(index)
).toBeVisible()
}
)
69 changes: 62 additions & 7 deletions apps/hostd-e2e/src/specs/contracts.spec.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
import { test, expect } from '@playwright/test'
import { test, expect, Page } from '@playwright/test'
import { navigateToContracts } from '../fixtures/navigate'
import { afterTest, beforeTest } from '../fixtures/beforeTest'
import {
getContractRowByIndex,
expectContractRowByIndex,
getContractRows,
getContractRowsAll,
} from '../fixtures/contracts'
import { ContractsResponse, contractsRoute } from '@siafoundation/hostd-types'

test.beforeEach(async ({ page }) => {
await beforeTest(page, {
renterdCount: 2,
renterdCount: 3,
})
})

Expand All @@ -20,15 +21,15 @@ test.afterEach(async () => {
test('contracts bulk integrity check', async ({ page }) => {
await navigateToContracts(page)
const rows = await getContractRowsAll(page)
rows.at(0).click()
rows.at(-1).click({ modifiers: ['Shift'] })
await rows.at(0).click({ position: { x: 5, y: 5 } })
await rows.at(2).click({ modifiers: ['Shift'] })

const menu = page.getByLabel('contract multi-select menu')

// Run check for each contract.
await menu.getByLabel('run integrity check for each contract').click()
await expect(
page.getByText('Integrity checks started for 2 contracts')
page.getByText('Integrity checks started for 3 contracts')
).toBeVisible()
})

Expand All @@ -51,5 +52,59 @@ test('viewing a page with no data shows the correct empty state', async ({
await expect(page.getByText('Back to first page')).toBeVisible()
await page.getByText('Back to first page').click()
// Ensure we are now seeing rows of data.
await getContractRowByIndex(page, 0, true)
await expectContractRowByIndex(page, 0)
})

test('paginating contracts with known total and client side pagination', async ({
page,
}) => {
await interceptApiContactsAndEnsure3Results(page)
await navigateToContracts(page)
const url = page.url()
await page.goto(url + '?limit=1')

const first = page.getByRole('button', { name: 'go to first page' })
const previous = page.getByRole('button', { name: 'go to previous page' })
const next = page.getByRole('button', { name: 'go to next page' })
const last = page.getByRole('button', { name: 'go to last page' })
const rows = getContractRows(page)
await expect(rows).toHaveCount(1)
await expect(first).toBeDisabled()
await expect(previous).toBeDisabled()
await expect(next).toBeEnabled()
await expect(last).toBeEnabled()
await next.click()
await expect(rows).toHaveCount(1)
await expect(first).toBeEnabled()
await expect(previous).toBeEnabled()
await expect(next).toBeEnabled()
await expect(last).toBeEnabled()
await next.click()
await expect(rows).toHaveCount(1)
await expect(first).toBeEnabled()
await expect(previous).toBeEnabled()
await expect(next).toBeDisabled()
await expect(last).toBeDisabled()
})

async function interceptApiContactsAndEnsure3Results(page: Page) {
await page.route(`**/api${contractsRoute}*`, async (route) => {
console.log('Intercepted contracts API request')
// Fetch the original response.
const response = await route.fetch()

// Parse the response body as JSON.
const originalData: ContractsResponse = await response.json()

// Slice the contracts down to exactly 3 items.
const modifiedData: ContractsResponse = {
contracts: originalData.contracts.slice(0, 3),
count: Math.min(3, originalData.count),
}

// Fulfill the route with the modified response.
await route.fulfill({
json: modifiedData,
})
})
}
83 changes: 82 additions & 1 deletion apps/hostd-e2e/src/specs/volumes.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,16 @@ import { navigateToVolumes } from '../fixtures/navigate'
import {
createVolume,
deleteVolume,
expectVolumeRowByIndex,
getVolumeRows,
openVolumeContextMenu,
volumeInList,
} from '../fixtures/volumes'
import { afterTest, beforeTest } from '../fixtures/beforeTest'
import fs from 'fs'
import os from 'os'
import { fillTextInputByName } from '@siafoundation/e2e'
import path from 'path'

let dirPath = '/'

Expand Down Expand Up @@ -36,7 +40,7 @@ test('can resize volume', async ({ page }) => {
await createVolume(page, name, dirPath)
await openVolumeContextMenu(page, `${dirPath}/${name}`)
await page.getByText('Resize').click()
await fillTextInputByName(page, 'size', '1300')
await fillTextInputByName(page, 'size', '1300000')
const dialog = page.getByRole('dialog')
await expect(dialog.getByText('Must be between 10.00 GB')).toBeVisible()
await fillTextInputByName(page, 'size', '13')
Expand All @@ -46,3 +50,80 @@ test('can resize volume', async ({ page }) => {
await expect(page.getByText('Volume resizing')).toBeVisible()
await expect(page.getByText('resizing')).toBeVisible()
})

test('paginating volumes with known total and client side pagination', async ({
page,
}) => {
await navigateToVolumes({ page })
// The cluster creates an initial volume so the following is the second volume.
await createVolume(page, 'v2', dirPath)
await createVolume(page, 'v3', dirPath)
const url = page.url()
await page.goto(url + '?limit=1')

const first = page.getByRole('button', { name: 'go to first page' })
const previous = page.getByRole('button', { name: 'go to previous page' })
const next = page.getByRole('button', { name: 'go to next page' })
const last = page.getByRole('button', { name: 'go to last page' })
await expect(getVolumeRows(page).getByText('sia-cluster')).toBeVisible()
await expect(first).toBeDisabled()
await expect(previous).toBeDisabled()
await expect(next).toBeEnabled()
await expect(last).toBeEnabled()
await next.click()
await volumeInList(page, getVolumePath(dirPath, 'v2'))
await expect(first).toBeEnabled()
await expect(previous).toBeEnabled()
await expect(next).toBeEnabled()
await expect(last).toBeEnabled()
await next.click()
await volumeInList(page, getVolumePath(dirPath, 'v3'))
await expect(first).toBeEnabled()
await expect(previous).toBeEnabled()
await expect(next).toBeDisabled()
await expect(last).toBeDisabled()
})

test('viewing a page with no data shows the correct empty state', async ({
page,
}) => {
await navigateToVolumes({ page })
// The cluster creates an initial volume so the following is the second volume.
await createVolume(page, 'v2', dirPath)
const url = page.url()
await page.goto(url + '?limit=1')

const first = page.getByRole('button', { name: 'go to first page' })
const previous = page.getByRole('button', { name: 'go to previous page' })
const next = page.getByRole('button', { name: 'go to next page' })
const last = page.getByRole('button', { name: 'go to last page' })
await expect(getVolumeRows(page).getByText('sia-cluster')).toBeVisible()
await expect(first).toBeDisabled()
await expect(previous).toBeDisabled()
await expect(next).toBeEnabled()
await expect(last).toBeEnabled()
await next.click()
await volumeInList(page, getVolumePath(dirPath, 'v2'))
await expect(first).toBeEnabled()
await expect(previous).toBeEnabled()
await expect(next).toBeDisabled()
await expect(last).toBeDisabled()

await deleteVolume(page, 'v2', dirPath)

await expect(
page.getByText('No data on this page, reset pagination to continue.')
).toBeVisible()
await expect(page.getByText('Back to first page')).toBeVisible()
await page.getByText('Back to first page').click()
// Ensure we are now seeing rows of data.
await expectVolumeRowByIndex(page, 0)
await expect(first).toBeDisabled()
await expect(previous).toBeDisabled()
await expect(next).toBeDisabled()
await expect(last).toBeDisabled()
})

function getVolumePath(dirPath: string, name: string) {
return path.join(dirPath, name)
}
4 changes: 2 additions & 2 deletions apps/hostd/components/Volumes/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@ import { useVolumes } from '../../contexts/volumes'
import { StateNoneYet } from './StateNoneYet'

export function Volumes() {
const { dataset, datasetState, isLoading, visibleColumns } = useVolumes()
const { datasetPage, datasetState, isLoading, visibleColumns } = useVolumes()
return (
<div className="p-6 min-w-fit">
<Table
testId="volumesTable"
isLoading={isLoading}
pageSize={20}
data={dataset}
data={datasetPage}
columns={visibleColumns}
emptyState={
<EmptyState datasetState={datasetState} noneYet={<StateNoneYet />} />
Expand Down
3 changes: 3 additions & 0 deletions apps/hostd/contexts/volumes/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ function useVolumesMain() {
} = useTableState('hostd/v0/volumes', {
columns,
columnsDefaultVisible,
defaultSortField: 'id',
defaultSortDirection: 'asc',
})

const response = useVolumesData({
Expand Down Expand Up @@ -68,6 +70,7 @@ function useVolumesMain() {
datasetPage,
isValidating,
error,
offset,
})

return {
Expand Down
Loading

0 comments on commit e983c15

Please sign in to comment.