@wordpress/e2e-tests 8.32.1-next.ff1cebbba.0 → 8.33.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
+ ## 8.33.0 (2025-10-17)
6
+
5
7
  ## 8.32.0 (2025-10-01)
6
8
 
7
9
  ## 8.31.0 (2025-09-17)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wordpress/e2e-tests",
3
- "version": "8.32.1-next.ff1cebbba.0",
3
+ "version": "8.33.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",
@@ -24,13 +24,13 @@
24
24
  "npm": ">=8.19.2"
25
25
  },
26
26
  "dependencies": {
27
- "@wordpress/e2e-test-utils": "^11.32.1-next.ff1cebbba.0",
28
- "@wordpress/interactivity": "^6.33.1-next.ff1cebbba.0",
29
- "@wordpress/interactivity-router": "^2.32.1-next.ff1cebbba.0",
30
- "@wordpress/jest-console": "^8.32.1-next.ff1cebbba.0",
31
- "@wordpress/jest-puppeteer-axe": "^7.32.1-next.ff1cebbba.0",
32
- "@wordpress/scripts": "^30.25.1-next.ff1cebbba.0",
33
- "@wordpress/url": "^4.32.1-next.ff1cebbba.0",
27
+ "@wordpress/e2e-test-utils": "^11.33.0",
28
+ "@wordpress/interactivity": "^6.33.0",
29
+ "@wordpress/interactivity-router": "^2.33.0",
30
+ "@wordpress/jest-console": "^8.33.0",
31
+ "@wordpress/jest-puppeteer-axe": "^7.33.0",
32
+ "@wordpress/scripts": "^30.26.0",
33
+ "@wordpress/url": "^4.33.0",
34
34
  "chalk": "^4.0.0",
35
35
  "expect-puppeteer": "^4.4.0",
36
36
  "filenamify": "^4.2.0",
@@ -47,5 +47,5 @@
47
47
  "publishConfig": {
48
48
  "access": "public"
49
49
  },
50
- "gitHead": "c5b659710aff01d40d2eb97b211eb2cec9c07d3b"
50
+ "gitHead": "2bbe0d34ab65458468758c48826296d7a753428b"
51
51
  }
@@ -13,6 +13,62 @@ wp_interactivity_state(
13
13
  )
14
14
  );
15
15
 
16
+ wp_interactivity_state(
17
+ 'test/deferred-store/derived-state',
18
+ array(
19
+ 'value' => function () {
20
+ $context = wp_interactivity_get_context( 'test/deferred-store/derived-state' );
21
+ return 'bind-' . $context['counter'];
22
+ },
23
+ 'below10' => function () {
24
+ $context = wp_interactivity_get_context( 'test/deferred-store/derived-state' );
25
+ return $context['counter'] < 10;
26
+ },
27
+ 'redOrGreen' => function () {
28
+ $context = wp_interactivity_get_context( 'test/deferred-store/derived-state' );
29
+ return $context['isReady'] ? 'rgb(0, 255, 0)' : 'rgb(255, 0, 0)';
30
+ },
31
+ 'derivedText' => function () {
32
+ $context = wp_interactivity_get_context( 'test/deferred-store/derived-state' );
33
+ return $context['isReady'] ? 'client-updated text' : 'server-rendered text';
34
+ },
35
+ 'radiotelephonicAlphabet' => function () {
36
+ $context = wp_interactivity_get_context( 'test/deferred-store/derived-state' );
37
+ $dictionary = array(
38
+ 'a' => 'alpha',
39
+ 'b' => 'bravo',
40
+ 'c' => 'charlie',
41
+ 'd' => 'delta',
42
+ );
43
+ return array_map(
44
+ function ( $item ) use ( $dictionary ) {
45
+ return $dictionary[ $item ];
46
+ },
47
+ $context['list']
48
+ );
49
+ },
50
+ )
51
+ );
52
+
53
+ add_filter(
54
+ 'script_module_data_@wordpress/interactivity',
55
+ function ( $data ) {
56
+ if ( ! isset( $data ) ) {
57
+ $data = array();
58
+ }
59
+ $data['derivedStateClosures'] = array(
60
+ 'test/deferred-store/derived-state' => array(
61
+ 'state.value',
62
+ 'state.below10',
63
+ 'state.redOrGreen',
64
+ 'state.derivedText',
65
+ 'state.radiotelephonicAlphabet',
66
+ ),
67
+ );
68
+ return $data;
69
+ }
70
+ );
71
+
16
72
  ?>
