Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .buildkite/pipeline.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,12 @@ steps:
command: make test-js
plugins: *plugins

- label: ':performing_arts: Test E2E'
command: |
npx playwright install chromium
make test-e2e
plugins: *plugins

- label: ':android: Publish Android Library'
command: |
make build REFRESH_L10N=1 REFRESH_JS_BUILD=1
Expand Down
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -196,5 +196,9 @@ local.properties
# Translation files
src/translations/

# Playwright
e2e/test-results/
playwright-report/

# Claude
.claude/settings.local.json
10 changes: 9 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ help: ## Display this help menu
@echo "Usage: make [target]"
@echo ""
@echo "Available targets:"
@grep -E '^[a-zA-Z_-]+:.*?## ' $(MAKEFILE_LIST) | \
@grep -E '^[a-zA-Z0-9_-]+:.*?## ' $(MAKEFILE_LIST) | \
awk 'BEGIN {FS = ":.*?## "}; {printf " \033[36m%-25s\033[0m %s\n", $$1, $$2}' | \
sort
@echo ""
Expand Down Expand Up @@ -136,6 +136,14 @@ lint-swift: ## Lint Swift code
# Testing Targets
################################################################################

.PHONY: test-e2e
test-e2e: npm-dependencies ## Run end-to-end tests
npm run test:e2e

.PHONY: test-e2e-ui
test-e2e-ui: npm-dependencies ## Run end-to-end tests in UI mode
npm run test:e2e:ui

.PHONY: test-js
test-js: npm-dependencies ## Run JavaScript tests
npm run test:unit
Expand Down
28 changes: 6 additions & 22 deletions docs/test-cases.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,60 +4,44 @@

**Purpose:** Verify the editor's core functionality: writing/formatting text, uploading media, saving/publishing, and basic block manipulation.

### S.1. Write and format text

- **Steps:**
- Add a Paragraph, List, or Heading block.
- Type some text.
- Apply bold, italic, and strikethrough formatting using the toolbar.
- **Expected Outcome:** Text is entered and formatting is applied as expected.

### S.2. Add a link to a paragraph
### S.1. Add a link to a paragraph

- **Steps:**
- Add a Paragraph, List, or Heading block.
- Type some text.
- Apply a link to the text.
- **Expected Outcome:** Link is applied to the text as expected.

### S.3. Merge and split blocks

- **Steps:**
- Write a long paragraph or list of multiple items.
- Place the cursor somwewhere in the middle and split the block into two blocks using Enter.
- Merge them back by deleting content at the start of the second block.
- **Expected Outcome:** Blocks split and merge as expected; content remains intact.

### S.4. Undo/Redo Actions
### S.2. Undo/Redo Actions

- **Steps:**
- Add, remove, and edit blocks and text.
- Use Undo and Redo buttons.
- **Expected Outcome:** Editor correctly undoes and redoes actions, restoring previous states.

### S.5. Upload an image
### S.3. Upload an image

- **Steps:**
- Add an Image block.
- Tap "Choose from device" and select an image.
- **Expected Outcome:** Image uploads and displays in the block. An activity indicator is shown while the image is uploading.

### S.6. Upload an video
### S.4. Upload an video

- **Steps:**
- Add a Video block.
- Tap "Choose from device" and select a video.
- **Expected Outcome:** Video uploads and displays in the block. An activity indicator is shown while the video is uploading.

### S.7. Reorder blocks
### S.5. Reorder blocks

- **Steps:**
- Add several content blocks to a post.
- Select a block.
- Use the up/down arrows in the block toolbar to relocate the block.
- **Expected Outcome:** The block ordering is updated as expected.

### S.8. Save and publish a post
### S.6. Save and publish a post

