-
Notifications
You must be signed in to change notification settings - Fork 1.2k
[v4] Export TestProvider with defaults and fix typescript react issue #1810
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
Conversation
src/index.ts
Outdated
export {UnstyledLinkProps, LinkLikeComponent} from './utilities/link'; | ||
export {createPolarisContext} from './utilities/create-polaris-context'; | ||
|
||
export {TestProvider} from './test-utilities/react-testing'; |
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.
Exporting this so consumers can use it
src/test-utilities/react-testing.tsx
Outdated
stickyManager, | ||
appBridge, | ||
link, | ||
themeProvider = createThemeContext(), |
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.
Adding default context so we don't have to expose our internals
appBridge, | ||
link, | ||
}) { | ||
const polarisContextDefault = createPolarisContext(); |
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.
Removing this since we don't use it anymore
@@ -1,6 +1,6 @@ | |||
import {createContext} from 'react'; | |||
import React from 'react'; |
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.
React doesn't need to be in scope from a babel perspective but in consuming apps I was getting a typescript reference errors with react so I'm thinking this will fix it 🤞
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 wonder if this is because typescript expects React to be in scope in tsx files. As we never use any JSX would renaming these files to .ts
fix it too?
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 thought that as well, but no 🎲
190cc1f
to
caf5766
Compare
config/rollup/index.js
Outdated
}), | ||
commonjs(), | ||
commonjs({ | ||
namedExports: { |
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 had trouble exporting TestProvider
because of an issue with createMount
. Here I'm following the docs (rollup docs, github rollup docs)
); | ||
} | ||
|
||
export default React.memo(UnstyledLink); |
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.
Unfortunately enzyme
is having trouble .find(UnstyledLink)
. We're not going to have a noticeable performance difference from this so I'm removing it for now till we find the root cause/enzyme fixes it.
|
||
export {UnstyledLinkProps, LinkLikeComponent} from './utilities/link'; | ||
export {createPolarisContext} from './utilities/create-polaris-context'; | ||
export { |
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.
These are used in web and we can't get around them for now :(
src/test-utilities/react-testing.tsx
Outdated
type Options = DeepPartial<ComplexProviders> & Partial<SimpleProviders>; | ||
type Context = ReturnedContext; | ||
interface Props extends ReturnedContext { | ||
interface Props extends Partial<ReturnedContext> { |
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.
Exporting the provider, now with defaults so props aren't mandatory
c3cccf4
to
80135ef
Compare
@@ -1,5 +0,0 @@ | |||
import React from 'react'; |
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.
This was actually a move to src/utilities
src/test-utilities/react-testing.tsx
Outdated
? React.cloneElement(children, props) | ||
: children; | ||
|
||
const Wrapper = strict? React.StrictMode : React.Fragment; |
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.
Going to have an optional strict mode so this utility can be used for enzyme tests as well
80135ef
to
17d8200
Compare
config/rollup/index.js
Outdated
exclude: 'node_modules/**', | ||
runtimeHelpers: true, | ||
}), | ||
commonjs({ |
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.
Ended up removing this since it didn't do what I originally thought
@@ -0,0 +1,68 @@ | |||
import React from 'react'; |
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.
Broke this apart from test-utilities/react-testing
since it's not package specific/export issue
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'm not sure what you mean by this? I'm not clear on the value of having this living in its own file
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.
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.
Following pairing: This TestProvider shall be the building block of our testing strategy. It is a generic component that can be used by both enzyme and react-testing's mounting systems.
legacy.tsx shall be updated to use this instead of providing its own version.
createPolarisContext shall be removed from our public API and instead 3rd party test configs (e.g. web) should use this TestProvider component as part of their test configuration, for both enzyme and react-testing setups
Related: Can we have a more specific name for this? PolarisTestContextProvider
or something like that? It's not very clear what the identifier "TestProvider" would do if you're in web's test setup files.
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.
Related: Can we have a more specific name for this? PolarisTestContextProvider or something like that? It's not very clear what the identifier "TestProvider" would do if you're in web's test setup files.
Yup that's definitely possible, it's actually similar to how I'm importing it right now
import {..., TestProvider as PolarisTestProvider ,...} from '@shopify/polaris'
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 there's some value in not naming our exports so generically people feel compelled to alias them :)
@@ -0,0 +1,33 @@ | |||
import {ClientApplication} from '@shopify/app-bridge'; |
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.
Broke out shared types to prevent circular imports
Should be ready for 👀
|
cc/ @alex-page @BPScott |
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.
Could you go over what our testing story is? Regarding modern vs legacy and what we consider "internal to us testing polaris" and "helpers that we publically expose so other teams can use us".
I know we've got "modern" being using @shopify/react-testing
and "legacy" using enzyme
but I'm not clear on how a test opts into one or the other and what helpers we expose publically for others to use.
@@ -0,0 +1,3 @@ | |||
import React from 'react'; | |||
|
|||
export const WithinContentContext = React.createContext<boolean>(false); |
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.
You don't need the as TS can infer the type from the argument you pass in
|
||
describe('TestProvider', () => { | ||
it('renders in strict mode', () => { | ||
it("doesn't renders in strict mode by default", () => { |
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("doesn't renders in strict mode by default", () => { | |
it("doesn't render in strict mode by default", () => { |
src/index.ts
Outdated
ScrollLockManagerContext as __UNSAFE_SECRET_INTERNAL_SCROLL_LOCK_MANAGER_CONTEXT, | ||
} from './utilities/scroll-lock-manager'; | ||
export { | ||
WithinContentContext as __UNSAFE_SECRET_INTERNAL_WITHIN_CONTENT_CONTEXT, |
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 we want this to be a public not-scary API for now, so that custom cards can take advantage of it per #1303
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.
We shouldn't really expose contexts
. There's a private part of the app, and having this publicly available could harm us in the future. We shouldn't be promoting the use of it. From a design perspective if we decide that WithinContext
is a variant that could be opted into we should probably expose a prop, rather than our internals. However keeping the export here will allow it to be used, if needed (which was by forked components in web).
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.
As an alternative name we could do with __UNSTABLE_INTERNAL_WITHIN_CONTENT_CONTEXT
. And we should mention that _UNSTABLE
is to be used at the consumer's own risk and may receive breaking changes in minor versions
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.
On second thoughts keeping this scarey means its' not strictly speaking public API and we can change it up in a minor version. Let's keep it this way;
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.
(Totally missed your replies when I commented there, and it sounds like I ignored ya, sorry)
You point about not exposing contexts is a good one. Keeping this scary internal name now gives us a chance to come up with a proper design for this later.
Poking about with naming - unstable and unsafe suggest they might become stable later, but it sounds like we might want to go a different route with how we expose contexts. React has __SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED, so perhaps drop that part of the prefix and have __SECRET_INTERNAL_SCROLL_LOCK_MANAGER_CONTEXT
and __SECRET_INTERNAL_WITHIN_CONTENT_CONTEXT
?
@@ -0,0 +1,68 @@ | |||
import React from 'react'; |
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'm not sure what you mean by this? I'm not clear on the value of having this living in its own file
import {mount} from 'enzyme'; | ||
import {mountWithAppProvider} from 'test-utilities/legacy'; | ||
import ContextualSaveBar from '../ContextualSaveBar'; | ||
import {FrameContext, createFrameContext} from '../../Frame'; |
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.
FrameProvider is inside TestProvider
so we just need to pass our spies through
expect(fileUpload.find(TextStyle).text()).toBe('or drop files to upload'); | ||
}); | ||
|
||
it('does not use default action title and hint when props are changed', () => { |
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.
This test failed in the conversion and I deleted it since we don't need it anymore. When this component was a class we stored this on state and this was a regression test however we don't follow that pattern in our functional component so 🔥
} | ||
|
||
export default UnstyledLink; | ||
export default React.memo(UnstyledLink); |
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 managed to get this working in web so I'm sneakily adding it back 😄
export * from './components'; | ||
|
||
export {UnstyledLinkProps, LinkLikeComponent} from './utilities/link'; | ||
export {createPolarisContext} from './utilities/create-polaris-context'; |
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.
Just as a quick note in a follow-up PR I can probably remove this entirely from polaris
@BPScott 👌 |
e97b761
to
32d871f
Compare
</I18nContext.Provider> | ||
); | ||
} | ||
const appBridge: any = app.appBridge; |
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 also just spotted that the appBridge typing in tests is kinda wonky. I'll try and come up with a plan once this and #1828 are merged.
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.
Yup app bridge is using {}
in tests as a mock but isn't actually a valid type 😬
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.
This looks amazing.
One petty naming bikeshed around naming our "internal" exports in https://github.com/Shopify/polaris-react/pull/1810/files#r303160285 but other than that , this is good to go.
I think I've got some half-baked ideas for simplifying some of our types in our tests but that's a plan for later.
Something that's been gnawing at me over the past two days: The more I think, the more I'm unsure of adding FrameContext into the TestProvider. Without it the TestProvider is an AppProvider but with the ability to set not-usually-accessible items like the sticky and scroll managers. That symmetry feels very useful as it makes it easy to describe what TestProvider does - it's the same as AppProvider, but for use in tests so you can set mocks (this helps us find a home for TestProvider too, as we could place it in src/AppProvider (this needs a little more thought)). It also makes the "mountWithAppProvider" function name a bit more true as it only adds the contexts provided by AppProvider and nothing else. Adding a value for FrameContext into the mix muddies the water and I'm not convinced the extra cognitive overhead to understand TestProvider as being a bit different from AppProvider is worth the slight extra convenience of all our contexts in one place - especially as 90% of the time you don't care about defining a value for FrameContext, you only care about appProvider based things. For internal testing, we could leverage react-testing's about-to-land ability to extend configs. to create a What do you think to leaving FrameContext out of TestProvider and undoing the changes to ContextualSaveBar, Loading and Toast? (eventually those files would be updated to use a mountWithFrame function as described above) |
I would say, the ideal situation for us is to not have to worry about what In |
Although if we did decide to have multiple mounts in polaris, I would be ok with that. I still think it's nice to have a |
To me that feels like many tests will have additional overhead that isn't needed. It won't have any noticeable impact on performance but it makes it a little hard to understand what is relevant and needed for a given test. e.g. "In this test for Button it is wrapped in a FrameContext Provider - does it need data from that context?' - the answer is no, but you can't infer that from looking at the test code. Ok, I'm happy to merge this as is as I've got some follow-up work that needs it, and we can continue to go back and forth on this later :) |
32d871f
to
c62c6df
Compare
WHY are these changes introduced?
Apps consuming polaris don't have a way to use our internal contexts
There's a types issue when reacting context
WHAT is this pull request doing?
test-utilities/react-testing
src/utilities