17
73
 
18
74
  <div
@@ -25,3 +81,68 @@ wp_interactivity_state(
25
81
  <span data-wp-text="state.number" data-testid="state-number"></span>
26
82
  <span data-wp-text="state.double" data-testid="state-double"></span>
27
83
  </div>
84
+
85
+ <div data-wp-interactive="test/deferred-store/derived-state">
86
+ <button data-wp-on--click="actions.load" data-testid="derived-state-load">load</button>
87
+ <span hidden data-wp-bind--hidden="!state.hydrated" data-testid="derived-state-hydrated">hydrated</span>
88
+ <span hidden data-wp-bind--hidden="!state.loaded" data-testid="derived-state-loaded">loaded</span>
89
+ </div>
90
+
91
+ <div data-wp-interactive="test/deferred-store/derived-state" data-wp-context='{"counter": 42}'>
92
+ <input
93
+ name="derived-bind"
94
+ type="text"
95
+ value="bind-42"
96
+ readonly
97
+ data-wp-bind--value="state.value"
98
+ data-testid="derived-bind-value"
99
+ >
100
+ <input
101
+ name="derived-bind-2"
102
+ type="text"
103
+ value="bind-42"
104
+ readonly
105
+ data-wp-bind--value="state.existingValue"
106
+ data-testid="derived-bind-value-2"
107
+ >
108
+ <button data-wp-on--click="actions.increment" data-testid="derived-bind-increment">+</button>
109
+ </div>
110
+
111
+ <div data-wp-interactive="test/deferred-store/derived-state" data-wp-context='{"counter": 9}'>
112
+ <output
113
+ class="below-10"
114
+ data-wp-class--below-10="state.below10"
115
+ data-wp-text="context.counter"
116
+ data-testid="derived-class-element"
117
+ >NaN</output>
118
+ <button data-wp-on--click="actions.increment" data-testid="derived-class-increment">+</button>
119
+ </div>
120
+
121
+ <div data-wp-interactive="test/deferred-store/derived-state" data-wp-context='{"isReady": false}'>
122
+ <output
123
+ style="color:red;"
124
+ data-wp-style--color="state.redOrGreen"
125
+ data-testid="derived-style-element"
126
+ >Style</output>
127
+ <button data-wp-on--click="actions.setReady" data-testid="derived-style-ready">Set ready</button>
128
+ </div>
129
+
130
+ <div data-wp-interactive="test/deferred-store/derived-state" data-wp-context='{"isReady": false}'>
131
+ <output
132
+ data-wp-text="state.derivedText"
133
+ data-testid="derived-text-element"
134
+ >server-rendered text</output>
135
+ <button data-wp-on--click="actions.setReady" data-testid="derived-text-update">Update</button>
136
+ </div>
137
+
138
+ <div data-wp-interactive="test/deferred-store/derived-state" data-wp-context='{"list": ["a", "b", "c"]}'>
139
+ <ol data-testid="derived-each-list">
140
+ <template data-wp-each="state.radiotelephonicAlphabet">
141
+ <li data-wp-text="context.item"></li>
142
+ </template>
143
+ <li data-wp-each-child="test/deferred-store/derived-state::state.radiotelephonicAlphabet" data-wp-text="context.item">alpha</li>
144
+ <li data-wp-each-child="test/deferred-store/derived-state::state.radiotelephonicAlphabet" data-wp-text="context.item">bravo</li>
145
+ <li data-wp-each-child="test/deferred-store/derived-state::state.radiotelephonicAlphabet" data-wp-text="context.item">charlie</li>
146
+ </ol>
147
+ <button data-wp-on--click="actions.addItem" data-testid="derived-each-additem">+</button>
148
+ </div>
@@ -36,3 +36,63 @@ window.addEventListener(
36
36
  },
37
37
  { once: true }
38
38
  );
