Skip to content

Commit 37ee0ab

Browse files
committed
test(e2e): modernize selectors and add centralized fixtures
- add data-testid attributes to File, FilesList, FilesGrid, GridFile - create fixtures.js with worker-scoped peerNode fixture for speed - create locators.js with centralized selector definitions - replace brittle CSS selectors with getByRole/getByTestId - replace waitFor() calls with web-first assertions (toBeVisible, toHaveClass) - update coverage.js to re-export from fixtures.js for backward compat - modernize all test files to use shared locators and fixtures - add test/e2e/test/ to gitignore (artifact from running tests)
1 parent 3e3d2d4 commit 37ee0ab

File tree

18 files changed

+519
-358
lines changed

18 files changed

+519
-358
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
/playwright-report
99
test/e2e/setup/ipfs-backend.json
1010
test/e2e/state.json
11+
test/e2e/test/
1112
storybook-static
1213
test-results/
1314

src/files/file/File.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ const File = ({
106106

107107
return (
108108
<div ref={drop}>
109-
<div className={className} style={styles} onContextMenu={handleCtxRightClick} ref={drag}>
109+
<div className={className} style={styles} onContextMenu={handleCtxRightClick} ref={drag} data-testid="file-row" data-type={type}>
110110
<div className={checkBoxCls}>
111111
<Checkbox disabled={cantSelect} checked={selected} onChange={select} aria-label={ t('checkboxLabel', { name })} />
112112
</div>

src/files/files-grid/files-grid.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,7 @@ const FilesGrid = ({
170170
<div ref={(el) => {
171171
drop(el)
172172
gridRef.current = el
173-
}} className={gridClassName} tabIndex={0} role="grid" aria-label={t('filesGridLabel')}>
173+
}} className={gridClassName} tabIndex={0} role="grid" aria-label={t('filesGridLabel')} data-testid="files-grid">
174174
{files.map(file => (
175175
<GridFile
176176
key={file.name}

src/files/files-grid/grid-file.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,7 @@ const GridFile: FC<GridFilePropsConnected> = ({
183183
role="button"
184184
ref={refSetter}
185185
data-type={type}
186+
data-testid="grid-file"
186187
tabIndex={0}
187188
title={`${name}`}
188189
aria-label={t('fileLabel', { name, type, size: formattedSize })}

src/files/files-list/FilesList.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -306,7 +306,7 @@ export const FilesList = ({
306306
}, [currentFilesRef, focused, listRef])
307307

308308
return (
309-
<section ref={drop} className={classnames('FilesList no-select sans-serif border-box w-100 flex flex-column', className)}>
309+
<section ref={drop} className={classnames('FilesList no-select sans-serif border-box w-100 flex flex-column', className)} data-testid="files-list">
310310
{ showLoadingAnimation
311311
? <LoadingAnimation />
312312
: <Fragment>

test/e2e/explore.test.js

Lines changed: 22 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,12 @@ import * as dagPb from '@ipld/dag-pb'
99
import { sha256 } from 'multiformats/hashes/sha2'
1010

1111
import { test, expect } from './setup/coverage.js'
12+
import { explore } from './setup/locators.js'
1213

1314
const __filename = fileURLToPath(import.meta.url)
1415
const __dirname = dirname(__filename)
1516

1617
/**
17-
*
1818
* @template {number} Code
1919
* @param {any} value
2020
* @param {import('multiformats/block').BlockEncoder<Code, any>} codec
@@ -43,18 +43,19 @@ const createCID = async (value, codec, hasher, version = 1) => {
4343
*/
4444
async function testExploredCid ({ cid, type, humanReadableCID, page, fillOutForm = true }) {
4545
if (fillOutForm) {
46-
await page.locator('[data-id="FilesExploreForm"] input[id="ipfs-path"]').fill(cid)
47-
await page.locator('[data-id="FilesExploreForm"] button[title="Inspect"]').press('Enter')
46+
await explore.cidInput(page).fill(cid)
47+
await explore.inspectButton(page).press('Enter')
4848
}
4949

50-
await page.locator(`.joyride-explorer-cid [title="${cid}"]`).waitFor() // cid is displayed in the CID INFO section.
51-
await page.locator(`[title="${type}"]`).waitFor()
50+
// wait for cid to be displayed in the CID INFO section
51+
await expect(page.locator(`.joyride-explorer-cid [title="${cid}"]`)).toBeVisible()
52+
await expect(page.locator(`[title="${type}"]`)).toBeVisible()
5253

5354
if (humanReadableCID != null) {
5455
// expect cid details
55-
await page.locator('#CidInfo-human-readable-cid').waitFor()
56-
const actualHumanReadableCID = await page.locator('#CidInfo-human-readable-cid').textContent()
57-
expect(actualHumanReadableCID).toBe(humanReadableCID)
56+
const humanReadableElement = explore.humanReadableCid(page)
57+
await expect(humanReadableElement).toBeVisible()
58+
await expect(humanReadableElement).toHaveText(humanReadableCID)
5859
}
5960
}
6061

@@ -84,14 +85,15 @@ async function loadBlockFixtures ({ ipfs, blockCid, blockPutArgs = { format: 'v0
8485
test.describe('Explore screen', () => {
8586
test.beforeEach(async ({ page }) => {
8687
await page.goto('/#/explore')
87-
await page.locator('.joyride-app-status .teal').waitFor() // '.joyride-app-status .red' means disconnected.
88+
// wait for connection indicator (teal = connected)
89+
await expect(explore.connectionIndicator(page)).toBeVisible()
8890
})
8991

9092
test.describe('Start Exploring', () => {
9193
test('should have Project Apollo Archive as one of examples', async ({ page }) => {
92-
await page.locator('a[href="#/explore/QmSnuWmxptJZdLJpKRarxBMS2Ju2oANVrgbr2xWbie9b2D"]').waitFor()
93-
await page.locator('text=Project Apollo Archives').waitFor()
94-
await page.locator('text=QmSnuWmxptJZdLJpKRarxBMS2Ju2oANVrgbr2xWbie9b2D').waitFor()
94+
await expect(page.locator('a[href="#/explore/QmSnuWmxptJZdLJpKRarxBMS2Ju2oANVrgbr2xWbie9b2D"]')).toBeVisible()
95+
await expect(page.getByText('Project Apollo Archives')).toBeVisible()
96+
await expect(page.getByText('QmSnuWmxptJZdLJpKRarxBMS2Ju2oANVrgbr2xWbie9b2D')).toBeVisible()
9597
})
9698
})
9799

@@ -192,9 +194,9 @@ test.describe('Explore screen', () => {
192194
humanReadableCID: 'base58btc - cidv0 - dag-pb - sha2-256~256~E536C7F88D731F374DCCB568AFF6F56E838A19382E488039B1CA8AD2599E82FE',
193195
type: 'dag-pb'
194196
})
195-
await page.locator('"UnixFS"').waitFor()
197+
await expect(page.getByText('UnixFS')).toBeVisible()
196198

197-
await page.locator('"QmbQDovX7wRe9ek7u6QXe9zgCXkTzoUSsTFJEkrYV1HrVR"').click()
199+
await page.getByText('QmbQDovX7wRe9ek7u6QXe9zgCXkTzoUSsTFJEkrYV1HrVR').click()
198200
await testExploredCid({
199201
fillOutForm: false,
200202
page,
@@ -203,7 +205,7 @@ test.describe('Explore screen', () => {
203205
type: 'dag-pb'
204206
})
205207

206-
await page.locator('"QmawceGscqN4o8Y8Fv26UUmB454kn2bnkXV5tEQYc4jBd6"').click()
208+
await page.getByText('QmawceGscqN4o8Y8Fv26UUmB454kn2bnkXV5tEQYc4jBd6').click()
207209
await testExploredCid({
208210
fillOutForm: false,
209211
page,
@@ -234,7 +236,7 @@ test.describe('Explore screen', () => {
234236
type: 'dag-pb'
235237
})
236238

237-
await page.locator('"QmeQtZfwuq6aWRarY9P3L9MWhZ6QTonDe9ahWECGBZjyEJ"').click()
239+
await page.getByText('QmeQtZfwuq6aWRarY9P3L9MWhZ6QTonDe9ahWECGBZjyEJ').click()
238240
await testExploredCid({
239241
page,
240242
cid: 'QmeQtZfwuq6aWRarY9P3L9MWhZ6QTonDe9ahWECGBZjyEJ',
@@ -243,7 +245,7 @@ test.describe('Explore screen', () => {
243245
fillOutForm: false
244246
})
245247

246-
await page.locator('"QmVmf9vLEdWeBjh74kTibHVkim6iLsRXs5jhHzbSdWjoLt"').click()
248+
await page.getByText('QmVmf9vLEdWeBjh74kTibHVkim6iLsRXs5jhHzbSdWjoLt').click()
247249
await testExploredCid({
248250
page,
249251
cid: 'QmVmf9vLEdWeBjh74kTibHVkim6iLsRXs5jhHzbSdWjoLt',
@@ -252,7 +254,7 @@ test.describe('Explore screen', () => {
252254
fillOutForm: false
253255
})
254256

255-
await page.locator('"QmT4hPa6EeeCaTAb4a6ddFf4Lk5da9C1f4nMBmMJgbAW3z"').click()
257+
await page.getByText('QmT4hPa6EeeCaTAb4a6ddFf4Lk5da9C1f4nMBmMJgbAW3z').click()
256258
await testExploredCid({
257259
page,
258260
cid: 'QmT4hPa6EeeCaTAb4a6ddFf4Lk5da9C1f4nMBmMJgbAW3z',
@@ -261,7 +263,7 @@ test.describe('Explore screen', () => {
261263
fillOutForm: false
262264
})
263265

264-
await page.locator('"QmZA6h4vP17Ktw5vyMdSQNTvzsncQKDSifYwJznY461rY2"').click()
266+
await page.getByText('QmZA6h4vP17Ktw5vyMdSQNTvzsncQKDSifYwJznY461rY2').click()
265267
await testExploredCid({
266268
page,
267269
cid: 'QmZA6h4vP17Ktw5vyMdSQNTvzsncQKDSifYwJznY461rY2',
@@ -270,7 +272,7 @@ test.describe('Explore screen', () => {
270272
fillOutForm: false
271273
})
272274

273-
await page.locator('"QmR2pm6hPxv7pEgNaPE477rVBNSZnbUgXsSn2R9RqK9tAH"').click()
275+
await page.getByText('QmR2pm6hPxv7pEgNaPE477rVBNSZnbUgXsSn2R9RqK9tAH').click()
274276
await testExploredCid({
275277
page,
276278
cid: 'QmR2pm6hPxv7pEgNaPE477rVBNSZnbUgXsSn2R9RqK9tAH',

test/e2e/files.test.js

Lines changed: 55 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
import { test } from './setup/coverage.js'
1+
import { test, expect } from './setup/coverage.js'
22
import { fixtureData } from './fixtures/index.js'
3+
import { files } from './setup/locators.js'
34
import all from 'it-all'
45
import filesize from 'filesize'
56
import * as kuboRpcModule from 'kubo-rpc-client'
@@ -9,62 +10,64 @@ test.describe('Files screen', () => {
910
await page.goto('/#/files')
1011
})
1112

12-
const button = 'button[id="import-button"]'
13-
1413
test('should have the active Add menu', async ({ page }) => {
15-
await page.locator(button).waitFor({ state: 'visible' })
16-
await page.locator(button).click()
17-
await page.locator('#add-file').waitFor({ state: 'visible' })
18-
await page.locator('button#add-file').waitFor()
19-
await page.locator('button#add-folder').waitFor()
20-
await page.locator('button#add-by-path').waitFor()
21-
await page.locator('button#add-new-folder').waitFor()
14+
const importButton = files.importButton(page)
15+
await expect(importButton).toBeVisible()
16+
await importButton.click()
17+
18+
await expect(files.addFileOption(page)).toBeVisible()
19+
await expect(files.addFolderOption(page)).toBeVisible()
20+
await expect(files.addByPathOption(page)).toBeVisible()
21+
await expect(files.addNewFolderOption(page)).toBeVisible()
22+
2223
// close menu with Escape key
2324
await page.keyboard.press('Escape')
2425
})
2526

2627
test('should allow for a successful import of two files', async ({ page }) => {
27-
await page.locator(button).waitFor({ state: 'visible' })
28-
await page.locator(button).click()
29-
await page.locator('#add-file').waitFor({ state: 'visible' })
28+
const importButton = files.importButton(page)
29+
await expect(importButton).toBeVisible()
30+
await importButton.click()
31+
32+
await expect(files.addFileOption(page)).toBeVisible()
3033

3134
const [fileChooser] = await Promise.all([
3235
page.waitForEvent('filechooser'),
33-
page.locator('button[id="add-file"]').click() // menu button that triggers file selection
36+
files.addFileOption(page).click()
3437
])
3538

36-
// select a single static text file via fileChooser
39+
// select static text files via fileChooser
3740
const file1 = fixtureData('file.txt')
3841
const file2 = fixtureData('file2.txt')
3942
await fileChooser.setFiles([file1.path, file2.path])
4043

41-
// expect file with matching filename to be added to the file list
42-
await page.locator('.File').first().waitFor()
43-
await page.locator('.File:has-text("file.txt")').waitFor()
44-
await page.locator('.File:has-text("file2.txt")').waitFor()
44+
// expect files to be added to the file list
45+
const fileRow1 = page.getByTestId('file-row').filter({ hasText: 'file.txt' })
46+
const fileRow2 = page.getByTestId('file-row').filter({ hasText: 'file2.txt' })
47+
await expect(fileRow1).toBeVisible()
48+
await expect(fileRow2).toBeVisible()
4549

4650
// expect valid CID to be present on the page
4751
const ipfs = kuboRpcModule.create(process.env.IPFS_RPC_ADDR)
4852
const [result1, result2] = await all(ipfs.addAll([file1.data, file2.data]))
49-
await page.locator(`text=${result1.cid.toString()}`).first().waitFor()
50-
await page.locator(`text=${result2.cid.toString()}`).first().waitFor()
53+
await expect(page.getByText(result1.cid.toString()).first()).toBeVisible()
54+
await expect(page.getByText(result2.cid.toString()).first()).toBeVisible()
5155

5256
// expect human readable sizes in format from ./src/lib/files.js#humanSize
53-
// this ensures metadata was correctly read for each item in the MFS
5457
const human = (b) => (b
5558
? filesize(b, {
5659
standard: 'iec',
5760
base: 2,
5861
round: b >= 1073741824 ? 1 : 0
5962
})
6063
: '-')
61-
// only check the files we just uploaded (not all MFS files, which may include files from other tests)
64+
65+
// only check the files we just uploaded
6266
for (const fileName of ['file.txt', 'file2.txt']) {
6367
const stat = await ipfs.files.stat(`/${fileName}`)
64-
// verify file row exists and shows the correct size
65-
const fileRow = page.locator(`.File:has-text("${fileName}")`)
66-
await fileRow.waitFor()
67-
await fileRow.locator(`text=${human(stat.size)}`).waitFor()
68+
const fileRow = page.getByTestId('file-row').filter({ hasText: fileName })
69+
await expect(fileRow).toBeVisible()
70+
await expect(fileRow.getByText(human(stat.size))).toBeVisible()
6871
}
6972
})
7073

@@ -73,30 +76,39 @@ test.describe('Files screen', () => {
7376
const testFilename = 'explorer-context-menu-test.txt'
7477
const testCid = 'bafkqaddjnzzxazldoqwxizltoq'
7578

76-
// first: create a test file
77-
const button = 'button[id="import-button"]'
78-
await page.locator(button).waitFor({ state: 'visible' })
79-
await page.locator(button).click()
80-
await page.locator('#add-by-path').waitFor({ state: 'visible' })
81-
await page.locator('button[id="add-by-path"]').click()
82-
await page.locator('div[role="dialog"] input[name="name"]').waitFor()
83-
await page.locator('div[role="dialog"] input[name="path"]').fill(`/ipfs/${testCid}`)
84-
await page.locator('div[role="dialog"] input[name="name"]').fill(testFilename)
79+
// first: create a test file via "Add by path"
80+
const importButton = files.importButton(page)
81+
await expect(importButton).toBeVisible()
82+
await importButton.click()
83+
84+
await expect(files.addByPathOption(page)).toBeVisible()
85+
await files.addByPathOption(page).click()
86+
87+
// wait for dialog inputs to be visible (dialog may have animation)
88+
const pathInput = files.dialogInput(page, 'path')
89+
await expect(pathInput).toBeVisible()
90+
91+
await pathInput.fill(`/ipfs/${testCid}`)
92+
await files.dialogInput(page, 'name').fill(testFilename)
8593
await page.keyboard.press('Enter')
94+
8695
// expect file with matching filename to be added to the file list
87-
await page.locator(`.File:has-text("${testFilename}")`).waitFor()
96+
const fileRow = page.getByTestId('file-row').filter({ hasText: testFilename })
97+
await expect(fileRow).toBeVisible()
8898

8999
// open context menu
90-
const cbutton = `button[aria-label="View more options for ${testFilename}"]`
91-
await page.locator(cbutton).waitFor({ state: 'visible' })
92-
await page.locator(cbutton).click()
93-
await page.locator('button[role="menuitem"]:has-text("Inspect")').waitFor({ state: 'visible' })
100+
const contextButton = files.contextMenuButton(page, testFilename)
101+
await expect(contextButton).toBeVisible()
102+
await contextButton.click()
103+
104+
const inspectMenuItem = files.contextMenuItem(page, 'Inspect')
105+
await expect(inspectMenuItem).toBeVisible()
94106

95107
// click on Inspect option
96-
await page.locator('button[role="menuitem"]:has-text("Inspect")').click()
108+
await inspectMenuItem.click()
97109

98110
// confirm Explore screen was opened with correct CID
99111
await page.waitForURL(`/#/explore/${testCid}`)
100-
await page.locator('text="CID info"').waitFor()
112+
await expect(page.getByText('CID info')).toBeVisible()
101113
})
102114
})

0 commit comments

Comments
 (0)