Skip to content

Commit

Permalink
Avoid saving metaboxes before a preview (#14877)
Browse files Browse the repository at this point in the history
* Remove logic that causes a metabox save before a preview

* Add e2e test for preview with custom fields active.

* Improve test robustness
  • Loading branch information
talldan authored Apr 9, 2019
1 parent 27fe0a2 commit b89869c
Show file tree
Hide file tree
Showing 2 changed files with 121 additions and 40 deletions.
155 changes: 120 additions & 35 deletions packages/e2e-tests/specs/preview.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,47 +12,79 @@ import {
createURL,
publishPost,
saveDraft,
clickOnMoreMenuItem,
pressKeyWithModifier,
} from '@wordpress/e2e-test-utils';

describe( 'Preview', () => {
beforeEach( async () => {
await createNewPost();
} );
async function openPreviewPage( editorPage ) {
let openTabs = await browser.pages();
const expectedTabsCount = openTabs.length + 1;
await editorPage.click( '.editor-post-preview' );

async function openPreviewPage( editorPage ) {
let openTabs = await browser.pages();
const expectedTabsCount = openTabs.length + 1;
await editorPage.click( '.editor-post-preview' );

// Wait for the new tab to open.
while ( openTabs.length < expectedTabsCount ) {
await editorPage.waitFor( 1 );
openTabs = await browser.pages();
}

const previewPage = last( openTabs );
// Wait for the preview to load. We can't do interstitial detection here,
// because it might load too quickly for us to pick up, so we wait for
// the preview to load by waiting for the title to appear.
await previewPage.waitForSelector( '.entry-title' );
return previewPage;
// Wait for the new tab to open.
while ( openTabs.length < expectedTabsCount ) {
await editorPage.waitFor( 1 );
openTabs = await browser.pages();
}

/**
* Given a Puppeteer Page instance for a preview window, clicks Preview, and
* awaits the window navigation.
*
* @param {puppeteer.Page} previewPage Page on which to await navigation.
*
* @return {Promise} Promise resolving once navigation completes.
*/
async function waitForPreviewNavigation( previewPage ) {
const navigationCompleted = previewPage.waitForNavigation();
await page.click( '.editor-post-preview' );
return navigationCompleted;
const previewPage = last( openTabs );
// Wait for the preview to load. We can't do interstitial detection here,
// because it might load too quickly for us to pick up, so we wait for
// the preview to load by waiting for the title to appear.
await previewPage.waitForSelector( '.entry-title' );
return previewPage;
}

/**
* Given a Puppeteer Page instance for a preview window, clicks Preview, and
* awaits the window navigation.
*
* @param {puppeteer.Page} previewPage Page on which to await navigation.
*
* @return {Promise} Promise resolving once navigation completes.
*/
async function waitForPreviewNavigation( previewPage ) {
const navigationCompleted = previewPage.waitForNavigation();
await page.click( '.editor-post-preview' );
return navigationCompleted;
}

/**
* Enables or disables the custom fields option.
*
* Note that this is implemented separately from the `toggleScreenOptions`
* utility, since the custom fields option triggers a page reload and requires
* extra async logic to wait for navigation to complete.
*
* @param {boolean} shouldBeChecked If true, turns the option on. If false, off.
*/
async function toggleCustomFieldsOption( shouldBeChecked ) {
const checkboxXPath = '//*[contains(@class, "edit-post-options-modal")]//label[contains(text(), "Custom Fields")]';
await clickOnMoreMenuItem( 'Options' );
await page.waitForXPath( checkboxXPath );
const [ checkboxHandle ] = await page.$x( checkboxXPath );

const isChecked = await page.evaluate(
( element ) => element.control.checked,
checkboxHandle
);

if ( isChecked !== shouldBeChecked ) {
const navigationCompleted = page.waitForNavigation();
await checkboxHandle.click();
await navigationCompleted;
return;
}

it( 'Should open a preview window for a new post', async () => {
await page.click( '.edit-post-options-modal button[aria-label="Close dialog"]' );
}

describe( 'Preview', () => {
beforeEach( async () => {
await createNewPost();
} );

it( 'should open a preview window for a new post', async () => {
const editorPage = page;

// Disabled until content present.
Expand Down Expand Up @@ -129,7 +161,7 @@ describe( 'Preview', () => {
await previewPage.close();
} );

it( 'Should not revert title during a preview right after a save draft', async () => {
it( 'should not revert title during a preview right after a save draft', async () => {
const editorPage = page;

// Type aaaaa in the title filed.
Expand Down Expand Up @@ -166,3 +198,56 @@ describe( 'Preview', () => {
await previewPage.close();
} );
} );

describe( 'Preview with Custom Fields enabled', async () => {
beforeEach( async () => {
await createNewPost();
await toggleCustomFieldsOption( true );
} );

afterEach( async () => {
await toggleCustomFieldsOption( false );
} );

// Catch regressions of https://github.com/WordPress/gutenberg/issues/12617
it( 'displays edits to the post title and content in the preview', async () => {
const editorPage = page;

// Add an initial title and content.
await editorPage.type( '.editor-post-title__input', 'title 1' );
await editorPage.keyboard.press( 'Tab' );
await editorPage.keyboard.type( 'content 1' );

// Open the preview page.
const previewPage = await openPreviewPage( editorPage );

// Check the title and preview match.
let previewTitle = await previewPage.$eval( '.entry-title', ( node ) => node.textContent );
expect( previewTitle ).toBe( 'title 1' );
let previewContent = await previewPage.$eval( '.entry-content p', ( node ) => node.textContent );
expect( previewContent ).toBe( 'content 1' );

// Return to editor and modify the title and content.
await editorPage.bringToFront();
await editorPage.click( '.editor-post-title__input' );
await pressKeyWithModifier( 'shift', 'Home' );
await editorPage.keyboard.press( 'Delete' );
await editorPage.keyboard.type( 'title 2' );
await editorPage.keyboard.press( 'Tab' );
await pressKeyWithModifier( 'shift', 'Home' );
await editorPage.keyboard.press( 'Delete' );
await editorPage.keyboard.type( 'content 2' );

// Open the preview page.
await waitForPreviewNavigation( previewPage );

// Title in preview should match input.
previewTitle = await previewPage.$eval( '.entry-title', ( node ) => node.textContent );
expect( previewTitle ).toBe( 'title 2' );
previewContent = await previewPage.$eval( '.entry-content p', ( node ) => node.textContent );
expect( previewContent ).toBe( 'content 2' );

// Make sure the editor is active for the afterEach function.
await editorPage.bringToFront();
} );
} );
6 changes: 1 addition & 5 deletions packages/edit-post/src/store/effects.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,26 +45,22 @@ const effects = {

let wasSavingPost = select( 'core/editor' ).isSavingPost();
let wasAutosavingPost = select( 'core/editor' ).isAutosavingPost();
let wasPreviewingPost = select( 'core/editor' ).isPreviewingPost();
// Save metaboxes when performing a full save on the post.
subscribe( () => {
const isSavingPost = select( 'core/editor' ).isSavingPost();
const isAutosavingPost = select( 'core/editor' ).isAutosavingPost();
const isPreviewingPost = select( 'core/editor' ).isPreviewingPost();
const hasActiveMetaBoxes = select( 'core/edit-post' ).hasMetaBoxes();

// Save metaboxes on save completion, except for autosaves that are not a post preview.
const shouldTriggerMetaboxesSave = (
hasActiveMetaBoxes && (
( wasSavingPost && ! isSavingPost && ! wasAutosavingPost ) ||
( wasAutosavingPost && wasPreviewingPost && ! isPreviewingPost )
( wasSavingPost && ! isSavingPost && ! wasAutosavingPost )
)
);

// Save current state for next inspection.
wasSavingPost = isSavingPost;
wasAutosavingPost = isAutosavingPost;
wasPreviewingPost = isPreviewingPost;

if ( shouldTriggerMetaboxesSave ) {
store.dispatch( requestMetaBoxUpdates() );
Expand Down

0 comments on commit b89869c

Please sign in to comment.