39
+
40
+ const { state } = store( 'test/deferred-store/derived-state', {
41
+ state: {
42
+ hydrated: true,
43
+ get existingValue() {
44
+ return state.value;
45
+ },
46
+ },
47
+ actions: {
48
+ load() {
49
+ store( 'test/deferred-store/derived-state', {
50
+ state: {
51
+ loaded: true,
52
+ get value() {
53
+ const { counter } = getContext();
54
+ return `bind-${ counter }`;
55
+ },
56
+ get below10() {
57
+ const { counter } = getContext();
58
+ return counter < 10;
59
+ },
60
+ get redOrGreen() {
61
+ const { isReady } = getContext();
62
+ return isReady ? 'rgb(0, 255, 0)' : 'rgb(255, 0, 0)';
63
+ },
64
+ get derivedText() {
65
+ const { isReady } = getContext();
66
+ return isReady
67
+ ? 'client-updated text'
68
+ : 'server-rendered text';
69
+ },
70
+ get radiotelephonicAlphabet() {
71
+ const { list } = getContext();
72
+ const dictionary = {
73
+ a: 'alpha',
74
+ b: 'bravo',
75
+ c: 'charlie',
76
+ d: 'delta',
77
+ };
78
+ return list.map( ( item ) => dictionary[ item ] );
79
+ },
80
+ },
81
+ actions: {
82
+ increment() {
83
+ getContext().counter += 1;
84
+ },
85
+ setReady() {
86
+ getContext().isReady = true;
87
+ },
88
+ addItem() {
89
+ const { list } = getContext();
90
+ if ( list.length === 3 ) {
91
+ list.push( 'd' );
92
+ }
93
+ },
94
+ },
95
+ } );
96
+ },
97
+ },
98
+ } );
@@ -213,6 +213,38 @@
213
213
  <p data-testid="item" data-wp-each-child>banana</p>
214
214
  <p data-testid="item" data-wp-each-child>cherimoya</p>
215
215
  </div>
216
+
217
+ <div data-testid="item from derived state">
218
+ <button
219
+ data-testid="rotate" data-wp-on--click="actions.rotateBooks"
220
+ >Rotate</button>
221
+ <button
222
+ data-testid="add" data-wp-on--click="actions.addBook"
223
+ >Add</button>
224
+ <button
225
+ data-testid="replace" data-wp-on--click="actions.replaceBook"
226
+ >Replace</button>
227
+ <button
228
+ data-testid="modify" data-wp-on--click="actions.modifyBook"
229
+ >Modify</button>
230
+ <button
231
+ data-testid="replace-all" data-wp-on--click="actions.replaceAllBooks"
232
+ >Replace All</button>
233
+ <template
234
+ data-wp-each--book-item="state.books"
235
+ data-wp-each-key="state.bookItem.isbn"
236
+ >
237
+ <p
238
+ data-testid="item"
239
+ data-wp-text="state.bookItem.title"
240
+ data-wp-on--click="actions.removeBookUsingDerivedState"
241
+ ></p>
242
+ </template>
243
+ <!-- SSRed elements; they should be removed on hydration -->
244
+ <p data-testid="item" data-wp-each-child>A Game of Thrones</p>
245
+ <p data-testid="item" data-wp-each-child>A Clash of Kings</p>
246
+ <p data-testid="item" data-wp-each-child>A Storm of Swords</p>
247
+ </div>
216
248
  </div>
217
249
 
218
250
  <hr>
@@ -83,12 +83,19 @@ store( 'directive-each', {
83
83
  isbn: '9780553573428',
84
84
  },
85
85
  ],
86
+ get bookItem() {
87
+ return getContext().bookItem;
88
+ },
86
89
  },