- **Steps:**
- Create a new post with text and media.
Expand Down
28 changes: 28 additions & 0 deletions e2e/.eslintrc.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
module.exports = {
extends: [ 'plugin:@wordpress/eslint-plugin/test-playwright' ],
rules: {
// Allow non-literal titles for test.describe/test parameterized tests.
'playwright/valid-title': 'off',
// The WordPress ESLint config enforces jsdoc on all functions, but
// test files don't benefit from it.
'jsdoc/require-jsdoc': 'off',
// Discourage deprecated Playwright APIs in favor of locators, aligned
// with the upstream Gutenberg ESLint configuration.
'no-restricted-syntax': [
'error',
{
selector: 'CallExpression[callee.property.name="$"]',
message: '`$` is discouraged, please use `locator` instead.',
},
{
selector: 'CallExpression[callee.property.name="$$"]',
message: '`$$` is discouraged, please use `locator` instead.',
},
{
selector:
'CallExpression[callee.object.name="page"][callee.property.name="waitForTimeout"]',
message: 'Prefer page.locator instead.',
},
],
},
};
67 changes: 67 additions & 0 deletions e2e/editor-load.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/**
* External dependencies
*/
import { test, expect } from '@playwright/test';

/**
* Internal dependencies
*/
import { setupEditor, getBlocks } from './editor-setup';

test.describe( 'Editor Load', () => {
test( 'should load the editor and reach ready state', async ( {
page,
} ) => {
await setupEditor( page );

await expect(
page.locator( '.gutenberg-kit-visual-editor' )
).toBeVisible();

await expect(
page.locator( '.editor-visual-editor__post-title-wrapper' )
).toBeVisible();

await expect(
page.locator( '.block-editor-block-list__layout.is-root-container' )
).toBeVisible();

await expect(
page.locator( '.gutenberg-kit-editor-toolbar' )
).toBeVisible();

await expect(
page.locator( 'button.gutenberg-kit-default-block-appender' )
).toBeAttached();
} );

test( 'should display an empty editor with no initial content', async ( {
page,
} ) => {
await setupEditor( page );

const blocks = await getBlocks( page );
expect( blocks ).toHaveLength( 0 );
} );

test( 'should load the editor with initial content', async ( { page } ) => {
const contentHtml =
'<!-- wp:paragraph -->\n<p>Hello from E2E</p>\n<!-- /wp:paragraph -->';

await setupEditor( page, {
post: {
id: 1,
type: 'post',
status: 'draft',
title: '',
content: contentHtml,
},
} );

const blocks = await getBlocks( page );
expect( blocks ).toHaveLength( 1 );
expect( blocks[ 0 ].name ).toBe( 'core/paragraph' );

await expect( page.getByText( 'Hello from E2E' ) ).toBeVisible();
} );
} );
64 changes: 64 additions & 0 deletions e2e/editor-setup.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/**
* Shared editor setup helper for E2E tests.
*
* Navigates to the editor in dev mode, injects the GBKit config, and waits
* for the editor to reach a ready state.
*/

/**
* Default GBKit configuration for dev-mode testing.
*
* @type {Object}
*/
const DEFAULT_GBKIT = {
post: {
id: -1,
type: 'post',
status: 'draft',
title: '',
content: '',
},
};

/**
* Navigate to the editor and wait for it to be fully ready.
*
* @param {import('@playwright/test').Page} page Playwright page object.
* @param {Object} [gbkit] Optional GBKit config override.
*/
export async function setupEditor( page, gbkit = DEFAULT_GBKIT ) {
// Inject GBKit config before page scripts run.
await page.addInitScript( ( config ) => {
window.GBKit = config;
}, gbkit );

// Navigate to the editor in dev mode.
await page.goto( '/?dev_mode=1' );

// Wait for the visual editor container to be visible.
await page.locator( '.gutenberg-kit-visual-editor' ).waitFor( {
state: 'visible',
timeout: 30_000,
} );

// Wait for WordPress editor data store to report ready.
await page.waitForFunction(
() =>
window.wp?.data
?.select( 'core/editor' )
?.__unstableIsEditorReady?.(),
{ timeout: 30_000 }
);
}

/**
* Retrieve all blocks from the editor via the WP data store.
*
* @param {import('@playwright/test').Page} page Playwright page object.
* @return {Promise<Array>} Array of block objects.
*/
export async function getBlocks( page ) {
return await page.evaluate( () =>
window.wp.data.select( 'core/block-editor' ).getBlocks()
);
}
Loading
Loading