Skip to content

Commit

Permalink
fix: renterd file rename, add e2e tests
Browse files Browse the repository at this point in the history
  • Loading branch information
alexfreska committed Jun 20, 2024
1 parent 1a4a2bd commit b573d33
Show file tree
Hide file tree
Showing 27 changed files with 419 additions and 109 deletions.
5 changes: 5 additions & 0 deletions .changeset/gentle-countries-cry.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@siafoundation/design-system': minor
---

Updated react-hook-form.
5 changes: 5 additions & 0 deletions .changeset/hip-turkeys-remember.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'renterd': patch
---

Fixed an issue where renaming a file would throw an error.
5 changes: 5 additions & 0 deletions .changeset/short-bears-grab.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'renterd': patch
---

Fixed an issue where the empty directory state was showing an empty bucket message.
16 changes: 16 additions & 0 deletions apps/renterd-e2e/src/fixtures/beforeTest.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { mockApiSiaCentralExchangeRates } from '@siafoundation/sia-central-mock'
import { login } from './login'
import { navigateToConfig } from './navigate'
import { configResetAllSettings } from './configResetAllSettings'
import { setViewMode } from './configViewMode'
import { Page } from 'playwright'

export async function beforeTest(page: Page) {
await mockApiSiaCentralExchangeRates({ page })
await login({ page })

// Reset state.
await navigateToConfig({ page })
await configResetAllSettings({ page })
await setViewMode({ page, state: 'basic' })
}
39 changes: 36 additions & 3 deletions apps/renterd-e2e/src/fixtures/buckets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { Page, expect } from '@playwright/test'
import { navigateToBuckets } from './navigate'
import { fillTextInputByName } from './textInput'
import { clearToasts } from './clearToasts'
import { deleteDirectory, deleteFile } from './files'

