Merge branch 'main' into tests/drop16

This commit is contained in:
Alexandre Bodin 2023-10-11 13:09:45 +02:00
commit af6ddbba9b
8 changed files with 102 additions and 56 deletions

View File

@ -6,6 +6,8 @@ inputs:
required: true
runEE:
description: 'Should run EE or CE tests'
jestOptions:
description: 'Jest options'
runs:
using: 'composite'
steps:
@ -13,4 +15,5 @@ runs:
env:
DB_OPTIONS: ${{ inputs.dbOptions }}
RUN_EE: ${{ inputs.runEE }}
JEST_OPTIONS: ${{ inputs.jestOptions }}
shell: bash

View File

@ -9,7 +9,8 @@ export ENV_PATH="$(pwd)/test-apps/api/.env"
export JWT_SECRET="aSecret"
opts=($DB_OPTIONS)
jestOptions=($JEST_OPTIONS)
yarn nx run-many --target=build --nx-ignore-cycles --skip-nx-cache
yarn run test:generate-app --appPath=test-apps/api "${opts[@]}"
yarn run test:api --no-generate-app
yarn run test:api --no-generate-app "${jestOptions[@]}"

View File

@ -181,10 +181,11 @@ jobs:
if: needs.changes.outputs.backend == 'true'
runs-on: ubuntu-latest
needs: [changes, lint, typescript, unit_back, unit_front]
name: '[CE] API Integration (postgres, node: ${{ matrix.node }})'
name: '[CE] API Integration (postgres, node: ${{ matrix.node }}, shard: ${{ matrix.shard }})'
strategy:
matrix:
node: [18, 20]
shard: [1/2, 2/2]
services:
postgres:
# Docker Hub image
@ -214,16 +215,18 @@ jobs:
- uses: ./.github/actions/run-api-tests
with:
dbOptions: '--dbclient=postgres --dbhost=localhost --dbport=5432 --dbname=strapi_test --dbusername=strapi --dbpassword=strapi'
jestOptions: '--shard=${{ matrix.shard }}'
api_ce_mysql:
if: needs.changes.outputs.backend == 'true'
runs-on: ubuntu-latest
needs: [changes, lint, typescript, unit_back, unit_front]
name: '[CE] API Integration (mysql:latest, client: ${{ matrix.db_client }}, node: ${{ matrix.node }})'
name: '[CE] API Integration (mysql:latest, client: ${{ matrix.db_client }}, node: ${{ matrix.node }}, shard: ${{ matrix.shard }})'
strategy:
matrix:
node: [18, 20]
db_client: ['mysql', 'mysql2']
shard: [1/2, 2/2]
services:
mysql:
image: bitnami/mysql:latest
@ -251,16 +254,18 @@ jobs:
- uses: ./.github/actions/run-api-tests
with:
dbOptions: '--dbclient=${{ matrix.db_client }} --dbhost=localhost --dbport=3306 --dbname=strapi_test --dbusername=strapi --dbpassword=strapi'
jestOptions: '--shard=${{ matrix.shard }}'
api_ce_mysql_5:
if: needs.changes.outputs.backend == 'true'
runs-on: ubuntu-latest
needs: [changes, lint, typescript, unit_back, unit_front]
name: '[CE] API Integration (mysql:5, client: ${{ matrix.db_client }} , node: ${{ matrix.node }})'
name: '[CE] API Integration (mysql:5, client: ${{ matrix.db_client }} , node: ${{ matrix.node }}, shard: ${{ matrix.shard }})'
strategy:
matrix:
node: [18, 20]
db_client: ['mysql', 'mysql2']
shard: [1/2, 2/2]
services:
mysql:
image: bitnami/mysql:5.7
@ -287,16 +292,18 @@ jobs:
- uses: ./.github/actions/run-api-tests
with:
dbOptions: '--dbclient=${{ matrix.db_client }} --dbhost=localhost --dbport=3306 --dbname=strapi_test --dbusername=strapi --dbpassword=strapi'
jestOptions: '--shard=${{ matrix.shard }}'
api_ce_sqlite:
if: needs.changes.outputs.backend == 'true'
runs-on: ubuntu-latest
needs: [changes, lint, typescript, unit_back, unit_front]
name: '[CE] API Integration (sqlite, client: ${{ matrix.sqlite_pkg }}, node: ${{ matrix.node }})'
name: '[CE] API Integration (sqlite, client: ${{ matrix.sqlite_pkg }}, node: ${{ matrix.node }}, shard: ${{ matrix.shard }})'
strategy:
matrix:
node: [18, 20]
sqlite_pkg: ['better-sqlite3', 'sqlite3']
shard: [1/2, 2/2]
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v3
@ -309,18 +316,20 @@ jobs:
SQLITE_PKG: ${{ matrix.sqlite_pkg }}
with:
dbOptions: '--dbclient=sqlite-legacy --dbfile=./tmp/data.db'
jestOptions: '--shard=${{ matrix.shard }}'
# EE
api_ee_pg:
runs-on: ubuntu-latest
needs: [changes, lint, typescript, unit_back, unit_front]
name: '[EE] API Integration (postgres, node: ${{ matrix.node }})'
name: '[EE] API Integration (postgres, node: ${{ matrix.node }}, shard: ${{ matrix.shard }})'
if: needs.changes.outputs.backend == 'true' && github.event.pull_request.head.repo.full_name == github.repository && !(github.actor == 'dependabot[bot]' || github.actor == 'dependabot-preview[bot]')
env:
STRAPI_LICENSE: ${{ secrets.strapiLicense }}
strategy:
matrix:
node: [18, 20]
shard: [1/2, 2/2]
services:
postgres:
# Docker Hub image
@ -351,11 +360,12 @@ jobs:
with:
dbOptions: '--dbclient=postgres --dbhost=localhost --dbport=5432 --dbname=strapi_test --dbusername=strapi --dbpassword=strapi'
runEE: true
jestOptions: '--shard=${{ matrix.shard }}'
api_ee_mysql:
runs-on: ubuntu-latest
needs: [changes, lint, typescript, unit_back, unit_front]
name: '[EE] API Integration (mysql:latest, client: ${{ matrix.db_client }}, node: ${{ matrix.node }})'
name: '[EE] API Integration (mysql:latest, client: ${{ matrix.db_client }}, node: ${{ matrix.node }}, shard: ${{ matrix.shard }})'
if: needs.changes.outputs.backend == 'true' && github.event.pull_request.head.repo.full_name == github.repository && !(github.actor == 'dependabot[bot]' || github.actor == 'dependabot-preview[bot]')
env:
STRAPI_LICENSE: ${{ secrets.strapiLicense }}
@ -363,6 +373,7 @@ jobs:
matrix:
node: [18, 20]
db_client: ['mysql', 'mysql2']
shard: [1/2, 2/2]
services:
mysql:
image: bitnami/mysql:latest
@ -391,11 +402,12 @@ jobs:
with:
dbOptions: '--dbclient=${{ matrix.db_client }} --dbhost=localhost --dbport=3306 --dbname=strapi_test --dbusername=strapi --dbpassword=strapi'
runEE: true
jestOptions: '--shard=${{ matrix.shard }}'
api_ee_sqlite:
runs-on: ubuntu-latest
needs: [changes, lint, typescript, unit_back, unit_front]
name: '[EE] API Integration (sqlite, client: ${{ matrix.sqlite_pkg }}, node: ${{ matrix.node }})'
name: '[EE] API Integration (sqlite, client: ${{ matrix.sqlite_pkg }}, node: ${{ matrix.node }}, shard: ${{ matrix.shard }})'
if: needs.changes.outputs.backend == 'true' && github.event.pull_request.head.repo.full_name == github.repository && !(github.actor == 'dependabot[bot]' || github.actor == 'dependabot-preview[bot]')
env:
STRAPI_LICENSE: ${{ secrets.strapiLicense }}
@ -403,6 +415,7 @@ jobs:
matrix:
node: [18, 20]
sqlite_pkg: ['better-sqlite3', 'sqlite3']
shard: [1/2, 2/2]
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v3
@ -416,3 +429,4 @@ jobs:
with:
dbOptions: '--dbclient=sqlite --dbfile=./tmp/data.db'
runEE: true
jestOptions: '--shard=${{ matrix.shard }}'

