Skip to content
Open
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
2 changes: 2 additions & 0 deletions .github/.codecov.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ ignore:
- '**/*.spec.tsx'
- '**/types.d.ts'
- 'playwright.config.ts'
- 'assets/src/components/icons/**'
- 'assets/src/js/**.js'

comment:
layout: 'reach,diff,flags,files'
Expand Down
4 changes: 4 additions & 0 deletions jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,10 @@ module.exports = {
'!assets/src/**/index.{js,tsx,jsx}',
// Exclude style files
'!assets/src/**/*.{css,scss}',
// Exclude static SVG icon components
'!assets/src/components/icons/**',
// Exclude the js files
'!assets/src/js/**.js',
],

// Coverage output directory
Expand Down
6 changes: 6 additions & 0 deletions playwright.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,12 @@ const config = defineConfig( {
...baseConfig,
testDir: './tests/e2e',
outputDir: './tests/_output/e2e',
webServer: {
command: 'npm run wp-env:test start',
port: 8889,
timeout: 120000,
reuseExistingServer: true,
},
} );

export default config;
67 changes: 67 additions & 0 deletions tests/e2e/settings/activation.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/**
* WordPress dependencies
*/
import { expect, test } from '@wordpress/e2e-test-utils-playwright';

test.describe( 'plugin activation', () => {
test( 'should activate and deactivate the plugin', async ( {
admin,
page,
} ) => {
await admin.visitAdminPage( '/plugins.php' );

// Helper to dismiss the onboarding modal if present.
const dismissOnboardingModal = async () => {
const modal = page.locator( '#oneupdate-site-selection-modal' );
const backdrop = page.locator(
'body.oneupdate-site-selection-modal'
);

if ( await modal.isVisible() ) {
await modal.evaluate( ( el ) => {
el.remove();
} );
}

if ( await backdrop.isVisible() ) {
await backdrop.evaluate( ( el ) => {
el.classList.remove( 'oneupdate-site-selection-modal' );
} );
}
};

const pluginRow = page.locator(
'tr[data-plugin="oneupdate/oneupdate.php"]'
);
await expect( pluginRow ).toBeVisible();

// Dismiss modal before interacting with plugin row.
await dismissOnboardingModal();

const activateLink = pluginRow.locator( 'a', { hasText: 'Activate' } );

await Promise.all( [
page.waitForURL( /plugins.php/ ),
activateLink.click(),
] );

await expect(
pluginRow.locator( 'a', { hasText: 'Deactivate' } )
).toBeVisible( { timeout: 10000 } );

// Dismiss modal again after activation.
await dismissOnboardingModal();

const deactivateLink = pluginRow.locator( 'a', {
hasText: 'Deactivate',
} );
await Promise.all( [
page.waitForURL( /plugins.php/ ),
deactivateLink.click(),
] );

await expect(
pluginRow.locator( 'a', { hasText: 'Activate' } )
).toBeVisible( { timeout: 10000 } );
} );
} );
96 changes: 96 additions & 0 deletions tests/js/OnboardingPage.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
/**
* External dependencies
*/
import { fireEvent, render, screen, waitFor } from '@testing-library/react';
/**
* WordPress dependencies
*/
import apiFetch from '@wordpress/api-fetch';

import OnboardingScreen from '@/admin/onboarding/page';

const mockedApiFetch = apiFetch as jest.MockedFunction< typeof apiFetch >;

describe( 'OnboardingScreen', () => {
beforeEach( () => {
mockedApiFetch.mockReset();
window.OneUpdateOnboarding = {
nonce: 'onboarding-nonce',
site_type: '',
setup_url: '/wp-admin/admin.php?page=oneupdate-settings',
};
} );

it( 'loads the current site type from settings', async () => {
mockedApiFetch.mockResolvedValueOnce( {
oneupdate_site_type: 'brand-site',
} );

render( <OnboardingScreen /> );

await waitFor( () => {
expect( screen.getByLabelText( 'Site Type' ) ).toHaveValue(
'brand-site'
);
} );
} );

it( 'shows an error notice when loading fails', async () => {
mockedApiFetch.mockRejectedValueOnce( new Error( 'load failed' ) );

render( <OnboardingScreen /> );

// WordPress renders notices in both accessibility region and visible content
expect(
await screen.findAllByText( 'Error fetching site type.' )
).toHaveLength( 2 );
} );

it( 'saves the chosen site type and redirects to setup page', async () => {
mockedApiFetch.mockResolvedValueOnce( {} ).mockResolvedValueOnce( {
oneupdate_site_type: 'governing-site',
} );

render( <OnboardingScreen /> );

const selectControl = await screen.findByLabelText( 'Site Type' );
fireEvent.change( selectControl, {
target: { value: 'governing-site' },
} );

const submitButton = screen.getByRole( 'button', {
name: 'Select Current Site Type',
} );
fireEvent.click( submitButton );

await waitFor( () => {
expect( mockedApiFetch ).toHaveBeenLastCalledWith( {
path: '/wp/v2/settings',
method: 'POST',
data: { oneupdate_site_type: 'governing-site' },
} );
} );
} );

it( 'shows an error notice when saving fails', async () => {
mockedApiFetch
.mockResolvedValueOnce( {} )
.mockRejectedValueOnce( new Error( 'save failed' ) );

render( <OnboardingScreen /> );

const selectControl = await screen.findByLabelText( 'Site Type' );
fireEvent.change( selectControl, {
target: { value: 'brand-site' },
} );

const submitButton = screen.getByRole( 'button', {
name: 'Select Current Site Type',
} );
fireEvent.click( submitButton );

expect(
await screen.findAllByText( 'Error setting site type.' )
).toHaveLength( 2 );
} );
} );
Loading
Loading