-
Notifications
You must be signed in to change notification settings - Fork 4.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Data: Create registry selector with self-contained registry proxying #16692
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think I grok why you are doing this, but I'm interested in knowing how the issue prompting this surfaced?
@@ -6,9 +11,25 @@ | |||
* @return {function} marked registry selector. | |||
*/ | |||
export function createRegistrySelector( registrySelector ) { | |||
registrySelector.isRegistrySelector = true; | |||
const selector = ( ...args ) => registrySelector( selector.registry.select )( ...args ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's unclear to me where selector.registry.select
is coming from as the argument passed to registrySelector
. Is it supposed to be defaultRegistry.select
or I'm guessing it will it be whatever is set on selector.registry
at the time it's invoked (with a fallback to defaultRegistry
)?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's unclear to me where
selector.registry.select
is coming from as the argument passed toregistrySelector
. Is it supposed to bedefaultRegistry.select
or I'm guessing it will it be whatever is set onselector.registry
at the time it's invoked (with a fallback todefaultRegistry
)?
It happens here:
gutenberg/packages/data/src/namespace-store/index.js
Lines 47 to 49 in 4edd1d0
if ( selector.isRegistrySelector ) { | |
selector.registry = registry; | |
} |
This occurs when a store is registered to a registry, so effectively the default registry is never used. I added it to simplify / respect the @type
declaration (tangentially related to #16693), but it could just as well be a noop stub.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Makes sense. I wonder if using a noop stub instead might lead to less head scratching for future looks at this code?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Makes sense. I wonder if using a noop stub instead might lead to less head scratching for future looks at this code?
If we stubbed it, I wouldn't feel comfortable adding @type {WPDataRegistry}
, since it'd be wrong to claim that the stub is of that type (and I expect potential future type checking could flag it as such).
I'm leaning that the current comment for selector.registry
assignment alone could be clear enough to explain its purpose.
In a pending branch, I'm bulk-updating a number of selectors to soft-deprecate them in favor of equivalent selectors in another registry store. This impacts a number of non-updated selectors which simply derive data from what are now registry selectors. Code example: // packages/editor/src/store/selectors.js
export const getPostEdits = createRegistrySelector( ( select ) => ( state ) => {
const postType = getCurrentPostType( state );
const postId = getCurrentPostId( state );
return select( 'core' ).getEntityRecordEdits( 'postType', postType, postId ) || EMPTY_OBJECT;
} );
export function hasChangedContent( state ) {
const edits = getPostEdits( state );
return '_blocks' in edits || 'content' in edits;
} |
@nerrad @talldan I'd be curious to have your thoughts on how to address the failing tests here. It seems gutenberg/packages/editor/src/store/test/selectors.js Lines 1270 to 1280 in 4edd1d0
One option might be to make the original, unwrapped selector available on some property (e.g.
Some related thoughts:
const isEditedPostAutosaveable = isEditedPostAutosaveable.unwrappedSelector(
sinon.stub()
.calledWith( 'core' ).returns( {
getCurrentUser() {},
// ...
} )
); |
The pull request for this is now available at #16761. |
Edit: I spoke too soon... I didn't test this on all the failing tests and there's some obvious flaws here :). I'll update when I've got something better to add. I now see the problem so all of the below is incorrect. Click to see original suggestion which does _not_ work but left in case someone jumps to the same conclusion I did.You're right, the issue here is that the function now returns a different value. However, it's still possible to mock the `registry.select` by just setting up the test differently (essentially reproducing what happens in the `@wordpress/data` module when the registry is constructed. I tried this locally in this branch and it worked.it( 'should return false if existing autosaves have not yet been fetched', () => {
const isEditedPostAutosaveable = isEditedPostAutosaveableRegistrySelector;
isEditedPostAutosaveable.registry = () => ( {
registry: {
select: {
getCurrentUser() {},
hasFetchedAutosaves() {
return false;
},
getAutosave() {
return {
title: 'sassel',
};
},
},
},
} );
const state = {
editor: {
present: {
blocks: {
value: [],
},
edits: {},
},
},
initialEdits: {},
currentPost: {
title: 'sassel',
},
saving: {
requesting: true,
},
};
expect( isEditedPostAutosaveable( state ) ).toBe( false );
} ); Essentially these are the two key changes: const isEditedPostAutosaveable = isEditedPostAutosaveableRegistrySelector;
isEditedPostAutosaveable.registry = () => ( { /** mocked registry object **/ } ); You probably could create a helper to set this up for each test condition. |
The only other thing I can think of here (other than what you already suggested @aduth) would be to mock |
@aduth That's difficult to answer. The original implementation of those tests perhaps wasn't ideal. It's a balance between testing as close to the actual implementation as possible, and making it nice and easy to write tests. I tested creating a mock version of the
I'll have a think about it. |
e815a99
to
a9e45dc
Compare
I spoke with @epiqueras about the approach for testing. He pushed up some changes in a9e45dc which I think are the simplest adaptation of the current approach which passes. To the broader question of a pattern for testing registry selectors, @talldan I think your idea could work well, and perhaps we don't even need to register the store, but rather assign a faked registry on the selector instance property. Example (via @epiqueras): isEditedPostAutosaveableRegistrySelector.registry = {
select() {
return {
getCurrentUser() {},
hasFetchedAutosaves() {
return false;
},
getAutosave() {
return {
title: 'sassel',
};
},
};
},
}; |
a9e45dc
to
5c3442b
Compare
…16692) * Data: Create registry selector with self-contained registry proxying * Editor: Fix tests that relied on registry selectors being higher order. * Data: Add typedef import reference for WPDataRegistry * Data: Omit removed registry argument from mapSelectors
…16692) * Data: Create registry selector with self-contained registry proxying * Editor: Fix tests that relied on registry selectors being higher order. * Data: Add typedef import reference for WPDataRegistry * Data: Omit removed registry argument from mapSelectors
Extracted from #16761
This pull request seeks to resolve an issue where calling a registry selector from a non-registry selector will produce an unexpected value, since the function returned by
createRegistrySelector
was the higher-order function expecting the registry as argument, not the underlying selector. With the changes here, a non-registry selector can call a registry selector as if it were any standard selector. The registry is automatically provided by the "current" registry in the mapping of selectors during store registration.Testing Instructions:
Ensure unit tests pass: