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

[RNMobile] Fix act warnings that might be triggered after test finishes #38052

Merged
merged 7 commits into from
Jan 19, 2022

Conversation

fluiddot
Copy link
Contributor

@fluiddot fluiddot commented Jan 18, 2022

Related to #33287 (comment).

Description

The approach for addressing the act warnings in this PR is simply to wait for the Javascript block execution to finish when the editor is initialized. This way we assure that no store updates, at least the ones related to the editor initialization, are executed after the test finish potentially leading to log act warnings and making tests fail when using @wordpress/jest-console package.

Context

When the editor is initialized, specifically the EditorProvider component, a set of selectors are called via withSelect (example). Some of these selectors, like the referenced on in the example getEditorBlocks, require to be resolved which is done asynchronously, but due to the fact that the API requests are mocked, this results on being executed at the end of execution block because it uses a setTimeout function with 0 duration.

As part of the resolution process, two actions startResolution and finishResolution are dispatched, which updates the store. Due to this behavior, and since they happen after the test finishes, it leads to the act warnings that we often identify in the output.

I added a breakpoint in the logic in charge of creating and executing the resolvers (reference), in order to provide a step by step explanation within the stack trace:

Click here to display screenshots

Step 1
Step 2
Step 3
Step 4

How has this been tested?

  1. Run command npm run native test
  2. Observe that all tests pass.

Screenshots

N/A

Types of changes

Bug fix

Checklist:

  • My code is tested.
  • My code follows the WordPress code style.
  • My code follows the accessibility standards.
  • I've tested my changes with keyboard and screen readers.
  • My code has proper inline documentation.
  • I've included developer documentation if appropriate.
  • I've updated all React Native files affected by any refactorings/renamings in this PR (please manually search all *.native.js files for terms that need renaming or removal).
  • I've updated related schemas if appropriate.

This is a workaround for addressing the act warnings that might caused by store updates executed after the test finishes.
@fluiddot fluiddot added [Type] Bug An existing feature does not function as intended Mobile App - i.e. Android or iOS Native mobile impl of the block editor. (Note: used in scripts, ping mobile folks to change) labels Jan 18, 2022
@fluiddot fluiddot self-assigned this Jan 18, 2022
@github-actions
Copy link

github-actions bot commented Jan 18, 2022

Size Change: 0 B

Total Size: 1.13 MB