export async function createBucket(page: Page, name: string) {
await navigateToBuckets({ page })
Expand All @@ -25,10 +26,36 @@ export async function deleteBucket(page: Page, name: string) {

export async function deleteBucketIfExists(page: Page, name: string) {
const doesBucketExist = await page
.getByRole('table')
.getByTestId('bucketsTable')
.getByText(name)
.isVisible()
if (doesBucketExist) {
await openBucket(page, name)
// The list changes to filesTable and is still loading=false for a split second
// before the files start fetching - this is why we need to wait for 1000ms.
// eslint-disable-next-line playwright/no-wait-for-timeout
await page.waitForTimeout(1000)
await expect(
page.locator('[data-testid=filesTable][data-loading=false]')
).toBeVisible()
const tableRows = await page
.getByTestId('filesTable')
.getByTestId(new RegExp(`${name}.*`))
.all()
// First delete all top-level objects in the bucket, because a bucket
// can't be deleted if there are objects in it.
for (const row of tableRows) {
const id = await row.getAttribute('data-testid')
if (id === '..') {
continue
}
if (id?.endsWith('/')) {
await deleteDirectory(page, id)
} else {
await deleteFile(page, id)
}
}
await navigateToBuckets({ page })
await deleteBucket(page, name)
}
}
Expand All @@ -37,10 +64,16 @@ export async function openBucketContextMenu(page: Page, name: string) {
await page.getByRole('row', { name }).getByRole('button').first().click()
}

export async function openBucket(page: Page, name: string) {
await page.getByRole('row').getByText(name).click()
await expect(page.getByTestId('navbar').getByText(name)).toBeVisible()
await expect(page.getByLabel('Upload files')).toBeVisible()
}

export async function bucketInList(page: Page, name: string) {
await expect(page.getByRole('table').getByText(name)).toBeVisible()
await expect(page.getByTestId('bucketsTable').getByText(name)).toBeVisible()
}

export async function bucketNotInList(page: Page, name: string) {
await expect(page.getByRole('table').getByText(name)).toBeHidden()
await expect(page.getByTestId('bucketsTable').getByText(name)).toBeHidden()
}
2 changes: 1 addition & 1 deletion apps/renterd-e2e/src/fixtures/configResetAllSettings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export async function configResetAllSettings({ page }: { page: Page }) {
await fillTextInputByName(page, 'storageTB', '1')
await fillTextInputByName(page, 'uploadTBMonth', '1')
await fillTextInputByName(page, 'downloadTBMonth', '1')
await fillTextInputByName(page, 'allowanceMonth', '1')
await fillTextInputByName(page, 'allowanceMonth', '21000')
await fillTextInputByName(page, 'periodWeeks', '6')
await fillTextInputByName(page, 'renewWindowWeeks', '2')
await fillTextInputByName(page, 'amountHosts', '12')
Expand Down
115 changes: 115 additions & 0 deletions apps/renterd-e2e/src/fixtures/files.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
import { Page, expect } from '@playwright/test'
import { readFileSync } from 'fs'
import { fillTextInputByName } from './textInput'

export async function deleteFile(page: Page, path: string) {
await openFileContextMenu(page, path)
await page.getByRole('menuitem', { name: 'Delete file' }).click()
await expect(page.getByRole('dialog').getByText('Delete file')).toBeVisible()
await page.locator('form button[type=submit]').click()
await expect(page.getByRole('dialog')).toBeHidden()
await fileNotInList(page, path)
}

export async function deleteFileIfExists(page: Page, path: string) {
const exists = await page.getByRole('table').getByTestId(path).isVisible()
if (exists) {
await deleteFile(page, path)
}
}

export async function deleteDirectory(page: Page, path: string) {
await openDirectoryContextMenu(page, path)
const deleteDirectoryItem = page.getByRole('menuitem', {
name: 'Delete directory',
})
await expect(deleteDirectoryItem).toBeVisible()
await deleteDirectoryItem.click()
await expect(
page.getByRole('dialog').getByText('Delete directory')
).toBeVisible()
await page.locator('form button[type=submit]').click()
await expect(page.getByRole('dialog')).toBeHidden()
await fileNotInList(page, path)
}

export async function deleteDirectoryIfExists(page: Page, path: string) {
const exists = await page.getByRole('table').getByTestId(path).isVisible()
if (exists) {
await deleteDirectory(page, path)
}
}

export async function openDirectoryContextMenu(page: Page, path: string) {
const selector = page.getByTestId(path).getByLabel('Directory context menu')
// Click doesn't work until animation is finished.
// eslint-disable-next-line playwright/no-wait-for-timeout
await page.waitForTimeout(100)
await expect(selector).toBeVisible()
await selector.click()
}

export async function openFileContextMenu(page: Page, path: string) {
const selector = page.getByTestId(path).getByLabel('File context menu')
await expect(selector).toBeVisible()
await selector.click()
}

export async function openDirectory(page: Page, path: string) {
await page.getByRole('table').getByTestId(path).click()
for (const dir of path.split('/').slice(0, -1)) {
await expect(page.getByTestId('navbar').getByText(dir)).toBeVisible()
}
}

export async function crateDirectory(page: Page, name: string) {
await expect(page.getByLabel('Create directory')).toBeVisible()
await page.getByLabel('Create directory').click()
await fillTextInputByName(page, 'name', name)
await page.locator('input[name=name]').press('Enter')
await expect(page.getByRole('dialog')).toBeHidden()
}

export async function createDirectoryIfNotExists(page: Page, name: string) {
const exists = await page.getByRole('table').getByTestId(name).isVisible()
if (!exists) {
await crateDirectory(page, name)
}
}

export async function fileInList(page: Page, path: string) {
await expect(page.getByRole('table').getByTestId(path)).toBeVisible()
}

export async function fileNotInList(page: Page, path: string) {
await expect(page.getByRole('table').getByTestId(path)).toBeHidden()
}

export async function dragAndDropFile(
page: Page,
selector: string,
filePath: string,
fileName: string,
fileType = ''
) {
const buffer = readFileSync(filePath).toString('base64')

const dataTransfer = await page.evaluateHandle(
async ({ bufferData, localFileName, localFileType }) => {
const dt = new DataTransfer()

const blobData = await fetch(bufferData).then((res) => res.blob())

const file = new File([blobData], localFileName, { type: localFileType })
dt.items.add(file)
return dt
},
{
bufferData: `data:application/octet-stream;base64,${buffer}`,
localFileName: fileName,
localFileType: fileType,
}
)

await page.dispatchEvent(selector, 'drop', { dataTransfer })
}
4 changes: 2 additions & 2 deletions apps/renterd-e2e/src/fixtures/navigate.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { Page, expect } from '@playwright/test'

export async function navigateToBuckets({ page }: { page: Page }) {
await page.getByLabel('Files').click()
await page.getByTestId('sidenav').getByLabel('Files').click()
await expect(page.getByTestId('navbar').getByText('Buckets')).toBeVisible()
}

export async function navigateToConfig({ page }: { page: Page }) {
await page.getByLabel('Configuration').click()
await page.getByTestId('sidenav').getByLabel('Configuration').click()
await expect(
page.getByTestId('navbar').getByText('Configuration')
).toBeVisible()
Expand Down
9 changes: 6 additions & 3 deletions apps/renterd-e2e/src/specs/buckets.spec.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,19 @@
import { test, expect } from '@playwright/test'
import { navigateToBuckets } from '../fixtures/navigate'
import { login } from '../fixtures/login'
import {
bucketInList,
createBucket,
deleteBucket,
deleteBucketIfExists,
openBucketContextMenu,
} from '../fixtures/buckets'
import { beforeTest } from '../fixtures/beforeTest'

test.beforeEach(async ({ page }) => {
await beforeTest(page)
})

test('can change a buckets policy', async ({ page }) => {
await login({ page })
await navigateToBuckets({ page })
await openBucketContextMenu(page, 'default')
await page.getByRole('menuitem', { name: 'Change policy' }).click()
Expand All @@ -22,7 +25,7 @@ test('can change a buckets policy', async ({ page }) => {
})

test('can create and delete a bucket', async ({ page }) => {
await login({ page })
await navigateToBuckets({ page })
await deleteBucketIfExists(page, 'my-new-bucket')
await createBucket(page, 'my-new-bucket')
await deleteBucket(page, 'my-new-bucket')
Expand Down
31 changes: 5 additions & 26 deletions apps/renterd-e2e/src/specs/config.spec.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,23 @@
import { test, expect } from '@playwright/test'
import { login } from '../fixtures/login'
import { expectSwitchByLabel, setSwitchByLabel } from '../fixtures/switchValue'
import { setViewMode } from '../fixtures/configViewMode'
import { navigateToConfig } from '../fixtures/navigate'
import { mockApiSiaCentralExchangeRates } from '@siafoundation/sia-central-mock'
import {
expectTextInputByName,
expectTextInputByNameAttribute,
fillTextInputByName,
} from '../fixtures/textInput'
import { configResetAllSettings } from '../fixtures/configResetAllSettings'
import { clearToasts } from '../fixtures/clearToasts'
import { clickIfEnabledAndWait, clickIf } from '../fixtures/click'
import { beforeTest } from '../fixtures/beforeTest'

test('basic field change and save behaviour', async ({ page }) => {
// Set up.
await mockApiSiaCentralExchangeRates({ page })
await login({ page })
test.beforeEach(async ({ page }) => {
await beforeTest(page)
})

test('basic field change and save behaviour', async ({ page }) => {
// Reset state.
await navigateToConfig({ page })
await configResetAllSettings({ page })
await setViewMode({ page, state: 'basic' })
await setSwitchByLabel(page, 'autoAllowance', true)

Expand Down Expand Up @@ -50,10 +47,6 @@ test('basic field change and save behaviour', async ({ page }) => {
test('estimate based off storage, pricing, and redundancy', async ({
page,
}) => {
// Set up.
await mockApiSiaCentralExchangeRates({ page })
await login({ page })

// Reset state.
await navigateToConfig({ page })
await setSwitchByLabel(page, 'autoAllowance', true)
Expand Down Expand Up @@ -84,10 +77,6 @@ test('estimate based off storage, pricing, and redundancy', async ({
})

test('configure with auto allowance', async ({ page }) => {
// Set up.
await mockApiSiaCentralExchangeRates({ page })
await login({ page })

// Reset state.
await navigateToConfig({ page })
await setSwitchByLabel(page, 'autoAllowance', true)
Expand All @@ -108,13 +97,8 @@ test('configure with auto allowance', async ({ page }) => {
})

test('configure allowance manually', async ({ page }) => {
// Set up.
await mockApiSiaCentralExchangeRates({ page })
await login({ page })

// Reset state.
await navigateToConfig({ page })
await configResetAllSettings({ page })
await setSwitchByLabel(page, 'autoAllowance', false)
await setViewMode({ page, state: 'basic' })
await fillTextInputByName(page, 'allowanceMonth', '777')
Expand All @@ -135,13 +119,8 @@ test('configure allowance manually', async ({ page }) => {
})

test('system offers recommendations', async ({ page }) => {
// Set up.
await mockApiSiaCentralExchangeRates({ page })
await login({ page })

// Reset state.
await navigateToConfig({ page })
await configResetAllSettings({ page })
await setViewMode({ page, state: 'basic' })
await setSwitchByLabel(page, 'autoAllowance', true)

Expand Down
Loading

0 comments on commit b573d33

Please sign in to comment.