87
90
  actions: {
88
91
  removeBook() {
89
92
  const { book } = getContext();
90
93
  state.books.splice( state.books.indexOf( book ), 1 );
91
94
  },
95
+ removeBookUsingDerivedState() {
96
+ const book = state.bookItem;
97
+ state.books.splice( state.books.indexOf( book ), 1 );
98
+ },
92
99
  rotateBooks() {
93
100
  const book = state.books.pop();
94
101
  state.books.splice( 0, 0, book );
@@ -113,6 +120,9 @@ store( 'directive-each', {
113
120
  const [ book ] = state.books;
114
121
  book.title = book.title.toUpperCase();
115
122
  },
123
+ replaceAllBooks() {
124
+ state.books = state.books.map( ( book ) => ( { ...book } ) );
125
+ },
116
126
  },
117
127
  } );
118
128
 
@@ -9,8 +9,9 @@
9
9
 
10
10
  $link1 = $attributes['links']['modified'];
11
11
  $link2 = $attributes['links']['newProps'];
12
- $parent_ctx = $attributes['parentContext'];
13
- $child_ctx = $attributes['childContext'];
12
+ $link3 = $attributes['links']['noContext'];
13
+ $parent_ctx = $attributes['parentContext'] ?? false;
14
+ $child_ctx = $attributes['childContext'] ?? false;
14
15
  ?>
15
16
 
16
17
  <nav
@@ -20,17 +21,29 @@ $child_ctx = $attributes['childContext'];
20
21
  >
21
22
  <a data-testid="modified" href="<?php echo esc_url( $link1 ); ?>">modified</a>
22
23
  <a data-testid="newProps" href="<?php echo esc_url( $link2 ); ?>">newProps</a>
24
+ <a data-testid="noContext" href="<?php echo esc_url( $link3 ); ?>">noContext</a>
23
25
  </nav>
24
26
 
25
27
  <div
26
28
  data-wp-interactive="test/get-server-context"
27
29
  data-wp-router-region="server-context"
28
30
  data-wp-watch="callbacks.updateServerContextParent"
29
- <?php echo wp_interactivity_data_wp_context( $parent_ctx ); ?>
31
+ <?php
32
+ if ( $parent_ctx ) {
33
+ echo wp_interactivity_data_wp_context( $parent_ctx );
34
+ }
35
+ ?>
30
36
  >
31
37
  <div
32
- data-wp-watch="callbacks.updateServerContextChild"
33
- <?php echo wp_interactivity_data_wp_context( $child_ctx ); ?>
38
+ data-wp-watch---child="callbacks.updateServerContextChild"
39
+ data-wp-watch---non-changing="callbacks.updateNonChanging"
40
+ data-wp-watch---only-in-main="callbacks.updateOnlyInMain"
41
+ data-wp-watch---only-in-modified="callbacks.updateOnlyInModified"
42
+ <?php
43
+ if ( $child_ctx ) {
44
+ echo wp_interactivity_data_wp_context( $child_ctx );
45
+ }
46
+ ?>
34
47
  >
35
48
  <div data-testid="prop" data-wp-text="context.prop"></div>
36
49
  <div data-testid="nested.prop" data-wp-text="context.nested.prop"></div>
@@ -38,6 +51,9 @@ $child_ctx = $attributes['childContext'];
38
51
  <div data-testid="nested.newProp" data-wp-text="context.nested.newProp"></div>
39
52
  <div data-testid="inherited.prop" data-wp-text="context.inherited.prop"></div>
40
53
  <div data-testid="inherited.newProp" data-wp-text="context.inherited.newProp"></div>
54
+ <div data-testid="nonChanging" data-wp-text="context.nonChanging"></div>
55
+ <div data-testid="onlyInMain" data-wp-text="context.onlyInMain"></div>
56
+ <div data-testid="onlyInModified" data-wp-text="context.onlyInModified"></div>
41
57
 
42
58
  <button
43
59
  data-testid="tryToModifyServerContext"
@@ -47,5 +63,12 @@ $child_ctx = $attributes['childContext'];
47
63
  >
48
64
  modify
