pull/236/merge
EgleKat 2026-09-17 11:29:44 +00:00 committed by GitHub
commit 30f5668e7c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 72 additions and 4 deletions

View File

@ -1,6 +1,8 @@
import React from 'react'; import React from 'react';
import { useDispatch } from 'react-redux';
import RDV, { DiffMethod } from 'react-diff-viewer-continued'; import RDV, { DiffMethod } from 'react-diff-viewer-continued';
import { useIntl } from 'react-intl'; 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. * An issue with the diff-viewer library causes a difference in the way the library is exported.
@ -19,13 +21,20 @@ if (typeof RDV.default !== 'undefined') {
} }
import { import {
Button,
Modal, Modal,
Grid, Grid,
Typography, Typography,
} from '@strapi/design-system'; } from '@strapi/design-system';
import ConfirmModal from '../ConfirmModal';
import { exportAllConfig, importAllConfig } from '../../state/actions/Config';
const ConfigDiff = ({ oldValue, newValue, configName, trigger }) => { const ConfigDiff = ({ oldValue, newValue, configName, trigger }) => {
const { formatMessage } = useIntl(); const { formatMessage } = useIntl();
const dispatch = useDispatch();
const { toggleNotification } = useNotification();
const { post, get } = getFetchClient();
const commonHeadingStyle = { paddingRight: '2rem' };
return ( return (
<Modal.Root> <Modal.Root>
@ -40,11 +49,24 @@ const ConfigDiff = ({ oldValue, newValue, configName, trigger }) => {
</Modal.Header> </Modal.Header>
<Modal.Body> <Modal.Body>
<Grid.Root paddingBottom={4} style={{ textAlign: 'center' }}> <Grid.Root paddingBottom={4} style={{ textAlign: 'center' }}>
<Grid.Item col={6}> <Grid.Item col={6} style={{ justifyContent: 'center' }}>
<Typography variant="delta" style={{ width: '100%' }}>{formatMessage({ id: 'config-sync.ConfigDiff.SyncDirectory' })}</Typography> <Typography variant="delta" style={commonHeadingStyle}>{formatMessage({ id: 'config-sync.ConfigDiff.SyncDirectory' })}
</Typography>
<ConfirmModal
type="import"
trigger={<Button title="Import config into DB for this file only">Import</Button>}
onSubmit={(force) => dispatch(importAllConfig([configName], force, toggleNotification, formatMessage, post, get))}
/>
</Grid.Item> </Grid.Item>
<Grid.Item col={6}> <Grid.Item col={6} style={{ justifyContent: 'center' }}>
<Typography variant="delta" style={{ width: '100%' }}>{formatMessage({ id: 'config-sync.ConfigDiff.Database' })}</Typography> <Typography variant="delta" style={commonHeadingStyle}>
{formatMessage({ id: 'config-sync.ConfigDiff.Database' })}
</Typography>
<ConfirmModal
type="export"
trigger={<Button title="Export DB config for this file only">Export</Button>}
onSubmit={() => dispatch(exportAllConfig([configName], toggleNotification, formatMessage, post, get))}
/>
</Grid.Item> </Grid.Item>
</Grid.Root> </Grid.Root>
<Typography variant="pi"> <Typography variant="pi">

View File

@ -1,10 +1,16 @@
'use strict'; 'use strict';
const fs = require('fs');
const util = require('util'); const util = require('util');
const exec = util.promisify(require('child_process').exec); const exec = util.promisify(require('child_process').exec);
jest.setTimeout(20000); 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', () => { describe('Test the config-sync CLI', () => {
afterAll(async () => { afterAll(async () => {
// Remove the generated files and the DB. // 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'); 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 () => { test('Import (delete)', async () => {
// Remove a file to trigger a delete. // Remove a file to trigger a delete.
await exec('mv config/sync/admin-role.strapi-editor.json .tmp'); await exec('mv config/sync/admin-role.strapi-editor.json .tmp');