Skip to content

Commit

Permalink
fixup! test(form): add e2e tests for copy paste
Browse files Browse the repository at this point in the history
Signed-off-by: Fred Carlsen <[email protected]>
  • Loading branch information
sjelfull committed Jun 17, 2024
1 parent 9e659bf commit 975acb3
Show file tree
Hide file tree
Showing 3 changed files with 138 additions and 68 deletions.
34 changes: 34 additions & 0 deletions playwright.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,39 @@ const playwrightConfig = createPlaywrightConfig({
projectId: readEnv('SANITY_E2E_PROJECT_ID'),
token: readEnv('SANITY_E2E_SESSION_TOKEN'),
playwrightOptions(config): PlaywrightTestConfig {
const projects = [
...(config?.projects?.map((project) => {
const projectConfig = {
...project,
}

if (project.name === 'chromium') {
return {
...projectConfig,
permissions: ['clipboard-read', 'clipboard-write'],
contextOptions: {
// chromium-specific permissions
permissions: ['clipboard-read', 'clipboard-write'],
},
}
}

if (project.name === 'firefox') {
return {
...projectConfig,
launchOptions: {
firefoxUserPrefs: {
'dom.events.asyncClipboard.readText': true,
'dom.events.testing.asyncClipboard': true,
},
},
}
}

return projectConfig
}) || []),
]

return {
...config,
reporter: excludeGithub(config.reporter),
Expand All @@ -32,6 +65,7 @@ const playwrightConfig = createPlaywrightConfig({
baseURL: 'http://localhost:3339',
headless: HEADLESS,
},
projects,
webServer: {
...config.webServer,
command: CI ? 'pnpm e2e:start' : 'pnpm e2e:dev',
Expand Down
77 changes: 38 additions & 39 deletions test/e2e/tests/desk/copyPaste.spec.ts
Original file line number Diff line number Diff line change
@@ -1,28 +1,27 @@
import {type Locator} from '@playwright/test'

import {expect, test} from '../fixtures/copyPasteFixture'

test.describe('copy and pasting of fields', () => {
test.use({
permissions: ['clipboard-read', 'clipboard-write'],
})
test.beforeEach(async ({context}) => {
await context.grantPermissions(['clipboard-read', 'clipboard-write'])
})
test(`its able to copy and paste an object field successfully via keyboard shortcuts`, async ({
page,
createDraftDocument,
getClipboard,
getClipboardItemsAsText,
}) => {
let $objectWrapper: Locator

async function navigateToNewDocumentAndGetObjectWrapper() {
await createDraftDocument('/test/content/input-standard;objectsTest')

await expect(page.getByTestId(`field-objectWithColumns`)).toBeVisible()
await createDraftDocument('/test/content/input-standard;objectsTest')

const $object = page.getByTestId('field-objectWithColumns').locator(`[tabindex="0"]`).first()
await expect(page.getByTestId(`field-objectWithColumns`)).toBeVisible()

await expect($object).toBeVisible()
const $objectWrapper = page
.getByTestId('field-objectWithColumns')
.locator(`[tabindex="0"]`)
.first()

return $object
}

$objectWrapper = await navigateToNewDocumentAndGetObjectWrapper()
await expect($objectWrapper).toBeVisible()

await page.getByTestId('field-objectWithColumns.string1').locator('input').focus()

Expand All @@ -39,8 +38,11 @@ test.describe('copy and pasting of fields', () => {

await expect(page.getByText(`Field Object with columns copied`)).toBeVisible()

// Now lets navigate to new document and paste the copied field
$objectWrapper = await navigateToNewDocumentAndGetObjectWrapper()
await expect(await getClipboardItemsAsText()).toContain('A string to copy')

await page.getByTestId('field-objectWithColumns.string1').locator('input').focus()
await page.keyboard.press('Meta+A')
await page.keyboard.press('Delete')

await $objectWrapper.focus()

Expand All @@ -51,30 +53,22 @@ test.describe('copy and pasting of fields', () => {
await expect($objectWrapper).toBeVisible()
await expect($objectWrapper).toBeFocused()

// await expect(page.getByText(`Field Object with columns updated`)).toBeVisible()
await expect(page.getByText(`updated`)).toBeVisible()
await expect(page.getByText(`Field Object with columns updated`)).toBeVisible()
})

test(`its able to copy and paste an object field successfully via field actions`, async ({
page,
createDraftDocument,
getClipboard,
getClipboardItemsAsText,
getClipboardItemByMimeTypeAsText,
}) => {
let $objectWrapper: Locator
await createDraftDocument('/test/content/input-standard;objectsTest')

async function navigateToNewDocumentAndGetObjectWrapper() {
await createDraftDocument('/test/content/input-standard;objectsTest')
await expect(page.getByTestId(`field-objectWithColumns`)).toBeVisible()

await expect(page.getByTestId(`field-objectWithColumns`)).toBeVisible()
const $object = page.getByTestId('field-objectWithColumns').locator(`[tabindex="0"]`).first()

const $object = page.getByTestId('field-objectWithColumns').locator(`[tabindex="0"]`).first()

await expect($object).toBeVisible()

return $object
}

await navigateToNewDocumentAndGetObjectWrapper()
await expect($object).toBeVisible()

await page.getByTestId('field-objectWithColumns.string1').locator('input').focus()

Expand All @@ -87,8 +81,6 @@ test.describe('copy and pasting of fields', () => {
.getByTestId('field-actions-menu-objectWithColumns')
.getByTestId('field-actions-trigger')

//await expect($fieldActions).toBeAttached()

await $fieldActions.focus()
await expect($fieldActions).toBeFocused()
await $fieldActions.click()
Expand All @@ -98,8 +90,17 @@ test.describe('copy and pasting of fields', () => {

await expect(page.getByText(`Field Object with columns copied`)).toBeVisible()

// Now lets navigate to new document and paste the copied field
await navigateToNewDocumentAndGetObjectWrapper()
// Check that the plain text version is set
await expect(await getClipboardItemsAsText()).toContain('A string to copy')

// Test the exact mimetype
await expect(await getClipboardItemByMimeTypeAsText('web application/sanity-studio')).toContain(
'A string to copy',
)

await page.getByTestId('field-objectWithColumns.string1').locator('input').focus()
await page.keyboard.press('Meta+A')
await page.keyboard.press('Delete')

$fieldActions = page
.getByTestId('field-actions-menu-objectWithColumns')
Expand All @@ -114,9 +115,7 @@ test.describe('copy and pasting of fields', () => {
await expect(page.getByRole('menuitem', {name: 'Paste field'})).toBeVisible()
await page.getByRole('menuitem', {name: 'Paste field'}).click()

// await expect(page.getByText(`Field Object with columns updated`)).toBeVisible()
await expect(page.getByText(`updated`)).toBeVisible()
await expect(getClipboard()).toContain('A string to copy')
await expect(page.getByText(`Field Object with columns updated`)).toBeVisible()

await expect(page.getByTestId('field-objectWithColumns.string1').locator('input')).toHaveValue(
'A string to copy',
Expand Down
95 changes: 66 additions & 29 deletions test/e2e/tests/fixtures/copyPasteFixture.ts
Original file line number Diff line number Diff line change
@@ -1,52 +1,89 @@
import {test as base} from '@sanity/test'

// Define a test context extension with clipboard functionality
export const test = base.extend<{
setClipboard: (content: string) => Promise<void>
getClipboard: () => Promise<string>
clipboardContent: {content: string}
getClipboardItemByMimeTypeAsText: (mimeType: string) => Promise<string | null>
setClipboardItems: (items: ClipboardItem[]) => Promise<void>
getClipboardItems: () => Promise<ClipboardItem[]>
getClipboardItemsAsText: () => Promise<string>
clipboardItems: {items: ClipboardItem[]}
}>({
// Persistent clipboard state across navigations
clipboardContent: [{content: ''}, {scope: 'test'}],

// Extend page fixture to apply clipboard mock on new pages
page: async ({page, clipboardContent}, use) => {
// Function to set up clipboard mocks on the page
const setupClipboardMocks = async () => {
await page.addInitScript((content) => {
;(navigator.clipboard as any).writeText = (text: string) => {
;(window as any).__clipboardContent = text
return Promise.resolve()
}
;(navigator.clipboard as any).readText = () => {
return Promise.resolve((window as any).__clipboardContent)
clipboardItems: [{items: [] as ClipboardItem[]}, {scope: 'test'}],

page: async ({page, clipboardItems}, use) => {
const setupClipboardMocks = () => {
page.addInitScript((items) => {
const mockClipboard = {
read: () => {
return Promise.resolve((window as any).__clipboardItems)
},
write: (newItems: ClipboardItem[]) => {
;(window as any).__clipboardItems = newItems

return Promise.resolve()
},
readText: () => {
const textItem = items.find((item) => item.types.includes('text/plain'))
return textItem
? textItem.getType('text/plain').then((blob: Blob) => blob.text())
: Promise.resolve('')
},
writeText: (text: string) => {
const textBlob = new Blob([text], {type: 'text/plain'})
items.push(new ClipboardItem({'text/plain': textBlob}))
return Promise.resolve()
},
}
;(window as any).__clipboardContent = content
}, clipboardContent.content)
Object.defineProperty(navigator, 'clipboard', {
value: mockClipboard,
writable: false,
})
;(window as any).__clipboardItems = items
}, clipboardItems.items)
}

// Initial setup of clipboard mocks
await setupClipboardMocks()

// Reapply mocks after each navigation
page.on('framenavigated', async () => {
await setupClipboardMocks()
})

// Use the modified page in the test
await use(page)
},

setClipboard: async ({page, clipboardContent}, use) => {
await use(async (content: string) => {
clipboardContent.content = content // Store the content in the test scope
await page.evaluate((value) => navigator.clipboard.writeText(value), content)
setClipboardItems: async ({clipboardItems}, use) => {
await use(async (items: ClipboardItem[]) => {
clipboardItems.items = items
})
},

getClipboardItems: async ({page}, use) => {
await use(() => {
return page.evaluate(() => navigator.clipboard.read())
})
},

getClipboard: async ({page}, use) => {
getClipboardItemsAsText: async ({page}, use) => {
await use(async () => {
return page.evaluate(() => navigator.clipboard.readText())
return page.evaluate(async () => {
const items = await navigator.clipboard.read()
const textItem = items.find((item) => item.types.includes('text/plain'))

return textItem
? textItem.getType('text/plain').then((blob: Blob) => blob.text())
: Promise.resolve('')
})
})
},

getClipboardItemByMimeTypeAsText: async ({page}, use) => {
await use(async (mimeType: string) => {
return page.evaluate(async (mime) => {
const items = await navigator.clipboard.read()
const textItem = items.find((item) => item.types.includes(mime))
const content = textItem ? textItem.getType(mime).then((blob: Blob) => blob.text()) : null

return content
}, mimeType)
})
},
})
Expand Down

0 comments on commit 975acb3

Please sign in to comment.