diff --git a/__tests__/component/imageEditor.spec.ts b/__tests__/component/imageEditor.spec.ts index 9dbcc6e..b3a2acf 100644 --- a/__tests__/component/imageEditor.spec.ts +++ b/__tests__/component/imageEditor.spec.ts @@ -80,6 +80,16 @@ describe('ImageEditor wrapper', () => { expect(wrapper.emitted('close')).toBeUndefined() }) + // The editor loads the image from that URL, so a name holding a `#` or + // a `?` has to be encoded the same way the save request encodes it. + it('hands the editor the encoded source', async () => { + const file = makeFile({ basename: 'a#b c?.jpg', mime: 'image/jpeg' }) + const wrapper = mount(ImageEditor, { props: { file } }) + await flushPromises() + + expect(wrapper.findComponent({ name: 'LibImageEditor' }).attributes('src')).toBe(file.encodedSource) + }) + it('closes without saving on cancel', async () => { const { wrapper, editor } = mountEditor() editor.vm.$emit('cancel') diff --git a/__tests__/component/media.spec.ts b/__tests__/component/media.spec.ts index b67a117..3f61829 100644 --- a/__tests__/component/media.spec.ts +++ b/__tests__/component/media.spec.ts @@ -149,6 +149,25 @@ describe('Images.vue', () => { expect(preloadMediaMock).not.toHaveBeenCalled() }) + // The element's `src` is a URL: a name holding a `#` or a `?` cuts it + // short unless it is encoded, and the image then fails to load. + it('renders the encoded source of a file whose name needs it', async () => { + const file = makeFile({ basename: 'a#b c?.jpg', mime: 'image/jpeg' }) + const wrapper = mountImages({ file, files: [file] }) + await flushPromises() + + expect(wrapper.find('img').attributes('src')).toBe(file.encodedSource) + }) + + it('renders the encoded source of a live photo', async () => { + const photo = makeFile({ id: 1, basename: 'a#b.jpg', attributes: { 'metadata-files-live-photo': 2 } }) + const movie = makeFile({ id: 2, basename: 'a#b.mov', mime: 'video/quicktime' }) + const wrapper = mountImages({ file: photo, files: [photo, movie] }) + await flushPromises() + + expect(wrapper.find('video').attributes('src')).toBe(movie.encodedSource) + }) + it('shows the hand-fetched bytes when the source fails to load', async () => { const file = makeFile({ basename: 'broken.jpg' }) const wrapper = mountImages({ file, files: [file] }) diff --git a/__tests__/utils.spec.ts b/__tests__/utils.spec.ts index f1adfe0..7b1b2eb 100644 --- a/__tests__/utils.spec.ts +++ b/__tests__/utils.spec.ts @@ -121,6 +121,14 @@ describe('previewUtils.getPreviewIfAny', () => { const file = makeFileWithAttributes({ hasPreview: false }) expect(getPreviewIfAny(file)).toBe(file.source) }) + + // What comes back is handed to a media element as its `src`, so a name + // holding a `#` or a `?` has to be encoded or the URL is cut short. + it('encodes the fallback source', () => { + const file = makeFile({ basename: 'a#b c?.jpg', attributes: { hasPreview: false } }) + expect(getPreviewIfAny(file)).toBe(file.encodedSource) + expect(getPreviewIfAny(file)).not.toContain('#') + }) }) describe('livePhotoUtils.findLivePhotoPeerFromFileId', () => { diff --git a/lib/components/ImageEditor.vue b/lib/components/ImageEditor.vue index 424d43a..6e76900 100644 --- a/lib/components/ImageEditor.vue +++ b/lib/components/ImageEditor.vue @@ -4,7 +4,7 @@ -->