From 78dab049633e0da2303709189486e9ed458c21de Mon Sep 17 00:00:00 2001 From: Carlos Garcia Date: Fri, 1 Dec 2023 19:34:54 +0100 Subject: [PATCH] Add raw handling tests This file is a clone of the same `blocks-raw-handling.js` file located in `gutenberg/test/integration`. The reason for the separation is that several of the test cases fail in the native version. For now, we are going to skip them, but we'd need to work on them in the future. Once all issues in tests are addressed, we'll remove this file in favor of the original one. --- .../blocks-raw-handling.native.js.snap | 73 +++ .../integration/blocks-raw-handling.native.js | 587 ++++++++++++++++++ 2 files changed, 660 insertions(+) create mode 100644 test/native/integration/__snapshots__/blocks-raw-handling.native.js.snap create mode 100644 test/native/integration/blocks-raw-handling.native.js diff --git a/test/native/integration/__snapshots__/blocks-raw-handling.native.js.snap b/test/native/integration/__snapshots__/blocks-raw-handling.native.js.snap new file mode 100644 index 00000000000000..4b0233e6c435f3 --- /dev/null +++ b/test/native/integration/__snapshots__/blocks-raw-handling.native.js.snap @@ -0,0 +1,73 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Blocks raw handling pasteHandler google-docs-list-only 1`] = `"
My first list item
A sub list item
A second sub list item
My second list item
My third list item"`; + +exports[`Blocks raw handling pasteHandler gutenberg 1`] = `"Test"`; + +exports[`Blocks raw handling pasteHandler iframe-embed 1`] = `""`; + +exports[`Blocks raw handling pasteHandler ms-word-styled 1`] = `"
Lorem ipsum dolor sit amet, consectetur adipiscing elit 


Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque aliquet hendrerit auctor. Nam lobortis, est vel lacinia tincidunt, purus tellus vehicula ex, nec pharetra justo dui sed lorem. Nam congue laoreet massa, quis varius est tincidunt ut."`; + +exports[`Blocks raw handling pasteHandler one-image 1`] = `""`; + +exports[`Blocks raw handling pasteHandler should remove extra blank lines 1`] = ` +" +

1

+ + + +

2

+" +`; + +exports[`Blocks raw handling pasteHandler should strip HTML formatting space from inline text 1`] = `""`; + +exports[`Blocks raw handling pasteHandler should strip some text-level elements 1`] = ` +" +

This is ncorect

+" +`; + +exports[`Blocks raw handling pasteHandler should strip windows data 1`] = ` +" +

Heading Win

+ + + +

Paragraph Win

+" +`; + +exports[`Blocks raw handling pasteHandler two-images 1`] = `" "`; + +exports[`Blocks raw handling should correctly handle quotes with mixed content 1`] = ` +" +
+

chicken

+ + + +

ribs

+
+" +`; + +exports[`rawHandler should convert HTML post to blocks with minimal content changes 1`] = `""`; + +exports[`rawHandler should convert a caption shortcode 1`] = `""`; + +exports[`rawHandler should convert a caption shortcode with caption 1`] = `""`; + +exports[`rawHandler should convert a caption shortcode with link 1`] = `""`; + +exports[`rawHandler should convert a list with attributes 1`] = `""`; + +exports[`rawHandler should convert to unsupported HTML block when no transformation is available 1`] = ` +" +

Hello world!

