@wordpress/e2e-test-utils-playwright 1.46.0 → 1.48.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.
Files changed (295) hide show
  1. package/build-types/admin/index.d.ts +3 -0
  2. package/build-types/admin/index.d.ts.map +1 -1
  3. package/build-types/admin/visit-site-editor.d.ts.map +1 -1
  4. package/build-types/admin/wait-for-site-editor.d.ts +12 -0
  5. package/build-types/admin/wait-for-site-editor.d.ts.map +1 -0
  6. package/build-types/metrics/index.d.ts +22 -5
  7. package/build-types/metrics/index.d.ts.map +1 -1
  8. package/build-types/request-utils/index.d.ts +3 -1
  9. package/build-types/request-utils/index.d.ts.map +1 -1
  10. package/build-types/request-utils/preferences.d.ts +11 -0
  11. package/build-types/request-utils/preferences.d.ts.map +1 -1
  12. package/package.json +4 -5
  13. package/src/admin/create-new-post.ts +47 -0
  14. package/src/admin/edit-post.ts +24 -0
  15. package/{build/admin/get-page-error.js → src/admin/get-page-error.ts} +12 -9
  16. package/src/admin/index.ts +52 -0
  17. package/src/admin/visit-admin-page.ts +43 -0
  18. package/src/admin/visit-site-editor.ts +62 -0
  19. package/src/admin/wait-for-site-editor.ts +52 -0
  20. package/src/config.ts +12 -0
  21. package/src/editor/click-block-options-menu-item.ts +18 -0
  22. package/src/editor/click-block-toolbar-button.ts +21 -0
  23. package/src/editor/get-blocks.ts +75 -0
  24. package/src/editor/get-edited-post-content.ts +19 -0
  25. package/src/editor/index.ts +95 -0
  26. package/src/editor/insert-block.ts +54 -0
  27. package/src/editor/open-document-settings-sidebar.ts +31 -0
  28. package/src/editor/preview.ts +32 -0
  29. package/src/editor/publish-post.ts +51 -0
  30. package/src/editor/save-draft.ts +20 -0
  31. package/src/editor/select-blocks.ts +48 -0
  32. package/src/editor/set-content.ts +24 -0
  33. package/src/editor/set-is-fixed-toolbar.ts +20 -0
  34. package/src/editor/set-preferences.ts +37 -0
  35. package/src/editor/show-block-toolbar.ts +20 -0
  36. package/src/editor/site-editor.ts +55 -0
  37. package/src/editor/switch-editor-tool.ts +34 -0
  38. package/src/editor/switch-to-legacy-canvas.ts +20 -0
  39. package/src/editor/transform-block-to.ts +34 -0
  40. package/src/index.ts +8 -0
  41. package/src/lighthouse/index.ts +75 -0
  42. package/src/metrics/index.ts +441 -0
  43. package/src/page-utils/drag-files.ts +173 -0
  44. package/src/page-utils/emulate-network-conditions.ts +101 -0
  45. package/src/page-utils/index.ts +54 -0
  46. package/src/page-utils/is-current-url.ts +20 -0
  47. package/src/page-utils/keycodes.ts +48 -0
  48. package/src/page-utils/press-keys.ts +195 -0
  49. package/{build → src}/page-utils/set-browser-viewport.js +14 -12
  50. package/src/request-utils/blocks.ts +58 -0
  51. package/src/request-utils/comments.ts +80 -0
  52. package/src/request-utils/gutenberg-experiments.ts +62 -0
  53. package/src/request-utils/index.ts +222 -0
  54. package/src/request-utils/login.ts +34 -0
  55. package/src/request-utils/media.ts +102 -0
  56. package/src/request-utils/menus.ts +145 -0
  57. package/src/request-utils/pages.ts +75 -0
  58. package/src/request-utils/patterns.ts +31 -0
  59. package/src/request-utils/plugins.ts +103 -0
  60. package/src/request-utils/posts.ts +69 -0
  61. package/src/request-utils/preferences.ts +63 -0
  62. package/{build/request-utils/records.js → src/request-utils/records.ts} +15 -10
  63. package/src/request-utils/rest.ts +222 -0
  64. package/src/request-utils/site-settings.ts +58 -0
  65. package/src/request-utils/templates.ts +83 -0
  66. package/src/request-utils/themes.ts +110 -0
  67. package/src/request-utils/users.ts +125 -0
  68. package/src/request-utils/widgets.js +68 -0
  69. package/src/test.ts +204 -0
  70. package/src/types.ts +13 -0
  71. package/build/admin/create-new-post.cjs +0 -51
  72. package/build/admin/create-new-post.cjs.map +0 -7
  73. package/build/admin/create-new-post.js +0 -31
  74. package/build/admin/create-new-post.js.map +0 -1
  75. package/build/admin/edit-post.cjs +0 -40
  76. package/build/admin/edit-post.cjs.map +0 -7
  77. package/build/admin/edit-post.js +0 -20
  78. package/build/admin/edit-post.js.map +0 -1
  79. package/build/admin/get-page-error.cjs +0 -36
  80. package/build/admin/get-page-error.cjs.map +0 -7
  81. package/build/admin/get-page-error.js.map +0 -1
  82. package/build/admin/index.cjs +0 -59
  83. package/build/admin/index.cjs.map +0 -7
  84. package/build/admin/index.js +0 -37
  85. package/build/admin/index.js.map +0 -1
  86. package/build/admin/visit-admin-page.cjs +0 -47
  87. package/build/admin/visit-admin-page.cjs.map +0 -7
  88. package/build/admin/visit-admin-page.js +0 -32
  89. package/build/admin/visit-admin-page.js.map +0 -1
  90. package/build/admin/visit-site-editor.cjs +0 -90
  91. package/build/admin/visit-site-editor.cjs.map +0 -7
  92. package/build/admin/visit-site-editor.js +0 -73
  93. package/build/admin/visit-site-editor.js.map +0 -1
  94. package/build/config.cjs +0 -45
  95. package/build/config.cjs.map +0 -7
  96. package/build/config.js +0 -13
  97. package/build/config.js.map +0 -1
  98. package/build/editor/click-block-options-menu-item.cjs +0 -34
  99. package/build/editor/click-block-options-menu-item.cjs.map +0 -7
  100. package/build/editor/click-block-options-menu-item.js +0 -17
  101. package/build/editor/click-block-options-menu-item.js.map +0 -1
  102. package/build/editor/click-block-toolbar-button.cjs +0 -38
  103. package/build/editor/click-block-toolbar-button.cjs.map +0 -7
  104. package/build/editor/click-block-toolbar-button.js +0 -16
  105. package/build/editor/click-block-toolbar-button.js.map +0 -1
  106. package/build/editor/get-blocks.cjs +0 -64
  107. package/build/editor/get-blocks.cjs.map +0 -7
  108. package/build/editor/get-blocks.js +0 -45
  109. package/build/editor/get-blocks.js.map +0 -1
  110. package/build/editor/get-edited-post-content.cjs +0 -36
  111. package/build/editor/get-edited-post-content.cjs.map +0 -7
  112. package/build/editor/get-edited-post-content.js +0 -15
  113. package/build/editor/get-edited-post-content.js.map +0 -1
  114. package/build/editor/index.cjs +0 -97
  115. package/build/editor/index.cjs.map +0 -7
  116. package/build/editor/index.js +0 -75
  117. package/build/editor/index.js.map +0 -1
  118. package/build/editor/insert-block.cjs +0 -55
  119. package/build/editor/insert-block.cjs.map +0 -7
  120. package/build/editor/insert-block.js +0 -23
  121. package/build/editor/insert-block.js.map +0 -1
  122. package/build/editor/open-document-settings-sidebar.cjs +0 -42
  123. package/build/editor/open-document-settings-sidebar.cjs.map +0 -7
  124. package/build/editor/open-document-settings-sidebar.js +0 -27
  125. package/build/editor/open-document-settings-sidebar.js.map +0 -1
  126. package/build/editor/preview.cjs +0 -42
  127. package/build/editor/preview.cjs.map +0 -7
  128. package/build/editor/preview.js +0 -21
  129. package/build/editor/preview.js.map +0 -1
  130. package/build/editor/publish-post.cjs +0 -47
  131. package/build/editor/publish-post.cjs.map +0 -7
  132. package/build/editor/publish-post.js +0 -45
  133. package/build/editor/publish-post.js.map +0 -1
  134. package/build/editor/save-draft.cjs +0 -34
  135. package/build/editor/save-draft.cjs.map +0 -7
  136. package/build/editor/save-draft.js +0 -18
  137. package/build/editor/save-draft.js.map +0 -1
  138. package/build/editor/select-blocks.cjs +0 -51
  139. package/build/editor/select-blocks.cjs.map +0 -7
  140. package/build/editor/select-blocks.js +0 -28
  141. package/build/editor/select-blocks.js.map +0 -1
  142. package/build/editor/set-content.cjs +0 -39
  143. package/build/editor/set-content.cjs.map +0 -7
  144. package/build/editor/set-content.js +0 -17
  145. package/build/editor/set-content.js.map +0 -1
  146. package/build/editor/set-is-fixed-toolbar.cjs +0 -36
  147. package/build/editor/set-is-fixed-toolbar.cjs.map +0 -7
  148. package/build/editor/set-is-fixed-toolbar.js +0 -18
  149. package/build/editor/set-is-fixed-toolbar.js.map +0 -1
  150. package/build/editor/set-preferences.cjs +0 -43
  151. package/build/editor/set-preferences.cjs.map +0 -7
  152. package/build/editor/set-preferences.js +0 -21
  153. package/build/editor/set-preferences.js.map +0 -1
  154. package/build/editor/show-block-toolbar.cjs +0 -35
  155. package/build/editor/show-block-toolbar.cjs.map +0 -7
  156. package/build/editor/show-block-toolbar.js +0 -19
  157. package/build/editor/show-block-toolbar.js.map +0 -1
  158. package/build/editor/site-editor.cjs +0 -51
  159. package/build/editor/site-editor.cjs.map +0 -7
  160. package/build/editor/site-editor.js +0 -44
  161. package/build/editor/site-editor.js.map +0 -1
  162. package/build/editor/switch-editor-tool.cjs +0 -47
  163. package/build/editor/switch-editor-tool.cjs.map +0 -7
  164. package/build/editor/switch-editor-tool.js +0 -33
  165. package/build/editor/switch-editor-tool.js.map +0 -1
  166. package/build/editor/switch-to-legacy-canvas.cjs +0 -39
  167. package/build/editor/switch-to-legacy-canvas.cjs.map +0 -7
  168. package/build/editor/switch-to-legacy-canvas.js +0 -18
  169. package/build/editor/switch-to-legacy-canvas.js.map +0 -1
  170. package/build/editor/transform-block-to.cjs +0 -46
  171. package/build/editor/transform-block-to.cjs.map +0 -7
  172. package/build/editor/transform-block-to.js +0 -24
  173. package/build/editor/transform-block-to.js.map +0 -1
  174. package/build/index.cjs +0 -51
  175. package/build/index.cjs.map +0 -7
  176. package/build/index.js +0 -19
  177. package/build/index.js.map +0 -1
  178. package/build/lighthouse/index.cjs +0 -96
  179. package/build/lighthouse/index.cjs.map +0 -7
  180. package/build/lighthouse/index.js +0 -61
  181. package/build/lighthouse/index.js.map +0 -1
  182. package/build/metrics/index.cjs +0 -309
  183. package/build/metrics/index.cjs.map +0 -7
  184. package/build/metrics/index.js +0 -258
  185. package/build/metrics/index.js.map +0 -1
  186. package/build/page-utils/drag-files.cjs +0 -143
  187. package/build/page-utils/drag-files.cjs.map +0 -7
  188. package/build/page-utils/drag-files.js +0 -119
  189. package/build/page-utils/drag-files.js.map +0 -1
  190. package/build/page-utils/emulate-network-conditions.cjs +0 -99
  191. package/build/page-utils/emulate-network-conditions.cjs.map +0 -7
  192. package/build/page-utils/emulate-network-conditions.js +0 -71
  193. package/build/page-utils/emulate-network-conditions.js.map +0 -1
  194. package/build/page-utils/index.cjs +0 -59
  195. package/build/page-utils/index.cjs.map +0 -7
  196. package/build/page-utils/index.js +0 -37
  197. package/build/page-utils/index.js.map +0 -1
  198. package/build/page-utils/is-current-url.cjs +0 -36
  199. package/build/page-utils/is-current-url.cjs.map +0 -7
  200. package/build/page-utils/is-current-url.js +0 -21
  201. package/build/page-utils/is-current-url.js.map +0 -1
  202. package/build/page-utils/keycodes.cjs +0 -55
  203. package/build/page-utils/keycodes.cjs.map +0 -7
  204. package/build/page-utils/keycodes.js +0 -41
  205. package/build/page-utils/keycodes.js.map +0 -1
  206. package/build/page-utils/press-keys.cjs +0 -165
  207. package/build/page-utils/press-keys.cjs.map +0 -7
  208. package/build/page-utils/press-keys.js +0 -141
  209. package/build/page-utils/press-keys.js.map +0 -1
  210. package/build/page-utils/set-browser-viewport.cjs +0 -39
  211. package/build/page-utils/set-browser-viewport.cjs.map +0 -7
  212. package/build/page-utils/set-browser-viewport.js.map +0 -1
  213. package/build/request-utils/blocks.cjs +0 -51
  214. package/build/request-utils/blocks.cjs.map +0 -7
  215. package/build/request-utils/blocks.js +0 -40
  216. package/build/request-utils/blocks.js.map +0 -1
  217. package/build/request-utils/comments.cjs +0 -66
  218. package/build/request-utils/comments.cjs.map +0 -7
  219. package/build/request-utils/comments.js +0 -52
  220. package/build/request-utils/comments.js.map +0 -1
  221. package/build/request-utils/gutenberg-experiments.cjs +0 -56
  222. package/build/request-utils/gutenberg-experiments.cjs.map +0 -7
  223. package/build/request-utils/gutenberg-experiments.js +0 -47
  224. package/build/request-utils/gutenberg-experiments.js.map +0 -1
  225. package/build/request-utils/index.cjs +0 -199
  226. package/build/request-utils/index.cjs.map +0 -7
  227. package/build/request-utils/index.js +0 -194
  228. package/build/request-utils/index.js.map +0 -1
  229. package/build/request-utils/login.cjs +0 -48
  230. package/build/request-utils/login.cjs.map +0 -7
  231. package/build/request-utils/login.js +0 -21
  232. package/build/request-utils/login.js.map +0 -1
  233. package/build/request-utils/media.cjs +0 -83
  234. package/build/request-utils/media.cjs.map +0 -7
  235. package/build/request-utils/media.js +0 -106
  236. package/build/request-utils/media.js.map +0 -1
  237. package/build/request-utils/menus.cjs +0 -128
  238. package/build/request-utils/menus.cjs.map +0 -7
  239. package/build/request-utils/menus.js +0 -116
  240. package/build/request-utils/menus.js.map +0 -1
  241. package/build/request-utils/pages.cjs +0 -66
  242. package/build/request-utils/pages.cjs.map +0 -7
  243. package/build/request-utils/pages.js +0 -53
  244. package/build/request-utils/pages.js.map +0 -1
  245. package/build/request-utils/patterns.cjs +0 -44
  246. package/build/request-utils/patterns.cjs.map +0 -7
  247. package/build/request-utils/patterns.js +0 -27
  248. package/build/request-utils/patterns.js.map +0 -1
  249. package/build/request-utils/plugins.cjs +0 -81
  250. package/build/request-utils/plugins.cjs.map +0 -7
  251. package/build/request-utils/plugins.js +0 -85
  252. package/build/request-utils/plugins.js.map +0 -1
  253. package/build/request-utils/posts.cjs +0 -56
  254. package/build/request-utils/posts.cjs.map +0 -7
  255. package/build/request-utils/posts.js +0 -42
  256. package/build/request-utils/posts.js.map +0 -1
  257. package/build/request-utils/preferences.cjs +0 -41
  258. package/build/request-utils/preferences.cjs.map +0 -7
  259. package/build/request-utils/preferences.js +0 -20
  260. package/build/request-utils/preferences.js.map +0 -1
  261. package/build/request-utils/records.cjs +0 -37
  262. package/build/request-utils/records.cjs.map +0 -7
  263. package/build/request-utils/records.js.map +0 -1
  264. package/build/request-utils/rest.cjs +0 -174
  265. package/build/request-utils/rest.cjs.map +0 -7
  266. package/build/request-utils/rest.js +0 -181
  267. package/build/request-utils/rest.js.map +0 -1
  268. package/build/request-utils/site-settings.cjs +0 -45
  269. package/build/request-utils/site-settings.cjs.map +0 -7
  270. package/build/request-utils/site-settings.js +0 -33
  271. package/build/request-utils/site-settings.js.map +0 -1
  272. package/build/request-utils/templates.cjs +0 -68
  273. package/build/request-utils/templates.cjs.map +0 -7
  274. package/build/request-utils/templates.js +0 -54
  275. package/build/request-utils/templates.js.map +0 -1
  276. package/build/request-utils/themes.cjs +0 -92
  277. package/build/request-utils/themes.cjs.map +0 -7
  278. package/build/request-utils/themes.js +0 -69
  279. package/build/request-utils/themes.js.map +0 -1
  280. package/build/request-utils/users.cjs +0 -83
  281. package/build/request-utils/users.cjs.map +0 -7
  282. package/build/request-utils/users.js +0 -81
  283. package/build/request-utils/users.js.map +0 -1
  284. package/build/request-utils/widgets.cjs +0 -79
  285. package/build/request-utils/widgets.cjs.map +0 -7
  286. package/build/request-utils/widgets.js +0 -61
  287. package/build/request-utils/widgets.js.map +0 -1
  288. package/build/test.cjs +0 -136
  289. package/build/test.cjs.map +0 -7
  290. package/build/test.js +0 -189
  291. package/build/test.js.map +0 -1
  292. package/build/types.cjs +0 -19
  293. package/build/types.cjs.map +0 -7
  294. package/build/types.js +0 -3
  295. package/build/types.js.map +0 -1
