feat(support): attach images and PDFs to the in-app contact form
Add optional image and PDF attachments to the existing in-app support contact form, with client-side limits, server-side validation, and email delivery. Preserve the existing subject, rate-limit, analytics, and storage behavior.
This commit is contained in:
@@ -271,5 +271,63 @@ describe('submitFeedback', () => {
|
||||
await submitFeedback({ subject: 'Moms', message: 'hemlig fritext om bolaget' })
|
||||
expect(JSON.stringify(captureMock.mock.calls)).not.toContain('hemlig fritext')
|
||||
})
|
||||
|
||||
it('does not add attachment metadata to analytics', async () => {
|
||||
stubFetchOk()
|
||||
await submitFeedback({
|
||||
message: 'Se bilagan',
|
||||
files: [new File(['x'], 'kontoutdrag-privat.png', { type: 'image/png' })],
|
||||
})
|
||||
const props = captureMock.mock.calls[0][1] as Record<string, unknown>
|
||||
expect(props).not.toHaveProperty('attachment_count')
|
||||
expect(JSON.stringify(captureMock.mock.calls)).not.toContain('kontoutdrag-privat')
|
||||
})
|
||||
})
|
||||
|
||||
describe('attachments', () => {
|
||||
it('sends multipart with the files when there are any', async () => {
|
||||
const fetchSpy = stubFetchOk()
|
||||
const file = new File(['x'], 'skarmbild.png', { type: 'image/png' })
|
||||
|
||||
await submitFeedback({ subject: 'Trasig vy', message: 'Ser ut så här', files: [file] })
|
||||
|
||||
const init = fetchSpy.mock.calls[0][1]
|
||||
expect(init.body).toBeInstanceOf(FormData)
|
||||
// The browser has to set the multipart boundary itself.
|
||||
expect(init.headers).toBeUndefined()
|
||||
const form = init.body as FormData
|
||||
expect(form.get('subject')).toBe('Trasig vy')
|
||||
expect(form.get('message')).toBe('Ser ut så här')
|
||||
expect(form.getAll('files')).toHaveLength(1)
|
||||
expect((form.get('files') as File).name).toBe('skarmbild.png')
|
||||
})
|
||||
|
||||
it('keeps the JSON body when the file list is empty', async () => {
|
||||
const fetchSpy = stubFetchOk()
|
||||
|
||||
await submitFeedback({ subject: 'Moms', message: 'Jag fastnar', files: [] })
|
||||
|
||||
expect(fetchSpy.mock.calls[0][1].body).toBe(
|
||||
JSON.stringify({ subject: 'Moms', message: 'Jag fastnar' })
|
||||
)
|
||||
})
|
||||
|
||||
it('surfaces the route error when an attachment is rejected', async () => {
|
||||
vi.stubGlobal(
|
||||
'fetch',
|
||||
vi.fn().mockResolvedValue({
|
||||
ok: false,
|
||||
json: async () => ({ error: 'Du kan bifoga max 5 filer' }),
|
||||
})
|
||||
)
|
||||
|
||||
const result = await submitFeedback({
|
||||
message: 'För många bilder',
|
||||
files: [new File(['x'], 'a.png', { type: 'image/png' })],
|
||||
})
|
||||
|
||||
expect(result.ok).toBe(false)
|
||||
expect(result.error).toBe('Du kan bifoga max 5 filer')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user