@wordpress/e2e-tests 7.21.0 → 7.22.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,8 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ ## 7.22.0 (2024-02-09)
6
+
5
7
  ## 7.21.0 (2024-01-24)
6
8
 
7
9
  ## 7.20.0 (2024-01-10)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wordpress/e2e-tests",
3
- "version": "7.21.0",
3
+ "version": "7.22.0",
4
4
  "description": "End-To-End (E2E) tests for WordPress.",
5
5
  "author": "The WordPress Contributors",
6
6
  "license": "GPL-2.0-or-later",
@@ -23,13 +23,13 @@
23
23
  "node": ">=14"
24
24
  },
25
25
  "dependencies": {
26
- "@wordpress/e2e-test-utils": "^10.21.0",
27
- "@wordpress/interactivity": "^4.0.0",
28
- "@wordpress/interactivity-router": "^1.0.0",
29
- "@wordpress/jest-console": "^7.21.0",
30
- "@wordpress/jest-puppeteer-axe": "^6.21.0",
31
- "@wordpress/scripts": "^27.1.0",
32
- "@wordpress/url": "^3.51.0",
26
+ "@wordpress/e2e-test-utils": "^10.22.0",
27
+ "@wordpress/interactivity": "^5.0.0",
28
+ "@wordpress/interactivity-router": "^1.1.0",
29
+ "@wordpress/jest-console": "^7.22.0",
30
+ "@wordpress/jest-puppeteer-axe": "^6.22.0",
31
+ "@wordpress/scripts": "^27.2.0",
32
+ "@wordpress/url": "^3.52.0",
33
33
  "chalk": "^4.0.0",
34
34
  "expect-puppeteer": "^4.4.0",
35
35
  "filenamify": "^4.2.0",
@@ -46,5 +46,5 @@
46
46
  "publishConfig": {
47
47
  "access": "public"
48
48
  },
49
- "gitHead": "45de2cb4212fed7f2763e95f10300d1ff9d0ec08"
49
+ "gitHead": "eb796371e9630636a4a8837033807b0c4a06ed67"
50
50
  }
@@ -0,0 +1,36 @@
1
+ <?php
2
+ /**
3
+ * Plugin Name: Gutenberg Test Block Bindings
4
+ * Plugin URI: https://github.com/WordPress/gutenberg
5
+ * Author: Gutenberg Team
6
+ *
7
+ * @package gutenberg-test-block-bindings
8
+ */
9
+
10
+ /**
11
+ * Register custom fields.
12
+ */
13
+ function gutenberg_test_block_bindings_register_custom_fields() {
14
+ register_meta(
15
+ 'post',
16
+ 'text_custom_field',
17
+ array(
18
+ 'show_in_rest' => true,
19
+ 'type' => 'string',
20
+ 'single' => true,
21
+ 'default' => 'Value of the text_custom_field',
22
+ )
23
+ );
24
+ // TODO: Change url.
25
+ register_meta(
26
+ 'post',
27
+ 'url_custom_field',
28
+ array(
29
+ 'show_in_rest' => true,
30
+ 'type' => 'string',
31
+ 'single' => true,
32
+ 'default' => '#url-custom-field',
33
+ )
34
+ );
35
+ }
36
+ add_action( 'init', 'gutenberg_test_block_bindings_register_custom_fields' );
@@ -134,3 +134,12 @@ wp_enqueue_script_module( 'directive-context-view' );
134
134
  <button data-testid="navigate" data-wp-on--click="actions.navigate">Navigate</button>
135
135
  <button data-testid="async navigate" data-wp-on--click="actions.asyncNavigate">Async Navigate</button>
136
136
  </div>
137
+
138
+ <div
139
+ data-wp-interactive='{"namespace": "directive-context-non-default"}'
140
+ data-wp-context--non-default='{ "text": "non default" }'
141
+ data-wp-context='{ "defaultText": "default" }'
142
+ >
143
+ <span data-testid="non-default suffix context" data-wp-text="context.text"></span>
144
+ <span data-testid="default suffix context" data-wp-text="context.defaultText"></span>
145
+ </div>
@@ -21,6 +21,18 @@ wp_enqueue_script_module( 'directive-each-view' );
21
21
 
22
22
  <hr>
23
23
 
24
+ <div data-testid="letters-kebab-case">
25
+ <template data-wp-each--my-item="state.letters">
26
+ <p data-wp-text="context.myItem" data-testid="item"></p>
27
+ </template>
28
+ <!-- SSRed elements; they should be removed on hydration -->
29
+ <p data-testid="item" data-wp-each-child>A</p>
30
+ <p data-testid="item" data-wp-each-child>B</p>
31
+ <p data-testid="item" data-wp-each-child>C</p>
32
+ </div>
33
+
34
+ <hr>
35
+
24
36
  <div data-testid="fruits">
25
37
  <button
26
38
  data-testid="rotate" data-wp-on--click="actions.rotateFruits"
@@ -1,7 +1,11 @@
1
1
  /**
2
2
  * WordPress dependencies
3
3
  */
4
- import { store, directive, getContext } from '@wordpress/interactivity';
4
+ import { store, getContext, privateApis } from '@wordpress/interactivity';
5
+
6
+ const { directive } = privateApis(
7
+ 'I acknowledge that using private APIs means my theme or plugin will inevitably break in the next version of WordPress.'
8
+ );
5
9
 
6
10
  // Mock `data-wp-show` directive to test when things are removed from the
7
11
  // DOM. Replace with `data-wp-show` when it's ready.
@@ -1,7 +1,11 @@
1
1
  /**
2
2
  * WordPress dependencies
3
3
  */
4
- import { store, directive } from '@wordpress/interactivity';
4
+ import { store, privateApis } from '@wordpress/interactivity';
5
+
6
+ const { directive } = privateApis(
7
+ 'I acknowledge that using private APIs means my theme or plugin will inevitably break in the next version of WordPress.'
8
+ );
5
9
 