+" +`; + +exports[`rawHandler should not strip any text-level elements 1`] = `""`; + +exports[`rawHandler should preserve alignment 1`] = `""`; diff --git a/test/native/integration/blocks-raw-handling.native.js b/test/native/integration/blocks-raw-handling.native.js new file mode 100644 index 00000000000000..941fb951a21f7d --- /dev/null +++ b/test/native/integration/blocks-raw-handling.native.js @@ -0,0 +1,587 @@ +/** + * External dependencies + */ +import fs from 'fs'; +import path from 'path'; + +/** + * WordPress dependencies + */ +import { + createBlock, + getBlockContent, + pasteHandler, + rawHandler, + registerBlockType, + serialize, +} from '@wordpress/blocks'; +import { registerCoreBlocks } from '@wordpress/block-library'; + +function readFile( filePath ) { + return fs.existsSync( filePath ) + ? fs.readFileSync( filePath, 'utf8' ).trim() + : ''; +} + +// Path to the fixtures provided in `gutenberg/test/integration`. +const fixturesPath = `${ __dirname }/../../integration`; + +// NOTE: This file is a clone of the same `blocks-raw-handling.js` file located in +// `gutenberg/test/integration`. The reason for the separation is that several of +// the test cases fail in the native version. For now, we are going to skip them, but +// we'd need to work on them in the future. +// +// Once all issues in tests are addressed, we'll remove this file in favor of the +// original one. +describe( 'Blocks raw handling', () => { + beforeAll( () => { + // Load all hooks that modify blocks. + require( '../../../packages/editor/src/hooks' ); + registerCoreBlocks(); + registerBlockType( 'test/gallery', { + title: 'Test Gallery', + category: 'text', + attributes: { + ids: { + type: 'array', + default: [], + }, + }, + transforms: { + from: [ + { + type: 'shortcode', + tag: 'gallery', + isMatch( { named: { ids } } ) { + return ids.indexOf( 42 ) > -1; + }, + attributes: { + ids: { + type: 'array', + shortcode: ( { named: { ids } } ) => + ids + .split( ',' ) + .map( ( id ) => parseInt( id, 10 ) ), + }, + }, + priority: 9, + }, + ], + }, + save: () => null, + } ); + + registerBlockType( 'test/non-inline-block', { + title: 'Test Non Inline Block', + category: 'text', + supports: { + pasteTextInline: false, + }, + transforms: { + from: [ + { + type: 'raw', + isMatch: ( node ) => { + return ( + 'words to live by' === node.textContent.trim() + ); + }, + transform: () => { + return createBlock( 'core/embed', { + url: 'https://www.youtube.com/watch?v=dQw4w9WgXcQ', + } ); + }, + }, + ], + }, + save: () => null, + } ); + + registerBlockType( 'test/transform-to-multiple-blocks', { + title: 'Test Transform to Multiple Blocks', + category: 'text', + transforms: { + from: [ + { + type: 'raw', + isMatch: ( node ) => { + return node.textContent + .split( ' ' ) + .every( ( chunk ) => /^P\S+?/.test( chunk ) ); + }, + transform: ( node ) => { + return node.textContent + .split( ' ' ) + .map( ( chunk ) => + createBlock( 'core/paragraph', { + content: chunk.substring( 1 ), + } ) + ); + }, + }, + ], + }, + save: () => null, + } ); + } ); + + it( 'should filter inline content', () => { + const filtered = pasteHandler( { + HTML: '

test

', + mode: 'INLINE', + } ); + + expect( filtered ).toBe( 'test' ); + expect( console ).toHaveLogged(); + } ); + + it( 'should ignore Google Docs UID tag', () => { + const filtered = pasteHandler( { + HTML: 'test', + mode: 'AUTO', + } ); + + expect( filtered ).toBe( 'test' ); + expect( console ).toHaveLogged(); + } ); + + it( 'should ignore Google Docs UID tag in inline mode', () => { + const filtered = pasteHandler( { + HTML: 'test', + mode: 'INLINE', + } ); + + expect( filtered ).toBe( 'test' ); + expect( console ).toHaveLogged(); + } ); + + it( 'should paste special whitespace', () => { + const filtered = pasteHandler( { + HTML: '

', + plainText: ' ', + mode: 'AUTO', + } ); + + expect( console ).toHaveLogged(); + expect( filtered ).toBe( ' ' ); + } ); + + it( 'should paste special whitespace in plain text only', () => { + const filtered = pasteHandler( { + HTML: '', + plainText: ' ', + mode: 'AUTO', + } ); + + expect( console ).toHaveLogged(); + expect( filtered ).toBe( ' ' ); + } ); + + it( 'should parse Markdown', () => { + const filtered = pasteHandler( { + HTML: '* one
* two
* three', + plainText: '* one\n* two\n* three', + mode: 'AUTO', + } ) + .map( getBlockContent ) + .join( '' ); + + expect( filtered ).toMatchInlineSnapshot( ` + "" + ` ); + expect( console ).toHaveLogged(); + } ); + + it( 'should parse bulleted list', () => { + const filtered = pasteHandler( { + HTML: '• one
• two
• three', + plainText: '• one\n• two\n• three', + mode: 'AUTO', + } ) + .map( getBlockContent ) + .join( '' ); + + expect( filtered ).toMatchInlineSnapshot( ` + "" + ` ); + expect( console ).toHaveLogged(); + } ); + + it( 'should parse inline Markdown', () => { + const filtered = pasteHandler( { + HTML: 'Some **bold** text.', + plainText: 'Some **bold** text.', + mode: 'AUTO', + } ); + + expect( filtered ).toBe( 'Some bold text.' ); + expect( console ).toHaveLogged(); + } ); + + it( 'should parse HTML in plainText', () => { + const filtered = pasteHandler( { + HTML: '<p>Some <strong>bold</strong> text.</p>', + plainText: '

