woocommerce/plugins/woocommerce-blocks/assets/js/base/hooks/test/use-collection.js

237 lines
6.2 KiB
JavaScript

/**
* External dependencies
*/
import TestRenderer, { act } from 'react-test-renderer';
import { createRegistry, RegistryProvider } from '@wordpress/data';
import { Component as ReactComponent } from '@wordpress/element';
import { COLLECTIONS_STORE_KEY as storeKey } from '@woocommerce/block-data';
/**
* Internal dependencies
*/
import { useCollection } from '../use-collection';
jest.mock( '@woocommerce/block-data', () => ( {
__esModule: true,
COLLECTIONS_STORE_KEY: 'test/store',
} ) );
class TestErrorBoundary extends ReactComponent {
constructor( props ) {
super( props );
this.state = { hasError: false, error: {} };
}
static getDerivedStateFromError( error ) {
// Update state so the next render will show the fallback UI.
return { hasError: true, error };
}
render() {
if ( this.state.hasError ) {
return <div error={ this.state.error } />;
}
return this.props.children;
}
}
describe( 'useStoreProducts', () => {
let registry, mocks, renderer;
const getProps = ( testRenderer ) => {
const { results, isLoading } = testRenderer.root.findByType(
'div'
).props;
return {
results,
isLoading,
};
};
const getWrappedComponents = ( Component, props ) => (
<RegistryProvider value={ registry }>
<TestErrorBoundary>
<Component { ...props } />
</TestErrorBoundary>
</RegistryProvider>
);
const getTestComponent = () => ( { options } ) => {
const items = useCollection( options );
return <div { ...items } />;
};
const setUpMocks = () => {
mocks = {
selectors: {
getCollection: jest
.fn()
.mockImplementation( () => ( { foo: 'bar' } ) ),
hasFinishedResolution: jest.fn().mockReturnValue( true ),
},
};
registry.registerStore( storeKey, {
reducer: () => ( {} ),
selectors: mocks.selectors,
} );
};
beforeEach( () => {
registry = createRegistry();
mocks = {};
renderer = null;
setUpMocks();
} );
it(
'should throw an error if an options object is provided without ' +
'a namespace property',
() => {
const TestComponent = getTestComponent();
act( () => {
renderer = TestRenderer.create(
getWrappedComponents( TestComponent, {
options: {
resourceName: 'products',
query: { bar: 'foo' },
},
} )
);
} );
const props = renderer.root.findByType( 'div' ).props;
expect( props.error.message ).toMatch( /options object/ );
expect( console ).toHaveErrored( /your React components:/ );
renderer.unmount();
}
);
it(
'should throw an error if an options object is provided without ' +
'a resourceName property',
() => {
const TestComponent = getTestComponent();
act( () => {
renderer = TestRenderer.create(
getWrappedComponents( TestComponent, {
options: {
namespace: 'test/store',
query: { bar: 'foo' },
},
} )
);
} );
const props = renderer.root.findByType( 'div' ).props;
expect( props.error.message ).toMatch( /options object/ );
expect( console ).toHaveErrored( /your React components:/ );
renderer.unmount();
}
);
it(
'should return expected behaviour for equivalent query on props ' +
'across renders',
() => {
const TestComponent = getTestComponent();
act( () => {
renderer = TestRenderer.create(
getWrappedComponents( TestComponent, {
options: {
namespace: 'test/store',
resourceName: 'products',
query: { bar: 'foo' },
},
} )
);
} );
const { results } = getProps( renderer );
// rerender
act( () => {
renderer.update(
getWrappedComponents( TestComponent, {
options: {
namespace: 'test/store',
resourceName: 'products',
query: { bar: 'foo' },
},
} )
);
} );
// re-render should result in same products object because although
// query-state is a different instance, it's still equivalent.
const { results: newResults } = getProps( renderer );
expect( newResults ).toBe( results );
// now let's change the query passed through to verify new object
// is created.
// remember this won't actually change the results because the mock
// selector is returning an equivalent object when it is called,
// however it SHOULD be a new object instance.
act( () => {
renderer.update(
getWrappedComponents( TestComponent, {
options: {
namespace: 'test/store',
resourceName: 'products',
query: { foo: 'bar' },
},
} )
);
} );
const { results: resultsVerification } = getProps( renderer );
expect( resultsVerification ).not.toBe( results );
expect( resultsVerification ).toEqual( results );
renderer.unmount();
}
);
it(
'should return expected behaviour for equivalent resourceValues on' +
' props across renders',
() => {
const TestComponent = getTestComponent();
act( () => {
renderer = TestRenderer.create(
getWrappedComponents( TestComponent, {
options: {
namespace: 'test/store',
resourceName: 'products',
resourceValues: [ 10, 20 ],
},
} )
);
} );
const { results } = getProps( renderer );
// rerender
act( () => {
renderer.update(
getWrappedComponents( TestComponent, {
options: {
namespace: 'test/store',
resourceName: 'products',
resourceValues: [ 10, 20 ],
},
} )
);
} );
// re-render should result in same products object because although
// query-state is a different instance, it's still equivalent.
const { results: newResults } = getProps( renderer );
expect( newResults ).toBe( results );
// now let's change the query passed through to verify new object
// is created.
// remember this won't actually change the results because the mock
// selector is returning an equivalent object when it is called,
// however it SHOULD be a new object instance.
act( () => {
renderer.update(
getWrappedComponents( TestComponent, {
options: {
namespace: 'test/store',
resourceName: 'products',
resourceValues: [ 20, 10 ],
},
} )
);
} );
const { results: resultsVerification } = getProps( renderer );
expect( resultsVerification ).not.toBe( results );
expect( resultsVerification ).toEqual( results );
renderer.unmount();
}
);
} );