@@ -0,0 +1,125 @@
1
+ /**
2
+ * Internal dependencies
3
+ */
4
+ import type { RequestUtils } from './index';
5
+
6
+ export interface User {
7
+ id: number;
8
+ name: string;
9
+ email: string;
10
+ }
11
+
12
+ export interface UserData {
13
+ username: string;
14
+ email: string;
15
+ firstName?: string;
16
+ lastName?: string;
17
+ password?: string;
18
+ roles?: string[];
19
+ }
20
+
21
+ export interface UserRequestData {
22
+ username: string;
23
+ email: string;
24
+ first_name?: string;
25
+ last_name?: string;
26
+ password?: string;
27
+ roles?: string[];
28
+ }
29
+
30
+ /**
31
+ * List all users.
32
+ *
33
+ * @see https://developer.wordpress.org/rest-api/reference/users/#list-users
34
+ * @param this
35
+ */
36
+ async function listUsers( this: RequestUtils ) {
37
+ const response = await this.rest< User[] >( {
38
+ method: 'GET',
39
+ path: '/wp/v2/users',
40
+ params: {
41
+ per_page: 100,
42
+ },
43
+ } );
44
+
45
+ return response;
46
+ }
47
+
48
+ /**
49
+ * Add a test user.
50
+ *
51
+ * @see https://developer.wordpress.org/rest-api/reference/users/#create-a-user
52
+ * @param this
53
+ * @param user User data to create.
54
+ */
55
+ async function createUser( this: RequestUtils, user: UserData ) {
56
+ const userData: UserRequestData = {
57
+ username: user.username,
58
+ email: user.email,
59
+ };
60
+
61
+ if ( user.firstName ) {
62
+ userData.first_name = user.firstName;
63
+ }
64
+
65
+ if ( user.lastName ) {
66
+ userData.last_name = user.lastName;
67
+ }
68
+
69
+ if ( user.password ) {
70
+ userData.password = user.password;
71
+ }
72
+
73
+ if ( user.roles ) {
74
+ userData.roles = user.roles;
75
+ }
76
+
77
+ const response = await this.rest< User >( {
78
+ method: 'POST',
79
+ path: '/wp/v2/users',
80
+ data: userData,
81
+ } );
82
+
83
+ return response;
84
+ }
85
+
86
+ /**
87
+ * Delete a user.
88
+ *
89
+ * @see https://developer.wordpress.org/rest-api/reference/users/#delete-a-user
90
+ * @param this
91
+ * @param userId The ID of the user.
92
+ */
93
+ async function deleteUser( this: RequestUtils, userId: number ) {
94
+ const response = await this.rest( {
95
+ method: 'DELETE',
96
+ path: `/wp/v2/users/${ userId }`,
97
+ params: { force: true, reassign: 1 },
98
+ } );
99
+
100
+ return response;
101
+ }
102
+
103
+ /**
104
+ * Delete all users except main root user.
105
+ *
106
+ * @param this
107
+ */
108
+ async function deleteAllUsers( this: RequestUtils ) {
109
+ const users = await listUsers.bind( this )();
110
+
111
+ // The users endpoint doesn't support batch request yet.
112
+ const responses = await Promise.all(
113
+ users
114
+ // Do not delete neither root user nor the current user.
115
+ .filter(
116
+ ( user: User ) =>
117
+ user.id !== 1 && user.name !== this.user.username
118
+ )
119
+ .map( ( user: User ) => deleteUser.bind( this )( user.id ) )
120
+ );
121
+
122
+ return responses;
123
+ }
124
+
125
+ export { createUser, deleteAllUsers };
@@ -0,0 +1,68 @@
1
+ /**
2
+ * Delete all the widgets in the widgets screen.
3
+ *
4
+ * @this {import('./index').RequestUtils}
5
+ */
6
+ export async function deleteAllWidgets() {
7
+ const [ widgets, sidebars ] = await Promise.all( [
8
+ this.rest( { path: '/wp/v2/widgets' } ),
9
+ this.rest( { path: '/wp/v2/sidebars' } ),
10
+ ] );
11
+
12
+ await this.batchRest(
13
+ widgets.map( ( widget ) => ( {
14
+ method: 'DELETE',
15
+ path: `/wp/v2/widgets/${ widget.id }?force=true`,
16
+ } ) )
17
+ );
18
+
19
+ // The endpoint doesn't support batch requests yet.
20
+ await Promise.all(
21
+ sidebars.map( ( sidebar ) =>
22
+ this.rest( {
23
+ method: 'POST',
24
+ path: `/wp/v2/sidebars/${ sidebar.id }`,
25
+ data: { id: sidebar.id, widgets: [] },
26
+ } )
27
+ )
28
+ );
29
+ }
30
+
31
+ /**
32
+ * Add a widget block to the widget area.
33
+ *
34
+ * @this {import('./index').RequestUtils}
35
+ * @param {string} serializedBlock The serialized content of the inserted block HTML.
36
+ * @param {string} widgetAreaId The ID of the widget area.
37
+ */
38
+ export async function addWidgetBlock( serializedBlock, widgetAreaId ) {
39
+ const { id: blockId } = await this.rest( {
40
+ method: 'POST',
41
+ path: '/wp/v2/widgets',
42
+ data: {
43
+ id_base: 'block',
44
+ sidebar: widgetAreaId,
45
+ instance: {
46
+ raw: { content: serializedBlock },
47
+ },
48
+ },
49
+ } );
50
+
51
+ const { widgets } = await this.rest( {
52
+ path: `/wp/v2/sidebars/${ widgetAreaId }`,
53
+ } );
54
+
55
+ const updatedWidgets = new Set( widgets );
56
+ // Remove duplicate.
57
+ updatedWidgets.delete( blockId );
58
+ // Add to last block.
59
+ updatedWidgets.add( blockId );
60
+
61
+ await this.rest( {
62
+ method: 'PUT',
63
+ path: `/wp/v2/sidebars/${ widgetAreaId }`,
64
+ data: {
65
+ widgets: [ ...updatedWidgets ],
66
+ },
67
+ } );
68
+ }
package/src/test.ts ADDED
@@ -0,0 +1,204 @@
1
+ // Playwright fixtures use `use()` which is not a React hook.
2
+
3
+ /**
4
+ * External dependencies
5
+ */
6
+ import * as path from 'path';
7
+ import { test as base, expect, chromium } from '@playwright/test';
8
+ import type { ConsoleMessage } from '@playwright/test';
9
+ import getPort from 'get-port';
10
+
11
+ /**
12
+ * Internal dependencies
13
+ */
14
+ import {
15
+ Admin,
16
+ Editor,
17
+ PageUtils,
18
+ RequestUtils,
19
+ Metrics,
20
+ Lighthouse,
21
+ } from './index';
22
+
23
+ const STORAGE_STATE_PATH =
24
+ process.env.STORAGE_STATE_PATH ||
25
+ path.join( process.cwd(), 'artifacts/storage-states/admin.json' );
26
+
27
+ /**
28
+ * Set of console logging types observed to protect against unexpected yet
29
+ * handled (i.e. not catastrophic) errors or warnings. Each key corresponds
30
+ * to the Playwright ConsoleMessage type, its value the corresponding function
31
+ * on the console global object.
32
+ */
33
+ const OBSERVED_CONSOLE_MESSAGE_TYPES = [ 'warn', 'error' ] as const;
34
+
35
+ /**
36
+ * Adds a page event handler to emit uncaught exception to process if one of
37
+ * the observed console logging types is encountered.
38
+ *
39
+ * @param message The console message.
40
+ */
41
+ function observeConsoleLogging( message: ConsoleMessage ) {
42
+ const type = message.type();
43
+ if (
44
+ ! OBSERVED_CONSOLE_MESSAGE_TYPES.includes(
45
+ type as ( typeof OBSERVED_CONSOLE_MESSAGE_TYPES )[ number ]
46
+ )
47
+ ) {
48
+ return;
49
+ }
50
+
51
+ const text = message.text();
52
+
53
+ // An exception is made for _blanket_ deprecation warnings: Those
54
+ // which log regardless of whether a deprecated feature is in use.
55
+ if ( text.includes( 'This is a global warning' ) ) {
56
+ return;
57
+ }
58
+
59
+ // A chrome advisory warning about SameSite cookies is informational
60
+ // about future changes, tracked separately for improvement in core.
61
+ //
62
+ // See: https://core.trac.wordpress.org/ticket/37000
63
+ // See: https://www.chromestatus.com/feature/5088147346030592
64
+ // See: https://www.chromestatus.com/feature/5633521622188032
65
+ if (
66
+ text.includes( 'A cookie associated with a cross-site resource' ) ||
67
+ text.includes(
68
+ 'https://developer.mozilla.org/docs/Web/HTTP/Headers/Set-Cookie/SameSite'
69
+ )
70
+ ) {
71
+ return;
72
+ }
73
+
74
+ // Viewing posts on the front end can result in this error, which
75
+ // has nothing to do with Gutenberg.
76
+ if ( text.includes( 'net::ERR_UNKNOWN_URL_SCHEME' ) ) {
77
+ return;
78
+ }
79
+
80
+ // TODO: Not implemented yet.
81
+ // Network errors are ignored only if we are intentionally testing
82
+ // offline mode.
83
+ // if (
84
+ // text.includes( 'net::ERR_INTERNET_DISCONNECTED' ) &&
85
+ // isOfflineMode()
86
+ // ) {
87
+ // return;
88
+ // }
89
+
90
+ // As of WordPress 5.3.2 in Chrome 79, navigating to the block editor
91
+ // (Posts > Add New) will display a console warning about
92
+ // non - unique IDs.
93
+ // See: https://core.trac.wordpress.org/ticket/23165
94
+ if ( text.includes( 'elements with non-unique id #_wpnonce' ) ) {
95
+ return;
96
+ }
97
+
98
+ // Ignore all JQMIGRATE (jQuery migrate) deprecation warnings.
99
+ if ( text.includes( 'JQMIGRATE' ) ) {
100
+ return;
101
+ }
102
+
103
+ // https://bugzilla.mozilla.org/show_bug.cgi?id=1404468
104
+ if (
105
+ text.includes( 'Layout was forced before the page was fully loaded' )
106
+ ) {
107
+ return;
108
+ }
109
+
110
+ // Deprecated warnings coming from the third-party libraries.
111
+ if ( text.includes( 'MouseEvent.moz' ) ) {
112
+ return;
113
+ }
114
+
115
+ const logFunction =
116
+ type as ( typeof OBSERVED_CONSOLE_MESSAGE_TYPES )[ number ];
117
+
118
+ // Disable reason: We intentionally bubble up the console message
119
+ // which, unless the test explicitly anticipates the logging via
120
+ // @wordpress/jest-console matchers, will cause the intended test
121
+ // failure.
122
+ // eslint-disable-next-line no-console
123
+ console[ logFunction ]( text );
124
+ }
125
+
126
+ const test = base.extend<
127
+ {
128
+ admin: Admin;
129
+ editor: Editor;
130
+ pageUtils: PageUtils;
131
+ snapshotConfig: void;
132
+ metrics: Metrics;
133
+ lighthouse: Lighthouse;
134
+ },
135
+ {
136
+ requestUtils: RequestUtils;
137
+ lighthousePort: number;
138
+ }
139
+ >( {
140
+ admin: async ( { page, pageUtils, editor }, use ) => {
141
+ await use( new Admin( { page, pageUtils, editor } ) );
142
+ },
143
+ editor: async ( { page }, use ) => {
144
+ await use( new Editor( { page } ) );
145
+ },
146
+ page: async ( { page }, use ) => {
147
+ page.on( 'console', observeConsoleLogging );
148
+
149
+ await use( page );
150
+
151
+ // Clear local storage after each test.
152
+ // This needs to be wrapped with a try/catch because it can fail when
153
+ // the test is skipped (e.g. via fixme).
154
+ try {
155
+ await page.evaluate( () => {
156
+ window.localStorage.clear();
157
+ } );
158
+ } catch {
159
+ // noop.
160
+ }
161
+
162
+ await page.close();
163
+ },
164
+ pageUtils: async ( { page, browserName }, use ) => {
165
+ await use( new PageUtils( { page, browserName } ) );
166
+ },
167
+ requestUtils: [
168
+ async ( {}, use, workerInfo ) => {
169
+ const requestUtils = await RequestUtils.setup( {
170
+ baseURL: workerInfo.project.use.baseURL,
171
+ storageStatePath: STORAGE_STATE_PATH,
172
+ } );
173
+
174
+ await use( requestUtils );
175
+ },
176
+ { scope: 'worker', auto: true },
177
+ ],
178
+ // Spins up a new browser for use by the Lighthouse fixture
179
+ // so that Lighthouse can connect to the debugging port.
180
+ // As a worker-scoped fixture, this will only launch 1
181
+ // instance for the whole test worker, so multiple tests
182
+ // will share the same instance with the same port.
183
+ lighthousePort: [
184
+ async ( {}, use ) => {
185
+ const port = await getPort();
186
+ const browser = await chromium.launch( {
187
+ args: [ `--remote-debugging-port=${ port }` ],
188
+ } );
189
+
190
+ await use( port );
191
+
192
+ await browser.close();
193
+ },
194
+ { scope: 'worker' },
195
+ ],
196
+ lighthouse: async ( { page, lighthousePort }, use ) => {
197
+ await use( new Lighthouse( { page, port: lighthousePort } ) );
198
+ },
199
+ metrics: async ( { page }, use ) => {
200
+ await use( new Metrics( { page } ) );
201
+ },
202
+ } );
203
+
204
+ export { test, expect };
package/src/types.ts ADDED
@@ -0,0 +1,13 @@
1
+ /**
2
+ * External dependencies
3
+ */
4
+ declare global {
5
+ interface Window {
6
+ // Silence the warning for `window.wp` in Playwright's evaluate functions.
7
+ wp: any;
8
+ // Helper function added by Metrics fixture for web-vitals.js.
9
+ __reportVitals__: ( data: string ) => void;
10
+ }
11
+ }
12
+
13
+ export {};
@@ -1,51 +0,0 @@
1
- "use strict";
2
- var __defProp = Object.defineProperty;
3
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
- var __getOwnPropNames = Object.getOwnPropertyNames;
5
- var __hasOwnProp = Object.prototype.hasOwnProperty;
6
- var __export = (target, all) => {
7
- for (var name in all)
8
- __defProp(target, name, { get: all[name], enumerable: true });
9
- };
10
- var __copyProps = (to, from, except, desc) => {
11
- if (from && typeof from === "object" || typeof from === "function") {
12
- for (let key of __getOwnPropNames(from))
13
- if (!__hasOwnProp.call(to, key) && key !== except)
14
- __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
- }
16
- return to;
17
- };
18
- var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
-
20
- // packages/e2e-test-utils-playwright/src/admin/create-new-post.ts
21
- var create_new_post_exports = {};
22
- __export(create_new_post_exports, {
23
- createNewPost: () => createNewPost
24
- });
25
- module.exports = __toCommonJS(create_new_post_exports);
26
- async function createNewPost(options = {}) {
27
- const query = new URLSearchParams();
28
- const { postType, title, content, excerpt } = options;
29
- if (postType) {
30
- query.set("post_type", postType);
31
- }
32
- if (title) {
33
- query.set("post_title", title);
34
- }
35
- if (content) {
36
- query.set("content", content);
37
- }
38
- if (excerpt) {
39
- query.set("excerpt", excerpt);
40
- }
41
- await this.visitAdminPage("post-new.php", query.toString());
42
- await this.editor.setPreferences("core/edit-post", {
43
- welcomeGuide: options.showWelcomeGuide ?? false,
44
- fullscreenMode: options.fullscreenMode ?? false
45
- });
46
- }
47
- // Annotate the CommonJS export names for ESM import in node:
48
- 0 && (module.exports = {
49
- createNewPost
50
- });
51
- //# sourceMappingURL=create-new-post.cjs.map
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../src/admin/create-new-post.ts"],
4
- "sourcesContent": ["/**\n * Internal dependencies\n */\nimport type { Admin } from './';\n\ninterface NewPostOptions {\n\tpostType?: string;\n\ttitle?: string;\n\tcontent?: string;\n\texcerpt?: string;\n\tshowWelcomeGuide?: boolean;\n\tfullscreenMode?: boolean;\n}\n\n/**\n * Creates new post.\n *\n * @param this\n * @param options Options to create new post.\n */\nexport async function createNewPost(\n\tthis: Admin,\n\toptions: NewPostOptions = {}\n) {\n\tconst query = new URLSearchParams();\n\tconst { postType, title, content, excerpt } = options;\n\n\tif ( postType ) {\n\t\tquery.set( 'post_type', postType );\n\t}\n\tif ( title ) {\n\t\tquery.set( 'post_title', title );\n\t}\n\tif ( content ) {\n\t\tquery.set( 'content', content );\n\t}\n\tif ( excerpt ) {\n\t\tquery.set( 'excerpt', excerpt );\n\t}\n\n\tawait this.visitAdminPage( 'post-new.php', query.toString() );\n\n\tawait this.editor.setPreferences( 'core/edit-post', {\n\t\twelcomeGuide: options.showWelcomeGuide ?? false,\n\t\tfullscreenMode: options.fullscreenMode ?? false,\n\t} );\n}\n"],
5
- "mappings": ";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAoBA,eAAsB,cAErB,UAA0B,CAAC,GAC1B;AACD,QAAM,QAAQ,IAAI,gBAAgB;AAClC,QAAM,EAAE,UAAU,OAAO,SAAS,QAAQ,IAAI;AAE9C,MAAK,UAAW;AACf,UAAM,IAAK,aAAa,QAAS;AAAA,EAClC;AACA,MAAK,OAAQ;AACZ,UAAM,IAAK,cAAc,KAAM;AAAA,EAChC;AACA,MAAK,SAAU;AACd,UAAM,IAAK,WAAW,OAAQ;AAAA,EAC/B;AACA,MAAK,SAAU;AACd,UAAM,IAAK,WAAW,OAAQ;AAAA,EAC/B;AAEA,QAAM,KAAK,eAAgB,gBAAgB,MAAM,SAAS,CAAE;AAE5D,QAAM,KAAK,OAAO,eAAgB,kBAAkB;AAAA,IACnD,cAAc,QAAQ,oBAAoB;AAAA,IAC1C,gBAAgB,QAAQ,kBAAkB;AAAA,EAC3C,CAAE;AACH;",
6
- "names": []
7
- }
@@ -1,31 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.createNewPost = createNewPost;
4
- /**
5
- * Creates new post.
6
- *
7
- * @param this
8
- * @param options Options to create new post.
9
- */
10
- async function createNewPost(options = {}) {
11
- const query = new URLSearchParams();
12
- const { postType, title, content, excerpt } = options;
13
- if (postType) {
14
- query.set('post_type', postType);
15
- }
16
- if (title) {
17
- query.set('post_title', title);
18
- }
19
- if (content) {
20
- query.set('content', content);
21
- }
22
- if (excerpt) {
23
- query.set('excerpt', excerpt);
24
- }
25
- await this.visitAdminPage('post-new.php', query.toString());
26
- await this.editor.setPreferences('core/edit-post', {
27
- welcomeGuide: options.showWelcomeGuide ?? false,
28
- fullscreenMode: options.fullscreenMode ?? false,
29
- });
30
- }
31
- //# sourceMappingURL=create-new-post.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"create-new-post.js","sourceRoot":"","sources":["../../src/admin/create-new-post.ts"],"names":[],"mappings":";;;AAcA;;;;;GAKG;AACI,KAAK,wBAEX,OAAO,GAAmB,EAAE;IAE5B,MAAM,KAAK,GAAG,IAAI,eAAe,EAAE,CAAC;IACpC,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC;IAEtD,IAAK,QAAQ,EAAG,CAAC;QAChB,KAAK,CAAC,GAAG,CAAE,WAAW,EAAE,QAAQ,CAAE,CAAC;IACpC,CAAC;IACD,IAAK,KAAK,EAAG,CAAC;QACb,KAAK,CAAC,GAAG,CAAE,YAAY,EAAE,KAAK,CAAE,CAAC;IAClC,CAAC;IACD,IAAK,OAAO,EAAG,CAAC;QACf,KAAK,CAAC,GAAG,CAAE,SAAS,EAAE,OAAO,CAAE,CAAC;IACjC,CAAC;IACD,IAAK,OAAO,EAAG,CAAC;QACf,KAAK,CAAC,GAAG,CAAE,SAAS,EAAE,OAAO,CAAE,CAAC;IACjC,CAAC;IAED,MAAM,IAAI,CAAC,cAAc,CAAE,cAAc,EAAE,KAAK,CAAC,QAAQ,EAAE,CAAE,CAAC;IAE9D,MAAM,IAAI,CAAC,MAAM,CAAC,cAAc,CAAE,gBAAgB,EAAE;QACnD,YAAY,EAAE,OAAO,CAAC,gBAAgB,IAAI,KAAK;QAC/C,cAAc,EAAE,OAAO,CAAC,cAAc,IAAI,KAAK;KAC/C,CAAE,CAAC;AACL,CAAC"}
@@ -1,40 +0,0 @@
1
- "use strict";
2
- var __defProp = Object.defineProperty;
3
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
- var __getOwnPropNames = Object.getOwnPropertyNames;
5
- var __hasOwnProp = Object.prototype.hasOwnProperty;
6
- var __export = (target, all) => {
7
- for (var name in all)
8
- __defProp(target, name, { get: all[name], enumerable: true });
9
- };
10
- var __copyProps = (to, from, except, desc) => {
11
- if (from && typeof from === "object" || typeof from === "function") {
12
- for (let key of __getOwnPropNames(from))
13
- if (!__hasOwnProp.call(to, key) && key !== except)
14
- __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
- }
16
- return to;
17
- };
18
- var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
-
20
- // packages/e2e-test-utils-playwright/src/admin/edit-post.ts
21
- var edit_post_exports = {};
22
- __export(edit_post_exports, {
23
- editPost: () => editPost
24
- });
25
- module.exports = __toCommonJS(edit_post_exports);
26
- async function editPost(postId) {
27
- const query = new URLSearchParams();
28
- query.set("post", String(postId));
29
- query.set("action", "edit");
30
- await this.visitAdminPage("post.php", query.toString());
31
- await this.editor.setPreferences("core/edit-post", {
32
- welcomeGuide: false,
33
- fullscreenMode: false
34
- });
35
- }
36
- // Annotate the CommonJS export names for ESM import in node:
37
- 0 && (module.exports = {
38
- editPost
39
- });
40
- //# sourceMappingURL=edit-post.cjs.map
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../src/admin/edit-post.ts"],
4
- "sourcesContent": ["/**\n * Internal dependencies\n */\nimport type { Admin } from '.';\n\n/**\n * Open the post with given ID in the editor.\n *\n * @param this\n * @param postId Post ID to visit.\n */\nexport async function editPost( this: Admin, postId: string | number ) {\n\tconst query = new URLSearchParams();\n\n\tquery.set( 'post', String( postId ) );\n\tquery.set( 'action', 'edit' );\n\n\tawait this.visitAdminPage( 'post.php', query.toString() );\n\n\tawait this.editor.setPreferences( 'core/edit-post', {\n\t\twelcomeGuide: false,\n\t\tfullscreenMode: false,\n\t} );\n}\n"],
5
- "mappings": ";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAWA,eAAsB,SAAuB,QAA0B;AACtE,QAAM,QAAQ,IAAI,gBAAgB;AAElC,QAAM,IAAK,QAAQ,OAAQ,MAAO,CAAE;AACpC,QAAM,IAAK,UAAU,MAAO;AAE5B,QAAM,KAAK,eAAgB,YAAY,MAAM,SAAS,CAAE;AAExD,QAAM,KAAK,OAAO,eAAgB,kBAAkB;AAAA,IACnD,cAAc;AAAA,IACd,gBAAgB;AAAA,EACjB,CAAE;AACH;",
6
- "names": []
7
- }
@@ -1,20 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.editPost = editPost;
4
- /**
5
- * Open the post with given ID in the editor.
6
- *
7
- * @param this
8
- * @param postId Post ID to visit.
9
- */
10
- async function editPost(postId) {
11
- const query = new URLSearchParams();
12
- query.set('post', String(postId));
13
- query.set('action', 'edit');
14
- await this.visitAdminPage('post.php', query.toString());
15
- await this.editor.setPreferences('core/edit-post', {
16
- welcomeGuide: false,
17
- fullscreenMode: false,
18
- });
19
- }
20
- //# sourceMappingURL=edit-post.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"edit-post.js","sourceRoot":"","sources":["../../src/admin/edit-post.ts"],"names":[],"mappings":";;;AAKA;;;;;GAKG;AACI,KAAK,mBAAiC,MAAuB;IACnE,MAAM,KAAK,GAAG,IAAI,eAAe,EAAE,CAAC;IAEpC,KAAK,CAAC,GAAG,CAAE,MAAM,EAAE,MAAM,CAAE,MAAM,CAAE,CAAE,CAAC;IACtC,KAAK,CAAC,GAAG,CAAE,QAAQ,EAAE,MAAM,CAAE,CAAC;IAE9B,MAAM,IAAI,CAAC,cAAc,CAAE,UAAU,EAAE,KAAK,CAAC,QAAQ,EAAE,CAAE,CAAC;IAE1D,MAAM,IAAI,CAAC,MAAM,CAAC,cAAc,CAAE,gBAAgB,EAAE;QACnD,YAAY,EAAE,KAAK;QACnB,cAAc,EAAE,KAAK;KACrB,CAAE,CAAC;AACL,CAAC"}
@@ -1,36 +0,0 @@
1
- "use strict";
2
- var __defProp = Object.defineProperty;
3
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
- var __getOwnPropNames = Object.getOwnPropertyNames;
5
- var __hasOwnProp = Object.prototype.hasOwnProperty;
6
- var __export = (target, all) => {
7
- for (var name in all)
8
- __defProp(target, name, { get: all[name], enumerable: true });
9
- };
10
- var __copyProps = (to, from, except, desc) => {
11
- if (from && typeof from === "object" || typeof from === "function") {
12
- for (let key of __getOwnPropNames(from))
13
- if (!__hasOwnProp.call(to, key) && key !== except)
14
- __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
- }
16
- return to;
17
- };
18
- var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
-
20
- // packages/e2e-test-utils-playwright/src/admin/get-page-error.ts
21
- var get_page_error_exports = {};
22
- __export(get_page_error_exports, {
23
- getPageError: () => getPageError
24
- });
25
- module.exports = __toCommonJS(get_page_error_exports);
26
- var REGEXP_PHP_ERROR = /(<b>)?(Fatal error|Recoverable fatal error|Warning|Parse error|Notice|Strict Standards|Deprecated|Unknown error)(<\/b>)?: (.*?) in (.*?) on line (<b>)?\d+(<\/b>)?/;
27
- async function getPageError() {
28
- const content = await this.page.content();
29
- const match = content.match(REGEXP_PHP_ERROR);
30
- return match ? match[0] : null;
31
- }
32
- // Annotate the CommonJS export names for ESM import in node:
33
- 0 && (module.exports = {
34
- getPageError
35
- });
36
- //# sourceMappingURL=get-page-error.cjs.map
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../src/admin/get-page-error.ts"],
4
- "sourcesContent": ["/**\n * Internal dependencies\n */\nimport type { Admin } from './';\n\n/**\n * Regular expression matching a displayed PHP error within a markup string.\n *\n * @see https://github.com/php/php-src/blob/598175e/main/main.c#L1257-L1297\n */\nconst REGEXP_PHP_ERROR =\n\t/(<b>)?(Fatal error|Recoverable fatal error|Warning|Parse error|Notice|Strict Standards|Deprecated|Unknown error)(<\\/b>)?: (.*?) in (.*?) on line (<b>)?\\d+(<\\/b>)?/;\n\n/**\n * Returns a promise resolving to one of either a string or null. A string will\n * be resolved if an error message is present in the contents of the page. If no\n * error is present, a null value will be resolved instead. This requires the\n * environment be configured to display errors.\n *\n * @see http://php.net/manual/en/function.error-reporting.php\n *\n * @param this\n * @return Promise resolving to a string or null, depending whether a page error is present.\n */\nexport async function getPageError( this: Admin ) {\n\tconst content = await this.page.content();\n\tconst match = content.match( REGEXP_PHP_ERROR );\n\treturn match ? match[ 0 ] : null;\n}\n"],
5
- "mappings": ";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAUA,IAAM,mBACL;AAaD,eAAsB,eAA4B;AACjD,QAAM,UAAU,MAAM,KAAK,KAAK,QAAQ;AACxC,QAAM,QAAQ,QAAQ,MAAO,gBAAiB;AAC9C,SAAO,QAAQ,MAAO,CAAE,IAAI;AAC7B;",
6
- "names": []
7
- }
@@ -1 +0,0 @@
1
- {"version":3,"file":"get-page-error.js","sourceRoot":"","sources":["../../src/admin/get-page-error.ts"],"names":[],"mappings":";;;AAKA;;;;GAIG;AACH,MAAM,gBAAgB,GACrB,oKAAoK,CAAC;AAEtK;;;;;;;;;;GAUG;AACI,KAAK;IACX,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC;IAC1C,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAE,gBAAgB,CAAE,CAAC;IAChD,OAAO,KAAK,CAAC,CAAC,CAAC,KAAK,CAAE,CAAC,CAAE,CAAC,CAAC,CAAC,IAAI,CAAC;AAClC,CAAC"}