feat: image upload on articles
This commit is contained in:
1 parent
d4fcfb2bba
commit
938263cd6a
12 files changed
+1436
-65
No files matched your search
@@ -205,6 +205,63 @@ async function typeExactBodyLength(page: Page, totalLength: number) {
|
||||
await page.keyboard.insertText(inner)
|
||||
}
|
||||
|
||||
function mib(value: number) {
|
||||
return Math.round(value * 1024 * 1024)
|
||||
}
|
||||
|
||||
async function openImageUploader(page: Page) {
|
||||
await page.getByRole('button', { name: 'Image', exact: true }).first().click()
|
||||
await page.getByRole('dialog').getByText('Add an image').waitFor()
|
||||
}
|
||||
|
||||
async function chooseImageFile(page: Page, filename: string, mimeType: string, size: number) {
|
||||
await page.locator('input[type="file"]').setInputFiles({
|
||||
name: filename,
|
||||
mimeType,
|
||||
buffer: Buffer.alloc(size),
|
||||
})
|
||||
}
|
||||
|
||||
async function mockUploadedImage(page: Page) {
|
||||
const isDark = await page.evaluate(
|
||||
() => window.matchMedia('(prefers-color-scheme: dark)').matches,
|
||||
)
|
||||
const background = isDark ? 'rgb(26,26,26)' : 'rgb(244,244,245)'
|
||||
const border = isDark ? 'rgb(64,64,64)' : 'rgb(212,212,216)'
|
||||
await page.route('**/static/uploadedImages/**', async (route) => {
|
||||
const pathname = new URL(route.request().url()).pathname
|
||||
const svg = `<svg xmlns="http://www.w3.org/2000/svg" width="900" height="450"><rect width="100%" height="100%" fill="${background}" stroke="${border}" stroke-width="2" /><text x="50%" y="50%" text-anchor="middle" dominant-baseline="middle" font-family="monospace" font-size="16" fill="${border}">${pathname}</text></svg>`
|
||||
await route.fulfill({ status: 200, contentType: 'image/svg+xml', body: svg })
|
||||
})
|
||||
}
|
||||
|
||||
async function freezeUploadProgress(page: Page, loaded: number, total: number) {
|
||||
await page.evaluate(
|
||||
({ loaded, total }) => {
|
||||
const proto = XMLHttpRequest.prototype as unknown as Record<string, unknown>
|
||||
const originalOpen = proto.open as (...args: unknown[]) => void
|
||||
proto.open = function (this: XMLHttpRequest, ...args: unknown[]) {
|
||||
;(this as unknown as { __imageUrl?: string }).__imageUrl = String(args[1])
|
||||
return originalOpen.apply(this, args)
|
||||
}
|
||||
const originalSend = proto.send as (...args: unknown[]) => void
|
||||
proto.send = function (this: XMLHttpRequest, ...args: unknown[]) {
|
||||
const url = (this as unknown as { __imageUrl?: string }).__imageUrl ?? ''
|
||||
if (url.includes('/image')) {
|
||||
setTimeout(() => {
|
||||
const event = new Event('progress')
|
||||
Object.assign(event, { loaded, total, lengthComputable: true })
|
||||
this.upload.dispatchEvent(event)
|
||||
}, 50)
|
||||
return
|
||||
}
|
||||
return originalSend.apply(this, args)
|
||||
}
|
||||
},
|
||||
{ loaded, total },
|
||||
)
|
||||
}
|
||||
|
||||
const newArticleTitle = 'Choosing tiles for a small bathroom'
|
||||
const newArticleBody = 'Small rooms show every grout line, so we start with the floor.'
|
||||
|
||||
@@ -846,6 +903,57 @@ export const visualStates: Record<string, Record<string, VisualState>> = {
|
||||
'GET /v1/article/9999': { status: 404 },
|
||||
},
|
||||
},
|
||||
'Upload: choose': {
|
||||
path: '/article/12/edit',
|
||||
now: editorNow,
|
||||
api: editorApi(),
|
||||
steps: async (page) => {
|
||||
await markDirty(page)
|
||||
await openImageUploader(page)
|
||||
},
|
||||
},
|
||||
'Upload: in progress': {
|
||||
path: '/article/12/edit',
|
||||
now: editorNow,
|
||||
api: editorApi(),
|
||||
steps: async (page) => {
|
||||
await markDirty(page)
|
||||
await openImageUploader(page)
|
||||
await freezeUploadProgress(page, mib(2.4), mib(3.9))
|
||||
await chooseImageFile(page, 'north-wall.jpg', 'image/jpeg', mib(3.9))
|
||||
await page.getByText('north-wall.jpg 62%').waitFor()
|
||||
},
|
||||
},
|
||||
'Upload: rejected': {
|
||||
path: '/article/12/edit',
|
||||
now: editorNow,
|
||||
api: editorApi(),
|
||||
steps: async (page) => {
|
||||
await markDirty(page)
|
||||
await openImageUploader(page)
|
||||
await chooseImageFile(page, 'north-wall.tif', 'image/png', mib(14.2))
|
||||
await page.getByText(`"north-wall.tif" can't be used`).waitFor()
|
||||
},
|
||||
},
|
||||
'Upload: inserted': {
|
||||
path: '/article/12/edit',
|
||||
now: editorNow,
|
||||
api: editorApi({
|
||||
'POST /v1/article/12/image': {
|
||||
status: 201,
|
||||
body: { location: '/static/uploadedImages/north-wall.jpg' },
|
||||
},
|
||||
}),
|
||||
steps: async (page) => {
|
||||
await mockUploadedImage(page)
|
||||
await page.getByText('add a margin on materials').click()
|
||||
await page.keyboard.press('Home')
|
||||
await openImageUploader(page)
|
||||
await chooseImageFile(page, 'north-wall.jpg', 'image/jpeg', mib(3.9))
|
||||
await page.getByText('Save to keep it in the article.').first().waitFor()
|
||||
await page.waitForTimeout(650)
|
||||
},
|
||||
},
|
||||
},
|
||||
'new-article': {
|
||||
Default: {
|
||||
|
||||
Reference in new issue
Block a user