From 391ed28fe998bf8a66f883041aad77efeca80068 Mon Sep 17 00:00:00 2001 From: Carlos Garcia Date: Fri, 5 May 2023 12:56:05 +0200 Subject: [PATCH 01/12] Add VideoPress upload test cases --- .../videopress.upload.test.js.snap | 11 + src/test/videopress.upload.test.js | 453 ++++++++++++++++++ 2 files changed, 464 insertions(+) create mode 100644 src/test/__snapshots__/videopress.upload.test.js.snap create mode 100644 src/test/videopress.upload.test.js diff --git a/src/test/__snapshots__/videopress.upload.test.js.snap b/src/test/__snapshots__/videopress.upload.test.js.snap new file mode 100644 index 0000000000..7e15b4b54f --- /dev/null +++ b/src/test/__snapshots__/videopress.upload.test.js.snap @@ -0,0 +1,11 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`VideoPress block - Uploads takes a video and uploads it: loading state 1`] = `""`; + +exports[`VideoPress block - Uploads takes a video and uploads it: video ready 1`] = `""`; + +exports[`VideoPress block - Uploads uploads a video from device: loading state 1`] = `""`; + +exports[`VideoPress block - Uploads uploads a video from device: video ready 1`] = `""`; + +exports[`VideoPress block - Uploads uploads a video from media library: video ready 1`] = `""`; diff --git a/src/test/videopress.upload.test.js b/src/test/videopress.upload.test.js new file mode 100644 index 0000000000..8951136c7d --- /dev/null +++ b/src/test/videopress.upload.test.js @@ -0,0 +1,453 @@ +/** + * External dependencies + */ +import { + fireEvent, + getBlock, + getEditorHtml, + initializeEditor, + setupCoreBlocks, + setupMediaUpload, + setupMediaPicker, + within, + setupPicker, + setupApiFetch, +} from 'test/helpers'; +import { Platform } from '@wordpress/element'; +import { ActionSheetIOS } from 'react-native'; +import apiFetch from '@wordpress/api-fetch'; + +/** + * Internal dependencies + */ +import { + registerJetpackBlocks, + setupJetpackEditor, +} from '../jetpack-editor-setup'; + +jest.mock( '@wordpress/api-fetch' ); + +const initialHtml = ''; + +const MEDIA_OPTIONS = [ + 'Choose from device', + 'Take a Video', + 'WordPress Media Library', + 'Insert from URL', +]; + +const generateApiResponses = ( guid ) => [ + { + request: { + path: `/wpcom/v2/media/videopress-playback-jwt/${ guid }`, + method: 'POST', + body: {}, + }, + response: { + metadata_token: 'videopress-token', + }, + }, + { + request: { + path: `/rest/v1.1/videos/${ guid }`, + credentials: 'omit', + global: true, + }, + response: { + description: 'video-description', + post_id: 1, + guid, + private_enabled_for_site: false, + title: 'video-title', + duration: 1200, + privacy_setting: 2, + original: `https://videos.files.wordpress.com/${ guid }/video.mp4`, + allow_download: false, + display_embed: true, + poster: `https://videos.files.wordpress.com/${ guid }/video.jpg`, + height: 270, + width: 480, + rating: 'G', + is_private: false, + }, + }, + { + request: { + path: `/oembed/1.0/proxy?url=https%3A%2F%2Fvideopress.com%2Fv%2F${ guid }%3FresizeToParent%3Dtrue%26cover%3Dtrue%26preloadContent%3Dmetadata`, + }, + response: { + height: 338, + provider_name: 'VideoPress', + html: ``, + width: 600, + type: 'video', + }, + }, +]; + +setupCoreBlocks(); + +beforeAll( () => { + // Register VideoPress block + setupJetpackEditor( { + blogId: 1, + isJetpackActive: true, + } ); + registerJetpackBlocks( { + capabilities: { videoPressBlock: true }, + } ); +} ); + +describe( 'VideoPress block - Uploads', () => { + it( 'displays media options picker when selecting the block', async () => { + // Initialize with an empty gallery + const { + getByLabelText, + getByText, + getByTestId, + } = await initializeEditor( { + initialHtml, + } ); + + fireEvent.press( getByText( 'ADD VIDEO' ) ); + + // Observe that media options picker is displayed + if ( Platform.isIOS ) { + // On iOS the picker is rendered natively, so we have + // to check the arguments passed to `ActionSheetIOS`. + expect( + ActionSheetIOS.showActionSheetWithOptions + ).toHaveBeenCalledWith( + expect.objectContaining( { + title: 'Choose video', + options: [ 'Cancel', ...MEDIA_OPTIONS ], + } ), + expect.any( Function ) + ); + } else { + expect( getByText( 'Choose video' ) ).toBeVisible(); + MEDIA_OPTIONS.forEach( ( option ) => + expect( getByText( option ) ).toBeVisible() + ); + + fireEvent( getByTestId( 'media-options-picker' ), 'backdropPress' ); + } + + const blockActionsButton = getByLabelText( /Open Block Actions Menu/ ); + expect( blockActionsButton ).toBeVisible(); + } ); + + it( 'uploads a video from device', async () => { + const guid = 'AAAAAAA1'; + const media = { + type: 'video', + localId: 1, + localUrl: 'file:///local-video-1.mp4', + serverId: 2000, + serverUrl: 'https://videopress.wordpress.com/local-video-1.mp4', + }; + + const { notifyUploadingState, notifySucceedState } = setupMediaUpload(); + const { + expectMediaPickerCall, + mediaPickerCallback, + } = setupMediaPicker(); + const apiResponses = generateApiResponses( guid ); + setupApiFetch( apiResponses ); + + const screen = await initializeEditor( { + initialHtml, + } ); + const { getByText, getByTestId } = screen; + const { selectOption } = setupPicker( screen, MEDIA_OPTIONS ); + // Clear previous calls to `apiFetch` + apiFetch.mockClear(); + + // Block is visible + const block = await getBlock( screen, 'VideoPress' ); + expect( block ).toBeVisible(); + + // Upload video from device + fireEvent.press( getByText( 'ADD VIDEO' ) ); + selectOption( 'Choose from device' ); + expectMediaPickerCall( 'DEVICE_MEDIA_LIBRARY', [ 'video' ], false ); + + // Block is uploading the video + await mediaPickerCallback( media ); + expect( getByTestId( 'videopress-uploading-video' ) ).toBeVisible(); + expect( getEditorHtml() ).toMatchSnapshot( 'loading state' ); + + // During upload progress we keep displaying the loading state + await notifyUploadingState( media ); + expect( getByTestId( 'videopress-uploading-video' ) ).toBeVisible(); + + // Upload finish + await notifySucceedState( { + ...media, + metadata: Platform.select( { + android: { videopressGUID: guid }, + ios: { id: guid }, + } ), + } ); + + // Token request was made + expect( apiFetch ).toHaveBeenNthCalledWith( + 1, + apiResponses[ 0 ].request + ); + // Metadata request was made + expect( apiFetch ).toHaveBeenNthCalledWith( + 2, + apiResponses[ 1 ].request + ); + // TODO: check why metadata request is called two times + expect( apiFetch ).toHaveBeenNthCalledWith( + 3, + apiResponses[ 1 ].request + ); + // Oembed request was made + expect( apiFetch ).toHaveBeenNthCalledWith( + 4, + apiResponses[ 2 ].request + ); + + // Check loading overlay is displayed before the player is ready + expect( within( block ).getByText( 'Loading' ) ).toBeVisible(); + const player = getByTestId( 'videopress-player' ); + + // Notify the player is ready + fireEvent( player, 'message', { + nativeEvent: { + data: JSON.stringify( { + type: 'message', + event: 'videopress_ready', + } ), + }, + } ); + expect( player ).toBeVisible(); + + // At this point the player should be showing the conversion state. + // Hence, let's notify the loaded state. + fireEvent( player, 'message', { + nativeEvent: { + data: JSON.stringify( { + type: 'message', + event: 'videopress_loading_state', + state: 'loaded', + } ), + }, + } ); + + // At this point the player should be ready to be used. + expect( within( block ).queryByText( 'Loading' ) ).toBeNull(); + expect( getEditorHtml() ).toMatchSnapshot( 'video ready' ); + } ); + + it( 'uploads a video from media library', async () => { + const guid = 'AAAAAAA2'; + const media = { + type: 'video', + id: 2000, + url: 'https://test.files.wordpress.com/local-video-2.mp4', + metadata: { + videopressGUID: guid, + }, + }; + const { + expectMediaPickerCall, + mediaPickerCallback, + } = setupMediaPicker(); + const apiResponses = generateApiResponses( guid ); + setupApiFetch( apiResponses ); + + const screen = await initializeEditor( { + initialHtml, + } ); + const { getByText, getByTestId } = screen; + const { selectOption } = setupPicker( screen, MEDIA_OPTIONS ); + // Clear previous calls to `apiFetch` + apiFetch.mockClear(); + + // Block is visible + const block = await getBlock( screen, 'VideoPress' ); + expect( block ).toBeVisible(); + + // Add video from WordPress media library + fireEvent.press( getByText( 'ADD VIDEO' ) ); + selectOption( 'WordPress Media Library' ); + expectMediaPickerCall( 'SITE_MEDIA_LIBRARY', [ 'video' ], false ); + + await mediaPickerCallback( media ); + + // Token request was made + expect( apiFetch ).toHaveBeenNthCalledWith( + 1, + apiResponses[ 0 ].request + ); + // Metadata request was made + expect( apiFetch ).toHaveBeenNthCalledWith( + 2, + apiResponses[ 1 ].request + ); + // TODO: check why metadata request is called two times + expect( apiFetch ).toHaveBeenNthCalledWith( + 3, + apiResponses[ 1 ].request + ); + // Oembed request was made + expect( apiFetch ).toHaveBeenNthCalledWith( + 4, + apiResponses[ 2 ].request + ); + + // Check loading overlay is displayed before the player is ready + expect( within( block ).getByText( 'Loading' ) ).toBeVisible(); + const player = getByTestId( 'videopress-player' ); + + // Notify the player is ready + fireEvent( player, 'message', { + nativeEvent: { + data: JSON.stringify( { + type: 'message', + event: 'videopress_ready', + } ), + }, + } ); + expect( player ).toBeVisible(); + + // At this point the player should be showing the conversion state. + // Hence, let's notify the loaded state. + fireEvent( player, 'message', { + nativeEvent: { + data: JSON.stringify( { + type: 'message', + event: 'videopress_loading_state', + state: 'loaded', + } ), + }, + } ); + + // At this point the player should be ready to be used. + expect( within( block ).queryByText( 'Loading' ) ).toBeNull(); + expect( getEditorHtml() ).toMatchSnapshot( 'video ready' ); + } ); + + it( 'takes a video and uploads it', async () => { + const guid = 'AAAAAAA3'; + const media = { + type: 'video', + localId: 3, + localUrl: 'file:///local-video-3.mp4', + serverId: 2000, + serverUrl: 'https://videopress.wordpress.com/local-video-3.mp4', + }; + + const { notifyUploadingState, notifySucceedState } = setupMediaUpload(); + const { + expectMediaPickerCall, + mediaPickerCallback, + } = setupMediaPicker(); + const apiResponses = generateApiResponses( guid ); + setupApiFetch( apiResponses ); + + const screen = await initializeEditor( { + initialHtml, + } ); + const { getByText, getByTestId } = screen; + const { selectOption } = setupPicker( screen, MEDIA_OPTIONS ); + // Clear previous calls to `apiFetch` + apiFetch.mockClear(); + + // Block is visible + const block = await getBlock( screen, 'VideoPress' ); + expect( block ).toBeVisible(); + + // Take a video and upload it + fireEvent.press( getByText( 'ADD VIDEO' ) ); + selectOption( 'Take a Video' ); + expectMediaPickerCall( 'DEVICE_CAMERA', [ 'video' ], false ); + + // Block is uploading the video + await mediaPickerCallback( media ); + expect( getByTestId( 'videopress-uploading-video' ) ).toBeVisible(); + expect( getEditorHtml() ).toMatchSnapshot( 'loading state' ); + + // During upload progress we keep displaying the loading state + await notifyUploadingState( media ); + expect( getByTestId( 'videopress-uploading-video' ) ).toBeVisible(); + + // Upload finish + await notifySucceedState( { + ...media, + metadata: Platform.select( { + android: { videopressGUID: guid }, + ios: { id: guid }, + } ), + } ); + + // Token request was made + expect( apiFetch ).toHaveBeenNthCalledWith( + 1, + apiResponses[ 0 ].request + ); + // Metadata request was made + expect( apiFetch ).toHaveBeenNthCalledWith( + 2, + apiResponses[ 1 ].request + ); + // TODO: check why metadata request is called two times + expect( apiFetch ).toHaveBeenNthCalledWith( + 3, + apiResponses[ 1 ].request + ); + // Oembed request was made + expect( apiFetch ).toHaveBeenNthCalledWith( + 4, + apiResponses[ 2 ].request + ); + + // Check loading overlay is displayed before the player is ready + expect( within( block ).getByText( 'Loading' ) ).toBeVisible(); + const player = getByTestId( 'videopress-player' ); + + // Notify the player is ready + fireEvent( player, 'message', { + nativeEvent: { + data: JSON.stringify( { + type: 'message', + event: 'videopress_ready', + } ), + }, + } ); + expect( player ).toBeVisible(); + + // At this point the player should be showing the conversion state. + // Hence, let's notify the loaded state. + fireEvent( player, 'message', { + nativeEvent: { + data: JSON.stringify( { + type: 'message', + event: 'videopress_loading_state', + state: 'loaded', + } ), + }, + } ); + + // At this point the player should be ready to be used. + expect( within( block ).queryByText( 'Loading' ) ).toBeNull(); + expect( getEditorHtml() ).toMatchSnapshot( 'video ready' ); + } ); + + it.skip( 'adds video by inserting URL', async () => {} ); + + it.skip( 'finishes pending uploads upon opening the editor', async () => {} ); + it.skip( 'handles upload failure', async () => {} ); + it.skip( 'cancel upload', async () => {} ); + + it.skip( 'sets caption', async () => {} ); + + it.skip( 'replace video with local video', async () => {} ); + it.skip( 'replace video taking a new one', async () => {} ); + it.skip( 'replace video video item from media library', async () => {} ); + it.skip( 'replace video video with new URL', async () => {} ); +} ); From 61eccb0ce7aa5ebf69690c2c6a74987567e594e5 Mon Sep 17 00:00:00 2001 From: Carlos Garcia Date: Fri, 5 May 2023 12:57:04 +0200 Subject: [PATCH 02/12] Add update test snapshot NPM command --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index 7549e7c6cf..23fc8af43f 100644 --- a/package.json +++ b/package.json @@ -79,6 +79,7 @@ "prewpandroid": "rm -Rf $TMPDIR/gbmobile-wpandroidfakernroot && mkdir $TMPDIR/gbmobile-wpandroidfakernroot && ln -s $(cd \"$(dirname \"../../../\")\"; pwd) $TMPDIR/gbmobile-wpandroidfakernroot/android", "wpandroid": "cd gutenberg && react-native run-android --root $TMPDIR/gbmobile-wpandroidfakernroot --variant wasabiDebug --appIdSuffix beta --appFolder WordPress --main-activity=ui.WPLaunchActivity", "test": "cross-env NODE_ENV=test jest --verbose --config ./jest.config.js", + "test:update": "cross-env NODE_ENV=test jest --verbose --config ./jest.config.js --updateSnapshot", "test:debug": "cross-env NODE_ENV=test node --inspect-brk node_modules/.bin/jest --runInBand --verbose --config jest.config.js", "device-tests": "cross-env NODE_ENV=test jest --maxWorkers=2 --testPathIgnorePatterns='canary|gutenberg-editor-rendering' --verbose --config jest_ui.config.js", "device-tests-canary": "cross-env NODE_ENV=test jest --maxWorkers=2 --testPathPattern=@canary --verbose --config jest_ui.config.js", From 0ef09fbc13bfc083455104e29d0a17d930da9a68 Mon Sep 17 00:00:00 2001 From: Carlos Garcia Date: Fri, 5 May 2023 18:56:16 +0200 Subject: [PATCH 03/12] Update Gutenberg ref --- gutenberg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gutenberg b/gutenberg index 27ed5ccc51..282da7ca4c 160000 --- a/gutenberg +++ b/gutenberg @@ -1 +1 @@ -Subproject commit 27ed5ccc512333f2c5ea2af243c11b7ccc94c9ec +Subproject commit 282da7ca4c74725193c2c362925c9c8b5946b993 From fd8173dc27733b97c3483f683569964707441b2d Mon Sep 17 00:00:00 2001 From: Carlos Garcia Date: Fri, 5 May 2023 18:56:21 +0200 Subject: [PATCH 04/12] Update Jetpack ref --- jetpack | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jetpack b/jetpack index b443d69447..eb6d106409 160000 --- a/jetpack +++ b/jetpack @@ -1 +1 @@ -Subproject commit b443d694471e1e4e9cc337ae16dd7f5b531ff797 +Subproject commit eb6d1064093de154a413681a89bb929d4dd50c0c From a8c1be96013c0f51df28fc9bab44b85d8122020d Mon Sep 17 00:00:00 2001 From: Carlos Garcia Date: Fri, 5 May 2023 19:00:07 +0200 Subject: [PATCH 05/12] Move VideoPress upload tests to `videopress` folder --- src/test/{videopress.upload.test.js => videopress/upload.js} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/test/{videopress.upload.test.js => videopress/upload.js} (100%) diff --git a/src/test/videopress.upload.test.js b/src/test/videopress/upload.js similarity index 100% rename from src/test/videopress.upload.test.js rename to src/test/videopress/upload.js From 24f82fac355974b25b550d22dfb45d9f97dc8784 Mon Sep 17 00:00:00 2001 From: Carlos Garcia Date: Fri, 5 May 2023 19:25:31 +0200 Subject: [PATCH 06/12] Fix import --- src/test/videopress/upload.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/videopress/upload.js b/src/test/videopress/upload.js index 8951136c7d..dd90bee548 100644 --- a/src/test/videopress/upload.js +++ b/src/test/videopress/upload.js @@ -23,7 +23,7 @@ import apiFetch from '@wordpress/api-fetch'; import { registerJetpackBlocks, setupJetpackEditor, -} from '../jetpack-editor-setup'; +} from '../../jetpack-editor-setup'; jest.mock( '@wordpress/api-fetch' ); From f351618a62ea5691e73eee4d20d5dedf4b86828e Mon Sep 17 00:00:00 2001 From: Carlos Garcia Date: Fri, 5 May 2023 19:26:25 +0200 Subject: [PATCH 07/12] Refactor upload test cases --- .../__snapshots__/upload.js.snap} | 6 +- src/test/videopress/upload.js | 212 ++++++++---------- 2 files changed, 91 insertions(+), 127 deletions(-) rename src/test/{__snapshots__/videopress.upload.test.js.snap => videopress/__snapshots__/upload.js.snap} (79%) diff --git a/src/test/__snapshots__/videopress.upload.test.js.snap b/src/test/videopress/__snapshots__/upload.js.snap similarity index 79% rename from src/test/__snapshots__/videopress.upload.test.js.snap rename to src/test/videopress/__snapshots__/upload.js.snap index 7e15b4b54f..abe8ba5550 100644 --- a/src/test/__snapshots__/videopress.upload.test.js.snap +++ b/src/test/videopress/__snapshots__/upload.js.snap @@ -2,10 +2,10 @@ exports[`VideoPress block - Uploads takes a video and uploads it: loading state 1`] = `""`; -exports[`VideoPress block - Uploads takes a video and uploads it: video ready 1`] = `""`; +exports[`VideoPress block - Uploads takes a video and uploads it: video ready 1`] = `""`; exports[`VideoPress block - Uploads uploads a video from device: loading state 1`] = `""`; -exports[`VideoPress block - Uploads uploads a video from device: video ready 1`] = `""`; +exports[`VideoPress block - Uploads uploads a video from device: video ready 1`] = `""`; -exports[`VideoPress block - Uploads uploads a video from media library: video ready 1`] = `""`; +exports[`VideoPress block - Uploads uploads a video from media library: video ready 1`] = `""`; diff --git a/src/test/videopress/upload.js b/src/test/videopress/upload.js index dd90bee548..ba2a890b86 100644 --- a/src/test/videopress/upload.js +++ b/src/test/videopress/upload.js @@ -16,6 +16,8 @@ import { import { Platform } from '@wordpress/element'; import { ActionSheetIOS } from 'react-native'; import apiFetch from '@wordpress/api-fetch'; +import { dispatch } from '@wordpress/data'; +import { store as coreStore } from '@wordpress/core-data'; /** * Internal dependencies @@ -36,10 +38,11 @@ const MEDIA_OPTIONS = [ 'Insert from URL', ]; -const generateApiResponses = ( guid ) => [ +const VIDEOPRESS_GUID = 'AbCdEfGh'; +const FETCH_ITEMS = [ { request: { - path: `/wpcom/v2/media/videopress-playback-jwt/${ guid }`, + path: `/wpcom/v2/media/videopress-playback-jwt/${ VIDEOPRESS_GUID }`, method: 'POST', body: {}, }, @@ -49,22 +52,22 @@ const generateApiResponses = ( guid ) => [ }, { request: { - path: `/rest/v1.1/videos/${ guid }`, + path: `/rest/v1.1/videos/${ VIDEOPRESS_GUID }`, credentials: 'omit', global: true, }, response: { description: 'video-description', post_id: 1, - guid, + guid: VIDEOPRESS_GUID, private_enabled_for_site: false, title: 'video-title', duration: 1200, privacy_setting: 2, - original: `https://videos.files.wordpress.com/${ guid }/video.mp4`, + original: `https://videos.files.wordpress.com/${ VIDEOPRESS_GUID }/video.mp4`, allow_download: false, display_embed: true, - poster: `https://videos.files.wordpress.com/${ guid }/video.jpg`, + poster: `https://videos.files.wordpress.com/${ VIDEOPRESS_GUID }/video.jpg`, height: 270, width: 480, rating: 'G', @@ -73,18 +76,25 @@ const generateApiResponses = ( guid ) => [ }, { request: { - path: `/oembed/1.0/proxy?url=https%3A%2F%2Fvideopress.com%2Fv%2F${ guid }%3FresizeToParent%3Dtrue%26cover%3Dtrue%26preloadContent%3Dmetadata`, + path: `/oembed/1.0/proxy?url=https%3A%2F%2Fvideopress.com%2Fv%2F${ VIDEOPRESS_GUID }%3FresizeToParent%3Dtrue%26cover%3Dtrue%26preloadContent%3Dmetadata`, }, response: { height: 338, provider_name: 'VideoPress', - html: ``, + html: ``, width: 600, type: 'video', }, }, ]; +const sendWebViewMessage = ( webView, message ) => + fireEvent( webView, 'message', { + nativeEvent: { + data: JSON.stringify( message ), + }, + } ); + setupCoreBlocks(); beforeAll( () => { @@ -96,6 +106,17 @@ beforeAll( () => { registerJetpackBlocks( { capabilities: { videoPressBlock: true }, } ); + + // Mock request reponses + setupApiFetch( FETCH_ITEMS ); +} ); + +beforeEach( () => { + // Invalidate `getEmbedPreview` resolutions to avoid + // caching the preview for the same VideoPress GUID. + dispatch( coreStore ).invalidateResolutionForStoreSelector( + 'getEmbedPreview' + ); } ); describe( 'VideoPress block - Uploads', () => { @@ -138,7 +159,6 @@ describe( 'VideoPress block - Uploads', () => { } ); it( 'uploads a video from device', async () => { - const guid = 'AAAAAAA1'; const media = { type: 'video', localId: 1, @@ -152,8 +172,6 @@ describe( 'VideoPress block - Uploads', () => { expectMediaPickerCall, mediaPickerCallback, } = setupMediaPicker(); - const apiResponses = generateApiResponses( guid ); - setupApiFetch( apiResponses ); const screen = await initializeEditor( { initialHtml, @@ -185,30 +203,23 @@ describe( 'VideoPress block - Uploads', () => { await notifySucceedState( { ...media, metadata: Platform.select( { - android: { videopressGUID: guid }, - ios: { id: guid }, + android: { + videopressGUID: VIDEOPRESS_GUID, + }, + ios: { + id: VIDEOPRESS_GUID, + }, } ), } ); - // Token request was made - expect( apiFetch ).toHaveBeenNthCalledWith( - 1, - apiResponses[ 0 ].request - ); - // Metadata request was made - expect( apiFetch ).toHaveBeenNthCalledWith( - 2, - apiResponses[ 1 ].request - ); - // TODO: check why metadata request is called two times - expect( apiFetch ).toHaveBeenNthCalledWith( - 3, - apiResponses[ 1 ].request - ); - // Oembed request was made - expect( apiFetch ).toHaveBeenNthCalledWith( - 4, - apiResponses[ 2 ].request + // Requests: + // - Token request + // - Metadata request x 2 + // - Check ownership request x 2 + // - Oembed request + expect( apiFetch ).toHaveBeenCalledTimes( 6 ); + FETCH_ITEMS.forEach( ( fetch ) => + expect( apiFetch ).toHaveBeenCalledWith( fetch.request ) ); // Check loading overlay is displayed before the player is ready @@ -216,26 +227,18 @@ describe( 'VideoPress block - Uploads', () => { const player = getByTestId( 'videopress-player' ); // Notify the player is ready - fireEvent( player, 'message', { - nativeEvent: { - data: JSON.stringify( { - type: 'message', - event: 'videopress_ready', - } ), - }, + sendWebViewMessage( player, { + type: 'message', + event: 'videopress_ready', } ); expect( player ).toBeVisible(); // At this point the player should be showing the conversion state. // Hence, let's notify the loaded state. - fireEvent( player, 'message', { - nativeEvent: { - data: JSON.stringify( { - type: 'message', - event: 'videopress_loading_state', - state: 'loaded', - } ), - }, + sendWebViewMessage( player, { + type: 'message', + event: 'videopress_loading_state', + state: 'loaded', } ); // At this point the player should be ready to be used. @@ -244,21 +247,18 @@ describe( 'VideoPress block - Uploads', () => { } ); it( 'uploads a video from media library', async () => { - const guid = 'AAAAAAA2'; const media = { type: 'video', id: 2000, url: 'https://test.files.wordpress.com/local-video-2.mp4', metadata: { - videopressGUID: guid, + videopressGUID: VIDEOPRESS_GUID, }, }; const { expectMediaPickerCall, mediaPickerCallback, } = setupMediaPicker(); - const apiResponses = generateApiResponses( guid ); - setupApiFetch( apiResponses ); const screen = await initializeEditor( { initialHtml, @@ -279,25 +279,14 @@ describe( 'VideoPress block - Uploads', () => { await mediaPickerCallback( media ); - // Token request was made - expect( apiFetch ).toHaveBeenNthCalledWith( - 1, - apiResponses[ 0 ].request - ); - // Metadata request was made - expect( apiFetch ).toHaveBeenNthCalledWith( - 2, - apiResponses[ 1 ].request - ); - // TODO: check why metadata request is called two times - expect( apiFetch ).toHaveBeenNthCalledWith( - 3, - apiResponses[ 1 ].request - ); - // Oembed request was made - expect( apiFetch ).toHaveBeenNthCalledWith( - 4, - apiResponses[ 2 ].request + // Requests: + // - Token request + // - Metadata request x 2 + // - Check ownership request x 2 + // - Oembed request + expect( apiFetch ).toHaveBeenCalledTimes( 6 ); + FETCH_ITEMS.forEach( ( fetch ) => + expect( apiFetch ).toHaveBeenCalledWith( fetch.request ) ); // Check loading overlay is displayed before the player is ready @@ -305,26 +294,19 @@ describe( 'VideoPress block - Uploads', () => { const player = getByTestId( 'videopress-player' ); // Notify the player is ready - fireEvent( player, 'message', { - nativeEvent: { - data: JSON.stringify( { - type: 'message', - event: 'videopress_ready', - } ), - }, + sendWebViewMessage( player, { + type: 'message', + event: 'videopress_ready', } ); + expect( player ).toBeVisible(); // At this point the player should be showing the conversion state. // Hence, let's notify the loaded state. - fireEvent( player, 'message', { - nativeEvent: { - data: JSON.stringify( { - type: 'message', - event: 'videopress_loading_state', - state: 'loaded', - } ), - }, + sendWebViewMessage( player, { + type: 'message', + event: 'videopress_loading_state', + state: 'loaded', } ); // At this point the player should be ready to be used. @@ -333,7 +315,6 @@ describe( 'VideoPress block - Uploads', () => { } ); it( 'takes a video and uploads it', async () => { - const guid = 'AAAAAAA3'; const media = { type: 'video', localId: 3, @@ -347,8 +328,6 @@ describe( 'VideoPress block - Uploads', () => { expectMediaPickerCall, mediaPickerCallback, } = setupMediaPicker(); - const apiResponses = generateApiResponses( guid ); - setupApiFetch( apiResponses ); const screen = await initializeEditor( { initialHtml, @@ -380,30 +359,23 @@ describe( 'VideoPress block - Uploads', () => { await notifySucceedState( { ...media, metadata: Platform.select( { - android: { videopressGUID: guid }, - ios: { id: guid }, + android: { + videopressGUID: VIDEOPRESS_GUID, + }, + ios: { + id: VIDEOPRESS_GUID, + }, } ), } ); - // Token request was made - expect( apiFetch ).toHaveBeenNthCalledWith( - 1, - apiResponses[ 0 ].request - ); - // Metadata request was made - expect( apiFetch ).toHaveBeenNthCalledWith( - 2, - apiResponses[ 1 ].request - ); - // TODO: check why metadata request is called two times - expect( apiFetch ).toHaveBeenNthCalledWith( - 3, - apiResponses[ 1 ].request - ); - // Oembed request was made - expect( apiFetch ).toHaveBeenNthCalledWith( - 4, - apiResponses[ 2 ].request + // Requests: + // - Token request + // - Metadata request x 2 + // - Check ownership request x 2 + // - Oembed request + expect( apiFetch ).toHaveBeenCalledTimes( 6 ); + FETCH_ITEMS.forEach( ( fetch ) => + expect( apiFetch ).toHaveBeenCalledWith( fetch.request ) ); // Check loading overlay is displayed before the player is ready @@ -411,26 +383,18 @@ describe( 'VideoPress block - Uploads', () => { const player = getByTestId( 'videopress-player' ); // Notify the player is ready - fireEvent( player, 'message', { - nativeEvent: { - data: JSON.stringify( { - type: 'message', - event: 'videopress_ready', - } ), - }, + sendWebViewMessage( player, { + type: 'message', + event: 'videopress_ready', } ); expect( player ).toBeVisible(); // At this point the player should be showing the conversion state. // Hence, let's notify the loaded state. - fireEvent( player, 'message', { - nativeEvent: { - data: JSON.stringify( { - type: 'message', - event: 'videopress_loading_state', - state: 'loaded', - } ), - }, + sendWebViewMessage( player, { + type: 'message', + event: 'videopress_loading_state', + state: 'loaded', } ); // At this point the player should be ready to be used. From 04b7603a4493f79963cc9eaa898932e56841b956 Mon Sep 17 00:00:00 2001 From: Carlos Garcia Date: Mon, 8 May 2023 13:20:24 +0200 Subject: [PATCH 08/12] Remove api fetch call count expectation The editor makes different requests during the initialization, some of them get cached once they are performed. Hence, fetch call count is not reliable value to check. --- src/test/videopress/upload.js | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/src/test/videopress/upload.js b/src/test/videopress/upload.js index ba2a890b86..8ed172dd42 100644 --- a/src/test/videopress/upload.js +++ b/src/test/videopress/upload.js @@ -154,6 +154,9 @@ describe( 'VideoPress block - Uploads', () => { fireEvent( getByTestId( 'media-options-picker' ), 'backdropPress' ); } + // Check that block remains selected after displaying the media + // options picker. This is performed by checking if the block + // actions menu button is visible. const blockActionsButton = getByLabelText( /Open Block Actions Menu/ ); expect( blockActionsButton ).toBeVisible(); } ); @@ -163,7 +166,7 @@ describe( 'VideoPress block - Uploads', () => { type: 'video', localId: 1, localUrl: 'file:///local-video-1.mp4', - serverId: 2000, + serverId: 1000, serverUrl: 'https://videopress.wordpress.com/local-video-1.mp4', }; @@ -214,19 +217,18 @@ describe( 'VideoPress block - Uploads', () => { // Requests: // - Token request - // - Metadata request x 2 - // - Check ownership request x 2 + // - Metadata request + // - Check ownership request // - Oembed request - expect( apiFetch ).toHaveBeenCalledTimes( 6 ); FETCH_ITEMS.forEach( ( fetch ) => expect( apiFetch ).toHaveBeenCalledWith( fetch.request ) ); // Check loading overlay is displayed before the player is ready expect( within( block ).getByText( 'Loading' ) ).toBeVisible(); - const player = getByTestId( 'videopress-player' ); // Notify the player is ready + const player = getByTestId( 'videopress-player' ); sendWebViewMessage( player, { type: 'message', event: 'videopress_ready', @@ -281,24 +283,22 @@ describe( 'VideoPress block - Uploads', () => { // Requests: // - Token request - // - Metadata request x 2 - // - Check ownership request x 2 + // - Metadata request + // - Check ownership request // - Oembed request - expect( apiFetch ).toHaveBeenCalledTimes( 6 ); FETCH_ITEMS.forEach( ( fetch ) => expect( apiFetch ).toHaveBeenCalledWith( fetch.request ) ); // Check loading overlay is displayed before the player is ready expect( within( block ).getByText( 'Loading' ) ).toBeVisible(); - const player = getByTestId( 'videopress-player' ); // Notify the player is ready + const player = getByTestId( 'videopress-player' ); sendWebViewMessage( player, { type: 'message', event: 'videopress_ready', } ); - expect( player ).toBeVisible(); // At this point the player should be showing the conversion state. @@ -319,7 +319,7 @@ describe( 'VideoPress block - Uploads', () => { type: 'video', localId: 3, localUrl: 'file:///local-video-3.mp4', - serverId: 2000, + serverId: 3000, serverUrl: 'https://videopress.wordpress.com/local-video-3.mp4', }; @@ -370,10 +370,9 @@ describe( 'VideoPress block - Uploads', () => { // Requests: // - Token request - // - Metadata request x 2 - // - Check ownership request x 2 + // - Metadata request + // - Check ownership request // - Oembed request - expect( apiFetch ).toHaveBeenCalledTimes( 6 ); FETCH_ITEMS.forEach( ( fetch ) => expect( apiFetch ).toHaveBeenCalledWith( fetch.request ) ); From 510b4b8ba18fd434efe9c11e8a7544defe89c298 Mon Sep 17 00:00:00 2001 From: Carlos Garcia Date: Mon, 8 May 2023 13:21:56 +0200 Subject: [PATCH 09/12] Add more test cases --- .../videopress/__snapshots__/upload.js.snap | 8 + src/test/videopress/upload.js | 256 +++++++++++++++++- 2 files changed, 251 insertions(+), 13 deletions(-) diff --git a/src/test/videopress/__snapshots__/upload.js.snap b/src/test/videopress/__snapshots__/upload.js.snap index abe8ba5550..77038ffadb 100644 --- a/src/test/videopress/__snapshots__/upload.js.snap +++ b/src/test/videopress/__snapshots__/upload.js.snap @@ -1,5 +1,13 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`VideoPress block - Uploads adds video by inserting URL: video ready 1`] = `""`; + +exports[`VideoPress block - Uploads cancel upload 1`] = `""`; + +exports[`VideoPress block - Uploads finishes pending uploads upon opening the editor 1`] = `""`; + +exports[`VideoPress block - Uploads handles upload failure 1`] = `""`; + exports[`VideoPress block - Uploads takes a video and uploads it: loading state 1`] = `""`; exports[`VideoPress block - Uploads takes a video and uploads it: video ready 1`] = `""`; diff --git a/src/test/videopress/upload.js b/src/test/videopress/upload.js index 8ed172dd42..c21593946e 100644 --- a/src/test/videopress/upload.js +++ b/src/test/videopress/upload.js @@ -1,7 +1,20 @@ +/** + * WordPress dependencies + */ +import { Platform } from '@wordpress/element'; +import apiFetch from '@wordpress/api-fetch'; +import { dispatch } from '@wordpress/data'; +import { store as coreStore } from '@wordpress/core-data'; +import { + requestImageFailedRetryDialog, + requestImageUploadCancelDialog, +} from '@wordpress/react-native-bridge'; + /** * External dependencies */ import { + act, fireEvent, getBlock, getEditorHtml, @@ -13,11 +26,8 @@ import { setupPicker, setupApiFetch, } from 'test/helpers'; -import { Platform } from '@wordpress/element'; import { ActionSheetIOS } from 'react-native'; -import apiFetch from '@wordpress/api-fetch'; -import { dispatch } from '@wordpress/data'; -import { store as coreStore } from '@wordpress/core-data'; +import prompt from 'react-native-prompt-android'; /** * Internal dependencies @@ -28,6 +38,7 @@ import { } from '../../jetpack-editor-setup'; jest.mock( '@wordpress/api-fetch' ); +jest.mock( 'react-native-prompt-android', () => jest.fn() ); const initialHtml = ''; @@ -379,9 +390,70 @@ describe( 'VideoPress block - Uploads', () => { // Check loading overlay is displayed before the player is ready expect( within( block ).getByText( 'Loading' ) ).toBeVisible(); + + // Notify the player is ready const player = getByTestId( 'videopress-player' ); + sendWebViewMessage( player, { + type: 'message', + event: 'videopress_ready', + } ); + expect( player ).toBeVisible(); + + // At this point the player should be showing the conversion state. + // Hence, let's notify the loaded state. + sendWebViewMessage( player, { + type: 'message', + event: 'videopress_loading_state', + state: 'loaded', + } ); + + // At this point the player should be ready to be used. + expect( within( block ).queryByText( 'Loading' ) ).toBeNull(); + expect( getEditorHtml() ).toMatchSnapshot( 'video ready' ); + } ); + + it( 'adds video by inserting URL', async () => { + let promptApply; + prompt.mockImplementation( ( title, message, [ , apply ] ) => { + promptApply = apply.onPress; + } ); + + const screen = await initializeEditor( { + initialHtml, + } ); + const { getByText, getByTestId } = screen; + const { selectOption } = setupPicker( screen, MEDIA_OPTIONS ); + // Clear previous calls to `apiFetch` + apiFetch.mockClear(); + + // Block is visible + const block = await getBlock( screen, 'VideoPress' ); + expect( block ).toBeVisible(); + + // Add video from WordPress media library + fireEvent.press( getByText( 'ADD VIDEO' ) ); + selectOption( 'Insert from URL' ); + expect( prompt ).toHaveBeenCalled(); + + // Mock prompt dialog + await act( () => + promptApply( `https://videopress.com/v/${ VIDEOPRESS_GUID }` ) + ); + + // Requests: + // - Token request + // - Metadata request + // - Check ownership request + // - Oembed request + FETCH_ITEMS.forEach( ( fetch ) => + expect( apiFetch ).toHaveBeenCalledWith( fetch.request ) + ); + + // Check loading overlay is displayed before the player is ready + expect( within( block ).getByText( 'Loading' ) ).toBeVisible(); // Notify the player is ready + const player = getByTestId( 'videopress-player' ); sendWebViewMessage( player, { type: 'message', event: 'videopress_ready', @@ -401,16 +473,174 @@ describe( 'VideoPress block - Uploads', () => { expect( getEditorHtml() ).toMatchSnapshot( 'video ready' ); } ); - it.skip( 'adds video by inserting URL', async () => {} ); + it( 'finishes pending uploads upon opening the editor', async () => { + const media = { + type: 'video', + localId: 4, + localUrl: 'file:///local-video-4.mp4', + serverId: 4000, + serverUrl: 'https://videopress.wordpress.com/local-video-4.mp4', + }; + const { notifyUploadingState, notifySucceedState } = setupMediaUpload(); + + const screen = await initializeEditor( { + initialHtml: ``, + } ); + const { getByTestId } = screen; + + // Block is visible + const block = await getBlock( screen, 'VideoPress' ); + expect( block ).toBeVisible(); + + // Notify that the media items are uploading + await notifyUploadingState( media ); + await notifyUploadingState( media ); - it.skip( 'finishes pending uploads upon opening the editor', async () => {} ); - it.skip( 'handles upload failure', async () => {} ); - it.skip( 'cancel upload', async () => {} ); + // During upload progress we keep displaying the loading state + expect( getByTestId( 'videopress-uploading-video' ) ).toBeVisible(); - it.skip( 'sets caption', async () => {} ); + // Upload finish + await notifySucceedState( { + ...media, + metadata: Platform.select( { + android: { + videopressGUID: VIDEOPRESS_GUID, + }, + ios: { + id: VIDEOPRESS_GUID, + }, + } ), + } ); + + // Requests: + // - Token request + // - Metadata request + // - Check ownership request + // - Oembed request + FETCH_ITEMS.forEach( ( fetch ) => + expect( apiFetch ).toHaveBeenCalledWith( fetch.request ) + ); - it.skip( 'replace video with local video', async () => {} ); - it.skip( 'replace video taking a new one', async () => {} ); - it.skip( 'replace video video item from media library', async () => {} ); - it.skip( 'replace video video with new URL', async () => {} ); + // Check loading overlay is displayed before the player is ready + expect( within( block ).getByText( 'Loading' ) ).toBeVisible(); + + // Notify the player is ready + const player = getByTestId( 'videopress-player' ); + sendWebViewMessage( player, { + type: 'message', + event: 'videopress_ready', + } ); + expect( player ).toBeVisible(); + + // At this point the player should be showing the conversion state. + // Hence, let's notify the loaded state. + sendWebViewMessage( player, { + type: 'message', + event: 'videopress_loading_state', + state: 'loaded', + } ); + + // At this point the player should be ready to be used. + expect( within( block ).queryByText( 'Loading' ) ).toBeNull(); + expect( getEditorHtml() ).toMatchSnapshot(); + } ); + + it( 'handles upload failure', async () => { + const media = { + type: 'video', + localId: 1, + localUrl: 'file:///local-video-1.mp4', + serverId: 1000, + serverUrl: 'https://videopress.wordpress.com/local-video-1.mp4', + }; + + const { notifyUploadingState, notifyFailedState } = setupMediaUpload(); + const { + expectMediaPickerCall, + mediaPickerCallback, + } = setupMediaPicker(); + + const screen = await initializeEditor( { + initialHtml, + } ); + const { getByText, getByTestId } = screen; + const { selectOption } = setupPicker( screen, MEDIA_OPTIONS ); + + // Block is visible + const block = await getBlock( screen, 'VideoPress' ); + expect( block ).toBeVisible(); + + // Upload video from device + fireEvent.press( getByText( 'ADD VIDEO' ) ); + selectOption( 'Choose from device' ); + expectMediaPickerCall( 'DEVICE_MEDIA_LIBRARY', [ 'video' ], false ); + + // Block is uploading the video + await mediaPickerCallback( media ); + await notifyUploadingState( media ); + + // During upload progress we keep displaying the loading state + expect( getByTestId( 'videopress-uploading-video' ) ).toBeVisible(); + + // Notify that the upload failed + await notifyFailedState( media ); + const uploadFailText = getByText( /Failed to insert media/ ); + expect( uploadFailText ).toBeVisible(); + + // Retry option available + fireEvent.press( uploadFailText ); + expect( requestImageFailedRetryDialog ).toHaveBeenCalledWith( + media.localId + ); + expect( getEditorHtml() ).toMatchSnapshot(); + } ); + + it( 'cancel upload', async () => { + const media = { + type: 'video', + localId: 1, + localUrl: 'file:///local-video-1.mp4', + serverId: 1000, + serverUrl: 'https://videopress.wordpress.com/local-video-1.mp4', + }; + + const { notifyUploadingState, notifyResetState } = setupMediaUpload(); + const { + expectMediaPickerCall, + mediaPickerCallback, + } = setupMediaPicker(); + + const screen = await initializeEditor( { + initialHtml, + } ); + const { getByText, getByTestId } = screen; + const { selectOption } = setupPicker( screen, MEDIA_OPTIONS ); + + // Block is visible + const block = await getBlock( screen, 'VideoPress' ); + expect( block ).toBeVisible(); + + // Upload video from device + fireEvent.press( getByText( 'ADD VIDEO' ) ); + selectOption( 'Choose from device' ); + expectMediaPickerCall( 'DEVICE_MEDIA_LIBRARY', [ 'video' ], false ); + + // Block is uploading the video + await mediaPickerCallback( media ); + await notifyUploadingState( media ); + + // During upload progress we keep displaying the loading state + const uploadingVideoView = getByTestId( 'videopress-uploading-video' ); + expect( uploadingVideoView ).toBeVisible(); + + // Cancel upload + fireEvent.press( uploadingVideoView ); + expect( requestImageUploadCancelDialog ).toHaveBeenCalledWith( + media.localId + ); + await notifyResetState( media ); + + expect( within( block ).queryByText( 'Loading' ) ).toBeNull(); + expect( getEditorHtml() ).toMatchSnapshot(); + } ); } ); From 9b53be641b68d05c24303485d8e6437286bfabc8 Mon Sep 17 00:00:00 2001 From: Carlos Garcia Date: Mon, 8 May 2023 19:09:23 +0200 Subject: [PATCH 10/12] Update Gutenberg ref --- gutenberg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gutenberg b/gutenberg index 282da7ca4c..3c72d13215 160000 --- a/gutenberg +++ b/gutenberg @@ -1 +1 @@ -Subproject commit 282da7ca4c74725193c2c362925c9c8b5946b993 +Subproject commit 3c72d1321555080fc0cf8d367cfeba0c3b581fb4 From 29a8c1cef789150bf0a1aa21e1b77b801bcd4640 Mon Sep 17 00:00:00 2001 From: Carlos Garcia Date: Wed, 10 May 2023 11:10:14 +0200 Subject: [PATCH 11/12] Update Jetpack ref --- jetpack | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jetpack b/jetpack index eb6d106409..81d188a99e 160000 --- a/jetpack +++ b/jetpack @@ -1 +1 @@ -Subproject commit eb6d1064093de154a413681a89bb929d4dd50c0c +Subproject commit 81d188a99e4ad35b07718cc566b46442992467ad From 546a04de5213cccd1bd5fc4090def59b04a76665 Mon Sep 17 00:00:00 2001 From: Carlos Garcia Date: Wed, 10 May 2023 11:10:32 +0200 Subject: [PATCH 12/12] Update Gutenberg ref --- gutenberg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gutenberg b/gutenberg index 3c72d13215..d0dba7f617 160000 --- a/gutenberg +++ b/gutenberg @@ -1 +1 @@ -Subproject commit 3c72d1321555080fc0cf8d367cfeba0c3b581fb4 +Subproject commit d0dba7f617e57159598808270d90cc021404862f