mirror of
https://github.com/strapi/strapi.git
synced 2025-09-18 04:47:27 +00:00
Merge branch 'fix/dts-bypass-schema-check' into fix/dts-engine-test
This commit is contained in:
commit
1811a7ca36
2
.github/dependabot.yml
vendored
2
.github/dependabot.yml
vendored
@ -6,6 +6,7 @@ updates:
|
||||
interval: weekly
|
||||
day: sunday
|
||||
time: '22:00'
|
||||
open-pull-requests-limit: 10
|
||||
versioning-strategy: increase
|
||||
ignore:
|
||||
# Only allow patch as minor babel versions need to be upgraded all together
|
||||
@ -23,6 +24,7 @@ updates:
|
||||
- 'source: dependencies'
|
||||
- 'pr: chore'
|
||||
- package-ecosystem: github-actions
|
||||
open-pull-requests-limit: 10
|
||||
directory: /
|
||||
schedule:
|
||||
interval: weekly
|
||||
|
@ -27,7 +27,7 @@
|
||||
"better-sqlite3": "8.3.0",
|
||||
"lodash": "4.17.21",
|
||||
"mysql": "2.18.1",
|
||||
"mysql2": "3.2.0",
|
||||
"mysql2": "3.3.0",
|
||||
"passport-google-oauth2": "0.2.0",
|
||||
"pg": "8.8.0",
|
||||
"react": "^17.0.2",
|
||||
|
@ -30,7 +30,7 @@ const moduleNameMapper = {
|
||||
module.exports = {
|
||||
rootDir: __dirname,
|
||||
moduleNameMapper,
|
||||
testPathIgnorePatterns: ['/node_modules/', '__tests__'],
|
||||
testPathIgnorePatterns: ['node_modules/', '__tests__'],
|
||||
globalSetup: '@strapi/admin-test-utils/global-setup',
|
||||
setupFiles: ['@strapi/admin-test-utils/environment'],
|
||||
setupFilesAfterEnv: ['@strapi/admin-test-utils/after-env'],
|
||||
@ -60,7 +60,7 @@ module.exports = {
|
||||
transformIgnorePatterns: [
|
||||
'node_modules/(?!(react-dnd|dnd-core|react-dnd-html5-backend|@strapi/design-system|@strapi/icons|fractional-indexing)/)',
|
||||
],
|
||||
testMatch: ['/**/tests/**/?(*.)+(spec|test).[jt]s?(x)'],
|
||||
testMatch: ['**/tests/**/?(*.)+(spec|test).[jt]s?(x)'],
|
||||
testEnvironmentOptions: {
|
||||
url: 'http://localhost:1337/admin',
|
||||
},
|
||||
|
@ -4,7 +4,7 @@ module.exports = {
|
||||
setupFilesAfterEnv: [__dirname + '/test/unit.setup.js'],
|
||||
modulePathIgnorePatterns: ['.cache', 'dist'],
|
||||
testPathIgnorePatterns: ['.testdata.js', '.test.utils.js'],
|
||||
testMatch: ['/**/__tests__/**/*.[jt]s?(x)'],
|
||||
testMatch: ['**/__tests__/**/*.[jt]s?(x)'],
|
||||
// Use `jest-watch-typeahead` version 0.6.5. Newest version 1.0.0 does not support jest@26
|
||||
// Reference: https://github.com/jest-community/jest-watch-typeahead/releases/tag/v1.0.0
|
||||
watchPlugins: ['jest-watch-typeahead/filename', 'jest-watch-typeahead/testname'],
|
||||
|
@ -101,7 +101,7 @@
|
||||
"jest-environment-jsdom": "29.0.3",
|
||||
"jest-watch-typeahead": "2.2.2",
|
||||
"lerna": "6.5.1",
|
||||
"lint-staged": "13.0.3",
|
||||
"lint-staged": "13.2.2",
|
||||
"lodash": "4.17.21",
|
||||
"nx": "15.8.3",
|
||||
"plop": "2.7.6",
|
||||
|
1
packages/admin-test-utils/custom.d.ts
vendored
1
packages/admin-test-utils/custom.d.ts
vendored
@ -10,6 +10,7 @@ declare global {
|
||||
isEnabled: (featureName?: string) => boolean;
|
||||
};
|
||||
projectType: string;
|
||||
telemetryDisabled: boolean;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -57,6 +57,7 @@ window.strapi = {
|
||||
isEnabled: () => false,
|
||||
},
|
||||
projectType: 'Community',
|
||||
telemetryDisabled: true,
|
||||
};
|
||||
|
||||
/* -------------------------------------------------------------------------------------------------
|
||||
|
@ -17,7 +17,6 @@ jest.mock('@strapi/helper-plugin', () => ({
|
||||
// eslint-disable-next-line
|
||||
CheckPermissions: ({ children }) => <div>{children}</div>,
|
||||
useNotification: jest.fn(),
|
||||
useTracking: jest.fn(() => ({ trackUsage: jest.fn() })),
|
||||
}));
|
||||
|
||||
const client = new QueryClient({
|
||||
|
@ -13,7 +13,6 @@ import ModelsContext from '../../../contexts/ModelsContext';
|
||||
jest.mock('@strapi/helper-plugin', () => ({
|
||||
...jest.requireActual('@strapi/helper-plugin'),
|
||||
useNotification: jest.fn(),
|
||||
useTracking: jest.fn(() => ({ trackUsage: jest.fn() })),
|
||||
}));
|
||||
|
||||
const client = new QueryClient({
|
||||
@ -191,24 +190,26 @@ describe('ADMIN | CM | LV | Configure the view', () => {
|
||||
screen.getByText("This value overrides the label displayed in the table's head")
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
|
||||
it('should close edit modal onSubmit', async () => {
|
||||
const history = createMemoryHistory();
|
||||
|
||||
|
||||
const { queryByText } = render(makeApp(history));
|
||||
await waitFor(() =>
|
||||
expect(screen.getByText('Configure the view - Michka')).toBeInTheDocument()
|
||||
);
|
||||
|
||||
|
||||
fireEvent.click(screen.getByLabelText('Edit address'));
|
||||
|
||||
|
||||
expect(
|
||||
screen.getByText("This value overrides the label displayed in the table's head")
|
||||
).toBeInTheDocument();
|
||||
|
||||
|
||||
fireEvent.click(screen.getByText('Finish'));
|
||||
|
||||
expect(queryByText("This value overrides the label displayed in the table's head")).not.toBeInTheDocument();
|
||||
|
||||
expect(
|
||||
queryByText("This value overrides the label displayed in the table's head")
|
||||
).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should not show sortable toggle input if field not sortable', async () => {
|
||||
|
@ -44,6 +44,9 @@ const SettingsPage = lazy(() =>
|
||||
);
|
||||
|
||||
// Simple hook easier for testing
|
||||
/**
|
||||
* TODO: remove this, it's bad.
|
||||
*/
|
||||
const useTrackUsage = () => {
|
||||
const { trackUsage } = useTracking();
|
||||
const dispatch = useDispatch();
|
||||
|
@ -15,9 +15,9 @@ jest.mock('react-redux', () => ({
|
||||
}));
|
||||
|
||||
jest.mock('@strapi/helper-plugin', () => ({
|
||||
...jest.requireActual('@strapi/helper-plugin'),
|
||||
LoadingIndicatorPage: () => <div>Loading</div>,
|
||||
useStrapiApp: jest.fn(() => ({ menu: [] })),
|
||||
useTracking: jest.fn(() => ({ trackUsage: jest.fn() })),
|
||||
NotFound: () => <div>not found</div>,
|
||||
CheckPagePermissions: ({ children }) => children,
|
||||
useGuidedTour: jest.fn(() => ({
|
||||
@ -34,7 +34,6 @@ jest.mock('@strapi/helper-plugin', () => ({
|
||||
|
||||
jest.mock('../../../hooks', () => ({
|
||||
useMenu: jest.fn(() => ({ isLoading: true, generalSectionLinks: [], pluginsSectionLinks: [] })),
|
||||
useTrackUsage: jest.fn(),
|
||||
useReleaseNotification: jest.fn(),
|
||||
useConfigurations: jest.fn(() => ({ showTutorials: false })),
|
||||
}));
|
||||
|
@ -1,18 +1,18 @@
|
||||
import { renderHook } from '@testing-library/react-hooks';
|
||||
import { useTrackUsage } from '..';
|
||||
import { useTrackUsage } from '../index';
|
||||
|
||||
const trackUsageMock = jest.fn();
|
||||
|
||||
jest.mock('@strapi/helper-plugin', () => ({
|
||||
useTracking: jest.fn(() => ({ trackUsage: trackUsageMock })),
|
||||
}));
|
||||
|
||||
jest.mock('react-redux', () => ({
|
||||
...jest.requireActual('react-redux'),
|
||||
useDispatch: jest.fn(() => jest.fn()),
|
||||
useSelector: jest.fn(() => 'init'),
|
||||
}));
|
||||
|
||||
jest.mock('@strapi/helper-plugin', () => ({
|
||||
useTracking: jest.fn(() => ({ trackUsage: trackUsageMock })),
|
||||
}));
|
||||
|
||||
describe('Admin | pages | Admin | useTrackUsage', () => {
|
||||
it('should call the trackUsage method on mount with didAccessAuthenticatedAdministration', () => {
|
||||
const { rerender } = renderHook(() => useTrackUsage());
|
||||
|
@ -149,7 +149,7 @@ const Register = ({ authType, fieldsToDisable, noSignin, onSubmit, schema }) =>
|
||||
return (
|
||||
<Form noValidate>
|
||||
<Main>
|
||||
<Flex direction="column" alignItems="stretch" gap={3}>
|
||||
<Flex direction="column" alignItems="center" gap={3}>
|
||||
<Logo />
|
||||
|
||||
<Typography as="h1" variant="alpha" textAlign="center">
|
||||
|
@ -11,7 +11,6 @@ import { useModels } from '../../../hooks';
|
||||
jest.mock('@strapi/helper-plugin', () => ({
|
||||
...jest.requireActual('@strapi/helper-plugin'),
|
||||
useAppInfo: jest.fn(() => ({ communityEdition: true })),
|
||||
useTracking: jest.fn(() => ({ trackUsage: jest.fn() })),
|
||||
useGuidedTour: jest.fn(() => ({
|
||||
isGuidedTourVisible: false,
|
||||
guidedTourState: {
|
||||
|
@ -115,10 +115,14 @@ exports[`Marketplace page - layout renders the online layout 1`] = `
|
||||
|
||||
.c63 {
|
||||
color: #666687;
|
||||
width: 0.75rem;
|
||||
height: 0.75rem;
|
||||
}
|
||||
|
||||
.c65 {
|
||||
color: #f29d41;
|
||||
width: 0.75rem;
|
||||
height: 0.75rem;
|
||||
}
|
||||
|
||||
.c68 {
|
||||
@ -1114,6 +1118,7 @@ exports[`Marketplace page - layout renders the online layout 1`] = `
|
||||
}
|
||||
|
||||
.c70 {
|
||||
width: 0.75rem;
|
||||
-webkit-transform: rotate(90deg);
|
||||
-ms-transform: rotate(90deg);
|
||||
transform: rotate(90deg);
|
||||
|
@ -17,12 +17,11 @@ jest.mock('../../../hooks/useNavigatorOnLine', () => jest.fn(() => true));
|
||||
|
||||
jest.mock('@strapi/helper-plugin', () => ({
|
||||
...jest.requireActual('@strapi/helper-plugin'),
|
||||
useTracking: jest.fn(() => ({ trackUsage: jest.fn() })),
|
||||
useNotification: jest.fn(() => {
|
||||
return toggleNotification;
|
||||
}),
|
||||
pxToRem: jest.fn(),
|
||||
CheckPagePermissions: ({ children }) => children,
|
||||
useTracking: jest.fn(() => ({ trackUsage: jest.fn() })),
|
||||
useAppInfo: jest.fn(() => ({
|
||||
autoReload: true,
|
||||
dependencies: {
|
||||
|
@ -20,9 +20,7 @@ jest.mock('@strapi/helper-plugin', () => ({
|
||||
useNotification: jest.fn(() => {
|
||||
return toggleNotification;
|
||||
}),
|
||||
pxToRem: jest.fn(),
|
||||
CheckPagePermissions: ({ children }) => children,
|
||||
useTracking: jest.fn(() => ({ trackUsage: jest.fn() })),
|
||||
useAppInfo: jest.fn(() => ({
|
||||
autoReload: true,
|
||||
dependencies: {
|
||||
|
@ -22,9 +22,7 @@ jest.mock('@strapi/helper-plugin', () => ({
|
||||
useNotification: jest.fn(() => {
|
||||
return toggleNotification;
|
||||
}),
|
||||
pxToRem: jest.fn(),
|
||||
CheckPagePermissions: ({ children }) => children,
|
||||
useTracking: jest.fn(() => ({ trackUsage: jest.fn() })),
|
||||
useAppInfo: jest.fn(() => ({
|
||||
autoReload: true,
|
||||
dependencies: {
|
||||
|
@ -20,7 +20,6 @@ jest.mock('@strapi/helper-plugin', () => ({
|
||||
useFocusWhenNavigate: jest.fn(),
|
||||
useAppInfo: jest.fn(() => ({ setUserDisplayName: jest.fn() })),
|
||||
useOverlayBlocker: jest.fn(() => ({ lockApp: jest.fn, unlockApp: jest.fn() })),
|
||||
useTracking: jest.fn(() => ({ trackUsage: jest.fn() })),
|
||||
}));
|
||||
|
||||
const client = new QueryClient({
|
||||
|
@ -6,11 +6,6 @@ import { createMemoryHistory } from 'history';
|
||||
import { ThemeProvider, lightTheme } from '@strapi/design-system';
|
||||
import SettingsNav from '../index';
|
||||
|
||||
jest.mock('@strapi/helper-plugin', () => ({
|
||||
...jest.requireActual('@strapi/helper-plugin'),
|
||||
useTracking: jest.fn(() => ({ trackUsage: jest.fn() })),
|
||||
}));
|
||||
|
||||
const menu = [
|
||||
{
|
||||
id: 'global',
|
||||
|
@ -6,11 +6,6 @@ import { NotificationsProvider } from '@strapi/helper-plugin';
|
||||
|
||||
import DeleteButton from '../index';
|
||||
|
||||
jest.mock('@strapi/helper-plugin', () => ({
|
||||
...jest.requireActual('@strapi/helper-plugin'),
|
||||
useTracking: jest.fn(() => ({ trackUsage: jest.fn() })),
|
||||
}));
|
||||
|
||||
function ComponentToTest(props) {
|
||||
return (
|
||||
<IntlProvider locale="en" messages={{}}>
|
||||
|
@ -14,7 +14,6 @@ jest.mock('@strapi/helper-plugin', () => ({
|
||||
...jest.requireActual('@strapi/helper-plugin'),
|
||||
useNotification: jest.fn(),
|
||||
useFocusWhenNavigate: jest.fn(),
|
||||
useTracking: jest.fn(() => ({ trackUsage: jest.fn() })),
|
||||
useRBAC: jest.fn(() => ({
|
||||
allowedActions: {
|
||||
canCreate: true,
|
||||
|
@ -24,9 +24,6 @@ const PROJECT_SETTINGS_DATA_FIXTURES = {
|
||||
},
|
||||
};
|
||||
|
||||
jest.mock('@strapi/helper-plugin', () => ({
|
||||
useTracking: jest.fn(() => ({ trackUsage: jest.fn() })),
|
||||
}));
|
||||
jest.mock('../../../../../../../hooks', () => ({
|
||||
useConfigurations: jest.fn(() => ({
|
||||
logos: {
|
||||
|
@ -19,7 +19,6 @@ jest.mock('@strapi/helper-plugin', () => ({
|
||||
...jest.requireActual('@strapi/helper-plugin'),
|
||||
useNotification: jest.fn(() => jest.fn()),
|
||||
useOverlayBlocker: jest.fn(() => ({ lockApp: jest.fn(), unlockApp: jest.fn() })),
|
||||
useTracking: jest.fn(() => ({ trackUsage: jest.fn() })),
|
||||
}));
|
||||
|
||||
const makeApp = (history) => (
|
||||
|
@ -19,7 +19,6 @@ jest.mock('@strapi/helper-plugin', () => ({
|
||||
...jest.requireActual('@strapi/helper-plugin'),
|
||||
useNotification: jest.fn(() => jest.fn()),
|
||||
useOverlayBlocker: jest.fn(() => ({ lockApp: jest.fn(), unlockApp: jest.fn() })),
|
||||
useTracking: jest.fn(() => ({ trackUsage: jest.fn() })),
|
||||
}));
|
||||
|
||||
const makeApp = (history) => (
|
||||
|
@ -14,7 +14,6 @@ jest.mock('@strapi/helper-plugin', () => ({
|
||||
...jest.requireActual('@strapi/helper-plugin'),
|
||||
useNotification: jest.fn(),
|
||||
useFocusWhenNavigate: jest.fn(),
|
||||
useTracking: jest.fn(() => ({ trackUsage: jest.fn() })),
|
||||
useRBAC: jest.fn(() => ({
|
||||
allowedActions: {
|
||||
canCreate: true,
|
||||
|
@ -219,6 +219,11 @@
|
||||
"Settings.profile.form.section.experience.title": "Опыт",
|
||||
"Settings.profile.form.section.helmet.title": "Профиль пользователя",
|
||||
"Settings.profile.form.section.profile.page.title": "Страница профиля",
|
||||
"Settings.review-workflows.page.title": "Просмотр рабочих процессов",
|
||||
"Settings.review-workflows.page.subtitle": "{count, plural, =0 {# этапов} one {# этап} other {# этапа}}",
|
||||
"Settings.review-workflows.page.isLoading": "Рабочие процессы загружаются",
|
||||
"Settings.review-workflows.page.delete.confirm.body": "Все записи, назначенные удаленным этапам, будут перемещены на предыдущий этап. Вы уверены, что хотите сохранить?",
|
||||
"Settings.review-workflows.stage.name.label": "Имя этапа",
|
||||
"Settings.roles.create.description": "Определите права, предоставленные ролью",
|
||||
"Settings.roles.create.title": "Создать роль",
|
||||
"Settings.roles.created": "Роль создана",
|
||||
@ -321,6 +326,7 @@
|
||||
"Users.components.List.empty": "Нет пользователей...",
|
||||
"Users.components.List.empty.withFilters": "Нет пользователей с применёнными фильтрами...",
|
||||
"Users.components.List.empty.withSearch": "Нет пользователей, соответствующих запросу ({search})...",
|
||||
"admin.pages.MarketPlacePage.sort.label": "Сортировка",
|
||||
"admin.pages.MarketPlacePage.filters.categories": "Категории",
|
||||
"admin.pages.MarketPlacePage.filters.categoriesSelected": "Выбрано {count, plural, =0 {# категорий} one {# категория} other {# категории}}",
|
||||
"admin.pages.MarketPlacePage.filters.collections": "Коллекции",
|
||||
@ -803,6 +809,7 @@
|
||||
"content-manager.relation.notAvailable": "Нет отношений",
|
||||
"content-manager.relation.publicationState.draft": "Черновик",
|
||||
"content-manager.relation.publicationState.published": "Опубликовано",
|
||||
"content-manager.reviewWorkflows.stage.label": "Просмотреть этап",
|
||||
"content-manager.select.currently.selected": "{count} выбрано",
|
||||
"content-manager.success.record.delete": "Удалено",
|
||||
"content-manager.success.record.publish": "Опубликовано",
|
||||
|
@ -3,10 +3,21 @@ import PropTypes from 'prop-types';
|
||||
import { Box, Flex, Typography } from '@strapi/design-system';
|
||||
import { pxToRem } from '@strapi/helper-plugin';
|
||||
|
||||
import { getStageColorByHex } from '../../../../../pages/SettingsPage/pages/ReviewWorkflows/utils/colors';
|
||||
|
||||
export function ReviewWorkflowsStageEE({ color, name }) {
|
||||
const { themeColorName } = getStageColorByHex(color);
|
||||
|
||||
return (
|
||||
<Flex alignItems="center" gap={2} maxWidth={pxToRem(300)}>
|
||||
<Box height={2} background={color} hasRadius shrink={0} width={2} />
|
||||
<Box
|
||||
height={2}
|
||||
background={color}
|
||||
borderColor={themeColorName === 'neutral0' ? 'neutral150' : 'transparent'}
|
||||
hasRadius
|
||||
shrink={0}
|
||||
width={2}
|
||||
/>
|
||||
|
||||
<Typography fontWeight="regular" textColor="neutral700" ellipsis>
|
||||
{name}
|
||||
|
@ -17,7 +17,7 @@ const setup = (props) => render(<ComponentFixture {...props} />);
|
||||
|
||||
describe('DynamicTable | ReviewWorkflowsStage', () => {
|
||||
test('render stage name', () => {
|
||||
const { getByText } = setup({ color: 'red', name: 'reviewed' });
|
||||
const { getByText } = setup({ color: '#4945FF', name: 'reviewed' });
|
||||
|
||||
expect(getByText('reviewed')).toBeInTheDocument();
|
||||
});
|
||||
|
@ -12,7 +12,7 @@ import { InformationBoxEE } from '../InformationBoxEE';
|
||||
const STAGE_ATTRIBUTE_NAME = 'strapi_reviewWorkflows_stage';
|
||||
const STAGE_FIXTURE = {
|
||||
id: 1,
|
||||
color: 'red',
|
||||
color: '#4945FF',
|
||||
name: 'Stage 1',
|
||||
worklow: 1,
|
||||
};
|
||||
@ -36,11 +36,12 @@ jest.mock(
|
||||
stages: [
|
||||
{
|
||||
id: 1,
|
||||
color: '#4945FF',
|
||||
name: 'Stage 1',
|
||||
},
|
||||
|
||||
{
|
||||
id: 2,
|
||||
color: '#4945FF',
|
||||
name: 'Stage 2',
|
||||
},
|
||||
],
|
||||
|
@ -3,13 +3,23 @@ import PropTypes from 'prop-types';
|
||||
import { components } from 'react-select';
|
||||
import { Flex, Typography } from '@strapi/design-system';
|
||||
|
||||
import { getStageColorByHex } from '../../../../../utils/colors';
|
||||
|
||||
export function OptionColor({ children, ...props }) {
|
||||
const { color } = props.data;
|
||||
const { themeColorName } = getStageColorByHex(color);
|
||||
|
||||
return (
|
||||
<components.Option {...props}>
|
||||
<Flex alignItems="center" gap={2}>
|
||||
<Flex height={2} background={color} hasRadius shrink={0} width={2} />
|
||||
<Flex
|
||||
height={2}
|
||||
background={color}
|
||||
borderColor={themeColorName === 'neutral0' ? 'neutral150' : 'transparent'}
|
||||
hasRadius
|
||||
shrink={0}
|
||||
width={2}
|
||||
/>
|
||||
|
||||
<Typography textColor="neutral800" ellipsis>
|
||||
{children}
|
||||
|
@ -3,13 +3,26 @@ import PropTypes from 'prop-types';
|
||||
import { components } from 'react-select';
|
||||
import { Flex, Typography } from '@strapi/design-system';
|
||||
|
||||
import { getStageColorByHex } from '../../../../../utils/colors';
|
||||
|
||||
export function SingleValueColor({ children, ...props }) {
|
||||
const { color } = props.data;
|
||||
// in case an entity was not assigned to a stage (which displays an error)
|
||||
// there is no color to display and we have to make sure the component does
|
||||
// not crash
|
||||
const { themeColorName } = color ? getStageColorByHex(color) : {};
|
||||
|
||||
return (
|
||||
<components.SingleValue {...props}>
|
||||
<Flex alignItems="center" gap={2}>
|
||||
<Flex height={2} background={color} hasRadius shrink={0} width={2} />
|
||||
<Flex
|
||||
height={2}
|
||||
background={color}
|
||||
borderColor={themeColorName === 'neutral0' ? 'neutral150' : 'transparent'}
|
||||
hasRadius
|
||||
shrink={0}
|
||||
width={2}
|
||||
/>
|
||||
|
||||
<Typography textColor="neutral800" ellipsis>
|
||||
{children}
|
||||
|
@ -15,11 +15,6 @@ import { reducer } from '../../../../reducer';
|
||||
|
||||
import { STAGE_COLOR_DEFAULT } from '../../../../constants';
|
||||
|
||||
jest.mock('@strapi/helper-plugin', () => ({
|
||||
...jest.requireActual('@strapi/helper-plugin'),
|
||||
useTracking: jest.fn().mockReturnValue({ trackUsage: jest.fn() }),
|
||||
}));
|
||||
|
||||
const STAGES_FIXTURE = {
|
||||
id: 1,
|
||||
index: 0,
|
||||
|
@ -21,11 +21,6 @@ jest.mock('../../../actions', () => ({
|
||||
...jest.requireActual('../../../actions'),
|
||||
}));
|
||||
|
||||
jest.mock('@strapi/helper-plugin', () => ({
|
||||
...jest.requireActual('@strapi/helper-plugin'),
|
||||
useTracking: jest.fn().mockReturnValue({ trackUsage: jest.fn() }),
|
||||
}));
|
||||
|
||||
const STAGES_FIXTURE = [
|
||||
{
|
||||
id: 1,
|
||||
|
@ -14,12 +14,13 @@ const WORKFLOWS_FIXTURE = [
|
||||
stages: [
|
||||
{
|
||||
id: 1,
|
||||
color: 'red',
|
||||
color: '#4945FF',
|
||||
name: 'stage-1',
|
||||
},
|
||||
|
||||
{
|
||||
id: 2,
|
||||
color: '#4945FF',
|
||||
name: 'stage-2',
|
||||
},
|
||||
],
|
||||
@ -353,7 +354,7 @@ describe('Admin | Settings | Review Workflows | reducer', () => {
|
||||
stages: expect.arrayContaining([
|
||||
{
|
||||
id: 1,
|
||||
color: 'red',
|
||||
color: '#4945FF',
|
||||
name: 'stage-1-modified',
|
||||
},
|
||||
]),
|
||||
|
@ -18,7 +18,6 @@ import { reducer } from '../reducer';
|
||||
jest.mock('@strapi/helper-plugin', () => ({
|
||||
...jest.requireActual('@strapi/helper-plugin'),
|
||||
useNotification: jest.fn().mockReturnValue(jest.fn()),
|
||||
useTracking: jest.fn().mockReturnValue({ trackUsage: jest.fn() }),
|
||||
// eslint-disable-next-line react/prop-types
|
||||
CheckPagePermissions({ children }) {
|
||||
return children;
|
||||
|
@ -3,10 +3,16 @@ import { lightTheme } from '@strapi/design-system';
|
||||
import { STAGE_COLORS } from '../constants';
|
||||
|
||||
export function getStageColorByHex(hex) {
|
||||
if (!hex) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// there are multiple colors with the same hex code in the design tokens. In order to find
|
||||
// the correct one we have to find all matching colors and then check, which ones are usable
|
||||
// for stages.
|
||||
const themeColors = Object.entries(lightTheme.colors).filter(([, value]) => value === hex);
|
||||
const themeColors = Object.entries(lightTheme.colors).filter(
|
||||
([, value]) => value.toUpperCase() === hex.toUpperCase()
|
||||
);
|
||||
const themeColorName = themeColors.reduce((acc, [name]) => {
|
||||
if (STAGE_COLORS?.[name]) {
|
||||
acc = name;
|
||||
|
@ -22,6 +22,11 @@ describe('Settings | Review Workflows | colors', () => {
|
||||
themeColorName: 'primary600',
|
||||
});
|
||||
|
||||
expect(getStageColorByHex('#4945FF')).toStrictEqual({
|
||||
name: 'Blue',
|
||||
themeColorName: 'primary600',
|
||||
});
|
||||
|
||||
expect(getStageColorByHex('random')).toStrictEqual(null);
|
||||
expect(getStageColorByHex()).toStrictEqual(null);
|
||||
});
|
||||
|
@ -10,7 +10,6 @@ import { SingleSignOn } from '../index';
|
||||
|
||||
jest.mock('@strapi/helper-plugin', () => ({
|
||||
...jest.requireActual('@strapi/helper-plugin'),
|
||||
useTracking: jest.fn(() => ({ trackUsage: jest.fn() })),
|
||||
useNotification: jest.fn().mockImplementation(() => jest.fn()),
|
||||
useOverlayBlocker: jest.fn(() => ({ lockApp: jest.fn(), unlockApp: jest.fn() })),
|
||||
useRBAC: jest.fn(),
|
||||
|
@ -46,7 +46,7 @@
|
||||
"@babel/preset-react": "^7.18.6",
|
||||
"@babel/runtime": "^7.20.13",
|
||||
"@casl/ability": "^5.4.3",
|
||||
"@fingerprintjs/fingerprintjs": "3.3.6",
|
||||
"@fingerprintjs/fingerprintjs": "3.4.1",
|
||||
"@pmmmwh/react-refresh-webpack-plugin": "0.5.10",
|
||||
"@strapi/babel-plugin-switch-ee-ce": "4.10.2",
|
||||
"@strapi/data-transfer": "4.10.2",
|
||||
@ -134,9 +134,9 @@
|
||||
"style-loader": "3.3.1",
|
||||
"styled-components": "5.3.3",
|
||||
"typescript": "5.0.4",
|
||||
"webpack": "^5.76.0",
|
||||
"webpack-cli": "^5.0.1",
|
||||
"webpack-dev-server": "^4.13.1",
|
||||
"webpack": "^5.82.0",
|
||||
"webpack-cli": "^5.1.0",
|
||||
"webpack-dev-server": "^4.15.0",
|
||||
"webpackbar": "^5.0.2",
|
||||
"yup": "^0.32.9"
|
||||
},
|
||||
@ -147,7 +147,7 @@
|
||||
"@testing-library/user-event": "14.4.3",
|
||||
"duplicate-dependencies-webpack-plugin": "^1.0.2",
|
||||
"glob": "8.0.3",
|
||||
"msw": "1.0.1",
|
||||
"msw": "1.2.1",
|
||||
"react-test-renderer": "^17.0.2",
|
||||
"speed-measure-webpack-plugin": "1.5.0",
|
||||
"webpack-bundle-analyzer": "^4.8.0"
|
||||
|
@ -25,7 +25,6 @@ const aliasExactMatch = [
|
||||
'react-intl',
|
||||
'react-query',
|
||||
'react-redux',
|
||||
'react-router',
|
||||
'react-router-dom',
|
||||
'react-window',
|
||||
'react-select',
|
||||
|
@ -34,9 +34,6 @@ jest.mock('@strapi/helper-plugin', () => ({
|
||||
get: jest.fn().mockReturnValue(mockCustomField),
|
||||
getAll,
|
||||
}),
|
||||
useTracking: jest.fn(() => ({
|
||||
trackUsage: jest.fn(),
|
||||
})),
|
||||
}));
|
||||
|
||||
const mockAttributes = [
|
||||
|
@ -3,12 +3,6 @@ import { INITIAL_STATE_DATA } from '../constants';
|
||||
import FormModalNavigationProvider from '../index';
|
||||
import useFormModalNavigation from '../../../hooks/useFormModalNavigation';
|
||||
|
||||
jest.mock('@strapi/helper-plugin', () => ({
|
||||
useTracking: jest.fn(() => ({
|
||||
trackUsage: jest.fn(),
|
||||
})),
|
||||
}));
|
||||
|
||||
const removeFunctionsFromObject = (state) => {
|
||||
const stringified = JSON.stringify(state);
|
||||
const parsed = JSON.parse(stringified);
|
||||
|
@ -32,7 +32,6 @@ jest.mock('@strapi/helper-plugin', () => ({
|
||||
...jest.requireActual('@strapi/helper-plugin'),
|
||||
// eslint-disable-next-line
|
||||
CheckPermissions: ({ children }) => <div>{children}</div>,
|
||||
useTracking: jest.fn(() => ({ trackUsage: jest.fn() })),
|
||||
}));
|
||||
|
||||
const makeApp = () => {
|
||||
|
@ -4,8 +4,8 @@ import { EOL } from 'os';
|
||||
import { isEmpty, uniq, last, isNumber, difference, omit, set } from 'lodash/fp';
|
||||
import { diff as semverDiff } from 'semver';
|
||||
import type { Schema } from '@strapi/strapi';
|
||||
import * as utils from '../utils';
|
||||
|
||||
import chalk from 'chalk';
|
||||
import type {
|
||||
IAsset,
|
||||
IDestinationProvider,
|
||||
@ -108,6 +108,8 @@ class TransferEngine<
|
||||
|
||||
#metadata: { source?: IMetadata; destination?: IMetadata } = {};
|
||||
|
||||
#ignoredDiffs: Record<string, utils.json.Diff[]> = {};
|
||||
|
||||
// Progress of the current stage
|
||||
progress: {
|
||||
// metrics on the progress such as size and record count
|
||||
@ -147,8 +149,8 @@ class TransferEngine<
|
||||
/**
|
||||
* Report a fatal error and throw it
|
||||
*/
|
||||
#panic(error: Error) {
|
||||
this.#reportError(error, 'fatal');
|
||||
panic(error: Error) {
|
||||
this.reportError(error, 'fatal');
|
||||
|
||||
throw error;
|
||||
}
|
||||
@ -156,7 +158,7 @@ class TransferEngine<
|
||||
/**
|
||||
* Report an error diagnostic
|
||||
*/
|
||||
#reportError(error: Error, severity: ErrorDiagnosticSeverity) {
|
||||
reportError(error: Error, severity: ErrorDiagnosticSeverity) {
|
||||
this.diagnostics.report({
|
||||
kind: 'error',
|
||||
details: {
|
||||
@ -172,7 +174,7 @@ class TransferEngine<
|
||||
/**
|
||||
* Report a warning diagnostic
|
||||
*/
|
||||
#reportWarning(message: string, origin?: string) {
|
||||
reportWarning(message: string, origin?: string) {
|
||||
this.diagnostics.report({
|
||||
kind: 'warning',
|
||||
details: { createdAt: new Date(), message, origin },
|
||||
@ -182,7 +184,7 @@ class TransferEngine<
|
||||
/**
|
||||
* Report an info diagnostic
|
||||
*/
|
||||
#reportInfo(message: string, params?: unknown) {
|
||||
reportInfo(message: string, params?: unknown) {
|
||||
this.diagnostics.report({
|
||||
kind: 'info',
|
||||
details: { createdAt: new Date(), message, params },
|
||||
@ -517,7 +519,7 @@ class TransferEngine<
|
||||
|
||||
results.forEach((state) => {
|
||||
if (state.status === 'rejected') {
|
||||
this.#reportWarning(state.reason, `transfer(${stage})`);
|
||||
this.reportWarning(state.reason, `transfer(${stage})`);
|
||||
}
|
||||
});
|
||||
|
||||
@ -544,7 +546,7 @@ class TransferEngine<
|
||||
.on('error', (e) => {
|
||||
updateEndTime();
|
||||
this.#emitStageUpdate('error', stage);
|
||||
this.#reportError(e, 'error');
|
||||
this.reportError(e, 'error');
|
||||
destination.destroy(e);
|
||||
reject(e);
|
||||
})
|
||||
@ -591,7 +593,7 @@ class TransferEngine<
|
||||
|
||||
results.forEach((result) => {
|
||||
if (result.status === 'rejected') {
|
||||
this.#panic(result.reason);
|
||||
this.panic(result.reason);
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -607,7 +609,7 @@ class TransferEngine<
|
||||
|
||||
results.forEach((result) => {
|
||||
if (result.status === 'rejected') {
|
||||
this.#panic(result.reason);
|
||||
this.panic(result.reason);
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -644,77 +646,26 @@ class TransferEngine<
|
||||
this.#assertSchemasMatching(sourceSchemas, destinationSchemas);
|
||||
}
|
||||
} catch (error) {
|
||||
// if this is a schema matching error
|
||||
// if this is a schema matching error, allow handlers to resolve it
|
||||
if (error instanceof TransferEngineValidationError && error.details?.details?.diffs) {
|
||||
const schemaDiffs = error.details?.details?.diffs as Record<string, Diff[]>;
|
||||
|
||||
const context = {
|
||||
ignoreDiffs: {},
|
||||
diffs: schemaDiffs,
|
||||
source: this.sourceProvider,
|
||||
destination: this.destinationProvider,
|
||||
};
|
||||
|
||||
let workflowsStatus;
|
||||
const source = 'Schema Integrity';
|
||||
|
||||
Object.entries(context.diffs).forEach(([uid, diffs]) => {
|
||||
for (const diff of diffs) {
|
||||
const path = `${uid}.${diff.path.join('.')}`;
|
||||
const endPath = diff.path[diff.path.length - 1];
|
||||
|
||||
// Catch known features
|
||||
// TODO: can this be moved outside of the engine?
|
||||
if (
|
||||
uid === 'admin::workflow' ||
|
||||
uid === 'admin::workflow-stage' ||
|
||||
endPath.startsWith('strapi_reviewWorkflows_')
|
||||
) {
|
||||
workflowsStatus = diff.kind;
|
||||
}
|
||||
// handle generic cases
|
||||
else if (diff.kind === 'added') {
|
||||
this.#reportWarning(
|
||||
chalk.red(`${chalk.bold(path)} does not exist on destination`),
|
||||
source
|
||||
);
|
||||
} else if (diff.kind === 'deleted') {
|
||||
this.#reportWarning(
|
||||
chalk.red(`${chalk.bold(path)} does not exist on source`),
|
||||
source
|
||||
);
|
||||
} else if (diff.kind === 'modified') {
|
||||
this.#reportWarning(
|
||||
chalk.red(`${chalk.bold(path)} has a different data type`),
|
||||
source
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// output the known feature warnings
|
||||
if (workflowsStatus === 'added') {
|
||||
this.#reportWarning(
|
||||
chalk.red(`Review workflows feature does not exist on destination`),
|
||||
source
|
||||
);
|
||||
} else if (workflowsStatus === 'deleted') {
|
||||
this.#reportWarning(
|
||||
chalk.red(`Review workflows feature does not exist on source`),
|
||||
source
|
||||
);
|
||||
} else if (workflowsStatus === 'modified') {
|
||||
this.#panic(
|
||||
new TransferEngineInitializationError(
|
||||
'Unresolved differences in schema [review workflows]'
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
await runMiddleware<typeof context>(context, this.#handlers.schemaDiff);
|
||||
|
||||
if (Object.keys(context.diffs).length) {
|
||||
this.#panic(new TransferEngineInitializationError('Unresolved differences in schema'));
|
||||
this.#ignoredDiffs = context.ignoreDiffs;
|
||||
|
||||
// if there are any remaining diffs that weren't ignored
|
||||
if (utils.json.diff(context.diffs, this.#ignoredDiffs).length) {
|
||||
this.panic(new TransferEngineInitializationError('Unresolved differences in schema'));
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@ -756,7 +707,7 @@ class TransferEngine<
|
||||
e instanceof Error &&
|
||||
(!lastDiagnostic || lastDiagnostic.kind !== 'error' || lastDiagnostic.details.error !== e)
|
||||
) {
|
||||
this.#reportError(e, (e as DataTransferError).severity || 'fatal');
|
||||
this.reportError(e, (e as DataTransferError).severity || 'fatal');
|
||||
}
|
||||
|
||||
// Rollback the destination provider if an exception is thrown during the transfer
|
||||
@ -780,9 +731,9 @@ class TransferEngine<
|
||||
} catch (error) {
|
||||
// Error happening during the before transfer step should be considered fatal errors
|
||||
if (error instanceof Error) {
|
||||
this.#panic(error);
|
||||
this.panic(error);
|
||||
} else {
|
||||
this.#panic(
|
||||
this.panic(
|
||||
new Error(`Unknwon error when executing "beforeTransfer" on the ${origin} provider`)
|
||||
);
|
||||
}
|
||||
@ -821,6 +772,7 @@ class TransferEngine<
|
||||
return callback(null, entity);
|
||||
}
|
||||
|
||||
// TODO: this would be safer if we only ignored things in ignoredDiffs, otherwise continue and let an error be thrown
|
||||
const availableContentTypes = Object.entries(schemas)
|
||||
.filter(([, schema]) => schema.modelType === 'contentType')
|
||||
.map(([uid]) => uid);
|
||||
@ -861,6 +813,7 @@ class TransferEngine<
|
||||
return callback(null, link);
|
||||
}
|
||||
|
||||
// TODO: this would be safer if we only ignored things in ignoredDiffs, otherwise continue and let an error be thrown
|
||||
const availableContentTypes = Object.entries(schemas)
|
||||
.filter(([, schema]) => schema.modelType === 'contentType')
|
||||
.map(([uid]) => uid);
|
||||
|
@ -39,7 +39,7 @@
|
||||
"devDependencies": {
|
||||
"@strapi/helper-plugin": "4.10.2",
|
||||
"@testing-library/react": "12.1.4",
|
||||
"msw": "1.0.0",
|
||||
"msw": "1.2.1",
|
||||
"react": "^17.0.2",
|
||||
"react-dom": "^17.0.2",
|
||||
"react-router-dom": "5.3.4",
|
||||
|
@ -77,8 +77,8 @@
|
||||
"rimraf": "3.0.2",
|
||||
"styled-components": "5.3.3",
|
||||
"typescript": "5.0.4",
|
||||
"webpack": "^5.76.0",
|
||||
"webpack-cli": "^5.0.1"
|
||||
"webpack": "^5.82.0",
|
||||
"webpack-cli": "^5.1.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@strapi/design-system": "^1.7.3",
|
||||
|
@ -3,21 +3,13 @@ import PropTypes from 'prop-types';
|
||||
import { Box, Flex, Button, Typography, Table as TableCompo } from '@strapi/design-system';
|
||||
import { useIntl } from 'react-intl';
|
||||
import { Trash } from '@strapi/icons';
|
||||
import styled from 'styled-components';
|
||||
|
||||
import useQueryParams from '../../hooks/useQueryParams';
|
||||
import { useTracking } from '../../features/Tracking';
|
||||
import ConfirmDialog from '../ConfirmDialog';
|
||||
import EmptyBodyTable from '../EmptyBodyTable';
|
||||
import TableHead from './TableHead';
|
||||
|
||||
const BlockActions = styled(Flex)`
|
||||
& > * + * {
|
||||
margin-left: ${({ theme }) => theme.spaces[2]};
|
||||
}
|
||||
|
||||
margin-left: ${({ pullRight }) => (pullRight ? 'auto' : undefined)};
|
||||
`;
|
||||
|
||||
const Table = ({
|
||||
action,
|
||||
children,
|
||||
@ -133,7 +125,7 @@ const Table = ({
|
||||
<Box>
|
||||
<Box paddingBottom={4}>
|
||||
<Flex justifyContent="space-between">
|
||||
<BlockActions>
|
||||
<Flex gap={3}>
|
||||
<Typography variant="epsilon" textColor="neutral600">
|
||||
{formatMessage(
|
||||
{
|
||||
@ -151,7 +143,7 @@ const Table = ({
|
||||
>
|
||||
{formatMessage({ id: 'global.delete', defaultMessage: 'Delete' })}
|
||||
</Button>
|
||||
</BlockActions>
|
||||
</Flex>
|
||||
</Flex>
|
||||
</Box>
|
||||
</Box>
|
||||
|
@ -21,7 +21,7 @@ import { useAppInfo } from './AppInfo';
|
||||
* @typedef {Object} TrackingContextValue
|
||||
* @property {string | boolean} uuid
|
||||
* @property {string | undefined} deviceId
|
||||
* @property {TelemetryProperties} telemetryProperties
|
||||
* @property {TelemetryProperties | undefined} telemetryProperties
|
||||
*/
|
||||
|
||||
/* -------------------------------------------------------------------------------------------------
|
||||
@ -32,7 +32,11 @@ import { useAppInfo } from './AppInfo';
|
||||
* @preserve
|
||||
* @type {React.Context<TrackingContextValue>}
|
||||
*/
|
||||
const TrackingContext = React.createContext();
|
||||
const TrackingContext = React.createContext({
|
||||
uuid: false,
|
||||
deviceId: undefined,
|
||||
telemetryProperties: undefined,
|
||||
});
|
||||
|
||||
/* -------------------------------------------------------------------------------------------------
|
||||
* Provider
|
||||
|
@ -34,6 +34,10 @@ const setup = (props) =>
|
||||
});
|
||||
|
||||
describe('useTracking', () => {
|
||||
beforeAll(() => {
|
||||
window.strapi.telemetryDisabled = false;
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
@ -75,6 +75,8 @@ describe('Export', () => {
|
||||
};
|
||||
}),
|
||||
exitMessageText: jest.fn(),
|
||||
getDiffHandler: jest.fn(),
|
||||
setSignalHandler: jest.fn(),
|
||||
};
|
||||
jest.mock(
|
||||
'../../../utils/data-transfer.js',
|
||||
|
@ -32,6 +32,7 @@ const createTransferEngine = jest.fn(() => {
|
||||
on: jest.fn().mockReturnThis(),
|
||||
onDiagnostic: jest.fn().mockReturnThis(),
|
||||
},
|
||||
onSchemaDiff: jest.fn(),
|
||||
};
|
||||
});
|
||||
|
||||
@ -81,6 +82,8 @@ describe('Import', () => {
|
||||
};
|
||||
}),
|
||||
exitMessageText: jest.fn(),
|
||||
getDiffHandler: jest.fn(),
|
||||
setSignalHandler: jest.fn(),
|
||||
};
|
||||
jest.mock(
|
||||
'../../../utils/data-transfer.js',
|
||||
|
@ -22,9 +22,9 @@ const {
|
||||
abortTransfer,
|
||||
getTransferTelemetryPayload,
|
||||
setSignalHandler,
|
||||
getDiffHandler,
|
||||
} = require('../../utils/data-transfer');
|
||||
const { exitWith } = require('../../utils/helpers');
|
||||
const { confirmMessage } = require('../../utils/commander');
|
||||
|
||||
/**
|
||||
* @typedef {import('@strapi/data-transfer/src/file/providers').ILocalFileSourceProviderOptions} ILocalFileSourceProviderOptions
|
||||
@ -113,30 +113,7 @@ module.exports = async (opts) => {
|
||||
|
||||
const { updateLoader } = loadersFactory();
|
||||
|
||||
engine.onSchemaDiff(async (context, next) => {
|
||||
// if we abort here, we need to actually exit the process because of conflict with inquirer prompt
|
||||
setSignalHandler(async () => {
|
||||
await abortTransfer({ engine, strapi });
|
||||
exitWith(1, exitMessageText('import', true));
|
||||
});
|
||||
|
||||
const confirmed = await confirmMessage(
|
||||
'There are differences in schema between the source and destination, and the data listed above will be lost. Are you sure you want to continue?',
|
||||
{
|
||||
force: opts.force,
|
||||
}
|
||||
);
|
||||
|
||||
// reset handler back to normal
|
||||
setSignalHandler(() => abortTransfer({ engine, strapi }));
|
||||
|
||||
if (confirmed) {
|
||||
context.diffs = [];
|
||||
return next(context);
|
||||
}
|
||||
|
||||
return next(context);
|
||||
});
|
||||
engine.onSchemaDiff(getDiffHandler(engine, { force: opts.force }));
|
||||
|
||||
progress.on(`stage::start`, ({ stage, data }) => {
|
||||
updateLoader(stage, data).start();
|
||||
|
@ -24,6 +24,8 @@ describe('Transfer', () => {
|
||||
};
|
||||
}),
|
||||
exitMessageText: jest.fn(),
|
||||
getDiffHandler: jest.fn(),
|
||||
setSignalHandler: jest.fn(),
|
||||
};
|
||||
jest.mock(
|
||||
'../../../utils/data-transfer.js',
|
||||
@ -66,6 +68,7 @@ describe('Transfer', () => {
|
||||
on: jest.fn().mockReturnThis(),
|
||||
onDiagnostic: jest.fn().mockReturnThis(),
|
||||
},
|
||||
onSchemaDiff: jest.fn(),
|
||||
};
|
||||
},
|
||||
},
|
||||
|
@ -23,9 +23,9 @@ const {
|
||||
abortTransfer,
|
||||
getTransferTelemetryPayload,
|
||||
setSignalHandler,
|
||||
getDiffHandler,
|
||||
} = require('../../utils/data-transfer');
|
||||
const { exitWith } = require('../../utils/helpers');
|
||||
const { confirmMessage } = require('../../utils/commander');
|
||||
|
||||
/**
|
||||
* @typedef TransferCommandOptions Options given to the CLI transfer command
|
||||
@ -148,30 +148,7 @@ module.exports = async (opts) => {
|
||||
|
||||
const { updateLoader } = loadersFactory();
|
||||
|
||||
engine.onSchemaDiff(async (context, next) => {
|
||||
// if we abort here, we need to actually exit the process because of conflict with inquirer prompt
|
||||
setSignalHandler(async () => {
|
||||
await abortTransfer({ engine, strapi });
|
||||
exitWith(1, exitMessageText('transfer', true));
|
||||
});
|
||||
|
||||
const confirmed = await confirmMessage(
|
||||
'There are differences in schema between the source and destination, and the data listed above will be lost. Are you sure you want to continue?',
|
||||
{
|
||||
force: opts.force,
|
||||
}
|
||||
);
|
||||
|
||||
// reset handler back to normal
|
||||
setSignalHandler(() => abortTransfer({ engine, strapi }));
|
||||
|
||||
if (confirmed) {
|
||||
context.diffs = [];
|
||||
return next(context);
|
||||
}
|
||||
|
||||
return next(context);
|
||||
});
|
||||
engine.onSchemaDiff(getDiffHandler(engine, { force: opts.force }));
|
||||
|
||||
progress.on(`stage::start`, ({ stage, data }) => {
|
||||
updateLoader(stage, data).start();
|
||||
|
@ -12,9 +12,11 @@ const {
|
||||
createLogger,
|
||||
} = require('@strapi/logger');
|
||||
const ora = require('ora');
|
||||
const { TransferEngineInitializationError } = require('@strapi/data-transfer/dist/engine/errors');
|
||||
const { merge } = require('lodash/fp');
|
||||
const { readableBytes, exitWith } = require('./helpers');
|
||||
const strapi = require('../../index');
|
||||
const { getParseListWithChoices, parseInteger } = require('./commander');
|
||||
const { getParseListWithChoices, parseInteger, confirmMessage } = require('./commander');
|
||||
|
||||
const exitMessageText = (process, error = false) => {
|
||||
const processCapitalized = process[0].toUpperCase() + process.slice(1);
|
||||
@ -266,6 +268,79 @@ const getTransferTelemetryPayload = (engine) => {
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Get a transfer engine schema diff handler that confirms with the user before bypassing a schema check
|
||||
*/
|
||||
const getDiffHandler = (engine, { force }) => {
|
||||
return async (context, next) => {
|
||||
// if we abort here, we need to actually exit the process because of conflict with inquirer prompt
|
||||
setSignalHandler(async () => {
|
||||
await abortTransfer({ engine, strapi });
|
||||
exitWith(1, exitMessageText('import', true));
|
||||
});
|
||||
|
||||
let workflowsStatus;
|
||||
const source = 'Schema Integrity';
|
||||
|
||||
Object.entries(context.diffs).forEach(([uid, diffs]) => {
|
||||
for (const diff of diffs) {
|
||||
const path = `${uid}.${diff.path.join('.')}`;
|
||||
const endPath = diff.path[diff.path.length - 1];
|
||||
|
||||
// Catch known features
|
||||
if (
|
||||
uid === 'admin::workflow' ||
|
||||
uid === 'admin::workflow-stage' ||
|
||||
endPath.startsWith('strapi_reviewWorkflows_')
|
||||
) {
|
||||
workflowsStatus = diff.kind;
|
||||
}
|
||||
// handle generic cases
|
||||
else if (diff.kind === 'added') {
|
||||
engine.reportWarning(
|
||||
chalk.red(`${chalk.bold(path)} does not exist on destination`),
|
||||
source
|
||||
);
|
||||
} else if (diff.kind === 'deleted') {
|
||||
engine.reportWarning(chalk.red(`${chalk.bold(path)} does not exist on source`), source);
|
||||
} else if (diff.kind === 'modified') {
|
||||
engine.reportWarning(chalk.red(`${chalk.bold(path)} has a different data type`), source);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// output the known feature warnings
|
||||
if (workflowsStatus === 'added') {
|
||||
engine.reportWarning(
|
||||
chalk.red(`Review workflows feature does not exist on destination`),
|
||||
source
|
||||
);
|
||||
} else if (workflowsStatus === 'deleted') {
|
||||
engine.reportWarning(chalk.red(`Review workflows feature does not exist on source`), source);
|
||||
} else if (workflowsStatus === 'modified') {
|
||||
engine.panic(
|
||||
new TransferEngineInitializationError('Unresolved differences in schema [review workflows]')
|
||||
);
|
||||
}
|
||||
|
||||
const confirmed = await confirmMessage(
|
||||
'There are differences in schema between the source and destination, and the data listed above will be lost. Are you sure you want to continue?',
|
||||
{
|
||||
force,
|
||||
}
|
||||
);
|
||||
|
||||
// reset handler back to normal
|
||||
setSignalHandler(() => abortTransfer({ engine, strapi }));
|
||||
|
||||
if (confirmed) {
|
||||
context.ignoreDiffs = merge(context.diffs, context.ignoredDiffs);
|
||||
}
|
||||
|
||||
return next(context);
|
||||
};
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
loadersFactory,
|
||||
buildTransferTable,
|
||||
@ -281,4 +356,5 @@ module.exports = {
|
||||
formatDiagnostic,
|
||||
abortTransfer,
|
||||
setSignalHandler,
|
||||
getDiffHandler,
|
||||
};
|
||||
|
@ -1,24 +1,18 @@
|
||||
import React, { useMemo } from 'react';
|
||||
import styled from 'styled-components';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Flex } from '@strapi/design-system';
|
||||
import { Box, Flex } from '@strapi/design-system';
|
||||
import { PaginationContext } from './PaginationContext';
|
||||
|
||||
const PaginationWrapper = styled.nav``;
|
||||
const PaginationList = styled(Flex)`
|
||||
& > * + * {
|
||||
margin-left: ${({ theme }) => theme.spaces[1]};
|
||||
}
|
||||
`;
|
||||
|
||||
export const Pagination = ({ children, label, activePage, pageCount }) => {
|
||||
const paginationValue = useMemo(() => ({ activePage, pageCount }), [activePage, pageCount]);
|
||||
|
||||
return (
|
||||
<PaginationContext.Provider value={paginationValue}>
|
||||
<PaginationWrapper aria-label={label}>
|
||||
<PaginationList as="ul">{children}</PaginationList>
|
||||
</PaginationWrapper>
|
||||
<Box as="nav" aria-label={label}>
|
||||
<Flex as="ul" gap={1}>
|
||||
{children}
|
||||
</Flex>
|
||||
</Box>
|
||||
</PaginationContext.Provider>
|
||||
);
|
||||
};
|
||||
|
@ -12,11 +12,6 @@ import SearchAsset from '../index';
|
||||
|
||||
const handleChange = jest.fn();
|
||||
|
||||
jest.mock('@strapi/helper-plugin', () => ({
|
||||
...jest.requireActual('@strapi/helper-plugin'),
|
||||
useTracking: jest.fn(() => ({ trackUsage: jest.fn() })),
|
||||
}));
|
||||
|
||||
const makeApp = (queryValue) => (
|
||||
<ThemeProvider theme={lightTheme}>
|
||||
<IntlProvider locale="en">
|
||||
|
@ -40,17 +40,6 @@ import PageSize from './PageSize';
|
||||
import SearchAsset from './SearchAsset';
|
||||
import { isSelectable } from './utils/isSelectable';
|
||||
|
||||
const StartBlockActions = styled(Flex)`
|
||||
& > * + * {
|
||||
margin-left: ${({ theme }) => theme.spaces[2]};
|
||||
}
|
||||
margin-left: ${({ pullRight }) => (pullRight ? 'auto' : undefined)};
|
||||
`;
|
||||
|
||||
const EndBlockActions = styled(StartBlockActions)`
|
||||
flex-shrink: 0;
|
||||
`;
|
||||
|
||||
const TypographyMaxWidth = styled(Typography)`
|
||||
max-width: 100%;
|
||||
`;
|
||||
@ -131,7 +120,7 @@ export const BrowseStep = ({
|
||||
<Box paddingBottom={4}>
|
||||
<Flex justifyContent="space-between" alignItems="flex-start">
|
||||
{(assetCount > 0 || folderCount > 0 || isFiltering) && (
|
||||
<StartBlockActions wrap="wrap">
|
||||
<Flex gap={2} wrap="wrap">
|
||||
{multiple && isGridView && (
|
||||
<Flex
|
||||
paddingLeft={2}
|
||||
@ -157,11 +146,11 @@ export const BrowseStep = ({
|
||||
appliedFilters={queryObject?.filters?.$and}
|
||||
onChangeFilters={onChangeFilters}
|
||||
/>
|
||||
</StartBlockActions>
|
||||
</Flex>
|
||||
)}
|
||||
|
||||
{(assetCount > 0 || folderCount > 0 || isSearching) && (
|
||||
<EndBlockActions pullRight>
|
||||
<Flex marginLeft="auto" shrink={0}>
|
||||
<ActionContainer paddingTop={1} paddingBottom={1}>
|
||||
<IconButton
|
||||
icon={isGridView ? <List /> : <Grid />}
|
||||
@ -180,7 +169,7 @@ export const BrowseStep = ({
|
||||
/>
|
||||
</ActionContainer>
|
||||
<SearchAsset onChangeSearch={onChangeSearch} queryValue={queryObject._q || ''} />
|
||||
</EndBlockActions>
|
||||
</Flex>
|
||||
)}
|
||||
</Flex>
|
||||
</Box>
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -14,7 +14,6 @@ jest.mock('../../../../hooks/useFolder');
|
||||
|
||||
jest.mock('@strapi/helper-plugin', () => ({
|
||||
...jest.requireActual('@strapi/helper-plugin'),
|
||||
useTracking: jest.fn(() => ({ trackUsage: jest.fn() })),
|
||||
usePersistentState: jest.fn().mockReturnValue([0, jest.fn()]),
|
||||
}));
|
||||
|
||||
|
@ -15,7 +15,6 @@ jest.mock('@strapi/helper-plugin', () => ({
|
||||
...jest.requireActual('@strapi/helper-plugin'),
|
||||
useNotification: jest.fn(() => jest.fn()),
|
||||
useQueryParams: jest.fn(),
|
||||
useTracking: jest.fn(() => ({ trackUsage: jest.fn() })),
|
||||
}));
|
||||
|
||||
jest.mock('../../../hooks/useMediaLibraryPermissions');
|
||||
|
@ -13,7 +13,6 @@ import { useMediaLibraryPermissions } from '../../../hooks/useMediaLibraryPermis
|
||||
jest.mock('@strapi/helper-plugin', () => ({
|
||||
...jest.requireActual('@strapi/helper-plugin'),
|
||||
useQueryParams: jest.fn().mockReturnValue([{ query: {} }]),
|
||||
useTracking: jest.fn(() => ({ trackUsage: jest.fn() })),
|
||||
useFetchClient: jest.fn().mockReturnValue({
|
||||
put: jest.fn().mockImplementation({}),
|
||||
}),
|
||||
|
@ -11,7 +11,6 @@ jest.mock('@strapi/helper-plugin', () => ({
|
||||
useNotification: jest.fn(() => ({
|
||||
toggleNotification: jest.fn(),
|
||||
})),
|
||||
useTracking: jest.fn(() => ({ trackUsage: jest.fn() })),
|
||||
}));
|
||||
|
||||
const ASSET_FIXTURES = [
|
||||
|
@ -8,7 +8,6 @@ import { TableList } from '..';
|
||||
|
||||
jest.mock('@strapi/helper-plugin', () => ({
|
||||
...jest.requireActual('@strapi/helper-plugin'),
|
||||
useTracking: jest.fn(() => ({ trackUsage: jest.fn() })),
|
||||
useQueryParams: jest.fn(() => [{ query: {} }]),
|
||||
}));
|
||||
|
||||
|
@ -9,7 +9,6 @@ jest.mock('../../../../utils/getTrad', () => (x) => x);
|
||||
|
||||
jest.mock('@strapi/helper-plugin', () => ({
|
||||
...jest.requireActual('@strapi/helper-plugin'),
|
||||
useTracking: jest.fn(() => ({ trackUsage: jest.fn() })),
|
||||
}));
|
||||
|
||||
const queryClient = new QueryClient({
|
||||
|
@ -25,7 +25,6 @@ const notificationStatusMock = jest.fn();
|
||||
jest.mock('@strapi/helper-plugin', () => ({
|
||||
...jest.requireActual('@strapi/helper-plugin'),
|
||||
useNotification: () => notificationStatusMock,
|
||||
useTracking: jest.fn(() => ({ trackUsage: jest.fn() })),
|
||||
useFetchClient: jest.fn().mockReturnValue({
|
||||
put: jest.fn().mockResolvedValue({ data: { data: {} } }),
|
||||
get: jest.fn(),
|
||||
|
@ -8,11 +8,6 @@ import { ThemeProvider, lightTheme } from '@strapi/design-system';
|
||||
|
||||
import useModalQueryParams from '../useModalQueryParams';
|
||||
|
||||
jest.mock('@strapi/helper-plugin', () => ({
|
||||
...jest.requireActual('@strapi/helper-plugin'),
|
||||
useTracking: jest.fn(() => ({ trackUsage: jest.fn() })),
|
||||
}));
|
||||
|
||||
const refetchQueriesMock = jest.fn();
|
||||
|
||||
jest.mock('react-query', () => ({
|
||||
|
@ -58,7 +58,7 @@
|
||||
"@testing-library/react": "12.1.4",
|
||||
"@testing-library/react-hooks": "8.0.1",
|
||||
"@testing-library/user-event": "14.4.3",
|
||||
"msw": "1.0.1",
|
||||
"msw": "1.2.1",
|
||||
"react": "^17.0.2",
|
||||
"react-dom": "^17.0.2",
|
||||
"react-router-dom": "5.3.4",
|
||||
|
@ -65,7 +65,7 @@
|
||||
"@apidevtools/swagger-parser": "^10.1.0",
|
||||
"@testing-library/react": "12.1.4",
|
||||
"history": "^4.9.0",
|
||||
"msw": "1.0.1",
|
||||
"msw": "1.2.1",
|
||||
"react": "^17.0.2",
|
||||
"react-dom": "^17.0.2",
|
||||
"react-router-dom": "5.3.4",
|
||||
|
62
packages/plugins/i18n/admin/src/translations/ru.json
Normal file
62
packages/plugins/i18n/admin/src/translations/ru.json
Normal file
@ -0,0 +1,62 @@
|
||||
{
|
||||
"CMEditViewCopyLocale.copy-failure": "Не удалось скопировать перевод",
|
||||
"CMEditViewCopyLocale.copy-success": "Перевод скопирована",
|
||||
"CMEditViewCopyLocale.copy-text": "Заполните данные из другого языка",
|
||||
"CMEditViewCopyLocale.submit-text": "Да, заполнить",
|
||||
"CMListView.popover.display-locales.label": "Отображать переведенные языки",
|
||||
"CheckboxConfirmation.Modal.body": "Вы хотите выключить это?",
|
||||
"CheckboxConfirmation.Modal.button-confirm": "Да, выключить",
|
||||
"CheckboxConfirmation.Modal.content": "Отключение перевода приведет к удалению всего вашего контента, кроме того, который связан с вашим языком по умолчанию (если вообще он существует).",
|
||||
"Field.localized": "Это значение уникально для выбранного перевода",
|
||||
"Field.not-localized": "Это значение является общим для всех переводов",
|
||||
"Settings.list.actions.add": "Добавить новый перевод",
|
||||
"Settings.list.actions.delete": "Удалить перевод",
|
||||
"Settings.list.actions.deleteAdditionalInfos": "Это приведет к удалению активных версий перевода <em>(из плагина интернационализации)</em>",
|
||||
"Settings.list.actions.edit": "Редактировать перевод",
|
||||
"Settings.list.description": "Настройте параметры для плагина интернационализации",
|
||||
"Settings.list.empty.description": "Это необычное поведение, означающее, что всё-таки вы изменили базу данных вручную. Убедитесь, что базе данных сохранён хотя бы один перевод, чтобы иметь возможность правильно использовать Strapi.",
|
||||
"Settings.list.empty.title": "Переводов нет.",
|
||||
"Settings.locales.default": "По умолчанию",
|
||||
"Settings.locales.list.sort.default": "Сортировать переводы по умолчанию",
|
||||
"Settings.locales.list.sort.displayName": "Сортировать по отображаемому имени",
|
||||
"Settings.locales.list.sort.id": "Сортировать по ID",
|
||||
"Settings.locales.modal.advanced": "Расширенные настройки",
|
||||
"Settings.locales.modal.advanced.setAsDefault": "Установить в качестве перевода по умолчанию",
|
||||
"Settings.locales.modal.advanced.setAsDefault.hint": "Необходим один перевод по умолчанию, вы можете изменить его выбрав другой перевод",
|
||||
"Settings.locales.modal.advanced.settings": "Настройки",
|
||||
"Settings.locales.modal.base": "Основные настройки",
|
||||
"Settings.locales.modal.create.alreadyExist": "Этот перевод уже существует",
|
||||
"Settings.locales.modal.create.defaultLocales.loading": "Загрузка доступных переводов...",
|
||||
"Settings.locales.modal.create.success": "Перевод успешно добавлен",
|
||||
"Settings.locales.modal.create.tab.label": "Переключение между основными настройками этого плагина и расширенными настройками",
|
||||
"Settings.locales.modal.delete.confirm": "Да, удалить",
|
||||
"Settings.locales.modal.delete.message": "Удаление этого перевода приведет к удалению всего связанного с ним содержимого. Если вы хотите сохранить какой-то контент, обязательно сначала перенесите его в другой язык (перераспределите в другой перевод).",
|
||||
"Settings.locales.modal.delete.secondMessage": "Вы хотите удалить этот перевод?",
|
||||
"Settings.locales.modal.delete.success": "Перевод успешно удалён",
|
||||
"Settings.locales.modal.edit.confirmation": "Готово!",
|
||||
"Settings.locales.modal.edit.locales.label": "Переводы",
|
||||
"Settings.locales.modal.edit.success": "Перевод успешно отредактирован",
|
||||
"Settings.locales.modal.edit.tab.label": "Переключение между основными настройками этого плагина и расширенными настройками",
|
||||
"Settings.locales.modal.locales.displayName": "Отображаемое имя перевода",
|
||||
"Settings.locales.modal.locales.displayName.description": "Перевод будет отображаться под этим именем в панели администратора",
|
||||
"Settings.locales.modal.locales.displayName.error": "Отображаемое имя перевода может содержать не более 50 символов.",
|
||||
"Settings.locales.modal.locales.label": "Переводы",
|
||||
"Settings.locales.modal.locales.loaded": "Переводы были успешно загружены.",
|
||||
"Settings.locales.modal.title": "Настройки",
|
||||
"Settings.locales.row.default-locale": "Перевод по умолчанию",
|
||||
"Settings.locales.row.displayName": "Отображаемое имя",
|
||||
"Settings.locales.row.id": "ID",
|
||||
"Settings.permissions.loading": "Разрешения на загрузку",
|
||||
"Settings.permissions.read.denied.description": "Чтобы иметь возможность прочитать это, обязательно свяжитесь с администратором вашей системы.",
|
||||
"Settings.permissions.read.denied.title": "У вас нет прав доступа к этому контенту.",
|
||||
"actions.select-locale": "Выбрать перевод",
|
||||
"components.Select.locales.not-available": "Нет доступного контента",
|
||||
"plugin.description.long": "Этот плагин позволяет создавать, читать и обновлять контент (словом, производить всевозможные действия с контентом) на разных языках, как из панели администратора, так и через API.",
|
||||
"plugin.description.short": "Этот плагин позволяет создавать, читать и обновлять контент на разных языках, как из панели администратора, так и через API.",
|
||||
"plugin.name": "Интернационализация",
|
||||
"plugin.schema.i18n.ensure-unique-localization": "Уникальные поля должны быть переведены",
|
||||
"plugin.schema.i18n.localized.description-content-type": "Позволяет перевести запись на разные языки",
|
||||
"plugin.schema.i18n.localized.description-field": "Поле может иметь разные значения на каждом языке",
|
||||
"plugin.schema.i18n.localized.label-content-type": "Интернационализация",
|
||||
"plugin.schema.i18n.localized.label-field": "Включить перевод для этого поля"
|
||||
}
|
@ -53,7 +53,7 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@testing-library/react": "12.1.4",
|
||||
"msw": "1.0.1",
|
||||
"msw": "1.2.1",
|
||||
"react": "^17.0.2",
|
||||
"react-dom": "^17.0.2",
|
||||
"react-router-dom": "5.3.4",
|
||||
|
@ -29,7 +29,7 @@
|
||||
"@sentry/node": "6.19.7",
|
||||
"@strapi/design-system": "1.7.3",
|
||||
"@strapi/helper-plugin": "4.10.2",
|
||||
"@strapi/icons": "1.6.5"
|
||||
"@strapi/icons": "1.7.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"react": "^17.0.2",
|
||||
|
@ -14,9 +14,6 @@ jest.mock('@strapi/helper-plugin', () => ({
|
||||
useOverlayBlocker: jest.fn(() => ({ lockApp: jest.fn, unlockApp: jest.fn() })),
|
||||
useRBAC: jest.fn(),
|
||||
CheckPagePermissions: ({ children }) => children,
|
||||
useTracking: jest.fn(() => ({
|
||||
trackUsage: jest.fn(),
|
||||
})),
|
||||
}));
|
||||
|
||||
const client = new QueryClient({
|
||||
|
@ -9,7 +9,6 @@ import { ProvidersPage } from '../index';
|
||||
|
||||
jest.mock('@strapi/helper-plugin', () => ({
|
||||
...jest.requireActual('@strapi/helper-plugin'),
|
||||
useTracking: jest.fn(() => ({ trackUsage: jest.fn() })),
|
||||
useNotification: jest.fn(),
|
||||
useOverlayBlocker: jest.fn(() => ({ lockApp: jest.fn(), unlockApp: jest.fn() })),
|
||||
useRBAC: jest.fn(),
|
||||
|
@ -19,7 +19,6 @@ jest.mock('@strapi/helper-plugin', () => {
|
||||
...jest.requireActual('@strapi/helper-plugin'),
|
||||
useNotification: mockUseNotification,
|
||||
useOverlayBlocker: jest.fn(() => ({ lockApp: jest.fn(), unlockApp: jest.fn() })),
|
||||
useTracking: jest.fn(() => ({ trackUsage: jest.fn() })),
|
||||
};
|
||||
});
|
||||
|
||||
|
@ -56,7 +56,7 @@
|
||||
"@testing-library/react-hooks": "8.0.1",
|
||||
"@testing-library/user-event": "14.4.3",
|
||||
"history": "^4.9.0",
|
||||
"msw": "1.0.1",
|
||||
"msw": "1.2.1",
|
||||
"react": "^17.0.2",
|
||||
"react-dom": "^17.0.2",
|
||||
"react-router-dom": "5.3.4",
|
||||
|
@ -23,7 +23,7 @@
|
||||
"dependencies": {
|
||||
"@babel/template": "^7.20.7",
|
||||
"reselect": "4.1.7",
|
||||
"resolve": "1.20.0"
|
||||
"resolve": "1.22.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/cli": "^7.20.7",
|
||||
|
Loading…
x
Reference in New Issue
Block a user