From a2ca88baf254c527ceeb1c1258f37344d6543e61 Mon Sep 17 00:00:00 2001 From: kat Date: Thu, 17 Sep 2026 11:28:35 +0100 Subject: [PATCH 1/3] test: individual import/export for a particular config file --- playground/__tests__/cli.test.js | 46 ++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/playground/__tests__/cli.test.js b/playground/__tests__/cli.test.js index 8ace6c6..1006267 100644 --- a/playground/__tests__/cli.test.js +++ b/playground/__tests__/cli.test.js @@ -1,10 +1,16 @@ 'use strict'; +const fs = require('fs'); const util = require('util'); const exec = util.promisify(require('child_process').exec); jest.setTimeout(20000); +const selectedConfig = 'admin-role.strapi-editor'; +const otherConfig = 'admin-role.strapi-author'; +const selectedConfigPath = `config/sync/${selectedConfig}.json`; +const otherConfigPath = `config/sync/${otherConfig}.json`; + describe('Test the config-sync CLI', () => { afterAll(async () => { // Remove the generated files and the DB. @@ -19,6 +25,46 @@ describe('Test the config-sync CLI', () => { expect(diffOutput).toContain('No differences between DB and sync directory'); }); + test('Partial export only exports the selected config', async () => { + const selectedConfigBefore = fs.readFileSync(selectedConfigPath, 'utf8'); + const otherConfigBefore = fs.readFileSync(otherConfigPath, 'utf8'); + const selectedConfigData = JSON.parse(selectedConfigBefore); + selectedConfigData.description = `${selectedConfigData.description} (partial export test)`; + fs.writeFileSync(selectedConfigPath, JSON.stringify(selectedConfigData)); + + try { + const { stdout: exportOutput } = await exec(`yarn cs export --partial ${selectedConfig} -y`); + + expect(exportOutput).toContain(`Exported ${selectedConfig}`); + expect(exportOutput).not.toContain(`Exported ${otherConfig}`); + expect(JSON.parse(fs.readFileSync(selectedConfigPath, 'utf8'))).toEqual(JSON.parse(selectedConfigBefore)); + expect(fs.readFileSync(otherConfigPath, 'utf8')).toBe(otherConfigBefore); + } finally { + fs.writeFileSync(selectedConfigPath, selectedConfigBefore); + } + }); + + test('Partial import only imports the selected config', async () => { + const selectedConfigBefore = fs.readFileSync(selectedConfigPath, 'utf8'); + const otherConfigBefore = fs.readFileSync(otherConfigPath, 'utf8'); + const selectedConfigData = JSON.parse(selectedConfigBefore); + selectedConfigData.description = `${selectedConfigData.description} (partial import test)`; + fs.writeFileSync(selectedConfigPath, JSON.stringify(selectedConfigData)); + + try { + const { stdout: importOutput } = await exec(`yarn cs import --partial ${selectedConfig} -y`); + const { stdout: diffOutput } = await exec('yarn cs diff'); + + expect(importOutput).toContain(`Imported ${selectedConfig}`); + expect(importOutput).not.toContain(`Imported ${otherConfig}`); + expect(diffOutput).toContain('No differences between DB and sync directory'); + expect(fs.readFileSync(otherConfigPath, 'utf8')).toBe(otherConfigBefore); + } finally { + fs.writeFileSync(selectedConfigPath, selectedConfigBefore); + await exec(`yarn cs import --partial ${selectedConfig} -y`); + } + }); + test('Import (delete)', async () => { // Remove a file to trigger a delete. await exec('mv config/sync/admin-role.strapi-editor.json .tmp'); From b172d90b2b59ac55be4e32e461f60ad0ed10e01f Mon Sep 17 00:00:00 2001 From: kat Date: Thu, 17 Sep 2026 12:16:36 +0100 Subject: [PATCH 2/3] feat: add import/export buttons for single config file --- admin/src/components/ConfigDiff/index.jsx | 30 ++++++++++++++++++++--- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/admin/src/components/ConfigDiff/index.jsx b/admin/src/components/ConfigDiff/index.jsx index 2599ac9..0d1e533 100644 --- a/admin/src/components/ConfigDiff/index.jsx +++ b/admin/src/components/ConfigDiff/index.jsx @@ -1,6 +1,9 @@ import React from 'react'; +import { useDispatch } from 'react-redux'; +import { Button } from '@strapi/design-system'; import RDV, { DiffMethod } from 'react-diff-viewer-continued'; import { useIntl } from 'react-intl'; +import { getFetchClient, useNotification } from '@strapi/strapi/admin'; /** * An issue with the diff-viewer library causes a difference in the way the library is exported. @@ -23,9 +26,15 @@ import { Grid, Typography, } from '@strapi/design-system'; +import ConfirmModal from '../ConfirmModal'; +import { exportAllConfig, importAllConfig } from '../../state/actions/Config'; const ConfigDiff = ({ oldValue, newValue, configName, trigger }) => { const { formatMessage } = useIntl(); + const dispatch = useDispatch(); + const { toggleNotification } = useNotification(); + const { post, get } = getFetchClient(); + const commonHeadingStyle = { paddingRight: '2rem' }; return ( @@ -40,11 +49,24 @@ const ConfigDiff = ({ oldValue, newValue, configName, trigger }) => { - - {formatMessage({ id: 'config-sync.ConfigDiff.SyncDirectory' })} + + {formatMessage({ id: 'config-sync.ConfigDiff.SyncDirectory' })} + + Import} + onSubmit={(force) => dispatch(importAllConfig([configName], force, toggleNotification, formatMessage, post, get))} + /> - - {formatMessage({ id: 'config-sync.ConfigDiff.Database' })} + + + {formatMessage({ id: 'config-sync.ConfigDiff.Database' })} + + Export} + onSubmit={() => dispatch(exportAllConfig([configName], toggleNotification, formatMessage, post, get))} + /> From a28754385c327e8f18ed980708cbad51c87331c5 Mon Sep 17 00:00:00 2001 From: kat Date: Thu, 17 Sep 2026 12:29:25 +0100 Subject: [PATCH 3/3] feat: fix eslint errprs --- admin/src/components/ConfigDiff/index.jsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/admin/src/components/ConfigDiff/index.jsx b/admin/src/components/ConfigDiff/index.jsx index 0d1e533..abdf266 100644 --- a/admin/src/components/ConfigDiff/index.jsx +++ b/admin/src/components/ConfigDiff/index.jsx @@ -1,6 +1,5 @@ import React from 'react'; import { useDispatch } from 'react-redux'; -import { Button } from '@strapi/design-system'; import RDV, { DiffMethod } from 'react-diff-viewer-continued'; import { useIntl } from 'react-intl'; import { getFetchClient, useNotification } from '@strapi/strapi/admin'; @@ -22,6 +21,7 @@ if (typeof RDV.default !== 'undefined') { } import { + Button, Modal, Grid, Typography, @@ -49,7 +49,7 @@ const ConfigDiff = ({ oldValue, newValue, configName, trigger }) => { - + {formatMessage({ id: 'config-sync.ConfigDiff.SyncDirectory' })} { - {formatMessage({ id: 'config-sync.ConfigDiff.Database' })} + {formatMessage({ id: 'config-sync.ConfigDiff.Database' })}