diff --git a/packages/ui/src/utilities/copyDataFromLocale.ts b/packages/ui/src/utilities/copyDataFromLocale.ts index 9294358b464..738eabde645 100644 --- a/packages/ui/src/utilities/copyDataFromLocale.ts +++ b/packages/ui/src/utilities/copyDataFromLocale.ts @@ -249,6 +249,7 @@ export const copyDataFromLocale = async (args: CopyDataFromLocaleArgs) => { ? payload.findGlobal({ slug: globalSlug, depth: 0, + draft: true, locale: fromLocale, overrideAccess: false, user, @@ -258,6 +259,7 @@ export const copyDataFromLocale = async (args: CopyDataFromLocaleArgs) => { id: docID, collection: collectionSlug, depth: 0, + draft: true, joins: false, locale: fromLocale, overrideAccess: false, @@ -268,6 +270,7 @@ export const copyDataFromLocale = async (args: CopyDataFromLocaleArgs) => { ? payload.findGlobal({ slug: globalSlug, depth: 0, + draft: true, locale: toLocale, overrideAccess: false, user, @@ -277,6 +280,7 @@ export const copyDataFromLocale = async (args: CopyDataFromLocaleArgs) => { id: docID, collection: collectionSlug, depth: 0, + draft: true, joins: false, locale: toLocale, overrideAccess: false, @@ -310,6 +314,7 @@ export const copyDataFromLocale = async (args: CopyDataFromLocaleArgs) => { ? await payload.updateGlobal({ slug: globalSlug, data, + draft: true, locale: toLocale, overrideAccess: false, req, @@ -319,6 +324,7 @@ export const copyDataFromLocale = async (args: CopyDataFromLocaleArgs) => { id: docID, collection: collectionSlug, data, + draft: true, locale: toLocale, overrideAccess: false, req, diff --git a/test/localization/e2e.spec.ts b/test/localization/e2e.spec.ts index 4aa40f252c9..ff2eea25f64 100644 --- a/test/localization/e2e.spec.ts +++ b/test/localization/e2e.spec.ts @@ -567,6 +567,50 @@ describe('Localization', () => { // Should show error await expect(page.locator('.payload-toast-container .toast-error')).toBeVisible() }) + + test('should not lose source locale data when copying with autosave enabled', async () => { + // This tests that copy-to-locale doesn't cause data loss + // when operating on a collection with autosave enabled (blocks-fields) + + // Create a document with blocks content in en locale + await changeLocale(page, defaultLocale) + await page.goto(urlBlocks.create) + + // Fill in the title + const titleField = page.locator('#field-title') + await titleField.fill('English Block Title') + + // Add a block with content + await addBlock({ page, fieldName: 'content', blockToSelect: 'Block Inside Block' }) + const blockTextField = page.locator('#field-content__0__text') + await blockTextField.fill('English block text content') + + // Wait for autosave to complete + await waitForAutoSaveToRunAndComplete(page) + + // Verify autosave worked + await expect(titleField).toHaveValue('English Block Title') + await expect(blockTextField).toHaveValue('English block text content') + + // Copy to Spanish locale + await openCopyToLocaleDrawer(page) + await setToLocale(page, 'Spanish') + await runCopy({ page, toLocale: spanishLocale }) + + // Wait for the form to be ready after copy/navigation + await waitForFormReady(page) + + // Verify Spanish locale has the copied data + await expect(page.locator('#field-title')).toHaveValue('English Block Title') + + // CRITICAL: Switch back to English and verify data is NOT lost + await changeLocale(page, defaultLocale) + + await expect(page.locator('#field-title')).toHaveValue('English Block Title') + await expect(page.locator('#field-content__0__text')).toHaveValue( + 'English block text content', + ) + }) }) describe('locale change', () => { diff --git a/test/localization/int.spec.ts b/test/localization/int.spec.ts index a355f5c4c52..46ead14f58d 100644 --- a/test/localization/int.spec.ts +++ b/test/localization/int.spec.ts @@ -3117,6 +3117,204 @@ describe('Localization', () => { // The source data should remain unchanged expect(refreshedDoc.topLevelArrayLocalized?.[0]?.text).toBe('some-text') }) + + it('should copy to locale without losing data when autosave and drafts are enabled', async () => { + // The blocks-fields collection has versions.drafts.autosave: true + // This test verifies that copyToLocale doesn't cause data loss + // when operating on a collection with autosave enabled + + // Create a document with content in en locale + const doc = await payload.create({ + collection: 'blocks-fields', + locale: 'en', + data: { + title: 'English Title', + content: [ + { + blockType: 'blockInsideBlock', + text: 'English block text', + content: [ + { + blockType: 'textBlock', + text: 'Nested English text', + }, + ], + }, + ], + }, + }) + + // Add content to Spanish locale separately + await payload.update({ + collection: 'blocks-fields', + id: doc.id, + locale: 'es', + data: { + title: 'Spanish Title', + content: [ + { + blockType: 'blockInsideBlock', + text: 'Spanish block text', + }, + ], + }, + }) + + // Verify initial state - English data should exist + const enDocBefore = await payload.findByID({ + id: doc.id, + collection: 'blocks-fields', + locale: 'en', + }) + + expect(enDocBefore.title).toBe('English Title') + expect(enDocBefore.content?.[0]?.text).toBe('English block text') + + // Copy data from en to es + const req = await createLocalReq({ user }, payload) + + await copyDataFromLocaleHandler({ + fromLocale: 'en', + req, + toLocale: 'es', + docID: doc.id, + collectionSlug: 'blocks-fields', + overrideData: true, + }) + + // CRITICAL: Verify English data is NOT lost after copy operation + const enDocAfter = await payload.findByID({ + id: doc.id, + collection: 'blocks-fields', + locale: 'en', + }) + + expect(enDocAfter.title).toBe('English Title') + expect(enDocAfter.content?.[0]?.text).toBe('English block text') + expect(enDocAfter.content?.[0]?.content?.[0]?.text).toBe('Nested English text') + + // Verify Spanish locale received the copied data (as a draft) + const esDocAfter = await payload.findByID({ + id: doc.id, + collection: 'blocks-fields', + locale: 'es', + draft: true, + }) + + expect(esDocAfter.title).toBe('English Title') + expect(esDocAfter.content?.[0]?.text).toBe('English block text') + }) + + it('should copy to locale without losing draft data when autosave is enabled', async () => { + // Create a document with draft content + const doc = await payload.create({ + collection: 'blocks-fields', + locale: 'en', + draft: true, + data: { + title: 'Draft English Title', + content: [ + { + blockType: 'blockInsideBlock', + text: 'Draft block text', + }, + ], + }, + }) + + // Verify draft exists + const draftBefore = await payload.findByID({ + id: doc.id, + collection: 'blocks-fields', + locale: 'en', + draft: true, + }) + + expect(draftBefore.title).toBe('Draft English Title') + + // Copy draft data to another locale + const req = await createLocalReq({ user }, payload) + + await copyDataFromLocaleHandler({ + fromLocale: 'en', + req, + toLocale: 'es', + docID: doc.id, + collectionSlug: 'blocks-fields', + }) + + // Verify the source draft is not lost + const draftAfter = await payload.findByID({ + id: doc.id, + collection: 'blocks-fields', + locale: 'en', + draft: true, + }) + + expect(draftAfter.title).toBe('Draft English Title') + expect(draftAfter.content?.[0]?.text).toBe('Draft block text') + }) + + it('should not overwrite published content when source has both published and draft versions', async () => { + // Create published doc in en + const doc = await payload.create({ + collection: 'blocks-fields', + locale: 'en', + data: { + title: 'Published EN', + }, + }) + + // Create draft with different content + await payload.update({ + collection: 'blocks-fields', + id: doc.id, + locale: 'en', + draft: true, + data: { + title: 'Draft EN', + }, + }) + + // Verify both published and draft exist with different content + const enPublishedBefore = await payload.findByID({ + id: doc.id, + collection: 'blocks-fields', + locale: 'en', + draft: false, + }) + const enDraftBefore = await payload.findByID({ + id: doc.id, + collection: 'blocks-fields', + locale: 'en', + draft: true, + }) + + expect(enPublishedBefore.title).toBe('Published EN') + expect(enDraftBefore.title).toBe('Draft EN') + + // Copy to another locale using the actual handler + const req = await createLocalReq({ user }, payload) + + await copyDataFromLocaleHandler({ + fromLocale: 'en', + req, + toLocale: 'es', + docID: doc.id, + collectionSlug: 'blocks-fields', + overrideData: true, + }) + + // Verify published content in source locale is NOT overwritten + const enPublishedAfter = await payload.findByID({ + id: doc.id, + collection: 'blocks-fields', + locale: 'en', + draft: false, + }) + + expect(enPublishedAfter.title).toBe('Published EN') + }) }) describe('Multiple fallback locales', () => {