Playwright plugin for testing WordPress and Gravity Wiz plugins
npm install @gravitywiz/playwright-plugin-gravity-wizA Playwright plugin that provides WordPress and Gravity Forms specific testing utilities for Gravity Wiz plugins. The plugin offers focused fixtures for different testing concerns, making tests more modular and maintainable.
``bash`
npm install @gravitywiz/playwright-plugin-gravity-wizor
yarn add @gravitywiz/playwright-plugin-gravity-wiz
First, create the required setup files in your project root:
global-setup.ts:
`typescript
import { globalSetup } from '@gravitywiz/playwright-plugin-gravity-wiz';
export default async function () {
return globalSetup();
}
`
global-teardown.ts:
`typescript
import { globalTeardown } from '@gravitywiz/playwright-plugin-gravity-wiz';
export default async function () {
return globalTeardown();
}
`
playwright.config.ts:
`typescript
import { defineConfig, devices } from '@playwright/test';
export default defineConfig({
testDir: './tests',
fullyParallel: true,
forbidOnly: !!process.env.CI,
retries: process.env.CI ? 2 : 0,
workers: process.env.CI ? 1 : undefined,
reporter: 'html',
// Global setup and teardown
globalSetup: require.resolve('./global-setup'),
globalTeardown: require.resolve('./global-teardown'),
use: {
baseURL: 'http://localhost',
trace: 'on-first-retry',
screenshot: 'only-on-failure',
},
projects: [
{
name: 'chromium',
use: { ...devices['Desktop Chrome'] },
},
],
});
`
`typescript
import { test as base, expect } from '@playwright/test';
import { createTest } from '@gravitywiz/playwright-plugin-gravity-wiz';
const test = createTest(base);
test.describe('My Plugin Tests', () => {
// The createTest function automatically runs wpCli.bootstrap() and gfCli.resetGF() before each test
test('should login to WordPress', async ({ page, wpAuth, pageHelpers }) => {
await wpAuth.login();
await pageHelpers.see('Dashboard');
await expect(page.locator('#wpadminbar')).toBeVisible();
});
});
`
The plugin provides focused fixtures for different testing concerns:
`typescript`
test('CLI operations', async ({ wpCli }) => {
await wpCli.bootstrap();
await wpCli.evalPhp('echo "Hello World";');
const blogname = await wpCli.getOption('blogname');
await wpCli.setOption('blogname', 'Test Site');
});
`typescript`
test('GF operations', async ({ gfCli }) => {
await gfCli.resetGF();
const version = await gfCli.getGFVersion();
const isModern = await gfCli.isGF25OrNewer();
await gfCli.importForm('contact-form.json');
const formId = await gfCli.getFormID('Contact Form');
});
`typescript`
test('Authentication', async ({ wpAuth }) => {
await wpAuth.login(); // Default: admin/admin
await wpAuth.login({ username: 'editor', password: 'password' });
await wpAuth.logout();
});
`typescript
test('Page interactions', async ({ pageHelpers, gfCli }) => {
await pageHelpers.see('Dashboard');
await pageHelpers.see('Welcome', '.welcome-panel');
await pageHelpers.goToChoicesSettings(); // GF-specific navigation
const result = await pageHelpers.retryUntil({
cmd: async () => await someAsyncOperation(),
until: (result) => result.success === true,
delay: 500,
timeout: 10000
});
});
`
`typescript`
test('Form operations', async ({ formHelpers }) => {
await formHelpers.importForm('contact-form.json');
await formHelpers.importForm('survey.json', 'Customer Survey');
await formHelpers.importEntries(1, 'sample-entries.json');
});
`typescript
test('Element utilities', async ({ page, elementUtils }) => {
await page.goto('/wp-admin/admin.php?page=gf_edit_forms');
const values = await elementUtils.toValuesAndLabels('select option');
const matches = await elementUtils.matchesOtherInputs('.source', '.target');
const differs = await elementUtils.doesNotMatchOtherInputs('.source', '.target');
});
`
`typescript
import { test as base, expect } from '@playwright/test';
import { createTest } from '@gravitywiz/playwright-plugin-gravity-wiz';
const test = createTest(base);
test.describe('Gravity Wiz Plugin Example', () => {
// Automatic setup: wpCli.bootstrap() and gfCli.resetGF() run before each test
test('should login to WordPress admin', async ({ page, wpAuth, pageHelpers }) => {
await wpAuth.login();
await pageHelpers.see('Dashboard');
await pageHelpers.see('Howdy,');
await expect(page.locator('#wpadminbar')).toBeVisible();
});
test('should import and work with forms', async ({ page, gfCli, formHelpers, wpAuth }) => {
await wpAuth.login();
// Import a form
await formHelpers.importForm('contact-form.json');
const formId = await gfCli.getFormID('Contact Form');
// Navigate to the form page
await page.goto('/contact-form');
// Test form functionality
await page.locator('#input_1_1').fill('John Doe');
await page.locator('#input_1_2').fill('john@example.com');
await page.locator('input[type="submit"]').click();
});
test('should check versions and execute commands', async ({ wpCli, gfCli }) => {
const gfVersion = await gfCli.getGFVersion();
expect(gfVersion).toBeTruthy();
const isModern = await gfCli.isGF25OrNewer();
expect(typeof isModern).toBe('boolean');
const result = await wpCli.execa('wp', ['option', 'get', 'blogname']);
expect(result.exitCode).toBe(0);
});
test('should use element utilities', async ({ page, wpAuth, elementUtils }) => {
await wpAuth.login();
await page.goto('/wp-admin/admin.php?page=gf_edit_forms');
const values = await elementUtils.toValuesAndLabels('select option');
expect(Array.isArray(values)).toBe(true);
});
});
`
The CLI fixtures (wpCli and gfCli) can be used in beforeAll hooks since they don't require a browser page:
`typescript
test.describe('Setup-heavy tests', () => {
test.beforeAll(async ({ wpCli, gfCli }) => {
// These work in beforeAll because they don't need a page
await wpCli.bootstrap();
await gfCli.resetGF();
await gfCli.importForm('complex-form.json');
});
test('should work with pre-imported form', async ({ page, wpAuth }) => {
await wpAuth.login();
// Form is already imported from beforeAll
await page.goto('/complex-form');
});
});
`
If you prefer manual control over setup, you can use the fixtures directly without the automatic bootstrap/resetGF:
`typescript
import { test as base } from '@playwright/test';
import { gravityWizFixture } from '@gravitywiz/playwright-plugin-gravity-wiz';
const test = base.extend(gravityWizFixture(base));
test('manual setup example', async ({ wpCli, gfCli, wpAuth }) => {
// Manual setup - only when you need it
await wpCli.bootstrap();
await gfCli.resetGF();
await wpAuth.login();
// Your test logic here
});
`
- Execute any command
- wpCli.bootstrap() - Prepare WordPress for testing
- wpCli.evalPhp(php) - Execute PHP code via WP-CLI
- wpCli.getOption(name) - Get WordPress option
- wpCli.setOption(name, value) - Set WordPress option$3
- gfCli.resetGF() - Reset Gravity Forms database
- gfCli.getGFVersion() - Get Gravity Forms version
- gfCli.isGF25OrNewer() - Check if GF 2.5+
- gfCli.getFormID(title) - Get form ID by title
- gfCli.importForm(filename, title?) - Import form
- gfCli.importEntries(formID, jsonPath) - Import entries$3
- wpAuth.login(options?) - Login to WordPress
- wpAuth.logout() - Logout from WordPress$3
- pageHelpers.see(text, selector?) - Check if text is visible
- pageHelpers.goToChoicesSettings() - Navigate to GF field choices
- pageHelpers.retryUntil(options) - Retry until condition is met$3
- formHelpers.importForm(filename, title?) - Import form
- formHelpers.importEntries(formID, jsonPath) - Import entries$3
- elementUtils.toValuesAndLabels(selector, trim?) - Extract values/labels
- elementUtils.matchesOtherInputs(selector, otherSelector) - Compare elements
- elementUtils.doesNotMatchOtherInputs(selector, otherSelector) - Compare (negated)Migration from Cypress
The new fixture structure provides better organization:
`typescript
// Old Cypress approach
cy.login();
cy.bootstrap();
cy.resetGF();
cy.importForm('contact-form');
cy.see('Dashboard');// New Playwright approach with focused fixtures
await wpAuth.login();
await wpCli.bootstrap(); // Or automatic via createTest()
await gfCli.resetGF(); // Or automatic via createTest()
await formHelpers.importForm('contact-form');
await pageHelpers.see('Dashboard');
`Migration from Old Playwright Plugin
If you're upgrading from the previous version with the monolithic
gravityWiz fixture:`typescript
// Old approach
test('old way', async ({ gravityWiz }) => {
await gravityWiz.login();
await gravityWiz.bootstrap();
await gravityWiz.see('Dashboard');
});// New approach
test('new way', async ({ wpAuth, pageHelpers }) => {
// bootstrap() runs automatically with createTest()
await wpAuth.login();
await pageHelpers.see('Dashboard');
});
`Prerequisites
$3
- Node.js 18+ and npm/yarn
- WordPress with WP-CLI installed and configured
- Gravity Forms plugin (licensed)
- Gravity Forms CLI plugin (automatically installed by setup)$3
The plugin expects a WordPress environment accessible via WP-CLI. Common setups include:
- Local WordPress installation (XAMPP, MAMP, Local by Flywheel)
- Docker-based WordPress environment
- Remote WordPress site accessible via WP-CLIConfiguration
$3
`typescript
// global-setup.ts
import { globalSetup } from '@gravitywiz/playwright-plugin-gravity-wiz';export default async function () {
return globalSetup({
emailsFolder: './test-emails', // Optional: custom email capture folder
});
}
`$3
`typescript
// playwright.config.ts
import { defineConfig, devices } from '@playwright/test';export default defineConfig({
testDir: './tests',
globalSetup: require.resolve('./global-setup'),
globalTeardown: require.resolve('./global-teardown'),
use: {
baseURL: 'http://localhost', // Your WordPress URL
},
projects: [
{
name: 'chromium',
use: { ...devices['Desktop Chrome'] },
},
],
});
`Troubleshooting
$3
WP-CLI Not Found:
`bash
Ensure WP-CLI is installed and accessible
wp --info
`Permission Errors:
`bash
Fix WordPress file permissions
sudo chown -R www-data:www-data /path/to/wordpress
`Plugin Activation Fails:
- Ensure the plugin files are in the correct WordPress plugins directory
- Check WordPress error logs for detailed error messages
Tests Timeout:
- Increase Playwright timeout settings
- Check if WordPress is accessible at the configured baseURL
- Verify database connectivity
$3
Enable debug logging:`typescript
test('debug example', async ({ wpCli }) => {
// Your test code here // Check for errors after test
const log = await wpCli.evalPhp('return file_get_contents(WP_CONTENT_DIR . "/debug.log");');
console.log('Debug log:', log.stdout);
});
`Performance Optimization
$3
Playwright supports parallel test execution:`typescript
// playwright.config.ts
export default defineConfig({
workers: process.env.CI ? 1 : undefined, // Adjust based on your setup
fullyParallel: true,
});
`$3
Each test gets a fresh browser context, and WordPress state is automatically reset with createTest(). For manual control:`typescript
test.beforeEach(async ({ wpCli, gfCli }) => {
await wpCli.bootstrap(); // Reset WordPress state
await gfCli.resetGF(); // Reset Gravity Forms
});
`Development
`bash
Install dependencies
yarn installBuild the plugin
yarn buildRun in development mode
yarn dev
``GPL-2.0-or-later