ℹ️ View Unchanged
Filename Size
build/a11y/index.min.js 960 B
build/admin-manifest/index.min.js 1.1 kB
build/annotations/index.min.js 2.75 kB
build/api-fetch/index.min.js 2.21 kB
build/autop/index.min.js 2.12 kB
build/blob/index.min.js 459 B
build/block-directory/index.min.js 6.28 kB
build/block-directory/style-rtl.css 1.01 kB
build/block-directory/style.css 1.01 kB
build/block-editor/default-editor-styles-rtl.css 378 B
build/block-editor/default-editor-styles.css 378 B
build/block-editor/index.min.js 140 kB
build/block-editor/style-rtl.css 14.6 kB
build/block-editor/style.css 14.6 kB
build/block-library/blocks/archives/editor-rtl.css 61 B
build/block-library/blocks/archives/editor.css 60 B
build/block-library/blocks/archives/style-rtl.css 65 B
build/block-library/blocks/archives/style.css 65 B
build/block-library/blocks/audio/editor-rtl.css 150 B
build/block-library/blocks/audio/editor.css 150 B
build/block-library/blocks/audio/style-rtl.css 111 B
build/block-library/blocks/audio/style.css 111 B
build/block-library/blocks/audio/theme-rtl.css 125 B
build/block-library/blocks/audio/theme.css 125 B
build/block-library/blocks/block/editor-rtl.css 161 B
build/block-library/blocks/block/editor.css 161 B
build/block-library/blocks/button/editor-rtl.css 470 B
build/block-library/blocks/button/editor.css 470 B
build/block-library/blocks/button/style-rtl.css 560 B
build/block-library/blocks/button/style.css 560 B
build/block-library/blocks/buttons/editor-rtl.css 292 B
build/block-library/blocks/buttons/editor.css 292 B
build/block-library/blocks/buttons/style-rtl.css 275 B
build/block-library/blocks/buttons/style.css 275 B
build/block-library/blocks/calendar/style-rtl.css 207 B
build/block-library/blocks/calendar/style.css 207 B
build/block-library/blocks/categories/editor-rtl.css 84 B
build/block-library/blocks/categories/editor.css 83 B
build/block-library/blocks/categories/style-rtl.css 79 B
build/block-library/blocks/categories/style.css 79 B
build/block-library/blocks/code/style-rtl.css 90 B
build/block-library/blocks/code/style.css 90 B
build/block-library/blocks/code/theme-rtl.css 131 B
build/block-library/blocks/code/theme.css 131 B
build/block-library/blocks/columns/editor-rtl.css 108 B
build/block-library/blocks/columns/editor.css 108 B
build/block-library/blocks/columns/style-rtl.css 406 B
build/block-library/blocks/columns/style.css 406 B
build/block-library/blocks/comment-template/style-rtl.css 127 B
build/block-library/blocks/comment-template/style.css 127 B
build/block-library/blocks/comments-pagination-numbers/editor-rtl.css 123 B
build/block-library/blocks/comments-pagination-numbers/editor.css 121 B
build/block-library/blocks/comments-pagination/editor-rtl.css 222 B
build/block-library/blocks/comments-pagination/editor.css 209 B
build/block-library/blocks/comments-pagination/style-rtl.css 235 B
build/block-library/blocks/comments-pagination/style.css 231 B
build/block-library/blocks/cover/editor-rtl.css 546 B
build/block-library/blocks/cover/editor.css 547 B
build/block-library/blocks/cover/style-rtl.css 1.22 kB
build/block-library/blocks/cover/style.css 1.22 kB
build/block-library/blocks/embed/editor-rtl.css 293 B
build/block-library/blocks/embed/editor.css 293 B
build/block-library/blocks/embed/style-rtl.css 417 B
build/block-library/blocks/embed/style.css 417 B
build/block-library/blocks/embed/theme-rtl.css 124 B
build/block-library/blocks/embed/theme.css 124 B
build/block-library/blocks/file/editor-rtl.css 300 B
build/block-library/blocks/file/editor.css 300 B
build/block-library/blocks/file/style-rtl.css 255 B
build/block-library/blocks/file/style.css 255 B
build/block-library/blocks/file/view.min.js 322 B
build/block-library/blocks/freeform/editor-rtl.css 2.44 kB
build/block-library/blocks/freeform/editor.css 2.44 kB
build/block-library/blocks/gallery/editor-rtl.css 965 B
build/block-library/blocks/gallery/editor.css 967 B
build/block-library/blocks/gallery/style-rtl.css 1.6 kB
build/block-library/blocks/gallery/style.css 1.6 kB
build/block-library/blocks/gallery/theme-rtl.css 122 B
build/block-library/blocks/gallery/theme.css 122 B
build/block-library/blocks/group/editor-rtl.css 159 B
build/block-library/blocks/group/editor.css 159 B
build/block-library/blocks/group/style-rtl.css 57 B
build/block-library/blocks/group/style.css 57 B
build/block-library/blocks/group/theme-rtl.css 78 B
build/block-library/blocks/group/theme.css 78 B
build/block-library/blocks/heading/style-rtl.css 114 B
build/block-library/blocks/heading/style.css 114 B
build/block-library/blocks/html/editor-rtl.css 332 B
build/block-library/blocks/html/editor.css 333 B
build/block-library/blocks/image/editor-rtl.css 810 B
build/block-library/blocks/image/editor.css 809 B
build/block-library/blocks/image/style-rtl.css 507 B
build/block-library/blocks/image/style.css 511 B
build/block-library/blocks/image/theme-rtl.css 124 B
build/block-library/blocks/image/theme.css 124 B
build/block-library/blocks/latest-comments/style-rtl.css 284 B
build/block-library/blocks/latest-comments/style.css 284 B
build/block-library/blocks/latest-posts/editor-rtl.css 137 B
build/block-library/blocks/latest-posts/editor.css 137 B
build/block-library/blocks/latest-posts/style-rtl.css 528 B
build/block-library/blocks/latest-posts/style.css 527 B
build/block-library/blocks/list/style-rtl.css 94 B
build/block-library/blocks/list/style.css 94 B
build/block-library/blocks/media-text/editor-rtl.css 266 B
build/block-library/blocks/media-text/editor.css 263 B
build/block-library/blocks/media-text/style-rtl.css 493 B
build/block-library/blocks/media-text/style.css 490 B
build/block-library/blocks/more/editor-rtl.css 431 B
build/block-library/blocks/more/editor.css 431 B
build/block-library/blocks/navigation-link/editor-rtl.css 649 B
build/block-library/blocks/navigation-link/editor.css 650 B
build/block-library/blocks/navigation-link/style-rtl.css 94 B
build/block-library/blocks/navigation-link/style.css 94 B
build/block-library/blocks/navigation-submenu/editor-rtl.css 299 B
build/block-library/blocks/navigation-submenu/editor.css 299 B
build/block-library/blocks/navigation-submenu/view.min.js 343 B
build/block-library/blocks/navigation/editor-rtl.css 1.93 kB
build/block-library/blocks/navigation/editor.css 1.94 kB
build/block-library/blocks/navigation/style-rtl.css 1.83 kB
build/block-library/blocks/navigation/style.css 1.82 kB
build/block-library/blocks/navigation/view.min.js 2.81 kB
build/block-library/blocks/nextpage/editor-rtl.css 395 B
build/block-library/blocks/nextpage/editor.css 395 B
build/block-library/blocks/page-list/editor-rtl.css 401 B
build/block-library/blocks/page-list/editor.css 402 B
build/block-library/blocks/page-list/style-rtl.css 175 B
build/block-library/blocks/page-list/style.css 175 B
build/block-library/blocks/paragraph/editor-rtl.css 157 B
build/block-library/blocks/paragraph/editor.css 157 B
build/block-library/blocks/paragraph/style-rtl.css 273 B
build/block-library/blocks/paragraph/style.css 273 B
build/block-library/blocks/post-author/style-rtl.css 175 B
build/block-library/blocks/post-author/style.css 176 B
build/block-library/blocks/post-comments-form/style-rtl.css 446 B
build/block-library/blocks/post-comments-form/style.css 446 B
build/block-library/blocks/post-comments/style-rtl.css 521 B
build/block-library/blocks/post-comments/style.css 521 B
build/block-library/blocks/post-excerpt/editor-rtl.css 73 B
build/block-library/blocks/post-excerpt/editor.css 73 B
build/block-library/blocks/post-excerpt/style-rtl.css 69 B
build/block-library/blocks/post-excerpt/style.css 69 B
build/block-library/blocks/post-featured-image/editor-rtl.css 721 B
build/block-library/blocks/post-featured-image/editor.css 721 B
build/block-library/blocks/post-featured-image/style-rtl.css 153 B
build/block-library/blocks/post-featured-image/style.css 153 B
build/block-library/blocks/post-template/editor-rtl.css 99 B
build/block-library/blocks/post-template/editor.css 98 B
build/block-library/blocks/post-template/style-rtl.css 305 B
build/block-library/blocks/post-template/style.css 305 B
build/block-library/blocks/post-terms/style-rtl.css 73 B
build/block-library/blocks/post-terms/style.css 73 B
build/block-library/blocks/post-title/style-rtl.css 80 B
build/block-library/blocks/post-title/style.css 80 B
build/block-library/blocks/preformatted/style-rtl.css 103 B
build/block-library/blocks/preformatted/style.css 103 B
build/block-library/blocks/pullquote/editor-rtl.css 198 B
build/block-library/blocks/pullquote/editor.css 198 B
build/block-library/blocks/pullquote/style-rtl.css 389 B
build/block-library/blocks/pullquote/style.css 388 B
build/block-library/blocks/pullquote/theme-rtl.css 167 B
build/block-library/blocks/pullquote/theme.css 167 B
build/block-library/blocks/query-pagination-numbers/editor-rtl.css 122 B
build/block-library/blocks/query-pagination-numbers/editor.css 121 B
build/block-library/blocks/query-pagination/editor-rtl.css 221 B
build/block-library/blocks/query-pagination/editor.css 211 B
build/block-library/blocks/query-pagination/style-rtl.css 234 B
build/block-library/blocks/query-pagination/style.css 231 B
build/block-library/blocks/query/editor-rtl.css 131 B
build/block-library/blocks/query/editor.css 132 B
build/block-library/blocks/quote/style-rtl.css 187 B
build/block-library/blocks/quote/style.css 187 B
build/block-library/blocks/quote/theme-rtl.css 223 B
build/block-library/blocks/quote/theme.css 226 B
build/block-library/blocks/rss/editor-rtl.css 202 B
build/block-library/blocks/rss/editor.css 204 B
build/block-library/blocks/rss/style-rtl.css 289 B
build/block-library/blocks/rss/style.css 288 B
build/block-library/blocks/search/editor-rtl.css 165 B
build/block-library/blocks/search/editor.css 165 B
build/block-library/blocks/search/style-rtl.css 397 B
build/block-library/blocks/search/style.css 398 B
build/block-library/blocks/search/theme-rtl.css 64 B
build/block-library/blocks/search/theme.css 64 B
build/block-library/blocks/separator/editor-rtl.css 99 B
build/block-library/blocks/separator/editor.css 99 B
build/block-library/blocks/separator/style-rtl.css 245 B
build/block-library/blocks/separator/style.css 245 B
build/block-library/blocks/separator/theme-rtl.css 172 B
build/block-library/blocks/separator/theme.css 172 B
build/block-library/blocks/shortcode/editor-rtl.css 474 B
build/block-library/blocks/shortcode/editor.css 474 B
build/block-library/blocks/site-logo/editor-rtl.css 744 B
build/block-library/blocks/site-logo/editor.css 744 B
build/block-library/blocks/site-logo/style-rtl.css 181 B
build/block-library/blocks/site-logo/style.css 181 B
build/block-library/blocks/site-tagline/editor-rtl.css 86 B
build/block-library/blocks/site-tagline/editor.css 86 B
build/block-library/blocks/site-title/editor-rtl.css 84 B
build/block-library/blocks/site-title/editor.css 84 B
build/block-library/blocks/social-link/editor-rtl.css 177 B
build/block-library/blocks/social-link/editor.css 177 B
build/block-library/blocks/social-links/editor-rtl.css 670 B
build/block-library/blocks/social-links/editor.css 669 B
build/block-library/blocks/social-links/style-rtl.css 1.32 kB
build/block-library/blocks/social-links/style.css 1.32 kB
build/block-library/blocks/spacer/editor-rtl.css 332 B
build/block-library/blocks/spacer/editor.css 332 B
build/block-library/blocks/spacer/style-rtl.css 48 B
build/block-library/blocks/spacer/style.css 48 B
build/block-library/blocks/table/editor-rtl.css 471 B
build/block-library/blocks/table/editor.css 472 B
build/block-library/blocks/table/style-rtl.css 481 B
build/block-library/blocks/table/style.css 481 B
build/block-library/blocks/table/theme-rtl.css 188 B
build/block-library/blocks/table/theme.css 188 B
build/block-library/blocks/tag-cloud/style-rtl.css 214 B
build/block-library/blocks/tag-cloud/style.css 215 B
build/block-library/blocks/template-part/editor-rtl.css 560 B
build/block-library/blocks/template-part/editor.css 559 B
build/block-library/blocks/template-part/theme-rtl.css 101 B
build/block-library/blocks/template-part/theme.css 101 B
build/block-library/blocks/text-columns/editor-rtl.css 95 B
build/block-library/blocks/text-columns/editor.css 95 B
build/block-library/blocks/text-columns/style-rtl.css 166 B
build/block-library/blocks/text-columns/style.css 166 B
build/block-library/blocks/verse/style-rtl.css 87 B
build/block-library/blocks/verse/style.css 87 B
build/block-library/blocks/video/editor-rtl.css 571 B
build/block-library/blocks/video/editor.css 572 B
build/block-library/blocks/video/style-rtl.css 173 B
build/block-library/blocks/video/style.css 173 B
build/block-library/blocks/video/theme-rtl.css 124 B
build/block-library/blocks/video/theme.css 124 B
build/block-library/common-rtl.css 908 B
build/block-library/common.css 905 B
build/block-library/editor-rtl.css 10 kB
build/block-library/editor.css 10 kB
build/block-library/index.min.js 165 kB
build/block-library/reset-rtl.css 474 B
build/block-library/reset.css 474 B
build/block-library/style-rtl.css 10.8 kB
build/block-library/style.css 10.8 kB
build/block-library/theme-rtl.css 672 B
build/block-library/theme.css 676 B
build/block-serialization-default-parser/index.min.js 1.09 kB
build/block-serialization-spec-parser/index.min.js 2.79 kB
build/blocks/index.min.js 46.3 kB
build/components/index.min.js 215 kB
build/components/style-rtl.css 15.5 kB
build/components/style.css 15.5 kB
build/compose/index.min.js 11.2 kB
build/core-data/index.min.js 13.3 kB
build/customize-widgets/index.min.js 11.4 kB
build/customize-widgets/style-rtl.css 1.5 kB
build/customize-widgets/style.css 1.49 kB
build/data-controls/index.min.js 631 B
build/data/index.min.js 7.49 kB
build/date/index.min.js 31.9 kB
build/deprecated/index.min.js 485 B
build/dom-ready/index.min.js 304 B
build/dom/index.min.js 4.5 kB
build/edit-navigation/index.min.js 16 kB
build/edit-navigation/style-rtl.css 3.76 kB
build/edit-navigation/style.css 3.76 kB
build/edit-post/classic-rtl.css 546 B
build/edit-post/classic.css 547 B
build/edit-post/index.min.js 29.6 kB
build/edit-post/style-rtl.css 7.16 kB
build/edit-post/style.css 7.16 kB
build/edit-site/index.min.js 37.8 kB
build/edit-site/style-rtl.css 6.85 kB
build/edit-site/style.css 6.84 kB
build/edit-widgets/index.min.js 16.5 kB
build/edit-widgets/style-rtl.css 4.17 kB
build/edit-widgets/style.css 4.17 kB
build/editor/index.min.js 38.4 kB
build/editor/style-rtl.css 3.71 kB
build/editor/style.css 3.71 kB
build/element/index.min.js 3.29 kB
build/escape-html/index.min.js 517 B
build/format-library/index.min.js 6.58 kB
build/format-library/style-rtl.css 571 B
build/format-library/style.css 571 B
build/hooks/index.min.js 1.63 kB
build/html-entities/index.min.js 424 B
build/i18n/index.min.js 3.75 kB
build/is-shallow-equal/index.min.js 501 B
build/keyboard-shortcuts/index.min.js 1.8 kB
build/keycodes/index.min.js 1.39 kB
build/list-reusable-blocks/index.min.js 1.72 kB
build/list-reusable-blocks/style-rtl.css 838 B
build/list-reusable-blocks/style.css 838 B
build/media-utils/index.min.js 2.92 kB
build/notices/index.min.js 925 B
build/nux/index.min.js 2.08 kB
build/nux/style-rtl.css 747 B
build/nux/style.css 743 B
build/plugins/index.min.js 1.84 kB
build/primitives/index.min.js 924 B
build/priority-queue/index.min.js 582 B
build/react-i18n/index.min.js 671 B
build/react-refresh-entry/index.min.js 8.44 kB
build/react-refresh-runtime/index.min.js 7.31 kB
build/redux-routine/index.min.js 2.65 kB
build/reusable-blocks/index.min.js 2.22 kB
build/reusable-blocks/style-rtl.css 256 B
build/reusable-blocks/style.css 256 B
build/rich-text/index.min.js 11 kB
build/server-side-render/index.min.js 1.58 kB
build/shortcode/index.min.js 1.49 kB
build/token-list/index.min.js 639 B
build/url/index.min.js 1.9 kB
build/viewport/index.min.js 1.05 kB
build/warning/index.min.js 248 B
build/widgets/index.min.js 7.15 kB
build/widgets/style-rtl.css 1.16 kB
build/widgets/style.css 1.16 kB
build/wordcount/index.min.js 1.04 kB