Some bold text.

', + mode: 'AUTO', + } ); + + expect( filtered ).toBe( 'Some bold text.' ); + expect( console ).toHaveLogged(); + } ); + + it( 'should parse Markdown with HTML', () => { + const filtered = pasteHandler( { + HTML: '', + plainText: '# Some heading\n\nA paragraph.', + mode: 'AUTO', + } ) + .map( getBlockContent ) + .join( '' ); + + expect( filtered ).toBe( + '

Some heading

A paragraph.

' + ); + expect( console ).toHaveLogged(); + } ); + + it.skip( 'should break up forced inline content', () => { + const filtered = pasteHandler( { + HTML: '

test

test

', + mode: 'INLINE', + } ); + + expect( filtered ).toBe( 'test
test' ); + expect( console ).toHaveLogged(); + } ); + + it( 'should normalize decomposed characters', () => { + const filtered = pasteHandler( { + HTML: 'schön', + mode: 'INLINE', + } ); + + expect( filtered ).toBe( 'schön' ); + expect( console ).toHaveLogged(); + } ); + + it.skip( 'should not treat single non-inlineable block as inline text', () => { + const filtered = pasteHandler( { + HTML: '

words to live by

', + plainText: 'words to live by\n', + mode: 'AUTO', + } ); + + expect( filtered ).toHaveLength( 1 ); + expect( filtered[ 0 ].name ).toBe( 'core/embed' ); + expect( console ).toHaveLogged(); + } ); + + it( 'should treat single heading as inline text', () => { + const filtered = pasteHandler( { + HTML: '

FOO

', + plainText: 'FOO\n', + mode: 'AUTO', + } ); + + expect( filtered ).toBe( 'FOO' ); + expect( console ).toHaveLogged(); + } ); + + it( 'should treat single list item as inline text', () => { + const filtered = pasteHandler( { + HTML: '', + plainText: 'Some bold text.\n', + mode: 'AUTO', + } ); + + expect( filtered ).toBe( 'Some bold text.' ); + expect( console ).toHaveLogged(); + } ); + + it( 'should treat multiple list items as a block', () => { + const filtered = pasteHandler( { + HTML: '', + plainText: 'One\nTwo\nThree\n', + mode: 'AUTO', + } ) + .map( getBlockContent ) + .join( '' ); + + expect( filtered ).toMatchInlineSnapshot( ` + "" + ` ); + expect( console ).toHaveLogged(); + } ); + + it( 'should correctly handle quotes with mixed content', () => { + const filtered = serialize( + pasteHandler( { + HTML: '

chicken

ribs

', + mode: 'AUTO', + } ) + ); + + expect( filtered ).toMatchSnapshot(); + expect( console ).toHaveLogged(); + } ); + + it( 'should paste gutenberg content from plain text', () => { + const block = ''; + expect( + serialize( + pasteHandler( { + plainText: block, + mode: 'AUTO', + } ) + ) + ).toBe( block ); + } ); + + it.skip( 'should handle transforms that return an array of blocks', () => { + const transformed = pasteHandler( { + HTML: '

P1 P2

', + plainText: 'P1 P2\n', + } ) + .map( getBlockContent ) + .join( '' ); + + expect( transformed ).toBe( '

1

2

' ); + expect( console ).toHaveLogged(); + } ); + + it( 'should convert pre', () => { + const transformed = pasteHandler( { + HTML: '
1\n2
', + plainText: '1\n2', + } ) + .map( getBlockContent ) + .join( '' ); + + expect( transformed ).toBe( + '
1\n2
' + ); + expect( console ).toHaveLogged(); + } ); + + it( 'should convert code', () => { + const transformed = pasteHandler( { + HTML: '
1\n2
', + plainText: '1\n2', + } ) + .map( getBlockContent ) + .join( '' ); + + expect( transformed ).toBe( + '
1\n2
' + ); + expect( console ).toHaveLogged(); + } ); + + describe( 'pasteHandler', () => { + // TODO: The cases commented should be eventually addressed and restored. + [ + // 'plain', + // 'classic', + // 'nested-divs', + // 'apple', + // 'google-docs', + 'google-docs-list-only', + // 'google-docs-table', + // 'google-docs-table-with-colspan', + // 'google-docs-table-with-rowspan', + // 'google-docs-table-with-comments', + // 'google-docs-with-comments', + // 'ms-word', + // 'ms-word-list', + 'ms-word-styled', + // 'ms-word-online', + // 'evernote', + 'iframe-embed', + 'one-image', + 'two-images', + // 'markdown', + // 'wordpress', + 'gutenberg', + // 'shortcode-matching', + // 'slack-quote', + // 'slack-paragraphs', + ].forEach( ( type ) => { + // eslint-disable-next-line jest/valid-title + it( type, () => { + const HTML = readFile( + path.join( + fixturesPath, + `fixtures/documents/${ type }-in.html` + ) + ); + const plainText = readFile( + path.join( + fixturesPath, + `fixtures/documents/${ type }-in.txt` + ) + ); + const output = readFile( + path.join( + fixturesPath, + `fixtures/documents/${ type }-out.html` + ) + ); + + if ( ! ( HTML || plainText ) || ! output ) { + throw new Error( `Expected fixtures for type ${ type }` ); + } + + const converted = pasteHandler( { HTML, plainText } ); + const serialized = + typeof converted === 'string' + ? converted + : serialize( converted ); + + expect( serialized ).toBe( output ); + + const convertedInline = pasteHandler( { + HTML, + plainText, + mode: 'INLINE', + } ); + + expect( convertedInline ).toMatchSnapshot(); + expect( console ).toHaveLogged(); + } ); + } ); + + it( 'should strip some text-level elements', () => { + const HTML = '