49
65
  </button>
66
+
67
+ <button
68
+ data-testid="updateNonChanging"
69
+ data-wp-on--click="actions.updateNonChanging"
70
+ >
71
+ update non-changing prop
72
+ </button>
50
73
  </div>
51
74
  </div>
@@ -25,27 +25,64 @@ store( 'test/get-server-context', {
25
25
  getContext().result = 'not modified ✅';
26
26
  }
27
27
  },
28
+ updateNonChanging() {
29
+ getContext().nonChanging = 'modified from client';
30
+ },
28
31
  },
29
32
  callbacks: {
30
33
  updateServerContextParent() {
31
34
  const ctx = getContext();
32
35
  const { prop, newProp, nested, inherited } = getServerContext();
33
36
  ctx.prop = prop;
34
- ctx.newProp = newProp;
37
+ if ( newProp ) {
38
+ ctx.newProp = newProp;
39
+ }
35
40
  ctx.nested.prop = nested.prop;
36
- ctx.nested.newProp = nested.newProp;
41
+ if ( nested?.newProp ) {
42
+ ctx.nested.newProp = nested.newProp;
43
+ }
37
44
  ctx.inherited.prop = inherited.prop;
38
- ctx.inherited.newProp = inherited.newProp;
45
+ if ( inherited?.newProp ) {
46
+ ctx.inherited.newProp = inherited.newProp;
47
+ }
39
48
  },
40
49
  updateServerContextChild() {
41
50
  const ctx = getContext();
42
51
  const { prop, newProp, nested, inherited } = getServerContext();
43
52
  ctx.prop = prop;
44
- ctx.newProp = newProp;
53
+ if ( newProp ) {
54
+ ctx.newProp = newProp;
55
+ }
45
56
  ctx.nested.prop = nested.prop;
46
- ctx.nested.newProp = nested.newProp;
57
+ if ( nested?.newProp ) {
58
+ ctx.nested.newProp = nested.newProp;
59
+ }
47
60
  ctx.inherited.prop = inherited.prop;
48
- ctx.inherited.newProp = inherited.newProp;
61
+ if ( inherited?.newProp ) {
62
+ ctx.inherited.newProp = inherited.newProp;
63
+ }
64
+ },
65
+ updateNonChanging() {
66
+ // This property never changes in the server, but it changes in the
67
+ // client so every time there's a navigation, we need to overwrite
68
+ // it.
69
+ const ctx = getContext();
70
+ const { nonChanging } = getServerContext();
71
+ ctx.nonChanging = nonChanging;
72
+ },
73
+ updateOnlyInMain() {
74
+ // This property only exists in the main page, so we need to clear
75
+ // it when navigating to a page that doesn't have it.
76
+ const ctx = getContext();
77
+ const { onlyInMain } = getServerContext();
78
+ ctx.onlyInMain = onlyInMain;
79
+ },
80
+ updateOnlyInModified() {
81
+ // This property only exists in the modified page, so we need to clear
82
+ // it when navigating to a page that doesn't have it.
83
+ const ctx = getContext();
84
+ const { onlyInModified } = getServerContext();
85
+ ctx.onlyInModified = onlyInModified;
49
86
  },
50
87
  },
51
88
  } );
