Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Try to rewrite e2e tests using jest-puppeter preset #8301

Merged
merged 25 commits into from
Aug 8, 2018

Conversation

gziolo
Copy link
Member

@gziolo gziolo commented Jul 30, 2018

Description

Closes #8619.

Improves #6956. See known issues to find out what is still necessary to make it bulletproof.

Tries to fix intermittent e2e test failures. In particular an error with preview test:

Starting to see a new one.

FAIL test/e2e/specs/preview.test.js
  ● Preview › Should open a preview window for a new post
    Protocol error (Runtime.callFunctionOn): Cannot find context with specified id undefined
      
      at Promise (node_modules/puppeteer/lib/Connection.js:198:56)
      at CDPSession.send (node_modules/puppeteer/lib/Connection.js:197:12)
      at ExecutionContext.evaluateHandle (node_modules/puppeteer/lib/ExecutionContext.js:71:75)
      at ExecutionContext.evaluate (node_modules/puppeteer/lib/ExecutionContext.js:46:31)
      at Frame.evaluate (node_modules/puppeteer/lib/FrameManager.js:299:20)

Example: #7841 https://travis-ci.org/WordPress/gutenberg/jobs/401939944

This is try to use jest-puppeteer preset recommended in Jest docs:

https://jestjs.io/docs/en/puppeteer#use-puppeteer-preset

The biggest difference is that we no longer use isolated browser instance per test suite but share one browser to speed up tests execution.

It also enables some new methods to expect API. See:
https://github.com/smooth-code/jest-puppeteer/tree/master/packages/expect-puppeteer#api

How has this been tested?

npm run test-e2e

Knows issues

We should open a follow up tasks for two issues.

Preview opens new tab after publish

Steps to reproduce:

  1. Type something in the title field.
  2. Click the preview button.
  3. Go back to the editor.
  4. Publish post.
  5. Click the preview button.
  6. Go back to the editor.
  7. Type something in the title field.
  8. Click the preview button.
  9. It shouldn't open a new tab. It should reuse the existing one.

Too many undo levels

It happens only in the headless mode when running e2e tests. Sometimes it creates two undo levels for the first paragraph. When you modify test and add snapshots after each undo action you might see the following:

exports[`undo Should undo to expected level intervals 5`] = `
“<!-- wp:paragraph -->
<p>This</p>
<!-- /wp:paragraph -->”
`;

exports[`undo Should undo to expected level intervals 6`] = `
“<!-- wp:paragraph -->
<p>Thi</p>
<!-- /wp:paragraph -->”
`;

This isn't expected behavior. It uncovers some edge case happening inside the logic which creates undo levels.

@gziolo gziolo force-pushed the fix/e2e-test-preview branch from 0683a23 to 249807f Compare July 30, 2018 15:52
@gziolo gziolo self-assigned this Jul 30, 2018
@gziolo gziolo added [Status] In Progress Tracking issues with work in progress [Type] Automated Testing Testing infrastructure changes impacting the execution of end-to-end (E2E) and/or unit tests. labels Jul 30, 2018
expect( previewPage.url() ).toBe( expectedPreviewURL );

// Return to editor to change title.
await editorPage.bringToFront();
await editorPage.type( '.editor-post-title__input', ' And more.' );

// Published preview should reuse same popup frame.
previewPage = await getOpenedPreviewPage();
// TODO: Fix code to reuse the same frame!
previewPage = await getOpenedPreviewPage( editorPage );
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the only place where we don't reuse the same frame. Very surprising given given that there is code comment which states it 😃

@gziolo gziolo force-pushed the fix/e2e-test-preview branch from 2e26a08 to 8d46c2f Compare July 30, 2018 16:27
@gziolo gziolo removed the [Status] In Progress Tracking issues with work in progress label Jul 31, 2018
@gziolo
Copy link
Member Author

gziolo commented Jul 31, 2018

Travis run for e2e tests is back to 7 minutes!

This is what I see locally:

Test Suites: 23 passed, 23 total
Tests:       3 skipped, 66 passed, 69 total
Snapshots:   15 passed, 15 total
Time:        93.87s, estimated 95s
Ran all test suites


