diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 0000000..6c1047d --- /dev/null +++ b/.eslintignore @@ -0,0 +1,9 @@ +vendor +node_modules +build + +.psalm +.github + +./coverage +./sources/server diff --git a/.eslintrc.js b/.eslintrc.js index 40d713f..9c60ba6 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -1,14 +1,15 @@ -const baseConfiguration = require('@wordpress/scripts/config/.eslintrc.js'); - module.exports = { - ...baseConfiguration, - ignorePatterns: [ - ...(baseConfiguration.ignorePatterns ?? []), - '**/sources/server/**/*.js', - ], + extends: [ 'plugin:@wordpress/eslint-plugin/recommended' ], + ignorePatterns: [ '**/sources/server/**/*.js' ], rules: { - ...baseConfiguration.rules, - '@typescript-eslint/array-type': ['error', { default: 'generic' }], + '@wordpress/dependency-group': 'error', + '@wordpress/i18n-text-domain': [ + 'error', + { + allowedTextDomain: 'wp-entities-search', + }, + ], + '@typescript-eslint/array-type': [ 'error', { default: 'generic' } ], }, settings: { 'import/resolver': { diff --git a/.github/workflows/js-qa.yml b/.github/workflows/js-qa.yml index 88b8fe2..0d31d1a 100644 --- a/.github/workflows/js-qa.yml +++ b/.github/workflows/js-qa.yml @@ -31,9 +31,6 @@ jobs: - name: Install run: yarn install - - name: Coding Style - run: yarn cs - - name: Linting JavaScript run: yarn lint:js diff --git a/.prettierignore b/.prettierignore index c4b8ffc..7fb5221 100644 --- a/.prettierignore +++ b/.prettierignore @@ -5,3 +5,4 @@ ./vendor ./node_modules ./build +./sources/server diff --git a/.prettierrc.js b/.prettierrc.js deleted file mode 100644 index 374f486..0000000 --- a/.prettierrc.js +++ /dev/null @@ -1,16 +0,0 @@ -const prettierConfig = require('@wordpress/scripts/config/.prettierrc.js'); - -module.exports = { - ...prettierConfig, - importOrder: [ - '', - '^@jest/(.*)$', - '^@testing-library/(.*)$', - '^@faker-js/faker', - '^@wordpress/(.*)$', - '^[./]', - ], - importOrderSeparation: true, - importOrderSortSpecifiers: false, - importOrderCaseInsensitive: false, -}; diff --git a/jest.config.ts b/jest.config.ts index 1cdd81d..fab30c2 100644 --- a/jest.config.ts +++ b/jest.config.ts @@ -3,7 +3,7 @@ module.exports = { clearMocks: true, preset: 'ts-jest', testEnvironment: 'jsdom', - moduleDirectories: ['node_modules'], - setupFilesAfterEnv: ['/tests/client/setup-tests.ts'], + moduleDirectories: [ 'node_modules' ], + setupFilesAfterEnv: [ '/tests/client/setup-tests.ts' ], maxWorkers: 8, }; diff --git a/package.json b/package.json index 1f61589..0f7d037 100644 --- a/package.json +++ b/package.json @@ -15,15 +15,12 @@ "@testing-library/react": "^14.0.0", "@testing-library/user-event": "^14.5.1", "@total-typescript/shoehorn": "^0.1.0", - "@trivago/prettier-plugin-sort-imports": "^4.0.0", "@wordpress/dependency-extraction-webpack-plugin": "^4.8.0", "@wordpress/env": "^5.9.0", - "@wordpress/eslint-plugin": "^14.8.0", "@wordpress/scripts": "^26.0.0", "eslint-import-resolver-typescript": "^3.5.5", "jest": "^29.4.3", "jest-environment-jsdom": "^29.5.0", - "prettier": "^2.8.1", "ts-jest": "^29.0.5", "ts-loader": "^9.4.2", "ts-node": "^10.9.1", @@ -46,10 +43,9 @@ "wp-env": "wp-env", "build": "wp-scripts build --webpack-src-dir=./sources/client/src", "build:dev": "wp-scripts start --webpack--devtool=inline-source-map --webpack-src-dir=./sources/client/src", - "cs": "yarn prettier --check ./sources/client", "cs:fix": "wp-scripts format ./sources/client", - "lint:js": "wp-scripts lint-js", - "lint:js:fix": "wp-scripts lint-js --fix", + "lint:js": "wp-scripts lint-js ./sources/client", + "lint:js:fix": "wp-scripts lint-js --fix ./sources/client", "test": "jest", "test:update-snapshots": "yarn test -u" } diff --git a/sources/client/src/api/fetch.ts b/sources/client/src/api/fetch.ts index c7f419f..470e4dd 100644 --- a/sources/client/src/api/fetch.ts +++ b/sources/client/src/api/fetch.ts @@ -1,3 +1,6 @@ +/** + * WordPress dependencies + */ import apiFetch from '@wordpress/api-fetch'; /** diff --git a/sources/client/src/api/search-entities.ts b/sources/client/src/api/search-entities.ts index 242bca8..d49436e 100644 --- a/sources/client/src/api/search-entities.ts +++ b/sources/client/src/api/search-entities.ts @@ -1,27 +1,36 @@ +/** + * External dependencies + */ import EntitiesSearch from '@types'; +/** + * WordPress dependencies + */ import { doAction } from '@wordpress/hooks'; +/** + * Internal dependencies + */ import { abortControllers } from '../services/abort-controllers'; import { ContextualAbortController } from '../services/contextual-abort-controller'; import { Set } from '../vo/set'; import { fetch } from './fetch'; -export async function searchEntities( +export async function searchEntities< E >( type: string, - subtype: Set, + subtype: Set< string >, phrase: string, - queryArguments?: EntitiesSearch.QueryArguments -): Promise> { + queryArguments?: EntitiesSearch.QueryArguments< string > +): Promise< Set< E > > { const { exclude, include, - fields = ['title', 'id'], + fields = [ 'title', 'id' ], ...restArguments } = queryArguments ?? {}; // @ts-ignore we need to pass string[] to the URLSearchParams - const params = new URLSearchParams({ + const params = new URLSearchParams( { per_page: '10', order: 'DESC', orderBy: 'title', @@ -33,30 +42,30 @@ export async function searchEntities( type, search: phrase, subtype: subtype.toArray(), - _fields: serializeFields(fields), - }).toString(); + _fields: serializeFields( fields ), + } ).toString(); const controller = abortControllers.add( new ContextualAbortController( params, - `Request aborted with parameters: ${params}` + `Request aborted with parameters: ${ params }` ) ); - const entities = await fetch>({ - path: `?rest_route=/wp/v2/search&${params}`, + const entities = await fetch< ReadonlyArray< E > >( { + path: `?rest_route=/wp/v2/search&${ params }`, signal: controller?.signal() ?? null, - }).catch((error) => { - if (error instanceof DOMException && error.name === 'AbortError') { - doAction('wp-entities-search.on-search.abort', error); + } ).catch( ( error ) => { + if ( error instanceof DOMException && error.name === 'AbortError' ) { + doAction( 'wp-entities-search.on-search.abort', error ); } throw error; - }); + } ); - return new Set(entities); + return new Set( entities ); } -function serializeFields(fields: EntitiesSearch.SearchQueryFields): string { - return fields.join(','); +function serializeFields( fields: EntitiesSearch.SearchQueryFields ): string { + return fields.join( ',' ); } diff --git a/sources/client/src/components/composite-entities-by-kind.tsx b/sources/client/src/components/composite-entities-by-kind.tsx index 8519b89..9c1e28f 100644 --- a/sources/client/src/components/composite-entities-by-kind.tsx +++ b/sources/client/src/components/composite-entities-by-kind.tsx @@ -1,8 +1,17 @@ +/** + * External dependencies + */ import type EntitiesSearch from '@types'; import React, { JSX } from 'react'; +/** + * WordPress dependencies + */ import { doAction } from '@wordpress/hooks'; +/** + * Internal dependencies + */ import { useEntitiesOptionsStorage } from '../hooks/use-entities-options-storage'; import { useSearch } from '../hooks/use-search'; import { orderSelectedOptionsAtTheTop } from '../utils/order-selected-options-at-the-top'; @@ -15,99 +24,102 @@ import { Set } from '../vo/set'; * @public * @param props The component props. */ -export function CompositeEntitiesByKind( - props: EntitiesSearch.CompositeEntitiesKinds +export function CompositeEntitiesByKind< E, K >( + props: EntitiesSearch.CompositeEntitiesKinds< E, K > ): JSX.Element { - const [state, dispatch] = useEntitiesOptionsStorage( + const [ state, dispatch ] = useEntitiesOptionsStorage< E, K >( { entities: props.entities.value, kind: props.kind.value, }, props.searchEntities ); - const search = useSearch( + const search = useSearch< E, K >( props.searchEntities, state.kind, state.entities, dispatch ); - const onChangeEntities = (entities: EntitiesSearch.Entities) => { - props.entities.onChange(entities); + const onChangeEntities = ( entities: EntitiesSearch.Entities< E > ) => { + props.entities.onChange( entities ); - if (entities.length() <= 0) { - dispatch({ + if ( entities.length() <= 0 ) { + dispatch( { type: 'UPDATE_SELECTED_ENTITIES_OPTIONS', selectedEntitiesOptions: new Set(), - }); + } ); return; } - Promise.all([ - props.searchEntities(state.searchPhrase, state.kind, { + Promise.all( [ + props.searchEntities( state.searchPhrase, state.kind, { exclude: entities, - }), - props.searchEntities('', state.kind, { + } ), + props.searchEntities( '', state.kind, { include: entities, per_page: '-1', - }), - ]) - .then((result) => { - const currentEntitiesOptions = result[0] ?? new Set(); - const selectedEntitiesOptions = result[1] ?? new Set(); + } ), + ] ) + .then( ( result ) => { + const currentEntitiesOptions = result[ 0 ] ?? new Set(); + const selectedEntitiesOptions = result[ 1 ] ?? new Set(); - dispatch({ + dispatch( { type: 'UPDATE_SELECTED_ENTITIES_OPTIONS', selectedEntitiesOptions, - }); - dispatch({ + } ); + dispatch( { type: 'UPDATE_CURRENT_ENTITIES_OPTIONS', currentEntitiesOptions, - }); - }) - .catch((error) => { - doAction('wp-entities-search.on-change-entities.error', error); - }); + } ); + } ) + .catch( ( error ) => { + doAction( + 'wp-entities-search.on-change-entities.error', + error + ); + } ); }; - const onChangeKind = (kind: EntitiesSearch.Kind) => { - const _kind = kind instanceof Set ? kind : new Set([kind]); - const emptySet = new Set(); + const onChangeKind = ( kind: EntitiesSearch.Kind< K > ) => { + const _kind = kind instanceof Set ? kind : new Set( [ kind ] ); + const emptySet = new Set< any >(); - props.kind.onChange(_kind); - props.entities.onChange(emptySet); + props.kind.onChange( _kind ); + props.entities.onChange( emptySet ); - if (_kind.length() <= 0) { - dispatch({ + if ( _kind.length() <= 0 ) { + dispatch( { type: 'CLEAN_ENTITIES_OPTIONS', - }); - dispatch({ type: 'UPDATE_KIND', kind: _kind }); + } ); + dispatch( { type: 'UPDATE_KIND', kind: _kind } ); return; } props - .searchEntities(state.searchPhrase, _kind, { + .searchEntities( state.searchPhrase, _kind, { exclude: state.entities, - }) - .then((entitiesOptions) => { - dispatch({ + } ) + .then( ( entitiesOptions ) => { + dispatch( { type: 'UPDATE_ENTITIES_OPTIONS_FOR_NEW_KIND', entitiesOptions, kind: _kind, - }); - }) - .catch((error) => { - dispatch({ + } ); + } ) + .catch( ( error ) => { + dispatch( { type: 'CLEAN_ENTITIES_OPTIONS', - }); - doAction('wp-entities-search.on-change-kind.error', error); - }); + } ); + doAction( 'wp-entities-search.on-change-kind.error', error ); + } ); }; - const entities: EntitiesSearch.BaseControl = { + const entities: EntitiesSearch.BaseControl< E > = { ...props.entities, value: state.entities, - options: orderSelectedOptionsAtTheTop( + options: orderSelectedOptionsAtTheTop< E >( uniqueControlOptions( state.selectedEntitiesOptions.concat( state.currentEntitiesOptions @@ -118,11 +130,11 @@ export function CompositeEntitiesByKind( onChange: onChangeEntities, }; - const kind: EntitiesSearch.BaseControl = { + const kind: EntitiesSearch.BaseControl< K > = { ...props.kind, value: state.kind, onChange: onChangeKind, }; - return <>{props.children(entities, kind, search)}; + return <>{ props.children( entities, kind, search ) }; } diff --git a/sources/client/src/components/no-options-message.tsx b/sources/client/src/components/no-options-message.tsx index c298471..e596a4f 100644 --- a/sources/client/src/components/no-options-message.tsx +++ b/sources/client/src/components/no-options-message.tsx @@ -1,11 +1,17 @@ +/** + * External dependencies + */ import React, { JSX } from 'react'; +/** + * WordPress dependencies + */ import { __ } from '@wordpress/i18n'; export function NoOptionsMessage(): JSX.Element { return (

- {__('No options', 'wp-entities-search')} + { __( 'No options', 'wp-entities-search' ) }

); } diff --git a/sources/client/src/components/plural-select-control.tsx b/sources/client/src/components/plural-select-control.tsx index 424a766..a3cbebb 100644 --- a/sources/client/src/components/plural-select-control.tsx +++ b/sources/client/src/components/plural-select-control.tsx @@ -1,58 +1,66 @@ +/** + * External dependencies + */ import EntitiesSearch from '@types'; import classnames from 'classnames'; import React, { JSX } from 'react'; +/** + * Internal dependencies + */ import { Set } from '../vo/set'; import { NoOptionsMessage } from './no-options-message'; export function PluralSelectControl( - props: EntitiesSearch.BaseControl & { + props: EntitiesSearch.BaseControl< EntitiesSearch.Value > & { className?: string; } ): JSX.Element { - const [selected, setSelected] = React.useState(props.value); + const [ selected, setSelected ] = React.useState( props.value ); const className = classnames( props.className, 'wes-select-control', 'wes-select-control--plural' ); - const onChange = (event: React.ChangeEvent) => { - if (event.target.selectedOptions.length <= 0) { - props.onChange(new Set()); + const onChange = ( event: React.ChangeEvent< HTMLSelectElement > ) => { + if ( event.target.selectedOptions.length <= 0 ) { + props.onChange( new Set() ); } - const selectedOptions = Array.from(event.target.selectedOptions).map( - (option) => option.value + const selectedOptions = Array.from( event.target.selectedOptions ).map( + ( option ) => option.value ); const selectedValues = props.options - .filter((option) => selectedOptions.includes(String(option.value))) - .map((option) => option.value); + .filter( ( option ) => + selectedOptions.includes( String( option.value ) ) + ) + .map( ( option ) => option.value ); - setSelected(selectedValues); - props.onChange(selectedValues); + setSelected( selectedValues ); + props.onChange( selectedValues ); }; - if (props.options.length() <= 0) { + if ( props.options.length() <= 0 ) { return ; } return ( ); } diff --git a/sources/client/src/components/radio-control.tsx b/sources/client/src/components/radio-control.tsx index fa44646..8db5ed7 100644 --- a/sources/client/src/components/radio-control.tsx +++ b/sources/client/src/components/radio-control.tsx @@ -1,67 +1,73 @@ +/** + * External dependencies + */ import EntitiesSearch from '@types'; import classnames from 'classnames'; import React, { JSX } from 'react'; +/** + * Internal dependencies + */ import { useId } from '../hooks/use-id'; -interface Option extends EntitiesSearch.ControlOption { - readonly selectedValue: EntitiesSearch.SingularControl['value']; - readonly onChange: (event: React.ChangeEvent) => void; +interface Option< V > extends EntitiesSearch.ControlOption< V > { + readonly selectedValue: EntitiesSearch.SingularControl< V >[ 'value' ]; + readonly onChange: ( event: React.ChangeEvent< HTMLInputElement > ) => void; } export function RadioControl( - props: EntitiesSearch.SingularControl & { + props: EntitiesSearch.SingularControl< EntitiesSearch.Value > & { className?: string; id?: string; } ): JSX.Element { - const className = classnames(props.className, 'wes-radio-control'); + const className = classnames( props.className, 'wes-radio-control' ); - const onChange = (event: React.ChangeEvent) => { + const onChange = ( event: React.ChangeEvent< HTMLInputElement > ) => { const { target } = event; const valueOption = props.options.find( - (option) => String(option.value) === target.value + ( option ) => String( option.value ) === target.value ); - if (!valueOption) { + if ( ! valueOption ) { return; } - props.onChange(valueOption.value); + props.onChange( valueOption.value ); }; return ( -
- {props.options.map((option) => ( +
+ { props.options.map( ( option ) => (
); } -function Option(props: Option): JSX.Element { +function Option< V >( props: Option< V > ): JSX.Element { const id = useId(); - const value = String(props.value); + const value = String( props.value ); return (
-
); diff --git a/sources/client/src/components/search-control.tsx b/sources/client/src/components/search-control.tsx index ee28471..c55cb58 100644 --- a/sources/client/src/components/search-control.tsx +++ b/sources/client/src/components/search-control.tsx @@ -1,20 +1,29 @@ +/** + * External dependencies + */ import EntitiesSearch from '@types'; import React, { JSX } from 'react'; +/** + * WordPress dependencies + */ import { __ } from '@wordpress/i18n'; +/** + * Internal dependencies + */ import { useId } from '../hooks/use-id'; export function SearchControl( props: EntitiesSearch.SearchControl ): JSX.Element { - const id = useId(props.id); - const label = props.label || __('Search', 'wp-entities-search'); - const [searchValue, setSearchValue] = React.useState(''); + const id = useId( props.id ); + const label = props.label || __( 'Search', 'wp-entities-search' ); + const [ searchValue, setSearchValue ] = React.useState( '' ); - const onChange = (event: React.ChangeEvent) => { - setSearchValue(event.target.value); - props.onChange(event.target.value); + const onChange = ( event: React.ChangeEvent< HTMLInputElement > ) => { + setSearchValue( event.target.value ); + props.onChange( event.target.value ); }; const inputProps = { @@ -26,9 +35,9 @@ export function SearchControl( return (
-
); diff --git a/sources/client/src/components/singular-select-control.tsx b/sources/client/src/components/singular-select-control.tsx index a361b59..8a6f7a8 100644 --- a/sources/client/src/components/singular-select-control.tsx +++ b/sources/client/src/components/singular-select-control.tsx @@ -1,11 +1,17 @@ +/** + * External dependencies + */ import EntitiesSearch from '@types'; import classnames from 'classnames'; import React, { JSX } from 'react'; +/** + * Internal dependencies + */ import { NoOptionsMessage } from './no-options-message'; export function SingularSelectControl( - props: EntitiesSearch.SingularControl & { + props: EntitiesSearch.SingularControl< string > & { className?: string; } ): JSX.Element { @@ -15,34 +21,38 @@ export function SingularSelectControl( 'wes-select-control--singular' ); - if (props.options.length() <= 0) { + if ( props.options.length() <= 0 ) { return ; } - const onChange = (event: React.ChangeEvent) => { + const onChange = ( event: React.ChangeEvent< HTMLSelectElement > ) => { const { target } = event; const valueOption = props.options.find( - (option) => String(option.value) === target.value + ( option ) => String( option.value ) === target.value ); - if (!valueOption) { + if ( ! valueOption ) { return; } - props.onChange(valueOption.value); + props.onChange( valueOption.value ); }; return ( - + { props.options.map( ( option ) => ( - ))} + ) ) } ); } diff --git a/sources/client/src/components/toggle-control.tsx b/sources/client/src/components/toggle-control.tsx index a41f42a..d820eab 100644 --- a/sources/client/src/components/toggle-control.tsx +++ b/sources/client/src/components/toggle-control.tsx @@ -1,77 +1,83 @@ +/** + * External dependencies + */ import EntitiesSearch from '@types'; import classnames from 'classnames'; import React, { JSX } from 'react'; +/** + * Internal dependencies + */ import { useId } from '../hooks/use-id'; import { NoOptionsMessage } from './no-options-message'; -interface Option extends EntitiesSearch.ControlOption { - readonly selectedValues: EntitiesSearch.BaseControl['value']; - readonly onChange: (event: React.ChangeEvent) => void; +interface Option< V > extends EntitiesSearch.ControlOption< V > { + readonly selectedValues: EntitiesSearch.BaseControl< V >[ 'value' ]; + readonly onChange: ( event: React.ChangeEvent< HTMLInputElement > ) => void; } export function ToggleControl( - props: EntitiesSearch.BaseControl & { + props: EntitiesSearch.BaseControl< EntitiesSearch.Value > & { className?: string; } ): JSX.Element { - const className = classnames(props.className, 'wes-toggle-control'); + const className = classnames( props.className, 'wes-toggle-control' ); - if (props.options.length() <= 0) { + if ( props.options.length() <= 0 ) { return ; } - const onChange = (event: React.ChangeEvent) => { + const onChange = ( event: React.ChangeEvent< HTMLInputElement > ) => { const { target } = event; const valueOption = props.options.find( - (option) => String(option.value) === target.value + ( option ) => String( option.value ) === target.value ); - if (!valueOption) { + if ( ! valueOption ) { return; } - if (target.checked) { - props.onChange(props.value.add(valueOption.value)); + if ( target.checked ) { + props.onChange( props.value.add( valueOption.value ) ); return; } - props.onChange(props.value.delete(valueOption.value)); + props.onChange( props.value.delete( valueOption.value ) ); }; return ( -
- {props.options.map((option) => ( - - key={option.value} - label={option.label} - value={option.value} - selectedValues={props.value} - onChange={onChange} +
+ { props.options.map( ( option ) => ( + + key={ option.value } + label={ option.label } + value={ option.value } + selectedValues={ props.value } + onChange={ onChange } /> - ))} + ) ) }
); } -function Option(props: Option): JSX.Element { +function Option< V >( props: Option< V > ): JSX.Element { const id = useId(); - const value = String(props.value); + const value = String( props.value ); return (
-
); diff --git a/sources/client/src/hooks/use-entities-options-storage.ts b/sources/client/src/hooks/use-entities-options-storage.ts index 4963642..0736a53 100644 --- a/sources/client/src/hooks/use-entities-options-storage.ts +++ b/sources/client/src/hooks/use-entities-options-storage.ts @@ -1,15 +1,24 @@ +/** + * External dependencies + */ import EntitiesSearch from '@types'; import { Reducer, Dispatch, useReducer, useEffect } from 'react'; +/** + * WordPress dependencies + */ import { doAction } from '@wordpress/hooks'; +/** + * Internal dependencies + */ import { makeInitialState } from '../storage/entities/initial-state'; import { reducer } from '../storage/entities/reducer'; import { Set } from '../vo/set'; -type _Reducer = Reducer< - EntitiesSearch.EntitiesState, - EntitiesSearch.StoreAction +type _Reducer< E, K > = Reducer< + EntitiesSearch.EntitiesState< E, K >, + EntitiesSearch.StoreAction< E, K > >; /** @@ -17,57 +26,60 @@ type _Reducer = Reducer< * @param initialState The initial state configuration * @param searchEntities The function that will search the entities */ -export function useEntitiesOptionsStorage( - initialState: Pick, 'entities' | 'kind'>, - searchEntities: EntitiesSearch.SearchEntitiesFunction +export function useEntitiesOptionsStorage< E, K >( + initialState: Pick< + EntitiesSearch.EntitiesState< E, K >, + 'entities' | 'kind' + >, + searchEntities: EntitiesSearch.SearchEntitiesFunction< E, K > ): Readonly< [ - EntitiesSearch.EntitiesState, - Dispatch> + EntitiesSearch.EntitiesState< E, K >, + Dispatch< EntitiesSearch.StoreAction< E, K > >, ] > { - const [state, dispatch] = useReducer<_Reducer>( + const [ state, dispatch ] = useReducer< _Reducer< E, K > >( reducer, - makeInitialState(initialState) + makeInitialState< E, K >( initialState ) ); - useEffect(() => { - Promise.all([ - searchEntities('', state.kind, { + useEffect( () => { + Promise.all( [ + searchEntities( '', state.kind, { exclude: state.entities, - }), + } ), state.entities.length() > 0 - ? searchEntities('', state.kind, { + ? searchEntities( '', state.kind, { include: state.entities, per_page: '-1', - }) + } ) : undefined, - ]) - .then((result) => { - const currentEntitiesOptions = result[0] ?? new Set(); - const selectedEntitiesOptions = result[1] ?? new Set(); + ] ) + .then( ( result ) => { + const currentEntitiesOptions = result[ 0 ] ?? new Set(); + const selectedEntitiesOptions = result[ 1 ] ?? new Set(); - dispatch({ + dispatch( { type: 'UPDATE_SELECTED_ENTITIES_OPTIONS', selectedEntitiesOptions, - }); - dispatch({ + } ); + dispatch( { type: 'UPDATE_CONTEXTUAL_ENTITIES_OPTIONS', contextualEntitiesOptions: currentEntitiesOptions, - }); - dispatch({ + } ); + dispatch( { type: 'UPDATE_CURRENT_ENTITIES_OPTIONS', currentEntitiesOptions, - }); - }) - .catch((error) => { + } ); + } ) + .catch( ( error ) => { doAction( 'wp-entities-search.on-storage-initialization.error', error ); - }); + } ); // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); + }, [] ); - return [state, dispatch] as const; + return [ state, dispatch ] as const; } diff --git a/sources/client/src/hooks/use-entity-records.ts b/sources/client/src/hooks/use-entity-records.ts index 655dee9..4725fcc 100644 --- a/sources/client/src/hooks/use-entity-records.ts +++ b/sources/client/src/hooks/use-entity-records.ts @@ -1,7 +1,16 @@ +/** + * External dependencies + */ import EntitiesSearch from '@types'; +/** + * WordPress dependencies + */ import { useEntityRecords as useCoreEntityRecords } from '@wordpress/core-data'; +/** + * Internal dependencies + */ import { Set } from '../vo/set'; enum ResolveStatus { @@ -20,27 +29,27 @@ enum ResolveStatus { * @param name The name of the entity to fetch. E.g. 'post', 'page', 'category', etc. * @param queryArgs The query args to pass to the entity fetch. E.g. { per_page: 100 } */ -export function useEntityRecords( +export function useEntityRecords< Entity >( kind: string, name: string, - queryArgs: Record = {} -): EntitiesSearch.EntitiesRecords { - const entities = useCoreEntityRecords(kind, name, queryArgs); + queryArgs: Record< string, unknown > = {} +): EntitiesSearch.EntitiesRecords< Entity > { + const entities = useCoreEntityRecords< Entity >( kind, name, queryArgs ); const status = entities.status as any as ResolveStatus; - return Object.freeze({ - records: () => new Set(entities.records ?? []), + return Object.freeze( { + records: () => new Set( entities.records ?? [] ), isResolving: () => entities.isResolving && - !entities.hasResolved && + ! entities.hasResolved && status === ResolveStatus.RESOLVING, - errored: () => makeStatusCallback(entities, ResolveStatus.ERROR), - succeed: () => makeStatusCallback(entities, ResolveStatus.SUCCESS), - }); + errored: () => makeStatusCallback( entities, ResolveStatus.ERROR ), + succeed: () => makeStatusCallback( entities, ResolveStatus.SUCCESS ), + } ); } -function makeStatusCallback( - entities: ReturnType>, +function makeStatusCallback< Entity >( + entities: ReturnType< typeof useCoreEntityRecords< Entity > >, status: string ): boolean { return entities?.hasResolved && entities?.status === status; diff --git a/sources/client/src/hooks/use-id.ts b/sources/client/src/hooks/use-id.ts index 4d9e583..faf400b 100644 --- a/sources/client/src/hooks/use-id.ts +++ b/sources/client/src/hooks/use-id.ts @@ -1,6 +1,9 @@ +/** + * External dependencies + */ import React from 'react'; -export function useId(maybeId?: string): string { +export function useId( maybeId?: string ): string { const fallbackId = React.useId(); return maybeId ?? fallbackId; } diff --git a/sources/client/src/hooks/use-query-viewable-post-types.ts b/sources/client/src/hooks/use-query-viewable-post-types.ts index a221877..8f161a2 100644 --- a/sources/client/src/hooks/use-query-viewable-post-types.ts +++ b/sources/client/src/hooks/use-query-viewable-post-types.ts @@ -1,5 +1,11 @@ +/** + * External dependencies + */ import EntitiesSearch from '@types'; +/** + * Internal dependencies + */ import { Set } from '../vo/set'; import { useEntityRecords } from './use-entity-records'; @@ -9,18 +15,16 @@ import { useEntityRecords } from './use-entity-records'; * * @public */ -export function useQueryViewablePostTypes(): EntitiesSearch.EntitiesRecords { - const entitiesRecords = useEntityRecords>( - 'root', - 'postType', - { per_page: -1 } - ); +export function useQueryViewablePostTypes(): EntitiesSearch.EntitiesRecords< EntitiesSearch.ViewablePostType > { + const entitiesRecords = useEntityRecords< + EntitiesSearch.PostType< 'edit' > + >( 'root', 'postType', { per_page: -1 } ); const viewablePostTypes = entitiesRecords .records() .filter( - (postType) => postType.viewable - ) as Set; + ( postType ) => postType.viewable + ) as Set< EntitiesSearch.ViewablePostType >; return { ...entitiesRecords, diff --git a/sources/client/src/hooks/use-query-viewable-taxonomies.ts b/sources/client/src/hooks/use-query-viewable-taxonomies.ts index 416230b..66b61e1 100644 --- a/sources/client/src/hooks/use-query-viewable-taxonomies.ts +++ b/sources/client/src/hooks/use-query-viewable-taxonomies.ts @@ -1,5 +1,11 @@ +/** + * External dependencies + */ import EntitiesSearch from '@types'; +/** + * Internal dependencies + */ import { Set } from '../vo/set'; import { useEntityRecords } from './use-entity-records'; @@ -9,18 +15,16 @@ import { useEntityRecords } from './use-entity-records'; * * @public */ -export function useQueryViewableTaxonomies(): EntitiesSearch.EntitiesRecords { - const entitiesRecords = useEntityRecords>( - 'root', - 'taxonomy', - { per_page: -1 } - ); +export function useQueryViewableTaxonomies(): EntitiesSearch.EntitiesRecords< EntitiesSearch.ViewableTaxonomy > { + const entitiesRecords = useEntityRecords< + EntitiesSearch.Taxonomy< 'edit' > + >( 'root', 'taxonomy', { per_page: -1 } ); const viewableTaxonomies = entitiesRecords .records() .filter( - (taxonomy) => taxonomy.visibility.publicly_queryable - ) as Set; + ( taxonomy ) => taxonomy.visibility.publicly_queryable + ) as Set< EntitiesSearch.ViewableTaxonomy >; return { ...entitiesRecords, diff --git a/sources/client/src/hooks/use-search.ts b/sources/client/src/hooks/use-search.ts index f5b7ed9..51beef6 100644 --- a/sources/client/src/hooks/use-search.ts +++ b/sources/client/src/hooks/use-search.ts @@ -1,13 +1,24 @@ +/** + * External dependencies + */ import EntitiesSearch from '@types'; import React from 'react'; +/** + * WordPress dependencies + */ import { useThrottle } from '@wordpress/compose'; import { doAction } from '@wordpress/hooks'; +/** + * Internal dependencies + */ import { Set } from '../vo/set'; -type SearchPhrase = Parameters[0]; -type SearchFunc = (phrase: SearchPhrase) => void; +type SearchPhrase = Parameters< + EntitiesSearch.SearchControl[ 'onChange' ] +>[ 0 ]; +type SearchFunc = ( phrase: SearchPhrase ) => void; /** * Build a function to search the entities by a phrase @@ -18,39 +29,41 @@ type SearchFunc = (phrase: SearchPhrase) => void; * @param entities The entities to exclude from the search * @param dispatch The dispatch function to update the state */ -export function useSearch( - searchEntities: EntitiesSearch.SearchEntitiesFunction, - kind: EntitiesSearch.Kind, - entities: EntitiesSearch.Entities, - dispatch: React.Dispatch> +export function useSearch< E, K >( + searchEntities: EntitiesSearch.SearchEntitiesFunction< E, K >, + kind: EntitiesSearch.Kind< K >, + entities: EntitiesSearch.Entities< E >, + dispatch: React.Dispatch< EntitiesSearch.StoreAction< E, K > > ): SearchFunc { return useThrottle( - (phrase: SearchPhrase) => { - const _phrase = extractPhrase(phrase); + ( phrase: SearchPhrase ) => { + const _phrase = extractPhrase( phrase ); - searchEntities(_phrase, kind, { + searchEntities( _phrase, kind, { exclude: entities, - }) - .then((result) => - dispatch({ + } ) + .then( ( result ) => + dispatch( { type: 'UPDATE_CURRENT_ENTITIES_OPTIONS', currentEntitiesOptions: result, - }) + } ) ) - .catch((error) => { - doAction('wp-entities-search.on-search.error', error); - const emptySet = new Set>(); - dispatch({ + .catch( ( error ) => { + doAction( 'wp-entities-search.on-search.error', error ); + const emptySet = new Set< + EntitiesSearch.ControlOption< E > + >(); + dispatch( { type: 'UPDATE_CURRENT_ENTITIES_OPTIONS', currentEntitiesOptions: emptySet, - }); + } ); return emptySet; - }) - .finally(() => - dispatch({ + } ) + .finally( () => + dispatch( { type: 'UPDATE_SEARCH_PHRASE', searchPhrase: _phrase, - }) + } ) ); }, 300, @@ -58,7 +71,7 @@ export function useSearch( ); } -function extractPhrase(phraseOrEvent: SearchPhrase): string { +function extractPhrase( phraseOrEvent: SearchPhrase ): string { const phrase = typeof phraseOrEvent === 'string' ? phraseOrEvent diff --git a/sources/client/src/logging/index.ts b/sources/client/src/logging/index.ts index 29dc402..bee10c4 100644 --- a/sources/client/src/logging/index.ts +++ b/sources/client/src/logging/index.ts @@ -1,3 +1,6 @@ +/** + * WordPress dependencies + */ import { addAction } from '@wordpress/hooks'; /* eslint-disable no-console */ @@ -5,10 +8,10 @@ import { addAction } from '@wordpress/hooks'; addAction( 'wp-entities-search.on-change-entities.error', 'wp-entities-search/wp-on-change-entities.error', - (error) => { + ( error ) => { console.error( `Composite Entities by Kind - on Change Entities: ${ - new Error(error).message + new Error( error ).message }` ); } @@ -17,7 +20,7 @@ addAction( addAction( 'wp-entities-search.on-change-kind.error', 'wp-entities-search/wp-on-change-entities.error', - (error) => { + ( error ) => { console.error( `Composite Entities by Kind - on Change Kind: ${ error.message ?? error @@ -29,15 +32,17 @@ addAction( addAction( 'wp-entities-search.on-storage-initialization.error', 'wp-entities-search/on-storage-initialization.error', - (error) => { - console.error(`Composite Entities by Kind: ${error.message ?? error}`); + ( error ) => { + console.error( + `Composite Entities by Kind: ${ error.message ?? error }` + ); } ); addAction( 'wp-entities-search.on-search.error', 'wp-entities-search/on-search.error', - (error) => { + ( error ) => { console.error( `Composite Entities by Kind - on Search Entities: ${ error.message ?? error diff --git a/sources/client/src/services/abort-controllers.ts b/sources/client/src/services/abort-controllers.ts index aeaa590..9dcdc4c 100644 --- a/sources/client/src/services/abort-controllers.ts +++ b/sources/client/src/services/abort-controllers.ts @@ -1,13 +1,16 @@ +/** + * Internal dependencies + */ import { ContextualAbortController } from './contextual-abort-controller'; /** * @internal */ class AbortControllers { - private controllers = new Map(); + private controllers = new Map< string, ContextualAbortController >(); - public has(controller: ContextualAbortController): boolean { - return this.controllers.has(controller.context()); + public has( controller: ContextualAbortController ): boolean { + return this.controllers.has( controller.context() ); } public add( @@ -15,22 +18,24 @@ class AbortControllers { ): ContextualAbortController { const context = controller.context(); - this.controller(context)?.abort(); - this.set(controller); + this.controller( context )?.abort(); + this.set( controller ); - return this.controller(context)!; + return this.controller( context )!; } - public delete(controller: ContextualAbortController): void { - this.controllers.delete(controller.context()); + public delete( controller: ContextualAbortController ): void { + this.controllers.delete( controller.context() ); } - private set(controller: ContextualAbortController): void { - this.controllers.set(controller.context(), controller); + private set( controller: ContextualAbortController ): void { + this.controllers.set( controller.context(), controller ); } - private controller(context: string): ContextualAbortController | undefined { - return this.controllers.get(context); + private controller( + context: string + ): ContextualAbortController | undefined { + return this.controllers.get( context ); } } diff --git a/sources/client/src/services/contextual-abort-controller.ts b/sources/client/src/services/contextual-abort-controller.ts index a7c46ba..b2a0179 100644 --- a/sources/client/src/services/contextual-abort-controller.ts +++ b/sources/client/src/services/contextual-abort-controller.ts @@ -6,9 +6,9 @@ export class ContextualAbortController { #context: string; #reason: string; - constructor(context: string, reason: string) { - if (context === '') { - throw new Error('Abort Controllers, context cannot be empty'); + constructor( context: string, reason: string ) { + if ( context === '' ) { + throw new Error( 'Abort Controllers, context cannot be empty' ); } this.#controller = new AbortController(); @@ -21,7 +21,7 @@ export class ContextualAbortController { } public abort(): ContextualAbortController { - this.#controller.abort(this.reason()); + this.#controller.abort( this.reason() ); return this; } @@ -34,6 +34,6 @@ export class ContextualAbortController { } private reason(): DOMException { - return new DOMException(this.#reason, 'AbortError'); + return new DOMException( this.#reason, 'AbortError' ); } } diff --git a/sources/client/src/storage/entities/initial-state.ts b/sources/client/src/storage/entities/initial-state.ts index 934d92c..6041023 100644 --- a/sources/client/src/storage/entities/initial-state.ts +++ b/sources/client/src/storage/entities/initial-state.ts @@ -1,22 +1,28 @@ +/** + * External dependencies + */ import EntitiesSearch from '@types'; +/** + * Internal dependencies + */ import { Set } from '../../vo/set'; -type Options = EntitiesSearch.ControlOption; +type Options< V > = EntitiesSearch.ControlOption< V >; /** * @internal * @param initialState The initial state to merge with the default state */ -export function makeInitialState( - initialState: Partial> -): EntitiesSearch.EntitiesState { +export function makeInitialState< E, K >( + initialState: Partial< EntitiesSearch.EntitiesState< E, K > > +): EntitiesSearch.EntitiesState< E, K > { return { - entities: new Set([]), - kind: new Set([]), - contextualEntitiesOptions: new Set>(), - currentEntitiesOptions: new Set>(), - selectedEntitiesOptions: new Set>(), + entities: new Set< E >( [] ), + kind: new Set< K >( [] ), + contextualEntitiesOptions: new Set< Options< E > >(), + currentEntitiesOptions: new Set< Options< E > >(), + selectedEntitiesOptions: new Set< Options< E > >(), searchPhrase: '', ...initialState, }; diff --git a/sources/client/src/storage/entities/reducer.ts b/sources/client/src/storage/entities/reducer.ts index 0d1f34b..913d516 100644 --- a/sources/client/src/storage/entities/reducer.ts +++ b/sources/client/src/storage/entities/reducer.ts @@ -1,5 +1,11 @@ +/** + * External dependencies + */ import EntitiesSearch from '@types'; +/** + * Internal dependencies + */ import { Set } from '../../vo/set'; /** @@ -7,11 +13,11 @@ import { Set } from '../../vo/set'; * @param state The state of the reducer * @param action The action to be performed */ -export function reducer( - state: EntitiesSearch.EntitiesState, - action: EntitiesSearch.StoreAction -): EntitiesSearch.EntitiesState { - switch (action.type) { +export function reducer< E, K >( + state: EntitiesSearch.EntitiesState< E, K >, + action: EntitiesSearch.StoreAction< E, K > +): EntitiesSearch.EntitiesState< E, K > { + switch ( action.type ) { case 'UPDATE_ENTITIES': return { ...state, @@ -41,7 +47,8 @@ export function reducer( ...state, selectedEntitiesOptions: action.selectedEntitiesOptions, entities: action.selectedEntitiesOptions.map( - (option: EntitiesSearch.ControlOption) => option.value + ( option: EntitiesSearch.ControlOption< E > ) => + option.value ), }; diff --git a/sources/client/src/utils/assert.ts b/sources/client/src/utils/assert.ts index 08da013..59a2d7f 100644 --- a/sources/client/src/utils/assert.ts +++ b/sources/client/src/utils/assert.ts @@ -1,5 +1,8 @@ -export function assert(condition: boolean, message: string): asserts condition { - if (!condition) { - throw new Error(message); +export function assert( + condition: boolean, + message: string +): asserts condition { + if ( ! condition ) { + throw new Error( message ); } } diff --git a/sources/client/src/utils/convert-entities-to-control-options.ts b/sources/client/src/utils/convert-entities-to-control-options.ts index 6917bcb..b86ce72 100644 --- a/sources/client/src/utils/convert-entities-to-control-options.ts +++ b/sources/client/src/utils/convert-entities-to-control-options.ts @@ -1,21 +1,27 @@ +/** + * External dependencies + */ import EntitiesSearch from '@types'; +/** + * Internal dependencies + */ import { ControlOption } from '../vo/control-option'; import { Set } from '../vo/set'; import { assert } from './assert'; export function convertEntitiesToControlOptions< V, - EntitiesFields extends { [p: string]: any } + EntitiesFields extends { [ p: string ]: any }, >( - entities: Set, + entities: Set< EntitiesFields >, labelKey: string, valueKey: string -): Set> { - return entities.map((entity) => { - const label = entity[labelKey]; - const value = entity[valueKey]; - assert(typeof label === 'string', 'Label Key must be a string'); - return new ControlOption(label, value); - }); +): Set< EntitiesSearch.ControlOption< V > > { + return entities.map( ( entity ) => { + const label = entity[ labelKey ]; + const value = entity[ valueKey ]; + assert( typeof label === 'string', 'Label Key must be a string' ); + return new ControlOption( label, value ); + } ); } diff --git a/sources/client/src/utils/is-control-option.ts b/sources/client/src/utils/is-control-option.ts index b90f2a5..a09217a 100644 --- a/sources/client/src/utils/is-control-option.ts +++ b/sources/client/src/utils/is-control-option.ts @@ -1,9 +1,12 @@ +/** + * External dependencies + */ import EntitiesSearch from '@types'; export function isControlOption( option: unknown -): option is EntitiesSearch.ControlOption { - if (option === null) { +): option is EntitiesSearch.ControlOption< any > { + if ( option === null ) { return false; } diff --git a/sources/client/src/utils/order-selected-options-at-the-top.ts b/sources/client/src/utils/order-selected-options-at-the-top.ts index 7b3636b..af0f19e 100644 --- a/sources/client/src/utils/order-selected-options-at-the-top.ts +++ b/sources/client/src/utils/order-selected-options-at-the-top.ts @@ -1,28 +1,34 @@ +/** + * External dependencies + */ import EntitiesSearch from '@types'; +/** + * Internal dependencies + */ import { Set } from '../vo/set'; -export function orderSelectedOptionsAtTheTop( - options: Set>, - collection: Set | undefined -): Set> { - if (options.length() <= 0) { +export function orderSelectedOptionsAtTheTop< V >( + options: Set< EntitiesSearch.ControlOption< V > >, + collection: Set< V > | undefined +): Set< EntitiesSearch.ControlOption< V > > { + if ( options.length() <= 0 ) { return options; } - if (collection === undefined || collection.length() <= 0) { + if ( collection === undefined || collection.length() <= 0 ) { return options; } - let _collection = new Set>(); - let _options = new Set>(); + let _collection = new Set< EntitiesSearch.ControlOption< V > >(); + let _options = new Set< EntitiesSearch.ControlOption< V > >(); - options.forEach((option) => { - if (collection.has(option.value)) { - _collection = _collection.add(option); + options.forEach( ( option ) => { + if ( collection.has( option.value ) ) { + _collection = _collection.add( option ); } else { - _options = _options.add(option); + _options = _options.add( option ); } - }); + } ); - return _collection.concat(_options); + return _collection.concat( _options ); } diff --git a/sources/client/src/utils/unique-control-options.ts b/sources/client/src/utils/unique-control-options.ts index e7e9f40..e675c3e 100644 --- a/sources/client/src/utils/unique-control-options.ts +++ b/sources/client/src/utils/unique-control-options.ts @@ -1,20 +1,26 @@ +/** + * External dependencies + */ import EntitiesSearch from '@types'; +/** + * Internal dependencies + */ import { Set } from '../vo/set'; // TODO Is this necessary due the new Set implementation? -export function uniqueControlOptions( - set: Set> -): Set> { - let uniqueOptions = new Set>(); - const temp: Array['value']> = []; +export function uniqueControlOptions< V >( + set: Set< EntitiesSearch.ControlOption< V > > +): Set< EntitiesSearch.ControlOption< V > > { + let uniqueOptions = new Set< EntitiesSearch.ControlOption< V > >(); + const temp: Array< EntitiesSearch.ControlOption< V >[ 'value' ] > = []; - for (const option of set) { - if (!temp.includes(option.value)) { - uniqueOptions = uniqueOptions.add(option); + for ( const option of set ) { + if ( ! temp.includes( option.value ) ) { + uniqueOptions = uniqueOptions.add( option ); } - temp.push(option.value); + temp.push( option.value ); } return uniqueOptions; diff --git a/sources/client/src/vo/control-option.ts b/sources/client/src/vo/control-option.ts index 5031712..4eb46a7 100644 --- a/sources/client/src/vo/control-option.ts +++ b/sources/client/src/vo/control-option.ts @@ -1,12 +1,18 @@ +/** + * External dependencies + */ import EntitiesSearch from '@types'; +/** + * Internal dependencies + */ import { assert } from '../utils/assert'; -export class ControlOption implements EntitiesSearch.ControlOption { +export class ControlOption< V > implements EntitiesSearch.ControlOption< V > { public readonly label: string; public readonly value: V; - public constructor(label: string, value: V) { + public constructor( label: string, value: V ) { assert( label !== '', 'ControlOption: Label must be a non empty string.' diff --git a/sources/client/src/vo/set.ts b/sources/client/src/vo/set.ts index 51bf256..d6a2ca7 100644 --- a/sources/client/src/vo/set.ts +++ b/sources/client/src/vo/set.ts @@ -1,83 +1,88 @@ +/** + * External dependencies + */ import { isEqual as _isEqual } from 'lodash'; -export class Set { - readonly #data: ReadonlyArray; +export class Set< T > { + readonly #data: ReadonlyArray< T >; - public constructor(data: ReadonlyArray = []) { + public constructor( data: ReadonlyArray< T > = [] ) { this.#data = data; } - public add(value: T): Set { - if (this.has(value)) { + public add( value: T ): Set< T > { + if ( this.has( value ) ) { return this; } - return new Set([...this.#data, value]); + return new Set( [ ...this.#data, value ] ); } - public delete(value: T): Set { - if (!this.has(value)) { + public delete( value: T ): Set< T > { + if ( ! this.has( value ) ) { return this; } - return new Set(this.#data.filter((item) => !this.isEqual(item, value))); + return new Set( + this.#data.filter( ( item ) => ! this.isEqual( item, value ) ) + ); } - public has(value: T): boolean { - return this.#data.some((current) => this.isEqual(current, value)); + public has( value: T ): boolean { + return this.#data.some( ( current ) => this.isEqual( current, value ) ); } - public map(fn: (value: T) => R): Set { - return new Set(this.#data.map(fn)); + public map< R = T >( fn: ( value: T ) => R ): Set< R > { + return new Set( this.#data.map( fn ) ); } - public toArray(): ReadonlyArray { - return Object.freeze([...this.#data]); + public toArray(): ReadonlyArray< T > { + return Object.freeze( [ ...this.#data ] ); } - public forEach(fn: (value: T) => void): void { - this.#data.forEach(fn); + public forEach( fn: ( value: T ) => void ): void { + this.#data.forEach( fn ); } public length(): number { return this.#data.length; } - public concat(set: Set): Set { - return new Set([...this.#data, ...set.toArray()]); + public concat( set: Set< T > ): Set< T > { + return new Set( [ ...this.#data, ...set.toArray() ] ); } - public filter(fn: (value: T) => boolean): Set { - return new Set(this.#data.filter(fn)); + public filter( fn: ( value: T ) => boolean ): Set< T > { + return new Set( this.#data.filter( fn ) ); } - public find(fn: (value: T) => boolean): T | undefined { - return this.#data.slice(0).find(fn); + public find( fn: ( value: T ) => boolean ): T | undefined { + return this.#data.slice( 0 ).find( fn ); } public first(): T | undefined { - return this.#data.slice(0)[0]; + return this.#data.slice( 0 )[ 0 ]; } public last(): T | undefined { - return this.#data.slice(-1)[0]; + return this.#data.slice( -1 )[ 0 ]; } - public copy(start: number, end: number): Set { - return new Set(this.#data.slice(start, end)); + public copy( start: number, end: number ): Set< T > { + return new Set( this.#data.slice( start, end ) ); } - public equals(set: Set): boolean { - if (this.length() !== set.length()) { + public equals( set: Set< T > ): boolean { + if ( this.length() !== set.length() ) { return false; } - if (this === set) { + if ( this === set ) { return true; } - for (const value of this) { - if (!set.has(value)) { + for ( const value of this ) { + if ( ! set.has( value ) ) { return false; } } @@ -85,13 +90,13 @@ export class Set { return true; } - public *[Symbol.iterator]() { - for (const value of this.#data) { + public *[ Symbol.iterator ]() { + for ( const value of this.#data ) { yield value; } } - private isEqual(a: unknown, b: unknown): boolean { - return _isEqual(a, b); + private isEqual( a: unknown, b: unknown ): boolean { + return _isEqual( a, b ); } } diff --git a/tests/client/integration/components/composite-entities-by-kind.test.tsx b/tests/client/integration/components/composite-entities-by-kind.test.tsx index 5cf7827..1fc699c 100644 --- a/tests/client/integration/components/composite-entities-by-kind.test.tsx +++ b/tests/client/integration/components/composite-entities-by-kind.test.tsx @@ -1,3 +1,6 @@ +/** + * External dependencies + */ import EntitiesSearch from '@types'; import React from 'react'; @@ -6,52 +9,58 @@ import { describe, it, expect, jest } from '@jest/globals'; import { render, act } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; +/** + * WordPress dependencies + */ import { doAction } from '@wordpress/hooks'; +/** + * Internal dependencies + */ import { CompositeEntitiesByKind } from '../../../../sources/client/src/components/composite-entities-by-kind'; import { PluralSelectControl } from '../../../../sources/client/src/components/plural-select-control'; import { SearchControl } from '../../../../sources/client/src/components/search-control'; import { SingularSelectControl } from '../../../../sources/client/src/components/singular-select-control'; import { Set } from '../../../../sources/client/src/vo/set'; -jest.mock('@wordpress/hooks', () => ({ +jest.mock( '@wordpress/hooks', () => ( { doAction: jest.fn(), -})); -jest.mock('@wordpress/compose', () => ({ - useThrottle: (fn: (phrase: any) => void) => fn, -})); +} ) ); +jest.mock( '@wordpress/compose', () => ( { + useThrottle: ( fn: ( phrase: any ) => void ) => fn, +} ) ); -describe('CompositeEntitiesByKind', () => { - it('Allow to select a kind', async () => { +describe( 'CompositeEntitiesByKind', () => { + it( 'Allow to select a kind', async () => { let expectedKinds = new Set(); const rendered = render( Promise.resolve(new Set())} - entities={{ + searchEntities={ () => Promise.resolve( new Set() ) } + entities={ { value: new Set(), onChange: () => {}, - }} - kind={{ - value: new Set(['post']), - options: new Set([ + } } + kind={ { + value: new Set( [ 'post' ] ), + options: new Set( [ { label: 'Post', value: 'post' }, { label: 'Page', value: 'page' }, - ]), - onChange: (value) => (expectedKinds = value), - }} + ] ), + onChange: ( value ) => ( expectedKinds = value ), + } } > - {(_posts, kind) => { + { ( _posts, kind ) => { return ( - kind.onChange(new Set([value])) + { ...kind } + value={ kind.value.first() ?? '' } + onChange={ ( value ) => + kind.onChange( new Set( [ value ] ) ) } /> ); - }} + } } ); @@ -59,37 +68,37 @@ describe('CompositeEntitiesByKind', () => { '.wes-select-control' ) as HTMLSelectElement; - await userEvent.selectOptions(kindSelect, 'page'); - expect(expectedKinds.first()).toBe('page'); - }); + await userEvent.selectOptions( kindSelect, 'page' ); + expect( expectedKinds.first() ).toBe( 'page' ); + } ); - it('Allow to select an entity', async () => { + it( 'Allow to select an entity', async () => { let expectedEntities = new Set(); - const rendered = await act(async () => + const rendered = await act( async () => render( + searchEntities={ () => Promise.resolve( - new Set([ + new Set( [ { label: 'Post 1', value: 'post-1' }, { label: 'Post 2', value: 'post-2' }, - ]) + ] ) ) } - entities={{ - value: new Set(), - onChange: (value) => (expectedEntities = value), - }} - kind={{ - value: new Set(['post']), + entities={ { + value: new Set< string >(), + onChange: ( value ) => ( expectedEntities = value ), + } } + kind={ { + value: new Set( [ 'post' ] ), options: new Set(), onChange: () => {}, - }} + } } > - {(entities) => { - return ; - }} + { ( entities ) => { + return ; + } } ) ); @@ -98,52 +107,52 @@ describe('CompositeEntitiesByKind', () => { '.wes-select-control' ) as HTMLSelectElement; - await userEvent.selectOptions(entitiesSelect, ['post-2']); + await userEvent.selectOptions( entitiesSelect, [ 'post-2' ] ); - expect(expectedEntities?.has('post-2')).toBe(true); - }); + expect( expectedEntities?.has( 'post-2' ) ).toBe( true ); + } ); - it('Reset the selected entities when the kind change', async () => { + it( 'Reset the selected entities when the kind change', async () => { let expectedEntities = new Set(); - const rendered = await act(async () => + const rendered = await act( async () => render( + searchEntities={ () => Promise.resolve( - new Set([ + new Set( [ { label: 'Post 1', value: 'post-1' }, { label: 'Post 2', value: 'post-2' }, - ]) + ] ) ) } - entities={{ - value: new Set(), - onChange: (value) => (expectedEntities = value), - }} - kind={{ - value: new Set(['post']), - options: new Set([ + entities={ { + value: new Set< string >(), + onChange: ( value ) => ( expectedEntities = value ), + } } + kind={ { + value: new Set( [ 'post' ] ), + options: new Set( [ { label: 'Post', value: 'post' }, { label: 'Page', value: 'page' }, - ]), + ] ), onChange: () => {}, - }} + } } > - {(entities, kind) => { + { ( entities, kind ) => { return ( <> - kind.onChange(new Set([value])) + { ...kind } + value={ kind.value.first() ?? '' } + onChange={ ( value ) => + kind.onChange( new Set( [ value ] ) ) } /> - + ); - }} + } } ) ); @@ -155,65 +164,67 @@ describe('CompositeEntitiesByKind', () => { '.wes-select-control--plural' ) as HTMLSelectElement; - await userEvent.selectOptions(entitiesSelect, ['post-2']); - expect(expectedEntities.has('post-2')).toBe(true); + await userEvent.selectOptions( entitiesSelect, [ 'post-2' ] ); + expect( expectedEntities.has( 'post-2' ) ).toBe( true ); - await userEvent.selectOptions(kindSelect, 'page'); - expect(expectedEntities.length()).toBe(0); - }); + await userEvent.selectOptions( kindSelect, 'page' ); + expect( expectedEntities.length() ).toBe( 0 ); + } ); - it('Pass to the children the updated entities options when the kind change', async () => { - let expectedEntities = new Set>(); + it( 'Pass to the children the updated entities options when the kind change', async () => { + let expectedEntities = new Set< + EntitiesSearch.ControlOption< string > + >(); - const rendered = await act(() => + const rendered = await act( () => render( { - if (_postType.first() === 'page') { + searchEntities={ ( _phrase, _postType ) => { + if ( _postType.first() === 'page' ) { return Promise.resolve( - new Set([ + new Set( [ { label: 'Page 1', value: 'page-1' }, { label: 'Page 2', value: 'page-2' }, - ]) + ] ) ); } return Promise.resolve( - new Set([ + new Set( [ { label: 'Post 1', value: 'post-1' }, { label: 'Post 2', value: 'post-2' }, - ]) + ] ) ); - }} - entities={{ - value: new Set(), + } } + entities={ { + value: new Set< string >(), onChange: () => {}, - }} - kind={{ - value: new Set(['post']), - options: new Set([ + } } + kind={ { + value: new Set< string >( [ 'post' ] ), + options: new Set( [ { label: 'Post', value: 'post' }, { label: 'Page', value: 'page' }, - ]), + ] ), onChange: () => {}, - }} + } } > - {(entities, kind) => { + { ( entities, kind ) => { expectedEntities = entities.options; return ( <> - kind.onChange(new Set([value])) + { ...kind } + value={ kind.value.first() ?? '' } + onChange={ ( value ) => + kind.onChange( new Set( [ value ] ) ) } /> - + ); - }} + } } ) ); @@ -222,66 +233,66 @@ describe('CompositeEntitiesByKind', () => { '.wes-select-control' ) as HTMLSelectElement; - expect(expectedEntities.length()).toBe(2); + expect( expectedEntities.length() ).toBe( 2 ); let options = expectedEntities.toArray(); - expect(options[0]?.value).toBe('post-1'); - expect(options[1]?.value).toBe('post-2'); + expect( options[ 0 ]?.value ).toBe( 'post-1' ); + expect( options[ 1 ]?.value ).toBe( 'post-2' ); - await userEvent.selectOptions(kindSelect, 'page'); - expect(expectedEntities.length()).toBe(2); + await userEvent.selectOptions( kindSelect, 'page' ); + expect( expectedEntities.length() ).toBe( 2 ); options = expectedEntities.toArray(); - expect(options[0]?.value).toBe('page-1'); - expect(options[1]?.value).toBe('page-2'); - }); + expect( options[ 0 ]?.value ).toBe( 'page-1' ); + expect( options[ 1 ]?.value ).toBe( 'page-2' ); + } ); - it('Set the entities options to an empty collection when there is an issue in retrieving them', async () => { + it( 'Set the entities options to an empty collection when there is an issue in retrieving them', async () => { let expectedEntities = new Set(); - const rendered = await act(() => + const rendered = await act( () => render( { - switch (kind.first()) { + searchEntities={ ( phrase, kind ) => { + switch ( kind.first() ) { case 'post': return Promise.resolve( - new Set([ + new Set( [ { label: 'Post 1', value: 'post-1' }, { label: 'Post 2', value: 'post-2' }, - ]) + ] ) ); case 'page': - return Promise.reject('Error'); + return Promise.reject( 'Error' ); } - }} - entities={{ - value: new Set(), + } } + entities={ { + value: new Set< string >(), onChange: () => {}, - }} - kind={{ - value: new Set(['post']), - options: new Set([ + } } + kind={ { + value: new Set( [ 'post' ] ), + options: new Set( [ { label: 'Post', value: 'post' }, { label: 'Page', value: 'page' }, - ]), + ] ), onChange: () => {}, - }} + } } > - {(entities, kind) => { + { ( entities, kind ) => { expectedEntities = entities.options; return ( <> - kind.onChange(new Set([value])) + { ...kind } + value={ kind.value.first() ?? '' } + onChange={ ( value ) => + kind.onChange( new Set( [ value ] ) ) } /> - + ); - }} + } } ) ); @@ -290,45 +301,47 @@ describe('CompositeEntitiesByKind', () => { '.wes-select-control--singular' ) as HTMLSelectElement; - await userEvent.selectOptions(kindSelect, 'page'); - expect(expectedEntities.length()).toBe(0); + await userEvent.selectOptions( kindSelect, 'page' ); + expect( expectedEntities.length() ).toBe( 0 ); - expect(jest.mocked(doAction)).toHaveBeenCalledWith( + expect( jest.mocked( doAction ) ).toHaveBeenCalledWith( 'wp-entities-search.on-change-kind.error', 'Error' ); - }); + } ); - it('Does not search for entities when changing the entities selection produce an empty collection', async () => { - let selectedEntities = new Set(['post-1', 'post-2']); - const searchEntities = jest.fn(() => + it( 'Does not search for entities when changing the entities selection produce an empty collection', async () => { + let selectedEntities = new Set( [ 'post-1', 'post-2' ] ); + const searchEntities = jest.fn( () => Promise.resolve( - new Set([ + new Set( [ { label: 'Post 1', value: 'post-1' }, { label: 'Post 2', value: 'post-2' }, - ]) + ] ) ) - ) as jest.Mock>; + ) as jest.Mock< + EntitiesSearch.SearchEntitiesFunction< string, string > + >; - const rendered = await act(() => + const rendered = await act( () => render( { + onChange: ( entities ) => { selectedEntities = entities; }, - }} - kind={{ - value: new Set(['post']), + } } + kind={ { + value: new Set( [ 'post' ] ), options: new Set(), onChange: () => {}, - }} + } } > - {(entities) => { - return ; - }} + { ( entities ) => { + return ; + } } ) ); @@ -337,117 +350,120 @@ describe('CompositeEntitiesByKind', () => { '.wes-select-control' ) as HTMLSelectElement; - expect(selectedEntities.length()).toBe(2); - expect(selectedEntities.has('post-1')).toEqual(true); - expect(selectedEntities.has('post-2')).toEqual(true); + expect( selectedEntities.length() ).toBe( 2 ); + expect( selectedEntities.has( 'post-1' ) ).toEqual( true ); + expect( selectedEntities.has( 'post-2' ) ).toEqual( true ); - await userEvent.deselectOptions(entitiesSelect, ['post-1', 'post-2']); - expect(selectedEntities.length()).toBe(0); - }); + await userEvent.deselectOptions( entitiesSelect, [ + 'post-1', + 'post-2', + ] ); + expect( selectedEntities.length() ).toBe( 0 ); + } ); - it('Call searchEntities with the right parameters when mounting the component', async () => { - const selectedEntities = new Set(['1', '2']); + it( 'Call searchEntities with the right parameters when mounting the component', async () => { + const selectedEntities = new Set( [ '1', '2' ] ); const searchEntities = jest.fn() as jest.Mock< EntitiesSearch.CompositeEntitiesKinds< string, string - >['searchEntities'] + >[ 'searchEntities' ] >; - await act(() => + await act( () => render( {}, - }} - kind={{ - value: new Set(['post']), + } } + kind={ { + value: new Set( [ 'post' ] ), options: new Set(), onChange: () => {}, - }} + } } > - {(_, __, search) => { - return ; - }} + { ( _, __, search ) => { + return ; + } } ) ); - expect(searchEntities).toHaveBeenNthCalledWith( + expect( searchEntities ).toHaveBeenNthCalledWith( 2, '', - new Set(['post']), + new Set( [ 'post' ] ), { include: selectedEntities, per_page: '-1', } ); - }); + } ); - it('Catch the error thrown by searchEntities when the component mount', async () => { - await act(() => { + it( 'Catch the error thrown by searchEntities when the component mount', async () => { + await act( () => { render( Promise.reject('Error')} - entities={{ + searchEntities={ () => Promise.reject( 'Error' ) } + entities={ { value: new Set(), onChange: () => {}, - }} - kind={{ - value: new Set(['post']), + } } + kind={ { + value: new Set( [ 'post' ] ), options: new Set(), onChange: () => {}, - }} + } } > - {(_, __, search) => { - return ; - }} + { ( _, __, search ) => { + return ; + } } ); - }); + } ); - expect(doAction).toHaveBeenCalledWith( + expect( doAction ).toHaveBeenCalledWith( 'wp-entities-search.on-storage-initialization.error', 'Error' ); - }); + } ); - it('Catch on change entities error', async () => { - const rendered = await act(() => + it( 'Catch on change entities error', async () => { + const rendered = await act( () => render( { - if (queryArguments?.include?.first() === 'post-1') { - return Promise.reject('Error'); + searchEntities={ ( phrase, kind, queryArguments ) => { + if ( queryArguments?.include?.first() === 'post-1' ) { + return Promise.reject( 'Error' ); } return Promise.resolve( - new Set([ + new Set( [ { label: 'Post 1', value: 'post-1' }, { label: 'Post 2', value: 'post-2' }, - ]) + ] ) ); - }} - entities={{ - value: new Set(), + } } + entities={ { + value: new Set< string >(), onChange: () => {}, - }} - kind={{ - value: new Set(['post']), - options: new Set([ + } } + kind={ { + value: new Set( [ 'post' ] ), + options: new Set( [ { label: 'Post', value: 'post' }, { label: 'Page', value: 'page' }, - ]), + ] ), onChange: () => {}, - }} + } } > - {(entities) => { - return ; - }} + { ( entities ) => { + return ; + } } ) ); @@ -456,36 +472,36 @@ describe('CompositeEntitiesByKind', () => { '.wes-select-control' ) as HTMLSelectElement; - await userEvent.selectOptions(entitiesSelect, ['post-1']); + await userEvent.selectOptions( entitiesSelect, [ 'post-1' ] ); - expect(jest.mocked(doAction)).toHaveBeenCalledWith( + expect( jest.mocked( doAction ) ).toHaveBeenCalledWith( 'wp-entities-search.on-change-entities.error', 'Error' ); - }); + } ); - it('Set to an empty collection the Kind when all kinds are unselected', async () => { - let expectedKinds = new Set(); + it( 'Set to an empty collection the Kind when all kinds are unselected', async () => { + let expectedKinds = new Set< string >(); const rendered = render( Promise.resolve(new Set())} - entities={{ + searchEntities={ () => Promise.resolve( new Set() ) } + entities={ { value: new Set(), onChange: () => {}, - }} - kind={{ - value: new Set(['post']), - options: new Set([ + } } + kind={ { + value: new Set( [ 'post' ] ), + options: new Set( [ { label: 'Post', value: 'post' }, { label: 'Page', value: 'page' }, - ]), - onChange: (value) => (expectedKinds = value), - }} + ] ), + onChange: ( value ) => ( expectedKinds = value ), + } } > - {(_posts, kind) => { - return ; - }} + { ( _posts, kind ) => { + return ; + } } ); @@ -493,7 +509,7 @@ describe('CompositeEntitiesByKind', () => { '.wes-select-control' ) as HTMLSelectElement; - await userEvent.deselectOptions(postTypeSelect, ['post']); - expect(expectedKinds.length()).toBe(0); - }); -}); + await userEvent.deselectOptions( postTypeSelect, [ 'post' ] ); + expect( expectedKinds.length() ).toBe( 0 ); + } ); +} ); diff --git a/tests/client/setup-tests.ts b/tests/client/setup-tests.ts index 4860e2c..66f5152 100644 --- a/tests/client/setup-tests.ts +++ b/tests/client/setup-tests.ts @@ -1 +1,4 @@ +/** + * External dependencies + */ import '@testing-library/jest-dom/jest-globals'; diff --git a/tests/client/unit/api/search-entities.test.ts b/tests/client/unit/api/search-entities.test.ts index df101c8..18dc60a 100644 --- a/tests/client/unit/api/search-entities.test.ts +++ b/tests/client/unit/api/search-entities.test.ts @@ -1,113 +1,124 @@ +/** + * External dependencies + */ import EntitiesSearch from '@types'; import { describe, expect, it, jest } from '@jest/globals'; +/** + * WordPress dependencies + */ import { doAction } from '@wordpress/hooks'; +/** + * Internal dependencies + */ import { fetch } from '../../../../sources/client/src/api/fetch'; import { searchEntities } from '../../../../sources/client/src/api/search-entities'; import { Set } from '../../../../sources/client/src/vo/set'; -jest.mock('@wordpress/hooks', () => ({ +jest.mock( '@wordpress/hooks', () => ( { doAction: jest.fn(), -})); +} ) ); -jest.mock('../../../../sources/client/src/api/fetch', () => ({ +jest.mock( '../../../../sources/client/src/api/fetch', () => ( { fetch: jest.fn(), -})); +} ) ); -describe('Search Posts', () => { - it('perform a search with the given parameters', async () => { +describe( 'Search Posts', () => { + it( 'perform a search with the given parameters', async () => { let expectedPath: string = ''; // @ts-ignore - jest.mocked(fetch).mockImplementation((options: any) => { + jest.mocked( fetch ).mockImplementation( ( options: any ) => { expectedPath = options.path; // Fetch result is irrelevant for this test - return Promise.resolve([]); - }); + return Promise.resolve( [] ); + } ); - await searchEntities('post', new Set(['post']), 'foo', { - exclude: new Set(['1', '3']), - include: new Set(['2', '5']), - }); + await searchEntities( 'post', new Set( [ 'post' ] ), 'foo', { + exclude: new Set< string >( [ '1', '3' ] ), + include: new Set< string >( [ '2', '5' ] ), + } ); - expect(expectedPath).toBe( + expect( expectedPath ).toBe( '?rest_route=/wp/v2/search&per_page=10&order=DESC&orderBy=title&exclude=1%2C3&include=2%2C5&type=post&search=foo&subtype=post&_fields=title%2Cid' ); - }); + } ); - it('return the immutable set of entities', async () => { + it( 'return the immutable set of entities', async () => { const expected = [ { id: 1, title: { rendered: 'Foo' } }, { id: 2, title: { rendered: 'Bar' } }, ]; // @ts-ignore - jest.mocked(fetch).mockResolvedValue(expected); + jest.mocked( fetch ).mockResolvedValue( expected ); - const entities = await searchEntities( + const entities = await searchEntities< EntitiesSearch.Post >( 'post', - new Set(['post']), + new Set( [ 'post' ] ), 'foo', { - exclude: new Set(['1', '3']), - include: new Set(['2', '5']), + exclude: new Set< string >( [ '1', '3' ] ), + include: new Set< string >( [ '2', '5' ] ), } ); - entities.toArray().forEach((entity, index) => { - expect(entity.id).toEqual(expected[index]?.id); - expect(entity.title.rendered).toEqual( - expected[index]?.title?.rendered + entities.toArray().forEach( ( entity, index ) => { + expect( entity.id ).toEqual( expected[ index ]?.id ); + expect( entity.title.rendered ).toEqual( + expected[ index ]?.title?.rendered ); - }); - }); + } ); + } ); - it('abort the request when the search is aborted', async () => { + it( 'abort the request when the search is aborted', async () => { let expectedError: Error; // @ts-ignore - jest.mocked(fetch).mockImplementation(() => - Promise.reject(new DOMException('Aborted Request', 'AbortError')) + jest.mocked( fetch ).mockImplementation( () => + Promise.reject( + new DOMException( 'Aborted Request', 'AbortError' ) + ) ); try { - await searchEntities('post', new Set(['post']), 'foo', { - exclude: new Set(['1', '3']), - include: new Set(['2', '5']), - }); - } catch (error: any) { + await searchEntities( 'post', new Set( [ 'post' ] ), 'foo', { + exclude: new Set< string >( [ '1', '3' ] ), + include: new Set< string >( [ '2', '5' ] ), + } ); + } catch ( error: any ) { expectedError = error; } // @ts-ignore - expect(expectedError.name).toBe('AbortError'); - expect(doAction).toHaveBeenCalledWith( + expect( expectedError.name ).toBe( 'AbortError' ); + expect( doAction ).toHaveBeenCalledWith( 'wp-entities-search.on-search.abort', // @ts-ignore expectedError ); - }); + } ); - it('do not execute aborted action when wrong error type', async () => { + it( 'do not execute aborted action when wrong error type', async () => { let expectedError: Error; // @ts-ignore - jest.mocked(fetch).mockImplementation(() => - Promise.reject(new Error('Aborted Request')) + jest.mocked( fetch ).mockImplementation( () => + Promise.reject( new Error( 'Aborted Request' ) ) ); try { - await searchEntities('post', new Set(['post']), 'foo', { - exclude: new Set(['1', '3']), - include: new Set(['2', '5']), - }); - } catch (error: any) { + await searchEntities( 'post', new Set( [ 'post' ] ), 'foo', { + exclude: new Set< string >( [ '1', '3' ] ), + include: new Set< string >( [ '2', '5' ] ), + } ); + } catch ( error: any ) { expectedError = error; } // @ts-ignore - expect(expectedError.message).toBe('Aborted Request'); - expect(doAction).not.toHaveBeenCalled(); - }); -}); + expect( expectedError.message ).toBe( 'Aborted Request' ); + expect( doAction ).not.toHaveBeenCalled(); + } ); +} ); diff --git a/tests/client/unit/components/plural-select-control.test.tsx b/tests/client/unit/components/plural-select-control.test.tsx index 2b44393..38e6fbc 100644 --- a/tests/client/unit/components/plural-select-control.test.tsx +++ b/tests/client/unit/components/plural-select-control.test.tsx @@ -1,3 +1,6 @@ +/** + * External dependencies + */ import EntitiesSearch from '@types'; import React from 'react'; @@ -8,29 +11,35 @@ import userEvent from '@testing-library/user-event'; import { faker } from '@faker-js/faker'; +/** + * Internal dependencies + */ import { PluralSelectControl } from '../../../../sources/client/src/components/plural-select-control'; import { Set } from '../../../../sources/client/src/vo/set'; import { buildOptions } from '../utils'; -describe('Posts Select', () => { - it('Call the given onChange handler', async () => { - const option: EntitiesSearch.ControlOption = { - label: faker.word.words(2), +describe( 'Posts Select', () => { + it( 'Call the given onChange handler', async () => { + const option: EntitiesSearch.ControlOption< string > = { + label: faker.word.words( 2 ), value: faker.word.noun(), }; - const options = new Set>([]) - .add(option) - .concat(buildOptions()); + const options = new Set< EntitiesSearch.ControlOption< string > >( [] ) + .add( option ) + .concat( buildOptions() ); const rendered = render( {}} + options={ options } + value={ new Set( [ option.value ] ) } + onChange={ () => {} } /> ); - const valuesToSelect = [option.value, String(options.last()?.value)]; + const valuesToSelect = [ + option.value, + String( options.last()?.value ), + ]; const select = rendered.container.querySelector( '.wes-select-control' ) as HTMLSelectElement; @@ -39,30 +48,37 @@ describe('Posts Select', () => { * We do not need to select two options since we are only testing the pure logic not the data but, * for logic correctness the `select` element is a multi select. */ - await userEvent.selectOptions(select, valuesToSelect); + await userEvent.selectOptions( select, valuesToSelect ); - expect(select.selectedOptions[0]?.value).toEqual(valuesToSelect[0]); - expect(select.selectedOptions[1]?.value).toEqual(valuesToSelect[1]); - }); + expect( select.selectedOptions[ 0 ]?.value ).toEqual( + valuesToSelect[ 0 ] + ); + expect( select.selectedOptions[ 1 ]?.value ).toEqual( + valuesToSelect[ 1 ] + ); + } ); - it('Deselect the options', async () => { - const option: EntitiesSearch.ControlOption = { - label: faker.word.words(2), + it( 'Deselect the options', async () => { + const option: EntitiesSearch.ControlOption< string > = { + label: faker.word.words( 2 ), value: faker.word.noun(), }; - const options = new Set>([]) - .add(option) - .concat(buildOptions()); + const options = new Set< EntitiesSearch.ControlOption< string > >( [] ) + .add( option ) + .concat( buildOptions() ); const rendered = render( {}} + options={ options } + value={ new Set( [ option.value ] ) } + onChange={ () => {} } /> ); - const valuesToSelect = [option.value, String(options.last()?.value)]; + const valuesToSelect = [ + option.value, + String( options.last()?.value ), + ]; const select = rendered.container.querySelector( '.wes-select-control' ) as HTMLSelectElement; @@ -71,24 +87,28 @@ describe('Posts Select', () => { * We do not need to select two options since we are only testing the pure logic not the data but, * for logic correctness the `select` element is a multi select. */ - await userEvent.selectOptions(select, valuesToSelect); + await userEvent.selectOptions( select, valuesToSelect ); - expect(select.selectedOptions[0]?.value).toEqual(valuesToSelect[0]); - expect(select.selectedOptions[1]?.value).toEqual(valuesToSelect[1]); + expect( select.selectedOptions[ 0 ]?.value ).toEqual( + valuesToSelect[ 0 ] + ); + expect( select.selectedOptions[ 1 ]?.value ).toEqual( + valuesToSelect[ 1 ] + ); - await userEvent.deselectOptions(select, valuesToSelect); - expect(select.selectedOptions.length).toBe(0); - }); + await userEvent.deselectOptions( select, valuesToSelect ); + expect( select.selectedOptions.length ).toBe( 0 ); + } ); - it('Render the NoOptionsMessage component', () => { + it( 'Render the NoOptionsMessage component', () => { const rendered = render( {}} + options={ new Set() } + value={ new Set() } + onChange={ () => {} } /> ); - expect(rendered.asFragment()).toMatchSnapshot(); - }); -}); + expect( rendered.asFragment() ).toMatchSnapshot(); + } ); +} ); diff --git a/tests/client/unit/components/radio-control.test.tsx b/tests/client/unit/components/radio-control.test.tsx index e603ca6..02580cc 100644 --- a/tests/client/unit/components/radio-control.test.tsx +++ b/tests/client/unit/components/radio-control.test.tsx @@ -1,3 +1,6 @@ +/** + * External dependencies + */ import EntitiesSearch from '@types'; import React from 'react'; @@ -5,42 +8,45 @@ import { expect, jest, describe, it } from '@jest/globals'; import { render, screen, fireEvent } from '@testing-library/react'; +/** + * Internal dependencies + */ import { RadioControl } from '../../../../sources/client/src/components/radio-control'; import { Set } from '../../../../sources/client/src/vo/set'; -describe('KindRadioControl', () => { - it('renders the component', () => { +describe( 'KindRadioControl', () => { + it( 'renders the component', () => { const props = { className: 'test-class', - options: new Set([ + options: new Set( [ { label: 'Option 1', value: 'option-one', }, - ]), + ] ), value: 'option-one', onChange: jest.fn(), }; - render(); + render( ); - expect(screen.getByLabelText('Option 1')).toMatchSnapshot(); - }); + expect( screen.getByLabelText( 'Option 1' ) ).toMatchSnapshot(); + } ); - it('renders the NoOptionsMessage when there are no options', () => { + it( 'renders the NoOptionsMessage when there are no options', () => { const props = { className: 'test-class', - options: new Set>(), + options: new Set< EntitiesSearch.ControlOption< any > >(), value: 'option-one', onChange: jest.fn(), }; - const { container } = render(); + const { container } = render( ); - expect(container.firstChild).toMatchSnapshot(); - }); + expect( container.firstChild ).toMatchSnapshot(); + } ); - it.each([ + it.each( [ [ - new Set([ + new Set( [ { label: 'Option 1', value: 'option-one', @@ -49,11 +55,11 @@ describe('KindRadioControl', () => { label: 'Option 2', value: 'option-2', }, - ]), + ] ), 'option-one', ], [ - new Set([ + new Set( [ { label: 'Option 1', value: 1, @@ -62,24 +68,24 @@ describe('KindRadioControl', () => { label: 'Option 2', value: 2, }, - ]), + ] ), 1, ], - ])('check the input based on the value given', (options, value) => { + ] )( 'check the input based on the value given', ( options, value ) => { const props = { className: 'test-class', options, value, onChange: jest.fn(), }; - render(); + render( ); - expect(screen.getByLabelText('Option 1')).toBeChecked(); - }); + expect( screen.getByLabelText( 'Option 1' ) ).toBeChecked(); + } ); - it.each([ + it.each( [ [ - new Set([ + new Set( [ { label: 'Option 1', value: 'option-one', @@ -88,12 +94,12 @@ describe('KindRadioControl', () => { label: 'Option 2', value: 'option-2', }, - ]), + ] ), 'option-one', 'option-2', ], [ - new Set([ + new Set( [ { label: 'Option 1', value: 1, @@ -102,29 +108,29 @@ describe('KindRadioControl', () => { label: 'Option 2', value: 2, }, - ]), + ] ), 1, 2, ], - ])( + ] )( 'changes the value when an option is selected', - (options, value, expected) => { + ( options, value, expected ) => { const props = { options, value, onChange: jest.fn(), }; - render(); + render( ); - fireEvent.click(screen.getByLabelText('Option 2')); + fireEvent.click( screen.getByLabelText( 'Option 2' ) ); - expect(props.onChange).toHaveBeenCalledWith(expected); + expect( props.onChange ).toHaveBeenCalledWith( expected ); } ); - it('does not change the value when an option is selected that does not exist', () => { + it( 'does not change the value when an option is selected that does not exist', () => { const props = { - options: new Set([ + options: new Set( [ { label: 'Option 1', value: 'option-one', @@ -133,17 +139,17 @@ describe('KindRadioControl', () => { label: 'Option 2', value: 'option-two', }, - ]), + ] ), value: 'option-two', onChange: jest.fn(), }; - render(); + render( ); - const option = screen.getByLabelText('Option 1'); + const option = screen.getByLabelText< HTMLInputElement >( 'Option 1' ); option.value = 'option-3'; - fireEvent.click(option); + fireEvent.click( option ); - expect(props.onChange).not.toHaveBeenCalled(); - }); -}); + expect( props.onChange ).not.toHaveBeenCalled(); + } ); +} ); diff --git a/tests/client/unit/components/search-control.test.tsx b/tests/client/unit/components/search-control.test.tsx index afc7b70..fb53deb 100644 --- a/tests/client/unit/components/search-control.test.tsx +++ b/tests/client/unit/components/search-control.test.tsx @@ -3,6 +3,9 @@ // - The component renders the input within the label if the id prop is passed // - The component renders the input outside the label if the id prop is not passed // - The component calls the onChange prop when the input value changes +/** + * External dependencies + */ import React from 'react'; import { describe, it, expect, jest } from '@jest/globals'; @@ -10,36 +13,41 @@ import { describe, it, expect, jest } from '@jest/globals'; import { render, screen } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; +/** + * Internal dependencies + */ import { SearchControl } from '../../../../sources/client/src/components/search-control'; -jest.mock('@wordpress/i18n', () => ({ - __: (text: string) => text, -})); +jest.mock( '@wordpress/i18n', () => ( { + __: ( text: string ) => text, +} ) ); -describe('SearchControl', () => { - it('renders the input within the label if the id prop is passed', () => { - render( {}} />); - expect(screen.getByLabelText('Search')).toMatchSnapshot(); - }); +describe( 'SearchControl', () => { + it( 'renders the input within the label if the id prop is passed', () => { + render( {} } /> ); + expect( screen.getByLabelText( 'Search' ) ).toMatchSnapshot(); + } ); - it('renders the input outside the label if the id prop is not passed', () => { + it( 'renders the input outside the label if the id prop is not passed', () => { const { container } = render( - {}} /> + {} } /> ); - expect(container.querySelector('[type="search"]')).toMatchSnapshot(); - }); + expect( + container.querySelector( '[type="search"]' ) + ).toMatchSnapshot(); + } ); - it('calls the onChange prop when the input value changes', async () => { + it( 'calls the onChange prop when the input value changes', async () => { const onChange = jest.fn(); - const { container } = render(); + const { container } = render( ); const input = container.querySelector( '[type="search"]' ) as HTMLInputElement; - await userEvent.type(input, 'test'); + await userEvent.type( input, 'test' ); - expect(input).toHaveValue('test'); - }); -}); + expect( input ).toHaveValue( 'test' ); + } ); +} ); diff --git a/tests/client/unit/components/singular-select-control.test.tsx b/tests/client/unit/components/singular-select-control.test.tsx index 5ea8acc..1f61d83 100644 --- a/tests/client/unit/components/singular-select-control.test.tsx +++ b/tests/client/unit/components/singular-select-control.test.tsx @@ -1,3 +1,6 @@ +/** + * External dependencies + */ import EntitiesSearch from '@types'; import React from 'react'; @@ -8,30 +11,34 @@ import userEvent from '@testing-library/user-event'; import { faker } from '@faker-js/faker'; +/** + * Internal dependencies + */ import { SingularSelectControl } from '../../../../sources/client/src/components/singular-select-control'; import { Set } from '../../../../sources/client/src/vo/set'; import { buildOptions } from '../utils'; -describe('Post Types Select', () => { +describe( 'Post Types Select', () => { /* * We want to ensure the internal logic of the component works as expected, we're not interested * in the React Select component, so we mock it. */ - it('call the given onChange handler', (done) => { - let expectedValue: EntitiesSearch.SingularControl['value'] = ''; - const option: EntitiesSearch.ControlOption = { - label: faker.word.words(2), + it( 'call the given onChange handler', ( done ) => { + let expectedValue: EntitiesSearch.SingularControl< string >[ 'value' ] = + ''; + const option: EntitiesSearch.ControlOption< string > = { + label: faker.word.words( 2 ), value: faker.word.noun(), }; - const options = new Set>() - .add(option) - .concat(buildOptions()); + const options = new Set< EntitiesSearch.ControlOption< string > >() + .add( option ) + .concat( buildOptions() ); const rendered = render( (expectedValue = value)} + options={ options } + value={ option.value } + onChange={ ( value ) => ( expectedValue = value ) } /> ); @@ -39,29 +46,29 @@ describe('Post Types Select', () => { '.wes-select-control' ) as HTMLSelectElement; - userEvent.selectOptions(select, option.value).then(() => { - expect(expectedValue).toBe(option.value); + userEvent.selectOptions( select, option.value ).then( () => { + expect( expectedValue ).toBe( option.value ); done(); - }); - }); + } ); + } ); - it('Render No Options Message', () => { + it( 'Render No Options Message', () => { const rendered = render( {}} + options={ new Set() } + value={ '' } + onChange={ () => {} } /> ); expect( - rendered.container.querySelector('.wes-no-option-message') + rendered.container.querySelector( '.wes-no-option-message' ) ).toBeInTheDocument(); - }); + } ); - it('does not change the value when an option is selected that does not exist', async () => { + it( 'does not change the value when an option is selected that does not exist', async () => { const props = { - options: new Set([ + options: new Set( [ { label: 'Option 1', value: 'option-one', @@ -70,11 +77,11 @@ describe('Post Types Select', () => { label: 'Option 2', value: 'option-two', }, - ]), + ] ), value: 'option-two', onChange: jest.fn(), }; - const rendered = render(); + const rendered = render( ); const select = rendered.container.querySelector( '.wes-select-control' @@ -85,8 +92,8 @@ describe('Post Types Select', () => { ) as HTMLOptionElement; option.value = 'option-3'; - await userEvent.selectOptions(select, option); + await userEvent.selectOptions( select, option ); - expect(props.onChange).not.toHaveBeenCalled(); - }); -}); + expect( props.onChange ).not.toHaveBeenCalled(); + } ); +} ); diff --git a/tests/client/unit/components/toggle-control.test.tsx b/tests/client/unit/components/toggle-control.test.tsx index 49330bd..c149621 100644 --- a/tests/client/unit/components/toggle-control.test.tsx +++ b/tests/client/unit/components/toggle-control.test.tsx @@ -1,3 +1,6 @@ +/** + * External dependencies + */ import React from 'react'; import { describe, expect, it, jest } from '@jest/globals'; @@ -5,79 +8,82 @@ import { describe, expect, it, jest } from '@jest/globals'; import { render, screen, fireEvent } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; +/** + * Internal dependencies + */ import { ToggleControl } from '../../../../sources/client/src/components/toggle-control'; import { Set } from '../../../../sources/client/src/vo/set'; -const options = new Set([ +const options = new Set( [ { label: 'Option 1', value: '1' }, { label: 'Option 2', value: '2' }, { label: 'Option 3', value: '3' }, -]); +] ); -describe('EntitiesToggleControl', () => { - it('renders correctly', () => { +describe( 'EntitiesToggleControl', () => { + it( 'renders correctly', () => { const { container } = render( {}} + value={ new Set( [ '1' ] ) } + options={ options } + onChange={ () => {} } /> ); - expect(container.firstChild).toMatchSnapshot(); - }); + expect( container.firstChild ).toMatchSnapshot(); + } ); - it('renders the NoOptionsMessage when there are no options', () => { + it( 'renders the NoOptionsMessage when there are no options', () => { const { container } = render( {}} + value={ new Set( [ '1' ] ) } + options={ new Set() } + onChange={ () => {} } /> ); - expect(container.firstChild).toMatchSnapshot(); - }); + expect( container.firstChild ).toMatchSnapshot(); + } ); - it('updates the value when one or more option are selected', () => { + it( 'updates the value when one or more option are selected', () => { const onChange = jest.fn(); render( ); - fireEvent.click(screen.getByLabelText('Option 1')); + fireEvent.click( screen.getByLabelText( 'Option 1' ) ); - expect(onChange).toHaveBeenCalledWith(new Set(['1'])); - }); + expect( onChange ).toHaveBeenCalledWith( new Set( [ '1' ] ) ); + } ); - it('updates the value when one or more option are unselected', () => { + it( 'updates the value when one or more option are unselected', () => { const onChange = jest.fn(); render( ); - fireEvent.click(screen.getByLabelText('Option 1')); + fireEvent.click( screen.getByLabelText( 'Option 1' ) ); - expect(onChange).toHaveBeenCalledWith(new Set(['2'])); - }); + expect( onChange ).toHaveBeenCalledWith( new Set( [ '2' ] ) ); + } ); - it('does not change the value when an option is selected that does not exist', async () => { + it( 'does not change the value when an option is selected that does not exist', async () => { const props = { - options: new Set([ + options: new Set( [ { label: 'Option 1', value: 'option-one', @@ -86,19 +92,19 @@ describe('EntitiesToggleControl', () => { label: 'Option 2', value: 'option-two', }, - ]), - value: new Set(['option-one']), + ] ), + value: new Set( [ 'option-one' ] ), onChange: jest.fn(), }; - const rendered = render(); + const rendered = render( ); const option = rendered.container.querySelector( '.wes-toggle-control-item__input-option-one' ) as HTMLOptionElement; option.value = 'option-3'; - await userEvent.click(option); + await userEvent.click( option ); - expect(props.onChange).not.toHaveBeenCalled(); - }); -}); + expect( props.onChange ).not.toHaveBeenCalled(); + } ); +} ); diff --git a/tests/client/unit/hooks/use-entities-options-storage.test.tsx b/tests/client/unit/hooks/use-entities-options-storage.test.tsx index 376771d..851b8e9 100644 --- a/tests/client/unit/hooks/use-entities-options-storage.test.tsx +++ b/tests/client/unit/hooks/use-entities-options-storage.test.tsx @@ -1,3 +1,6 @@ +/** + * External dependencies + */ import EntitiesSearch from '@types'; import React from 'react'; @@ -5,32 +8,40 @@ import { describe, expect, it, jest } from '@jest/globals'; import { act, render } from '@testing-library/react'; +/** + * WordPress dependencies + */ import { doAction } from '@wordpress/hooks'; +/** + * Internal dependencies + */ import { useEntitiesOptionsStorage } from '../../../../sources/client/src/hooks/use-entities-options-storage'; import { Set } from '../../../../sources/client/src/vo/set'; -jest.mock('@wordpress/hooks', () => ({ +jest.mock( '@wordpress/hooks', () => ( { doAction: jest.fn(), -})); +} ) ); -describe('Use Posts Options Storage', () => { - it('Ensure seachEntities is called with the right data on state hydratation.', async () => { - const kind = new Set(['post']); - const entities = new Set([1, 2, 3]); - const searchEntities = jest.fn(() => +describe( 'Use Posts Options Storage', () => { + it( 'Ensure seachEntities is called with the right data on state hydratation.', async () => { + const kind = new Set( [ 'post' ] ); + const entities = new Set( [ 1, 2, 3 ] ); + const searchEntities = jest.fn( () => Promise.resolve( - new Set([ + new Set( [ { label: 'post-title', value: 1, }, - ]) + ] ) ) - ) as jest.Mock>; + ) as jest.Mock< + EntitiesSearch.SearchEntitiesFunction< number, string > + >; const Component = () => { - useEntitiesOptionsStorage( + useEntitiesOptionsStorage< number, string >( { kind, entities, @@ -41,21 +52,21 @@ describe('Use Posts Options Storage', () => { return null; }; - await act(() => render()); + await act( () => render( ) ); - expect(searchEntities).toHaveBeenCalledWith('', kind, { + expect( searchEntities ).toHaveBeenCalledWith( '', kind, { exclude: entities, - }); - expect(searchEntities).toHaveBeenCalledWith('', kind, { + } ); + expect( searchEntities ).toHaveBeenCalledWith( '', kind, { include: entities, per_page: '-1', - }); - }); + } ); + } ); - it('Update the state based on the given kind and entities', async () => { - const kind = new Set(['post']); - const entities = new Set([1, 2, 3]); - const selectedEntitiesOptions = new Set([ + it( 'Update the state based on the given kind and entities', async () => { + const kind = new Set( [ 'post' ] ); + const entities = new Set( [ 1, 2, 3 ] ); + const selectedEntitiesOptions = new Set( [ { label: 'post-title-1', value: 1, @@ -68,8 +79,8 @@ describe('Use Posts Options Storage', () => { label: 'post-title-3', value: 3, }, - ]); - const currentEntitiesOptions = new Set([ + ] ); + const currentEntitiesOptions = new Set( [ { label: 'post-title-4', value: 4, @@ -82,26 +93,28 @@ describe('Use Posts Options Storage', () => { label: 'post-title-6', value: 6, }, - ]); + ] ); - const searchEntities = jest.fn((_phrase, _kind, options) => { - if (options?.include) { - return Promise.resolve(selectedEntitiesOptions); + const searchEntities = jest.fn( ( _phrase, _kind, options ) => { + if ( options?.include ) { + return Promise.resolve( selectedEntitiesOptions ); } return options?.include - ? Promise.resolve(selectedEntitiesOptions) - : Promise.resolve(currentEntitiesOptions); - }) as jest.Mock>; + ? Promise.resolve( selectedEntitiesOptions ) + : Promise.resolve( currentEntitiesOptions ); + } ) as jest.Mock< + EntitiesSearch.SearchEntitiesFunction< number, string > + >; const dispatch = jest.fn(); - jest.spyOn(React, 'useReducer').mockImplementation((_, state) => [ + jest.spyOn( React, 'useReducer' ).mockImplementation( ( _, state ) => [ state, dispatch, - ]); + ] ); const Component = () => { - useEntitiesOptionsStorage( + useEntitiesOptionsStorage< number, string >( { kind, entities, @@ -112,37 +125,37 @@ describe('Use Posts Options Storage', () => { return null; }; - await act(() => render()); + await act( () => render( ) ); - expect(dispatch).toHaveBeenCalledWith({ + expect( dispatch ).toHaveBeenCalledWith( { type: 'UPDATE_SELECTED_ENTITIES_OPTIONS', selectedEntitiesOptions, - }); - expect(dispatch).toHaveBeenCalledWith({ + } ); + expect( dispatch ).toHaveBeenCalledWith( { type: 'UPDATE_CONTEXTUAL_ENTITIES_OPTIONS', contextualEntitiesOptions: currentEntitiesOptions, - }); - expect(dispatch).toHaveBeenCalledWith({ + } ); + expect( dispatch ).toHaveBeenCalledWith( { type: 'UPDATE_CURRENT_ENTITIES_OPTIONS', currentEntitiesOptions, - }); - }); + } ); + } ); - it('Sete the current and selected entities options to an empty set if searchEntities fails', async () => { - const kind = new Set(['post']); - const entities = new Set([1, 2, 3]); - const searchEntities = jest.fn(() => - Promise.resolve(null) - ) as jest.Mock<() => Promise>; + it( 'Sete the current and selected entities options to an empty set if searchEntities fails', async () => { + const kind = new Set( [ 'post' ] ); + const entities = new Set( [ 1, 2, 3 ] ); + const searchEntities = jest.fn( () => + Promise.resolve( null ) + ) as jest.Mock< () => Promise< null > >; const dispatch = jest.fn(); - jest.spyOn(React, 'useReducer').mockImplementation((_, state) => [ + jest.spyOn( React, 'useReducer' ).mockImplementation( ( _, state ) => [ state, dispatch, - ]); + ] ); const Component = () => { - useEntitiesOptionsStorage( + useEntitiesOptionsStorage< number, string >( { kind, entities, @@ -154,40 +167,42 @@ describe('Use Posts Options Storage', () => { return null; }; - await act(() => render()); + await act( () => render( ) ); const expectedSet = new Set(); - expect(dispatch).toHaveBeenCalledWith({ + expect( dispatch ).toHaveBeenCalledWith( { type: 'UPDATE_SELECTED_ENTITIES_OPTIONS', selectedEntitiesOptions: expectedSet, - }); - expect(dispatch).toHaveBeenCalledWith({ + } ); + expect( dispatch ).toHaveBeenCalledWith( { type: 'UPDATE_CONTEXTUAL_ENTITIES_OPTIONS', contextualEntitiesOptions: expectedSet, - }); - expect(dispatch).toHaveBeenCalledWith({ + } ); + expect( dispatch ).toHaveBeenCalledWith( { type: 'UPDATE_CURRENT_ENTITIES_OPTIONS', currentEntitiesOptions: expectedSet, - }); - }); + } ); + } ); - it('Does not call searchEntities with includes if entities is empty', async () => { - const kind = new Set(['post']); + it( 'Does not call searchEntities with includes if entities is empty', async () => { + const kind = new Set( [ 'post' ] ); const entities = new Set(); - const searchEntities = jest.fn(() => + const searchEntities = jest.fn( () => Promise.resolve( - new Set([ + new Set( [ { label: 'post-title', value: 1, }, - ]) + ] ) ) - ) as jest.Mock>; + ) as jest.Mock< + EntitiesSearch.SearchEntitiesFunction< number, string > + >; const Component = () => { - useEntitiesOptionsStorage( + useEntitiesOptionsStorage< number, string >( { kind, // @ts-ignore @@ -199,27 +214,27 @@ describe('Use Posts Options Storage', () => { return null; }; - await act(() => render()); + await act( () => render( ) ); - expect(searchEntities).toHaveBeenCalledTimes(1); - expect(searchEntities).toHaveBeenCalledWith('', kind, { + expect( searchEntities ).toHaveBeenCalledTimes( 1 ); + expect( searchEntities ).toHaveBeenCalledWith( '', kind, { exclude: entities, - }); - expect(searchEntities).not.toHaveBeenCalledWith('', kind, { + } ); + expect( searchEntities ).not.toHaveBeenCalledWith( '', kind, { include: entities, per_page: '-1', - }); - }); - - it('Execute the action wp-entities-search.on-storage-initialization.error when there is an error on searchEntites', async () => { - const kind = new Set(['post']); - const entities = new Set([1, 2, 3]); - const searchEntities = jest.fn(() => - Promise.reject('Search Entities Failed.') + } ); + } ); + + it( 'Execute the action wp-entities-search.on-storage-initialization.error when there is an error on searchEntites', async () => { + const kind = new Set( [ 'post' ] ); + const entities = new Set( [ 1, 2, 3 ] ); + const searchEntities = jest.fn( () => + Promise.reject( 'Search Entities Failed.' ) ); const Component = () => { - useEntitiesOptionsStorage( + useEntitiesOptionsStorage< number, string >( { kind, entities, @@ -231,11 +246,11 @@ describe('Use Posts Options Storage', () => { return null; }; - await act(() => render()); + await act( () => render( ) ); - expect(doAction).toHaveBeenCalledWith( + expect( doAction ).toHaveBeenCalledWith( 'wp-entities-search.on-storage-initialization.error', 'Search Entities Failed.' ); - }); -}); + } ); +} ); diff --git a/tests/client/unit/hooks/use-entity-records.test.ts b/tests/client/unit/hooks/use-entity-records.test.ts index 356ca94..b37ace0 100644 --- a/tests/client/unit/hooks/use-entity-records.test.ts +++ b/tests/client/unit/hooks/use-entity-records.test.ts @@ -1,118 +1,127 @@ +/** + * External dependencies + */ import { fromPartial } from '@total-typescript/shoehorn'; import EntitiesSearch from '@types'; import { describe, it, jest, expect } from '@jest/globals'; +/** + * WordPress dependencies + */ import { useEntityRecords as useCoreEntityRecords } from '@wordpress/core-data'; +/** + * Internal dependencies + */ import { useEntityRecords } from '../../../../sources/client/src'; -jest.mock('@wordpress/core-data', () => ({ +jest.mock( '@wordpress/core-data', () => ( { useEntityRecords: jest.fn(), -})); +} ) ); -describe('useEntityRecords', () => { +describe( 'useEntityRecords', () => { /* * We want to guarantee the Records are returned and the hook succeeds. * Shouldn't be possible to obtain a succeed status if the records are resolving or errored. */ - it('return set of records and succeed', () => { + it( 'return set of records and succeed', () => { const records = [ - fromPartial>({ + fromPartial< EntitiesSearch.PostType< 'edit' > >( { name: 'foo', - }), - fromPartial>({ + } ), + fromPartial< EntitiesSearch.PostType< 'edit' > >( { name: 'foo-3', - }), + } ), ]; - jest.mocked(useCoreEntityRecords).mockReturnValue({ + jest.mocked( useCoreEntityRecords ).mockReturnValue( { records, hasResolved: true, isResolving: false, // @ts-ignore status: 'SUCCESS', - }); + } ); - const result = useEntityRecords>( + const result = useEntityRecords< EntitiesSearch.PostType< 'edit' > >( 'root', 'postType' ); - expect(result.records().length()).toEqual(2); - for (const record of records) { - expect(result.records()).toContain(record); + expect( result.records().length() ).toEqual( 2 ); + for ( const record of records ) { + expect( result.records() ).toContain( record ); } - expect(result.isResolving()).toEqual(false); - expect(result.errored()).toEqual(false); - expect(result.succeed()).toEqual(true); - }); + expect( result.isResolving() ).toEqual( false ); + expect( result.errored() ).toEqual( false ); + expect( result.succeed() ).toEqual( true ); + } ); /* * We want to guarantee `records()` returns null when the hook is resolving. */ - it('return an empty collection of records when resolving', () => { - jest.mocked(useCoreEntityRecords).mockReturnValue({ + it( 'return an empty collection of records when resolving', () => { + jest.mocked( useCoreEntityRecords ).mockReturnValue( { records: null, hasResolved: false, isResolving: true, // @ts-ignore status: 'RESOLVING', - }); + } ); - const result = useEntityRecords>( + const result = useEntityRecords< EntitiesSearch.PostType< 'edit' > >( 'root', 'postType' ); - expect(result.records().length()).toEqual(0); - expect(result.isResolving()).toEqual(true); - expect(result.errored()).toEqual(false); - expect(result.succeed()).toEqual(false); - }); + expect( result.records().length() ).toEqual( 0 ); + expect( result.isResolving() ).toEqual( true ); + expect( result.errored() ).toEqual( false ); + expect( result.succeed() ).toEqual( false ); + } ); /* * We want to errored and succeed are not returning the same value. */ - it('return errored and succeed as different values', () => { - jest.mocked(useCoreEntityRecords).mockReturnValue({ + it( 'return errored and succeed as different values', () => { + jest.mocked( useCoreEntityRecords ).mockReturnValue( { records: null, hasResolved: true, isResolving: false, // @ts-ignore status: 'ERROR', - }); + } ); - const result = useEntityRecords>( + const result = useEntityRecords< EntitiesSearch.PostType< 'edit' > >( 'root', 'postType' ); - expect(result.isResolving()).toEqual(false); - expect(result.errored()).toEqual(true); - expect(result.succeed()).toEqual(false); - }); + expect( result.isResolving() ).toEqual( false ); + expect( result.errored() ).toEqual( true ); + expect( result.succeed() ).toEqual( false ); + } ); /* * We want to guarantee the errored and success statuses are not returning `true` while resolving. */ - it('return errored and succeed as false while resolving', () => { - jest.mocked(useCoreEntityRecords).mockReturnValue({ + it( 'return errored and succeed as false while resolving', () => { + jest.mocked( useCoreEntityRecords ).mockReturnValue( { records: null, hasResolved: false, isResolving: true, // @ts-ignore status: 'RESOLVING', - }); + } ); - const result = useEntityRecords>( + const result = useEntityRecords< EntitiesSearch.PostType< 'edit' > >( 'root', 'postType' ); - expect(result.isResolving()).toEqual(true); - expect(result.errored()).toEqual(false); - expect(result.succeed()).toEqual(false); - }); -}); + expect( result.isResolving() ).toEqual( true ); + expect( result.errored() ).toEqual( false ); + expect( result.succeed() ).toEqual( false ); + } ); +} ); diff --git a/tests/client/unit/hooks/use-query-viewable-post-types.test.ts b/tests/client/unit/hooks/use-query-viewable-post-types.test.ts index cb58de3..c05a1a9 100644 --- a/tests/client/unit/hooks/use-query-viewable-post-types.test.ts +++ b/tests/client/unit/hooks/use-query-viewable-post-types.test.ts @@ -1,58 +1,64 @@ +/** + * External dependencies + */ import { fromPartial } from '@total-typescript/shoehorn'; import EntitiesSearch from '@types'; import { describe, it, jest, expect } from '@jest/globals'; +/** + * Internal dependencies + */ import { useEntityRecords } from '../../../../sources/client/src/hooks/use-entity-records'; import { useQueryViewablePostTypes } from '../../../../sources/client/src/hooks/use-query-viewable-post-types'; import { Set } from '../../../../sources/client/src/vo/set'; -jest.mock('@wordpress/data', () => { +jest.mock( '@wordpress/data', () => { return { useSelect: jest.fn(), }; -}); -jest.mock('../../../../sources/client/src/hooks/use-entity-records', () => { +} ); +jest.mock( '../../../../sources/client/src/hooks/use-entity-records', () => { return { useEntityRecords: jest.fn(), }; -}); +} ); -describe('Post Types Query', () => { - it('retrieve all viewable post types', () => { - jest.mocked(useEntityRecords).mockReturnValue({ +describe( 'Post Types Query', () => { + it( 'retrieve all viewable post types', () => { + jest.mocked( useEntityRecords ).mockReturnValue( { isResolving: () => false, errored: () => false, succeed: () => true, records: () => - new Set([ - fromPartial>({ + new Set( [ + fromPartial< EntitiesSearch.PostType< 'edit' > >( { slug: 'viewable-post-type', viewable: true, - }), - fromPartial>({ + } ), + fromPartial< EntitiesSearch.PostType< 'edit' > >( { slug: 'not-viewable-post-type', viewable: false, - }), - ]), - }); + } ), + ] ), + } ); const postTypes = useQueryViewablePostTypes(); - expect(postTypes.records().length()).toEqual(1); - for (const viewablePostType of postTypes.records()) { - expect(viewablePostType.viewable).toEqual(true); + expect( postTypes.records().length() ).toEqual( 1 ); + for ( const viewablePostType of postTypes.records() ) { + expect( viewablePostType.viewable ).toEqual( true ); } - }); + } ); - it('returns empty set while resolving the post types', () => { - jest.mocked(useEntityRecords).mockReturnValue({ + it( 'returns empty set while resolving the post types', () => { + jest.mocked( useEntityRecords ).mockReturnValue( { isResolving: () => true, errored: () => false, succeed: () => false, records: () => new Set(), - }); + } ); const viewablePostTypes = useQueryViewablePostTypes(); - expect(viewablePostTypes.records().length()).toEqual(0); - }); -}); + expect( viewablePostTypes.records().length() ).toEqual( 0 ); + } ); +} ); diff --git a/tests/client/unit/hooks/use-query-viewable-taxonomies.test.ts b/tests/client/unit/hooks/use-query-viewable-taxonomies.test.ts index d602561..cfdff7c 100644 --- a/tests/client/unit/hooks/use-query-viewable-taxonomies.test.ts +++ b/tests/client/unit/hooks/use-query-viewable-taxonomies.test.ts @@ -1,53 +1,59 @@ +/** + * External dependencies + */ import { fromPartial } from '@total-typescript/shoehorn'; import EntitiesSearch from '@types'; import { describe, expect, it, jest } from '@jest/globals'; +/** + * Internal dependencies + */ import { useEntityRecords } from '../../../../sources/client/src/hooks/use-entity-records'; import { useQueryViewableTaxonomies } from '../../../../sources/client/src/hooks/use-query-viewable-taxonomies'; import { Set } from '../../../../sources/client/src/vo/set'; -jest.mock('../../../../sources/client/src/hooks/use-entity-records', () => { +jest.mock( '../../../../sources/client/src/hooks/use-entity-records', () => { return { useEntityRecords: jest.fn(), }; -}); +} ); -describe('useQueryViewableTaxonomies', () => { - it('should return the viewable taxonomies', () => { - jest.mocked(useEntityRecords).mockReturnValue({ +describe( 'useQueryViewableTaxonomies', () => { + it( 'should return the viewable taxonomies', () => { + jest.mocked( useEntityRecords ).mockReturnValue( { isResolving: () => false, errored: () => false, succeed: () => true, records: () => - new Set([ - fromPartial>({ + new Set( [ + fromPartial< EntitiesSearch.Taxonomy< 'edit' > >( { name: 'Category', visibility: { publicly_queryable: true, }, - }), - fromPartial>({ + } ), + fromPartial< EntitiesSearch.Taxonomy< 'edit' > >( { name: 'Tag', visibility: { publicly_queryable: true, }, - }), - fromPartial>({ + } ), + fromPartial< EntitiesSearch.Taxonomy< 'edit' > >( { name: 'Author', visibility: { publicly_queryable: false, }, - }), - ]), - }); + } ), + ] ), + } ); const taxonomies = useQueryViewableTaxonomies(); - expect(taxonomies.records().length()).toEqual(2); - for (const viewableTaxonomy of taxonomies.records()) { - expect(viewableTaxonomy.visibility.publicly_queryable).toEqual( + expect( taxonomies.records().length() ).toEqual( 2 ); + for ( const viewableTaxonomy of taxonomies.records() ) { + expect( viewableTaxonomy.visibility.publicly_queryable ).toEqual( true ); } - }); -}); + } ); +} ); diff --git a/tests/client/unit/hooks/use-search.test.ts b/tests/client/unit/hooks/use-search.test.ts index bac4c3f..fa2ee60 100644 --- a/tests/client/unit/hooks/use-search.test.ts +++ b/tests/client/unit/hooks/use-search.test.ts @@ -4,87 +4,96 @@ * We also want to mock the searchEntities function to return a set of options or when necessary throw an error. * The dispatch function is also mocked to check if the state is updated correctly. */ +/** + * External dependencies + */ import EntitiesSearch from '@types'; import { expect, it, describe, beforeEach, jest } from '@jest/globals'; import { renderHook, act } from '@testing-library/react'; +/** + * WordPress dependencies + */ import { doAction } from '@wordpress/hooks'; +/** + * Internal dependencies + */ import { useSearch } from '../../../../sources/client/src/hooks/use-search'; import { Set } from '../../../../sources/client/src/vo/set'; -jest.mock('@wordpress/compose', () => ({ - useThrottle: (callback: (phrase: string) => void) => callback, -})); -jest.mock('@wordpress/hooks', () => ({ +jest.mock( '@wordpress/compose', () => ( { + useThrottle: ( callback: ( phrase: string ) => void ) => callback, +} ) ); +jest.mock( '@wordpress/hooks', () => ( { doAction: jest.fn(), -})); +} ) ); -describe('useSearch', () => { - beforeEach(() => { +describe( 'useSearch', () => { + beforeEach( () => { jest.clearAllMocks(); - }); - - it('Should update the search phrase', async () => { - const searchEntities = jest.fn(() => - Promise.resolve(new Set([{ label: 'Label', value: 1 }])) - ) as jest.Mock>; - const kind = new Set(['post']); - const entities = new Set(); + } ); + + it( 'Should update the search phrase', async () => { + const searchEntities = jest.fn( () => + Promise.resolve( new Set( [ { label: 'Label', value: 1 } ] ) ) + ) as jest.Mock< EntitiesSearch.SearchEntitiesFunction< any, any > >; + const kind = new Set( [ 'post' ] ); + const entities = new Set< number >(); const dispatch = jest.fn(); - const { result } = renderHook(() => - useSearch(searchEntities, kind, entities, dispatch) + const { result } = renderHook( () => + useSearch( searchEntities, kind, entities, dispatch ) ); - await act(() => { - result.current('phrase'); - }); + await act( () => { + result.current( 'phrase' ); + } ); - expect(dispatch).toHaveBeenCalledWith({ + expect( dispatch ).toHaveBeenCalledWith( { type: 'UPDATE_SEARCH_PHRASE', searchPhrase: 'phrase', - }); - }); - - it('Should search the entities', () => { - const searchEntities = jest.fn(() => - Promise.resolve(new Set([{ label: 'Label', value: 1 }])) - ) as jest.Mock>; - const kind = new Set(['post']); - const entities = new Set(); + } ); + } ); + + it( 'Should search the entities', () => { + const searchEntities = jest.fn( () => + Promise.resolve( new Set( [ { label: 'Label', value: 1 } ] ) ) + ) as jest.Mock< EntitiesSearch.SearchEntitiesFunction< any, any > >; + const kind = new Set( [ 'post' ] ); + const entities = new Set< number >(); const dispatch = jest.fn(); - const { result } = renderHook(() => - useSearch(searchEntities, kind, entities, dispatch) + const { result } = renderHook( () => + useSearch( searchEntities, kind, entities, dispatch ) ); - act(() => { - result.current('phrase'); - }); + act( () => { + result.current( 'phrase' ); + } ); - expect(searchEntities).toHaveBeenCalledTimes(1); - expect(searchEntities).toHaveBeenCalledWith('phrase', kind, { + expect( searchEntities ).toHaveBeenCalledTimes( 1 ); + expect( searchEntities ).toHaveBeenCalledWith( 'phrase', kind, { exclude: entities, - }); - }); - - it('Should update the current entities options', async () => { - const searchEntities = jest.fn(() => - Promise.resolve(new Set([{ label: 'Label', value: 1 }])) - ) as jest.Mock>; - const kind = new Set(['post']); - const entities = new Set(); + } ); + } ); + + it( 'Should update the current entities options', async () => { + const searchEntities = jest.fn( () => + Promise.resolve( new Set( [ { label: 'Label', value: 1 } ] ) ) + ) as jest.Mock< EntitiesSearch.SearchEntitiesFunction< any, any > >; + const kind = new Set( [ 'post' ] ); + const entities = new Set< number >(); const dispatch = jest.fn(); - const { result } = renderHook(() => - useSearch(searchEntities, kind, entities, dispatch) + const { result } = renderHook( () => + useSearch( searchEntities, kind, entities, dispatch ) ); searchEntities.mockResolvedValueOnce( - new Set([ + new Set( [ { value: 1, label: 'Post One', @@ -93,16 +102,16 @@ describe('useSearch', () => { value: 2, label: 'Post Two', }, - ]) + ] ) ); - await act(() => { - result.current('phrase'); - }); + await act( () => { + result.current( 'phrase' ); + } ); - expect(dispatch).toHaveBeenCalledWith({ + expect( dispatch ).toHaveBeenCalledWith( { type: 'UPDATE_CURRENT_ENTITIES_OPTIONS', - currentEntitiesOptions: new Set([ + currentEntitiesOptions: new Set( [ { value: 1, label: 'Post One', @@ -111,32 +120,32 @@ describe('useSearch', () => { value: 2, label: 'Post Two', }, - ]), - }); - }); - - it('Should update the current entities options with an empty set when an error occurs', async () => { - const searchEntities = jest.fn(() => - Promise.resolve(new Set([{ label: 'Label', value: 1 }])) - ) as jest.Mock>; - const kind = new Set(['post']); - const entities = new Set(); + ] ), + } ); + } ); + + it( 'Should update the current entities options with an empty set when an error occurs', async () => { + const searchEntities = jest.fn( () => + Promise.resolve( new Set( [ { label: 'Label', value: 1 } ] ) ) + ) as jest.Mock< EntitiesSearch.SearchEntitiesFunction< any, any > >; + const kind = new Set( [ 'post' ] ); + const entities = new Set< number >(); const dispatch = jest.fn(); - const { result } = renderHook(() => - useSearch(searchEntities, kind, entities, dispatch) + const { result } = renderHook( () => + useSearch( searchEntities, kind, entities, dispatch ) ); - searchEntities.mockRejectedValueOnce(new Error('Error')); + searchEntities.mockRejectedValueOnce( new Error( 'Error' ) ); - await act(() => { - result.current('phrase'); - }); + await act( () => { + result.current( 'phrase' ); + } ); - expect(jest.mocked(doAction)).toHaveBeenCalledTimes(1); - expect(jest.mocked(doAction)).toHaveBeenCalledWith( + expect( jest.mocked( doAction ) ).toHaveBeenCalledTimes( 1 ); + expect( jest.mocked( doAction ) ).toHaveBeenCalledWith( 'wp-entities-search.on-search.error', - new Error('Error') + new Error( 'Error' ) ); - }); -}); + } ); +} ); diff --git a/tests/client/unit/services/abort-controllers.test.ts b/tests/client/unit/services/abort-controllers.test.ts index ce43f16..2e9423e 100644 --- a/tests/client/unit/services/abort-controllers.test.ts +++ b/tests/client/unit/services/abort-controllers.test.ts @@ -1,32 +1,38 @@ +/** + * External dependencies + */ import { it, jest, describe, expect, beforeEach } from '@jest/globals'; +/** + * Internal dependencies + */ import { abortControllers } from '../../../../sources/client/src/services/abort-controllers'; import { ContextualAbortController } from '../../../../sources/client/src/services/contextual-abort-controller'; -describe('AbortControllers', () => { +describe( 'AbortControllers', () => { let controller: ContextualAbortController; - beforeEach(() => { - controller = new ContextualAbortController('context', 'reason'); - }); + beforeEach( () => { + controller = new ContextualAbortController( 'context', 'reason' ); + } ); - it('should add a controller', () => { - abortControllers.add(controller); - expect(abortControllers.has(controller)).toBe(true); - }); + it( 'should add a controller', () => { + abortControllers.add( controller ); + expect( abortControllers.has( controller ) ).toBe( true ); + } ); - it('should delete a controller', () => { - abortControllers.add(controller); - expect(abortControllers.has(controller)).toBe(true); + it( 'should delete a controller', () => { + abortControllers.add( controller ); + expect( abortControllers.has( controller ) ).toBe( true ); - abortControllers.delete(controller); - expect(abortControllers.has(controller)).toBe(false); - }); + abortControllers.delete( controller ); + expect( abortControllers.has( controller ) ).toBe( false ); + } ); - it('should abort a controller', () => { - const spy = jest.spyOn(controller, 'abort'); - abortControllers.add(controller); - abortControllers.add(controller); - expect(spy).toHaveBeenCalled(); - }); -}); + it( 'should abort a controller', () => { + const spy = jest.spyOn( controller, 'abort' ); + abortControllers.add( controller ); + abortControllers.add( controller ); + expect( spy ).toHaveBeenCalled(); + } ); +} ); diff --git a/tests/client/unit/services/contextual-abort-controller.test.ts b/tests/client/unit/services/contextual-abort-controller.test.ts index a51ddb6..cdcf224 100644 --- a/tests/client/unit/services/contextual-abort-controller.test.ts +++ b/tests/client/unit/services/contextual-abort-controller.test.ts @@ -1,45 +1,54 @@ +/** + * External dependencies + */ import { afterEach, beforeEach, describe, it, expect } from '@jest/globals'; +/** + * Internal dependencies + */ import { ContextualAbortController } from '../../../../sources/client/src/services/contextual-abort-controller'; -describe('ContextualAbortController', () => { +describe( 'ContextualAbortController', () => { let controller: ContextualAbortController; - beforeEach(() => { - controller = new ContextualAbortController('testContext', 'testReason'); - }); + beforeEach( () => { + controller = new ContextualAbortController( + 'testContext', + 'testReason' + ); + } ); - afterEach(() => { + afterEach( () => { controller.abort(); - }); + } ); - it('should create a new instance of ContextualAbortController', () => { - expect(controller).toBeInstanceOf(ContextualAbortController); - }); + it( 'should create a new instance of ContextualAbortController', () => { + expect( controller ).toBeInstanceOf( ContextualAbortController ); + } ); - it('should return the correct context', () => { - expect(controller.context()).toBe('testContext'); - }); + it( 'should return the correct context', () => { + expect( controller.context() ).toBe( 'testContext' ); + } ); - it('should abort the controller', () => { + it( 'should abort the controller', () => { controller.abort(); - expect(controller.isAborted()).toBe(true); - }); + expect( controller.isAborted() ).toBe( true ); + } ); - it('should return the correct signal', () => { + it( 'should return the correct signal', () => { const signal = controller.signal(); - expect(signal).toBeInstanceOf(AbortSignal); - }); + expect( signal ).toBeInstanceOf( AbortSignal ); + } ); - it('should return the correct aborted state', () => { - expect(controller.isAborted()).toBe(false); + it( 'should return the correct aborted state', () => { + expect( controller.isAborted() ).toBe( false ); controller.abort(); - expect(controller.isAborted()).toBe(true); - }); + expect( controller.isAborted() ).toBe( true ); + } ); - it('should throw an error if the context is empty', () => { + it( 'should throw an error if the context is empty', () => { expect( - () => new ContextualAbortController('', 'testReason') - ).toThrowError('Abort Controllers, context cannot be empty'); - }); -}); + () => new ContextualAbortController( '', 'testReason' ) + ).toThrowError( 'Abort Controllers, context cannot be empty' ); + } ); +} ); diff --git a/tests/client/unit/storage/initial-state.test.ts b/tests/client/unit/storage/initial-state.test.ts index 36a4763..7ccf28e 100644 --- a/tests/client/unit/storage/initial-state.test.ts +++ b/tests/client/unit/storage/initial-state.test.ts @@ -1,12 +1,18 @@ +/** + * External dependencies + */ import { describe, expect, it } from '@jest/globals'; +/** + * Internal dependencies + */ import { makeInitialState } from '../../../../sources/client/src/storage/entities/initial-state'; -describe('Initial state', () => { - it('ensure all options are empty', () => { - const initialState = makeInitialState({}); - expect(initialState.contextualEntitiesOptions.length()).toBe(0); - expect(initialState.currentEntitiesOptions.length()).toBe(0); - expect(initialState.selectedEntitiesOptions.length()).toBe(0); - }); -}); +describe( 'Initial state', () => { + it( 'ensure all options are empty', () => { + const initialState = makeInitialState( {} ); + expect( initialState.contextualEntitiesOptions.length() ).toBe( 0 ); + expect( initialState.currentEntitiesOptions.length() ).toBe( 0 ); + expect( initialState.selectedEntitiesOptions.length() ).toBe( 0 ); + } ); +} ); diff --git a/tests/client/unit/storage/reducer.test.ts b/tests/client/unit/storage/reducer.test.ts index 8803f46..4dd7024 100644 --- a/tests/client/unit/storage/reducer.test.ts +++ b/tests/client/unit/storage/reducer.test.ts @@ -1,77 +1,83 @@ +/** + * External dependencies + */ import EntitiesSearch from '@types'; import { describe, expect, it } from '@jest/globals'; import { faker } from '@faker-js/faker'; +/** + * Internal dependencies + */ import { reducer } from '../../../../sources/client/src/storage/entities/reducer'; import { Set } from '../../../../sources/client/src/vo/set'; -let state: EntitiesSearch.EntitiesState; +let state: EntitiesSearch.EntitiesState< number, string >; -describe('reducer', () => { - it('Update the Entities', () => { - const entities = new Set(); - const newState = reducer(state, { +describe( 'reducer', () => { + it( 'Update the Entities', () => { + const entities = new Set< number >(); + const newState = reducer( state, { type: 'UPDATE_ENTITIES', entities, - }); + } ); - expect(newState.entities).toEqual(entities); - }); + expect( newState.entities ).toEqual( entities ); + } ); - it('Update the Kind', () => { - const kind = new Set(['post']); - const newState = reducer(state, { + it( 'Update the Kind', () => { + const kind = new Set( [ 'post' ] ); + const newState = reducer( state, { type: 'UPDATE_KIND', kind, - }); + } ); - expect(newState.kind).toEqual(kind); - }); + expect( newState.kind ).toEqual( kind ); + } ); - it('Update the Contextual Posts Options', () => { - const contextualPostsOptions = new Set([ + it( 'Update the Contextual Posts Options', () => { + const contextualPostsOptions = new Set( [ { - value: faker.number.int(10), + value: faker.number.int( 10 ), label: 'Post One', }, { - value: faker.number.int({ min: 11, max: 20 }), + value: faker.number.int( { min: 11, max: 20 } ), label: 'Post Two', }, - ]); - const newState = reducer(state, { + ] ); + const newState = reducer( state, { type: 'UPDATE_CONTEXTUAL_ENTITIES_OPTIONS', contextualEntitiesOptions: contextualPostsOptions, - }); + } ); - expect(newState.contextualEntitiesOptions).toEqual( + expect( newState.contextualEntitiesOptions ).toEqual( contextualPostsOptions ); - }); + } ); - it('Update the Posts Options', () => { - const postsOptions = new Set([ + it( 'Update the Posts Options', () => { + const postsOptions = new Set( [ { - value: faker.number.int(10), + value: faker.number.int( 10 ), label: 'Post Two', }, { - value: faker.number.int({ min: 11, max: 20 }), + value: faker.number.int( { min: 11, max: 20 } ), label: 'Post Three', }, - ]); - const newState = reducer(state, { + ] ); + const newState = reducer( state, { type: 'UPDATE_CURRENT_ENTITIES_OPTIONS', currentEntitiesOptions: postsOptions, - }); + } ); - expect(newState.currentEntitiesOptions).toEqual(postsOptions); - }); + expect( newState.currentEntitiesOptions ).toEqual( postsOptions ); + } ); - it('Update the Selected Posts Options with Control Options', () => { - const posts = new Set([ + it( 'Update the Selected Posts Options with Control Options', () => { + const posts = new Set( [ { value: 55, label: 'Post FiftyFive', @@ -80,66 +86,66 @@ describe('reducer', () => { value: 10, label: 'Post Ten', }, - ]); - const newState = reducer(state, { + ] ); + const newState = reducer( state, { type: 'UPDATE_SELECTED_ENTITIES_OPTIONS', selectedEntitiesOptions: posts, - }); + } ); - expect(newState.selectedEntitiesOptions).toEqual(posts); - }); + expect( newState.selectedEntitiesOptions ).toEqual( posts ); + } ); - it('does nothing if the action type is not recognized', () => { - const newState = reducer(state, { + it( 'does nothing if the action type is not recognized', () => { + const newState = reducer( state, { // @ts-ignore type: 'NOT_RECOGNIZED_ACTION', - }); + } ); - expect(newState).toEqual(state); - }); + expect( newState ).toEqual( state ); + } ); - it('update search phrase', () => { + it( 'update search phrase', () => { const searchPhrase = faker.lorem.word(); - const newState = reducer(state, { + const newState = reducer( state, { type: 'UPDATE_SEARCH_PHRASE', searchPhrase, - }); + } ); - expect(newState.searchPhrase).toEqual(searchPhrase); - }); + expect( newState.searchPhrase ).toEqual( searchPhrase ); + } ); - it('clean entities options', () => { - const newState = reducer(state, { + it( 'clean entities options', () => { + const newState = reducer( state, { type: 'CLEAN_ENTITIES_OPTIONS', - }); + } ); - expect(newState.selectedEntitiesOptions).toEqual(new Set()); - expect(newState.contextualEntitiesOptions).toEqual(new Set()); - expect(newState.currentEntitiesOptions).toEqual(new Set()); - }); + expect( newState.selectedEntitiesOptions ).toEqual( new Set() ); + expect( newState.contextualEntitiesOptions ).toEqual( new Set() ); + expect( newState.currentEntitiesOptions ).toEqual( new Set() ); + } ); - it('update entities options for new kind', () => { - const kind = new Set(['post']); - const entitiesOptions = new Set([ + it( 'update entities options for new kind', () => { + const kind = new Set( [ 'post' ] ); + const entitiesOptions = new Set( [ { - value: faker.number.int(10), + value: faker.number.int( 10 ), label: 'Post One', }, { - value: faker.number.int({ min: 11, max: 20 }), + value: faker.number.int( { min: 11, max: 20 } ), label: 'Post Two', }, - ]); - const newState = reducer(state, { + ] ); + const newState = reducer( state, { type: 'UPDATE_ENTITIES_OPTIONS_FOR_NEW_KIND', kind, entitiesOptions, - }); - - expect(newState.contextualEntitiesOptions).toEqual(entitiesOptions); - expect(newState.currentEntitiesOptions).toEqual(entitiesOptions); - expect(newState.selectedEntitiesOptions).toEqual(new Set()); - expect(newState.entities).toEqual(new Set()); - expect(newState.kind).toEqual(kind); - }); -}); + } ); + + expect( newState.contextualEntitiesOptions ).toEqual( entitiesOptions ); + expect( newState.currentEntitiesOptions ).toEqual( entitiesOptions ); + expect( newState.selectedEntitiesOptions ).toEqual( new Set() ); + expect( newState.entities ).toEqual( new Set() ); + expect( newState.kind ).toEqual( kind ); + } ); +} ); diff --git a/tests/client/unit/utils.ts b/tests/client/unit/utils.ts index 3d02bac..6bd695f 100644 --- a/tests/client/unit/utils.ts +++ b/tests/client/unit/utils.ts @@ -1,17 +1,23 @@ +/** + * External dependencies + */ import EntitiesSearch from '@types'; import { faker } from '@faker-js/faker'; +/** + * Internal dependencies + */ import { Set } from '../../../sources/client/src/vo/set'; -export function buildOptions(): Set> { - let options = new Set>(); +export function buildOptions(): Set< EntitiesSearch.ControlOption< string > > { + let options = new Set< EntitiesSearch.ControlOption< string > >(); - for (let count = 0; count < 9; ++count) { - options = options.add({ - label: faker.word.words(2), + for ( let count = 0; count < 9; ++count ) { + options = options.add( { + label: faker.word.words( 2 ), value: faker.word.noun(), - }); + } ); } return options; diff --git a/tests/client/unit/utils/assert.test.ts b/tests/client/unit/utils/assert.test.ts index 48807a4..f44cbb4 100644 --- a/tests/client/unit/utils/assert.test.ts +++ b/tests/client/unit/utils/assert.test.ts @@ -1,16 +1,19 @@ -/* - * Write tests cases to ensure the assert function behaves as expected +/** + * External dependencies */ import { expect, it, describe } from '@jest/globals'; +/** + * Internal dependencies + */ import { assert } from '../../../../sources/client/src/utils/assert'; -describe('assert', () => { - it('should throw an error if the condition is false', () => { - expect(() => assert(false, 'Failed')).toThrow(); - }); +describe( 'assert', () => { + it( 'should throw an error if the condition is false', () => { + expect( () => assert( false, 'Failed' ) ).toThrow(); + } ); - it('should not throw an error if the condition is true', () => { - expect(() => assert(true, 'Failed')).not.toThrow(); - }); -}); + it( 'should not throw an error if the condition is true', () => { + expect( () => assert( true, 'Failed' ) ).not.toThrow(); + } ); +} ); diff --git a/tests/client/unit/utils/convert-entities-to-control-options.test.ts b/tests/client/unit/utils/convert-entities-to-control-options.test.ts index 2b0a720..2d4eb86 100644 --- a/tests/client/unit/utils/convert-entities-to-control-options.test.ts +++ b/tests/client/unit/utils/convert-entities-to-control-options.test.ts @@ -1,3 +1,6 @@ +/** + * External dependencies + */ import { fromPartial } from '@total-typescript/shoehorn'; import EntitiesSearch from '@types'; @@ -5,22 +8,25 @@ import { describe, expect, it } from '@jest/globals'; import { faker } from '@faker-js/faker'; +/** + * Internal dependencies + */ import { convertEntitiesToControlOptions } from '../../../../sources/client/src/utils/convert-entities-to-control-options'; import { Set } from '../../../../sources/client/src/vo/set'; -describe('Convert Entities To Control Options', () => { - it('correctly convert entities to control options', () => { +describe( 'Convert Entities To Control Options', () => { + it( 'correctly convert entities to control options', () => { const rawEntities = []; - for (let count = 0; count < 10; ++count) { + for ( let count = 0; count < 10; ++count ) { rawEntities.push( - fromPartial({ + fromPartial< EntitiesSearch.SearchEntityFields >( { title: faker.word.noun(), id: faker.number.int(), - }) + } ) ); } - const entities = new Set( + const entities = new Set< EntitiesSearch.SearchEntityFields >( rawEntities ); @@ -28,30 +34,30 @@ describe('Convert Entities To Control Options', () => { entities, 'title', 'id' - ).map((option) => option.value); - for (const entity of entities) { - expect(options.has(entity.id)).toEqual(true); + ).map( ( option ) => option.value ); + for ( const entity of entities ) { + expect( options.has( entity.id ) ).toEqual( true ); } - }); + } ); - it('throws an error if the option label is not a string', () => { + it( 'throws an error if the option label is not a string', () => { const rawEntities = []; - for (let count = 0; count < 10; ++count) { + for ( let count = 0; count < 10; ++count ) { rawEntities.push( - fromPartial({ + fromPartial< EntitiesSearch.SearchEntityFields >( { title: faker.word.noun(), id: faker.number.int(), - }) + } ) ); } - const entities = new Set( + const entities = new Set< EntitiesSearch.SearchEntityFields >( rawEntities ); - expect(() => { + expect( () => { // To make the test fail, we pass the id as the label key - convertEntitiesToControlOptions(entities, 'id', 'id'); - }).toThrow(); - }); -}); + convertEntitiesToControlOptions( entities, 'id', 'id' ); + } ).toThrow(); + } ); +} ); diff --git a/tests/client/unit/utils/is-control-option.test.ts b/tests/client/unit/utils/is-control-option.test.ts index dccf81b..6f5518e 100644 --- a/tests/client/unit/utils/is-control-option.test.ts +++ b/tests/client/unit/utils/is-control-option.test.ts @@ -1,18 +1,30 @@ +/** + * External dependencies + */ import { describe, expect, it } from '@jest/globals'; +/** + * Internal dependencies + */ import { isControlOption } from '../../../../sources/client/src/utils/is-control-option'; -describe('isControlOption', () => { - it('shall return true when given object is of type of ControlOption', () => { - expect(isControlOption({ label: 'label', value: 'value' })).toBe(true); - }); +describe( 'isControlOption', () => { + it( 'shall return true when given object is of type of ControlOption', () => { + expect( isControlOption( { label: 'label', value: 'value' } ) ).toBe( + true + ); + } ); - describe.each([{ label: 1, value: 1 }, {}, null, undefined, 1, 'string'])( - '%#:%s', - (maybeControlOption) => { - it('shall return false when given object is not of type of ControlOption:', () => { - expect(isControlOption(maybeControlOption)).toBe(false); - }); - } - ); -}); + describe.each( [ + { label: 1, value: 1 }, + {}, + null, + undefined, + 1, + 'string', + ] )( '%#:%s', ( maybeControlOption ) => { + it( 'shall return false when given object is not of type of ControlOption:', () => { + expect( isControlOption( maybeControlOption ) ).toBe( false ); + } ); + } ); +} ); diff --git a/tests/client/unit/utils/ordered-selected-options-at-the-top.test.ts b/tests/client/unit/utils/ordered-selected-options-at-the-top.test.ts index bf09691..8a2c213 100644 --- a/tests/client/unit/utils/ordered-selected-options-at-the-top.test.ts +++ b/tests/client/unit/utils/ordered-selected-options-at-the-top.test.ts @@ -1,81 +1,95 @@ +/** + * External dependencies + */ import EntitiesSearch from '@types'; import { describe, expect, it } from '@jest/globals'; import { faker } from '@faker-js/faker'; +/** + * Internal dependencies + */ import { orderSelectedOptionsAtTheTop } from '../../../../sources/client/src/utils/order-selected-options-at-the-top'; import { Set } from '../../../../sources/client/src/vo/set'; -describe('Ordered Selected Options at the Top', () => { +describe( 'Ordered Selected Options at the Top', () => { /* * Given options are always re-ordered to includes the given collection values at the top */ - function generateOptions(): Array> { - const options: Array> = []; - - for (let i = 1; i <= 10; i++) { - options.push({ - value: `${i}`, - label: `Option ${i}`, - }); + function generateOptions(): Array< + EntitiesSearch.ControlOption< string > + > { + const options: Array< EntitiesSearch.ControlOption< string > > = []; + + for ( let i = 1; i <= 10; i++ ) { + options.push( { + value: `${ i }`, + label: `Option ${ i }`, + } ); } return options; } - function generateRandomNumbersAsStringCollection(): Array { - const collection: Array = []; + function generateRandomNumbersAsStringCollection(): Array< string > { + const collection: Array< string > = []; - for (let count = 1; count <= 4; count++) { - const number = faker.number.int({ min: 1, max: 10 }); + for ( let count = 1; count <= 4; count++ ) { + const number = faker.number.int( { min: 1, max: 10 } ); - if (collection.includes(`${number}`)) { + if ( collection.includes( `${ number }` ) ) { count = 1; continue; } - collection.push(`${number}`); + collection.push( `${ number }` ); } - return collection.slice(0, 4); + return collection.slice( 0, 4 ); } - it('should return the given options at the top', () => { + it( 'should return the given options at the top', () => { const rawCollection = generateRandomNumbersAsStringCollection(); - const collection = new Set(rawCollection); - const options = new Set(generateOptions()); + const collection = new Set( rawCollection ); + const options = new Set( generateOptions() ); - const result = orderSelectedOptionsAtTheTop(options, collection).map( - (option) => option.value + const result = orderSelectedOptionsAtTheTop( options, collection ).map( + ( option ) => option.value ); - if (rawCollection.length < 4) { - throw new Error('The collection must have at least 4 values'); + if ( rawCollection.length < 4 ) { + throw new Error( 'The collection must have at least 4 values' ); } - expect(result.has(`${rawCollection[0]}`)).toEqual(true); - expect(result.has(`${rawCollection[1]}`)).toEqual(true); - expect(result.has(`${rawCollection[2]}`)).toEqual(true); - expect(result.has(`${rawCollection[3]}`)).toEqual(true); - }); + expect( result.has( `${ rawCollection[ 0 ] }` ) ).toEqual( true ); + expect( result.has( `${ rawCollection[ 1 ] }` ) ).toEqual( true ); + expect( result.has( `${ rawCollection[ 2 ] }` ) ).toEqual( true ); + expect( result.has( `${ rawCollection[ 3 ] }` ) ).toEqual( true ); + } ); - it('should return the given selected options when the collection is empty', () => { - const options = new Set>( + it( 'should return the given selected options when the collection is empty', () => { + const options = new Set< EntitiesSearch.ControlOption< string > >( generateOptions() ); - const result = orderSelectedOptionsAtTheTop(options, new Set()); + const result = orderSelectedOptionsAtTheTop( + options, + new Set< string >() + ); - expect(result).toBe(options); - }); + expect( result ).toBe( options ); + } ); - it('should return the given options when the options are empty', () => { - const options = new Set>(); + it( 'should return the given options when the options are empty', () => { + const options = new Set< EntitiesSearch.ControlOption< string > >(); - const result = orderSelectedOptionsAtTheTop(options, new Set()); + const result = orderSelectedOptionsAtTheTop( + options, + new Set< string >() + ); - expect(result.length()).toEqual(0); - }); -}); + expect( result.length() ).toEqual( 0 ); + } ); +} ); diff --git a/tests/client/unit/utils/unique-control-options.test.ts b/tests/client/unit/utils/unique-control-options.test.ts index 5bcba3f..6482f1a 100644 --- a/tests/client/unit/utils/unique-control-options.test.ts +++ b/tests/client/unit/utils/unique-control-options.test.ts @@ -1,23 +1,29 @@ +/** + * External dependencies + */ import EntitiesSearch from '@types'; import { describe, expect, it } from '@jest/globals'; +/** + * Internal dependencies + */ import { uniqueControlOptions } from '../../../../sources/client/src/utils/unique-control-options'; import { Set } from '../../../../sources/client/src/vo/set'; -describe('Unique Control Options', () => { - it('Do not allow same control options within the same set', () => { - const set = new Set>([ +describe( 'Unique Control Options', () => { + it( 'Do not allow same control options within the same set', () => { + const set = new Set< EntitiesSearch.ControlOption< string > >( [ { label: 'foo', value: 'foo' }, { label: 'bar', value: 'bar' }, { label: 'foo', value: 'foo' }, { label: 'bar', value: 'bar' }, - ]); + ] ); - expect(set.length()).toBe(4); - const uniqueSet = uniqueControlOptions(set); - expect(uniqueSet.length()).toBe(2); - expect(uniqueSet.first()?.value).toBe('foo'); - expect(uniqueSet.last()?.value).toBe('bar'); - }); -}); + expect( set.length() ).toBe( 4 ); + const uniqueSet = uniqueControlOptions( set ); + expect( uniqueSet.length() ).toBe( 2 ); + expect( uniqueSet.first()?.value ).toBe( 'foo' ); + expect( uniqueSet.last()?.value ).toBe( 'bar' ); + } ); +} ); diff --git a/tests/client/unit/vo/control-option.test.ts b/tests/client/unit/vo/control-option.test.ts index 3e2037a..89744a6 100644 --- a/tests/client/unit/vo/control-option.test.ts +++ b/tests/client/unit/vo/control-option.test.ts @@ -1,23 +1,29 @@ +/** + * External dependencies + */ import { describe, it, expect } from '@jest/globals'; +/** + * Internal dependencies + */ import { ControlOption } from '../../../../sources/client/src/vo/control-option'; -describe('ControlOption', () => { - it('should create a new ControlOption', () => { - const controlOption = new ControlOption('label', 'value'); - expect(controlOption.label).toBe('label'); - expect(controlOption.value).toBe('value'); - }); +describe( 'ControlOption', () => { + it( 'should create a new ControlOption', () => { + const controlOption = new ControlOption( 'label', 'value' ); + expect( controlOption.label ).toBe( 'label' ); + expect( controlOption.value ).toBe( 'value' ); + } ); - it('should throw an error when creating a new ControlOption with an empty label', () => { - expect(() => new ControlOption('', 'value')).toThrow( + it( 'should throw an error when creating a new ControlOption with an empty label', () => { + expect( () => new ControlOption( '', 'value' ) ).toThrow( 'ControlOption: Label must be a non empty string.' ); - }); + } ); - it('should throw an error when creating a new ControlOption with an empty value', () => { - expect(() => new ControlOption('label', '')).toThrow( + it( 'should throw an error when creating a new ControlOption with an empty value', () => { + expect( () => new ControlOption( 'label', '' ) ).toThrow( 'ControlOption: Value must be a non empty string.' ); - }); -}); + } ); +} ); diff --git a/tests/client/unit/vo/set.test.ts b/tests/client/unit/vo/set.test.ts index 55d2f9f..6174fbb 100644 --- a/tests/client/unit/vo/set.test.ts +++ b/tests/client/unit/vo/set.test.ts @@ -1,61 +1,67 @@ +/** + * External dependencies + */ import { describe, expect, it } from '@jest/globals'; +/** + * Internal dependencies + */ import { Set } from '../../../../sources/client/src/vo/set'; -describe('Set', () => { - it('Should be empty when created', () => { - const set = new Set(); - expect(set.length()).toBe(0); - }); - - it('Should add elements', () => { - const set = new Set(); - expect(set.add(1).add(2).add(3).length()).toBe(3); - }); - - it('Should remove elements', () => { - const set = new Set(); - expect(set.add(1).add(2).add(3).delete(2).length()).toBe(2); - expect(set.delete(1).delete(3).length()).toBe(0); - }); - - it('Should check element existence', () => { - const set = new Set(); - const newSet = set.add(1).add(2).add(3); - expect(newSet.has(2)).toBe(true); - expect(newSet.has(4)).toBe(false); - }); - - it('Should return the concat of two sets', () => { - const set = new Set(); - const set1 = set.add(1).add(2).add(3); - const set2 = set.add(2).add(3).add(4); - const concat = set1.concat(set2); - expect(concat.length()).toBe(6); - expect(concat.has(1)).toBe(true); - expect(concat.has(2)).toBe(true); - expect(concat.has(3)).toBe(true); - expect(concat.has(4)).toBe(true); - }); - - it('Should not add the same object again', () => { +describe( 'Set', () => { + it( 'Should be empty when created', () => { + const set = new Set< number >(); + expect( set.length() ).toBe( 0 ); + } ); + + it( 'Should add elements', () => { + const set = new Set< number >(); + expect( set.add( 1 ).add( 2 ).add( 3 ).length() ).toBe( 3 ); + } ); + + it( 'Should remove elements', () => { + const set = new Set< number >(); + expect( set.add( 1 ).add( 2 ).add( 3 ).delete( 2 ).length() ).toBe( 2 ); + expect( set.delete( 1 ).delete( 3 ).length() ).toBe( 0 ); + } ); + + it( 'Should check element existence', () => { + const set = new Set< number >(); + const newSet = set.add( 1 ).add( 2 ).add( 3 ); + expect( newSet.has( 2 ) ).toBe( true ); + expect( newSet.has( 4 ) ).toBe( false ); + } ); + + it( 'Should return the concat of two sets', () => { + const set = new Set< number >(); + const set1 = set.add( 1 ).add( 2 ).add( 3 ); + const set2 = set.add( 2 ).add( 3 ).add( 4 ); + const concat = set1.concat( set2 ); + expect( concat.length() ).toBe( 6 ); + expect( concat.has( 1 ) ).toBe( true ); + expect( concat.has( 2 ) ).toBe( true ); + expect( concat.has( 3 ) ).toBe( true ); + expect( concat.has( 4 ) ).toBe( true ); + } ); + + it( 'Should not add the same object again', () => { const obj = { a: 1 }; - const set = new Set(); - expect(set.add(obj).add(obj).length()).toBe(1); - }); + const set = new Set< any >(); + expect( set.add( obj ).add( obj ).length() ).toBe( 1 ); + } ); - it('Should not add the same object again if it is a different reference', () => { + it( 'Should not add the same object again if it is a different reference', () => { const obj = { a: 1 }; const obj2 = { a: 1 }; - const set = new Set(); - expect(set.add(obj).add(obj2).length()).toBe(1); - }); - - it.each([ - [[1, 2, 3, 4, 5], 3, true], - [[1, 2, 3, 4, 5], '4', false], - [['3', '4', '5'], 4, false], - [[{ a: 1 }, { b: 2 }, { c: 3 }], { b: 2 }, true], + const set = new Set< any >(); + expect( set.add( obj ).add( obj2 ).length() ).toBe( 1 ); + } ); + + it.each( [ + [ [ 1, 2, 3, 4, 5 ], 3, true ], + [ [ 1, 2, 3, 4, 5 ], '4', false ], + [ [ '3', '4', '5' ], 4, false ], + [ [ { a: 1 }, { b: 2 }, { c: 3 } ], { b: 2 }, true ], [ [ { label: 'Label 1', value: 1 }, @@ -65,93 +71,135 @@ describe('Set', () => { { label: 'Label 2', value: 2 }, true, ], - [[{ a: 1 }, { b: 2 }, { c: 3 }], 'b', false], - ])( + [ [ { a: 1 }, { b: 2 }, { c: 3 } ], 'b', false ], + ] )( 'Should return true if a given value is the same in shape of an existing one', - (collection, given, expected) => { - const set = new Set(collection); - expect(set.has(given)).toBe(expected); + ( collection, given, expected ) => { + const set = new Set< any >( collection ); + expect( set.has( given ) ).toBe( expected ); } ); - it('Return the first element', () => { - const set = new Set(); - expect(set.add(4).add(2).add(3).first()).toBe(4); - }); - - it('Return the last element', () => { - const set = new Set(); - expect(set.add(4).add(2).add(3).last()).toBe(3); - }); - - it('Should copy the set', () => { - const set = new Set(); - const newSet = set.add(1).add(2).add(3).add(4).add(5); - const sliced = newSet.copy(1, 3); - expect(sliced.length()).toBe(2); - expect(sliced !== newSet); - }); - - it('Map a ControlOption to a string', () => { - const set = new Set<{ label: string; value: number }>([ + it( 'Return the first element', () => { + const set = new Set< number >(); + expect( set.add( 4 ).add( 2 ).add( 3 ).first() ).toBe( 4 ); + } ); + + it( 'Return the last element', () => { + const set = new Set< number >(); + expect( set.add( 4 ).add( 2 ).add( 3 ).last() ).toBe( 3 ); + } ); + + it( 'Should copy the set', () => { + const set = new Set< number >(); + const newSet = set.add( 1 ).add( 2 ).add( 3 ).add( 4 ).add( 5 ); + const sliced = newSet.copy( 1, 3 ); + expect( sliced.length() ).toBe( 2 ); + expect( sliced !== newSet ); + } ); + + it( 'Map a ControlOption to a string', () => { + const set = new Set< { label: string; value: number } >( [ { value: 1, label: 'First' }, { value: 11232, label: 'Second' }, { value: 34543543, label: 'Third' }, { value: 56234231, label: 'Fourth' }, - ]); - const mapped = set.map((option) => option.value); - expect(mapped.length()).toBe(4); - expect(mapped.has(1)).toBe(true); - expect(mapped.has(11232)).toBe(true); - expect(mapped.has(34543543)).toBe(true); - expect(mapped.has(56234231)).toBe(true); - }); - - it('Filters out non valid elements', () => { - const set = new Set(['a', 1, 'b', 2, 'c', 3]); - const filtered = set.filter((element) => typeof element === 'string'); - expect(filtered.length()).toBe(3); - expect(filtered.has(1)).toBe(false); - expect(filtered.has(2)).toBe(false); - expect(filtered.has(3)).toBe(false); - }); - - it('Should walk through the set', () => { - const set = new Set().add(1).add(2).add(3).add(4).add(5); - const walked: Array = []; - set.forEach((element) => walked.push(element)); - expect(walked.length).toBe(5); - }); - - it('Should iterate over the elements', () => { - const set = new Set().add(1).add(2).add(3).add(4).add(5); + ] ); + const mapped = set.map( ( option ) => option.value ); + expect( mapped.length() ).toBe( 4 ); + expect( mapped.has( 1 ) ).toBe( true ); + expect( mapped.has( 11232 ) ).toBe( true ); + expect( mapped.has( 34543543 ) ).toBe( true ); + expect( mapped.has( 56234231 ) ).toBe( true ); + } ); + + it( 'Filters out non valid elements', () => { + const set = new Set< string | number >( [ 'a', 1, 'b', 2, 'c', 3 ] ); + const filtered = set.filter( + ( element ) => typeof element === 'string' + ); + expect( filtered.length() ).toBe( 3 ); + expect( filtered.has( 1 ) ).toBe( false ); + expect( filtered.has( 2 ) ).toBe( false ); + expect( filtered.has( 3 ) ).toBe( false ); + } ); + + it( 'Should walk through the set', () => { + const set = new Set< number >() + .add( 1 ) + .add( 2 ) + .add( 3 ) + .add( 4 ) + .add( 5 ); + const walked: Array< number > = []; + set.forEach( ( element ) => walked.push( element ) ); + expect( walked.length ).toBe( 5 ); + } ); + + it( 'Should iterate over the elements', () => { + const set = new Set< number >() + .add( 1 ) + .add( 2 ) + .add( 3 ) + .add( 4 ) + .add( 5 ); const iterated = []; - for (const element of set) { - iterated.push(element); + for ( const element of set ) { + iterated.push( element ); } - expect(iterated.length).toBe(5); - }); - - it('Should ensure that two sets are equal in shape and content', () => { - const set = new Set().add(1).add(2).add(3).add(4).add(5); - const set2 = new Set().add(1).add(2).add(3).add(4).add(5); - expect(set.equals(set2)).toBe(true); - }); - - it('Should ensure that two sets are equal if they are the same reference', () => { - const set = new Set().add(1).add(2).add(3).add(4).add(5); - expect(set.equals(set)).toBe(true); - }); - - it('Should ensure two sets are not equals if they have different length', () => { - const set = new Set().add(1).add(2).add(3).add(4).add(5); - const set2 = new Set().add(1).add(2).add(3).add(4); - expect(set.equals(set2)).toBe(false); - }); - - it('Should ensure two sets are not equals if they have different content', () => { - const set = new Set().add(1).add(2).add(3).add(4).add(5); - const set2 = new Set().add(1).add(2).add(3).add(4).add(6); - expect(set.equals(set2)).toBe(false); - }); -}); + expect( iterated.length ).toBe( 5 ); + } ); + + it( 'Should ensure that two sets are equal in shape and content', () => { + const set = new Set< number >() + .add( 1 ) + .add( 2 ) + .add( 3 ) + .add( 4 ) + .add( 5 ); + const set2 = new Set< number >() + .add( 1 ) + .add( 2 ) + .add( 3 ) + .add( 4 ) + .add( 5 ); + expect( set.equals( set2 ) ).toBe( true ); + } ); + + it( 'Should ensure that two sets are equal if they are the same reference', () => { + const set = new Set< number >() + .add( 1 ) + .add( 2 ) + .add( 3 ) + .add( 4 ) + .add( 5 ); + expect( set.equals( set ) ).toBe( true ); + } ); + + it( 'Should ensure two sets are not equals if they have different length', () => { + const set = new Set< number >() + .add( 1 ) + .add( 2 ) + .add( 3 ) + .add( 4 ) + .add( 5 ); + const set2 = new Set< number >().add( 1 ).add( 2 ).add( 3 ).add( 4 ); + expect( set.equals( set2 ) ).toBe( false ); + } ); + + it( 'Should ensure two sets are not equals if they have different content', () => { + const set = new Set< number >() + .add( 1 ) + .add( 2 ) + .add( 3 ) + .add( 4 ) + .add( 5 ); + const set2 = new Set< number >() + .add( 1 ) + .add( 2 ) + .add( 3 ) + .add( 4 ) + .add( 6 ); + expect( set.equals( set2 ) ).toBe( false ); + } ); +} ); diff --git a/webpack.config.js b/webpack.config.js index dba8ae4..dc8a3fd 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -1,6 +1,12 @@ -const baseConfiguration = require('@wordpress/scripts/config/webpack.config'); -const DependencyExtractionWebpackPlugin = require('@wordpress/dependency-extraction-webpack-plugin'); -const path = require('path'); +/** + * WordPress dependencies + */ +const baseConfiguration = require( '@wordpress/scripts/config/webpack.config' ); +const DependencyExtractionWebpackPlugin = require( '@wordpress/dependency-extraction-webpack-plugin' ); +/** + * External dependencies + */ +const path = require( 'path' ); module.exports = { ...baseConfiguration, @@ -10,18 +16,18 @@ module.exports = { }, plugins: [ ...baseConfiguration.plugins.filter( - (plugin) => + ( plugin ) => plugin.constructor.name !== 'DependencyExtractionWebpackPlugin' ), - new DependencyExtractionWebpackPlugin({ + new DependencyExtractionWebpackPlugin( { outputFormat: 'php', - }), + } ), ], resolve: { ...baseConfiguration.resolve, alias: { ...baseConfiguration.resolve.alias, - '@types': path.resolve(__dirname, './@types/index.d.ts'), + '@types': path.resolve( __dirname, './@types/index.d.ts' ), }, }, output: { diff --git a/yarn.lock b/yarn.lock index c210e6f..6d25250 100644 --- a/yarn.lock +++ b/yarn.lock @@ -62,7 +62,7 @@ __metadata: languageName: node linkType: hard -"@babel/code-frame@npm:^7.0.0, @babel/code-frame@npm:^7.10.4, @babel/code-frame@npm:^7.12.13, @babel/code-frame@npm:^7.22.13, @babel/code-frame@npm:^7.23.5": +"@babel/code-frame@npm:^7.0.0, @babel/code-frame@npm:^7.10.4, @babel/code-frame@npm:^7.12.13, @babel/code-frame@npm:^7.23.5": version: 7.23.5 resolution: "@babel/code-frame@npm:7.23.5" dependencies: @@ -116,18 +116,7 @@ __metadata: languageName: node linkType: hard -"@babel/generator@npm:7.17.7": - version: 7.17.7 - resolution: "@babel/generator@npm:7.17.7" - dependencies: - "@babel/types": "npm:^7.17.0" - jsesc: "npm:^2.5.1" - source-map: "npm:^0.5.0" - checksum: 8088453c4418e0ee6528506fbd5847bbdfd56327a0025ca9496a259261e162c594ffd08be0d63e74c32feced795616772f38acc5f5e493a86a45fd439fd9feb0 - languageName: node - linkType: hard - -"@babel/generator@npm:^7.23.0, @babel/generator@npm:^7.23.6, @babel/generator@npm:^7.7.2": +"@babel/generator@npm:^7.23.6, @babel/generator@npm:^7.7.2": version: 7.23.6 resolution: "@babel/generator@npm:7.23.6" dependencies: @@ -352,7 +341,7 @@ __metadata: languageName: node linkType: hard -"@babel/helper-validator-identifier@npm:^7.16.7, @babel/helper-validator-identifier@npm:^7.22.20": +"@babel/helper-validator-identifier@npm:^7.22.20": version: 7.22.20 resolution: "@babel/helper-validator-identifier@npm:7.22.20" checksum: dcad63db345fb110e032de46c3688384b0008a42a4845180ce7cd62b1a9c0507a1bed727c4d1060ed1a03ae57b4d918570259f81724aaac1a5b776056f37504e @@ -399,7 +388,7 @@ __metadata: languageName: node linkType: hard -"@babel/parser@npm:^7.1.0, @babel/parser@npm:^7.14.7, @babel/parser@npm:^7.20.5, @babel/parser@npm:^7.20.7, @babel/parser@npm:^7.23.0, @babel/parser@npm:^7.23.9": +"@babel/parser@npm:^7.1.0, @babel/parser@npm:^7.14.7, @babel/parser@npm:^7.20.7, @babel/parser@npm:^7.23.9": version: 7.23.9 resolution: "@babel/parser@npm:7.23.9" bin: @@ -1516,24 +1505,6 @@ __metadata: languageName: node linkType: hard -"@babel/traverse@npm:7.23.2": - version: 7.23.2 - resolution: "@babel/traverse@npm:7.23.2" - dependencies: - "@babel/code-frame": "npm:^7.22.13" - "@babel/generator": "npm:^7.23.0" - "@babel/helper-environment-visitor": "npm:^7.22.20" - "@babel/helper-function-name": "npm:^7.23.0" - "@babel/helper-hoist-variables": "npm:^7.22.5" - "@babel/helper-split-export-declaration": "npm:^7.22.6" - "@babel/parser": "npm:^7.23.0" - "@babel/types": "npm:^7.23.0" - debug: "npm:^4.1.0" - globals: "npm:^11.1.0" - checksum: d096c7c4bab9262a2f658298a3c630ae4a15a10755bb257ae91d5ab3e3b2877438934859c8d34018b7727379fe6b26c4fa2efc81cf4c462a7fe00caf79fa02ff - languageName: node - linkType: hard - "@babel/traverse@npm:^7.23.9": version: 7.23.9 resolution: "@babel/traverse@npm:7.23.9" @@ -1552,17 +1523,7 @@ __metadata: languageName: node linkType: hard -"@babel/types@npm:7.17.0": - version: 7.17.0 - resolution: "@babel/types@npm:7.17.0" - dependencies: - "@babel/helper-validator-identifier": "npm:^7.16.7" - to-fast-properties: "npm:^2.0.0" - checksum: ad09224272b40fedb00b262677d12b6838f5b5df5c47d67059ba1181bd4805439993393a8de32459dae137b536d60ebfcaf39ae84d8b3873f1e81cc75f5aeae8 - languageName: node - linkType: hard - -"@babel/types@npm:^7.0.0, @babel/types@npm:^7.17.0, @babel/types@npm:^7.20.7, @babel/types@npm:^7.21.3, @babel/types@npm:^7.22.15, @babel/types@npm:^7.22.19, @babel/types@npm:^7.22.5, @babel/types@npm:^7.23.0, @babel/types@npm:^7.23.4, @babel/types@npm:^7.23.6, @babel/types@npm:^7.23.9, @babel/types@npm:^7.3.3, @babel/types@npm:^7.4.4, @babel/types@npm:^7.8.3": +"@babel/types@npm:^7.0.0, @babel/types@npm:^7.20.7, @babel/types@npm:^7.21.3, @babel/types@npm:^7.22.15, @babel/types@npm:^7.22.19, @babel/types@npm:^7.22.5, @babel/types@npm:^7.23.0, @babel/types@npm:^7.23.4, @babel/types@npm:^7.23.6, @babel/types@npm:^7.23.9, @babel/types@npm:^7.3.3, @babel/types@npm:^7.4.4, @babel/types@npm:^7.8.3": version: 7.23.9 resolution: "@babel/types@npm:7.23.9" dependencies: @@ -1780,17 +1741,6 @@ __metadata: languageName: node linkType: hard -"@es-joy/jsdoccomment@npm:~0.36.1": - version: 0.36.1 - resolution: "@es-joy/jsdoccomment@npm:0.36.1" - dependencies: - comment-parser: "npm:1.3.1" - esquery: "npm:^1.4.0" - jsdoc-type-pratt-parser: "npm:~3.1.0" - checksum: 7bb082c64a1270ec2a8d604a8fcfa8a9a98745200bf4945e93835e99ab09e93ac43180e24180e7710ac3e9ba2845c0c04ba6c4ce363f1e5ea0511680b33bc2ad - languageName: node - linkType: hard - "@es-joy/jsdoccomment@npm:~0.41.0": version: 0.41.0 resolution: "@es-joy/jsdoccomment@npm:0.41.0" @@ -1813,7 +1763,7 @@ __metadata: languageName: node linkType: hard -"@eslint-community/regexpp@npm:^4.4.0, @eslint-community/regexpp@npm:^4.5.1, @eslint-community/regexpp@npm:^4.6.1": +"@eslint-community/regexpp@npm:^4.5.1, @eslint-community/regexpp@npm:^4.6.1": version: 4.10.0 resolution: "@eslint-community/regexpp@npm:4.10.0" checksum: c5f60ef1f1ea7649fa7af0e80a5a79f64b55a8a8fa5086de4727eb4c86c652aedee407a9c143b8995d2c0b2d75c1222bec9ba5d73dbfc1f314550554f0979ef4 @@ -3185,26 +3135,6 @@ __metadata: languageName: node linkType: hard -"@trivago/prettier-plugin-sort-imports@npm:^4.0.0": - version: 4.3.0 - resolution: "@trivago/prettier-plugin-sort-imports@npm:4.3.0" - dependencies: - "@babel/generator": "npm:7.17.7" - "@babel/parser": "npm:^7.20.5" - "@babel/traverse": "npm:7.23.2" - "@babel/types": "npm:7.17.0" - javascript-natural-sort: "npm:0.7.1" - lodash: "npm:^4.17.21" - peerDependencies: - "@vue/compiler-sfc": 3.x - prettier: 2.x - 3.x - peerDependenciesMeta: - "@vue/compiler-sfc": - optional: true - checksum: 42270fb9c89e54a3f8b6ac8c43e6d0e03350e2857e902cdad4de22c78ef1864da600525595311bc7e94e51c16c7dd3882c2e048a162fdab59761ffa893756aa2 - languageName: node - linkType: hard - "@trysound/sax@npm:0.2.0": version: 0.2.0 resolution: "@trysound/sax@npm:0.2.0" @@ -3784,30 +3714,6 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/eslint-plugin@npm:^5.3.0": - version: 5.62.0 - resolution: "@typescript-eslint/eslint-plugin@npm:5.62.0" - dependencies: - "@eslint-community/regexpp": "npm:^4.4.0" - "@typescript-eslint/scope-manager": "npm:5.62.0" - "@typescript-eslint/type-utils": "npm:5.62.0" - "@typescript-eslint/utils": "npm:5.62.0" - debug: "npm:^4.3.4" - graphemer: "npm:^1.4.0" - ignore: "npm:^5.2.0" - natural-compare-lite: "npm:^1.4.0" - semver: "npm:^7.3.7" - tsutils: "npm:^3.21.0" - peerDependencies: - "@typescript-eslint/parser": ^5.0.0 - eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 - peerDependenciesMeta: - typescript: - optional: true - checksum: 3f40cb6bab5a2833c3544e4621b9fdacd8ea53420cadc1c63fac3b89cdf5c62be1e6b7bcf56976dede5db4c43830de298ced3db60b5494a3b961ca1b4bff9f2a - languageName: node - linkType: hard - "@typescript-eslint/eslint-plugin@npm:^6.4.1": version: 6.21.0 resolution: "@typescript-eslint/eslint-plugin@npm:6.21.0" @@ -3833,23 +3739,6 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/parser@npm:^5.3.0": - version: 5.62.0 - resolution: "@typescript-eslint/parser@npm:5.62.0" - dependencies: - "@typescript-eslint/scope-manager": "npm:5.62.0" - "@typescript-eslint/types": "npm:5.62.0" - "@typescript-eslint/typescript-estree": "npm:5.62.0" - debug: "npm:^4.3.4" - peerDependencies: - eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 - peerDependenciesMeta: - typescript: - optional: true - checksum: 315194b3bf39beb9bd16c190956c46beec64b8371e18d6bb72002108b250983eb1e186a01d34b77eb4045f4941acbb243b16155fbb46881105f65e37dc9e24d4 - languageName: node - linkType: hard - "@typescript-eslint/parser@npm:^6.4.1": version: 6.21.0 resolution: "@typescript-eslint/parser@npm:6.21.0" @@ -3888,23 +3777,6 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/type-utils@npm:5.62.0": - version: 5.62.0 - resolution: "@typescript-eslint/type-utils@npm:5.62.0" - dependencies: - "@typescript-eslint/typescript-estree": "npm:5.62.0" - "@typescript-eslint/utils": "npm:5.62.0" - debug: "npm:^4.3.4" - tsutils: "npm:^3.21.0" - peerDependencies: - eslint: "*" - peerDependenciesMeta: - typescript: - optional: true - checksum: 93112e34026069a48f0484b98caca1c89d9707842afe14e08e7390af51cdde87378df29d213d3bbd10a7cfe6f91b228031b56218515ce077bdb62ddea9d9f474 - languageName: node - linkType: hard - "@typescript-eslint/type-utils@npm:6.21.0": version: 6.21.0 resolution: "@typescript-eslint/type-utils@npm:6.21.0" @@ -3973,24 +3845,6 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/utils@npm:5.62.0, @typescript-eslint/utils@npm:^5.10.0": - version: 5.62.0 - resolution: "@typescript-eslint/utils@npm:5.62.0" - dependencies: - "@eslint-community/eslint-utils": "npm:^4.2.0" - "@types/json-schema": "npm:^7.0.9" - "@types/semver": "npm:^7.3.12" - "@typescript-eslint/scope-manager": "npm:5.62.0" - "@typescript-eslint/types": "npm:5.62.0" - "@typescript-eslint/typescript-estree": "npm:5.62.0" - eslint-scope: "npm:^5.1.1" - semver: "npm:^7.3.7" - peerDependencies: - eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 - checksum: f09b7d9952e4a205eb1ced31d7684dd55cee40bf8c2d78e923aa8a255318d97279825733902742c09d8690f37a50243f4c4d383ab16bd7aefaf9c4b438f785e1 - languageName: node - linkType: hard - "@typescript-eslint/utils@npm:6.21.0": version: 6.21.0 resolution: "@typescript-eslint/utils@npm:6.21.0" @@ -4008,6 +3862,24 @@ __metadata: languageName: node linkType: hard +"@typescript-eslint/utils@npm:^5.10.0": + version: 5.62.0 + resolution: "@typescript-eslint/utils@npm:5.62.0" + dependencies: + "@eslint-community/eslint-utils": "npm:^4.2.0" + "@types/json-schema": "npm:^7.0.9" + "@types/semver": "npm:^7.3.12" + "@typescript-eslint/scope-manager": "npm:5.62.0" + "@typescript-eslint/types": "npm:5.62.0" + "@typescript-eslint/typescript-estree": "npm:5.62.0" + eslint-scope: "npm:^5.1.1" + semver: "npm:^7.3.7" + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + checksum: f09b7d9952e4a205eb1ced31d7684dd55cee40bf8c2d78e923aa8a255318d97279825733902742c09d8690f37a50243f4c4d383ab16bd7aefaf9c4b438f785e1 + languageName: node + linkType: hard + "@typescript-eslint/visitor-keys@npm:5.62.0": version: 5.62.0 resolution: "@typescript-eslint/visitor-keys@npm:5.62.0" @@ -4288,7 +4160,7 @@ __metadata: languageName: node linkType: hard -"@wordpress/babel-preset-default@npm:^7.23.0, @wordpress/babel-preset-default@npm:^7.32.0, @wordpress/babel-preset-default@npm:^7.35.0": +"@wordpress/babel-preset-default@npm:^7.32.0, @wordpress/babel-preset-default@npm:^7.35.0": version: 7.35.0 resolution: "@wordpress/babel-preset-default@npm:7.35.0" dependencies: @@ -4769,40 +4641,6 @@ __metadata: languageName: node linkType: hard -"@wordpress/eslint-plugin@npm:^14.8.0": - version: 14.12.0 - resolution: "@wordpress/eslint-plugin@npm:14.12.0" - dependencies: - "@babel/eslint-parser": "npm:^7.16.0" - "@typescript-eslint/eslint-plugin": "npm:^5.3.0" - "@typescript-eslint/parser": "npm:^5.3.0" - "@wordpress/babel-preset-default": "npm:^7.23.0" - "@wordpress/prettier-config": "npm:^2.22.0" - cosmiconfig: "npm:^7.0.0" - eslint-config-prettier: "npm:^8.3.0" - eslint-plugin-import: "npm:^2.25.2" - eslint-plugin-jest: "npm:^27.2.1" - eslint-plugin-jsdoc: "npm:^39.6.9" - eslint-plugin-jsx-a11y: "npm:^6.5.1" - eslint-plugin-prettier: "npm:^3.3.0" - eslint-plugin-react: "npm:^7.27.0" - eslint-plugin-react-hooks: "npm:^4.3.0" - globals: "npm:^13.12.0" - requireindex: "npm:^1.2.0" - peerDependencies: - "@babel/core": ">=7" - eslint: ">=8" - prettier: ">=2" - typescript: ">=4" - peerDependenciesMeta: - prettier: - optional: true - typescript: - optional: true - checksum: 074fde828f75cc17cdd213ec32aac29b5d6e0f78e9abb6eb246076ca796df484ee7f754aaa84cb530bb43a0348d0264c7d61c76fa9fbcdc9f5fdb75eab21771a - languageName: node - linkType: hard - "@wordpress/eslint-plugin@npm:^17.5.0": version: 17.8.0 resolution: "@wordpress/eslint-plugin@npm:17.8.0" @@ -5014,15 +4852,6 @@ __metadata: languageName: node linkType: hard -"@wordpress/prettier-config@npm:^2.22.0": - version: 2.25.13 - resolution: "@wordpress/prettier-config@npm:2.25.13" - peerDependencies: - prettier: ">=2" - checksum: c99383280128134bcb4748eaa8fa01c9264d45ae68507879aa86a69b8df4fe43121dba3b9059a0d4984812e112267055bd5b315b01966f57c98fa550da349293 - languageName: node - linkType: hard - "@wordpress/prettier-config@npm:^3.5.0, @wordpress/prettier-config@npm:^3.8.0": version: 3.8.0 resolution: "@wordpress/prettier-config@npm:3.8.0" @@ -6737,13 +6566,6 @@ __metadata: languageName: node linkType: hard -"comment-parser@npm:1.3.1": - version: 1.3.1 - resolution: "comment-parser@npm:1.3.1" - checksum: 848b3bbcf2eeb780831a8dd5a4cc52f914dd8c321c610403539237324c507040ea8fdca7bd1f8edb0a477e7c90138f54c4d05328a9d87fe6d651d5a2822cb14b - languageName: node - linkType: hard - "comment-parser@npm:1.4.1": version: 1.4.1 resolution: "comment-parser@npm:1.4.1" @@ -8225,7 +8047,7 @@ __metadata: languageName: node linkType: hard -"eslint-plugin-jest@npm:^27.2.1, eslint-plugin-jest@npm:^27.2.3": +"eslint-plugin-jest@npm:^27.2.3": version: 27.8.0 resolution: "eslint-plugin-jest@npm:27.8.0" dependencies: @@ -8243,23 +8065,6 @@ __metadata: languageName: node linkType: hard -"eslint-plugin-jsdoc@npm:^39.6.9": - version: 39.9.1 - resolution: "eslint-plugin-jsdoc@npm:39.9.1" - dependencies: - "@es-joy/jsdoccomment": "npm:~0.36.1" - comment-parser: "npm:1.3.1" - debug: "npm:^4.3.4" - escape-string-regexp: "npm:^4.0.0" - esquery: "npm:^1.4.0" - semver: "npm:^7.3.8" - spdx-expression-parse: "npm:^3.0.1" - peerDependencies: - eslint: ^7.0.0 || ^8.0.0 - checksum: 6c4c8623613d15bf39d78b73311dadb32a8c6ead139ad1ae1c9a3a29c1a99854953ee51783b32be7fedd8129d823131ce54ddcec73fc4b1756797fa87173204f - languageName: node - linkType: hard - "eslint-plugin-jsdoc@npm:^46.4.6": version: 46.10.1 resolution: "eslint-plugin-jsdoc@npm:46.10.1" @@ -8318,21 +8123,6 @@ __metadata: languageName: node linkType: hard -"eslint-plugin-prettier@npm:^3.3.0": - version: 3.4.1 - resolution: "eslint-plugin-prettier@npm:3.4.1" - dependencies: - prettier-linter-helpers: "npm:^1.0.0" - peerDependencies: - eslint: ">=5.0.0" - prettier: ">=1.13.0" - peerDependenciesMeta: - eslint-config-prettier: - optional: true - checksum: b2599dd22b5b0d2e3baffc94ba55a33ed525d642125d657fbc8511a2458146bdcc2bc810418713bb0049e37765def92b51213a4467984f4c758807bea224d0c5 - languageName: node - linkType: hard - "eslint-plugin-prettier@npm:^5.0.0": version: 5.1.3 resolution: "eslint-plugin-prettier@npm:5.1.3" @@ -8491,7 +8281,7 @@ __metadata: languageName: node linkType: hard -"esquery@npm:^1.4.0, esquery@npm:^1.4.2, esquery@npm:^1.5.0": +"esquery@npm:^1.4.2, esquery@npm:^1.5.0": version: 1.5.0 resolution: "esquery@npm:1.5.0" dependencies: @@ -10645,13 +10435,6 @@ __metadata: languageName: node linkType: hard -"javascript-natural-sort@npm:0.7.1": - version: 0.7.1 - resolution: "javascript-natural-sort@npm:0.7.1" - checksum: 340f8ffc5d30fb516e06dc540e8fa9e0b93c865cf49d791fed3eac3bdc5fc71f0066fc81d44ec1433edc87caecaf9f13eec4a1fce8c5beafc709a71eaedae6fe - languageName: node - linkType: hard - "jest-changed-files@npm:^29.7.0": version: 29.7.0 resolution: "jest-changed-files@npm:29.7.0" @@ -11202,13 +10985,6 @@ __metadata: languageName: node linkType: hard -"jsdoc-type-pratt-parser@npm:~3.1.0": - version: 3.1.0 - resolution: "jsdoc-type-pratt-parser@npm:3.1.0" - checksum: b55f42b59d79fe22c22369c25defaf0b13522c623e6c45e9264ecba431d7b5d98c7a1316912bc1b59938b49faadb1b6abf9ae89fbffe29e74768f59cc8e6ca8b - languageName: node - linkType: hard - "jsdoc-type-pratt-parser@npm:~4.0.0": version: 4.0.0 resolution: "jsdoc-type-pratt-parser@npm:4.0.0" @@ -12372,13 +12148,6 @@ __metadata: languageName: node linkType: hard -"natural-compare-lite@npm:^1.4.0": - version: 1.4.0 - resolution: "natural-compare-lite@npm:1.4.0" - checksum: f6cef26f5044515754802c0fc475d81426f3b90fe88c20fabe08771ce1f736ce46e0397c10acb569a4dd0acb84c7f1ee70676122f95d5bfdd747af3a6c6bbaa8 - languageName: node - linkType: hard - "natural-compare@npm:^1.4.0": version: 1.4.0 resolution: "natural-compare@npm:1.4.0" @@ -13650,15 +13419,6 @@ __metadata: languageName: node linkType: hard -"prettier@npm:^2.8.1": - version: 2.8.8 - resolution: "prettier@npm:2.8.8" - bin: - prettier: bin-prettier.js - checksum: 463ea8f9a0946cd5b828d8cf27bd8b567345cf02f56562d5ecde198b91f47a76b7ac9eae0facd247ace70e927143af6135e8cf411986b8cb8478784a4d6d724a - languageName: node - linkType: hard - "prettier@npm:wp-prettier@3.0.3": version: 3.0.3 resolution: "wp-prettier@npm:3.0.3" @@ -15162,7 +14922,7 @@ __metadata: languageName: node linkType: hard -"source-map@npm:^0.5.0, source-map@npm:^0.5.7": +"source-map@npm:^0.5.7": version: 0.5.7 resolution: "source-map@npm:0.5.7" checksum: 904e767bb9c494929be013017380cbba013637da1b28e5943b566031e29df04fba57edf3f093e0914be094648b577372bd8ad247fa98cfba9c600794cd16b599 @@ -15210,7 +14970,7 @@ __metadata: languageName: node linkType: hard -"spdx-expression-parse@npm:^3.0.0, spdx-expression-parse@npm:^3.0.1": +"spdx-expression-parse@npm:^3.0.0": version: 3.0.1 resolution: "spdx-expression-parse@npm:3.0.1" dependencies: @@ -17116,7 +16876,6 @@ __metadata: "@testing-library/react": "npm:^14.0.0" "@testing-library/user-event": "npm:^14.5.1" "@total-typescript/shoehorn": "npm:^0.1.0" - "@trivago/prettier-plugin-sort-imports": "npm:^4.0.0" "@types/lodash": "npm:^4" "@wordpress/api-fetch": "npm:~6.21.0" "@wordpress/components": "npm:~23.1.0" @@ -17124,7 +16883,6 @@ __metadata: "@wordpress/core-data": "npm:~6.12.0" "@wordpress/dependency-extraction-webpack-plugin": "npm:^4.8.0" "@wordpress/env": "npm:^5.9.0" - "@wordpress/eslint-plugin": "npm:^14.8.0" "@wordpress/hooks": "npm:^3.49.0" "@wordpress/i18n": "npm:~4.24.0" "@wordpress/scripts": "npm:^26.0.0" @@ -17133,7 +16891,6 @@ __metadata: jest: "npm:^29.4.3" jest-environment-jsdom: "npm:^29.5.0" lodash: "npm:^4.17.21" - prettier: "npm:^2.8.1" react: "npm:~18.2.0" ts-jest: "npm:^29.0.5" ts-loader: "npm:^9.4.2"