From 53bd656e5b4022c112c075f4a900dd7af8595ef5 Mon Sep 17 00:00:00 2001 From: Jozsef Damokos Date: Tue, 26 May 2026 14:45:02 +0300 Subject: [PATCH 1/6] fix: persist partial premapping --- .../card/swag-migration-premapping/index.ts | 37 ++--- .../card/swag-migration-premapping.spec.js | 128 ++++++++++++++++++ 2 files changed, 148 insertions(+), 17 deletions(-) create mode 100644 tests/Jest/src/module/swag-migration/component/card/swag-migration-premapping.spec.js diff --git a/src/Resources/app/administration/src/module/swag-migration/component/card/swag-migration-premapping/index.ts b/src/Resources/app/administration/src/module/swag-migration/component/card/swag-migration-premapping/index.ts index e4c3854e4..03c27a3a8 100644 --- a/src/Resources/app/administration/src/module/swag-migration/component/card/swag-migration-premapping/index.ts +++ b/src/Resources/app/administration/src/module/swag-migration/component/card/swag-migration-premapping/index.ts @@ -15,6 +15,7 @@ const { debounce } = Shopware.Utils; export interface SwagMigrationPremappingData { isLoading: boolean; migrationStore: MigrationStore; + savePremappingDebounced: null | (() => void); } /** @@ -32,9 +33,20 @@ export default Shopware.Component.wrapComponentConfig({ return { isLoading: false, migrationStore: Store.get(MIGRATION_STORE_ID), + savePremappingDebounced: null, }; }, + created() { + this.savePremappingDebounced = debounce(async () => { + try { + await this.savePremapping(); + } finally { + this.migrationStore.setIsLoading(false); + } + }, 500); + }, + computed: { ...mapState( () => Store.get(MIGRATION_STORE_ID), @@ -73,29 +85,20 @@ export default Shopware.Component.wrapComponentConfig({ return; } - const filledOut = this.premapping.every((group: MigrationPremapping) => - group.mapping.every( - (mapping) => - mapping.destinationUuid !== null && - mapping.destinationUuid !== undefined && - mapping.destinationUuid !== '', - ), + await this.migrationApiService.writePremapping( + this.premapping.map((group: MigrationPremapping) => ({ + ...group, + mapping: group.mapping.filter((mapping) => { + return !!mapping.destinationUuid; + }), + })), ); - - if (!filledOut) { - return; - } - - await this.migrationApiService.writePremapping(this.premapping); }, async onPremappingChanged() { this.migrationStore.setIsLoading(true); - debounce(async () => { - await this.savePremapping(); - this.migrationStore.setIsLoading(false); - }, 500)(); + this.savePremappingDebounced?.(); }, }, }); diff --git a/tests/Jest/src/module/swag-migration/component/card/swag-migration-premapping.spec.js b/tests/Jest/src/module/swag-migration/component/card/swag-migration-premapping.spec.js new file mode 100644 index 000000000..26e268bf9 --- /dev/null +++ b/tests/Jest/src/module/swag-migration/component/card/swag-migration-premapping.spec.js @@ -0,0 +1,128 @@ +import { mount } from '@vue/test-utils'; +import swagMigrationPremapping from 'SwagMigrationAssistant/module/swag-migration/component/card/swag-migration-premapping'; +import { fixturePreMapping } from '@/fixture'; + +Shopware.Component.register('swag-migration-premapping', swagMigrationPremapping); + +const migrationApiServiceMock = { + generatePremapping: jest.fn(() => Promise.resolve([])), + writePremapping: jest.fn(() => Promise.resolve()), +}; + +async function createWrapper() { + return mount(await Shopware.Component.build('swag-migration-premapping'), { + global: { + plugins: [Shopware.Store._rootState], + provide: { + migrationApiService: migrationApiServiceMock, + }, + stubs: { + 'swag-migration-tab-card': true, + 'swag-migration-grid-selection': true, + }, + }, + }); +} + +describe('module/swag-migration/component/card/swag-migration-premapping', () => { + let store = null; + + beforeEach(async () => { + jest.clearAllMocks(); + + await import('SwagMigrationAssistant/module/swag-migration/store/migration.store'); + + store = Shopware.Store.get('swagMigration'); + store.$reset(); + }); + + afterEach(() => { + jest.useRealTimers(); + }); + + it('writes partially filled premapping', async () => { + store.premapping = [ + { + ...fixturePreMapping[0], + mapping: [ + fixturePreMapping[0].mapping[0], + { + ...fixturePreMapping[0].mapping[1], + destinationUuid: null, + }, + ], + }, + ]; + + const wrapper = await createWrapper(); + + await wrapper.vm.savePremapping(); + + expect(migrationApiServiceMock.writePremapping).toHaveBeenCalledTimes(1); + expect(migrationApiServiceMock.writePremapping).toHaveBeenCalledWith([ + { + ...store.premapping[0], + mapping: [ + store.premapping[0].mapping[0], + ], + }, + ]); + }); + + it('persists regenerated partial premapping after changing the data selection', async () => { + const regeneratedPremapping = [ + { + ...fixturePreMapping[0], + mapping: [ + { + ...fixturePreMapping[0].mapping[0], + destinationUuid: 'mr', + }, + { + ...fixturePreMapping[0].mapping[1], + destinationUuid: null, + }, + ], + }, + ]; + + store.dataSelectionIds = ['customersOrders']; + migrationApiServiceMock.generatePremapping.mockResolvedValueOnce(regeneratedPremapping); + + const wrapper = await createWrapper(); + + await wrapper.vm.fetchPremapping(); + + expect(migrationApiServiceMock.generatePremapping).toHaveBeenCalledTimes(1); + expect(migrationApiServiceMock.generatePremapping).toHaveBeenCalledWith(['customersOrders']); + expect(migrationApiServiceMock.writePremapping).toHaveBeenCalledTimes(1); + expect(migrationApiServiceMock.writePremapping).toHaveBeenCalledWith([ + { + ...store.premapping[0], + mapping: [ + regeneratedPremapping[0].mapping[0], + ], + }, + ]); + }); + + it('debounces repeated premapping changes into a single save', async () => { + jest.useFakeTimers(); + + store.premapping = fixturePreMapping; + + const wrapper = await createWrapper(); + + await wrapper.vm.onPremappingChanged(); + await wrapper.vm.onPremappingChanged(); + + expect(store.isLoading).toBe(true); + expect(migrationApiServiceMock.writePremapping).not.toHaveBeenCalled(); + + jest.advanceTimersByTime(500); + await flushPromises(); + + expect(migrationApiServiceMock.writePremapping).toHaveBeenCalledTimes(1); + expect(store.isLoading).toBe(false); + }); +}); From 7768ad61007cfaac43092f3cb90aaf9ef536f8d7 Mon Sep 17 00:00:00 2001 From: Jozsef Damokos Date: Tue, 26 May 2026 17:06:52 +0300 Subject: [PATCH 2/6] threads --- .../card/swag-migration-premapping/index.ts | 20 ++++------ .../card/swag-migration-premapping.spec.js | 38 ++++++++++++++----- 2 files changed, 35 insertions(+), 23 deletions(-) diff --git a/src/Resources/app/administration/src/module/swag-migration/component/card/swag-migration-premapping/index.ts b/src/Resources/app/administration/src/module/swag-migration/component/card/swag-migration-premapping/index.ts index 03c27a3a8..7c63247be 100644 --- a/src/Resources/app/administration/src/module/swag-migration/component/card/swag-migration-premapping/index.ts +++ b/src/Resources/app/administration/src/module/swag-migration/component/card/swag-migration-premapping/index.ts @@ -15,7 +15,6 @@ const { debounce } = Shopware.Utils; export interface SwagMigrationPremappingData { isLoading: boolean; migrationStore: MigrationStore; - savePremappingDebounced: null | (() => void); } /** @@ -33,20 +32,9 @@ export default Shopware.Component.wrapComponentConfig({ return { isLoading: false, migrationStore: Store.get(MIGRATION_STORE_ID), - savePremappingDebounced: null, }; }, - created() { - this.savePremappingDebounced = debounce(async () => { - try { - await this.savePremapping(); - } finally { - this.migrationStore.setIsLoading(false); - } - }, 500); - }, - computed: { ...mapState( () => Store.get(MIGRATION_STORE_ID), @@ -98,7 +86,13 @@ export default Shopware.Component.wrapComponentConfig({ async onPremappingChanged() { this.migrationStore.setIsLoading(true); - this.savePremappingDebounced?.(); + this.savePremappingDebounced(); }, + + savePremappingDebounced: debounce(function savePremappingDebounced() { + void this.savePremapping().finally(() => { + this.migrationStore.setIsLoading(false); + }); + }, 500), }, }); diff --git a/tests/Jest/src/module/swag-migration/component/card/swag-migration-premapping.spec.js b/tests/Jest/src/module/swag-migration/component/card/swag-migration-premapping.spec.js index 26e268bf9..3e0d7355b 100644 --- a/tests/Jest/src/module/swag-migration/component/card/swag-migration-premapping.spec.js +++ b/tests/Jest/src/module/swag-migration/component/card/swag-migration-premapping.spec.js @@ -1,4 +1,5 @@ import { mount } from '@vue/test-utils'; +import 'SwagMigrationAssistant/module/swag-migration/store/migration.store'; import swagMigrationPremapping from 'SwagMigrationAssistant/module/swag-migration/component/card/swag-migration-premapping'; import { fixturePreMapping } from '@/fixture'; @@ -9,6 +10,20 @@ const migrationApiServiceMock = { writePremapping: jest.fn(() => Promise.resolve()), }; +const swagMigrationTabCardStub = { + name: 'swag-migration-tab-card', + props: ['items'], + template: ` +
+ +
+ `, +}; + async function createWrapper() { return mount(await Shopware.Component.build('swag-migration-premapping'), { global: { @@ -17,7 +32,7 @@ async function createWrapper() { migrationApiService: migrationApiServiceMock, }, stubs: { - 'swag-migration-tab-card': true, + 'swag-migration-tab-card': swagMigrationTabCardStub, 'swag-migration-grid-selection': true, }, }, @@ -27,11 +42,9 @@ async function createWrapper() { describe('module/swag-migration/component/card/swag-migration-premapping', () => { let store = null; - beforeEach(async () => { + beforeEach(() => { jest.clearAllMocks(); - await import('SwagMigrationAssistant/module/swag-migration/store/migration.store'); - store = Shopware.Store.get('swagMigration'); store.$reset(); }); @@ -55,8 +68,12 @@ describe('module/swag-migration/component/card/swag-migration-premapping', () => ]; const wrapper = await createWrapper(); + jest.useFakeTimers(); + const gridSelection = wrapper.findComponent({ name: 'swag-migration-grid-selection' }); - await wrapper.vm.savePremapping(); + gridSelection.vm.$emit('update:value'); + jest.advanceTimersByTime(500); + await flushPromises(); expect(migrationApiServiceMock.writePremapping).toHaveBeenCalledTimes(1); expect(migrationApiServiceMock.writePremapping).toHaveBeenCalledWith([ @@ -86,12 +103,12 @@ describe('module/swag-migration/component/card/swag-migration-premapping', () => }, ]; - store.dataSelectionIds = ['customersOrders']; migrationApiServiceMock.generatePremapping.mockResolvedValueOnce(regeneratedPremapping); - const wrapper = await createWrapper(); + await createWrapper(); - await wrapper.vm.fetchPremapping(); + store.setDataSelectionIds(['customersOrders']); + await flushPromises(); expect(migrationApiServiceMock.generatePremapping).toHaveBeenCalledTimes(1); expect(migrationApiServiceMock.generatePremapping).toHaveBeenCalledWith(['customersOrders']); @@ -112,9 +129,10 @@ describe('module/swag-migration/component/card/swag-migration-premapping', () => store.premapping = fixturePreMapping; const wrapper = await createWrapper(); + const gridSelection = wrapper.findComponent({ name: 'swag-migration-grid-selection' }); - await wrapper.vm.onPremappingChanged(); - await wrapper.vm.onPremappingChanged(); + gridSelection.vm.$emit('update:value'); + gridSelection.vm.$emit('update:value'); expect(store.isLoading).toBe(true); expect(migrationApiServiceMock.writePremapping).not.toHaveBeenCalled(); From ffc53a0339b8b70ad1879ad67569d70384243800 Mon Sep 17 00:00:00 2001 From: Jozsef Damokos Date: Wed, 27 May 2026 11:11:11 +0300 Subject: [PATCH 3/6] retrigger CI From 28de2cd2c27705ee2b1f02615faaeb35f051b0ed Mon Sep 17 00:00:00 2001 From: Jozsef Damokos Date: Wed, 27 May 2026 18:36:42 +0300 Subject: [PATCH 4/6] retrigger CI From 48570f8ec76db09771419c41e1f02f736dbb9c03 Mon Sep 17 00:00:00 2001 From: Jozsef Damokos Date: Thu, 28 May 2026 10:52:13 +0300 Subject: [PATCH 5/6] retrigger CI From 5545807c8651832298e1806ae4db713715fee530 Mon Sep 17 00:00:00 2001 From: Jozsef Damokos Date: Thu, 28 May 2026 12:55:00 +0300 Subject: [PATCH 6/6] always run cs-fixer and phpstan --- .github/workflows/php.yaml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/.github/workflows/php.yaml b/.github/workflows/php.yaml index 25e13e53f..b03e4f0e2 100644 --- a/.github/workflows/php.yaml +++ b/.github/workflows/php.yaml @@ -6,11 +6,6 @@ on: branches: - trunk pull_request: - paths: - - composer.json - - src/**/*.php - - tests/**/*.php - - .github/workflows/** concurrency: group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}