compressed-size-action

@fluiddot fluiddot marked this pull request as ready for review January 18, 2022 15:37
@fluiddot fluiddot requested review from dcalhoun and geriux January 18, 2022 15:37
Comment on lines 53 to 58
// Some of the store updates that happen upon editor initialization are executed at the end of the current
// Javascript block execution and after the test is finished. In order to prevent "act" warnings due to
// this behavior, we wait for the execution block to be finished before acting on the test.
act(
() => new Promise( ( actResolve ) => setImmediate( actResolve ) )
).then( () => {
Copy link
Member

Choose a reason for hiding this comment

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

Without full context of the origin issue, this solution comes across as somewhat "mask" for the core issue rather than a fix. That is not to say it is not a valid solution, I just hope to understand the issue itself as well.

Were you able to identify what specific update occurs? That context would be helpful to understanding the proposed fix. 🙇🏻

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I totally understand your point, I'm actually gathering insights for providing a full context, I'll let you know when it's ready 🙇 .

Copy link
Contributor Author

@fluiddot fluiddot Jan 18, 2022

Choose a reason for hiding this comment

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

@dcalhoun I added a Context section in the PR's description regarding this topic, it's a bit hard to explain as it's related to asynchronicity and store updates, so let me know if you'd like me to expand anything.

Probably, the best way to have a better context is by modifying the code and adding some breakpoints, I referenced where I placed mine for testing (reference) in case it helps out, but feel free to test further in this regard.

Copy link
Member

@dcalhoun dcalhoun Jan 19, 2022

Choose a reason for hiding this comment

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

@dcalhoun I added a Context section in the PR's description regarding this topic, it's a bit hard to explain as it's related to asynchronicity and store updates, so let me know if you'd like me to expand anything.

Fantastic! Thank you so much for taking the time to document this context. It is immensely helpful for me, and I imagine it will be for future readers as well.

Probably, the best way to have a better context is by modifying the code and adding some breakpoints, I referenced where I placed mine for testing (reference) in case it helps out, but feel free to test further in this regard.

Agreed. I will likely circle back to further explore this subject at some point. Specifically, thanks to the wonderful context you provided, I wonder if we might be able to wrap either the resolver promise and/or a manual tick of fake timers with act to explicitly await the core asynchronous work. I.e. I question if Promise + setImmediate is the most straightforward way of expressing what we are awaiting.

I do not share that thought to question the current implementation or imply that we should explore it now, but merely "thinking out loud."

Copy link
Member

@dcalhoun dcalhoun left a comment

Choose a reason for hiding this comment

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

🚀 LGTM. Thank you for researching this complex topic and proposing a solution. I left one suggestion for consideration, and an other comment merely for discussion. Neither are blockers to merging this work.

I could likely spend a few more days exploring to better understand the issue myself, but I do not believe that should block the Jest 27 upgrade if you would like to move forward and merge this. 👍🏻

test/native/helpers.js Outdated Show resolved Hide resolved
Comment on lines 53 to 58
// Some of the store updates that happen upon editor initialization are executed at the end of the current
// Javascript block execution and after the test is finished. In order to prevent "act" warnings due to
// this behavior, we wait for the execution block to be finished before acting on the test.
act(
() => new Promise( ( actResolve ) => setImmediate( actResolve ) )
).then( () => {
Copy link
Member

@dcalhoun dcalhoun Jan 19, 2022

Choose a reason for hiding this comment

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

@dcalhoun I added a Context section in the PR's description regarding this topic, it's a bit hard to explain as it's related to asynchronicity and store updates, so let me know if you'd like me to expand anything.

Fantastic! Thank you so much for taking the time to document this context. It is immensely helpful for me, and I imagine it will be for future readers as well.

Probably, the best way to have a better context is by modifying the code and adding some breakpoints, I referenced where I placed mine for testing (reference) in case it helps out, but feel free to test further in this regard.

Agreed. I will likely circle back to further explore this subject at some point. Specifically, thanks to the wonderful context you provided, I wonder if we might be able to wrap either the resolver promise and/or a manual tick of fake timers with act to explicitly await the core asynchronous work. I.e. I question if Promise + setImmediate is the most straightforward way of expressing what we are awaiting.

I do not share that thought to question the current implementation or imply that we should explore it now, but merely "thinking out loud."

@fluiddot
Copy link
Contributor Author

The mobile unit tests began failing after pushing the last commit 😞 . That change only updated a comment so it's unrelated to the failure hence, this might be revealing that the solution provided is not stable. In fact, I restarted the CI job on the first commit that succeed and it also failed, so looks like we might have seen a false positive.

However, so far I couldn't reproduce it locally, but in any case, I'll keep investigating the issue further, in case I can introduce a better fix 🔧 .

@gziolo
Copy link
Member

gziolo commented Jan 19, 2022

I see that the test still fails again. This is a very complex issue as I read the description of the PR. In this particular case, would it help if we warm up the store by triggering the selectors earlier so they are already resolved when calling render in initializeEditor? It might be very brittle but as long as it's contained in the helper it might be good enough.

The function is now synchronous as we don't have to wait for any element.
…m:WordPress/gutenberg into rnmobile/fix/act-warnings-rich-text-tests

# Conflicts:
#	test/native/helpers.js
@fluiddot
Copy link
Contributor Author

fluiddot commented Jan 19, 2022

Here are my findings after trying different approaches, in order to assure that we wait for the store updates that occur after the test finish:

  1. ✅ Use fake timers (based on this post): The idea is basically to mock the timeout function used to execute the resolvers (reference) and advance the timers within the act function. However, I couldn't manage to make it work because the RN animation library fails with the following error:
    TypeError: global.cancelAnimationFrame is not a function

      at TimingAnimation.stop (node_modules/react-native/Libraries/Animated/animations/TimingAnimation.js:163:12)
      at AnimatedValue.stopAnimation (node_modules/react-native/Libraries/Animated/nodes/AnimatedValue.js:186:40)
      at AnimatedValue.__detach (node_modules/react-native/Libraries/Animated/nodes/AnimatedValue.js:105:10)
      at AnimatedValue.__removeChild (node_modules/react-native/Libraries/Animated/nodes/AnimatedWithChildren.js:67:12)
      at AnimatedInterpolation.__detach (node_modules/react-native/Libraries/Animated/nodes/AnimatedInterpolation.js:343:18)
      at AnimatedInterpolation.__removeChild (node_modules/react-native/Libraries/Animated/nodes/AnimatedWithChildren.js:67:12)
      at forEach (node_modules/react-native/Libraries/Animated/nodes/AnimatedTransform.js:84:17)
          at Array.forEach (<anonymous>)
      at AnimatedTransform.__detach (node_modules/react-native/Libraries/Animated/nodes/AnimatedTransform.js:80:22)
      at AnimatedTransform.__removeChild (node_modules/react-native/Libraries/Animated/nodes/AnimatedWithChildren.js:67:12)

UPDATE: This was the option that finally solved the issue 🎊 .

  1. ❌ Identify the selectors that have resolvers pending and wait for them to be finished: The goal of this approach is to assure that we wait for all selectors with resolvers that have been triggered upon the editor initialization. It turned out to be a bit complex as it requires extracting all the resolvers from the data registry, and even having them, I couldn't find a way to identify which ones are pending to be resolved.

  2. ❌ Use setImmediate: This is the current approach that is currently failing. I investigated the failure further and looks like it might be related to the machine where they are run, as locally it can't be reproduced. I made a POC by replacing setImmediate with setTimeout and I got failures by setting different timeout durations. In some cases, even re-running the test with the same value fails randomly. So neither waiting for the current block execution to finish (using setImmediate) nor waiting fixed time (using setTimeout) options aren't really reliable as a long-term fix.

  3. ❌ Assure that the editor is unmounted when the test finishes: I was wondering why we have this failure because if the editor is unmounted when the test finishes then there shouldn't be any React tree update. I did another POC by waiting extra time on the resolvers (reference) and surprisingly the act warnings went away. Thanks to this, I understood that the failure is caused by a very particular condition, selectors that are resolved in the time lapse between the test finish and the unmounting triggered by the React Native Testing library 🤯 .

This means that, at least for the case that is failing, we could fix it just by manually unmounting the editor at the end of the test 🔧 . I applied this workaround in af1d8ca.

@fluiddot fluiddot requested a review from dcalhoun January 19, 2022 13:31
@fluiddot
Copy link
Contributor Author

I see that the test still fails again. This is a very complex issue as I read the description of the PR. In this particular case, would it help if we warm up the store by triggering the selectors earlier so they are already resolved when calling render in initializeEditor? It might be very brittle but as long as it's contained in the helper it might be good enough.

This is an interesting solution, thanks @gziolo for the suggestion 🙇. I think it should work since the next time the selectors are called they will use the cached values. In any case, I already provided a workaround in af1d8ca that should solve it, but I'll be more than happy to use this one if we prefer it, I'd like to wait for @dcalhoun input regarding this.

@fluiddot
Copy link
Contributor Author

After checking the latest test output, I noticed that some tests from block-library/src/image/test/edit.native.js are now giving act warnings, they're not producing a failure but it would be great to review them.

@gziolo
Copy link
Member

gziolo commented Jan 19, 2022

This means that, at least for the case that is failing, we could fix it just by manually unmounting the editor at the end of the test 🔧 . I applied this workaround in af1d8ca.

That sounds like a way to go, too. It's a bit strange though, that the React Testing Library expects wrapping act for component's side effects.

@dcalhoun
Copy link
Member

@fluiddot @gziolo I opened #38077 for your consideration to address the act errors using legacy fake timers. I ran the tests locally multiple times without act errors, but please feel free to do so yourself as well, as we have seen them sporadically show up.

* Leverage fake timers to resolve store resolvers

During editor initialization, asynchronous store resolvers rely upon
`setTimeout` to run at the end of the current JavaScript block
execution. In order to prevent "act" warnings triggered by updates to
the React tree, we leverage fake timers to manually tick and await the
resolution of the current block execution before proceeding.

* Update RichText test `initializeEditor` usage

The refactor of `initializeEditor` to leverage fake timers means this
test no longer needs to `await` asynchronous work or manuall `unmount`
its subject component.

* Refactor usage of `initializeEditor` in tests

Now that `initializeEditor` leverages fake timers to resolve
asynchronous store resolvers, the tests no longer need to await a
resolution of `initializeEditor`.

* Fix `act` warnings in Image edit test

Retrieving text from the clipboard is an asynchronous action. The
component updated React state once the clipboard resolved. This resulted
in a state update triggering an `act` warning. Because there is no
visible change to the rendered output, e.g. updated text or UI, we must
await the resolution of the clipboard promise itself.
@fluiddot
Copy link
Contributor Author

@fluiddot @gziolo I opened #38077 for your consideration to address the act errors using legacy fake timers. I ran the tests locally multiple times without act errors, but please feel free to do so yourself as well, as we have seen them sporadically show up.

Thanks @dcalhoun for solving the issue 🎊, I've just approved and merged the PR.

@fluiddot
Copy link
Contributor Author

@dcalhoun @gziolo Looks like we finally managed to fix the errors in all mobile unit tests 🎊, thanks for your help 🙇 !
I'd appreciate it if you could do a quick last review, and then we could merge it into the parent branch 👍 .

Copy link
Member

@dcalhoun dcalhoun left a comment

Choose a reason for hiding this comment

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

🚀 LGTM. Great work collaborating to find a solution. 👏🏻

@fluiddot fluiddot merged commit 80c8b7e into update/jest-27 Jan 19, 2022
@fluiddot fluiddot deleted the rnmobile/fix/act-warnings-rich-text-tests branch January 19, 2022 18:09
gziolo added a commit that referenced this pull request Jan 20, 2022
* Testing: Upgrade Jest to v27

* Fix native test timeouts caused by combining fake timers and setImmediate (#37715)

* Upgrade to @testing-library/[email protected]

* Clean up fake timer usage after tests

This may be unnecessary, but avoid potential issues where fake timers
are unexpectedly used and cause breakages in other tests.

* Enable combination of modern fake timers and waitFor

Previously, `waitFor` would timeout when `jest.useFakeTimers('modern')`
was enabled. The 'modern' version is now the default in Jest 27.

The Jest preset from `@testing-library/react-native` provides a
workaround for a larger issue in React Native and Jest that mutates the
global `Promise` object. https://git.io/JSDZI

* Remove global enabling of fake timers

Enabling fake timers can have negative consequences with `waitFor`, e.g.
causing unexpected timeouts. Enabling it globally is far-reaching and
this should likely be enabled within individual tests as needed.

* Replace jest-jasmine2 with jest-circus

The latter is considered the successor to the former. We seemingly do
not depend on anything explicitly provided by `jest-jasmine2` and should
likely move on from it.

* Switch testing environment from jsdom to node

Improves speed of test environment setup and fixes a timeout issue when
combining `waitFor` and `jest.useFakeTimers('modern')`. It is not yet
exactly pinpointed as to _why_ this fixes the timeout issue. It appears
to related to `setImmediate` and `setTimeout`.

* Remove setImmediate global for testing environment

This may be unnecessary if `testEnvironment: 'node'` is retained.
However, most tests are currently broken due to missing DOM APIs.

* Polyfill required DOM APIs for testing environment

Now that `testEnvironment: 'node'` is utilized for the testing
environment, we must mirror the app runtime and polyfill the necessary
DOM APIs used in the source.

The Enzyme configuration removed conflicts with the switch from
`testEnvironment: 'jsdom'` to `testEnvironment: 'node'`. Enzyme depends
upon `react-dom`, which introduces far more dependencies upon DOM APIs.
Currently, all Enzyme-related tests fail and need to be replaced with
`@testing-library/react-native`.

* Avoid import of react-dom within native file

Importing `react-dom` introduces additional dependencies upon the DOM
API and is incompatible with `testEnvironment: 'node'`. The `act`
utility is available from `@testing-library/react-native`.

* Explicitly toggle fake timers in tests

This may not be necessary, but may help avoid unexpected issues from
lingering fake timers, e.g. timeout errors while using `waitFor`.

* Reinstate legacy Jest timers

The Jest preset from `@testing-library/react-native` that fixed support
for "modern" timers by modifying the polyfilled the global `Promise`
resulted in new failures from within React Native itself. Specifically,
core React Native components rely upon `.done` from the `promise`
package. `.done` is a non-standard method that does not exist on the
global `Promise` used by `@testing-library/react-native`'s preset.

* Disable erroneously failing test

This previously passing test now fails after upgrading
`@testing-library/react-native` due to changes in the library. Setting
`pointerEvent` to "box-none" or "none" currently erroneously prevents
triggering other events unrelated to pressing on the element, e.g.
`onTouch*`, `onLayout`.

https://git.io/JSHZt

* Reinstate jest-jasmine2 to support done callback

The Jest team create jest-circus as the predecessor for jest-jasmine2.
The former does not support the `done` callback with async/await, and
would appear to have no plans to do so.

It would likely benefit us to refactor the one current test using
`done` away from it, and embrace `jest-circus` to maintain alignment
with Jest core.

* Refactor native unit tests away from done callback

The Jest team created `jest-circus` as the predecessor for
`jest-jasmine2`. The former does not support the `done` callback with
async/await, and would appear to have no plans to do so.

In order to embrace `jest-circus` and maintain alignment with Jest core,
the one test using `done` was refactored to avoid it

- https://git.io/JSHWU
- https://git.io/JSHWk

* Remove Enzyme from Editor tests

* Remove Enzyme from Paragraph tests

* Remove Enzyme from BlockMover tests

* Remove Enzyme from BlockEdit test

* Remove Enzyme from LinksUI test

* Remove Enzyme from ListEdit tests

* Remove Enzyme from Platform tests

* Remove Enzyme from BlockTypesTab tests

* Remove Enzyme from HTMLTextInput tests

* Remove Enzyme from ReusableBlockTab tests

* Remove Enzyme from MediaUpload tests

* Remove Enzyme from BlockMediaUpdateProgress tests

* Remove Enzyme from MediaUploadProgress tests

* Fix ReferencEerror in Inserter test

Usage of `react-test-renderer` caused the following error. Leveraging
`@testing-library/react-native` instead resolved it for an unknown
reason.

```
ReferenceError: You are trying to `import` a file after the Jest environment has been torn down. From packages/block-editor/src/components/inserter/test/index.native.js.

      468 |                     style: listStyle,
      469 |                     safeAreaBottomInset,
    > 470 |                     scrollEnabled,
          |                                             ^
      471 |                     automaticallyAdjustContentInsets: false,
      472 |             };
      473 |

      at Object.get PanResponder [as PanResponder] (node_modules/react-native/index.js:251:12)
      at BottomSheet.render (packages/components/src/mobile/bottom-sheet/index.native.js:470:39)
      at finishClassComponent (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:8459:31)
      at updateClassComponent (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:8409:24)
      at beginWork (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:9990:16)
```

* Fix ReferencEerror in Verse test

Usage of `react-test-renderer` caused the following error. Leveraging
`@testing-library/react-native` instead resolved it for an unknown
reason.

```
ReferenceError: You are trying to `import` a file after the Jest environment has been torn down. From packages/block-editor/src/components/inserter/test/index.native.js.

      468 |                     style: listStyle,
      469 |                     safeAreaBottomInset,
    > 470 |                     scrollEnabled,
          |                                             ^
      471 |                     automaticallyAdjustContentInsets: false,
      472 |             };
      473 |

      at Object.get PanResponder [as PanResponder] (node_modules/react-native/index.js:251:12)
      at BottomSheet.render (packages/components/src/mobile/bottom-sheet/index.native.js:470:39)
      at finishClassComponent (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:8459:31)
      at updateClassComponent (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:8409:24)
      at beginWork (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:9990:16)
```

* Fix ReferencEerror in Audio test

Usage of `react-test-renderer` caused the following error. Leveraging
`@testing-library/react-native` instead resolved it for an unknown
reason.

```
ReferenceError: You are trying to `import` a file after the Jest environment has been torn down. From packages/block-editor/src/components/inserter/test/index.native.js.

      468 |                     style: listStyle,
      469 |                     safeAreaBottomInset,
    > 470 |                     scrollEnabled,
          |                                             ^
      471 |                     automaticallyAdjustContentInsets: false,
      472 |             };
      473 |

      at Object.get PanResponder [as PanResponder] (node_modules/react-native/index.js:251:12)
      at BottomSheet.render (packages/components/src/mobile/bottom-sheet/index.native.js:470:39)
      at finishClassComponent (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:8459:31)
      at updateClassComponent (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:8409:24)
      at beginWork (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:9990:16)
```

* Fix ReferencEerror in File test

Usage of `react-test-renderer` caused the following error. Leveraging
`@testing-library/react-native` instead resolved it for an unknown
reason.

```
ReferenceError: You are trying to `import` a file after the Jest environment has been torn down. From packages/block-editor/src/components/inserter/test/index.native.js.

      468 |                     style: listStyle,
      469 |                     safeAreaBottomInset,
    > 470 |                     scrollEnabled,
          |                                             ^
      471 |                     automaticallyAdjustContentInsets: false,
      472 |             };
      473 |

      at Object.get PanResponder [as PanResponder] (node_modules/react-native/index.js:251:12)
      at BottomSheet.render (packages/components/src/mobile/bottom-sheet/index.native.js:470:39)
      at finishClassComponent (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:8459:31)
      at updateClassComponent (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:8409:24)
      at beginWork (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:9990:16)
```

* Fix ReferencEerror in Search test

Usage of `react-test-renderer` caused the following error. Leveraging
`@testing-library/react-native` instead resolved it for an unknown
reason.

```
ReferenceError: You are trying to `import` a file after the Jest environment has been torn down. From packages/block-editor/src/components/inserter/test/index.native.js.

      468 |                     style: listStyle,
      469 |                     safeAreaBottomInset,
    > 470 |                     scrollEnabled,
          |                                             ^
      471 |                     automaticallyAdjustContentInsets: false,
      472 |             };
      473 |

      at Object.get PanResponder [as PanResponder] (node_modules/react-native/index.js:251:12)
      at BottomSheet.render (packages/components/src/mobile/bottom-sheet/index.native.js:470:39)
      at finishClassComponent (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:8459:31)
      at updateClassComponent (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:8409:24)
      at beginWork (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:9990:16)
```

* Fix ReferencEerror in Missing test

Usage of `react-test-renderer` caused the following error. Leveraging
`@testing-library/react-native` instead resolved it for an unknown
reason.

```
ReferenceError: You are trying to `import` a file after the Jest environment has been torn down. From packages/block-editor/src/components/inserter/test/index.native.js.

      468 |                     style: listStyle,
      469 |                     safeAreaBottomInset,
    > 470 |                     scrollEnabled,
          |                                             ^
      471 |                     automaticallyAdjustContentInsets: false,
      472 |             };
      473 |

      at Object.get PanResponder [as PanResponder] (node_modules/react-native/index.js:251:12)
      at BottomSheet.render (packages/components/src/mobile/bottom-sheet/index.native.js:470:39)
      at finishClassComponent (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:8459:31)
      at updateClassComponent (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:8409:24)
      at beginWork (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:9990:16)
```

* Upgrade to @testing-library/[email protected]

* Add assertions to block type tab tests

Improve test clarity with explicit expect assertions.

Co-authored-by: Carlos Garcia <[email protected]>

* Remove unnecessary abstraction

The switch away from Enzyme reduced this abstraction to a single line,
so it provides less value now.

* Consistently assert media block update progress spinner removal

This increases consistency amongst the tests, as most already include
this assertion.

* Update MediaUpload test to select 'Choose from device' option

This selection better aligns with the test description.

* Remove duplicative matchMedia global definition

The test environment now imports the globals setup file used by the app
runtime. That file includes a `matchMedia` global definition as well.

* Add assertions to LinkSettings tests

Improve test clarity with explicit expect assertions.

* Removed shallow renderer usage in tests

Shallow rendering components is generally considered a non-optimal
approach to testing React components by the React community. This
replaces `shallow` with `render` to further test integration of the
subject components.

* Remove unused import

The `React` import was utilized by the now removed `shallow` render
implementation.

* Remove unnecessary top-level beforeAll usage

Jest runs each test file independently, so top-level code will not
impact other test files. Since the `(before|after)All` usage in code
changed is all top-level, and not within a `describe`, it is
superfluous.

Co-authored-by: Carlos Garcia <[email protected]>

* Fix erroneous react-dom usage in native tests (#37921)

* Reinstate react-platform.native.js file

This file was removed in the following commit, as it was no longer
strictly necessary. b7b62d2#diff-f02069349f238fb47a268bb7fcc03c9768331db18ead0e28d8ecad7bbc05037c

However, the removal led to Jest loading the DOM-specific version of the
file when mocking Gutenberg modules that depended upon
`react-platform.js`. Jest would seemingly load `react-dom` when
attempting to auto-mock a module, e.g. `jest.mock( '@wordpress/data/src/components/use-select' );`.

The loading of `react-dom` resulted in the following error:

```
TypeError: Cannot use 'in' operator to search for 'WebkitAnimation' in undefined

  at getVendorPrefixedEventName (node_modules/react-dom/cjs/react-dom.development.js:5011:58)
  at node_modules/react-dom/cjs/react-dom.development.js:5019:21
  at Object.<anonymous> (node_modules/react-dom/cjs/react-dom.development.js:26261:5)
  at Object.<anonymous> (node_modules/react-dom/index.js:37:20)
```

Reinstating `react-platform.native.js` addresses this issue by ensuring
that Jest does not encounter an import of `react-dom`.

* Fix false-positive ReactNativeEditor test

This test failed when run in isolation. It would appear it was dependent
upon the `jest.mock('../setup')` found in sibling tests.

After defining a mock for this specific test, it was discovered that the
assertion did not await the asynchronous query found within the test.
The assertion resulted in a false-positive as the returned query
`Promise` technically matches `toBeDefined`. `async`/`await` was added
to ensure the test assertion awaits the query result, as well as fixes
the following related log warning.

```
A worker process has failed to exit gracefully and has been force exited. This is likely caused by tests leaking due to improper teardown. Try running with --detectOpenHandles to find leaks. Active timers can also cause this, ensure that .unref() was called on them.
```

* [RNMobile] Fix `act` warnings that might be triggered after test finishes (#38052)

* Fix act warnings after test finishes

This is a workaround for addressing the act warnings that might caused by store updates executed after the test finishes.

* Update comment of setImmediate workaround

Co-authored-by: David Calhoun <[email protected]>

* Unmount editor when test finish

* Update initialize editor helper

The function is now synchronous as we don't have to wait for any element.

* Update tests that use initialize editor

* Fix act warnings from store resolvers with fake timers (#38077)

* Leverage fake timers to resolve store resolvers

During editor initialization, asynchronous store resolvers rely upon
`setTimeout` to run at the end of the current JavaScript block
execution. In order to prevent "act" warnings triggered by updates to
the React tree, we leverage fake timers to manually tick and await the
resolution of the current block execution before proceeding.

* Update RichText test `initializeEditor` usage

The refactor of `initializeEditor` to leverage fake timers means this
test no longer needs to `await` asynchronous work or manuall `unmount`
its subject component.

* Refactor usage of `initializeEditor` in tests

Now that `initializeEditor` leverages fake timers to resolve
asynchronous store resolvers, the tests no longer need to await a
resolution of `initializeEditor`.

* Fix `act` warnings in Image edit test

Retrieving text from the clipboard is an asynchronous action. The
component updated React state once the clipboard resolved. This resulted
in a state update triggering an `act` warning. Because there is no
visible change to the rendered output, e.g. updated text or UI, we must
await the resolution of the clipboard promise itself.

Co-authored-by: David Calhoun <[email protected]>

Co-authored-by: David Calhoun <[email protected]>
Co-authored-by: Carlos Garcia <[email protected]>
@fluiddot
Copy link
Contributor Author

While working on adding new tests, I bumped again into this issue, I've opened this issue outlining what I found so far. I'll try to figure out if we can provide a new workaround for this case 🔧 .

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Mobile App - i.e. Android or iOS Native mobile impl of the block editor. (Note: used in scripts, ping mobile folks to change) [Type] Bug An existing feature does not function as intended
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants