Skip to content

Commit 4186e45

Browse files
authored
test(e2e): create some example tests with the fixtures from @repo/e2e (#560)
### Description This PR uses the tooling established in the last two PRs to give examples of how to use the tooling that was created there. It also updates the Kitchensink slightly to make things more testable and repeatable. ### What to review - The new e2e test files for document-editor and document-list - Changes to the DocumentEditorRoute component to support loading documents by ID - Added data-testid attributes throughout the components - Document preview enhancements to support testing ### Testing Added comprehensive e2e tests that verify: - Document loading and editing functionality - Real-time updates in the document list view - Document content validation before and after changes ### Fun gif ![Testing cat](https://media.giphy.com/media/v1.Y2lkPTc5MGI3NjExcWJtZnRqZnRqZGJtZnRqZnRqZGJtZnRqZnRqZGJtZnRqZnRqZGJtZnRqZg/3oKIPnAiaMCws8nOsE/giphy.gif)
1 parent edaea3f commit 4186e45

File tree

6 files changed

+152
-18
lines changed

6 files changed

+152
-18
lines changed
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import {expect, test} from '@repo/e2e'
2+
3+
test.describe('Document Editor', () => {
4+
test('can edit an author document', async ({page, createDocuments}) => {
5+
// Create an author document
6+
const {
7+
documentIds: [id],
8+
} = await createDocuments([
9+
{
10+
_type: 'author',
11+
name: 'Test Author for document editor',
12+
biography: 'This is a test biography',
13+
},
14+
])
15+
16+
// Navigate to the document editor
17+
await page.goto('/document-editor')
18+
19+
// Wait for the document to load
20+
await page.getByTestId('document-id-input').fill(id.replace('drafts.', ''))
21+
await page.getByTestId('load-document-button').click()
22+
23+
// Wait for the document to be loaded and match expected initial values
24+
await expect(async () => {
25+
const content = await page.getByTestId('document-content').textContent()
26+
const document = JSON.parse(content || '{}')
27+
expect(document.name).toBe('Test Author for document editor')
28+
expect(document.biography).toBe('This is a test biography')
29+
}).toPass({timeout: 5000})
30+
31+
// Update content
32+
await page.getByTestId('name-input').fill('Updated Author Name')
33+
await page.getByTestId('name-input').press('Enter')
34+
35+
// Verify the changes are reflected
36+
const updatedContent = await page.getByTestId('document-content').textContent()
37+
const updatedDocument = JSON.parse(updatedContent || '{}')
38+
expect(updatedDocument.name).toBe('Updated Author Name')
39+
expect(updatedDocument.biography).toBe('This is a test biography')
40+
41+
// we should play with the other actions here (delete, discard, publish, etc)
42+
// and also see what happens in the browser if we update the value via the client (does it sync? race conditions?)
43+
})
44+
})
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import {expect, test} from '@repo/e2e'
2+
3+
test.describe('Document List', () => {
4+
test('can list and update author documents', async ({page, getClient, createDocuments}) => {
5+
const client = getClient()
6+
7+
// Create 10 author documents in a single transaction
8+
const {documentIds} = await createDocuments(
9+
Array.from({length: 10}, (_, i) => ({
10+
_type: 'author',
11+
name: `Test Author ${i}`,
12+
biography: `This is a test biography for author ${i}`,
13+
})),
14+
)
15+
16+
// Query for the most recent document by _updatedAt
17+
// (it should be at the top of the list and thus visible in the viewport for the test)
18+
const mostRecentDoc = await client.fetch(
19+
`*[_type == "author"] | order(_updatedAt desc)[0]{_id}`,
20+
{documentIds},
21+
)
22+
const id = mostRecentDoc._id.replace('drafts.', '')
23+
24+
// Navigate to the document list
25+
await page.goto('/document-list')
26+
27+
// Wait for the target document to be visible
28+
const targetDocumentPreview = page.getByTestId(`document-preview-${id}`)
29+
await targetDocumentPreview.waitFor()
30+
await expect(targetDocumentPreview).toBeVisible()
31+
32+
// Update the document using the client
33+
await client.patch(`drafts.${id}`).set({name: 'Updated Author Name'}).commit()
34+
35+
// Wait for the document preview to update with the new name
36+
await expect(async () => {
37+
const updatedDocumentPreview = page.getByTestId(`document-preview-${id}`)
38+
await expect(updatedDocumentPreview).toBeVisible()
39+
40+
const updatedDocumentTitle = await updatedDocumentPreview
41+
.getByTestId('document-title')
42+
.textContent()
43+
expect(updatedDocumentTitle).toBe('Updated Author Name')
44+
}).toPass({timeout: 5000})
45+
})
46+
})

apps/kitchensink-react/src/DocumentCollection/DocumentEditorRoute.tsx

Lines changed: 49 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
/* eslint-disable no-console */
22
import {
33
createDocument,
4+
createDocumentHandle,
45
deleteDocument,
56
discardDocument,
67
type DocumentHandle,
@@ -19,9 +20,9 @@ import {Box, Button, TextInput, Tooltip} from '@sanity/ui'
1920
import {JsonData, JsonEditor} from 'json-edit-react'
2021
import {type JSX, useState} from 'react'
2122

22-
function DocumentEditor({docHandle}: {docHandle: DocumentHandle<'author'>}) {
23-
const [value, setValue] = useState('')
23+
import {devConfigs, e2eConfigs} from '../sanityConfigs'
2424

25+
function DocumentEditor({docHandle}: {docHandle: DocumentHandle<'author'>}) {
2526
useDocumentEvent({...docHandle, onEvent: (e) => console.log(e)})
2627
const synced = useDocumentSyncStatus(docHandle)
2728
const apply = useApplyDocumentActions()
@@ -101,19 +102,14 @@ function DocumentEditor({docHandle}: {docHandle: DocumentHandle<'author'>}) {
101102
</div>
102103

103104
<Box paddingY={5}>
104-
<TextInput
105-
label="Value"
106-
type="text"
107-
value={value}
108-
onChange={(e) => setValue(e.currentTarget.value)}
109-
/>
110105
<TextInput
111106
label="Name"
112107
type="text"
113108
value={name}
114109
onChange={(e) => setName(e.currentTarget.value)}
110+
data-testid="name-input"
115111
/>
116-
<pre>{JSON.stringify(document, null, 2)}</pre>
112+
<pre data-testid="document-content">{JSON.stringify(document, null, 2)}</pre>
117113
</Box>
118114
</Box>
119115
)
@@ -125,17 +121,55 @@ function Editor() {
125121
batchSize: 1,
126122
})
127123

128-
const docHandle = documents[0] ?? null
124+
const [docHandle, setDocHandle] = useState<DocumentHandle<'author'> | null>(documents[0] ?? null)
125+
const [newDocumentId, setNewDocumentId] = useState<string>('')
126+
const {projectId, dataset} = import.meta.env['VITE_IS_E2E'] ? e2eConfigs[0] : devConfigs[0]
127+
128+
const handleLoadDocument = () => {
129+
if (newDocumentId) {
130+
setDocHandle(
131+
createDocumentHandle({
132+
documentType: 'author',
133+
documentId: newDocumentId,
134+
projectId,
135+
dataset,
136+
}),
137+
)
138+
}
139+
}
129140

130-
if (!docHandle) {
131-
return <Box padding={4}>No documents found</Box>
141+
const updateDocHandle = (newValue: string) => {
142+
setNewDocumentId(newValue)
132143
}
133144

134-
if (!docHandle) {
135-
return <Box padding={4}>Loading...</Box>
145+
if (!documents.length) {
146+
return <Box padding={4}>No documents found</Box>
136147
}
137148

138-
return <DocumentEditor docHandle={docHandle} />
149+
return (
150+
<Box padding={4}>
151+
<Box paddingY={4}>
152+
<Box style={{display: 'flex', gap: '8px', alignItems: 'flex-end'}}>
153+
<Box style={{flex: 1}}>
154+
<TextInput
155+
label="Document ID"
156+
type="text"
157+
value={newDocumentId || docHandle?.documentId || ''}
158+
placeholder="Enter document ID"
159+
data-testid="document-id-input"
160+
onChange={(e) => updateDocHandle(e.currentTarget.value)}
161+
/>
162+
</Box>
163+
<Button
164+
text="Load"
165+
onClick={() => handleLoadDocument()}
166+
data-testid="load-document-button"
167+
/>
168+
</Box>
169+
</Box>
170+
{!docHandle ? <Box padding={4}>Loading...</Box> : <DocumentEditor docHandle={docHandle} />}
171+
</Box>
172+
)
139173
}
140174

141175
export function DocumentEditorRoute(): JSX.Element {

apps/kitchensink-react/src/DocumentCollection/DocumentListRoute.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import {LoadMore} from './LoadMore'
99
export function DocumentListRoute(): JSX.Element {
1010
const {isPending, data, hasMore, loadMore} = useDocuments({
1111
documentType: 'author',
12-
orderings: [{field: '_updatedAt', direction: 'asc'}],
12+
orderings: [{field: '_updatedAt', direction: 'desc'}],
1313
})
1414

1515
return (

apps/kitchensink-react/src/DocumentCollection/DocumentPreview.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ function DocumentPreviewResolved({onClick, ...docHandle}: DocumentPreviewProps):
5252
docType={docHandle.documentType}
5353
media={media}
5454
status={statusLabel}
55+
documentId={docHandle.documentId}
5556
onClick={() => (onClick ? onClick() : alert(`Hello from ${title}`))}
5657
/>
5758
)

apps/kitchensink-react/src/components/DocumentPreviewLayout/DocumentPreviewLayout.tsx

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ export interface DocumentPreviewLayoutProps {
1515
status?: string
1616
subtitle?: string
1717
title: string
18+
documentId?: string
1819
}
1920

2021
/**
@@ -35,6 +36,7 @@ export const DocumentPreviewLayout = forwardRef(
3536
status = '',
3637
subtitle = '',
3738
title,
39+
documentId,
3840
}: DocumentPreviewLayoutProps,
3941
ref: React.Ref<HTMLElement>,
4042
): JSX.Element => {
@@ -131,15 +133,22 @@ export const DocumentPreviewLayout = forwardRef(
131133
onClick={onClick}
132134
ref={ref as React.Ref<HTMLButtonElement>}
133135
className={`DocumentPreviewLayout block si-100 text-start p-1 radius1 ${selected ? 'selected' : ''}`}
136+
data-testid={`document-preview-${documentId || 'unknown'}`}
134137
>
135138
<div className="container-inline flex align-items-center gap-2 font-sans">
136139
<figure className="Media border0 border-solid flex-none flex align-items-center justify-content-center object-cover">
137140
{PreviewMedia}
138141
</figure>
139142

140143
<div className="leading2 flex-grow overflow-hidden">
141-
<p className="Title text-1 font-medium truncate">{title}</p>
142-
{subtitle && <p className="Subtitle text-1 truncate">{subtitle}</p>}
144+
<p className="Title text-1 font-medium truncate" data-testid="document-title">
145+
{title}
146+
</p>
147+
{subtitle && (
148+
<p className="Subtitle text-1 truncate" data-testid="document-subtitle">
149+
{subtitle}
150+
</p>
151+
)}
143152
</div>
144153

145154
{docType && (

0 commit comments

Comments
 (0)