afterEach( async () => {
// Clear localStorage tips so they aren't persisted for the next test.
await clearLocalStorage();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this not needed anymore?

Copy link
Member Author

@gziolo gziolo Jul 31, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We clear localStorage after every test suite. In addition, when opening the editor and enableTips flag is passed we clean all disabled tips and reload the page.

@@ -17,7 +15,7 @@ describe( 'Multi-block selection', () => {
const multiSelectedCssClass = 'is-multi-selected';

// Creating test blocks
await page.click( '.editor-default-block-appender' );
await page.click( '.editor-default-block-appender__content' );
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just curious why this change?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For consistency, we probably should wrap it with a utility method. Initially, I suspected that it might be the reason why undo test fails sometimes.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This updated class is the one which has the events bound, so it's more "correct".

newPost,
pressWithModifier,
searchForBlock,
} from '../support/utils';

function waitForAndAcceptDialog() {
return new Promise( ( resolve ) => {
page.once( 'dialog', async ( dialog ) => {
await dialog.accept();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are we removing this?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have global handler so you don't need to accept it twice.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we remove the entire waitForAndAcceptDialog function, then?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We use this Promise, so not quite sure 🤷‍♂️

@@ -66,8 +61,7 @@ describe( 'Publishing', () => {

it( 'Should reopen sidebar the sidebar when resizing from mobile to desktop if the sidebar was closed automatically', async () => {
await setViewport( 'large' );
await newPost();
await setViewport( 'small' );
await newPost( { viewport: 'small' } );
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's weird to have an option to set the viewport size as part of the newPost helper, I prefer a separate helper for that.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can revert those changes. Previously, we were setting large viewport using newDesktopBrowserPage helper.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reverted 👍

headless: PUPPETEER_HEADLESS !== 'false',
slowMo: parseInt( PUPPETEER_SLOWMO, 10 ) || 0,
} );
enablePageDialogAccept();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't like that we're accepting dialogs globally, I prefer the explicitness we had before, because sometimes we want to test these dialogs

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need for the majority of test suites as far as I discovered today. There is disablePageDialogAccept method which would be useful in case when you want to handle it yourself. I'm happy to refactor, but then we would have to put it for every test which types something.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok 👍 It's fine

@youknowriad
Copy link
Contributor

I'd appreciate if we extend our testing docs with steps to:

  • Run a unique test with a custom environment
  • Run a custom environment
  • Watch a unique test if possible?

Just trying to think of alternative workflows (other than CI) needed to debug and build tests.

@gziolo
Copy link
Member Author

gziolo commented Jul 31, 2018

I brought back explicit calls for setViewport with 78825c0 as suggested by @youknowriad.

I also added a change which ensures npm run test-e2e is executed with the latest codebase with aecf753 as suggested by @notnownikki.

@gziolo
Copy link
Member Author

gziolo commented Jul 31, 2018

@youknowriad I opened a follow-up issue for your comment about documentation - #8319.

@gziolo
Copy link
Member Author

gziolo commented Jul 31, 2018

Added another small tweak which improves the trigger which wraps up after tests cleanup: 384cf5e.

@notnownikki
Copy link
Member

Pushed up some code in 5ce6245 that makes the preview test pass consistently everywhere I try it.

Looked at this, and there's a problem with using the targetcreated event, because sometimes the listeners don't get triggered quickly enough and the test carries on without getting the target, and we get a crash. It's not just us getting caught by this, but popups are considered an edge case so it's not high priority to fix. See berstend/puppeteer-extra#6

Because we can't reliably get the created target, I've introduced a waitForTab which waits for the number of tabs to change, so we know we've got the preview tab open.

I also changed the test to only reuse the preview page in tests that we explicitly check for reuse behaviour. There are also assertions that we have the expected number of tabs open at all times.

The test passes consistently for me now, both with the browser and in headless mode (both of which resulted in different race conditions in the original test - it used to be possible for the test to fail because it thought the preview page had reloaded with updated content, but it hadn't yet even though it was in the process of doing so).

Copy link
Member

@noisysocks noisysocks left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Left a few minor comments. Would love to get this in very soon—it's hard to review PRs when they're all 🔴! 😅

}
function sleep( time ) {
return new Promise( ( resolve ) => setTimeout( resolve, time ) );
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Puppeteer already provides this function in the form of page.waitFor.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, I think we discussed this previously and the conclusion is to prefer waitForSelector over waitFor/sleep, since it's more reliable.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can't wait for a selector on a page that doesn't exist yet though, you have to have the page instance, and that's what this is for.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, that's fine, can we add an inline comment with this explanation?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, in fact I've already changed this code locally and used the editor page to call waitFor, with an inline comment :)

expect( previewPage.url() ).toBe( expectedPreviewURL );

// Return to editor to change title.
await editorPage.bringToFront();
await editorPage.type( '.editor-post-title__input', ' And more.' );

// Published preview should reuse same popup frame.
previewPage = await getOpenedPreviewPage();
// TODO: Fix an existing bug which opens a new tab.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we create an issue to track this work?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#8330 - thanks :)
Issue is filed: #8318.

newPost,
pressWithModifier,
searchForBlock,
} from '../support/utils';

function waitForAndAcceptDialog() {
return new Promise( ( resolve ) => {
page.once( 'dialog', async ( dialog ) => {
await dialog.accept();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we remove the entire waitForAndAcceptDialog function, then?

@noisysocks
Copy link
Member

Heads up that #8330 contains a fix for the preview E2E test as well.

@gziolo gziolo force-pushed the fix/e2e-test-preview branch from 6e7f7de to 6687d0a Compare August 8, 2018 10:14
@gziolo
Copy link
Member Author

gziolo commented Aug 8, 2018

It passed 2 times after recent tweaks. We should merge as soon as someone confirms I didn't break anything else 😅

@notnownikki
Copy link
Member

🚢

Works well on my poor little laptop even when it's under load! This is a huge improvement. Thank you!

@gziolo gziolo merged commit 1b9af37 into master Aug 8, 2018
@gziolo gziolo deleted the fix/e2e-test-preview branch August 8, 2018 11:04
@gziolo gziolo added this to the 3.5 milestone Aug 8, 2018
@gziolo gziolo added the [Type] Enhancement A suggestion for improvement. label Aug 8, 2018
@gziolo
Copy link
Member Author

gziolo commented Aug 8, 2018

Thank you all for commits, reviews, and testing. Let's iterate on this as it is still not perfect 🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
[Type] Automated Testing Testing infrastructure changes impacting the execution of end-to-end (E2E) and/or unit tests. [Type] Enhancement A suggestion for improvement.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Create a "click on the block appender" test helper for e2e tests
6 participants