@wordpress/e2e-test-utils-playwright 1.47.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.
- package/package.json +3 -3
- package/src/admin/create-new-post.ts +47 -0
- package/src/admin/edit-post.ts +24 -0
- package/{build/admin/get-page-error.js → src/admin/get-page-error.ts} +12 -9
- package/src/admin/index.ts +52 -0
- package/src/admin/visit-admin-page.ts +43 -0
- package/src/admin/visit-site-editor.ts +62 -0
- package/src/admin/wait-for-site-editor.ts +52 -0
- package/src/config.ts +12 -0
- package/src/editor/click-block-options-menu-item.ts +18 -0
- package/src/editor/click-block-toolbar-button.ts +21 -0
- package/src/editor/get-blocks.ts +75 -0
- package/src/editor/get-edited-post-content.ts +19 -0
- package/src/editor/index.ts +95 -0
- package/src/editor/insert-block.ts +54 -0
- package/src/editor/open-document-settings-sidebar.ts +31 -0
- package/src/editor/preview.ts +32 -0
- package/src/editor/publish-post.ts +51 -0
- package/src/editor/save-draft.ts +20 -0
- package/src/editor/select-blocks.ts +48 -0
- package/src/editor/set-content.ts +24 -0
- package/src/editor/set-is-fixed-toolbar.ts +20 -0
- package/src/editor/set-preferences.ts +37 -0
- package/src/editor/show-block-toolbar.ts +20 -0
- package/src/editor/site-editor.ts +55 -0
- package/src/editor/switch-editor-tool.ts +34 -0
- package/src/editor/switch-to-legacy-canvas.ts +20 -0
- package/src/editor/transform-block-to.ts +34 -0
- package/src/index.ts +8 -0
- package/src/lighthouse/index.ts +75 -0
- package/src/metrics/index.ts +441 -0
- package/src/page-utils/drag-files.ts +173 -0
- package/src/page-utils/emulate-network-conditions.ts +101 -0
- package/src/page-utils/index.ts +54 -0
- package/src/page-utils/is-current-url.ts +20 -0
- package/src/page-utils/keycodes.ts +48 -0
- package/src/page-utils/press-keys.ts +195 -0
- package/{build → src}/page-utils/set-browser-viewport.js +14 -12
- package/src/request-utils/blocks.ts +58 -0
- package/src/request-utils/comments.ts +80 -0
- package/src/request-utils/gutenberg-experiments.ts +62 -0
- package/src/request-utils/index.ts +222 -0
- package/src/request-utils/login.ts +34 -0
- package/src/request-utils/media.ts +102 -0
- package/src/request-utils/menus.ts +145 -0
- package/src/request-utils/pages.ts +75 -0
- package/src/request-utils/patterns.ts +31 -0
- package/src/request-utils/plugins.ts +103 -0
- package/src/request-utils/posts.ts +69 -0
- package/src/request-utils/preferences.ts +63 -0
- package/{build/request-utils/records.js → src/request-utils/records.ts} +15 -10
- package/src/request-utils/rest.ts +222 -0
- package/src/request-utils/site-settings.ts +58 -0
- package/src/request-utils/templates.ts +83 -0
- package/src/request-utils/themes.ts +110 -0
- package/src/request-utils/users.ts +125 -0
- package/src/request-utils/widgets.js +68 -0
- package/src/test.ts +204 -0
- package/src/types.ts +13 -0
- package/build/admin/create-new-post.js +0 -31
- package/build/admin/create-new-post.js.map +0 -1
- package/build/admin/edit-post.js +0 -20
- package/build/admin/edit-post.js.map +0 -1
- package/build/admin/get-page-error.js.map +0 -1
- package/build/admin/index.js +0 -40
- package/build/admin/index.js.map +0 -1
- package/build/admin/visit-admin-page.js +0 -32
- package/build/admin/visit-admin-page.js.map +0 -1
- package/build/admin/visit-site-editor.js +0 -45
- package/build/admin/visit-site-editor.js.map +0 -1
- package/build/admin/wait-for-site-editor.js +0 -41
- package/build/admin/wait-for-site-editor.js.map +0 -1
- package/build/config.js +0 -13
- package/build/config.js.map +0 -1
- package/build/editor/click-block-options-menu-item.js +0 -17
- package/build/editor/click-block-options-menu-item.js.map +0 -1
- package/build/editor/click-block-toolbar-button.js +0 -16
- package/build/editor/click-block-toolbar-button.js.map +0 -1
- package/build/editor/get-blocks.js +0 -45
- package/build/editor/get-blocks.js.map +0 -1
- package/build/editor/get-edited-post-content.js +0 -15
- package/build/editor/get-edited-post-content.js.map +0 -1
- package/build/editor/index.js +0 -75
- package/build/editor/index.js.map +0 -1
- package/build/editor/insert-block.js +0 -23
- package/build/editor/insert-block.js.map +0 -1
- package/build/editor/open-document-settings-sidebar.js +0 -27
- package/build/editor/open-document-settings-sidebar.js.map +0 -1
- package/build/editor/preview.js +0 -21
- package/build/editor/preview.js.map +0 -1
- package/build/editor/publish-post.js +0 -45
- package/build/editor/publish-post.js.map +0 -1
- package/build/editor/save-draft.js +0 -18
- package/build/editor/save-draft.js.map +0 -1
- package/build/editor/select-blocks.js +0 -28
- package/build/editor/select-blocks.js.map +0 -1
- package/build/editor/set-content.js +0 -17
- package/build/editor/set-content.js.map +0 -1
- package/build/editor/set-is-fixed-toolbar.js +0 -18
- package/build/editor/set-is-fixed-toolbar.js.map +0 -1
- package/build/editor/set-preferences.js +0 -21
- package/build/editor/set-preferences.js.map +0 -1
- package/build/editor/show-block-toolbar.js +0 -19
- package/build/editor/show-block-toolbar.js.map +0 -1
- package/build/editor/site-editor.js +0 -44
- package/build/editor/site-editor.js.map +0 -1
- package/build/editor/switch-editor-tool.js +0 -33
- package/build/editor/switch-editor-tool.js.map +0 -1
- package/build/editor/switch-to-legacy-canvas.js +0 -18
- package/build/editor/switch-to-legacy-canvas.js.map +0 -1
- package/build/editor/transform-block-to.js +0 -24
- package/build/editor/transform-block-to.js.map +0 -1
- package/build/index.js +0 -19
- package/build/index.js.map +0 -1
- package/build/lighthouse/index.js +0 -61
- package/build/lighthouse/index.js.map +0 -1
- package/build/metrics/index.js +0 -314
- package/build/metrics/index.js.map +0 -1
- package/build/page-utils/drag-files.js +0 -119
- package/build/page-utils/drag-files.js.map +0 -1
- package/build/page-utils/emulate-network-conditions.js +0 -71
- package/build/page-utils/emulate-network-conditions.js.map +0 -1
- package/build/page-utils/index.js +0 -37
- package/build/page-utils/index.js.map +0 -1
- package/build/page-utils/is-current-url.js +0 -21
- package/build/page-utils/is-current-url.js.map +0 -1
- package/build/page-utils/keycodes.js +0 -41
- package/build/page-utils/keycodes.js.map +0 -1
- package/build/page-utils/press-keys.js +0 -141
- package/build/page-utils/press-keys.js.map +0 -1
- package/build/page-utils/set-browser-viewport.js.map +0 -1
- package/build/request-utils/blocks.js +0 -40
- package/build/request-utils/blocks.js.map +0 -1
- package/build/request-utils/comments.js +0 -52
- package/build/request-utils/comments.js.map +0 -1
- package/build/request-utils/gutenberg-experiments.js +0 -47
- package/build/request-utils/gutenberg-experiments.js.map +0 -1
- package/build/request-utils/index.js +0 -196
- package/build/request-utils/index.js.map +0 -1
- package/build/request-utils/login.js +0 -21
- package/build/request-utils/login.js.map +0 -1
- package/build/request-utils/media.js +0 -106
- package/build/request-utils/media.js.map +0 -1
- package/build/request-utils/menus.js +0 -116
- package/build/request-utils/menus.js.map +0 -1
- package/build/request-utils/pages.js +0 -53
- package/build/request-utils/pages.js.map +0 -1
- package/build/request-utils/patterns.js +0 -27
- package/build/request-utils/patterns.js.map +0 -1
- package/build/request-utils/plugins.js +0 -85
- package/build/request-utils/plugins.js.map +0 -1
- package/build/request-utils/posts.js +0 -42
- package/build/request-utils/posts.js.map +0 -1
- package/build/request-utils/preferences.js +0 -54
- package/build/request-utils/preferences.js.map +0 -1
- package/build/request-utils/records.js.map +0 -1
- package/build/request-utils/rest.js +0 -181
- package/build/request-utils/rest.js.map +0 -1
- package/build/request-utils/site-settings.js +0 -33
- package/build/request-utils/site-settings.js.map +0 -1
- package/build/request-utils/templates.js +0 -54
- package/build/request-utils/templates.js.map +0 -1
- package/build/request-utils/themes.js +0 -69
- package/build/request-utils/themes.js.map +0 -1
- package/build/request-utils/users.js +0 -81
- package/build/request-utils/users.js.map +0 -1
- package/build/request-utils/widgets.js +0 -61
- package/build/request-utils/widgets.js.map +0 -1
- package/build/test.js +0 -189
- package/build/test.js.map +0 -1
- package/build/types.js +0 -3
- package/build/types.js.map +0 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wordpress/e2e-test-utils-playwright",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.48.0",
|
|
4
4
|
"description": "End-To-End (E2E) test utils for WordPress.",
|
|
5
5
|
"author": "The WordPress Contributors",
|
|
6
6
|
"license": "GPL-2.0-or-later",
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
"npm": ">=8.19.2"
|
|
26
26
|
},
|
|
27
27
|
"files": [
|
|
28
|
-
"
|
|
28
|
+
"src",
|
|
29
29
|
"build-types"
|
|
30
30
|
],
|
|
31
31
|
"exports": {
|
|
@@ -54,5 +54,5 @@
|
|
|
54
54
|
"publishConfig": {
|
|
55
55
|
"access": "public"
|
|
56
56
|
},
|
|
57
|
-
"gitHead": "
|
|
57
|
+
"gitHead": "e7856693aeb4e2522d13608cd32c994e4a97cb9c"
|
|
58
58
|
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Internal dependencies
|
|
3
|
+
*/
|
|
4
|
+
import type { Admin } from './';
|
|
5
|
+
|
|
6
|
+
interface NewPostOptions {
|
|
7
|
+
postType?: string;
|
|
8
|
+
title?: string;
|
|
9
|
+
content?: string;
|
|
10
|
+
excerpt?: string;
|
|
11
|
+
showWelcomeGuide?: boolean;
|
|
12
|
+
fullscreenMode?: boolean;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Creates new post.
|
|
17
|
+
*
|
|
18
|
+
* @param this
|
|
19
|
+
* @param options Options to create new post.
|
|
20
|
+
*/
|
|
21
|
+
export async function createNewPost(
|
|
22
|
+
this: Admin,
|
|
23
|
+
options: NewPostOptions = {}
|
|
24
|
+
) {
|
|
25
|
+
const query = new URLSearchParams();
|
|
26
|
+
const { postType, title, content, excerpt } = options;
|
|
27
|
+
|
|
28
|
+
if ( postType ) {
|
|
29
|
+
query.set( 'post_type', postType );
|
|
30
|
+
}
|
|
31
|
+
if ( title ) {
|
|
32
|
+
query.set( 'post_title', title );
|
|
33
|
+
}
|
|
34
|
+
if ( content ) {
|
|
35
|
+
query.set( 'content', content );
|
|
36
|
+
}
|
|
37
|
+
if ( excerpt ) {
|
|
38
|
+
query.set( 'excerpt', excerpt );
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
await this.visitAdminPage( 'post-new.php', query.toString() );
|
|
42
|
+
|
|
43
|
+
await this.editor.setPreferences( 'core/edit-post', {
|
|
44
|
+
welcomeGuide: options.showWelcomeGuide ?? false,
|
|
45
|
+
fullscreenMode: options.fullscreenMode ?? false,
|
|
46
|
+
} );
|
|
47
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Internal dependencies
|
|
3
|
+
*/
|
|
4
|
+
import type { Admin } from '.';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Open the post with given ID in the editor.
|
|
8
|
+
*
|
|
9
|
+
* @param this
|
|
10
|
+
* @param postId Post ID to visit.
|
|
11
|
+
*/
|
|
12
|
+
export async function editPost( this: Admin, postId: string | number ) {
|
|
13
|
+
const query = new URLSearchParams();
|
|
14
|
+
|
|
15
|
+
query.set( 'post', String( postId ) );
|
|
16
|
+
query.set( 'action', 'edit' );
|
|
17
|
+
|
|
18
|
+
await this.visitAdminPage( 'post.php', query.toString() );
|
|
19
|
+
|
|
20
|
+
await this.editor.setPreferences( 'core/edit-post', {
|
|
21
|
+
welcomeGuide: false,
|
|
22
|
+
fullscreenMode: false,
|
|
23
|
+
} );
|
|
24
|
+
}
|
|
@@ -1,12 +1,16 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Internal dependencies
|
|
3
|
+
*/
|
|
4
|
+
import type { Admin } from './';
|
|
5
|
+
|
|
4
6
|
/**
|
|
5
7
|
* Regular expression matching a displayed PHP error within a markup string.
|
|
6
8
|
*
|
|
7
9
|
* @see https://github.com/php/php-src/blob/598175e/main/main.c#L1257-L1297
|
|
8
10
|
*/
|
|
9
|
-
const REGEXP_PHP_ERROR =
|
|
11
|
+
const REGEXP_PHP_ERROR =
|
|
12
|
+
/(<b>)?(Fatal error|Recoverable fatal error|Warning|Parse error|Notice|Strict Standards|Deprecated|Unknown error)(<\/b>)?: (.*?) in (.*?) on line (<b>)?\d+(<\/b>)?/;
|
|
13
|
+
|
|
10
14
|
/**
|
|
11
15
|
* Returns a promise resolving to one of either a string or null. A string will
|
|
12
16
|
* be resolved if an error message is present in the contents of the page. If no
|
|
@@ -18,9 +22,8 @@ const REGEXP_PHP_ERROR = /(<b>)?(Fatal error|Recoverable fatal error|Warning|Par
|
|
|
18
22
|
* @param this
|
|
19
23
|
* @return Promise resolving to a string or null, depending whether a page error is present.
|
|
20
24
|
*/
|
|
21
|
-
async function getPageError() {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
+
export async function getPageError( this: Admin ) {
|
|
26
|
+
const content = await this.page.content();
|
|
27
|
+
const match = content.match( REGEXP_PHP_ERROR );
|
|
28
|
+
return match ? match[ 0 ] : null;
|
|
25
29
|
}
|
|
26
|
-
//# sourceMappingURL=get-page-error.js.map
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* External dependencies
|
|
3
|
+
*/
|
|
4
|
+
import type { Browser, Page, BrowserContext } from '@playwright/test';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Internal dependencies
|
|
8
|
+
*/
|
|
9
|
+
import { createNewPost } from './create-new-post';
|
|
10
|
+
import { getPageError } from './get-page-error';
|
|
11
|
+
import { visitAdminPage } from './visit-admin-page';
|
|
12
|
+
import { editPost } from './edit-post';
|
|
13
|
+
import { visitSiteEditor } from './visit-site-editor';
|
|
14
|
+
import { waitForSiteEditor } from './wait-for-site-editor';
|
|
15
|
+
import type { PageUtils } from '../page-utils';
|
|
16
|
+
import type { Editor } from '../editor';
|
|
17
|
+
|
|
18
|
+
type AdminConstructorProps = {
|
|
19
|
+
page: Page;
|
|
20
|
+
pageUtils: PageUtils;
|
|
21
|
+
editor: Editor;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export class Admin {
|
|
25
|
+
page: Page;
|
|
26
|
+
context: BrowserContext;
|
|
27
|
+
browser: Browser;
|
|
28
|
+
pageUtils: PageUtils;
|
|
29
|
+
editor: Editor;
|
|
30
|
+
|
|
31
|
+
constructor( { page, pageUtils, editor }: AdminConstructorProps ) {
|
|
32
|
+
this.page = page;
|
|
33
|
+
this.context = page.context();
|
|
34
|
+
this.browser = this.context.browser()!;
|
|
35
|
+
this.pageUtils = pageUtils;
|
|
36
|
+
this.editor = editor;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** @borrows createNewPost as this.createNewPost */
|
|
40
|
+
createNewPost: typeof createNewPost = createNewPost.bind( this );
|
|
41
|
+
/** @borrows editPost as this.editPost */
|
|
42
|
+
editPost: typeof editPost = editPost.bind( this );
|
|
43
|
+
/** @borrows getPageError as this.getPageError */
|
|
44
|
+
getPageError: typeof getPageError = getPageError.bind( this );
|
|
45
|
+
/** @borrows visitAdminPage as this.visitAdminPage */
|
|
46
|
+
visitAdminPage: typeof visitAdminPage = visitAdminPage.bind( this );
|
|
47
|
+
/** @borrows visitSiteEditor as this.visitSiteEditor */
|
|
48
|
+
visitSiteEditor: typeof visitSiteEditor = visitSiteEditor.bind( this );
|
|
49
|
+
/** @borrows waitForSiteEditor as this.waitForSiteEditor */
|
|
50
|
+
waitForSiteEditor: typeof waitForSiteEditor =
|
|
51
|
+
waitForSiteEditor.bind( this );
|
|
52
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* External dependencies
|
|
3
|
+
*/
|
|
4
|
+
import { join } from 'path';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Internal dependencies
|
|
8
|
+
*/
|
|
9
|
+
import type { Admin } from './';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Visits admin page and handle errors.
|
|
13
|
+
*
|
|
14
|
+
* @param this
|
|
15
|
+
* @param adminPath String to be serialized as pathname.
|
|
16
|
+
* @param query String to be serialized as query portion of URL.
|
|
17
|
+
*/
|
|
18
|
+
export async function visitAdminPage(
|
|
19
|
+
this: Admin,
|
|
20
|
+
adminPath: string,
|
|
21
|
+
query?: string
|
|
22
|
+
) {
|
|
23
|
+
await this.page.goto(
|
|
24
|
+
join( 'wp-admin', adminPath ) + ( query ? `?${ query }` : '' )
|
|
25
|
+
);
|
|
26
|
+
|
|
27
|
+
// Handle upgrade required screen
|
|
28
|
+
if ( this.pageUtils.isCurrentURL( 'wp-admin/upgrade.php' ) ) {
|
|
29
|
+
// Click update
|
|
30
|
+
await this.page.click( '.button.button-large.button-primary' );
|
|
31
|
+
// Click continue
|
|
32
|
+
await this.page.click( '.button.button-large' );
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if ( this.pageUtils.isCurrentURL( 'wp-login.php' ) ) {
|
|
36
|
+
throw new Error( 'Not logged in' );
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const error = await this.getPageError();
|
|
40
|
+
if ( error ) {
|
|
41
|
+
throw new Error( 'Unexpected error in page content: ' + error );
|
|
42
|
+
}
|
|
43
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Internal dependencies
|
|
3
|
+
*/
|
|
4
|
+
import type { Admin } from './';
|
|
5
|
+
|
|
6
|
+
interface SiteEditorOptions {
|
|
7
|
+
postId?: string | number;
|
|
8
|
+
postType?: string;
|
|
9
|
+
path?: string;
|
|
10
|
+
canvas?: string;
|
|
11
|
+
activeView?: string;
|
|
12
|
+
showWelcomeGuide?: boolean;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Visits the Site Editor main page.
|
|
17
|
+
*
|
|
18
|
+
* @param this
|
|
19
|
+
* @param options Options to visit the site editor.
|
|
20
|
+
*/
|
|
21
|
+
export async function visitSiteEditor(
|
|
22
|
+
this: Admin,
|
|
23
|
+
options: SiteEditorOptions = {}
|
|
24
|
+
) {
|
|
25
|
+
const { postId, postType, path, canvas, activeView } = options;
|
|
26
|
+
const query = new URLSearchParams();
|
|
27
|
+
|
|
28
|
+
if ( postId ) {
|
|
29
|
+
query.set( 'postId', String( postId ) );
|
|
30
|
+
}
|
|
31
|
+
if ( postType ) {
|
|
32
|
+
query.set( 'postType', postType );
|
|
33
|
+
}
|
|
34
|
+
if ( path ) {
|
|
35
|
+
query.set( 'path', path );
|
|
36
|
+
}
|
|
37
|
+
if ( canvas ) {
|
|
38
|
+
query.set( 'canvas', canvas );
|
|
39
|
+
}
|
|
40
|
+
if ( activeView ) {
|
|
41
|
+
query.set( 'activeView', activeView );
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
await this.visitAdminPage( 'site-editor.php', query.toString() );
|
|
45
|
+
|
|
46
|
+
if ( ! options.showWelcomeGuide ) {
|
|
47
|
+
await this.editor.setPreferences( 'core/edit-site', {
|
|
48
|
+
welcomeGuide: false,
|
|
49
|
+
welcomeGuideStyles: false,
|
|
50
|
+
welcomeGuidePage: false,
|
|
51
|
+
welcomeGuideTemplate: false,
|
|
52
|
+
} );
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Wait until the editor is loaded. The logic is a copy of the
|
|
57
|
+
* `waitWhileSiteEditorLoading` function in the `edit-site` package.
|
|
58
|
+
*/
|
|
59
|
+
if ( ! query.size || postId || canvas === 'edit' ) {
|
|
60
|
+
await this.waitForSiteEditor();
|
|
61
|
+
}
|
|
62
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Internal dependencies
|
|
3
|
+
*/
|
|
4
|
+
import type { Admin } from './';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Waits for the Site Editor to finish loading, i.e., for all resolvers
|
|
8
|
+
* in the `core` store to finish and then pause for a short period.
|
|
9
|
+
*
|
|
10
|
+
* @param this
|
|
11
|
+
*/
|
|
12
|
+
export async function waitForSiteEditor( this: Admin ) {
|
|
13
|
+
await this.page.evaluate( () => {
|
|
14
|
+
const MAX_LOADING_TIME = 10000;
|
|
15
|
+
const MAX_PAUSE_TIME = 100;
|
|
16
|
+
|
|
17
|
+
return new Promise< void >( ( resolve ) => {
|
|
18
|
+
let pauseTimeout: ReturnType< typeof setTimeout > | undefined;
|
|
19
|
+
|
|
20
|
+
function finish() {
|
|
21
|
+
unsubscribe();
|
|
22
|
+
clearTimeout( pauseTimeout );
|
|
23
|
+
clearTimeout( maxTimeout );
|
|
24
|
+
resolve();
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const maxTimeout = setTimeout( finish, MAX_LOADING_TIME );
|
|
28
|
+
|
|
29
|
+
function checkResolving() {
|
|
30
|
+
const isResolving = window.wp.data
|
|
31
|
+
.select( 'core' )
|
|
32
|
+
.hasResolvingSelectors();
|
|
33
|
+
|
|
34
|
+
if ( isResolving ) {
|
|
35
|
+
clearTimeout( pauseTimeout );
|
|
36
|
+
pauseTimeout = undefined;
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if ( ! pauseTimeout ) {
|
|
41
|
+
pauseTimeout = setTimeout( finish, MAX_PAUSE_TIME );
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const unsubscribe = window.wp.data.subscribe(
|
|
46
|
+
checkResolving,
|
|
47
|
+
'core'
|
|
48
|
+
);
|
|
49
|
+
checkResolving();
|
|
50
|
+
} );
|
|
51
|
+
} );
|
|
52
|
+
}
|
package/src/config.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
const {
|
|
2
|
+
WP_USERNAME = 'admin',
|
|
3
|
+
WP_PASSWORD = 'password',
|
|
4
|
+
WP_BASE_URL = 'http://localhost:8889',
|
|
5
|
+
} = process.env;
|
|
6
|
+
|
|
7
|
+
const WP_ADMIN_USER = {
|
|
8
|
+
username: WP_USERNAME,
|
|
9
|
+
password: WP_PASSWORD,
|
|
10
|
+
} as const;
|
|
11
|
+
|
|
12
|
+
export { WP_ADMIN_USER, WP_USERNAME, WP_PASSWORD, WP_BASE_URL };
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Internal dependencies
|
|
3
|
+
*/
|
|
4
|
+
import type { Editor } from './index';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Clicks a block toolbar button.
|
|
8
|
+
*
|
|
9
|
+
* @param this
|
|
10
|
+
* @param label The text string of the button label.
|
|
11
|
+
*/
|
|
12
|
+
export async function clickBlockOptionsMenuItem( this: Editor, label: string ) {
|
|
13
|
+
await this.clickBlockToolbarButton( 'Options' );
|
|
14
|
+
await this.page
|
|
15
|
+
.getByRole( 'menu', { name: 'Options' } )
|
|
16
|
+
.getByRole( 'menuitem', { name: label } )
|
|
17
|
+
.click();
|
|
18
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Internal dependencies
|
|
3
|
+
*/
|
|
4
|
+
import type { Editor } from './index';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Clicks a block toolbar button.
|
|
8
|
+
*
|
|
9
|
+
* @param this
|
|
10
|
+
* @param label The text string of the button label.
|
|
11
|
+
*/
|
|
12
|
+
export async function clickBlockToolbarButton( this: Editor, label: string ) {
|
|
13
|
+
await this.showBlockToolbar();
|
|
14
|
+
|
|
15
|
+
const blockToolbar = this.page.locator(
|
|
16
|
+
'role=toolbar[name="Block tools"i]'
|
|
17
|
+
);
|
|
18
|
+
const button = blockToolbar.locator( `role=button[name="${ label }"]` );
|
|
19
|
+
|
|
20
|
+
await button.click();
|
|
21
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Internal dependencies
|
|
3
|
+
*/
|
|
4
|
+
import type { Editor } from './index';
|
|
5
|
+
|
|
6
|
+
type Block = {
|
|
7
|
+
name: string;
|
|
8
|
+
attributes: Record< string, unknown >;
|
|
9
|
+
innerBlocks: Block[];
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Returns the edited blocks.
|
|
14
|
+
*
|
|
15
|
+
* @param options
|
|
16
|
+
* @param options.clientId Limit the results to be only under a partial tree of the specified clientId.
|
|
17
|
+
* @param options.full Whether to return the full block data or just the name and attributes.
|
|
18
|
+
*
|
|
19
|
+
* @return The blocks.
|
|
20
|
+
*/
|
|
21
|
+
export async function getBlocks(
|
|
22
|
+
this: Editor,
|
|
23
|
+
{ clientId, full = false }: { clientId?: string; full?: boolean } = {}
|
|
24
|
+
) {
|
|
25
|
+
await this.page.waitForFunction(
|
|
26
|
+
() => window?.wp?.blocks && window?.wp?.data
|
|
27
|
+
);
|
|
28
|
+
|
|
29
|
+
return await this.page.evaluate(
|
|
30
|
+
( [ _full, _clientId ] ) => {
|
|
31
|
+
// Serialize serializable attributes of blocks.
|
|
32
|
+
function serializeAttributes(
|
|
33
|
+
attributes: Record< string, unknown >
|
|
34
|
+
) {
|
|
35
|
+
return Object.fromEntries(
|
|
36
|
+
Object.entries( attributes ).map( ( [ key, value ] ) => {
|
|
37
|
+
// Serialize RichTextData to string.
|
|
38
|
+
if (
|
|
39
|
+
value instanceof window.wp.richText.RichTextData
|
|
40
|
+
) {
|
|
41
|
+
return [ key, ( value as string ).toString() ];
|
|
42
|
+
}
|
|
43
|
+
return [ key, value ];
|
|
44
|
+
} )
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Remove other unpredictable properties like clientId from blocks for testing purposes.
|
|
49
|
+
function recursivelyTransformBlocks( blocks: Block[] ): Block[] {
|
|
50
|
+
return blocks.map( ( block ) => ( {
|
|
51
|
+
name: block.name,
|
|
52
|
+
attributes: serializeAttributes( block.attributes ),
|
|
53
|
+
innerBlocks: recursivelyTransformBlocks(
|
|
54
|
+
block.innerBlocks
|
|
55
|
+
),
|
|
56
|
+
} ) );
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const blocks = window.wp.data
|
|
60
|
+
.select( 'core/block-editor' )
|
|
61
|
+
.getBlocks( _clientId ) as Block[];
|
|
62
|
+
|
|
63
|
+
// The editor might still contain an unmodified empty block even when it's technically "empty".
|
|
64
|
+
if (
|
|
65
|
+
blocks.length === 1 &&
|
|
66
|
+
window.wp.blocks.isUnmodifiedDefaultBlock( blocks[ 0 ] )
|
|
67
|
+
) {
|
|
68
|
+
return [];
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return _full ? blocks : recursivelyTransformBlocks( blocks );
|
|
72
|
+
},
|
|
73
|
+
[ full, clientId ]
|
|
74
|
+
);
|
|
75
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Internal dependencies
|
|
3
|
+
*/
|
|
4
|
+
import type { Editor } from './index';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Returns a promise which resolves with the edited post content (HTML string).
|
|
8
|
+
*
|
|
9
|
+
* @param this
|
|
10
|
+
*
|
|
11
|
+
* @return Promise resolving with post content markup.
|
|
12
|
+
*/
|
|
13
|
+
export async function getEditedPostContent( this: Editor ) {
|
|
14
|
+
await this.page.waitForFunction( () => window?.wp?.data );
|
|
15
|
+
|
|
16
|
+
return await this.page.evaluate( () =>
|
|
17
|
+
window.wp.data.select( 'core/editor' ).getEditedPostContent()
|
|
18
|
+
);
|
|
19
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* External dependencies
|
|
3
|
+
*/
|
|
4
|
+
import type {
|
|
5
|
+
Browser,
|
|
6
|
+
Page,
|
|
7
|
+
BrowserContext,
|
|
8
|
+
FrameLocator,
|
|
9
|
+
} from '@playwright/test';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Internal dependencies
|
|
13
|
+
*/
|
|
14
|
+
import { clickBlockOptionsMenuItem } from './click-block-options-menu-item';
|
|
15
|
+
import { clickBlockToolbarButton } from './click-block-toolbar-button';
|
|
16
|
+
import { getBlocks } from './get-blocks';
|
|
17
|
+
import { getEditedPostContent } from './get-edited-post-content';
|
|
18
|
+
import { insertBlock } from './insert-block';
|
|
19
|
+
import { openDocumentSettingsSidebar } from './open-document-settings-sidebar';
|
|
20
|
+
import { openPreviewPage } from './preview';
|
|
21
|
+
import { publishPost } from './publish-post';
|
|
22
|
+
import { saveDraft } from './save-draft';
|
|
23
|
+
import { selectBlocks } from './select-blocks';
|
|
24
|
+
import { setContent } from './set-content';
|
|
25
|
+
import { setPreferences } from './set-preferences';
|
|
26
|
+
import { showBlockToolbar } from './show-block-toolbar';
|
|
27
|
+
import { saveSiteEditorEntities } from './site-editor';
|
|
28
|
+
import { setIsFixedToolbar } from './set-is-fixed-toolbar';
|
|
29
|
+
import { switchToLegacyCanvas } from './switch-to-legacy-canvas';
|
|
30
|
+
import { transformBlockTo } from './transform-block-to';
|
|
31
|
+
import { switchEditorTool } from './switch-editor-tool';
|
|
32
|
+
|
|
33
|
+
type EditorConstructorProps = {
|
|
34
|
+
page: Page;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export class Editor {
|
|
38
|
+
browser: Browser;
|
|
39
|
+
page: Page;
|
|
40
|
+
context: BrowserContext;
|
|
41
|
+
|
|
42
|
+
constructor( { page }: EditorConstructorProps ) {
|
|
43
|
+
this.page = page;
|
|
44
|
+
this.context = page.context();
|
|
45
|
+
this.browser = this.context.browser()!;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
get canvas(): FrameLocator {
|
|
49
|
+
return this.page.frameLocator( '[name="editor-canvas"]' );
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** @borrows clickBlockOptionsMenuItem as this.clickBlockOptionsMenuItem */
|
|
53
|
+
clickBlockOptionsMenuItem: typeof clickBlockOptionsMenuItem =
|
|
54
|
+
clickBlockOptionsMenuItem.bind( this );
|
|
55
|
+
/** @borrows clickBlockToolbarButton as this.clickBlockToolbarButton */
|
|
56
|
+
clickBlockToolbarButton: typeof clickBlockToolbarButton =
|
|
57
|
+
clickBlockToolbarButton.bind( this );
|
|
58
|
+
/** @borrows getBlocks as this.getBlocks */
|
|
59
|
+
getBlocks: typeof getBlocks = getBlocks.bind( this );
|
|
60
|
+
/** @borrows getEditedPostContent as this.getEditedPostContent */
|
|
61
|
+
getEditedPostContent: typeof getEditedPostContent =
|
|
62
|
+
getEditedPostContent.bind( this );
|
|
63
|
+
/** @borrows insertBlock as this.insertBlock */
|
|
64
|
+
insertBlock: typeof insertBlock = insertBlock.bind( this );
|
|
65
|
+
/** @borrows openDocumentSettingsSidebar as this.openDocumentSettingsSidebar */
|
|
66
|
+
openDocumentSettingsSidebar: typeof openDocumentSettingsSidebar =
|
|
67
|
+
openDocumentSettingsSidebar.bind( this );
|
|
68
|
+
/** @borrows openPreviewPage as this.openPreviewPage */
|
|
69
|
+
openPreviewPage: typeof openPreviewPage = openPreviewPage.bind( this );
|
|
70
|
+
/** @borrows publishPost as this.publishPost */
|
|
71
|
+
publishPost: typeof publishPost = publishPost.bind( this );
|
|
72
|
+
/** @borrows saveDraft as this.saveDraft */
|
|
73
|
+
saveDraft: typeof saveDraft = saveDraft.bind( this );
|
|
74
|
+
/** @borrows saveSiteEditorEntities as this.saveSiteEditorEntities */
|
|
75
|
+
saveSiteEditorEntities: typeof saveSiteEditorEntities =
|
|
76
|
+
saveSiteEditorEntities.bind( this );
|
|
77
|
+
/** @borrows selectBlocks as this.selectBlocks */
|
|
78
|
+
selectBlocks: typeof selectBlocks = selectBlocks.bind( this );
|
|
79
|
+
/** @borrows setContent as this.setContent */
|
|
80
|
+
setContent: typeof setContent = setContent.bind( this );
|
|
81
|
+
/** @borrows setPreferences as this.setPreferences */
|
|
82
|
+
setPreferences: typeof setPreferences = setPreferences.bind( this );
|
|
83
|
+
/** @borrows showBlockToolbar as this.showBlockToolbar */
|
|
84
|
+
showBlockToolbar: typeof showBlockToolbar = showBlockToolbar.bind( this );
|
|
85
|
+
/** @borrows setIsFixedToolbar as this.setIsFixedToolbar */
|
|
86
|
+
setIsFixedToolbar: typeof setIsFixedToolbar =
|
|
87
|
+
setIsFixedToolbar.bind( this );
|
|
88
|
+
/** @borrows switchEditorTool as this.switchEditorTool */
|
|
89
|
+
switchEditorTool: typeof switchEditorTool = switchEditorTool.bind( this );
|
|
90
|
+
/** @borrows switchToLegacyCanvas as this.switchToLegacyCanvas */
|
|
91
|
+
switchToLegacyCanvas: typeof switchToLegacyCanvas =
|
|
92
|
+
switchToLegacyCanvas.bind( this );
|
|
93
|
+
/** @borrows transformBlockTo as this.transformBlockTo */
|
|
94
|
+
transformBlockTo: typeof transformBlockTo = transformBlockTo.bind( this );
|
|
95
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Internal dependencies
|
|
3
|
+
*/
|
|
4
|
+
import type { Editor } from './index';
|
|
5
|
+
|
|
6
|
+
interface BlockRepresentation {
|
|
7
|
+
name: string;
|
|
8
|
+
attributes?: Object;
|
|
9
|
+
innerBlocks?: BlockRepresentation[];
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Insert a block.
|
|
14
|
+
*
|
|
15
|
+
* @param blockRepresentation Inserted block representation.
|
|
16
|
+
* @param options
|
|
17
|
+
* @param options.clientId Client ID of the parent block to insert into.
|
|
18
|
+
*/
|
|
19
|
+
async function insertBlock(
|
|
20
|
+
this: Editor,
|
|
21
|
+
blockRepresentation: BlockRepresentation,
|
|
22
|
+
{ clientId }: { clientId?: string } = {}
|
|
23
|
+
) {
|
|
24
|
+
await this.page.waitForFunction(
|
|
25
|
+
() => window?.wp?.blocks && window?.wp?.data
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
await this.page.evaluate(
|
|
29
|
+
( [ _blockRepresentation, _clientId ] ) => {
|
|
30
|
+
function recursiveCreateBlock( {
|
|
31
|
+
name,
|
|
32
|
+
attributes = {},
|
|
33
|
+
innerBlocks = [],
|
|
34
|
+
}: BlockRepresentation ): Object {
|
|
35
|
+
return window.wp.blocks.createBlock(
|
|
36
|
+
name,
|
|
37
|
+
attributes,
|
|
38
|
+
innerBlocks.map( ( innerBlock ) =>
|
|
39
|
+
recursiveCreateBlock( innerBlock )
|
|
40
|
+
)
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
const block = recursiveCreateBlock( _blockRepresentation );
|
|
44
|
+
|
|
45
|
+
window.wp.data
|
|
46
|
+
.dispatch( 'core/block-editor' )
|
|
47
|
+
.insertBlock( block, undefined, _clientId );
|
|
48
|
+
},
|
|
49
|
+
[ blockRepresentation, clientId ] as const
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export type { BlockRepresentation };
|
|
54
|
+
export { insertBlock };
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Internal dependencies
|
|
3
|
+
*/
|
|
4
|
+
import type { Editor } from './index';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Clicks on the button in the header which opens Document Settings sidebar when
|
|
8
|
+
* it is closed.
|
|
9
|
+
*
|
|
10
|
+
* @param this
|
|
11
|
+
*/
|
|
12
|
+
export async function openDocumentSettingsSidebar( this: Editor ) {
|
|
13
|
+
const toggleButton = this.page
|
|
14
|
+
.getByRole( 'region', { name: 'Editor top bar' } )
|
|
15
|
+
.getByRole( 'button', {
|
|
16
|
+
name: 'Settings',
|
|
17
|
+
exact: true,
|
|
18
|
+
disabled: false,
|
|
19
|
+
} );
|
|
20
|
+
|
|
21
|
+
const isClosed =
|
|
22
|
+
( await toggleButton.getAttribute( 'aria-expanded' ) ) === 'false';
|
|
23
|
+
|
|
24
|
+
if ( isClosed ) {
|
|
25
|
+
await toggleButton.click();
|
|
26
|
+
await this.page
|
|
27
|
+
.getByRole( 'region', { name: 'Editor settings' } )
|
|
28
|
+
.getByRole( 'button', { name: 'Close Settings' } )
|
|
29
|
+
.waitFor();
|
|
30
|
+
}
|
|
31
|
+
}
|