mirror of
https://github.com/strapi/strapi.git
synced 2025-09-25 08:19:07 +00:00
Merge branch 'main' into features/data-transfer
This commit is contained in:
commit
880ba7af86
4
.github/actions/check-pr-status/package.json
vendored
4
.github/actions/check-pr-status/package.json
vendored
@ -9,8 +9,8 @@
|
||||
"watch": "NODE_ENV=production ncc build index.js -w -o dist --minify"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@actions/core": "1.9.1",
|
||||
"@actions/github": "5.0.0",
|
||||
"@actions/core": "1.10.0",
|
||||
"@actions/github": "5.1.1",
|
||||
"@vercel/ncc": "0.36.1"
|
||||
}
|
||||
}
|
||||
|
@ -24,11 +24,11 @@
|
||||
"react": "^17.0.2",
|
||||
"react-dom": "^17.0.2",
|
||||
"react-is": "^17.0.2",
|
||||
"redux": "^4.0.1",
|
||||
"redux": "^4.2.1",
|
||||
"styled-components": "5.3.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"redux": "^4.0.1"
|
||||
"redux": "^4.2.1"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
|
Binary file not shown.
After Width: | Height: | Size: 2.8 KiB |
@ -0,0 +1,46 @@
|
||||
import { Book, PaperPlane } from '@strapi/icons';
|
||||
|
||||
export const VIDEO_LINKS = [
|
||||
{
|
||||
label: {
|
||||
id: 'app.components.Onboarding.link.build-content',
|
||||
defaultMessage: 'Build a content architecture',
|
||||
},
|
||||
href: 'https://www.youtube.com/watch?v=G9GjN0RxhkE',
|
||||
duration: '5:48',
|
||||
},
|
||||
{
|
||||
label: {
|
||||
id: 'app.components.Onboarding.link.manage-content',
|
||||
defaultMessage: 'Add & manage content',
|
||||
},
|
||||
href: 'https://www.youtube.com/watch?v=DEZw4KbybAI',
|
||||
duration: '3:18',
|
||||
},
|
||||
{
|
||||
label: { id: 'app.components.Onboarding.link.manage-media', defaultMessage: 'Manage media' },
|
||||
href: 'https://www.youtube.com/watch?v=-61MuiMQb38',
|
||||
duration: '3:41',
|
||||
},
|
||||
];
|
||||
|
||||
export const WATCH_MORE = {
|
||||
href: 'https://www.youtube.com/playlist?list=PL7Q0DQYATmvidz6lEmwE5nIcOAYagxWqq',
|
||||
label: {
|
||||
id: 'app.components.Onboarding.link.more-videos',
|
||||
defaultMessage: 'Watch more videos',
|
||||
},
|
||||
};
|
||||
|
||||
export const DOCUMENTATION_LINKS = [
|
||||
{
|
||||
label: { id: 'global.documentation', defaultMessage: 'documentation' },
|
||||
href: 'https://docs.strapi.io',
|
||||
icon: Book,
|
||||
},
|
||||
{
|
||||
label: { id: 'app.static.links.cheatsheet', defaultMessage: 'cheatsheet' },
|
||||
href: 'https://strapi-showcase.s3-us-west-2.amazonaws.com/CheatSheet.pdf',
|
||||
icon: PaperPlane,
|
||||
},
|
||||
];
|
@ -1,92 +1,91 @@
|
||||
import React, { useState } from 'react';
|
||||
import React, { useRef, useState } from 'react';
|
||||
import styled from 'styled-components';
|
||||
import { useIntl } from 'react-intl';
|
||||
import { Box, Flex, FocusTrap, Typography, Icon, Stack } from '@strapi/design-system';
|
||||
import { Book, Cross, Information, Question } from '@strapi/icons';
|
||||
import { pxToRem } from '@strapi/helper-plugin';
|
||||
import {
|
||||
Box,
|
||||
Button,
|
||||
Divider,
|
||||
Flex,
|
||||
FocusTrap,
|
||||
Icon,
|
||||
Portal,
|
||||
PopoverPrimitives,
|
||||
Stack,
|
||||
Typography,
|
||||
VisuallyHidden,
|
||||
} from '@strapi/design-system';
|
||||
import { Cross, Play, Question } from '@strapi/icons';
|
||||
|
||||
import { useConfigurations } from '../../../hooks';
|
||||
import onboardingPreview from '../../../assets/images/onboarding-preview.png';
|
||||
import { VIDEO_LINKS, DOCUMENTATION_LINKS, WATCH_MORE } from './constants';
|
||||
|
||||
const OnboardingWrapper = styled(Box)`
|
||||
position: fixed;
|
||||
bottom: ${({ theme }) => theme.spaces[2]};
|
||||
right: ${({ theme }) => theme.spaces[2]};
|
||||
`;
|
||||
|
||||
const Button = styled(Box)`
|
||||
width: ${({ theme }) => theme.spaces[8]};
|
||||
height: ${({ theme }) => theme.spaces[8]};
|
||||
background: ${({ theme }) => theme.colors.primary600};
|
||||
box-shadow: ${({ theme }) => theme.shadows.tableShadow};
|
||||
// TODO: use new Button props derived from Box props with next DS release
|
||||
const HelperButton = styled(Button)`
|
||||
border-radius: 50%;
|
||||
|
||||
svg path {
|
||||
fill: ${({ theme }) => theme.colors.buttonNeutral0};
|
||||
}
|
||||
padding: ${({ theme }) => theme.spaces[3]};
|
||||
/* Resetting 2rem height defined by Button component */
|
||||
height: 100%;
|
||||
`;
|
||||
|
||||
const LinksWrapper = styled(Box)`
|
||||
bottom: ${({ theme }) => `${theme.spaces[9]}`};
|
||||
min-width: ${200 / 16}rem;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
const IconWrapper = styled(Flex)`
|
||||
transform: translate(-50%, -50%);
|
||||
`;
|
||||
|
||||
const StyledLink = styled(Flex)`
|
||||
const VideoLinkWrapper = styled(Flex)`
|
||||
text-decoration: none;
|
||||
|
||||
svg path {
|
||||
fill: ${({ theme }) => theme.colors.neutral600};
|
||||
:focus-visible {
|
||||
outline-offset: ${({ theme }) => `-${theme.spaces[1]}`};
|
||||
}
|
||||
|
||||
&:focus,
|
||||
&:hover {
|
||||
background: ${({ theme }) => theme.colors.neutral100};
|
||||
:hover {
|
||||
background: ${({ theme }) => theme.colors.primary100};
|
||||
|
||||
svg path {
|
||||
fill: ${({ theme }) => theme.colors.neutral700};
|
||||
/* Hover style for the number displayed */
|
||||
${Typography}:first-child {
|
||||
color: ${({ theme }) => theme.colors.primary500};
|
||||
}
|
||||
|
||||
${[Typography]} {
|
||||
color: ${({ theme }) => theme.colors.neutral700};
|
||||
/* Hover style for the label */
|
||||
${Typography}:nth-child(1) {
|
||||
color: ${({ theme }) => theme.colors.primary600};
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const Preview = styled.img`
|
||||
width: ${({ theme }) => theme.spaces[10]};
|
||||
height: ${({ theme }) => theme.spaces[8]};
|
||||
/* Same overlay used in ModalLayout */
|
||||
background: ${({ theme }) => `${theme.colors.neutral800}1F`};
|
||||
border-radius: ${({ theme }) => theme.borderRadius};
|
||||
`;
|
||||
|
||||
const TypographyLineHeight = styled(Typography)`
|
||||
/* line height of label and watch more to 1 so they can be better aligned visually */
|
||||
line-height: 1;
|
||||
`;
|
||||
|
||||
const TextLink = styled(TypographyLineHeight)`
|
||||
text-decoration: none;
|
||||
|
||||
:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
`;
|
||||
|
||||
const Onboarding = () => {
|
||||
const triggerRef = useRef();
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const { formatMessage } = useIntl();
|
||||
const { showTutorials } = useConfigurations();
|
||||
|
||||
if (!showTutorials) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const STATIC_LINKS = [
|
||||
{
|
||||
Icon: <Book />,
|
||||
label: formatMessage({
|
||||
id: 'global.documentation',
|
||||
defaultMessage: 'Documentation',
|
||||
}),
|
||||
destination: 'https://docs.strapi.io',
|
||||
},
|
||||
{
|
||||
Icon: <Information />,
|
||||
label: formatMessage({ id: 'app.static.links.cheatsheet', defaultMessage: 'CheatSheet' }),
|
||||
destination: 'https://strapi-showcase.s3-us-west-2.amazonaws.com/CheatSheet.pdf',
|
||||
},
|
||||
];
|
||||
|
||||
const handleClick = () => {
|
||||
const handlePopoverVisibility = () => {
|
||||
setIsOpen((prev) => !prev);
|
||||
};
|
||||
|
||||
return (
|
||||
<OnboardingWrapper as="aside">
|
||||
<Button
|
||||
as="button"
|
||||
id="onboarding"
|
||||
<Box as="aside" position="fixed" bottom={2} right={2}>
|
||||
<HelperButton
|
||||
aria-label={formatMessage(
|
||||
isOpen
|
||||
? {
|
||||
@ -98,37 +97,110 @@ const Onboarding = () => {
|
||||
defaultMessage: 'Open help menu',
|
||||
}
|
||||
)}
|
||||
onClick={handleClick}
|
||||
onClick={handlePopoverVisibility}
|
||||
ref={triggerRef}
|
||||
>
|
||||
<Icon as={isOpen ? Cross : Question} height={pxToRem(16)} width={pxToRem(16)} />
|
||||
</Button>
|
||||
<Icon as={isOpen ? Cross : Question} color="buttonNeutral0" />
|
||||
</HelperButton>
|
||||
|
||||
{/* FIX ME - replace with popover when overflow popover is fixed
|
||||
+ when v4 mockups for onboarding component are ready */}
|
||||
{isOpen && (
|
||||
<FocusTrap onEscape={handleClick}>
|
||||
<LinksWrapper background="neutral0" hasRadius shadow="tableShadow" padding={2}>
|
||||
{STATIC_LINKS.map((link) => (
|
||||
<StyledLink
|
||||
as="a"
|
||||
key={link.label}
|
||||
rel="nofollow noreferrer noopener"
|
||||
target="_blank"
|
||||
href={link.destination}
|
||||
padding={2}
|
||||
hasRadius
|
||||
alignItems="center"
|
||||
<Portal>
|
||||
<PopoverPrimitives.Content
|
||||
padding={0}
|
||||
source={triggerRef}
|
||||
placement="top-end"
|
||||
spacing={12}
|
||||
>
|
||||
<Stack horizontal spacing={2}>
|
||||
{link.Icon}
|
||||
<Typography color="neutral600">{link.label}</Typography>
|
||||
</Stack>
|
||||
</StyledLink>
|
||||
<FocusTrap onEscape={handlePopoverVisibility}>
|
||||
<Flex
|
||||
justifyContent="space-between"
|
||||
paddingBottom={5}
|
||||
paddingRight={6}
|
||||
paddingLeft={6}
|
||||
paddingTop={6}
|
||||
>
|
||||
<TypographyLineHeight fontWeight="bold">
|
||||
{formatMessage({
|
||||
id: 'app.components.Onboarding.title',
|
||||
defaultMessage: 'Get started videos',
|
||||
})}
|
||||
</TypographyLineHeight>
|
||||
<TextLink
|
||||
as="a"
|
||||
href={WATCH_MORE.href}
|
||||
target="_blank"
|
||||
rel="noreferrer noopener"
|
||||
variant="pi"
|
||||
textColor="primary600"
|
||||
>
|
||||
{formatMessage(WATCH_MORE.label)}
|
||||
</TextLink>
|
||||
</Flex>
|
||||
<Divider />
|
||||
{VIDEO_LINKS.map(({ href, duration, label }, index) => (
|
||||
<VideoLinkWrapper
|
||||
as="a"
|
||||
href={href}
|
||||
target="_blank"
|
||||
rel="noreferrer noopener"
|
||||
key={href}
|
||||
hasRadius
|
||||
paddingTop={4}
|
||||
paddingBottom={4}
|
||||
paddingLeft={6}
|
||||
paddingRight={11}
|
||||
>
|
||||
<Box paddingRight={5}>
|
||||
<Typography textColor="neutral200" variant="alpha">
|
||||
{index + 1}
|
||||
</Typography>
|
||||
</Box>
|
||||
<Box position="relative">
|
||||
<Preview src={onboardingPreview} alt="" />
|
||||
<IconWrapper
|
||||
position="absolute"
|
||||
top="50%"
|
||||
left="50%"
|
||||
background="primary600"
|
||||
borderRadius="50%"
|
||||
justifyContent="center"
|
||||
width={6}
|
||||
height={6}
|
||||
>
|
||||
<Icon as={Play} color="buttonNeutral0" width={3} height={3} />
|
||||
</IconWrapper>
|
||||
</Box>
|
||||
<Flex direction="column" alignItems="start" paddingLeft={4}>
|
||||
<Typography fontWeight="bold">{formatMessage(label)}</Typography>
|
||||
<VisuallyHidden>:</VisuallyHidden>
|
||||
<Typography textColor="neutral600" variant="pi">
|
||||
{duration}
|
||||
</Typography>
|
||||
</Flex>
|
||||
</VideoLinkWrapper>
|
||||
))}
|
||||
</LinksWrapper>
|
||||
<Stack spacing={2} paddingLeft={5} paddingTop={2} paddingBottom={5}>
|
||||
{DOCUMENTATION_LINKS.map(({ label, href, icon }) => (
|
||||
<Stack horizontal spacing={3} key={href}>
|
||||
<Icon as={icon} color="primary600" />
|
||||
<TextLink
|
||||
as="a"
|
||||
href={href}
|
||||
target="_blank"
|
||||
rel="noreferrer noopener"
|
||||
variant="sigma"
|
||||
textColor="primary700"
|
||||
>
|
||||
{formatMessage(label)}
|
||||
</TextLink>
|
||||
</Stack>
|
||||
))}
|
||||
</Stack>
|
||||
</FocusTrap>
|
||||
</PopoverPrimitives.Content>
|
||||
</Portal>
|
||||
)}
|
||||
</OnboardingWrapper>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -1,55 +0,0 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Onboarding renders and matches the snapshot 1`] = `
|
||||
.c2 {
|
||||
color: #666687;
|
||||
width: 1rem;
|
||||
height: 1rem;
|
||||
}
|
||||
|
||||
.c3 path {
|
||||
fill: #666687;
|
||||
}
|
||||
|
||||
.c0 {
|
||||
position: fixed;
|
||||
bottom: 8px;
|
||||
right: 8px;
|
||||
}
|
||||
|
||||
.c1 {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
background: #4945ff;
|
||||
box-shadow: 0px 1px 4px rgba(33,33,52,0.1);
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.c1 svg path {
|
||||
fill: #ffffff;
|
||||
}
|
||||
|
||||
<aside
|
||||
class="c0"
|
||||
>
|
||||
<button
|
||||
aria-label="Open help menu"
|
||||
class="c1"
|
||||
id="onboarding"
|
||||
>
|
||||
<svg
|
||||
class="c2 c3"
|
||||
fill="none"
|
||||
height="1em"
|
||||
viewBox="0 0 15 14"
|
||||
width="1em"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M5.08 4.1c0-1.19 1.18-2.17 2.42-2.17s2.43.98 2.43 2.17c0 1.1-.56 1.61-1.31 2.28l-.03.03c-.75.65-1.66 1.47-1.66 3.09a.57.57 0 101.15 0c0-1.08.55-1.6 1.3-2.26l.02-.02c.75-.66 1.67-1.48 1.67-3.12C11.07 2.13 9.22.78 7.5.78 5.78.78 3.93 2.13 3.93 4.1a.57.57 0 101.15 0zm2.42 9.26a.88.88 0 100-1.75.88.88 0 000 1.75z"
|
||||
fill="#212134"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
</aside>
|
||||
`;
|
@ -1,15 +1,9 @@
|
||||
import React from 'react';
|
||||
import { render, screen, fireEvent } from '@testing-library/react';
|
||||
import { render, fireEvent } from '@testing-library/react';
|
||||
import { IntlProvider } from 'react-intl';
|
||||
import { ThemeProvider, lightTheme } from '@strapi/design-system';
|
||||
import Onboarding from '../index';
|
||||
|
||||
jest.mock('../../../../hooks', () => ({
|
||||
useConfigurations: jest.fn(() => {
|
||||
return { showTutorials: true };
|
||||
}),
|
||||
}));
|
||||
|
||||
const App = (
|
||||
<ThemeProvider theme={lightTheme}>
|
||||
<IntlProvider locale="en" messages={{}} defaultLocale="en" textComponent="span">
|
||||
@ -19,18 +13,18 @@ const App = (
|
||||
);
|
||||
|
||||
describe('Onboarding', () => {
|
||||
it('renders and matches the snapshot', async () => {
|
||||
const {
|
||||
container: { firstChild },
|
||||
} = render(App);
|
||||
test.each([
|
||||
'watch more videos',
|
||||
'build a content architecture',
|
||||
'add & manage content',
|
||||
'manage media',
|
||||
'documentation',
|
||||
'cheatsheet',
|
||||
])('should display %s link', (link) => {
|
||||
const { getByRole } = render(App);
|
||||
|
||||
expect(firstChild).toMatchSnapshot();
|
||||
});
|
||||
fireEvent.click(getByRole('button', { name: /open help menu/i }));
|
||||
|
||||
it('should open links when button is clicked', () => {
|
||||
render(App);
|
||||
|
||||
fireEvent.click(document.querySelector('#onboarding'));
|
||||
expect(screen.getByText('Documentation')).toBeInTheDocument();
|
||||
expect(getByRole('link', { name: new RegExp(link, 'i') })).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
@ -10,10 +10,11 @@ import { useTracking, LoadingIndicatorPage, useStrapiApp } from '@strapi/helper-
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import { DndProvider } from 'react-dnd';
|
||||
import { HTML5Backend } from 'react-dnd-html5-backend';
|
||||
|
||||
import GuidedTourModal from '../../components/GuidedTour/Modal';
|
||||
import LeftMenu from '../../components/LeftMenu';
|
||||
import AppLayout from '../../layouts/AppLayout';
|
||||
import { useMenu } from '../../hooks';
|
||||
import { useMenu, useConfigurations } from '../../hooks';
|
||||
import { createRoute } from '../../utils';
|
||||
import { SET_APP_RUNTIME_STATUS } from '../App/constants';
|
||||
import Onboarding from './Onboarding';
|
||||
@ -65,6 +66,7 @@ const Admin = () => {
|
||||
useTrackUsage();
|
||||
const { isLoading, generalSectionLinks, pluginsSectionLinks } = useMenu();
|
||||
const { menu } = useStrapiApp();
|
||||
const { showTutorials } = useConfigurations();
|
||||
|
||||
const routes = useMemo(() => {
|
||||
return menu
|
||||
@ -106,7 +108,8 @@ const Admin = () => {
|
||||
</Switch>
|
||||
</Suspense>
|
||||
<GuidedTourModal />
|
||||
<Onboarding />
|
||||
|
||||
{showTutorials && <Onboarding />}
|
||||
</AppLayout>
|
||||
</DndProvider>
|
||||
);
|
||||
|
@ -444,7 +444,11 @@
|
||||
"app.components.Official": "Official",
|
||||
"app.components.Onboarding.help.button": "Help button",
|
||||
"app.components.Onboarding.label.completed": "% completed",
|
||||
"app.components.Onboarding.title": "Get Started Videos",
|
||||
"app.components.Onboarding.title": "Get started videos",
|
||||
"app.components.Onboarding.link.more-videos": "Watch more videos",
|
||||
"app.components.Onboarding.link.build-content": "Build a content architecture",
|
||||
"app.components.Onboarding.link.manage-content": "Add & manage content",
|
||||
"app.components.Onboarding.link.manage-media": "Manage media",
|
||||
"app.components.PluginCard.Button.label.download": "Download",
|
||||
"app.components.PluginCard.Button.label.install": "Already installed",
|
||||
"app.components.PluginCard.PopUpWarning.install.impossible.autoReload.needed": "The autoReload feature needs to be enabled. Please start your app with `yarn develop`.",
|
||||
|
@ -100,7 +100,6 @@
|
||||
"markdown-it-sup": "1.0.0",
|
||||
"match-sorter": "^4.0.2",
|
||||
"mini-css-extract-plugin": "2.7.2",
|
||||
"msw": "0.49.1",
|
||||
"node-polyfill-webpack-plugin": "2.0.1",
|
||||
"node-schedule": "2.1.0",
|
||||
"p-map": "4.0.0",
|
||||
@ -118,12 +117,12 @@
|
||||
"react-intl": "6.2.7",
|
||||
"react-is": "^17.0.2",
|
||||
"react-query": "3.24.3",
|
||||
"react-redux": "7.2.8",
|
||||
"react-redux": "8.0.5",
|
||||
"react-refresh": "0.14.0",
|
||||
"react-router": "5.2.0",
|
||||
"react-router-dom": "5.3.4",
|
||||
"react-window": "1.8.7",
|
||||
"redux": "^4.0.1",
|
||||
"redux": "^4.2.1",
|
||||
"reselect": "^4.0.0",
|
||||
"rimraf": "3.0.2",
|
||||
"sanitize-html": "2.7.3",
|
||||
@ -145,6 +144,7 @@
|
||||
"@testing-library/user-event": "14.4.3",
|
||||
"duplicate-dependencies-webpack-plugin": "^1.0.2",
|
||||
"glob": "8.0.3",
|
||||
"msw": "1.0.0",
|
||||
"react-test-renderer": "^17.0.2",
|
||||
"speed-measure-webpack-plugin": "1.5.0",
|
||||
"webpack-bundle-analyzer": "^4.7.0"
|
||||
|
@ -1,428 +0,0 @@
|
||||
let jwt;
|
||||
|
||||
const frontEndUrl = Cypress.config('baseUrl');
|
||||
const frontLoadingDelay = Cypress.config('frontLoadingDelay');
|
||||
const backendUrl = Cypress.config('backendUrl');
|
||||
|
||||
const getCreateRedirectUrl = (model, sort = '_id') => {
|
||||
return `${frontEndUrl}/admin/plugins/content-manager/${model}/create?redirectUrl=/plugins/content-manager/${model}?_limit=10&_page=1&_sort=${sort}&source=content-manager`;
|
||||
};
|
||||
const getRequest = (model, sort = '_id') => {
|
||||
return `${backendUrl}/content-manager/explorer/${model}?_limit=10&_start=0&_sort=${sort}:ASC&source=content-manager`;
|
||||
};
|
||||
|
||||
describe('Testing Content Manager createPages', function () {
|
||||
before(() => {
|
||||
cy.login()
|
||||
.then((data) => {
|
||||
jwt = data.jwt;
|
||||
|
||||
return cy.createCTMApis(data.jwt).then(() => jwt);
|
||||
})
|
||||
.wait(1000);
|
||||
|
||||
Cypress.Commands.add('ctmTagLink', () => {
|
||||
return cy.get('a[href="/admin/plugins/content-manager/tag?source=content-manager"]');
|
||||
});
|
||||
Cypress.Commands.add('ctmProductLink', () => {
|
||||
return cy.get('a[href="/admin/plugins/content-manager/product?source=content-manager"]');
|
||||
});
|
||||
Cypress.Commands.add('ctmCategoryLink', () => {
|
||||
return cy.get('a[href="/admin/plugins/content-manager/category?source=content-manager"]');
|
||||
});
|
||||
Cypress.Commands.add('ctmAddButton', () => {
|
||||
return cy.get('button#addEntry');
|
||||
});
|
||||
Cypress.Commands.add('inputError', (name) => {
|
||||
return cy.get(`#errorOf${name} > span`);
|
||||
});
|
||||
Cypress.Commands.add('getListTagsOrderedByName', () => {
|
||||
return cy.ctmTagLink().click().get('tr > th:nth-child(3) > span').click();
|
||||
});
|
||||
Cypress.Commands.add('fillProductForm', (product) => {
|
||||
Object.keys(product).forEach((key) => {
|
||||
if (key === 'description') {
|
||||
cy.get(`textarea[name="${key}"]`).type(product[key]);
|
||||
} else {
|
||||
cy.get(`input[name="${key}"]`).type(product[key]);
|
||||
}
|
||||
});
|
||||
});
|
||||
Cypress.Commands.add('getProduct', (index) => {
|
||||
return cy
|
||||
.ctmProductLink()
|
||||
.click()
|
||||
.wait(1000)
|
||||
.get(`tbody > tr:nth-child(${index})`)
|
||||
.click()
|
||||
.wait(1000)
|
||||
.window()
|
||||
.its('__store__')
|
||||
.its('store');
|
||||
});
|
||||
});
|
||||
|
||||
after(() => {
|
||||
cy.deleteApi('tag', jwt).deleteApi('category', jwt).deleteApi('product', jwt);
|
||||
});
|
||||
|
||||
context('Creating data with no relation', () => {
|
||||
beforeEach(() => {
|
||||
cy.server();
|
||||
cy.route(`${backendUrl}/content-manager/models`).as('initContentManager');
|
||||
cy.login()
|
||||
.then((data) => {
|
||||
jwt = data.jwt;
|
||||
})
|
||||
.visit('/admin')
|
||||
.wait(frontLoadingDelay)
|
||||
.wait('@initContentManager');
|
||||
});
|
||||
|
||||
after(() => {
|
||||
cy.deleteAllModelData('tag', jwt)
|
||||
.deleteAllModelData('category', jwt)
|
||||
.deleteAllModelData('product', jwt);
|
||||
});
|
||||
|
||||
it('Should create a tag with no relation', () => {
|
||||
cy.server();
|
||||
cy.route(getRequest('tag')).as('getTags');
|
||||
cy.ctmTagLink().click().ctmAddButton().click();
|
||||
const tagsToCreate = [
|
||||
'tag1',
|
||||
'tag2',
|
||||
'tag3',
|
||||
'superTag',
|
||||
'badTag',
|
||||
"I'm running out of idea tag",
|
||||
];
|
||||
// Check redirect url
|
||||
cy.url().should('equal', getCreateRedirectUrl('tag'));
|
||||
|
||||
// Try to save empty data
|
||||
cy.submitForm()
|
||||
.get('input#name')
|
||||
.invoke('attr', 'class')
|
||||
.should('include', 'form-control is-invalid');
|
||||
|
||||
tagsToCreate.forEach((tagName, index) => {
|
||||
cy.get('input#name')
|
||||
.type(tagName)
|
||||
.submitForm()
|
||||
.wait('@getTags')
|
||||
.get('tbody')
|
||||
.children()
|
||||
.should('have.length', index + 1);
|
||||
|
||||
if (index < tagsToCreate.length - 1) {
|
||||
cy.ctmAddButton().click();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('Should create a category with no relation', () => {
|
||||
cy.server();
|
||||
cy.route(getRequest('category', 'name')).as('getCategories');
|
||||
cy.ctmCategoryLink()
|
||||
.click()
|
||||
.get('tr > th:nth-child(3) > span')
|
||||
.click()
|
||||
.ctmAddButton()
|
||||
.click();
|
||||
const catsToCreate = [
|
||||
'drinks',
|
||||
'food',
|
||||
'junk food',
|
||||
'french food',
|
||||
'good french food',
|
||||
'greasy',
|
||||
"you don't want to eat that",
|
||||
];
|
||||
// Check redirect url
|
||||
cy.url().should('equal', getCreateRedirectUrl('category', 'name'));
|
||||
|
||||
catsToCreate.forEach((catName, index) => {
|
||||
cy.get('input#name')
|
||||
.type(catName)
|
||||
.submitForm()
|
||||
.wait('@getCategories')
|
||||
.get('tbody')
|
||||
.children()
|
||||
.should('have.length', index + 1);
|
||||
|
||||
if (index < catsToCreate.length - 1) {
|
||||
cy.ctmAddButton().click();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('Should display an error for unique fields for categories', () => {
|
||||
cy.ctmCategoryLink()
|
||||
.click()
|
||||
.ctmAddButton()
|
||||
.click()
|
||||
.get('input#name')
|
||||
.type('drinks')
|
||||
.submitForm()
|
||||
.get('input#name')
|
||||
.invoke('attr', 'class')
|
||||
.should('includes', 'form-control is-invalid')
|
||||
.get('input#name')
|
||||
.inputError('name')
|
||||
.should('have.text', 'This name is already taken ');
|
||||
});
|
||||
|
||||
it('Should delete all data using the UI', () => {
|
||||
cy.server();
|
||||
cy.route(getRequest('tag')).as('getTags');
|
||||
cy.route(getRequest('category', 'name')).as('getCategories');
|
||||
|
||||
cy.ctmTagLink()
|
||||
.click()
|
||||
.wait('@getTags')
|
||||
.wait(1000)
|
||||
.get('thead > tr > th:first-child')
|
||||
.click()
|
||||
.get('span#deleteAllData')
|
||||
.click()
|
||||
.get('button#ctaConfirm')
|
||||
.click()
|
||||
.wait(2000)
|
||||
.window()
|
||||
.its('__store__')
|
||||
.its('store')
|
||||
.then((pluginStore) => {
|
||||
const records = pluginStore
|
||||
.getState()
|
||||
.getIn(['content-manager_listPage', 'records', 'tag'])
|
||||
.toJS();
|
||||
|
||||
expect(records).to.have.length(0);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
context('Creating and updating data with relation', () => {
|
||||
before(() => {
|
||||
cy.server();
|
||||
cy.route(`${backendUrl}/content-manager/models`).as('initContentManager');
|
||||
cy.login()
|
||||
.then((data) => {
|
||||
jwt = data.jwt;
|
||||
|
||||
return data.jwt;
|
||||
})
|
||||
.then((jwt) => {
|
||||
return cy.seedData('tag', jwt).then(() => jwt);
|
||||
})
|
||||
.then((jwt) => {
|
||||
return cy.seedData('category', jwt);
|
||||
});
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
cy.server();
|
||||
cy.route(`${backendUrl}/content-manager/models`).as('initContentManager');
|
||||
cy.login()
|
||||
.then((data) => {
|
||||
jwt = data.jwt;
|
||||
|
||||
return data.jwt;
|
||||
})
|
||||
.visit('/admin')
|
||||
.wait(frontLoadingDelay)
|
||||
.wait('@initContentManager');
|
||||
});
|
||||
|
||||
it('Should create a product and link several tags and 1 category', () => {
|
||||
cy.server();
|
||||
cy.route(
|
||||
`${backendUrl}/content-manager/explorer/tag?_limit=10&_start=0&_sort=name:ASC&source=content-manager`
|
||||
).as('getTags');
|
||||
cy.ctmProductLink().click().ctmAddButton().click();
|
||||
|
||||
// Test default value
|
||||
cy.get('button#__OFF__bool')
|
||||
.invoke('attr', 'class')
|
||||
.should('includes', 'gradientOff')
|
||||
.get('button#__ON__bool1')
|
||||
.invoke('attr', 'class')
|
||||
.should('includes', 'gradientOn');
|
||||
|
||||
// Create a product
|
||||
const product = {
|
||||
name: 'product1',
|
||||
description: 'This is a super description',
|
||||
price: 1337,
|
||||
email: 'hi@strapi.io',
|
||||
};
|
||||
|
||||
Object.keys(product).forEach((key) => {
|
||||
if (key === 'description') {
|
||||
cy.get(`textarea[name="${key}"]`).type(product[key]);
|
||||
} else {
|
||||
cy.get(`input[name="${key}"]`).type(product[key]);
|
||||
}
|
||||
});
|
||||
|
||||
cy.get('button#__ON__bool').click().get('button#__OFF__bool1').click();
|
||||
|
||||
cy.get('input#tags')
|
||||
.type('special t', { force: true })
|
||||
.type('{enter}', { force: true })
|
||||
.type('ta', { force: true })
|
||||
.type('{enter}', { force: true })
|
||||
.get('ul#sortableListOftags')
|
||||
.children('li')
|
||||
.should((children) => {
|
||||
expect(children[0].innerText.trim()).to.equal('special tag');
|
||||
expect(children[1].innerText.trim()).to.equal('tag1');
|
||||
})
|
||||
.get('input#category')
|
||||
.type('french food', { force: true })
|
||||
.type('{enter}')
|
||||
.invoke('attr', 'value')
|
||||
.should('equal', 'french food')
|
||||
.submitForm();
|
||||
|
||||
cy.getListTagsOrderedByName()
|
||||
.wait('@getTags')
|
||||
.wait(1000)
|
||||
.get('tbody > tr:first-child')
|
||||
.click()
|
||||
.get('ul#sortableListOfproducts')
|
||||
.children()
|
||||
.should((children) => {
|
||||
expect(children).to.have.length(1);
|
||||
expect(children[0].innerText.trim()).to.equal('product1');
|
||||
});
|
||||
|
||||
cy.getListTagsOrderedByName()
|
||||
.wait('@getTags')
|
||||
.wait(2000)
|
||||
.get('tbody > tr:nth-child(2)')
|
||||
.click()
|
||||
.get('ul#sortableListOfproducts')
|
||||
.children()
|
||||
.should((children) => {
|
||||
expect(children).to.have.length(1);
|
||||
expect(children[0].innerText.trim()).to.equal('product1');
|
||||
});
|
||||
});
|
||||
|
||||
it('Should delete a product in tag1', () => {
|
||||
cy.getListTagsOrderedByName()
|
||||
.wait(frontLoadingDelay)
|
||||
.get('tbody > tr:nth-child(2)')
|
||||
.click()
|
||||
.wait(1000)
|
||||
.get('ul#sortableListOfproducts > li:nth-child(1) > div:nth-child(2) > img')
|
||||
.click()
|
||||
.submitForm()
|
||||
.ctmProductLink()
|
||||
.click()
|
||||
.wait(1000)
|
||||
.get('tbody > tr:nth-child(1)')
|
||||
.click()
|
||||
.wait(frontLoadingDelay)
|
||||
.get('ul#sortableListOftags')
|
||||
.children()
|
||||
.should((children) => {
|
||||
expect(children).to.have.length(1);
|
||||
expect(children[0].innerText.trim()).to.equal('special tag');
|
||||
});
|
||||
});
|
||||
|
||||
it('Should add several products to category french food', () => {
|
||||
cy.server();
|
||||
cy.route(
|
||||
`${backendUrl}/content-manager/explorer/category?_limit=10&_start=0&_sort=_id:ASC&source=content-manager`
|
||||
).as('getCategories');
|
||||
cy.route(
|
||||
`${backendUrl}/content-manager/explorer/product?_limit=10&_start=0&_sort=_id:ASC&source=content-manager`
|
||||
).as('getProducts');
|
||||
const product = {
|
||||
name: 'MacBook',
|
||||
description: 'A laptop',
|
||||
price: 2000,
|
||||
email: 'kai@strapi.io',
|
||||
};
|
||||
const product2 = {
|
||||
name: 'Dell',
|
||||
description: 'Not a mac',
|
||||
price: 4,
|
||||
email: 'bob@strapi.io',
|
||||
};
|
||||
|
||||
cy.ctmProductLink().click().ctmAddButton().click();
|
||||
|
||||
cy.fillProductForm(product)
|
||||
.submitForm()
|
||||
.ctmAddButton()
|
||||
.click()
|
||||
.fillProductForm(product2)
|
||||
.submitForm();
|
||||
|
||||
cy.ctmCategoryLink()
|
||||
.click()
|
||||
.wait('@getCategories')
|
||||
.wait(1000)
|
||||
.get('tbody > tr:nth-child(5)')
|
||||
.click()
|
||||
.get('ul#sortableListOfproducts')
|
||||
.as('relations')
|
||||
.children()
|
||||
.should((children) => {
|
||||
expect(children).to.have.length(1);
|
||||
expect(children[0].innerText.trim()).to.equal('product1');
|
||||
})
|
||||
.get('ul#sortableListOfproducts > li:nth-child(1) > div:nth-child(2) > img')
|
||||
.click()
|
||||
.get('input#products')
|
||||
.type('mac', { force: true })
|
||||
.type('{enter}', { force: true })
|
||||
.type('dell', { force: true })
|
||||
.type('{enter}', { force: true })
|
||||
.get('@relations')
|
||||
.children()
|
||||
.should((children) => {
|
||||
expect(children).to.have.length(2);
|
||||
expect(children[0].innerText.trim()).to.equal('MacBook');
|
||||
expect(children[1].innerText.trim()).to.equal('Dell');
|
||||
})
|
||||
.submitForm();
|
||||
|
||||
cy.getProduct(1).then((pluginStore) => {
|
||||
const category = pluginStore
|
||||
.getState()
|
||||
.getIn(['content-manager_editPage', 'record', 'category']);
|
||||
|
||||
expect(category).to.equal(null);
|
||||
});
|
||||
|
||||
cy.getProduct(2)
|
||||
.then((pluginStore) => {
|
||||
const category = pluginStore
|
||||
.getState()
|
||||
.getIn(['content-manager_editPage', 'record', 'category', 'name']);
|
||||
|
||||
expect(category).to.equal('french food');
|
||||
})
|
||||
.getProduct(3)
|
||||
.then((pluginStore) => {
|
||||
const category = pluginStore
|
||||
.getState()
|
||||
.getIn(['content-manager_editPage', 'record', 'category', 'name']);
|
||||
|
||||
expect(category).to.equal('french food');
|
||||
});
|
||||
});
|
||||
|
||||
after(() => {
|
||||
cy.deleteAllModelData('tag', jwt)
|
||||
.deleteAllModelData('category', jwt)
|
||||
.deleteAllModelData('product', jwt);
|
||||
});
|
||||
});
|
||||
});
|
@ -1,90 +0,0 @@
|
||||
let jwt;
|
||||
const animDelay = Cypress.config('animDelay');
|
||||
const backendUrl = Cypress.config('backendUrl');
|
||||
const frontLoadingDelay = Cypress.config('frontLoadingDelay');
|
||||
const links = {
|
||||
Category: '/admin/plugins/content-manager/category?source=content-manager',
|
||||
Product: '/admin/plugins/content-manager/product?source=content-manager',
|
||||
settings: '/admin/plugins/content-manager/ctm-configurations',
|
||||
Tag: '/admin/plugins/content-manager/tag?source=content-manager',
|
||||
User: '/admin/plugins/content-manager/user?source=users-permissions',
|
||||
};
|
||||
|
||||
describe('Testing build and schema core_store', () => {
|
||||
before(() => {
|
||||
cy.login()
|
||||
.then((data) => {
|
||||
jwt = data.jwt;
|
||||
|
||||
return cy.createCTMApis(data.jwt);
|
||||
})
|
||||
.wait(1000);
|
||||
});
|
||||
|
||||
after(() => {
|
||||
cy.deleteApi('tag', jwt).deleteApi('category', jwt).deleteApi('product', jwt);
|
||||
});
|
||||
|
||||
context('Testing views', () => {
|
||||
beforeEach(() => {
|
||||
cy.login()
|
||||
.then((data) => {
|
||||
jwt = data.jwt;
|
||||
})
|
||||
.visit('/admin')
|
||||
.wait(frontLoadingDelay);
|
||||
});
|
||||
|
||||
it('Should visit all list pages without any errors', () => {
|
||||
cy.server();
|
||||
cy.route(`${backendUrl}/content-manager/models`).as('initCTM');
|
||||
cy.get(`a[href="${links.settings}"]`).click().wait('@initCTM');
|
||||
|
||||
// Check all list views are rendered without any error
|
||||
for (let i = 0; i < 4; i++) {
|
||||
Object.keys(links).forEach((link) => {
|
||||
const name = link === 'settings' ? 'Content Manager' : link;
|
||||
|
||||
cy.get(`a[href="${links[link]}"]`).click().get('h1').should('have', name);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
it('Should visit all views once without any errors', () => {
|
||||
cy.server();
|
||||
cy.route(`${backendUrl}/content-manager/models`).as('initCTM');
|
||||
cy.get(`a[href="${links.settings}"]`).click().wait('@initCTM');
|
||||
|
||||
// Testing errors related to reactstrap
|
||||
cy.get('#cancelChanges')
|
||||
.click()
|
||||
.wait(animDelay)
|
||||
.checkModalOpening()
|
||||
.should('be.visible')
|
||||
.type('{esc}');
|
||||
|
||||
// Test setting view
|
||||
Object.keys(links).forEach((link) => {
|
||||
if (link !== 'settings') {
|
||||
cy.get(`#${link.toLowerCase()}`)
|
||||
.click()
|
||||
.get('h1')
|
||||
.should('have', `Content Manager - ${link}`)
|
||||
.get(`a[href="${links.settings}"]`)
|
||||
.click();
|
||||
}
|
||||
});
|
||||
|
||||
Object.keys(links).forEach((link) => {
|
||||
if (link !== 'settings') {
|
||||
cy.get(`a[href="${links[link]}"]`)
|
||||
.click()
|
||||
.get('button#addEntry')
|
||||
.click()
|
||||
.get('h1')
|
||||
.should('have', 'New Entry');
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
@ -1,179 +0,0 @@
|
||||
let jwt;
|
||||
const animDelay = Cypress.config('animDelay');
|
||||
const frontLoadingDelay = Cypress.config('frontLoadingDelay');
|
||||
const backendUrl = Cypress.config('backendUrl');
|
||||
|
||||
describe('Testing Content Manager ListPages', function () {
|
||||
before(() => {
|
||||
cy.login()
|
||||
.then((data) => {
|
||||
jwt = data.jwt;
|
||||
|
||||
return cy.createCTMApis(data.jwt).then(() => jwt);
|
||||
})
|
||||
.then((jwt) => {
|
||||
cy.seedData('product', jwt);
|
||||
})
|
||||
.wait(1000);
|
||||
});
|
||||
|
||||
after(() => {
|
||||
cy.deleteAllModelData('product', jwt);
|
||||
cy.deleteApi('tag', jwt).deleteApi('category', jwt).deleteApi('product', jwt);
|
||||
});
|
||||
|
||||
context('Testing sorting options', () => {
|
||||
beforeEach(() => {
|
||||
cy.login()
|
||||
.then((data) => {
|
||||
jwt = data.jwt;
|
||||
})
|
||||
.visit('/admin')
|
||||
.wait(frontLoadingDelay);
|
||||
});
|
||||
|
||||
it('Should have the Id default sort', () => {
|
||||
cy.get('a[href="/admin/plugins/content-manager/product?source=content-manager"]')
|
||||
.click()
|
||||
.wait(frontLoadingDelay);
|
||||
|
||||
cy.get('tr > th:nth-child(2) > span')
|
||||
.children('i')
|
||||
.should('be.visible')
|
||||
.invoke('attr', 'class')
|
||||
.should('includes', 'fa-sort-up');
|
||||
});
|
||||
|
||||
it('Should change the default sort of product to name ASC then name DESC', () => {
|
||||
cy.server();
|
||||
cy.route(
|
||||
`${backendUrl}/content-manager/explorer/product?_limit=10&_start=0&_sort=_id:ASC&source=content-manager`
|
||||
).as('getProduct');
|
||||
cy.route(
|
||||
`${backendUrl}/content-manager/explorer/product?_limit=10&_start=0&_sort=name:ASC&source=content-manager`
|
||||
).as('getSortByNameASC');
|
||||
cy.route(
|
||||
`${backendUrl}/content-manager/explorer/product?_limit=10&_start=0&_sort=name:DESC&source=content-manager`
|
||||
).as('getSortByNameDESC');
|
||||
|
||||
cy.get('a[href="/admin/plugins/content-manager/product?source=content-manager"]')
|
||||
.click()
|
||||
.wait('@getProduct')
|
||||
.get('tr > th:nth-child(3) > span')
|
||||
.as('getName')
|
||||
.click();
|
||||
|
||||
cy.wait('@getSortByNameASC')
|
||||
.get('@getName')
|
||||
.children('i')
|
||||
.should('be.visible')
|
||||
.invoke('attr', 'class')
|
||||
.should('includes', 'iconAsc')
|
||||
.get('tbody > tr:nth-child(1) > td:nth-child(3)')
|
||||
.as('firstResult')
|
||||
.should('have.text', 'name');
|
||||
|
||||
cy.get('@getName')
|
||||
.click()
|
||||
.wait('@getSortByNameDESC')
|
||||
.get('@getName')
|
||||
.children('i')
|
||||
.should('be.visible')
|
||||
.invoke('attr', 'class')
|
||||
.should('includes', 'iconDesc')
|
||||
.get('@firstResult')
|
||||
.should('have.text', 'name1');
|
||||
});
|
||||
|
||||
it('Should set the product default sort to name', () => {
|
||||
cy.get('a[href="/admin/plugins/content-manager/ctm-configurations"]')
|
||||
.click()
|
||||
.get('#product')
|
||||
.click()
|
||||
.get('select[name="product.defaultSort"]')
|
||||
.as('defaultSort')
|
||||
.select('name')
|
||||
.should('have.value', 'name')
|
||||
.get('select[name="product.sort"]')
|
||||
.as('sortOption')
|
||||
.select('DESC')
|
||||
.should('have.value', 'DESC')
|
||||
.submitForm()
|
||||
.get('#ctaConfirm')
|
||||
.click()
|
||||
.wait(frontLoadingDelay)
|
||||
.get('a[href="/admin/plugins/content-manager/product?source=content-manager"]')
|
||||
.click()
|
||||
.wait(frontLoadingDelay)
|
||||
.get('tr > th:nth-child(3) > span')
|
||||
.as('getName')
|
||||
.children('i')
|
||||
.invoke('attr', 'class')
|
||||
.should('includes', 'iconDesc')
|
||||
.get('tbody > tr:nth-child(1) > td:nth-child(3)')
|
||||
.should('have.text', 'name1');
|
||||
|
||||
// Set it back to normal
|
||||
cy.get('a[href="/admin/plugins/content-manager/ctm-configurations"]')
|
||||
.click()
|
||||
.get('#product')
|
||||
.click()
|
||||
.get('@defaultSort')
|
||||
.select('_id')
|
||||
.should('have.value', '_id')
|
||||
.get('@sortOption')
|
||||
.select('ASC')
|
||||
.should('have.value', 'ASC')
|
||||
.submitForm()
|
||||
.get('#ctaConfirm')
|
||||
.click()
|
||||
.wait(frontLoadingDelay)
|
||||
.get('a[href="/admin/plugins/content-manager/product?source=content-manager"]')
|
||||
.click()
|
||||
.wait(frontLoadingDelay)
|
||||
.get('tr > th:nth-child(2) > span')
|
||||
.children('i')
|
||||
.invoke('attr', 'class')
|
||||
.should('includes', 'iconAsc');
|
||||
});
|
||||
});
|
||||
|
||||
context('Testing filters', () => {
|
||||
beforeEach(() => {
|
||||
cy.login()
|
||||
.then((data) => {
|
||||
jwt = data.jwt;
|
||||
})
|
||||
.visit('/admin')
|
||||
.wait(frontLoadingDelay);
|
||||
});
|
||||
|
||||
it('Should apply filters for product data', () => {
|
||||
cy.get('a[href="/admin/plugins/content-manager/product?source=content-manager"]')
|
||||
.click()
|
||||
.wait(frontLoadingDelay);
|
||||
cy.get('button#addFilterCTA')
|
||||
.as('toggleFilter')
|
||||
.click()
|
||||
.wait(animDelay)
|
||||
.get('div#filterPickWrapper')
|
||||
.as('filterWrapper')
|
||||
.children('div')
|
||||
.should('have.length', 1);
|
||||
|
||||
cy.get('input[name="0.value"]')
|
||||
.type('name')
|
||||
.get('button#newFilter')
|
||||
.click()
|
||||
.get('select[name="1.attr"]')
|
||||
.select('bool')
|
||||
.get(
|
||||
'button[label="content-manager.components.FiltersPickWrapper.PluginHeader.actions.apply"]'
|
||||
)
|
||||
.click()
|
||||
.wait(animDelay)
|
||||
.get('tbody > tr')
|
||||
.should('have.length', 1);
|
||||
});
|
||||
});
|
||||
});
|
@ -37,10 +37,10 @@
|
||||
"react": "^17.0.2",
|
||||
"react-dom": "^17.0.2",
|
||||
"react-intl": "6.2.7",
|
||||
"react-redux": "7.2.8",
|
||||
"react-redux": "8.0.5",
|
||||
"react-router": "^5.2.0",
|
||||
"react-router-dom": "5.3.4",
|
||||
"redux": "^4.0.1",
|
||||
"redux": "^4.2.1",
|
||||
"reselect": "^4.0.0",
|
||||
"yup": "^0.32.9"
|
||||
},
|
||||
|
@ -1,231 +0,0 @@
|
||||
// import 'whatwg-fetch';
|
||||
|
||||
let jwt;
|
||||
let userId;
|
||||
const animDelay = Cypress.config('animDelay');
|
||||
const frontEndUrl = Cypress.config('baseUrl');
|
||||
const frontLoadingDelay = Cypress.config('frontLoadingDelay');
|
||||
const backendUrl = Cypress.config('backendUrl');
|
||||
const pluginUrl = `${frontEndUrl}/admin/plugins/content-type-builder`;
|
||||
const TAG_API = {
|
||||
name: 'tag',
|
||||
description: 'This is a super tag \nwith multi \nlines description.',
|
||||
};
|
||||
|
||||
describe('Test CTB', () => {
|
||||
context('Check create and update API', () => {
|
||||
beforeEach(() => {
|
||||
cy.server();
|
||||
cy.route(`${backendUrl}/content-type-builder/autoReload`).as('initContentTypeBuilder');
|
||||
cy.login().then((data) => {
|
||||
jwt = data.jwt;
|
||||
userId = data.user._id || data.user.id;
|
||||
});
|
||||
cy.visit('/admin');
|
||||
cy.wait(frontLoadingDelay);
|
||||
cy.wait('@initContentTypeBuilder');
|
||||
});
|
||||
|
||||
it('Should visit the content type builder', () => {
|
||||
cy.get('a[href="/admin/plugins/content-type-builder"').click();
|
||||
cy.url().should('equal', pluginUrl);
|
||||
});
|
||||
|
||||
it('Should prevent the user from creating a camelCase api', () => {
|
||||
cy.server();
|
||||
cy.route('GET', `${backendUrl}/content-type-builder/models`).as('models');
|
||||
|
||||
cy.get('a[href="/admin/plugins/content-type-builder"')
|
||||
.click()
|
||||
.wait('@models')
|
||||
.get('#openAddCT')
|
||||
.click()
|
||||
.get('#name')
|
||||
.type('camelCase')
|
||||
.get('#description')
|
||||
.type('\n')
|
||||
.get('#name')
|
||||
.should('have.value', 'camelcase')
|
||||
.get('#name')
|
||||
.type('{selectall}')
|
||||
.type('not camel-case')
|
||||
.get('#description')
|
||||
.type('{backspace}')
|
||||
.get('#name')
|
||||
.should('have.value', 'notcamelcase');
|
||||
});
|
||||
|
||||
it('Should create a TAG API', function () {
|
||||
cy.server();
|
||||
cy.route('GET', `${backendUrl}/content-type-builder/models`).as('models');
|
||||
cy.route('POST', `${backendUrl}/content-type-builder/models`).as('createModel');
|
||||
cy.route('DELETE', `${backendUrl}/content-type-builder/models/tag`).as('deleteTag');
|
||||
|
||||
cy.get('a[href="/admin/plugins/content-type-builder"').click().wait('@models');
|
||||
|
||||
// Open modal
|
||||
cy.get('#openAddCT').click().wait(animDelay);
|
||||
|
||||
// Check the modal is opened this will tell is if we have a build issue
|
||||
cy.checkModalOpening();
|
||||
cy.get('.modal').invoke('show');
|
||||
|
||||
// Fill the form
|
||||
Object.keys(TAG_API).forEach((key) => {
|
||||
cy.log(key);
|
||||
cy.get(`#${key}`).type(TAG_API[key]);
|
||||
});
|
||||
|
||||
// Submit the form and navigate to product page
|
||||
cy.submitForm().url().should('equal', `${pluginUrl}/models/tag`);
|
||||
|
||||
// Open the attributes's modal
|
||||
cy.get('#openAddAttr').click().wait(animDelay);
|
||||
|
||||
// Check that we don't have a build error from reacstrap
|
||||
cy.checkModalOpening().should('be.visible');
|
||||
|
||||
// Ensure the modal is opened to get #attrCardstring
|
||||
cy.wait(1000)
|
||||
.get('button#attrCardstring')
|
||||
.click()
|
||||
.get('input[name="name"]')
|
||||
.type('name')
|
||||
.get('#continue')
|
||||
.click();
|
||||
|
||||
cy.get('button#saveData')
|
||||
.should('have.id', 'saveData')
|
||||
.click()
|
||||
.wait('@createModel')
|
||||
.wait(frontLoadingDelay);
|
||||
|
||||
cy.get('#attributesList li').first().should('contain', 'name');
|
||||
|
||||
// Delete tag API
|
||||
cy.get('a[href="/admin/plugins/content-type-builder"]')
|
||||
.click()
|
||||
.wait(frontLoadingDelay)
|
||||
.wait(frontLoadingDelay)
|
||||
.get('#deletetag')
|
||||
.click()
|
||||
.checkModalOpening()
|
||||
.should('be.visible')
|
||||
.get('#ctaConfirm')
|
||||
.click()
|
||||
.wait('@deleteTag')
|
||||
.wait(frontLoadingDelay)
|
||||
.get('#ctbModelsList li')
|
||||
.should('have.length', 4)
|
||||
.waitRestart();
|
||||
});
|
||||
|
||||
it('Should update PRODUCT API field and visit the create product page', () => {
|
||||
cy.server();
|
||||
cy.createProductAndTagApis(jwt);
|
||||
cy.route(`${backendUrl}/content-type-builder/models/product?`).as('getProductModel');
|
||||
cy.route('PUT', `${backendUrl}/content-type-builder/models/product`).as('updateProductModel');
|
||||
|
||||
cy.visit(
|
||||
'/admin/plugins/content-type-builder/models/product#editproduct::attributestring::baseSettings::0'
|
||||
);
|
||||
cy.wait('@getProductModel');
|
||||
cy.wait(frontLoadingDelay);
|
||||
|
||||
// Open the modal via url
|
||||
cy.checkModalOpening()
|
||||
.should('be.visible')
|
||||
.get('input[name="name"]')
|
||||
.type('{selectall}')
|
||||
.type('updatedName')
|
||||
.get('#continue')
|
||||
.click();
|
||||
|
||||
cy.get('#attributesList li').first().contains('updatedName'); // Yield el in .nav containing 'About'
|
||||
|
||||
cy.get('button#saveData').click().wait('@updateProductModel').wait(frontLoadingDelay);
|
||||
|
||||
// Check that we can still go to the create page
|
||||
cy.get('a[href="/admin/plugins/content-manager/product?source=content-manager"')
|
||||
.click()
|
||||
.get('button[label="content-manager.containers.List.addAnEntry"')
|
||||
.click();
|
||||
|
||||
cy.window()
|
||||
.its('__store__')
|
||||
.its('store')
|
||||
.then((pluginStore) => {
|
||||
const displayedFields = pluginStore
|
||||
.getState()
|
||||
.getIn([
|
||||
'content-manager_global',
|
||||
'schema',
|
||||
'models',
|
||||
'product',
|
||||
'editDisplay',
|
||||
'fields',
|
||||
])
|
||||
.toJS();
|
||||
|
||||
expect(displayedFields).to.include.members([
|
||||
'description',
|
||||
'price',
|
||||
'updatedName',
|
||||
'bool',
|
||||
'bool1',
|
||||
'email',
|
||||
]);
|
||||
});
|
||||
|
||||
cy.waitRestart();
|
||||
});
|
||||
|
||||
it('Should update PRODUCT API name and visit the create product page', () => {
|
||||
cy.server();
|
||||
// cy.createProductAndTagApis(jwt);
|
||||
cy.route(`${backendUrl}/content-type-builder/models/product?`).as('getProductModel');
|
||||
cy.route('PUT', `${backendUrl}/content-type-builder/models/product`).as('updateProductModel');
|
||||
|
||||
cy.visit(
|
||||
'/admin/plugins/content-type-builder/models/product#editproduct::contentType::baseSettings'
|
||||
);
|
||||
cy.wait('@getProductModel');
|
||||
cy.wait(frontLoadingDelay);
|
||||
|
||||
// Open the modal via url
|
||||
cy.checkModalOpening()
|
||||
.should('be.visible')
|
||||
.get('input[name="name"]')
|
||||
.type('{selectall}')
|
||||
.type('produit')
|
||||
.submitForm()
|
||||
.wait('@updateProductModel')
|
||||
.wait(frontLoadingDelay);
|
||||
|
||||
// Check that we can still go to the create page
|
||||
cy.get('a[href="/admin/plugins/content-manager/produit?source=content-manager"')
|
||||
.click()
|
||||
.wait(frontLoadingDelay)
|
||||
.get('button[label="content-manager.containers.List.addAnEntry"')
|
||||
.click()
|
||||
.get('h1')
|
||||
.should('have.id', 'addNewEntry');
|
||||
|
||||
// cy.window()
|
||||
// .its('__store__')
|
||||
// .its('content-manager')
|
||||
// .then(pluginStore => {
|
||||
// const displayedFields = pluginStore
|
||||
// .getState()
|
||||
// .getIn(['global', 'schema', 'models', 'product', 'editDisplay', 'fields'])
|
||||
// .toJS();
|
||||
|
||||
// expect(displayedFields).to.include.members(['description', 'price', 'updatedName', 'bool', 'bool1', 'email']);
|
||||
// });
|
||||
});
|
||||
});
|
||||
|
||||
after(() => {
|
||||
cy.deleteApi('tag', jwt).deleteApi('produit', jwt).deleteUser(userId, jwt);
|
||||
});
|
||||
});
|
@ -39,7 +39,7 @@
|
||||
"react-copy-to-clipboard": "^5.1.0",
|
||||
"react-dom": "^17.0.2",
|
||||
"react-intl": "6.2.7",
|
||||
"react-redux": "7.2.8",
|
||||
"react-redux": "8.0.5",
|
||||
"react-router": "^5.2.0",
|
||||
"react-router-dom": "5.3.4",
|
||||
"sharp": "0.31.1"
|
||||
@ -49,7 +49,7 @@
|
||||
"@testing-library/react": "12.1.4",
|
||||
"@testing-library/react-hooks": "8.0.1",
|
||||
"@testing-library/user-event": "14.4.3",
|
||||
"msw": "0.49.1",
|
||||
"msw": "1.0.0",
|
||||
"react-test-renderer": "^17.0.2"
|
||||
},
|
||||
"engines": {
|
||||
|
@ -37,10 +37,10 @@
|
||||
"react-copy-to-clipboard": "^5.1.0",
|
||||
"react-dom": "^17.0.2",
|
||||
"react-intl": "6.2.7",
|
||||
"react-redux": "7.2.8",
|
||||
"react-redux": "8.0.5",
|
||||
"react-router": "^5.2.0",
|
||||
"react-router-dom": "5.3.4",
|
||||
"redux": "^4.0.1",
|
||||
"redux": "^4.2.1",
|
||||
"reselect": "^4.0.0",
|
||||
"swagger-ui-dist": "4.15.5",
|
||||
"yaml": "1.10.2"
|
||||
@ -50,7 +50,7 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@testing-library/react": "12.1.4",
|
||||
"msw": "0.49.1"
|
||||
"msw": "1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.19.1 <=18.x.x",
|
||||
|
@ -29,7 +29,7 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@testing-library/react": "12.1.4",
|
||||
"msw": "0.49.1"
|
||||
"msw": "1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.19.1 <=18.x.x",
|
||||
|
@ -40,7 +40,7 @@
|
||||
"react": "^17.0.2",
|
||||
"react-dom": "^17.0.2",
|
||||
"react-intl": "6.2.7",
|
||||
"react-redux": "7.2.8",
|
||||
"react-redux": "8.0.5",
|
||||
"react-router": "^5.2.0",
|
||||
"react-router-dom": "5.3.4",
|
||||
"request": "^2.83.0",
|
||||
@ -51,7 +51,7 @@
|
||||
"@testing-library/react": "12.1.4",
|
||||
"@testing-library/react-hooks": "8.0.1",
|
||||
"@testing-library/user-event": "14.4.3",
|
||||
"msw": "0.49.1",
|
||||
"msw": "1.0.0",
|
||||
"react-test-renderer": "^17.0.2"
|
||||
},
|
||||
"engines": {
|
||||
|
107
yarn.lock
107
yarn.lock
@ -2,30 +2,23 @@
|
||||
# yarn lockfile v1
|
||||
|
||||
|
||||
"@actions/core@1.9.1":
|
||||
version "1.9.1"
|
||||
resolved "https://registry.yarnpkg.com/@actions/core/-/core-1.9.1.tgz#97c0201b1f9856df4f7c3a375cdcdb0c2a2f750b"
|
||||
integrity sha512-5ad+U2YGrmmiw6du20AQW5XuWo7UKN2052FjSV7MX+Wfjf8sCqcsZe62NfgHys4QI4/Y+vQvLKYL8jWtA1ZBTA==
|
||||
"@actions/core@1.10.0":
|
||||
version "1.10.0"
|
||||
resolved "https://registry.yarnpkg.com/@actions/core/-/core-1.10.0.tgz#44551c3c71163949a2f06e94d9ca2157a0cfac4f"
|
||||
integrity sha512-2aZDDa3zrrZbP5ZYg159sNoLRb61nQ7awl5pSvIq5Qpj81vwDzdMRKzkWJGJuwVvWpvZKx7vspJALyvaaIQyug==
|
||||
dependencies:
|
||||
"@actions/http-client" "^2.0.1"
|
||||
uuid "^8.3.2"
|
||||
|
||||
"@actions/github@5.0.0":
|
||||
version "5.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@actions/github/-/github-5.0.0.tgz#1754127976c50bd88b2e905f10d204d76d1472f8"
|
||||
integrity sha512-QvE9eAAfEsS+yOOk0cylLBIO/d6WyWIOvsxxzdrPFaud39G6BOkUwScXZn1iBzQzHyu9SBkkLSWlohDWdsasAQ==
|
||||
"@actions/github@5.1.1":
|
||||
version "5.1.1"
|
||||
resolved "https://registry.yarnpkg.com/@actions/github/-/github-5.1.1.tgz#40b9b9e1323a5efcf4ff7dadd33d8ea51651bbcb"
|
||||
integrity sha512-Nk59rMDoJaV+mHCOJPXuvB1zIbomlKS0dmSIqPGxd0enAXBnOfn4VWF+CGtRCwXZG9Epa54tZA7VIRlJDS8A6g==
|
||||
dependencies:
|
||||
"@actions/http-client" "^1.0.11"
|
||||
"@octokit/core" "^3.4.0"
|
||||
"@octokit/plugin-paginate-rest" "^2.13.3"
|
||||
"@octokit/plugin-rest-endpoint-methods" "^5.1.1"
|
||||
|
||||
"@actions/http-client@^1.0.11":
|
||||
version "1.0.11"
|
||||
resolved "https://registry.yarnpkg.com/@actions/http-client/-/http-client-1.0.11.tgz#c58b12e9aa8b159ee39e7dd6cbd0e91d905633c0"
|
||||
integrity sha512-VRYHGQV1rqnROJqdMvGUbY/Kn8vriQe/F9HR2AlYHzmKuM/p3kjNuXhmdBfcVgsvRWTz5C5XW5xvndZrVBuAYg==
|
||||
dependencies:
|
||||
tunnel "0.0.6"
|
||||
"@actions/http-client" "^2.0.1"
|
||||
"@octokit/core" "^3.6.0"
|
||||
"@octokit/plugin-paginate-rest" "^2.17.0"
|
||||
"@octokit/plugin-rest-endpoint-methods" "^5.13.0"
|
||||
|
||||
"@actions/http-client@^2.0.1":
|
||||
version "2.0.1"
|
||||
@ -3830,7 +3823,7 @@
|
||||
dependencies:
|
||||
"@octokit/types" "^7.0.0"
|
||||
|
||||
"@octokit/core@^3.4.0":
|
||||
"@octokit/core@^3.6.0":
|
||||
version "3.6.0"
|
||||
resolved "https://registry.yarnpkg.com/@octokit/core/-/core-3.6.0.tgz#3376cb9f3008d9b3d110370d90e0a1fcd5fe6085"
|
||||
integrity sha512-7RKRKuA4xTjMhY+eG3jthb3hlZCsOwg3rztWh75Xc+ShDWOfDDATWbeZpAHBNRpm4Tv9WgBMOy1zEJYXG6NJ7Q==
|
||||
@ -3907,7 +3900,7 @@
|
||||
resolved "https://registry.yarnpkg.com/@octokit/plugin-enterprise-rest/-/plugin-enterprise-rest-6.0.1.tgz#e07896739618dab8da7d4077c658003775f95437"
|
||||
integrity sha512-93uGjlhUD+iNg1iWhUENAtJata6w5nE+V4urXOAlIXdco6xNZtUSfYY8dzp3Udy74aqO/B5UZL80x/YMa5PKRw==
|
||||
|
||||
"@octokit/plugin-paginate-rest@^2.13.3":
|
||||
"@octokit/plugin-paginate-rest@^2.17.0":
|
||||
version "2.21.3"
|
||||
resolved "https://registry.yarnpkg.com/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.21.3.tgz#7f12532797775640dbb8224da577da7dc210c87e"
|
||||
integrity sha512-aCZTEf0y2h3OLbrgKkrfFdjRL6eSOo8komneVQJnYecAxIej7Bafor2xhuDJOIFau4pk0i/P28/XgtbyPF0ZHw==
|
||||
@ -3926,7 +3919,7 @@
|
||||
resolved "https://registry.yarnpkg.com/@octokit/plugin-request-log/-/plugin-request-log-1.0.4.tgz#5e50ed7083a613816b1e4a28aeec5fb7f1462e85"
|
||||
integrity sha512-mLUsMkgP7K/cnFEw07kWqXGF5LKrOkD+lhCrKvPHXWDywAwuDUeDwWBpc69XK3pNX0uKiVt8g5z96PJ6z9xCFA==
|
||||
|
||||
"@octokit/plugin-rest-endpoint-methods@^5.1.1":
|
||||
"@octokit/plugin-rest-endpoint-methods@^5.13.0":
|
||||
version "5.16.2"
|
||||
resolved "https://registry.yarnpkg.com/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.16.2.tgz#7ee8bf586df97dd6868cf68f641354e908c25342"
|
||||
integrity sha512-8QFz29Fg5jDuTPXVtey05BLm7OB+M8fnvE64RNegzX7U+5NUXcOcnpTIK0YfSHBg8gYd0oxIq3IZTe9SfPZiRw==
|
||||
@ -6122,7 +6115,7 @@
|
||||
dependencies:
|
||||
"@types/unist" "*"
|
||||
|
||||
"@types/hoist-non-react-statics@^3.3.0", "@types/hoist-non-react-statics@^3.3.1":
|
||||
"@types/hoist-non-react-statics@^3.3.1":
|
||||
version "3.3.1"
|
||||
resolved "https://registry.yarnpkg.com/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz#1124aafe5118cb591977aeb1ceaaed1070eb039f"
|
||||
integrity sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA==
|
||||
@ -6433,16 +6426,6 @@
|
||||
dependencies:
|
||||
"@types/react" "*"
|
||||
|
||||
"@types/react-redux@^7.1.20":
|
||||
version "7.1.24"
|
||||
resolved "https://registry.yarnpkg.com/@types/react-redux/-/react-redux-7.1.24.tgz#6caaff1603aba17b27d20f8ad073e4c077e975c0"
|
||||
integrity sha512-7FkurKcS1k0FHZEtdbbgN8Oc6b+stGSfZYjQGicofJ0j4U0qIn/jaSvnP2pLwZKiai3/17xqqxkkrxTgN8UNbQ==
|
||||
dependencies:
|
||||
"@types/hoist-non-react-statics" "^3.3.0"
|
||||
"@types/react" "*"
|
||||
hoist-non-react-statics "^3.3.0"
|
||||
redux "^4.0.0"
|
||||
|
||||
"@types/react-transition-group@^4.4.0":
|
||||
version "4.4.5"
|
||||
resolved "https://registry.yarnpkg.com/@types/react-transition-group/-/react-transition-group-4.4.5.tgz#aae20dcf773c5aa275d5b9f7cdbca638abc5e416"
|
||||
@ -6586,6 +6569,11 @@
|
||||
resolved "https://registry.yarnpkg.com/@types/unist/-/unist-2.0.6.tgz#250a7b16c3b91f672a24552ec64678eeb1d3a08d"
|
||||
integrity sha512-PBjIUxZHOuj0R15/xuwJYjFi+KZdNFrehocChv4g5hu6aFroHue8m0lBP0POdK2nKzbw0cgV1mws8+V/JAcEkQ==
|
||||
|
||||
"@types/use-sync-external-store@^0.0.3":
|
||||
version "0.0.3"
|
||||
resolved "https://registry.yarnpkg.com/@types/use-sync-external-store/-/use-sync-external-store-0.0.3.tgz#b6725d5f4af24ace33b36fafd295136e75509f43"
|
||||
integrity sha512-EwmlvuaxPNej9+T4v5AuBPJa2x2UOJVdjCtDHgcDqitUeOtjnJKJ+apYjVcAoBEMjKW1VVFGZLUb5+qqa09XFA==
|
||||
|
||||
"@types/uuid@9.0.0":
|
||||
version "9.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-9.0.0.tgz#53ef263e5239728b56096b0a869595135b7952d2"
|
||||
@ -16516,10 +16504,10 @@ ms@2.1.3, ms@^2.0.0, ms@^2.1.1:
|
||||
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2"
|
||||
integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==
|
||||
|
||||
msw@0.49.1:
|
||||
version "0.49.1"
|
||||
resolved "https://registry.yarnpkg.com/msw/-/msw-0.49.1.tgz#615e5c35736d8ec9924da6dce1f0c5c3528d4f26"
|
||||
integrity sha512-JpIIq4P65ofj0HVmDMkJuRwgP9s5kcdutpZ15evMTb2k91/USB7IKWRLV9J1Mzc3OqTdwNj4RwtOWJ5y/FulQQ==
|
||||
msw@1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/msw/-/msw-1.0.0.tgz#4f8e63aa23912561a63b99ff560a089da6969418"
|
||||
integrity sha512-8QVa1RAN/Nzbn/tKmtimJ+b2M1QZOMdETQW7/1TmBOZ4w+wJojfxuh1Hj5J4FYdBgZWW/TK4CABUOlOM4OjTOA==
|
||||
dependencies:
|
||||
"@mswjs/cookies" "^0.2.2"
|
||||
"@mswjs/interceptors" "^0.17.5"
|
||||
@ -16537,7 +16525,7 @@ msw@0.49.1:
|
||||
node-fetch "^2.6.7"
|
||||
outvariant "^1.3.0"
|
||||
path-to-regexp "^6.2.0"
|
||||
strict-event-emitter "^0.2.6"
|
||||
strict-event-emitter "^0.4.3"
|
||||
type-fest "^2.19.0"
|
||||
yargs "^17.3.1"
|
||||
|
||||
@ -17959,8 +17947,6 @@ path-case@^2.1.0:
|
||||
version "2.1.1"
|
||||
resolved "https://registry.yarnpkg.com/path-case/-/path-case-2.1.1.tgz#94b8037c372d3fe2906e465bb45e25d226e8eea5"
|
||||
integrity sha1-lLgDfDctP+KQbkZbtF4l0ibo7qU=
|
||||
dependencies:
|
||||
no-case "^2.2.0"
|
||||
|
||||
path-dirname@^1.0.0:
|
||||
version "1.0.2"
|
||||
@ -19018,17 +19004,17 @@ react-query@3.24.3:
|
||||
broadcast-channel "^3.4.1"
|
||||
match-sorter "^6.0.2"
|
||||
|
||||
react-redux@7.2.8:
|
||||
version "7.2.8"
|
||||
resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-7.2.8.tgz#a894068315e65de5b1b68899f9c6ee0923dd28de"
|
||||
integrity sha512-6+uDjhs3PSIclqoCk0kd6iX74gzrGc3W5zcAjbrFgEdIjRSQObdIwfx80unTkVUYvbQ95Y8Av3OvFHq1w5EOUw==
|
||||
react-redux@8.0.5:
|
||||
version "8.0.5"
|
||||
resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-8.0.5.tgz#e5fb8331993a019b8aaf2e167a93d10af469c7bd"
|
||||
integrity sha512-Q2f6fCKxPFpkXt1qNRZdEDLlScsDWyrgSj0mliK59qU6W5gvBiKkdMEG2lJzhd1rCctf0hb6EtePPLZ2e0m1uw==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.15.4"
|
||||
"@types/react-redux" "^7.1.20"
|
||||
"@babel/runtime" "^7.12.1"
|
||||
"@types/hoist-non-react-statics" "^3.3.1"
|
||||
"@types/use-sync-external-store" "^0.0.3"
|
||||
hoist-non-react-statics "^3.3.2"
|
||||
loose-envify "^1.4.0"
|
||||
prop-types "^15.7.2"
|
||||
react-is "^17.0.2"
|
||||
react-is "^18.0.0"
|
||||
use-sync-external-store "^1.0.0"
|
||||
|
||||
react-refresh@0.14.0:
|
||||
version "0.14.0"
|
||||
@ -19326,13 +19312,20 @@ redent@^3.0.0:
|
||||
indent-string "^4.0.0"
|
||||
strip-indent "^3.0.0"
|
||||
|
||||
redux@^4.0.0, redux@^4.0.1, redux@^4.1.1, redux@^4.1.2:
|
||||
redux@^4.1.1, redux@^4.1.2:
|
||||
version "4.2.0"
|
||||
resolved "https://registry.yarnpkg.com/redux/-/redux-4.2.0.tgz#46f10d6e29b6666df758780437651eeb2b969f13"
|
||||
integrity sha512-oSBmcKKIuIR4ME29/AeNUnl5L+hvBq7OaJWzaptTQJAntaPvxIJqfnjbaEiCzzaIz+XmVILfqAM3Ob0aXLPfjA==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.9.2"
|
||||
|
||||
redux@^4.2.1:
|
||||
version "4.2.1"
|
||||
resolved "https://registry.yarnpkg.com/redux/-/redux-4.2.1.tgz#c08f4306826c49b5e9dc901dee0452ea8fce6197"
|
||||
integrity sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.9.2"
|
||||
|
||||
regenerate-unicode-properties@^10.0.1:
|
||||
version "10.0.1"
|
||||
resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-10.0.1.tgz#7f442732aa7934a3740c779bb9b3340dccc1fb56"
|
||||
@ -20776,13 +20769,18 @@ streamsearch@0.1.2:
|
||||
resolved "https://registry.yarnpkg.com/streamsearch/-/streamsearch-0.1.2.tgz#808b9d0e56fc273d809ba57338e929919a1a9f1a"
|
||||
integrity sha512-jos8u++JKm0ARcSUTAZXOVC0mSox7Bhn6sBgty73P1f3JGf7yG2clTbBNHUdde/kdvP2FESam+vM6l8jBrNxHA==
|
||||
|
||||
strict-event-emitter@^0.2.4, strict-event-emitter@^0.2.6:
|
||||
strict-event-emitter@^0.2.4:
|
||||
version "0.2.8"
|
||||
resolved "https://registry.yarnpkg.com/strict-event-emitter/-/strict-event-emitter-0.2.8.tgz#b4e768927c67273c14c13d20e19d5e6c934b47ca"
|
||||
integrity sha512-KDf/ujU8Zud3YaLtMCcTI4xkZlZVIYxTLr+XIULexP+77EEVWixeXroLUXQXiVtH4XH2W7jr/3PT1v3zBuvc3A==
|
||||
dependencies:
|
||||
events "^3.3.0"
|
||||
|
||||
strict-event-emitter@^0.4.3:
|
||||
version "0.4.6"
|
||||
resolved "https://registry.yarnpkg.com/strict-event-emitter/-/strict-event-emitter-0.4.6.tgz#ff347c8162b3e931e3ff5f02cfce6772c3b07eb3"
|
||||
integrity sha512-12KWeb+wixJohmnwNFerbyiBrAlq5qJLwIt38etRtKtmmHyDSoGlIqFE9wx+4IwG0aDjI7GV8tc8ZccjWZZtTg==
|
||||
|
||||
string-argv@^0.3.1, string-argv@~0.3.1:
|
||||
version "0.3.1"
|
||||
resolved "https://registry.yarnpkg.com/string-argv/-/string-argv-0.3.1.tgz#95e2fbec0427ae19184935f816d74aaa4c5c19da"
|
||||
@ -21738,7 +21736,7 @@ tunnel-agent@^0.6.0:
|
||||
dependencies:
|
||||
safe-buffer "^5.0.1"
|
||||
|
||||
tunnel@0.0.6, tunnel@^0.0.6:
|
||||
tunnel@^0.0.6:
|
||||
version "0.0.6"
|
||||
resolved "https://registry.yarnpkg.com/tunnel/-/tunnel-0.0.6.tgz#72f1314b34a5b192db012324df2cc587ca47f92c"
|
||||
integrity sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==
|
||||
@ -22168,6 +22166,11 @@ use-isomorphic-layout-effect@^1.1.2:
|
||||
resolved "https://registry.yarnpkg.com/use-isomorphic-layout-effect/-/use-isomorphic-layout-effect-1.1.2.tgz#497cefb13d863d687b08477d9e5a164ad8c1a6fb"
|
||||
integrity sha512-49L8yCO3iGT/ZF9QttjwLF/ZD9Iwto5LnH5LmEdk/6cFmXddqi2ulF0edxTwjj+7mqvpVVGQWvbXZdn32wRSHA==
|
||||
|
||||
use-sync-external-store@^1.0.0:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz#7dbefd6ef3fe4e767a0cf5d7287aacfb5846928a"
|
||||
integrity sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==
|
||||
|
||||
use@^3.1.0:
|
||||
version "3.1.1"
|
||||
resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f"
|
||||
|
Loading…
x
Reference in New Issue
Block a user