This is ncorect

'; + expect( serialize( pasteHandler( { HTML } ) ) ).toMatchSnapshot(); + expect( console ).toHaveLogged(); + } ); + + it( 'should remove extra blank lines', () => { + const HTML = readFile( + path.join( + fixturesPath, + 'fixtures/documents/google-docs-blank-lines.html' + ) + ); + expect( serialize( pasteHandler( { HTML } ) ) ).toMatchSnapshot(); + expect( console ).toHaveLogged(); + } ); + + it( 'should strip windows data', () => { + const HTML = readFile( + path.join( fixturesPath, 'fixtures/documents/windows.html' ) + ); + expect( serialize( pasteHandler( { HTML } ) ) ).toMatchSnapshot(); + } ); + + it.skip( 'should strip HTML formatting space from inline text', () => { + const HTML = readFile( + path.join( + fixturesPath, + 'fixtures/documents/inline-with-html-formatting-space.html' + ) + ); + expect( pasteHandler( { HTML } ) ).toMatchSnapshot(); + expect( console ).toHaveLogged(); + } ); + } ); +} ); + +describe( 'rawHandler', () => { + it.skip( 'should convert HTML post to blocks with minimal content changes', () => { + const HTML = readFile( + path.join( + fixturesPath, + 'fixtures/documents/wordpress-convert.html' + ) + ); + expect( serialize( rawHandler( { HTML } ) ) ).toMatchSnapshot(); + } ); + + it.skip( 'should convert a caption shortcode', () => { + const HTML = readFile( + path.join( + fixturesPath, + 'fixtures/documents/shortcode-caption.html' + ) + ); + expect( serialize( rawHandler( { HTML } ) ) ).toMatchSnapshot(); + } ); + + it.skip( 'should convert a caption shortcode with link', () => { + const HTML = readFile( + path.join( + fixturesPath, + 'fixtures/documents/shortcode-caption-with-link.html' + ) + ); + expect( serialize( rawHandler( { HTML } ) ) ).toMatchSnapshot(); + } ); + + it.skip( 'should convert a caption shortcode with caption', () => { + const HTML = readFile( + path.join( + fixturesPath, + 'fixtures/documents/shortcode-caption-with-caption-link.html' + ) + ); + expect( serialize( rawHandler( { HTML } ) ) ).toMatchSnapshot(); + } ); + + it.skip( 'should convert a list with attributes', () => { + const HTML = readFile( + path.join( + fixturesPath, + 'fixtures/documents/list-with-attributes.html' + ) + ); + expect( serialize( rawHandler( { HTML } ) ) ).toMatchSnapshot(); + } ); + + it.skip( 'should not strip any text-level elements', () => { + const HTML = '

This is ncorect

'; + expect( serialize( rawHandler( { HTML } ) ) ).toMatchSnapshot(); + } ); + + it.skip( 'should preserve alignment', () => { + const HTML = '

center

'; + expect( serialize( rawHandler( { HTML } ) ) ).toMatchSnapshot(); + } ); + + // This is an extra test added to cover the case fixed in: + // `rnmobile/fix/div-tag-convert-to-blocks`. + it( 'should convert to unsupported HTML block when no transformation is available', () => { + const HTML = '

Hello world!

'; + expect( serialize( rawHandler( { HTML } ) ) ).toMatchSnapshot(); + } ); +} );