@wordpress/block-library
Version:
Block library for the WordPress editor.
1,000 lines (854 loc) • 33 kB
JavaScript
/**
* External dependencies
*/
import {
act,
addBlock,
dismissModal,
getBlock,
typeInRichText,
fireEvent,
getEditorHtml,
initializeEditor,
render,
setupCoreBlocks,
triggerBlockListLayout,
waitFor,
within,
withFakeTimers,
waitForElementToBeRemoved,
waitForModalVisible,
} from 'test/helpers';
import Clipboard from '@react-native-clipboard/clipboard';
import TextInputState from 'react-native/Libraries/Components/TextInput/TextInputState';
/**
* WordPress dependencies
*/
import { BACKSPACE, ENTER } from '@wordpress/keycodes';
/**
* Internal dependencies
*/
import Paragraph from '../edit';
// Mock debounce to prevent potentially belated state updates.
jest.mock( '@wordpress/compose/src/utils/debounce', () => ( {
debounce: ( fn ) => {
fn.cancel = jest.fn();
return fn;
},
} ) );
// Mock link suggestions that are fetched by the link picker
// when typing a search query.
jest.mock( '@wordpress/core-data/src/fetch', () => ( {
__experimentalFetchLinkSuggestions: jest.fn().mockResolvedValue( [ {} ] ),
} ) );
setupCoreBlocks();
const getTestComponentWithContent = ( content ) => {
return render(
<Paragraph
attributes={ { content } }
setAttributes={ jest.fn() }
onReplace={ jest.fn() }
insertBlocksAfter={ jest.fn() }
/>
);
};
describe( 'Paragraph block', () => {
it( 'should render without crashing and match snapshot', () => {
const screen = getTestComponentWithContent( '' );
expect( screen.toJSON() ).toMatchSnapshot();
} );
it( 'should prevent deleting the first Paragraph block when pressing backspace at the start', async () => {
// Arrange
const screen = await initializeEditor();
await addBlock( screen, 'Paragraph' );
// Act
const paragraphBlock = getBlock( screen, 'Paragraph' );
fireEvent.press( paragraphBlock );
const paragraphTextInput =
within( paragraphBlock ).getByPlaceholderText( 'Start writing…' );
typeInRichText(
paragraphTextInput,
'A quick brown fox jumps over the lazy dog.',
{ finalSelectionStart: 0, finalSelectionEnd: 0 }
);
fireEvent( paragraphTextInput, 'onKeyDown', {
nativeEvent: {},
preventDefault() {},
keyCode: BACKSPACE,
} );
// Assert
expect( getEditorHtml() ).toMatchSnapshot();
} );
it( 'should be able to use a prefix to create a Heading block', async () => {
const screen = await initializeEditor();
await addBlock( screen, 'Paragraph' );
const text = '# ';
const paragraphBlock = getBlock( screen, 'Paragraph' );
fireEvent.press( paragraphBlock );
const paragraphTextInput =
within( paragraphBlock ).getByPlaceholderText( 'Start writing…' );
typeInRichText( paragraphTextInput, text, {
finalSelectionStart: 1,
finalSelectionEnd: 1,
} );
fireEvent( paragraphTextInput, 'onChange', {
nativeEvent: { text },
preventDefault() {},
} );
const headingBlock = getBlock( screen, 'Heading' );
expect( headingBlock ).toBeVisible();
expect( getEditorHtml() ).toMatchSnapshot();
} );
it( 'should be able to use a prefix to create a Quote block', async () => {
const screen = await initializeEditor();
await addBlock( screen, 'Paragraph' );
const text = '> ';
const paragraphBlock = getBlock( screen, 'Paragraph' );
fireEvent.press( paragraphBlock );
const paragraphTextInput =
within( paragraphBlock ).getByPlaceholderText( 'Start writing…' );
typeInRichText( paragraphTextInput, text, {
finalSelectionStart: 1,
finalSelectionEnd: 1,
} );
fireEvent( paragraphTextInput, 'onChange', {
nativeEvent: { text },
preventDefault() {},
} );
const quoteBlock = getBlock( screen, 'Quote' );
await triggerBlockListLayout( quoteBlock );
expect( quoteBlock ).toBeVisible();
expect( getEditorHtml() ).toMatchSnapshot();
} );
it( 'should be able to use a prefix to create a List block', async () => {
const screen = await initializeEditor();
await addBlock( screen, 'Paragraph' );
const text = '- ';
const paragraphBlock = getBlock( screen, 'Paragraph' );
fireEvent.press( paragraphBlock );
const paragraphTextInput =
within( paragraphBlock ).getByPlaceholderText( 'Start writing…' );
typeInRichText( paragraphTextInput, text, {
finalSelectionStart: 1,
finalSelectionEnd: 1,
} );
fireEvent( paragraphTextInput, 'onChange', {
nativeEvent: { text },
preventDefault() {},
} );
const listBlock = getBlock( screen, 'List' );
await triggerBlockListLayout( listBlock );
expect( listBlock ).toBeVisible();
expect( getEditorHtml() ).toMatchSnapshot();
} );
it( 'should be able to use a prefix to create a numbered List block', async () => {
const screen = await initializeEditor();
await addBlock( screen, 'Paragraph' );
const text = '1. ';
const paragraphBlock = getBlock( screen, 'Paragraph' );
fireEvent.press( paragraphBlock );
const paragraphTextInput =
within( paragraphBlock ).getByPlaceholderText( 'Start writing…' );
typeInRichText( paragraphTextInput, text, {
finalSelectionStart: 2,
finalSelectionEnd: 2,
} );
fireEvent( paragraphTextInput, 'onChange', {
nativeEvent: { text },
preventDefault() {},
} );
const listBlock = getBlock( screen, 'List' );
await triggerBlockListLayout( listBlock );
expect( listBlock ).toBeVisible();
expect( getEditorHtml() ).toMatchSnapshot();
} );
it( 'should bold text', async () => {
// Arrange
const screen = await initializeEditor();
await addBlock( screen, 'Paragraph' );
// Act
const paragraphBlock = getBlock( screen, 'Paragraph' );
fireEvent.press( paragraphBlock );
const paragraphTextInput =
within( paragraphBlock ).getByPlaceholderText( 'Start writing…' );
typeInRichText(
paragraphTextInput,
'A quick brown fox jumps over the lazy dog.',
{ finalSelectionStart: 2, finalSelectionEnd: 7 }
);
fireEvent.press( screen.getByLabelText( 'Bold' ) );
// Assert
expect( getEditorHtml() ).toMatchInlineSnapshot( `
"<!-- wp:paragraph -->
<p>A <strong>quick</strong> brown fox jumps over the lazy dog.</p>
<!-- /wp:paragraph -->"
` );
} );
it( 'should italicize text', async () => {
// Arrange
const screen = await initializeEditor();
await addBlock( screen, 'Paragraph' );
// Act
const paragraphBlock = getBlock( screen, 'Paragraph' );
fireEvent.press( paragraphBlock );
const paragraphTextInput =
within( paragraphBlock ).getByPlaceholderText( 'Start writing…' );
typeInRichText(
paragraphTextInput,
'A quick brown fox jumps over the lazy dog.',
{ finalSelectionStart: 2, finalSelectionEnd: 7 }
);
fireEvent.press( screen.getByLabelText( 'Italic' ) );
// Assert
expect( getEditorHtml() ).toMatchInlineSnapshot( `
"<!-- wp:paragraph -->
<p>A <em>quick</em> brown fox jumps over the lazy dog.</p>
<!-- /wp:paragraph -->"
` );
} );
it( 'should strikethrough text', async () => {
// Arrange
const screen = await initializeEditor();
await addBlock( screen, 'Paragraph' );
// Act
const paragraphBlock = getBlock( screen, 'Paragraph' );
fireEvent.press( paragraphBlock );
const paragraphTextInput =
within( paragraphBlock ).getByPlaceholderText( 'Start writing…' );
typeInRichText(
paragraphTextInput,
'A quick brown fox jumps over the lazy dog.',
{ finalSelectionStart: 2, finalSelectionEnd: 7 }
);
fireEvent.press( screen.getByLabelText( 'Strikethrough' ) );
// Assert
expect( getEditorHtml() ).toMatchInlineSnapshot( `
"<!-- wp:paragraph -->
<p>A <s>quick</s> brown fox jumps over the lazy dog.</p>
<!-- /wp:paragraph -->"
` );
} );
it( 'should left align text', async () => {
// Arrange
const screen = await initializeEditor();
await addBlock( screen, 'Paragraph' );
// Act
const paragraphBlock = getBlock( screen, 'Paragraph' );
fireEvent.press( paragraphBlock );
const paragraphTextInput =
within( paragraphBlock ).getByPlaceholderText( 'Start writing…' );
typeInRichText(
paragraphTextInput,
'A quick brown fox jumps over the lazy dog.'
);
fireEvent.press( screen.getByLabelText( 'Align text' ) );
fireEvent.press( screen.getByLabelText( 'Align text left' ) );
// Assert
expect( getEditorHtml() ).toMatchInlineSnapshot( `
"<!-- wp:paragraph {"align":"left"} -->
<p class="has-text-align-left">A quick brown fox jumps over the lazy dog.</p>
<!-- /wp:paragraph -->"
` );
} );
it( 'should center align text', async () => {
// Arrange
const screen = await initializeEditor();
await addBlock( screen, 'Paragraph' );
// Act
const paragraphBlock = getBlock( screen, 'Paragraph' );
fireEvent.press( paragraphBlock );
const paragraphTextInput =
within( paragraphBlock ).getByPlaceholderText( 'Start writing…' );
typeInRichText(
paragraphTextInput,
'A quick brown fox jumps over the lazy dog.'
);
fireEvent.press( screen.getByLabelText( 'Align text' ) );
fireEvent.press( screen.getByLabelText( 'Align text center' ) );
// Assert
expect( getEditorHtml() ).toMatchInlineSnapshot( `
"<!-- wp:paragraph {"align":"center"} -->
<p class="has-text-align-center">A quick brown fox jumps over the lazy dog.</p>
<!-- /wp:paragraph -->"
` );
} );
it( 'should right align text', async () => {
// Arrange
const screen = await initializeEditor();
await addBlock( screen, 'Paragraph' );
// Act
const paragraphBlock = getBlock( screen, 'Paragraph' );
fireEvent.press( paragraphBlock );
const paragraphTextInput =
within( paragraphBlock ).getByPlaceholderText( 'Start writing…' );
typeInRichText(
paragraphTextInput,
'A quick brown fox jumps over the lazy dog.'
);
fireEvent.press( screen.getByLabelText( 'Align text' ) );
fireEvent.press( screen.getByLabelText( 'Align text right' ) );
// Assert
expect( getEditorHtml() ).toMatchInlineSnapshot( `
"<!-- wp:paragraph {"align":"right"} -->
<p class="has-text-align-right">A quick brown fox jumps over the lazy dog.</p>
<!-- /wp:paragraph -->"
` );
} );
it( 'should inherit parent alignment', async () => {
// Arrange
const screen = await initializeEditor();
await addBlock( screen, 'Quote' );
await triggerBlockListLayout( getBlock( screen, 'Quote' ) );
// Act
const paragraphBlock = getBlock( screen, 'Paragraph' );
fireEvent.press( paragraphBlock );
const paragraphTextInput =
within( paragraphBlock ).getByPlaceholderText( 'Start writing…' );
typeInRichText(
paragraphTextInput,
'A quick brown fox jumps over the lazy dog.'
);
fireEvent.press( screen.getByLabelText( 'Navigate Up' ) );
fireEvent.press( screen.getByLabelText( 'Align text' ) );
fireEvent.press( screen.getByLabelText( 'Align text right' ) );
// Assert
// This not an ideal assertion, as it relies implementation details of the
// component: prop names. However, the only aspect we can assert is the prop
// passed to Aztec, the native module controlling visual alignment. A less
// brittle alternative might be snapshotting, but RNTL does not yet support
// focused snapshots, which means the snapshot would be huge.
// https://github.com/facebook/react/pull/25329
expect(
screen.UNSAFE_queryAllByProps( {
value: '<p>A quick brown fox jumps over the lazy dog.</p>',
placeholder: 'Start writing…',
textAlign: 'right',
} ).length
).toBe( 2 ); // One for Aztec mock, one for the TextInput.
} );
it( 'should preserve alignment when split', async () => {
// Arrange
const screen = await initializeEditor();
await addBlock( screen, 'Paragraph' );
// Act
const paragraphBlock = getBlock( screen, 'Paragraph' );
fireEvent.press( paragraphBlock );
fireEvent.press( screen.getByLabelText( 'Align text' ) );
fireEvent.press( screen.getByLabelText( 'Align text center' ) );
const paragraphTextInput =
within( paragraphBlock ).getByPlaceholderText( 'Start writing…' );
const string = 'A quick brown fox jumps over the lazy dog.';
typeInRichText( paragraphTextInput, string, {
finalSelectionStart: string.length / 2,
finalSelectionEnd: string.length / 2,
} );
fireEvent( paragraphTextInput, 'onKeyDown', {
nativeEvent: {},
preventDefault() {},
keyCode: ENTER,
} );
// Assert
expect( getEditorHtml() ).toMatchInlineSnapshot( `
"<!-- wp:paragraph {"align":"center"} -->
<p class="has-text-align-center">A quick brown fox jum</p>
<!-- /wp:paragraph -->
<!-- wp:paragraph {"align":"center"} -->
<p class="has-text-align-center">ps over the lazy dog.</p>
<!-- /wp:paragraph -->"
` );
} );
it( 'should link text without selection', async () => {
// Arrange
const screen = await initializeEditor();
await addBlock( screen, 'Paragraph' );
// Act
const paragraphBlock = getBlock( screen, 'Paragraph' );
fireEvent.press( paragraphBlock );
fireEvent.press( screen.getByLabelText( 'Link' ) );
fireEvent.changeText(
screen.getByPlaceholderText( 'Add link text' ),
'WordPress'
);
fireEvent.press(
screen.getByLabelText( 'Link to, Search or type URL' )
);
const typeURLInput = await waitFor( () =>
screen.getByPlaceholderText( 'Search or type URL' )
);
fireEvent.changeText( typeURLInput, 'wordpress.org' );
await waitForElementToBeRemoved( () =>
screen.getByTestId( 'link-picker-loading' )
);
// Back navigation from link picker uses `setTimeout`
await withFakeTimers( () => {
fireEvent.press( screen.getByLabelText( 'Apply' ) );
act( () => jest.runOnlyPendingTimers() );
} );
// Assert
expect( getEditorHtml() ).toMatchInlineSnapshot( `
"<!-- wp:paragraph -->
<p><a href="http://wordpress.org">WordPress</a></p>
<!-- /wp:paragraph -->"
` );
} );
it( 'should link text with selection', async () => {
// Arrange
const screen = await initializeEditor();
await addBlock( screen, 'Paragraph' );
// Act
const paragraphBlock = getBlock( screen, 'Paragraph' );
fireEvent.press( paragraphBlock );
const paragraphTextInput =
within( paragraphBlock ).getByPlaceholderText( 'Start writing…' );
typeInRichText(
paragraphTextInput,
'A quick brown fox jumps over the lazy dog.',
{
finalSelectionStart: 2,
finalSelectionEnd: 7,
}
);
fireEvent.press( screen.getByLabelText( 'Link' ) );
fireEvent.press(
screen.getByLabelText( 'Link to, Search or type URL' )
);
const typeURLInput = await waitFor( () =>
screen.getByPlaceholderText( 'Search or type URL' )
);
fireEvent.changeText( typeURLInput, 'wordpress.org' );
await waitForElementToBeRemoved( () =>
screen.getByTestId( 'link-picker-loading' )
);
// Back navigation from link picker uses `setTimeout`
await withFakeTimers( () => {
fireEvent.press( screen.getByLabelText( 'Apply' ) );
act( () => jest.runOnlyPendingTimers() );
} );
// Assert
expect( getEditorHtml() ).toMatchInlineSnapshot( `
"<!-- wp:paragraph -->
<p>A <a href="http://wordpress.org">quick</a> brown fox jumps over the lazy dog.</p>
<!-- /wp:paragraph -->"
` );
} );
it( 'should link text with clipboard contents', async () => {
// Arrange
Clipboard.getString.mockResolvedValue( 'https://wordpress.org' );
const screen = await initializeEditor();
await addBlock( screen, 'Paragraph' );
// Act
const paragraphBlock = getBlock( screen, 'Paragraph' );
fireEvent.press( paragraphBlock );
const paragraphTextInput =
within( paragraphBlock ).getByPlaceholderText( 'Start writing…' );
typeInRichText(
paragraphTextInput,
'A quick brown fox jumps over the lazy dog.',
{
finalSelectionStart: 2,
finalSelectionEnd: 7,
}
);
// Await React Navigation: https://github.com/WordPress/gutenberg/issues/35685#issuecomment-961919931
await act( () => fireEvent.press( screen.getByLabelText( 'Link' ) ) );
// Await React Navigation: https://github.com/WordPress/gutenberg/issues/35685#issuecomment-961919931
await act( () =>
fireEvent.press(
screen.getByLabelText( 'Link to, Search or type URL' )
)
);
// Assert
expect( getEditorHtml() ).toMatchInlineSnapshot( `
"<!-- wp:paragraph -->
<p>A <a href="https://wordpress.org">quick</a> brown fox jumps over the lazy dog.</p>
<!-- /wp:paragraph -->"
` );
Clipboard.getString.mockReset();
} );
it( 'should not remove leading or trailing whitespace when formatting', async () => {
// Arrange
const screen = await initializeEditor();
await addBlock( screen, 'Paragraph' );
// Act
const paragraphBlock = getBlock( screen, 'Paragraph' );
fireEvent.press( paragraphBlock );
const paragraphTextInput =
within( paragraphBlock ).getByPlaceholderText( 'Start writing…' );
typeInRichText( paragraphTextInput, ' some text ', {
finalSelectionStart: 5,
finalSelectionEnd: 14,
} );
fireEvent.press( screen.getByLabelText( 'Italic' ) );
// Assert
expect( getEditorHtml() ).toMatchInlineSnapshot( `
"<!-- wp:paragraph -->
<p> <em>some text</em> </p>
<!-- /wp:paragraph -->"
` );
} );
it( 'should set a text color', async () => {
// Arrange
const screen = await initializeEditor();
await addBlock( screen, 'Paragraph' );
// Act
const paragraphBlock = getBlock( screen, 'Paragraph' );
fireEvent.press( paragraphBlock );
const paragraphTextInput =
within( paragraphBlock ).getByPlaceholderText( 'Start writing…' );
typeInRichText(
paragraphTextInput,
'A quick brown fox jumps over the lazy dog.'
);
// Open Block Settings.
fireEvent.press( screen.getByLabelText( 'Open Settings' ) );
// Wait for Block Settings to be visible.
const blockSettingsModal = screen.getByTestId( 'block-settings-modal' );
await waitFor( () => blockSettingsModal.props.isVisible );
// Open Text color settings
fireEvent.press( screen.getByLabelText( 'Text, Default' ) );
// Tap one color
fireEvent.press( screen.getByLabelText( 'Pale pink' ) );
// TODO(jest-console): Fix the warning and remove the expect below.
expect( console ).toHaveWarnedWith(
`Non-serializable values were found in the navigation state. Check:\n\nColor > params.onColorChange (Function)\n\nThis can break usage such as persisting and restoring state. This might happen if you passed non-serializable values such as function, class instances etc. in params. If you need to use components with callbacks in your options, you can use 'navigation.setOptions' instead. See https://reactnavigation.org/docs/troubleshooting#i-get-the-warning-non-serializable-values-were-found-in-the-navigation-state for more details.`
);
// Dismiss the Block Settings modal.
fireEvent( blockSettingsModal, 'backdropPress' );
// Assert
expect( getEditorHtml() ).toMatchInlineSnapshot( `
"<!-- wp:paragraph {"textColor":"pale-pink"} -->
<p class="has-pale-pink-color has-text-color">A quick brown fox jumps over the lazy dog.</p>
<!-- /wp:paragraph -->"
` );
} );
it( 'should set a background color', async () => {
// Arrange
const screen = await initializeEditor();
await addBlock( screen, 'Paragraph' );
// Act
const paragraphBlock = getBlock( screen, 'Paragraph' );
fireEvent.press( paragraphBlock );
const paragraphTextInput =
within( paragraphBlock ).getByPlaceholderText( 'Start writing…' );
typeInRichText(
paragraphTextInput,
'A quick brown fox jumps over the lazy dog.'
);
// Open Block Settings.
fireEvent.press( screen.getByLabelText( 'Open Settings' ) );
// Wait for Block Settings to be visible.
const blockSettingsModal = screen.getByTestId( 'block-settings-modal' );
await waitFor( () => blockSettingsModal.props.isVisible );
// Open Background color settings
fireEvent.press( screen.getByLabelText( 'Background, Default' ) );
// Tap one color
fireEvent.press( screen.getByLabelText( 'Luminous vivid orange' ) );
// Dismiss the Block Settings modal.
fireEvent( blockSettingsModal, 'backdropPress' );
// Assert
expect( getEditorHtml() ).toMatchInlineSnapshot( `
"<!-- wp:paragraph {"backgroundColor":"luminous-vivid-orange"} -->
<p class="has-luminous-vivid-orange-background-color has-background">A quick brown fox jumps over the lazy dog.</p>
<!-- /wp:paragraph -->"
` );
} );
it( 'should set a text and background color', async () => {
// Arrange
const screen = await initializeEditor();
await addBlock( screen, 'Paragraph' );
// Act
const paragraphBlock = getBlock( screen, 'Paragraph' );
fireEvent.press( paragraphBlock );
const paragraphTextInput =
within( paragraphBlock ).getByPlaceholderText( 'Start writing…' );
typeInRichText(
paragraphTextInput,
'A quick brown fox jumps over the lazy dog.'
);
// Open Block Settings.
fireEvent.press( screen.getByLabelText( 'Open Settings' ) );
// Wait for Block Settings to be visible.
const blockSettingsModal = screen.getByTestId( 'block-settings-modal' );
await waitFor( () => blockSettingsModal.props.isVisible );
// Open Text color settings
fireEvent.press( screen.getByLabelText( 'Text, Default' ) );
// Tap one color
fireEvent.press( screen.getByLabelText( 'White' ) );
// Go back to the settings menu
fireEvent.press( screen.getByLabelText( 'Go back' ) );
// Open Background color settings
fireEvent.press( screen.getByLabelText( 'Background, Default' ) );
// Tap one color
fireEvent.press( screen.getByLabelText( 'Luminous vivid orange' ) );
// Dismiss the Block Settings modal.
fireEvent( blockSettingsModal, 'backdropPress' );
// Assert
expect( getEditorHtml() ).toMatchInlineSnapshot( `
"<!-- wp:paragraph {"backgroundColor":"luminous-vivid-orange","textColor":"white"} -->
<p class="has-white-color has-luminous-vivid-orange-background-color has-text-color has-background">A quick brown fox jumps over the lazy dog.</p>
<!-- /wp:paragraph -->"
` );
} );
it( 'should remove text and background colors', async () => {
// Arrange
const screen = await initializeEditor( {
initialHtml: `<!-- wp:paragraph {"backgroundColor":"luminous-vivid-orange","textColor":"white"} -->
<p class="has-white-color has-luminous-vivid-orange-background-color has-text-color has-background">A quick brown fox jumps over the lazy dog.</p>
<!-- /wp:paragraph -->`,
} );
// Act
const paragraphBlock = getBlock( screen, 'Paragraph' );
fireEvent.press( paragraphBlock );
// Open Block Settings.
fireEvent.press( screen.getByLabelText( 'Open Settings' ) );
// Wait for Block Settings to be visible.
const blockSettingsModal = screen.getByTestId( 'block-settings-modal' );
await waitFor( () => blockSettingsModal.props.isVisible );
// Open Text color settings
fireEvent.press( screen.getByLabelText( 'Text. Empty' ) );
// Reset color
fireEvent.press( await screen.findByText( 'Reset' ) );
// Go back to the settings menu
fireEvent.press( screen.getByLabelText( 'Go back' ) );
// Open Background color settings
fireEvent.press( screen.getByLabelText( 'Background. Empty' ) );
// Reset color
fireEvent.press( await screen.findByText( 'Reset' ) );
// Dismiss the Block Settings modal.
fireEvent( blockSettingsModal, 'backdropPress' );
// Assert
expect( getEditorHtml() ).toMatchInlineSnapshot( `
"<!-- wp:paragraph -->
<p>A quick brown fox jumps over the lazy dog.</p>
<!-- /wp:paragraph -->"
` );
} );
it( 'should not have a gradient background color option', async () => {
// Arrange
const screen = await initializeEditor();
await addBlock( screen, 'Paragraph' );
// Act
const paragraphBlock = getBlock( screen, 'Paragraph' );
fireEvent.press( paragraphBlock );
const paragraphTextInput =
within( paragraphBlock ).getByPlaceholderText( 'Start writing…' );
typeInRichText(
paragraphTextInput,
'A quick brown fox jumps over the lazy dog.'
);
// Open Block Settings.
fireEvent.press( screen.getByLabelText( 'Open Settings' ) );
// Wait for Block Settings to be visible.
const blockSettingsModal = screen.getByTestId( 'block-settings-modal' );
await waitFor( () => blockSettingsModal.props.isVisible );
// Open Background color settings
fireEvent.press( screen.getByLabelText( 'Background, Default' ) );
// Assert
const colorButton = screen.getByLabelText( 'Luminous vivid orange' );
expect( colorButton ).toBeDefined();
const gradientButton = screen.queryByLabelText( 'Gradient' );
expect( gradientButton ).toBeNull();
} );
it( 'should set a theme text color', async () => {
// Arrange
const screen = await initializeEditor( { withGlobalStyles: true } );
await addBlock( screen, 'Paragraph' );
// Act
const paragraphBlock = getBlock( screen, 'Paragraph' );
fireEvent.press( paragraphBlock );
const paragraphTextInput =
within( paragraphBlock ).getByPlaceholderText( 'Start writing…' );
typeInRichText(
paragraphTextInput,
'A quick brown fox jumps over the lazy dog.'
);
// Open Block Settings.
fireEvent.press( screen.getByLabelText( 'Open Settings' ) );
// Wait for Block Settings to be visible.
const blockSettingsModal = screen.getByTestId( 'block-settings-modal' );
await waitFor( () => blockSettingsModal.props.isVisible );
// Open Text color settings
fireEvent.press( screen.getByLabelText( 'Text, Default' ) );
// Tap one color
fireEvent.press( screen.getByLabelText( 'Tertiary' ) );
// Dismiss the Block Settings modal.
fireEvent( blockSettingsModal, 'backdropPress' );
// Assert
expect( getEditorHtml() ).toMatchInlineSnapshot( `
"<!-- wp:paragraph {"textColor":"tertiary"} -->
<p class="has-tertiary-color has-text-color">A quick brown fox jumps over the lazy dog.</p>
<!-- /wp:paragraph -->"
` );
} );
it( 'should show the contrast check warning', async () => {
// Arrange
const screen = await initializeEditor( {
initialHtml: `<!-- wp:paragraph {"backgroundColor":"white","textColor":"white"} -->
<p class="has-white-color has-white-background-color has-text-color has-background">A quick brown fox jumps over the lazy dog.</p>
<!-- /wp:paragraph -->`,
} );
// Act
const paragraphBlock = getBlock( screen, 'Paragraph' );
fireEvent.press( paragraphBlock );
// Open Block Settings.
fireEvent.press( screen.getByLabelText( 'Open Settings' ) );
// Wait for Block Settings to be visible.
const blockSettingsModal = screen.getByTestId( 'block-settings-modal' );
await waitFor( () => blockSettingsModal.props.isVisible );
// Assert
const contrastCheckElement = screen.getByText(
/This color combination/
);
expect( contrastCheckElement ).toBeDefined();
} );
it( 'should highlight text with selection', async () => {
// Arrange
const screen = await initializeEditor( { withGlobalStyles: true } );
await addBlock( screen, 'Paragraph' );
// Act
const paragraphBlock = getBlock( screen, 'Paragraph' );
fireEvent.press( paragraphBlock );
const paragraphTextInput =
within( paragraphBlock ).getByPlaceholderText( 'Start writing…' );
typeInRichText(
paragraphTextInput,
'A quick brown fox jumps over the lazy dog.',
{ finalSelectionStart: 2, finalSelectionEnd: 7 }
);
fireEvent.press( screen.getByLabelText( 'Text color' ) );
fireEvent.press( await screen.findByLabelText( 'Tertiary' ) );
// TODO(jest-console): Fix the warning and remove the expect below.
expect( console ).toHaveWarnedWith(
`Non-serializable values were found in the navigation state. Check:\n\ntext-color > Palette > params.onColorChange (Function)\n\nThis can break usage such as persisting and restoring state. This might happen if you passed non-serializable values such as function, class instances etc. in params. If you need to use components with callbacks in your options, you can use 'navigation.setOptions' instead. See https://reactnavigation.org/docs/troubleshooting#i-get-the-warning-non-serializable-values-were-found-in-the-navigation-state for more details.`
);
// Assert
expect( getEditorHtml() ).toMatchInlineSnapshot( `
"<!-- wp:paragraph -->
<p>A <mark style="background-color:rgba(0, 0, 0, 0);color:#2411a4" class="has-inline-color has-tertiary-color">quick</mark> brown fox jumps over the lazy dog.</p>
<!-- /wp:paragraph -->"
` );
} );
it( 'should show the expected font sizes values', async () => {
// Arrange
const screen = await initializeEditor( { withGlobalStyles: true } );
await addBlock( screen, 'Paragraph' );
// Act
const paragraphBlock = getBlock( screen, 'Paragraph' );
fireEvent.press( paragraphBlock );
const paragraphTextInput =
within( paragraphBlock ).getByPlaceholderText( 'Start writing…' );
typeInRichText(
paragraphTextInput,
'A quick brown fox jumps over the lazy dog.'
);
// Open Block Settings.
fireEvent.press( screen.getByLabelText( 'Open Settings' ) );
// Wait for Block Settings to be visible.
const blockSettingsModal = screen.getByTestId( 'block-settings-modal' );
await waitForModalVisible( blockSettingsModal );
// Open Font size settings
fireEvent.press( screen.getByLabelText( 'Font Size, Custom' ) );
await waitFor( () => screen.getByLabelText( 'Selected: Default' ) );
// Assert
const modalContent = within( blockSettingsModal );
expect( modalContent.getByLabelText( 'Small' ) ).toBeVisible();
expect( modalContent.getByText( '14px' ) ).toBeVisible();
expect( modalContent.getByLabelText( 'Medium' ) ).toBeVisible();
expect( modalContent.getByText( '17px' ) ).toBeVisible();
expect( modalContent.getByLabelText( 'Large' ) ).toBeVisible();
expect( modalContent.getByText( '30px' ) ).toBeVisible();
expect( modalContent.getByLabelText( 'Extra Large' ) ).toBeVisible();
expect( modalContent.getByText( '40px' ) ).toBeVisible();
expect(
modalContent.getByLabelText( 'Extra Extra Large' )
).toBeVisible();
expect( modalContent.getByText( '52px' ) ).toBeVisible();
} );
it( 'should set a font size value', async () => {
// Arrange
const screen = await initializeEditor( { withGlobalStyles: true } );
await addBlock( screen, 'Paragraph' );
// Act
const paragraphBlock = getBlock( screen, 'Paragraph' );
fireEvent.press( paragraphBlock );
const paragraphTextInput =
within( paragraphBlock ).getByPlaceholderText( 'Start writing…' );
typeInRichText(
paragraphTextInput,
'A quick brown fox jumps over the lazy dog.'
);
// Open Block Settings.
fireEvent.press( screen.getByLabelText( 'Open Settings' ) );
// Wait for Block Settings to be visible.
const blockSettingsModal = screen.getByTestId( 'block-settings-modal' );
await waitForModalVisible( blockSettingsModal );
// Open Font size settings
fireEvent.press( screen.getByLabelText( 'Font Size, Custom' ) );
// Tap one font size
fireEvent.press( screen.getByLabelText( 'Large' ) );
// Dismiss the Block Settings modal.
await dismissModal( blockSettingsModal );
// Assert
expect( getEditorHtml() ).toMatchSnapshot();
} );
it( 'should set a line height value', async () => {
// Arrange
const screen = await initializeEditor( { withGlobalStyles: true } );
await addBlock( screen, 'Paragraph' );
// Act
const paragraphBlock = getBlock( screen, 'Paragraph' );
fireEvent.press( paragraphBlock );
const paragraphTextInput =
within( paragraphBlock ).getByPlaceholderText( 'Start writing…' );
typeInRichText(
paragraphTextInput,
'A quick brown fox jumps over the lazy dog.'
);
// Open Block Settings.
fireEvent.press( screen.getByLabelText( 'Open Settings' ) );
// Wait for Block Settings to be visible.
const blockSettingsModal = screen.getByTestId( 'block-settings-modal' );
await waitForModalVisible( blockSettingsModal );
const lineHeightControl = screen.getByLabelText( /Line Height/ );
fireEvent.press(
within( lineHeightControl ).getByText( '1.5', { hidden: true } )
);
const lineHeightTextInput = within(
lineHeightControl
).getByDisplayValue( '1.5', { hidden: true } );
fireEvent.changeText( lineHeightTextInput, '1.8' );
// Dismiss the Block Settings modal.
await dismissModal( blockSettingsModal );
// Assert
expect( getEditorHtml() ).toMatchSnapshot();
} );
it( 'should focus on the previous Paragraph block when backspacing in an empty Paragraph block', async () => {
// Arrange
const screen = await initializeEditor();
await addBlock( screen, 'Paragraph' );
// Act
const paragraphBlock = getBlock( screen, 'Paragraph' );
fireEvent.press( paragraphBlock );
const paragraphTextInput =
within( paragraphBlock ).getByPlaceholderText( 'Start writing…' );
typeInRichText( paragraphTextInput, 'A quick brown fox jumps' );
await addBlock( screen, 'Paragraph' );
const secondParagraphBlock = getBlock( screen, 'Paragraph', {
rowIndex: 2,
} );
fireEvent.press( secondParagraphBlock );
// Clear mock history
TextInputState.focusTextInput.mockClear();
const secondParagraphTextInput =
within( secondParagraphBlock ).getByPlaceholderText(
'Start writing…'
);
fireEvent( secondParagraphTextInput, 'onKeyDown', {
nativeEvent: {},
preventDefault() {},
keyCode: BACKSPACE,
} );
// Assert
expect( TextInputState.focusTextInput ).toHaveBeenCalled();
} );
} );