@@ -14,12 +14,18 @@ if ( isset( $attributes['state'] ) ) {
14
14
 
15
15
  <div
16
16
  data-wp-interactive="test/get-server-state"
17
- data-wp-watch="callbacks.updateState"
17
+ data-wp-watch---update-state="callbacks.updateState"
18
+ data-wp-watch---non-changing="callbacks.updateNonChanging"
19
+ data-wp-watch---only-in-main="callbacks.updateOnlyInMain"
20
+ data-wp-watch---only-in-link-1="callbacks.updateOnlyInLink1"
18
21
  >
19
22
  <div data-testid="prop" data-wp-text="state.prop"></div>
20
23
  <div data-testid="nested.prop" data-wp-text="state.nested.prop"></div>
21
24
  <div data-testid="newProp" data-wp-text="state.newProp"></div>
22
25
  <div data-testid="nested.newProp" data-wp-text="state.nested.newProp"></div>
26
+ <div data-testid="nonChanging" data-wp-text="state.nonChanging"></div>
27
+ <div data-testid="onlyInMain" data-wp-text="state.onlyInMain"></div>
28
+ <div data-testid="onlyInLink1" data-wp-text="state.onlyInLink1"></div>
23
29
 
24
30
  <button
25
31
  data-testid="tryToModifyServerState"
@@ -30,6 +36,12 @@ if ( isset( $attributes['state'] ) ) {
30
36
  modify
31
37
  </button>
32
38
 
39
+ <button
40
+ data-testid="updateNonChanging"
41
+ data-wp-on--click="actions.updateNonChanging"
42
+ >
43
+ update non-changing prop
44
+ </button>
33
45
 
34
46
  <nav>
35
47
  <?php
@@ -25,14 +25,40 @@ const { state } = store( 'test/get-server-state', {
25
25
  getContext().result = 'not modified ✅';
26
26
  }
27
27
  },
28
+ updateNonChanging() {
29
+ state.nonChanging = 'modified from client';
30
+ },
28
31
  },
29
32
  callbacks: {
30
33
  updateState() {
31
34
  const { prop, newProp, nested } = getServerState();
32
35
  state.prop = prop;
33
- state.newProp = newProp;
34
- state.nested.prop = nested.prop;
35
- state.nested.newProp = nested.newProp;
36
+ state.nested.prop = nested?.prop;
37
+ if ( newProp ) {
38
+ state.newProp = newProp;
39
+ }
40
+ if ( nested.newProp ) {
41
+ state.nested.newProp = nested?.newProp;
42
+ }
43
+ },
44
+ updateNonChanging() {
45
+ // This property never changes in the server, but it changes in the
46
+ // client so every time there's a navigation, we need to overwrite
47
+ // it.
48
+ const { nonChanging } = getServerState();
49
+ state.nonChanging = nonChanging;
50
+ },
51
+ updateOnlyInMain() {
52
+ // This property only exists in the main link, so we need to clear
53
+ // it when navigating to a page that doesn't have it.
54
+ const { onlyInMain } = getServerState();
55
+ state.onlyInMain = onlyInMain;
56
+ },
57
+ updateOnlyInLink1() {
58
+ // This property only exists in link 1, so we need to clear it when
59
+ // navigating to a page that doesn't have it.
60
+ const { onlyInLink1 } = getServerState();
61
+ state.onlyInLink1 = onlyInLink1;
36
62
  },
37
63
  },
38
64
  } );
@@ -84,6 +84,16 @@ $wrapper_attributes = get_block_wrapper_attributes();
84
84
  <?php echo $content; ?>
85
85
  </div>
86
86
 
87
+ <!-- Flag to check whether hydration has occurred. -->
88
+ <div
89
+ data-testid="hydrated"
90
+ data-wp-interactive="test/router-styles"
91
+ data-wp-bind--hidden="state.undefined"
92
+ hidden
93
+ >
94
+ Hydrated
95
+ </div>
96
+
87
97
  <!-- Text to check whether a navigation was client-side. -->
88
98
  <div
89
99
  data-testid="client-side navigation"
@@ -18,6 +18,18 @@ add_action(
18
18
  foreach ( $block_json_files as $block_json_file ) {
19
19
  register_block_type( $block_json_file );
20
20
  }
21
+
22
+ // Manually register script modules for navigation tests.
23
+ $test_router_script_modules = array(
24
+ 'test-router-script-modules-alpha-view-script-module',
25
+ 'test-router-script-modules-bravo-view-script-module',
26
+ 'test-router-script-modules-charlie-view-script-module',
27
+ 'test-router-script-modules-wrapper-view-script-module',
28
+ );
29
+
30
+ foreach ( $test_router_script_modules as $module_id ) {
31
+ gutenberg_register_interactive_script_module_id( $module_id );
32
+ }
21
33
  }
22
34
 
23
35
  /*