View File

@ -199,6 +199,8 @@ const ImageDialog = ({ handleClose }) => {
const MediaLibraryDialog = components['media-library'];
const insertImages = (images) => {
// Image node created using select or existing selection node needs to be deleted before adding new image nodes
Transforms.removeNodes(editor);
images.forEach((img) => {
const image = { type: 'image', image: img, children: [{ type: 'text', text: '' }] };
Transforms.insertNodes(editor, image);
@ -290,10 +292,7 @@ const BlocksDropdown = ({ disabled }) => {
* @param {string} optionKey - key of the heading selected
*/
const selectOption = (optionKey) => {
if (optionKey === 'image') {
// Image node created using select or existing selection node needs to be deleted before adding new image nodes
Transforms.removeNodes(editor);
} else if (['list-ordered', 'list-unordered'].includes(optionKey)) {
if (['list-ordered', 'list-unordered'].includes(optionKey)) {
// retrieve the list format
const listFormat = blocks[optionKey].value.format;
@ -302,7 +301,7 @@ const BlocksDropdown = ({ disabled }) => {
// toggle the list
toggleList(editor, isActive, listFormat);
} else {
} else if (optionKey !== 'image') {
toggleBlock(editor, blocks[optionKey].value);
}
@ -507,6 +506,31 @@ const LinkButton = ({ disabled }) => {
return Boolean(match);
};
const isLinkDisabled = () => {
// Always disabled when the whole editor is disabled
if (disabled) {
return true;
}
// Always enabled when there's no selection
if (!editor.selection) {
return false;
}
// Get the block node closest to the anchor and focus
const anchorNodeEntry = Editor.above(editor, {
at: editor.selection.anchor,
match: (node) => node.type !== 'text',
});
const focusNodeEntry = Editor.above(editor, {
at: editor.selection.focus,
match: (node) => node.type !== 'text',
});
// Disabled if the anchor and focus are not in the same block
return anchorNodeEntry[0] !== focusNodeEntry[0];
};
const addLink = () => {
// We insert an empty anchor, so we split the DOM to have a element we can use as reference for the popover
insertLink(editor, { url: '' });
@ -522,7 +546,7 @@ const LinkButton = ({ disabled }) => {
}}
isActive={isLinkActive()}
handleClick={addLink}
disabled={disabled}
disabled={isLinkDisabled()}
/>
);
};

View File

@ -32,7 +32,10 @@ const mixedInitialValue = [
{
type: 'heading',
level: 1,
children: [{ type: 'text', text: 'A heading one' }],
children: [
{ type: 'text', text: 'A heading one' },
{ type: 'text', text: ' with modifiers', bold: true },
],
},
{
type: 'paragraph',
@ -433,4 +436,25 @@ describe('BlocksEditor toolbar', () => {
const blocksDropdown = screen.getByRole('combobox', { name: /Select a block/i });
expect(within(blocksDropdown).getByText(/heading 1/i)).toBeInTheDocument();
});
it('should disable the link button when multiple blocks are selected', async () => {
setup(mixedInitialValue);
// Set the selection to cover the first and second
await select({
anchor: { path: [0, 0], offset: 0 },
focus: { path: [1, 0], offset: 0 },
});
const linkButton = screen.getByLabelText(/link/i);
expect(linkButton).toBeDisabled();
// Set the selection to a range inside the same block node
await select({
anchor: { path: [0, 0], offset: 0 },
focus: { path: [0, 1], offset: 2 },
});
expect(linkButton).not.toBeDisabled();
});
});

View File

@ -221,7 +221,7 @@ describe('useBlocksStore', () => {
const { result } = renderHook(useBlocksStore, { wrapper: Wrapper });
render(
result.current['heading-six'].renderElement({
result.current['heading-two'].renderElement({
children: 'Some heading',
element: { level: 2 },
attributes: {},

View File

@ -37,6 +37,10 @@ import styled, { css } from 'styled-components';
import { composeRefs } from '../../../utils';
import { editLink, removeLink } from '../utils/links';
const StyledBaseLink = styled(BaseLink)`
text-decoration: none;
`;
const H1 = styled(Typography).attrs({ as: 'h1' })`
font-size: ${42 / 16}rem;
line-height: ${({ theme }) => theme.lineHeights[1]};
@ -67,33 +71,6 @@ const H6 = styled(Typography).attrs({ as: 'h6' })`
line-height: ${({ theme }) => theme.lineHeights[1]};
`;
const Heading = ({ attributes, children, element }) => {
switch (element.level) {
case 1:
return <H1 {...attributes}>{children}</H1>;
case 2:
return <H2 {...attributes}>{children}</H2>;
case 3:
return <H3 {...attributes}>{children}</H3>;
case 4:
return <H4 {...attributes}>{children}</H4>;
case 5:
return <H5 {...attributes}>{children}</H5>;
case 6:
return <H6 {...attributes}>{children}</H6>;
default: // do nothing
return null;
}
};
Heading.propTypes = {
attributes: PropTypes.object.isRequired,
children: PropTypes.node.isRequired,
element: PropTypes.shape({
level: PropTypes.oneOf([1, 2, 3, 4, 5, 6]).isRequired,
}).isRequired,
};
const CodeBlock = styled.pre.attrs({ role: 'code' })`
border-radius: ${({ theme }) => theme.borderRadius};
background-color: ${({ theme }) => theme.colors.neutral100};
@ -113,8 +90,9 @@ const CodeBlock = styled.pre.attrs({ role: 'code' })`
const Blockquote = styled.blockquote.attrs({ role: 'blockquote' })`
margin: ${({ theme }) => `${theme.spaces[4]} 0`};
font-weight: ${({ theme }) => theme.fontWeights.regular};
border-left: ${({ theme }) => `${theme.spaces[1]} solid ${theme.colors.neutral150}`};
border-left: ${({ theme }) => `${theme.spaces[1]} solid ${theme.colors.neutral200}`};
padding: ${({ theme }) => theme.spaces[2]} ${({ theme }) => theme.spaces[5]};
color: ${({ theme }) => theme.colors.neutral600};
`;
const listStyle = css`
@ -320,7 +298,7 @@ const Link = React.forwardRef(({ element, children, ...attributes }, forwardedRe
return (
<>
<BaseLink
<StyledBaseLink
{...attributes}
ref={composedRefs}
href={element.url}
@ -328,7 +306,7 @@ const Link = React.forwardRef(({ element, children, ...attributes }, forwardedRe
color="primary600"
>
{children}
</BaseLink>
</StyledBaseLink>
{popoverOpen && (
<Popover source={linkRef} onDismiss={handleDismiss} padding={4} contentEditable={false}>
{isEditing ? (
@ -382,9 +360,11 @@ const Link = React.forwardRef(({ element, children, ...attributes }, forwardedRe
) : (
<Flex direction="column" gap={4} alignItems="start" width="400px">
<Typography>{elementText}</Typography>
<BaseLink href={element.url} target="_blank" color="primary600">
{element.url}
</BaseLink>
<Typography>
<StyledBaseLink href={element.url} target="_blank" color="primary600">
{element.url}
</StyledBaseLink>
</Typography>
<Flex justifyContent="end" width="100%" gap={2}>
<IconButton
icon={<Trash />}
@ -503,7 +483,7 @@ export function useBlocksStore() {
},
},
'heading-one': {
renderElement: (props) => <Heading {...props} />,
renderElement: (props) => <H1 {...props.attributes}>{props.children}</H1>,
icon: HeadingOne,
label: {
id: 'components.Blocks.blocks.heading1',
@ -517,7 +497,7 @@ export function useBlocksStore() {
isInBlocksSelector: true,
},
'heading-two': {
renderElement: (props) => <Heading {...props} />,
renderElement: (props) => <H2 {...props.attributes}>{props.children}</H2>,
icon: HeadingTwo,
label: {
id: 'components.Blocks.blocks.heading2',
@ -531,7 +511,7 @@ export function useBlocksStore() {
isInBlocksSelector: true,
},
'heading-three': {
renderElement: (props) => <Heading {...props} />,
renderElement: (props) => <H3 {...props.attributes}>{props.children}</H3>,
icon: HeadingThree,
label: {
id: 'components.Blocks.blocks.heading3',
@ -545,7 +525,7 @@ export function useBlocksStore() {
isInBlocksSelector: true,
},
'heading-four': {
renderElement: (props) => <Heading {...props} />,
renderElement: (props) => <H4 {...props.attributes}>{props.children}</H4>,
icon: HeadingFour,
label: {
id: 'components.Blocks.blocks.heading4',
@ -559,7 +539,7 @@ export function useBlocksStore() {
isInBlocksSelector: true,
},
'heading-five': {
renderElement: (props) => <Heading {...props} />,
renderElement: (props) => <H5 {...props.attributes}>{props.children}</H5>,
icon: HeadingFive,
label: {
id: 'components.Blocks.blocks.heading5',
@ -573,7 +553,7 @@ export function useBlocksStore() {
isInBlocksSelector: true,
},
'heading-six': {
renderElement: (props) => <Heading {...props} />,
renderElement: (props) => <H6 {...props.attributes}>{props.children}</H6>,
icon: HeadingSix,
label: {
id: 'components.Blocks.blocks.heading6',

View File

@ -133,7 +133,7 @@ const Notification = ({
},
onClose,
timeout = 2500,
title = 'success',
title,
type,
}: NotificationProps) => {
const { formatMessage } = useIntl();