diff --git a/packages/web-app-unzip/src/composables/useUnzipAction.ts b/packages/web-app-unzip/src/composables/useUnzipAction.ts index f17677ac..36a59feb 100644 --- a/packages/web-app-unzip/src/composables/useUnzipAction.ts +++ b/packages/web-app-unzip/src/composables/useUnzipAction.ts @@ -23,6 +23,11 @@ import workerUrl from '@zip.js/zip.js/dist/zip-web-worker.js?worker&url' const SUPPORTED_MIME_TYPES = ['application/zip'] const MAX_SIZE_MB = 64 // in mb +// uppy's convention for directory-upload metadata on the file object. mirrors the +// (non-exported) FileWithPath in web/packages/web-pkg/src/services/uppy/uppyService.ts. +// uppyService.getRelativeFilePath reads .relativePath from file.data. +type FileWithPath = Blob & { relativePath?: string } + export const useUnzipAction = () => { const { $gettext, current: currentLanguage } = useGettext() const clientService = useClientService() @@ -88,13 +93,16 @@ export const useUnzipAction = () => { const path = dirname(result.filename) const name = path === '.' ? result.filename : result.filename.substring(path.length + 1) + if (path !== '.') { + // attach relativePath to the data blob so uppyService preserves + // the subfolder structure when uploading the extracted files. + ;(data as FileWithPath).relativePath = urlJoin(path, name) + } + return { name, data, - meta: { - ...(path !== '.' && { webkitRelativePath: urlJoin(path, name) }), - uploadId - } + meta: { uploadId } } as unknown as OcMinimalUppyFile }) }) diff --git a/packages/web-app-unzip/tests/e2e/extractZip.spec.ts b/packages/web-app-unzip/tests/e2e/extractZip.spec.ts index d5f46b72..1f1726b1 100644 --- a/packages/web-app-unzip/tests/e2e/extractZip.spec.ts +++ b/packages/web-app-unzip/tests/e2e/extractZip.spec.ts @@ -15,18 +15,33 @@ test.afterEach(async () => { await logout(userPage) }) -// FIXME: currently skipped because of https://github.com/opencloud-eu/web/pull/2506 -// unskip with the next OpenCloud release (v7 probably) -test.skip('extract zip file', async () => { +// FIXME: skipped until the bundled OpenCloud image ships a web build that contains +// both: +// - https://github.com/opencloud-eu/web/pull/2506 (services announced before apps +// init, otherwise uppyService is undefined when the extract handler runs and the +// archive extraction fails) +// - https://github.com/opencloud-eu/web/pull/2513 (suppresses a false-positive +// "Folder already exists" dialog that fires whenever the zip's top-level folder +// name matches anything in the user's current folder, which is the case for +// data.zip) +test.skip('extract zip file preserves subfolder structure', async () => { const uploadFile = new FilesAppBar(userPage) await uploadFile.uploadFile('data.zip') const file = new FilesPage(userPage) await file.extractZip('data.zip') + // extraction creates a folder named after the archive await file.openFolder('data') - await expect( - userPage.locator('span.oc-resource-basename', { hasText: 'logo-wide' }) - ).toBeVisible() - await expect(userPage.locator('span.oc-resource-basename', { hasText: 'lorem' })).toBeVisible() + // the zip's own top-level "data/" folder must be preserved inside + await expect(userPage.locator('[data-test-resource-name="data"]')).toBeVisible() + + await file.openFolder('data') + await expect(userPage.locator('[data-test-resource-name="logo-wide.png"]')).toBeVisible() + await expect(userPage.locator('[data-test-resource-name="lorem.txt"]')).toBeVisible() + await expect(userPage.locator('[data-test-resource-name="dir"]')).toBeVisible() + + // nested subfolder content must be preserved, not flattened + await file.openFolder('dir') + await expect(userPage.locator('[data-test-resource-name="lorem.txt"]')).toBeVisible() })