6
10
  // Mock `data-wp-show` directive to test when things are removed from the
7
11
  // DOM. Replace with `data-wp-show` when it's ready.
@@ -1,7 +1,11 @@
1
1
  /**
2
2
  * WordPress dependencies
3
3
  */
4
- import { store, directive } from '@wordpress/interactivity';
4
+ import { store, privateApis } from '@wordpress/interactivity';
5
+
6
+ const { directive } = privateApis(
7
+ 'I acknowledge that using private APIs means my theme or plugin will inevitably break in the next version of WordPress.'
8
+ );
5
9
 
6
10
  // Mock `data-wp-show` directive to test when things are removed from the
7
11
  // DOM. Replace with `data-wp-show` when it's ready.
@@ -4,12 +4,14 @@
4
4
  import {
5
5
  store,
6
6
  getContext,
7
- directive,
8
- deepSignal,
9
7
  useEffect,
10
- createElement as h,
8
+ privateApis
11
9
  } from '@wordpress/interactivity';
12
10
 
11
+ const { directive, deepSignal, h } = privateApis(
12
+ 'I acknowledge that using private APIs means my theme or plugin will inevitably break in the next version of WordPress.'
13
+ );
14
+
13
15
  /**
14
16
  * Namespace used in custom directives and store.
15
17
  */
@@ -3,13 +3,16 @@
3
3
  */
4
4
  import {
5
5
  store,
6
- directive,
7
6
  useInit,
8
7
  useWatch,
9
- cloneElement,
10
8
  getElement,
9
+ privateApis
11
10
  } from '@wordpress/interactivity';
12
11
 
12
+ const { directive, cloneElement } = privateApis(
13
+ 'I acknowledge that using private APIs means my theme or plugin will inevitably break in the next version of WordPress.'
14
+ );
15
+
13
16
  // Custom directive to show hide the content elements in which it is placed.
