diff --git a/test/e2e/config/encrypted.enc b/test/e2e/config/encrypted.enc index 94b57a42c68e3..c9f3ccbe78a9e 100644 Binary files a/test/e2e/config/encrypted.enc and b/test/e2e/config/encrypted.enc differ diff --git a/test/e2e/lib/gutenberg/blocks/blog-posts-block-component.js b/test/e2e/lib/gutenberg/blocks/blog-posts-block-component.js new file mode 100644 index 0000000000000..7569e441f3bcd --- /dev/null +++ b/test/e2e/lib/gutenberg/blocks/blog-posts-block-component.js @@ -0,0 +1,11 @@ +/** + * Internal dependencies + */ +import GutenbergBlockComponent from './gutenberg-block-component'; + +class BlogPostsBlockComponent extends GutenbergBlockComponent { + static blockTitle = 'Blog Posts'; + static blockName = 'a8c/blog-posts'; +} + +export { BlogPostsBlockComponent }; diff --git a/test/e2e/lib/gutenberg/blocks/contact-form-block-component.js b/test/e2e/lib/gutenberg/blocks/contact-form-block-component.js index abf8cc9a4431f..9b484b43931af 100644 --- a/test/e2e/lib/gutenberg/blocks/contact-form-block-component.js +++ b/test/e2e/lib/gutenberg/blocks/contact-form-block-component.js @@ -9,13 +9,15 @@ import { By } from 'selenium-webdriver'; import * as driverHelper from '../../driver-helper'; import GutenbergBlockComponent from './gutenberg-block-component'; -export class ContactFormBlockComponent extends GutenbergBlockComponent { - constructor( driver, blockID ) { - super( driver, blockID ); - } +class ContactFormBlockComponent extends GutenbergBlockComponent { + static blockTitle = 'Form'; + static blockName = 'jetpack/contact-form'; async _postInit() { - return await driverHelper.clickWhenClickable( this.driver, By.css( '.components-button.block-editor-block-variation-picker__variation' ) ); + return await driverHelper.clickWhenClickable( + this.driver, + By.css( '.components-button.block-editor-block-variation-picker__variation' ) + ); } async openEditSettings() { @@ -50,3 +52,5 @@ export class ContactFormBlockComponent extends GutenbergBlockComponent { ); } } + +export { ContactFormBlockComponent }; diff --git a/test/e2e/lib/gutenberg/blocks/contact-info-block-component.js b/test/e2e/lib/gutenberg/blocks/contact-info-block-component.js new file mode 100644 index 0000000000000..8911f4d385d29 --- /dev/null +++ b/test/e2e/lib/gutenberg/blocks/contact-info-block-component.js @@ -0,0 +1,11 @@ +/** + * Internal dependencies + */ +import GutenbergBlockComponent from './gutenberg-block-component'; + +class ContactInfoBlockComponent extends GutenbergBlockComponent { + static blockTitle = 'Contact Info'; + static blockName = 'jetpack/contact-info'; +} + +export { ContactInfoBlockComponent }; diff --git a/test/e2e/lib/gutenberg/blocks/dynamic-separator-block-component.js b/test/e2e/lib/gutenberg/blocks/dynamic-separator-block-component.js new file mode 100644 index 0000000000000..020780534b3dd --- /dev/null +++ b/test/e2e/lib/gutenberg/blocks/dynamic-separator-block-component.js @@ -0,0 +1,11 @@ +/** + * Internal dependencies + */ +import GutenbergBlockComponent from './gutenberg-block-component'; + +class DynamicSeparatorBlockComponent extends GutenbergBlockComponent { + static blockTitle = 'Dynamic HR'; + static blockName = 'coblocks/dynamic-separator'; +} + +export { DynamicSeparatorBlockComponent }; diff --git a/test/e2e/lib/gutenberg/blocks/embeds-block-component.js b/test/e2e/lib/gutenberg/blocks/embeds-block-component.js index 0c4afdc263632..9c9e46abd598e 100644 --- a/test/e2e/lib/gutenberg/blocks/embeds-block-component.js +++ b/test/e2e/lib/gutenberg/blocks/embeds-block-component.js @@ -11,10 +11,6 @@ import * as driverHelper from '../../driver-helper'; import GutenbergBlockComponent from './gutenberg-block-component'; export default class EmbedsBlockComponent extends GutenbergBlockComponent { - constructor( driver, blockID ) { - super( driver, blockID ); - } - async embedUrl( url ) { await driverHelper.setWhenSettable( this.driver, diff --git a/test/e2e/lib/gutenberg/blocks/gallery-masonry-block-component.js b/test/e2e/lib/gutenberg/blocks/gallery-masonry-block-component.js new file mode 100644 index 0000000000000..5cb1d2fe38e14 --- /dev/null +++ b/test/e2e/lib/gutenberg/blocks/gallery-masonry-block-component.js @@ -0,0 +1,11 @@ +/** + * Internal dependencies + */ +import GutenbergBlockComponent from './gutenberg-block-component'; + +class GalleryMasonryBlockComponent extends GutenbergBlockComponent { + static blockTitle = 'Masonry'; + static blockName = 'coblocks/gallery-masonry'; +} + +export { GalleryMasonryBlockComponent }; diff --git a/test/e2e/lib/gutenberg/blocks/image-block-component.js b/test/e2e/lib/gutenberg/blocks/image-block-component.js index 12f2fc76039a0..0699a89ed84f0 100644 --- a/test/e2e/lib/gutenberg/blocks/image-block-component.js +++ b/test/e2e/lib/gutenberg/blocks/image-block-component.js @@ -10,10 +10,6 @@ import * as driverHelper from '../../driver-helper'; import GutenbergBlockComponent from './gutenberg-block-component'; export class ImageBlockComponent extends GutenbergBlockComponent { - constructor( driver, blockID ) { - super( driver, blockID ); - } - async uploadImage( fileDetails ) { await driverHelper.waitTillPresentAndDisplayed( this.driver, diff --git a/test/e2e/lib/gutenberg/blocks/index.js b/test/e2e/lib/gutenberg/blocks/index.js new file mode 100644 index 0000000000000..709da7d278e87 --- /dev/null +++ b/test/e2e/lib/gutenberg/blocks/index.js @@ -0,0 +1,18 @@ +// TODO some blocks are exported as default and some not... maybe change all of them to export as default? +export { BlogPostsBlockComponent } from './blog-posts-block-component'; +export { ContactFormBlockComponent } from './contact-form-block-component'; +export { ContactInfoBlockComponent } from './contact-info-block-component'; +export { DynamicSeparatorBlockComponent } from './dynamic-separator-block-component'; +export { GalleryMasonryBlockComponent } from './gallery-masonry-block-component'; +export { ImageBlockComponent } from './image-block-component'; +export { LayoutGridBlockComponent } from './layout-grid-block-component'; +export { RatingStarBlockComponent } from './rating-star-block-component'; +export { ShortcodeBlockComponent } from './shortcode-block-component'; +export { SlideshowBlockComponent } from './slideshow-block-component'; +export { SubscriptionsBlockComponent } from './subscriptions-block-component'; +export { TiledGalleryBlockComponent } from './tiled-gallery-block-component'; +export { YoutubeBlockComponent } from './youtube-block-component'; +export { default as EmbedsBlockComponent } from './embeds-block-component'; +export { default as GutenbergBlockComponent } from './gutenberg-block-component'; +export { default as MarkdownBlockComponent } from './markdown-block-component'; +export { default as SimplePaymentBlockComponent } from './payment-block-component'; diff --git a/test/e2e/lib/gutenberg/blocks/layout-grid-block-component.js b/test/e2e/lib/gutenberg/blocks/layout-grid-block-component.js new file mode 100644 index 0000000000000..8aab24352934a --- /dev/null +++ b/test/e2e/lib/gutenberg/blocks/layout-grid-block-component.js @@ -0,0 +1,11 @@ +/** + * Internal dependencies + */ +import GutenbergBlockComponent from './gutenberg-block-component'; + +class LayoutGridBlockComponent extends GutenbergBlockComponent { + static blockTitle = 'Layout Grid'; + static blockName = 'jetpack/layout-grid'; +} + +export { LayoutGridBlockComponent }; diff --git a/test/e2e/lib/gutenberg/blocks/markdown-block-component.js b/test/e2e/lib/gutenberg/blocks/markdown-block-component.js index 942b57bcec297..de91523c9fbd0 100644 --- a/test/e2e/lib/gutenberg/blocks/markdown-block-component.js +++ b/test/e2e/lib/gutenberg/blocks/markdown-block-component.js @@ -10,10 +10,6 @@ import * as driverHelper from '../../driver-helper'; import GutenbergBlockComponent from './gutenberg-block-component'; export default class MarkdownBlockComponent extends GutenbergBlockComponent { - constructor( driver, blockID ) { - super( driver, blockID ); - } - async setContent( content ) { const inputSelector = By.css( `${ this.blockID } textarea.wp-block-jetpack-markdown__editor` ); diff --git a/test/e2e/lib/gutenberg/blocks/payment-block-component.js b/test/e2e/lib/gutenberg/blocks/payment-block-component.js index c8cb424e25bd7..64b9bac55820b 100644 --- a/test/e2e/lib/gutenberg/blocks/payment-block-component.js +++ b/test/e2e/lib/gutenberg/blocks/payment-block-component.js @@ -10,10 +10,6 @@ import * as driverHelper from '../../driver-helper'; import GutenbergBlockComponent from './gutenberg-block-component'; export default class SimplePaymentsBlockComponent extends GutenbergBlockComponent { - constructor( driver, blockID ) { - super( driver, blockID ); - } - async insertPaymentButtonDetails( { title = 'Button', description = 'Description', diff --git a/test/e2e/lib/gutenberg/blocks/rating-star-block-component.js b/test/e2e/lib/gutenberg/blocks/rating-star-block-component.js new file mode 100644 index 0000000000000..922f8adb7240e --- /dev/null +++ b/test/e2e/lib/gutenberg/blocks/rating-star-block-component.js @@ -0,0 +1,11 @@ +/** + * Internal dependencies + */ +import GutenbergBlockComponent from './gutenberg-block-component'; + +class RatingStarBlockComponent extends GutenbergBlockComponent { + static blockTitle = 'Star Rating'; + static blockName = 'jetpack/rating-star'; +} + +export { RatingStarBlockComponent }; diff --git a/test/e2e/lib/gutenberg/blocks/shortcode-block-component.js b/test/e2e/lib/gutenberg/blocks/shortcode-block-component.js index 9126ee4054a13..c40b7bdf82536 100644 --- a/test/e2e/lib/gutenberg/blocks/shortcode-block-component.js +++ b/test/e2e/lib/gutenberg/blocks/shortcode-block-component.js @@ -10,10 +10,6 @@ import * as driverHelper from '../../driver-helper'; import GutenbergBlockComponent from './gutenberg-block-component'; export class ShortcodeBlockComponent extends GutenbergBlockComponent { - constructor( driver, blockID ) { - super( driver, blockID ); - } - async enterShortcode( shortcode ) { const shortcodeSelector = By.css( 'textarea.editor-plain-text' ); await driverHelper.waitTillPresentAndDisplayed( this.driver, shortcodeSelector ); diff --git a/test/e2e/lib/gutenberg/blocks/slideshow-block-component.js b/test/e2e/lib/gutenberg/blocks/slideshow-block-component.js new file mode 100644 index 0000000000000..3cc08e8b2c360 --- /dev/null +++ b/test/e2e/lib/gutenberg/blocks/slideshow-block-component.js @@ -0,0 +1,11 @@ +/** + * Internal dependencies + */ +import GutenbergBlockComponent from './gutenberg-block-component'; + +class SlideshowBlockComponent extends GutenbergBlockComponent { + static blockTitle = 'Slideshow'; + static blockName = 'jetpack/slideshow'; +} + +export { SlideshowBlockComponent }; diff --git a/test/e2e/lib/gutenberg/blocks/subscriptions-block-component.js b/test/e2e/lib/gutenberg/blocks/subscriptions-block-component.js new file mode 100644 index 0000000000000..24f1ed782dc8d --- /dev/null +++ b/test/e2e/lib/gutenberg/blocks/subscriptions-block-component.js @@ -0,0 +1,11 @@ +/** + * Internal dependencies + */ +import GutenbergBlockComponent from './gutenberg-block-component'; + +class SubscriptionsBlockComponent extends GutenbergBlockComponent { + static blockTitle = 'Subscription Form'; + static blockName = 'jetpack/subscriptions'; +} + +export { SubscriptionsBlockComponent }; diff --git a/test/e2e/lib/gutenberg/blocks/tiled-gallery-block-component.js b/test/e2e/lib/gutenberg/blocks/tiled-gallery-block-component.js new file mode 100644 index 0000000000000..f0e51dc02bcac --- /dev/null +++ b/test/e2e/lib/gutenberg/blocks/tiled-gallery-block-component.js @@ -0,0 +1,28 @@ +/** + * External dependencies + */ +import { By } from 'selenium-webdriver'; + +/** + * Internal dependencies + */ +import GutenbergBlockComponent from './gutenberg-block-component'; + +class TiledGalleryBlockComponent extends GutenbergBlockComponent { + static blockTitle = 'Tiled Gallery'; + static blockName = 'jetpack/tiled-gallery'; + + fileInputSelector = By.xpath( + `//*[@id="${ this.blockID.slice( 1 ) }"]/div/div/div[3]/div[2]/input` + ); + + async uploadImages( filesDetails ) { + const fileInput = this.driver.findElement( this.fileInputSelector ); + + const files = filesDetails.map( ( f ) => f.file ).join( '\n ' ); + + fileInput.sendKeys( files ); + } +} + +export { TiledGalleryBlockComponent }; diff --git a/test/e2e/lib/gutenberg/blocks/youtube-block-component.js b/test/e2e/lib/gutenberg/blocks/youtube-block-component.js new file mode 100644 index 0000000000000..3696756ff611e --- /dev/null +++ b/test/e2e/lib/gutenberg/blocks/youtube-block-component.js @@ -0,0 +1,11 @@ +/** + * Internal dependencies + */ +import GutenbergBlockComponent from './gutenberg-block-component'; + +class YoutubeBlockComponent extends GutenbergBlockComponent { + static blockTitle = 'YouTube'; + static blockName = 'core-embed/youtube'; +} + +export { YoutubeBlockComponent }; diff --git a/test/e2e/lib/gutenberg/gutenberg-editor-component.js b/test/e2e/lib/gutenberg/gutenberg-editor-component.js index 961a33f1e6f98..1c525af73538e 100644 --- a/test/e2e/lib/gutenberg/gutenberg-editor-component.js +++ b/test/e2e/lib/gutenberg/gutenberg-editor-component.js @@ -10,7 +10,7 @@ import { kebabCase } from 'lodash'; import * as driverHelper from '../driver-helper'; import * as driverManager from '../driver-manager.js'; import AsyncBaseContainer from '../async-base-container'; -import { ContactFormBlockComponent } from './blocks/contact-form-block-component'; +import { ContactFormBlockComponent, GutenbergBlockComponent } from './blocks'; import { ShortcodeBlockComponent } from './blocks/shortcode-block-component'; import { ImageBlockComponent } from './blocks/image-block-component'; @@ -145,16 +145,72 @@ export default class GutenbergEditorComponent extends AsyncBaseContainer { const contactFormBlock = await ContactFormBlockComponent.Expect( this.driver, blockID ); await contactFormBlock.openEditSettings(); await contactFormBlock.insertEmail( email ); - return await contactFormBlock.insertSubject( subject ); + return contactFormBlock.insertSubject( subject ); } - async contactFormDisplayedInEditor() { - return await driverHelper.isEventuallyPresentAndDisplayed( + toggleMoreToolsAndOptions() { + return driverHelper.clickWhenClickable( this.driver, - By.css( '[data-type="jetpack/contact-form"]' ) + By.xpath( "//button[@aria-label='More tools & options']" ) ); } + async switchToCodeView() { + await this.toggleMoreToolsAndOptions(); + + // Now click to switch to the code editor + await driverHelper.clickWhenClickable( + this.driver, + By.xpath( "//div[@aria-label='More tools & options']/div[2]/div[2]/button[2]" ) + ); + + const textAreaSelector = By.css( 'textarea.editor-post-text-editor' ); + + await driverHelper.waitTillPresentAndDisplayed( this.driver, textAreaSelector ); + + // close the menu + await this.toggleMoreToolsAndOptions(); + + return this.driver.findElement( textAreaSelector ); + } + + async switchToBlockEditor() { + await this.toggleMoreToolsAndOptions(); + + await driverHelper.clickWhenClickable( + this.driver, + By.xpath( "//div[@aria-label='More tools & options']/div[2]/div[2]/button[1]" ) + ); + + // close the menu + await this.toggleMoreToolsAndOptions(); + } + + async copyBlocksCode() { + const codeEditor = await this.switchToCodeView(); + return this.driver.executeScript( + 'arguments[0].select(); document.execCommand("copy");', + codeEditor + ); + } + + async pasteBlocksCode() { + const codeEditor = await this.switchToCodeView(); + // Might not work in a Mac? + return codeEditor.sendKeys( Key.CONTROL + 'v' ); + } + + blockDisplayedInEditor( dataTypeSelectorVal ) { + return driverHelper.isEventuallyPresentAndDisplayed( + this.driver, + By.css( `[data-type="${ dataTypeSelectorVal }"]` ) + ); + } + + contactFormDisplayedInEditor() { + return this.blockDisplayedInEditor( 'jetpack/contact-form' ); + } + async errorDisplayed() { await this.driver.sleep( 1000 ); return await driverHelper.isElementPresent( this.driver, By.css( '.editor-error-boundary' ) ); @@ -213,13 +269,13 @@ export default class GutenbergEditorComponent extends AsyncBaseContainer { } // return blockID - top level block id which is looks like `block-b91ce479-fb2d-45b7-ad92-22ae7a58cf04`. Should be used for further interaction with added block. - async addBlock( name ) { - name = name.charAt( 0 ).toUpperCase() + name.slice( 1 ); // Capitalize block name - let blockClass = kebabCase( name.toLowerCase() ); + async addBlock( title ) { + title = title.charAt( 0 ).toUpperCase() + title.slice( 1 ); // Capitalize block name + let blockClass = kebabCase( title.toLowerCase() ); let hasChildBlocks = false; let ariaLabel; let prefix = ''; - switch ( name ) { + switch ( title ) { case 'Instagram': case 'Twitter': case 'YouTube': @@ -257,9 +313,37 @@ export default class GutenbergEditorComponent extends AsyncBaseContainer { case 'Heading': ariaLabel = 'Write heading…'; break; + case 'Layout Grid': + prefix = 'jetpack-'; + break; + case 'Blog Posts': + prefix = 'a8c-'; + break; + case 'Subscription Form': + prefix = 'jetpack-'; + blockClass = 'subscriptions'; + break; + case 'Tiled Gallery': + prefix = 'jetpack-'; + break; + case 'Contact Info': + prefix = 'jetpack-'; + hasChildBlocks = true; + break; + case 'Slideshow': + prefix = 'jetpack-'; + break; + case 'Star Rating': + prefix = 'jetpack-'; + blockClass = 'rating-star'; + break; + case 'Masonry': + prefix = 'coblocks-'; + blockClass = 'gallery-masonry'; + break; } - const selectorAriaLabel = ariaLabel || `Block: ${ name }`; + const selectorAriaLabel = ariaLabel || `Block: ${ title }`; const inserterBlockItemSelector = By.css( `.edit-post-layout__inserter-panel .block-editor-inserter__block-list button.editor-block-list-item-${ prefix }${ blockClass }` @@ -270,7 +354,7 @@ export default class GutenbergEditorComponent extends AsyncBaseContainer { }[aria-label*='${ selectorAriaLabel }']` ); - await this.openBlockInserterAndSearch( name ); + await this.openBlockInserterAndSearch( title ); if ( await driverHelper.elementIsNotPresent( this.driver, inserterBlockItemSelector ) ) { await driverHelper.waitTillPresentAndDisplayed( this.driver, inserterBlockItemSelector ); @@ -284,6 +368,21 @@ export default class GutenbergEditorComponent extends AsyncBaseContainer { return await this.driver.findElement( insertedBlockSelector ).getAttribute( 'id' ); } + /** + * An alternative way of adding blocks to the editor by accepting the actual constructor + * class for the block, adding it to the editor, and returning an instance of this class. + * + * This allows for adding new blocks without the need to create new factory method in this class. + * You can just import the class of the block(s) you want to add and pass it to this function, which + * also means we don't need to couple the block class with this one. + * + * @param { GutenbergBlockComponent } blockClass A block class that responds to title and name + */ + async insertBlock( blockClass ) { + const blockID = await this.addBlock( blockClass.blockTitle ); + return blockClass.Expect( this.driver, blockID ); + } + async titleShown() { const titleSelector = By.css( '.editor-post-title__input' ); await driverHelper.waitTillPresentAndDisplayed( this.driver, titleSelector ); diff --git a/test/e2e/lib/media-helper.js b/test/e2e/lib/media-helper.js index 91de19bf300e5..be163b682a859 100644 --- a/test/e2e/lib/media-helper.js +++ b/test/e2e/lib/media-helper.js @@ -79,10 +79,7 @@ export function writeScreenshot( data, filenameCallback, metadata ) { screenShotBase = process.env.TEMP_ASSET_PATH; } - let directoryName = 'screenshots'; - if ( process.env.SCREENSHOTDIR ) { - directoryName = process.env.SCREENSHOTDIR; - } + const directoryName = screenShotsDir(); const screenShotDir = path.resolve( screenShotBase, directoryName ); if ( ! fs.existsSync( screenShotDir ) ) { @@ -120,3 +117,7 @@ export function writeTextLogFile( textContent, prefix, pathOverride ) { return logPath; } + +export function screenShotsDir() { + return process.env.SCREENSHOTDIR || 'screenshots'; +} diff --git a/test/e2e/specs/wp-calypso-gutenberg-upgrade-spec.js b/test/e2e/specs/wp-calypso-gutenberg-upgrade-spec.js new file mode 100644 index 0000000000000..49382b9607618 --- /dev/null +++ b/test/e2e/specs/wp-calypso-gutenberg-upgrade-spec.js @@ -0,0 +1,285 @@ +/** + * External dependencies + */ +import assert from 'assert'; +import config from 'config'; +import { times } from 'lodash'; +import { By } from 'selenium-webdriver'; +import { promises as fs } from 'fs'; +import { join } from 'path'; + +/** + * Internal dependencies + */ +import LoginFlow from '../lib/flows/login-flow.js'; + +import GutenbergEditorComponent from '../lib/gutenberg/gutenberg-editor-component'; + +import * as driverManager from '../lib/driver-manager.js'; +import * as dataHelper from '../lib/data-helper.js'; +import * as mediaHelper from '../lib/media-helper'; + +const mochaTimeOut = config.get( 'mochaTimeoutMS' ); +const startBrowserTimeoutMS = config.get( 'startBrowserTimeoutMS' ); +const screenSize = driverManager.currentScreenSize(); +const host = dataHelper.getJetpackHost(); + +import { + BlogPostsBlockComponent, + ContactFormBlockComponent, + ContactInfoBlockComponent, + DynamicSeparatorBlockComponent, + GalleryMasonryBlockComponent, + LayoutGridBlockComponent, + RatingStarBlockComponent, + SlideshowBlockComponent, + SubscriptionsBlockComponent, + TiledGalleryBlockComponent, + YoutubeBlockComponent, +} from '../lib/gutenberg/blocks'; + +let driver; +let loginFlow; +let gEditorComponent; +let currentGutenbergBlocksCode; +let galleryImages; + +before( async function () { + this.timeout( startBrowserTimeoutMS ); + driver = await driverManager.startBrowser(); + + loginFlow = new LoginFlow( driver, 'gutenbergUpgradeUser' ); + galleryImages = times( 5, () => mediaHelper.createFile() ); +} ); + +// Should we keep the @parallel tag here for this e2e test? +describe( `[${ host }] Test popular Gutenberg blocks in edge and non-edge sites across most popular themes (${ screenSize })`, function () { + this.timeout( mochaTimeOut ); + + async function takeScreenshot( siteName, totalHeight, viewportHeight, scrollCb ) { + const now = Date.now() / 1000; + + const siteSshotDir = join( mediaHelper.screenShotsDir(), siteName ); + await fs.access( siteSshotDir ).catch( () => fs.mkdir( siteSshotDir ) ); + + for ( let i = 0; i <= totalHeight / viewportHeight; i++ ) { + await scrollCb( i ); + + await driver.takeScreenshot().then( ( data ) => { + return driver + .getCurrentUrl() + .then( ( url ) => + mediaHelper.writeScreenshot( + data, + () => join( siteName, `${ siteName }-${ i }-${ now }.png` ), + { url } + ) + ); + } ); + } + } + + async function assertBlockIsDisplayed( blockClass ) {} + + async function assertNoErrorInEditor() { + assert.strictEqual( + await gEditorComponent.errorDisplayed(), + false, + 'There is an error shown on the editor page!' + ); + } + + async function assertNoInvalidBlocksInEditor() { + assert.strictEqual( + await gEditorComponent.hasInvalidBlocks(), + false, + 'There is at least one invalid block on the editor page!' + ); + } + + async function takeBlockScreenshots( siteName ) { + const editorViewport = await driver.findElement( + By.css( 'div.interface-interface-skeleton__content' ) + ); + + const editorViewportScrollHeight = await driver.executeScript( + 'return arguments[0].scrollHeight', + editorViewport + ); + const editorViewportClientHeight = await driver.executeScript( + 'return arguments[0].clientHeight', + editorViewport + ); + + await takeScreenshot( + `${ siteName }-editor`, + editorViewportScrollHeight, + editorViewportClientHeight, + ( i ) => + driver.executeScript( + `arguments[0].scroll({top: arguments[0].clientHeight*${ i }})`, + editorViewport + ) + ); + } + + async function takePublishedScreenshots( siteName ) { + await gEditorComponent.publish( { visit: true } ); + + // Give blocks a chance to render and load assets before taking the screenshots + await driver.sleep( 2000 ); + + const totalHeight = await driver.executeScript( 'return document.body.offsetHeight' ); + const windowHeight = await driver.executeScript( 'return window.outerHeight' ); + + await takeScreenshot( `${ siteName }-published`, totalHeight, windowHeight, ( i ) => + driver.executeScript( `window.scrollTo(0, window.outerHeight*${ i })` ) + ); + } + + function verifyBlocksInEditor( siteName ) { + step( 'Blocks are displayed in the editor', async function () { + await Promise.all( + [ + BlogPostsBlockComponent, + ContactFormBlockComponent, + ContactInfoBlockComponent, + DynamicSeparatorBlockComponent, + GalleryMasonryBlockComponent, + LayoutGridBlockComponent, + RatingStarBlockComponent, + SlideshowBlockComponent, + SubscriptionsBlockComponent, + TiledGalleryBlockComponent, + YoutubeBlockComponent, + ].map( async ( blockClass ) => + assert.strictEqual( + await gEditorComponent.blockDisplayedInEditor( blockClass.blockName ), + true, + `The block "${ blockClass.blockName }" was not found in the editor` + ) + ) + ); + } ); + + step( 'Blocks do not error in the editor', async function () { + await assertNoErrorInEditor(); + } ); + + // Commented-out for now because of https://github.com/Automattic/jetpack/issues/16514 + /*step( 'Blocks do not invalidate', async function () { + await assertNoInvalidBlocksInEditor(); + } );*/ + + step( 'Take screenshots of all the blocks in the editor', async function () { + await takeBlockScreenshots( siteName ); + } ); + } + + [ + 'e2egbupgradehever', + 'e2egbupgradeshawburn', + 'e2egbupgrademorden', + 'e2egbupgradeexford', + 'e2egbupgrademayland', + ].forEach( ( siteName ) => { + describe( 'Can add most popular blocks to the editor without errors', function () { + step( `Login to ${ siteName }`, async function () { + await loginFlow.loginAndStartNewPost( `${ siteName }.wordpress.com`, true ); + gEditorComponent = await GutenbergEditorComponent.Expect( driver ); + } ); + + // There's the potential for simplifying (reducing) the following steps further by encapsulating the configuration + // of the block into each block's class. This way, we could just loop through a list of block classes + // since the API would be the same. + + step( 'Insert and configure jetpack/tiled-gallery', async function () { + const tiledGallery = await gEditorComponent.insertBlock( TiledGalleryBlockComponent ); + await tiledGallery.uploadImages( galleryImages ); + } ); + + step( 'Insert and configure jetpack/contact-form', async function () { + const contactEmail = 'testing@automattic.com'; + const subject = "Let's work together"; + + // I re-used this method since it was already available + await gEditorComponent.insertContactForm( contactEmail, subject ); + } ); + + step( 'Insert and configure jetpack/layout-grid', async function () { + await gEditorComponent.insertBlock( LayoutGridBlockComponent ); + } ); + + step( 'Insert and configure core-embed/youtube', async function () { + await gEditorComponent.insertBlock( YoutubeBlockComponent ); + } ); + + step( 'Insert and configure a8c/blog-posts', async function () { + await gEditorComponent.insertBlock( BlogPostsBlockComponent ); + } ); + + step( 'Insert and configure jetpack/subscriptions', async function () { + await gEditorComponent.insertBlock( SubscriptionsBlockComponent ); + } ); + + step( 'Insert and configure jetpack/slideshow', async function () { + await gEditorComponent.insertBlock( SlideshowBlockComponent ); + } ); + + step( 'Insert and configure jetpack/rating-star', async function () { + await gEditorComponent.insertBlock( RatingStarBlockComponent ); + } ); + + step( 'Insert and configure coblocks/dynamic-separator', async function () { + await gEditorComponent.insertBlock( DynamicSeparatorBlockComponent ); + } ); + + step( 'Insert and configure coblocks/gallery-masonry', async function () { + await gEditorComponent.insertBlock( GalleryMasonryBlockComponent ); + } ); + + step( 'Insert and configure jetpack/contact-info', async function () { + await gEditorComponent.insertBlock( ContactInfoBlockComponent ); + } ); + + verifyBlocksInEditor( siteName ); + + step( + 'Switch to the code editor and copy the code markup for all the blocks', + async function () { + currentGutenbergBlocksCode = await gEditorComponent.copyBlocksCode(); + } + ); + + step( 'Take screenshots of the published page', async function () { + await takePublishedScreenshots( siteName ); + } ); + + describe( 'Test the same blocks in the corresponding edge site', function () { + const edgeSiteName = siteName + 'edge'; + + step( 'Switches to edge site', async function () { + // Re-use the same session created earlier but change the site + await loginFlow.loginAndStartNewPost( `${ edgeSiteName }.wordpress.com`, true ); + + // Loads the same blocks from the non-edge site by pasting the code markup code in the code editor + // and then switching to the block editor + await gEditorComponent.pasteBlocksCode( currentGutenbergBlocksCode ); + await gEditorComponent.switchToBlockEditor(); + } ); + + verifyBlocksInEditor( edgeSiteName ); + + step( 'Take screenshots of the published page', async function () { + await takePublishedScreenshots( siteName ); + } ); + } ); + } ); + } ); +} ); + +after( async function () { + await Promise.all( + galleryImages.map( ( fileDetails ) => mediaHelper.deleteFile( fileDetails ) ) + ); +} );