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
25 changes: 16 additions & 9 deletions src/utils/run-ability.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,30 +95,37 @@ const buildFetchOptions = (
input: AbilityInput,
method: Method
) => {
const normalizedInput = input ?? null;

if ( method === 'GET' || method === 'DELETE' ) {
return {
path:
normalizedInput === null
input === undefined || input === null
? `/wp-abilities/v1/abilities/${ ability }/run`
: addQueryArgs(
`/wp-abilities/v1/abilities/${ ability }/run`,
{
input: normalizedInput,
input,
}
),
method,
};
}

return {
const options: {
path: string;
method: 'POST';
data?: {
input: AbilityInput;
};
} = {
path: `/wp-abilities/v1/abilities/${ ability }/run`,
method: 'POST' as const,
data: {
input: normalizedInput,
},
};

if ( input !== undefined ) {
options.data = { input };
}

return options;
};

export async function runAbility< T = unknown >(
Expand All @@ -131,7 +138,7 @@ export async function runAbility< T = unknown >(
if ( typeof abilitiesModule?.executeAbility === 'function' ) {
return ( await abilitiesModule.executeAbility(
ability,
input ?? null
input
) ) as T;
}

Expand Down
82 changes: 82 additions & 0 deletions tests/unit/run-ability.test.ts

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't currently have any unit tests for our TS files. While I'm not opposed to getting that added, that will require more than just a single test file, we'll need all the underlying code to run those tests as well (and get those set up in CI). May be worth doing that in a separate PR instead of rolling into this one

Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
/**
* WordPress dependencies
*/
import { executeAbility } from '@wordpress/abilities';
import apiFetch from '@wordpress/api-fetch';

/**
* Internal dependencies
*/
import { runAbility } from '../../src/utils/run-ability';

jest.mock( '@wordpress/api-fetch', () => jest.fn() );
jest.mock( '@wordpress/core-abilities', () => ( {
ready: Promise.resolve(),
} ) );
jest.mock( '@wordpress/abilities', () => ( {
executeAbility: jest.fn(),
} ) );

describe( 'runAbility', () => {
const mockedApiFetch = apiFetch as jest.Mock;
const mockedExecuteAbility = executeAbility as jest.Mock;

beforeEach( () => {
jest.clearAllMocks();
jest.spyOn( console, 'error' ).mockImplementation( () => undefined );
jest.spyOn( console, 'warn' ).mockImplementation( () => undefined );

mockedApiFetch.mockResolvedValue( 'fallback result' );
mockedExecuteAbility.mockResolvedValue( 'client result' );
} );

afterEach( () => {
jest.restoreAllMocks();
} );

it( 'passes omitted input to the client ability as undefined', async () => {
const result = await runAbility( 'core/users' );

expect( result ).toBe( 'client result' );
expect( mockedExecuteAbility ).toHaveBeenCalledWith(
'core/users',
undefined
);
expect( mockedApiFetch ).not.toHaveBeenCalled();
} );

it( 'omits REST input data when fallback runs without input', async () => {
mockedExecuteAbility.mockRejectedValueOnce(
Object.assign( new Error( 'Ability not found' ), {
code: 'ability_not_found',
} )
);

const result = await runAbility( 'core/users' );

expect( result ).toBe( 'fallback result' );
expect( mockedApiFetch ).toHaveBeenCalledWith( {
path: '/wp-abilities/v1/abilities/core/users/run',
method: 'POST',
} );
} );

it( 'keeps explicit empty-object input in REST fallback data', async () => {
mockedExecuteAbility.mockRejectedValueOnce(
Object.assign( new Error( 'Ability not found' ), {
code: 'ability_not_found',
} )
);

const result = await runAbility( 'core/users', {} );

expect( result ).toBe( 'fallback result' );
expect( mockedApiFetch ).toHaveBeenCalledWith( {
path: '/wp-abilities/v1/abilities/core/users/run',
method: 'POST',
data: {
input: {},
},
} );
} );
} );
Loading