14
17
  directive(
15
18
  'show-children',
@@ -1,12 +1,17 @@
1
1
  /**
2
2
  * WordPress dependencies
3
3
  */
4
- import { store, getContext, createElement } from '@wordpress/interactivity';
4
+ import { store, getContext, privateApis } from '@wordpress/interactivity';
5
+
6
+ const { h } = privateApis(
7
+ 'I acknowledge that using private APIs means my theme or plugin will inevitably break in the next version of WordPress.'
8
+ );
9
+
5
10
 
6
11
  const { state } = store( 'directive-context', {
7
12
  state: {
8
13
  text: 'Text 1',
9
- component: () => (createElement( 'div', {}, state.text )),
14
+ component: () => ( h( 'div', {}, state.text ) ),
10
15
  number: 1,
11
16
  boolean: true
12
17
  },
@@ -1,7 +1,11 @@
1
1
  /**
2
2
  * WordPress dependencies
3
3
  */
4
- import { store, directive } from '@wordpress/interactivity';
4
+ import { store, privateApis } from '@wordpress/interactivity';
5
+
6
+ const { directive } = privateApis(
7
+ 'I acknowledge that using private APIs means my theme or plugin will inevitably break in the next version of WordPress.'
8
+ );
5
9
 
6
10
  // Fake `data-wp-show-mock` directive to test when things are removed from the
7
11
  // DOM. Replace with `data-wp-show` when it's ready.
@@ -8,6 +8,13 @@
8
8
  */
9
9
 
10
10
  wp_enqueue_script_module( 'router-navigate-view' );
11
+
12
+ if ( $attributes['disableNavigation'] ) {
13
+ wp_interactivity_config(
14
+ 'core/router',
15
+ array( 'clientNavigationDisabled' => true )
16
+ );
17
+ }
11
18
  ?>
12
19
 
13
20
  <div
@@ -17,7 +17,15 @@ wp_enqueue_script_module( 'tovdom-islands-view' );
17
17
 
18
18
  <div data-wp-interactive='{ "namespace": "tovdom-islands" }'>
19
19
  <div data-wp-show-mock="state.falseValue">
20
- <span data-testid="inside an island">
20
+ <span data-testid="inside an island with json object">
21
+ This should not be shown because it is inside an island.
22
+ </span>
23
+ </div>
24
+ </div>
25
+
26
+ <div data-wp-interactive="tovdom-islands">
27
+ <div data-wp-show-mock="state.falseValue">
28
+ <span data-testid="inside an island with string">
21
29
  This should not be shown because it is inside an island.
22
30
  </span>
23
31
  </div>
@@ -69,8 +77,6 @@ wp_enqueue_script_module( 'tovdom-islands-view' );
69
77
  </div>
70
78
  </div>
71
79
 
72
-
73
-
74
80
  <div data-wp-interactive='{ "namespace": "tovdom-islands" }'>
75
81
  <div data-wp-interactive='{ "namespace": "something-new" }'></div>
76
82
  <div data-wp-show-mock="state.falseValue">
@@ -1,7 +1,11 @@
1
1
  /**
2
2
  * WordPress dependencies
3
3
  */
4
- import { store, directive, createElement as h } from '@wordpress/interactivity';
4
+ import { store, privateApis } from '@wordpress/interactivity';
5
+
6
+ const { directive, h } = privateApis(
7
+ 'I acknowledge that using private APIs means my theme or plugin will inevitably break in the next version of WordPress.'
8
+ );
5
9
 
6
10
  // Fake `data-wp-show-mock` directive to test when things are removed from the
7
11
  // DOM. Replace with `data-wp-show` when it's ready.
@@ -1,15 +0,0 @@
1
- {
2
- "$schema": "https://schemas.wp.org/trunk/block.json",
3
- "apiVersion": 2,
4
- "name": "test/directive-body",
5
- "title": "E2E Interactivity tests - directive body",
6
- "category": "text",
7
- "icon": "heart",
8
- "description": "",
9
- "supports": {
10
- "interactivity": true
11
- },
12
- "textdomain": "e2e-interactivity",
13
- "viewScript": "directive-body-view",
14
- "render": "file:./render.php"
15
- }
@@ -1,24 +0,0 @@
1
- <?php
2
- /**
3
- * HTML for testing the directive `data-wp-body`.
4
- *
5
- * @package gutenberg-test-interactive-blocks
6
- */
7
-
8
- wp_enqueue_script_module( 'directive-body-view' );
9
- ?>
10
-
11
- <div
12
- data-wp-interactive='{ "namespace":"directive-body" }'
13
- data-wp-context='{"text":"text-1"}'
14
- >
15
- <div data-testid="container">
16
- <aside data-wp-body data-testid="element with data-wp-body">
17
- <p data-wp-text="context.text" data-testid="text">initial</p>
18
- </aside>
19
- </div>
20
- <button
21
- data-wp-on--click="actions.toggleText"
22
- data-testid="toggle text"
23
- >toggle text</button>
24
- </div>
@@ -1,13 +0,0 @@
1
- /**
2
- * WordPress dependencies
3
- */
4
- import { store, getContext } from '@wordpress/interactivity';
5
-
6
- store( 'directive-body', {
7
- actions: {
8
- toggleText: () => {
9
- const context = getContext();
10
- context.text = context.text === 'text-1' ? 'text-2' : 'text-1';
11
- },
12
- },
13
- } );
@@ -1,118 +0,0 @@
1
- // Jest Snapshot v1, https://goo.gl/fbAQLP
2
-
3
- exports[`Inserting blocks Should insert content using the placeholder and the regular inserter 1`] = `
4
- "<!-- wp:paragraph -->
5
- <p>Paragraph block</p>
6
- <!-- /wp:paragraph -->
7
-
8
- <!-- wp:quote -->
9
- <blockquote class=\\"wp-block-quote\\"><p>Quote block</p></blockquote>
10
- <!-- /wp:quote -->
11
-
12
- <!-- wp:image -->
13
- <figure class=\\"wp-block-image\\"><img alt=\\"\\"/></figure>
14
- <!-- /wp:image -->
15
-
16
- <!-- wp:paragraph -->
17
- <p></p>
18
- <!-- /wp:paragraph -->"
19
- `;
20
-
21
- exports[`Inserting blocks Should insert content using the placeholder and the regular inserter 2`] = `
22
- "<!-- wp:paragraph -->
23
- <p>Paragraph block</p>
24
- <!-- /wp:paragraph -->
25
-
26
- <!-- wp:quote -->
27
- <blockquote class=\\"wp-block-quote\\"><p>Quote block</p></blockquote>
28
- <!-- /wp:quote -->"
29
- `;
30
-
31
- exports[`Inserting blocks Should insert content using the placeholder and the regular inserter 3`] = `
32
- "<!-- wp:paragraph -->
33
- <p>Paragraph block</p>
34
- <!-- /wp:paragraph -->
35
-
36
- <!-- wp:paragraph -->
37
- <p>Second paragraph</p>
38
- <!-- /wp:paragraph -->
39
-
40
- <!-- wp:quote -->
41
- <blockquote class=\\"wp-block-quote\\"><p>Quote block</p></blockquote>
42
- <!-- /wp:quote -->
43
-
44
- <!-- wp:preformatted -->
45
- <pre class=\\"wp-block-preformatted\\">Pre text
46
-
47
- Foo</pre>
48
- <!-- /wp:preformatted -->
49
-
50
- <!-- wp:shortcode -->
51
- [myshortcode]With multiple
52
- lines preserved[/myshortcode]
53
- <!-- /wp:shortcode -->"
54
- `;
55
-
56
- exports[`Inserting blocks inserts a block in proper place after having clicked \`Browse All\` from block appender 1`] = `
57
- "<!-- wp:group {"layout":{"type":"constrained"}} -->
58
- <div class="wp-block-group"><!-- wp:paragraph -->
59
- <p>Paragraph inside group</p>
60
- <!-- /wp:paragraph --></div>
61
- <!-- /wp:group -->
62
-
63
- <!-- wp:paragraph -->
64
- <p>Paragraph after group</p>
65
- <!-- /wp:paragraph -->"
66
- `;
67
-
68
- exports[`Inserting blocks inserts a block in proper place after having clicked \`Browse All\` from inline inserter 1`] = `
69
- "<!-- wp:paragraph -->
70
- <p>First paragraph</p>
71
- <!-- /wp:paragraph -->
72
-
73
- <!-- wp:heading -->
74
- <h2 class="wp-block-heading">Heading</h2>
75
- <!-- /wp:heading -->
76
-
77
- <!-- wp:paragraph -->
78
- <p>Second paragraph</p>
79
- <!-- /wp:paragraph -->
80
-
81
- <!-- wp:paragraph -->
82
- <p>Third paragraph</p>
83
- <!-- /wp:paragraph -->"
84
- `;
85
-
86
- exports[`Inserting blocks inserts a block in proper place after having clicked \`Browse All\` from inline inserter 2`] = `
87
- "<!-- wp:paragraph -->
88
- <p>First paragraph</p>
89
- <!-- /wp:paragraph -->
90
-
91
- <!-- wp:cover {"layout":{"type":"constrained"}} -->
92
- <div class="wp-block-cover"><span aria-hidden="true" class="wp-block-cover__background has-background-dim-100 has-background-dim"></span><div class="wp-block-cover__inner-container"></div></div>
93
- <!-- /wp:cover -->
94
-
95
- <!-- wp:heading -->
96
- <h2 class="wp-block-heading">Heading</h2>
97
- <!-- /wp:heading -->
98
-
99
- <!-- wp:paragraph -->
100
- <p>Second paragraph</p>
101
- <!-- /wp:paragraph -->
102
-
103
- <!-- wp:paragraph -->
104
- <p>Third paragraph</p>
105
- <!-- /wp:paragraph -->"
106
- `;
107
-
108
- exports[`Inserting blocks inserts blocks at root level when using the root appender while selection is in an inner block 1`] = `
109
- "<!-- wp:buttons -->
110
- <div class="wp-block-buttons"><!-- wp:button -->
111
- <div class="wp-block-button"><a class="wp-block-button__link wp-element-button">1.1</a></div>
112
- <!-- /wp:button --></div>
113
- <!-- /wp:buttons -->
114
-
115
- <!-- wp:paragraph -->
116
- <p>2</p>
117
- <!-- /wp:paragraph -->"
118
- `;
@@ -1,11 +0,0 @@
1
- // Jest Snapshot v1, https://goo.gl/fbAQLP
2
-
3
- exports[`Pattern blocks can be created from multiselection and converted back to regular blocks 1`] = `
4
- "<!-- wp:paragraph -->
5
- <p>Hello there!</p>
6
- <!-- /wp:paragraph -->
7
-
8
- <!-- wp:paragraph -->
9
- <p>Second paragraph</p>
10
- <!-- /wp:paragraph -->"
11
- `;
@@ -1,388 +0,0 @@
1
- /**
2
- * WordPress dependencies
3
- */
4
- import {
5
- closeGlobalBlockInserter,
6
- createNewPost,
7
- getEditedPostContent,
8
- insertBlock,
9
- openGlobalBlockInserter,
10
- pressKeyTimes,
11
- searchForBlock,
12
- setBrowserViewport,
13
- pressKeyWithModifier,
14
- canvas,
15
- } from '@wordpress/e2e-test-utils';
16
-
17
- /** @typedef {import('puppeteer-core').ElementHandle} ElementHandle */
18
-
19
- /**
20
- * Waits for all patterns in the inserter to have a height, which should
21
- * indicate they've been parsed and are visible.
22
- *
23
- * This allows a test to wait for the layout in the inserter menu to stabilize
24
- * before attempting to interact with the menu contents.
25
- */
26
- async function waitForInserterPatternLoad() {
27
- await page.waitForFunction( () => {
28
- const previewElements = document.querySelectorAll(
29
- '.block-editor-block-preview__container'
30
- );
31
-
32
- if ( ! previewElements.length ) {
33
- return true;
34
- }
35
-
36
- return Array.from( previewElements ).every(
37
- ( previewElement ) => previewElement.offsetHeight > 0
38
- );
39
- } );
40
- }
41
-
42
- describe( 'Inserting blocks', () => {
43
- beforeEach( async () => {
44
- await createNewPost();
45
- } );
46
-
47
- /**
48
- * Given a Puppeteer ElementHandle, clicks below its bounding box.
49
- *
50
- * @param {ElementHandle} elementHandle Element handle.
51
- *
52
- * @return {Promise} Promise resolving when click occurs.
53
- */
54
- async function clickAtBottom( elementHandle ) {
55
- const box = await elementHandle.boundingBox();
56
- const x = box.x + box.width / 2;
57
- const y = box.y + box.height - 50;
58
- return page.mouse.click( x, y );
59
- }
60
-
61
- it.skip( 'Should insert content using the placeholder and the regular inserter', async () => {
62
- // This ensures the editor is loaded in navigation mode.
63
- await page.reload();
64
- await page.waitForSelector( '.edit-post-layout' );
65
-
66
- // Set a tall viewport. The typewriter's intrinsic height can be enough
67
- // to scroll the page on a shorter viewport, thus obscuring the presence
68
- // of any potential buggy behavior with the "stretched" click redirect.
69
- await setBrowserViewport( { width: 960, height: 1400 } );
70
-
71
- // Click below editor to focus last field (block appender)
72
- await clickAtBottom(
73
- await page.$( '.interface-interface-skeleton__content' )
74
- );
75
- expect(
76
- await page.waitForSelector( '[data-type="core/paragraph"]' )
77
- ).not.toBeNull();
78
- await page.keyboard.type( 'Paragraph block' );
79
-
80
- // Using the slash command.
81
- await page.keyboard.press( 'Enter' );
82
- await page.keyboard.type( '/quote' );
83
- await page.waitForXPath(
84
- `//*[contains(@class, "components-autocomplete__result") and contains(@class, "is-selected") and contains(text(), 'Quote')]`
85
- );
86
- await page.keyboard.press( 'Enter' );
87
- await page.keyboard.type( 'Quote block' );
88
-
89
- // Arrow down into default appender.
90
- await page.keyboard.press( 'ArrowDown' );
91
- await page.keyboard.press( 'ArrowDown' );
92
-
93
- // Focus should be moved to block focus boundary on a block which does
94
- // not have its own inputs (e.g. image). Proceeding to press enter will
95
- // append the default block. Pressing backspace on the focused block
96
- // will remove it.
97
- await page.keyboard.type( '/image' );
98
- await page.waitForXPath(
99
- `//*[contains(@class, "components-autocomplete__result") and contains(@class, "is-selected") and contains(text(), 'Image')]`
100
- );
101
- await page.keyboard.press( 'Enter' );
102
- await page.keyboard.press( 'Enter' );
103
- expect( await getEditedPostContent() ).toMatchSnapshot();
104
- await page.keyboard.press( 'Backspace' );
105
- await page.keyboard.press( 'Backspace' );
106
- expect( await getEditedPostContent() ).toMatchSnapshot();
107
-
108
- // Using the regular inserter.
109
- await insertBlock( 'Preformatted' );
110
- await page.keyboard.type( 'Pre block' );
111
- await page.keyboard.press( 'Enter' );
112
- await page.keyboard.press( 'Enter' );
113
-
114
- // Verify vertical traversal at offset. This has been buggy in the past
115
- // where verticality on a blank newline would skip into previous block.
116
- await page.keyboard.type( 'Foo' );
117
- await page.keyboard.press( 'ArrowUp' );
118
- await page.keyboard.press( 'ArrowUp' );
119
- await pressKeyTimes( 'Delete', 6 );
120
- await page.keyboard.type( ' text' );
121
-
122
- // Ensure newline preservation in shortcode block.
123
- // See: https://github.com/WordPress/gutenberg/issues/4456
124
- await insertBlock( 'Shortcode' );
125
- await page.keyboard.type( '[myshortcode]With multiple' );
126
- await page.keyboard.press( 'Enter' );
127
- await page.keyboard.type( 'lines preserved[/myshortcode]' );
128
-
129
- // Unselect blocks to avoid conflicts with the inbetween inserter
130
- await page.click( '.editor-post-title__input' );
131
- await closeGlobalBlockInserter();
132
-
133
- // Using the between inserter.
134
- const insertionPoint = await page.$( '[data-type="core/quote"]' );
135
- const rect = await insertionPoint.boundingBox();
136
- await page.mouse.move( rect.x + rect.width / 2, rect.y - 10, {
137
- steps: 10,
138
- } );
139
- const lineInserter = await page.waitForSelector(
140
- '.block-editor-block-list__insertion-point .block-editor-inserter__toggle'
141
- );
142
- await lineInserter.click();
143
- // [TODO]: Search input should be focused immediately. It shouldn't be
144
- // necessary to have `waitForFunction`.
145
- await page.waitForFunction(
146
- () =>
147
- document.activeElement &&
148
- document.activeElement.classList.contains(
149
- 'components-search-control__input'
150
- )
151
- );
152
- await page.keyboard.type( 'para' );
153
- await pressKeyTimes( 'Tab', 2 );
154
- await page.keyboard.press( 'Enter' );
155
- await page.keyboard.type( 'Second paragraph' );
156
-
157
- expect( await getEditedPostContent() ).toMatchSnapshot();
158
- } );
159
-
160
- it( 'should insert block with the slash inserter when using multiple words', async () => {
161
- await page.keyboard.press( 'Enter' );
162
- await page.keyboard.type( '/tag cloud' );
163
- await page.waitForXPath(
164
- `//*[contains(@class, "components-autocomplete__result") and contains(@class, "is-selected") and contains(text(), 'Tag Cloud')]`
165
- );
166
- await page.keyboard.press( 'Enter' );
167
-
168
- expect(
169
- await canvas().waitForSelector( '[data-type="core/tag-cloud"]' )
170
- ).not.toBeNull();
171
- } );
172
-
173
- // Check for regression of https://github.com/WordPress/gutenberg/issues/23263
174
- it( 'inserts blocks at root level when using the root appender while selection is in an inner block', async () => {
175
- await insertBlock( 'Buttons' );
176
- await page.keyboard.type( '1.1' );
177
-
178
- // After inserting the Buttons block the inner button block should be selected.
179
- const selectedButtonBlocks = await canvas().$$(
180
- '.wp-block-button.is-selected'
181
- );
182
- expect( selectedButtonBlocks.length ).toBe( 1 );
183
-
184
- // The block appender is only visible when there's no selection.
185
- await page.evaluate( () =>
186
- window.wp.data.dispatch( 'core/block-editor' ).clearSelectedBlock()
187
- );
188
- // Specifically click the root container appender.
189
- await canvas().click(
190
- '.block-editor-block-list__layout.is-root-container > .block-list-appender .block-editor-inserter__toggle'
191
- );
192
-
193
- // Insert a paragraph block.
194
- await page.waitForSelector( '.block-editor-inserter__search input' );
195
-
196
- // Search for the paragraph block if it's not in the list of blocks shown.
197
- if ( ! page.$( '.editor-block-list-item-paragraph' ) ) {
198
- await page.keyboard.type( 'Paragraph' );
199
- await page.waitForSelector( '.editor-block-list-item-paragraph' );
200
- await waitForInserterPatternLoad();
201
- }
202
-
203
- // Add the block.
204
- await page.click( '.editor-block-list-item-paragraph' );
205
- await page.keyboard.type( '2' );
206
-
207
- // The snapshot should show a buttons block followed by a paragraph.
208
- // The buttons block should contain a single button.
209
- expect( await getEditedPostContent() ).toMatchSnapshot();
210
- } );
211
-
212
- // Check for regression of https://github.com/WordPress/gutenberg/issues/24262
213
- it( 'inserts a block in proper place after having clicked `Browse All` from inline inserter', async () => {
214
- await insertBlock( 'Paragraph' );
215
- await page.keyboard.type( 'First paragraph' );
216
- await insertBlock( 'Heading' );
217
- await page.keyboard.type( 'Heading' );
218
- await page.keyboard.press( 'Enter' );
219
- await insertBlock( 'Paragraph' );
220
- await page.keyboard.type( 'Second paragraph' );
221
- await page.keyboard.press( 'Enter' );
222
- await page.keyboard.type( 'Third paragraph' );
223
- expect( await getEditedPostContent() ).toMatchSnapshot();
224
-
225
- // Using the between inserter.
226
- const insertionPoint = await canvas().$( '[data-type="core/heading"]' );
227
- const rect = await insertionPoint.boundingBox();
228
- await page.mouse.move( rect.x + rect.width / 2, rect.y - 10, {
229
- steps: 10,
230
- } );
231
- const insertionLine = await page.waitForSelector(
232
- '.block-editor-block-list__insertion-point .block-editor-inserter__toggle'
233
- );
234
- await insertionLine.click();
235
- const browseAll = await page.waitForSelector(
236
- 'button.block-editor-inserter__quick-inserter-expand'
237
- );
238
- await browseAll.click();
239
- // `insertBlock` uses the currently open panel.
240
- await insertBlock( 'Cover' );
241
-
242
- expect( await getEditedPostContent() ).toMatchSnapshot();
243
- } );
244
-
245
- // Check for regression of https://github.com/WordPress/gutenberg/issues/25785
246
- it( 'inserts a block should show a blue line indicator', async () => {
247
- // First insert a random Paragraph.
248
- await insertBlock( 'Paragraph' );
249
- await page.keyboard.type( 'First paragraph' );
250
- await insertBlock( 'Image' );
251
- const paragraphBlock = await canvas().$(
252
- 'p[aria-label="Block: Paragraph"]'
253
- );
254
- paragraphBlock.click();
255
- await page.evaluate( () => new Promise( window.requestIdleCallback ) );
256
-
257
- // Open the global inserter and search for the Heading block.
258
- await searchForBlock( 'Heading' );
259
-
260
- const headingButton = (
261
- await page.$x( `//button//span[contains(text(), 'Heading')]` )
262
- )[ 0 ];
263
- // Hover over the block should show the blue line indicator.
264
- await headingButton.hover();
265
-
266
- // Should show the blue line indicator somewhere.
267
- const indicator = await page.waitForSelector(
268
- '.block-editor-block-list__insertion-point-indicator'
269
- );
270
- const indicatorRect = await indicator.boundingBox();
271
- const paragraphRect = await paragraphBlock.boundingBox();
272
-
273
- // The blue line indicator should be below the last block.
274
- expect( indicatorRect.x ).toBe( paragraphRect.x );
275
- expect( indicatorRect.y > paragraphRect.y ).toBe( true );
276
- } );
277
-
278
- // Check for regression of https://github.com/WordPress/gutenberg/issues/24403
279
- it( 'inserts a block in proper place after having clicked `Browse All` from block appender', async () => {
280
- await insertBlock( 'Group' );
281
- // Select the default, selected Group layout from the variation picker.
282
- await canvas().click(
283
- 'button[aria-label="Group: Gather blocks in a container."]'
284
- );
285
- await insertBlock( 'Paragraph' );
286
- await page.keyboard.type( 'Paragraph after group' );
287
- // Click the Group first to make the appender inside it clickable.
288
- await canvas().click( '[data-type="core/group"]' );
289
- await canvas().click(
290
- '[data-type="core/group"] [aria-label="Add block"]'
291
- );
292
- const browseAll = await page.waitForXPath(
293
- '//button[text()="Browse all"]'
294
- );
295
- await browseAll.click();
296
- await insertBlock( 'Paragraph' );
297
- await page.keyboard.type( 'Paragraph inside group' );
298
- expect( await getEditedPostContent() ).toMatchSnapshot();
299
- } );
300
-
301
- it( 'passes the search value in the main inserter when clicking `Browse all`', async () => {
302
- const INSERTER_SEARCH_SELECTOR =
303
- '.block-editor-inserter__search input,.block-editor-inserter__search-input,input.block-editor-inserter__search';
304
- await insertBlock( 'Group' );
305
- // Select the default, selected Group layout from the variation picker.
306
- await canvas().click(
307
- 'button[aria-label="Group: Gather blocks in a container."]'
308
- );
309
- await insertBlock( 'Paragraph' );
310
- await page.keyboard.type( 'Text' );
311
- // Click the Group first to make the appender inside it clickable.
312
- await canvas().click( '[data-type="core/group"]' );
313
- await canvas().click(
314
- '[data-type="core/group"] [aria-label="Add block"]'
315
- );
316
- await page.waitForSelector( INSERTER_SEARCH_SELECTOR );
317
- await page.focus( INSERTER_SEARCH_SELECTOR );
318
- await pressKeyWithModifier( 'primary', 'a' );
319
- const searchTerm = 'Verse';
320
- await page.keyboard.type( searchTerm );
321
- const browseAll = await page.waitForXPath(
322
- '//button[text()="Browse all"]'
323
- );
324
- await browseAll.click();
325
- const availableBlocks = await page.$$(
326
- '.editor-inserter-sidebar .block-editor-block-types-list__list-item'
327
- );
328
- expect( availableBlocks ).toHaveLength( 1 );
329
- } );
330
-
331
- // Check for regression of https://github.com/WordPress/gutenberg/issues/27586
332
- it( 'closes the main inserter after inserting a single-use block, like the More block', async () => {
333
- await insertBlock( 'More' );
334
- await page.waitForSelector(
335
- '.editor-document-tools__inserter-toggle:not(.is-pressed)'
336
- );
337
-
338
- // The inserter panel should've closed.
339
- const inserterPanels = await page.$$( '.editor-inserter-sidebar' );
340
- expect( inserterPanels.length ).toBe( 0 );
341
-
342
- // The editable 'Read More' text should be focused.
343
- const isFocusInBlock = await canvas().evaluate( () =>
344
- document
345
- .querySelector( '[data-type="core/more"]' )
346
- .contains( document.activeElement )
347
- );
348
- expect( isFocusInBlock ).toBe( true );
349
- } );
350
-
351
- it( 'shows block preview when hovering over block in inserter', async () => {
352
- await openGlobalBlockInserter();
353
- const paragraphButton = (
354
- await page.$x( `//button//span[contains(text(), 'Paragraph')]` )
355
- )[ 0 ];
356
- await paragraphButton.hover();
357
- const preview = await page.waitForSelector(
358
- '.block-editor-inserter__preview',
359
- {
360
- visible: true,
361
- }
362
- );
363
- const isPreviewVisible = await preview.isIntersectingViewport();
364
- expect( isPreviewVisible ).toBe( true );
365
- } );
366
-
367
- it.each( [ 'large', 'small' ] )(
368
- 'last-inserted block should be given and keep the focus (%s viewport)',
369
- async ( viewport ) => {
370
- await setBrowserViewport( viewport );
371
-
372
- await canvas().type(
373
- '.block-editor-default-block-appender__content',
374
- 'Testing inserted block focus'
375
- );
376
-
377
- await insertBlock( 'Image' );
378
-
379
- await canvas().waitForSelector( 'figure[data-type="core/image"]' );
380
-
381
- const selectedBlock = await page.evaluate( () => {
382
- return wp.data.select( 'core/block-editor' ).getSelectedBlock();
383
- } );
384
-
385
- expect( selectedBlock.name ).toBe( 'core/image' );
386
- }
387
- );
388
- } );
@@ -1,289 +0,0 @@
1
- /**
2
- * WordPress dependencies
3
- */
4
- import {
5
- clickMenuItem,
6
- insertBlock,
7
- insertPattern,
8
- createNewPost,
9
- clickBlockToolbarButton,
10
- pressKeyWithModifier,
11
- getEditedPostContent,
12
- trashAllPosts,
13
- visitAdminPage,
14
- toggleGlobalBlockInserter,
15
- openDocumentSettingsSidebar,
16
- saveDraft,
17
- createReusableBlock,
18
- canvas,
19
- } from '@wordpress/e2e-test-utils';
20
-
21
- const patternBlockNameInputSelector =
22
- '.patterns-menu-items__convert-modal .components-text-control__input';
23
- const patternBlockInspectorNameSelector =
24
- '.block-editor-block-inspector h2.block-editor-block-card__title';
25
- const syncToggleSelectorChecked =
26
- '.patterns-menu-items__convert-modal .components-form-toggle.is-checked';
27
-
28
- const clearAllBlocks = async () => {
29
- // Remove all blocks from the post so that we're working with a clean slate.
30
- await page.evaluate( () => {
31
- const blocks = wp.data.select( 'core/block-editor' ).getBlocks();
32
- const clientIds = blocks.map( ( block ) => block.clientId );
33
- wp.data.dispatch( 'core/block-editor' ).removeBlocks( clientIds );
34
- } );
35
- };
36
-
37
- describe( 'Pattern blocks', () => {
38
- afterAll( async () => {
39
- await trashAllPosts( 'wp_block' );
40
- } );
41
-
42
- beforeEach( async () => {
43
- await createNewPost();
44
- } );
45
-
46
- it( 'can be created, inserted, and converted to a regular block.', async () => {
47
- await createReusableBlock( 'Hello there!', 'Greeting block' );
48
- await clearAllBlocks();
49
-
50
- // Insert the reusable block we created above.
51
- await insertPattern( 'Greeting block' );
52
-
53
- // Check that its content is up to date.
54
- const text = await canvas().$eval(
55
- '.block-editor-block-list__block[data-type="core/block"] p',
56
- ( element ) => element.innerText
57
- );
58
- expect( text ).toMatch( 'Hello there!' );
59
-
60
- await clearAllBlocks();
61
-
62
- // Insert the reusable block we edited above.
63
- await insertPattern( 'Greeting block' );
64
-
65
- // Convert block to a regular block.
66
- await clickBlockToolbarButton( 'Options' );
67
- await clickMenuItem( 'Detach' );
68
-
69
- // Check that we have a paragraph block on the page.
70
- const paragraphBlock = await canvas().$(
71
- '.block-editor-block-list__block[data-type="core/paragraph"]'
72
- );
73
- expect( paragraphBlock ).not.toBeNull();
74
-
75
- // Check that its content is up to date.
76
- const paragraphContent = await canvas().$eval(
77
- '.block-editor-block-list__block[data-type="core/paragraph"]',
78
- ( element ) => element.innerText
79
- );
80
- expect( paragraphContent ).toMatch( 'Hello there!' );
81
- } );
82
-
83
- it( 'can be inserted after refresh', async () => {
84
- await createReusableBlock( 'Awesome Paragraph', 'Awesome block' );
85
-
86
- // Step 2. Create new post.
87
- await createNewPost();
88
-
89
- // Step 3. Insert the block created in Step 1.
90
- await insertPattern( 'Awesome block' );
91
-
92
- // Check the title.
93
- await openDocumentSettingsSidebar();
94
- const title = await page.$eval(
95
- patternBlockInspectorNameSelector,
96
- ( element ) => element.textContent
97
- );
98
- expect( title ).toBe( 'Awesome block' );
99
- } );
100
-
101
- it( 'can be created from multiselection and converted back to regular blocks', async () => {
102
- await createNewPost();
103
-
104
- // Insert a Two paragraphs block.
105
- await insertBlock( 'Paragraph' );
106
- await page.keyboard.type( 'Hello there!' );
107
- await page.keyboard.press( 'Enter' );
108
- await page.keyboard.type( 'Second paragraph' );
109
-
110
- // Select all the blocks.
111
- await pressKeyWithModifier( 'primary', 'a' );
112
- await pressKeyWithModifier( 'primary', 'a' );
113
-
114
- // Convert block to a reusable block.
115
- await clickBlockToolbarButton( 'Options' );
116
- await clickMenuItem( 'Create pattern' );
117
-
118
- // Set title.
119
- const nameInput = await page.waitForSelector(
120
- patternBlockNameInputSelector
121
- );
122
- await nameInput.click();
123
- await page.keyboard.type( 'Multi-selection reusable block' );
124
- await page.waitForSelector( syncToggleSelectorChecked );
125
- await page.keyboard.press( 'Enter' );
126
-
127
- // Wait for creation to finish.
128
- await page.waitForXPath(
129
- '//*[contains(@class, "components-snackbar")]/*[contains(text(),"pattern created:")]'
130
- );
131
-
132
- await clearAllBlocks();
133
-
134
- // Insert the reusable block we edited above.
135
- await insertPattern( 'Multi-selection reusable block' );
136
-
137
- // Convert block to a regular block.
138
- await clickBlockToolbarButton( 'Options' );
139
- await clickMenuItem( 'Detach' );
140
-
141
- // Check that we have two paragraph blocks on the page.
142
- expect( await getEditedPostContent() ).toMatchSnapshot();
143
- } );
144
-
145
- it( 'will not break the editor if empty', async () => {
146
- await createReusableBlock(
147
- 'Awesome Paragraph',
148
- 'Random reusable block'
149
- );
150
- await clearAllBlocks();
151
- await insertPattern( 'Random reusable block' );
152
-
153
- await visitAdminPage( 'edit.php', [ 'post_type=wp_block' ] );
154
-
155
- const [ editButton ] = await page.$x(
156
- `//a[contains(@aria-label, 'Random reusable block')]`
157
- );
158
- await editButton.click();
159
-
160
- await page.waitForNavigation();
161
- await page.waitForSelector( 'iframe[name="editor-canvas"]' );
162
-
163
- // Click the block to give it focus.
164
- const blockSelector = 'p[data-title="Paragraph"]';
165
- await canvas().waitForSelector( blockSelector );
166
- await canvas().click( blockSelector );
167
-
168
- // Delete the block, leaving the reusable block empty.
169
- await clickBlockToolbarButton( 'Options' );
170
- const deleteButton = await page.waitForXPath(
171
- '//button/span[text()="Delete"]'
172
- );
173
- deleteButton.click();
174
-
175
- // Wait for the Update button to become enabled.
176
- const publishButtonSelector = '.editor-post-publish-button__button';
177
- await page.waitForSelector(
178
- publishButtonSelector + '[aria-disabled="false"]'
179
- );
180
-
181
- // Save the reusable block.
182
- await page.click( publishButtonSelector );
183
- await page.waitForXPath(
184
- '//*[contains(@class, "components-snackbar")]/*[text()="Pattern updated."]'
185
- );
186
-
187
- await createNewPost();
188
-
189
- await toggleGlobalBlockInserter();
190
-
191
- expect( console ).not.toHaveErrored();
192
- } );
193
-
194
- it( 'Should show a proper message when the reusable block is missing', async () => {
195
- // Insert a non-existant reusable block.
196
- await page.evaluate( () => {
197
- const { createBlock } = window.wp.blocks;
198
- const { dispatch } = window.wp.data;
199
- dispatch( 'core/block-editor' ).resetBlocks( [
200
- createBlock( 'core/block', { ref: 123456 } ),
201
- ] );
202
- } );
203
-
204
- await canvas().waitForXPath(
205
- '//*[contains(@class, "block-editor-warning")]/*[text()="Block has been deleted or is unavailable."]'
206
- );
207
-
208
- // This happens when the 404 is returned.
209
- expect( console ).toHaveErrored();
210
- } );
211
-
212
- it( 'should be able to insert a reusable block twice', async () => {
213
- await createReusableBlock(
214
- 'Awesome Paragraph',
215
- 'Duplicated reusable block'
216
- );
217
- await clearAllBlocks();
218
- await insertPattern( 'Duplicated reusable block' );
219
- await insertPattern( 'Duplicated reusable block' );
220
- await saveDraft();
221
- await page.reload();
222
- await page.waitForSelector( 'iframe[name="editor-canvas"]' );
223
-
224
- // Wait for the paragraph to be loaded.
225
- await canvas().waitForSelector(
226
- '.block-editor-block-list__block[data-type="core/paragraph"]'
227
- );
228
- // The first click selects the reusable block wrapper.
229
- // The second click selects the actual paragraph block.
230
- await canvas().click( '.wp-block-block' );
231
- await canvas().focus(
232
- '.block-editor-block-list__block[data-type="core/paragraph"]'
233
- );
234
- await pressKeyWithModifier( 'primary', 'a' );
235
- await page.keyboard.press( 'End' );
236
- await page.keyboard.type( ' modified' );
237
-
238
- // Wait for async mode to dispatch the update.
239
- // eslint-disable-next-line no-restricted-syntax
240
- await page.waitForTimeout( 1000 );
241
-
242
- // Check that the content of the second reusable block has been updated.
243
- const reusableBlocks = await page.$$( '.wp-block-block' );
244
- await Promise.all(
245
- reusableBlocks.map( async ( paragraph ) => {
246
- const content = await paragraph.$eval(
247
- 'p',
248
- ( element ) => element.textContent
249
- );
250
- expect( content ).toEqual( 'Awesome Paragraph modified' );
251
- } )
252
- );
253
- } );
254
-
255
- // Test for regressions of https://github.com/WordPress/gutenberg/issues/27243.
256
- it( 'should allow a block with styles to be converted to a reusable block', async () => {
257
- // Insert a quote and reload the page.
258
- insertBlock( 'Quote' );
259
- await saveDraft();
260
- await page.reload();
261
- await page.waitForSelector( 'iframe[name="editor-canvas"]' );
262
-
263
- // The quote block should have a visible preview in the sidebar for this test to be valid.
264
- const quoteBlock = await canvas().waitForSelector(
265
- '.block-editor-block-list__block[aria-label="Block: Quote"]'
266
- );
267
- // Select the quote block.
268
- await quoteBlock.focus();
269
- await openDocumentSettingsSidebar();
270
- await page.waitForXPath(
271
- '//*[@role="region"][@aria-label="Editor settings"]//button[.="Styles"]'
272
- );
273
-
274
- // Convert to reusable.
275
- await clickBlockToolbarButton( 'Options' );
276
- await clickMenuItem( 'Create pattern' );
277
- const nameInput = await page.waitForSelector(
278
- patternBlockNameInputSelector
279
- );
280
- await nameInput.click();
281
- await page.keyboard.type( 'Block with styles' );
282
- await page.waitForSelector( syncToggleSelectorChecked );
283
- await page.keyboard.press( 'Enter' );
284
- const reusableBlock = await canvas().waitForSelector(
285
- '.block-editor-block-list__block[aria-label="Block: Pattern"]'
286
- );
287
- expect( reusableBlock ).toBeTruthy();
288
- } );
289
- } );