Comments (21)
We have some tests that stubs the props data of e.g. Fragment Containers. The types were failing since I upgraded because of $fragmentRefs
and $refType
which weren't there before.
My current solution is to remove those just for the specific tests that requires it.
Here is how we do it, maybe this is helpful for you too.
/** Get the Prop Type of a Component */
type PropTypesOf<T> = T extends React.ComponentType<infer R>
? R
: T extends React.Component<infer S> ? S : {};
/** Remove all traces of `$fragmentRefs` and `$refType` from type recursively */
type NoFragmentRefs<T> = T extends object
? {
[P in Exclude<keyof T, " $fragmentRefs" | " $refType">]: NoFragmentRefs<T[P]>
}
: T;
/** Remove fragment refs from a Component Type */
type NoFragmentRefsComponent<T> = ComponentType<
NoFragmentRefs<PropTypesOf<T>>
>;
// Remove fragment refs so we can stub the props.
const CommentContainerN = CommentContainer as NoFragmentRefsComponent<
typeof CommentContainer
>;
it("renders correctly", () => {
const props: PropTypesOf<typeof CommentContainerN> = {
data: {
id: "comment-id",
author: {
username: "Marvin",
},
body: "Woof",
createdAt: "1995-12-17T03:24:00.000Z",
},
};
const wrapper = shallow(<CommentContainerN {...props} />);
expect(wrapper).toMatchSnapshot();
});
from relay-compiler-language-typescript.
I agree - however the point is that the types are different (runtime and compile time) - as the types encode the idea that Relay will be able to find the data needed for any containers rendered using the data.
This is definitely an area where some helper code would come in handy.
Have you considered flipping the problem on its head? I'm thinking something like:
type WithFragmentRefs<T> = T extends object ? { [P in keyof T]: WithFragmentRefs<T[P]> } & { " $fragmentRefs": any; " $refType": any } : T;
function withFragmentRefs<TProps>(props: TProps): WithFragmentRefs<T> {
return props as any;
}
Now you should be able to take any plain props and pass them through the function which should add the needed "magic" types to the props such that you won't get type errors. (warning: I have not tested the above code)
from relay-compiler-language-typescript.
Can you provide some examples of tests where this would be used? I'm not entirely clear on the semantics we're trying to type up here. But it is definitely a good idea to support testing better! :)
from relay-compiler-language-typescript.
Something like this:
const MyComponent = createFragmentContainer(
props => <div>{props.artist.name}</div>,
graphql`
fragment MyComponent_artist on Artist {
name
}
`
)
it("renders the artist’s name", () => {
const artist = { name: "banksy" }
const wrapper = shallow(<MyComponent artist={artist} />)
expect(wrapper.find('div').text()).toEqual("banksy")
})
from relay-compiler-language-typescript.
And this actually works? I didn't think that was allowed? Does Relay generate warnings for this?
from relay-compiler-language-typescript.
You need to mock the Relay API, such that it doesn’t actually do much of anything https://github.com/artsy/emission/blob/master/__mocks__/react-relay.js, but then it works yeah.
It also works without mocking, but then Relay will indeed give a warning facebook/relay#2291.
We’re actually planning on moving to a stubbed local schema in the near future, but we’re definitely not the only ones doing this, so just something to keep in mind.
from relay-compiler-language-typescript.
This is definitely something to think about. I'm kinda torn up on this. Given that I'd not want to miss out on production errors that could occur such as this:
const Parent = createFragmentContainer(ParentComponent, graphql`fragment Parent_data on Todo { id text }`);
const Child = createFragmentContainer(ChildComponent, graphql`fragment Child_data on Todo { text }`);
Given that the Parent renders the Child - if we change the type definitions (to what you proposed) then no type errors will happen here, but still warnings when running the code.
Really what we'd like would be some sort of way to have different type definitions loaded under tests compared to when the actual production code is written. If people are dumping all their tests under one __tests__
folder, then this could be accomplished by simply putting a different tsconfig in that folder, redirecting react-relay
(and friends) to files which strip out the special fragment tainting that we do in the compiler.
from relay-compiler-language-typescript.
Agreed, it’s a slippery slope.
Actually I like the idea of having different types for tests, it could also disable Relay’s masking feature and thus provide types that actually help the dev to know all of the data they need to provide for the tree of the component they are working with.
What are you doing with tests, a local stubbed copy of your schema?
from relay-compiler-language-typescript.
We're still in the process of getting our codebase under test coverage - so far the focus area has been all the server code, so we don't really have that much experience in testing the client code, sadly.
I think testing an in memory copy of the schema is quite a nice way of dealing with it though. But certainly also a much more involved method (it would be nice to be able to test just a single component I think).
If we want to support the idea of having different types to require for tests, the next question is where to put the "mock" type definition files? Do they go into definitely typed? Or this repo? One would more or less have to change the tsconfig under compilerOptions
to be something like:
{
"compilerOptions": {
"paths": {
"react-relay": ["somepathhere"]
}
}
}
from relay-compiler-language-typescript.
I guess that depends on wether or not the user needs to use this plugin for that or not. If it’s not necessarily a hard requirement, then it probably makes more sense to add it to DT.
from relay-compiler-language-typescript.
Just so you know, I’m going to be focussing more on the move to local stubbed schema for now, so maybe it makes most sense to continue this when either you get to wanting such tests or I decide we still need to be able to do this.
from relay-compiler-language-typescript.
Agreed, the only thing is that it would mean adding some valid module path that doesn't actually exist - I don't know what the policy on doing that is inside DT.
Just so you know, I’m going to be focussing more on the move to local stubbed schema for now, so maybe it makes most sense to continue this when either you get to wanting such tests or I decide we still need to be able to do this.
Sounds like a good idea. It is worth remembering though :) But at least this should be something that people can experiment with, without needing our interaction.
from relay-compiler-language-typescript.
I think that as long as we can get it to work in DT it’ll be fine.
Totally! 👍
from relay-compiler-language-typescript.
That’s great, thanks @cvle!
from relay-compiler-language-typescript.
@cvle that's a pretty sweet solution :)
It would be kinda sweet to come up with an easier way to do this though, but something like your solution should work while we still think about that :)
from relay-compiler-language-typescript.
Agree. I'm a bit sceptical with using a different set of types for tests though, it sounds like a configuration nightmare.
Btw I simplified the above solution a little bit:
/** Remove all traces of `$fragmentRefs` and `$refType` from type recursively */
type NoFragmentRefs<T> = T extends object
? {
[P in Exclude<keyof T, " $fragmentRefs" | " $refType">]: NoFragmentRefs<
T[P]
>
}
: T;
/** Remove fragment refs from Proptypes so we can stub the props. */
function removeFragmentRefs<T>(
component: ComponentType<T>
): ComponentType<NoFragmentRefs<T>> {
return component as any;
}
const CommentContainerN = removeFragmentRefs(CommentContainer);
from relay-compiler-language-typescript.
Not sure if I can follow your idea, we use the generated types in our props so they already include the fragmentref related props. Even if we could have the plain props somewhere, I would still need some typescript casting magic to convince it to use a component with these props. I think for now removing the fragmentref related props manually in our tests works good enough.
from relay-compiler-language-typescript.
Well my solution does pretty much the inverse of yours, instead of removing the types from the React component I add them to the props such that you can do:
const props = withFragmentRefs({
viewer: {
id: 'my-id',
name: 'Some name',
},
});
const wrapper = <SomeContainer {...props} />;
expect(wrapper).toMatchSnapshot();
from relay-compiler-language-typescript.
I’ve raised this issue, with the intent that you could have a dummy fragment in your tests that would generate the typings for the data needed by all components in the tree.
import { MyTest_fullTypings } from "__generated__/MyTest_dummy.graphql"
graphql`
fragment MyTest_fullTypings on Foo {
...TestSubjectComponent_foo @relay(maskRecursively: false)
}
`
const mockData: MyTest_fullTypings = {
// ...
}
from relay-compiler-language-typescript.
I’m personally looking into using an actual mocked copy of the schema to run tests against, thus ensuring everything around Relay is actually wired up correctly.
from relay-compiler-language-typescript.
@kastermester gotcha! That's interesting. Wondering how a proper withFragmentRefs
implementation would look like 😄
@alloy also interesting, we are using https://github.com/relay-tools/relay-local-schema to mock out the schema. Currently the data is without types, I was thinking of using https://github.com/dangcuuson/graphql-schema-typescript maybe as we do successfully on the server side, but your idea would also be interesting because the typings are narrowed down.
from relay-compiler-language-typescript.
Related Issues (20)
- Generated refetch definition with variables can not pass the validation of backend, such as github.com/vektah/gqlparser/v2
- Generate GraphQL fragment files for VSCode GraphQL Extension fragment autocompletion HOT 2
- Cannot read property 'createKeywordTypeNode' of undefined HOT 8
- Support for [email protected] HOT 8
- Migrate travis CI release process to GitHub actions HOT 3
- Unknown directive 'appendNode' HOT 1
- module esnext HOT 2
- Why is it not releasing HOT 7
- `require` substitution is not working HOT 6
- doc: explain why configuration with a single artifact directory is suggested
- Update to TS 4.4 and update types to support exactOptionalPropertyTypes HOT 1
- @stream argument naming by schema spec is: `initialCount` not `initial_count` HOT 1
- Invalid AST Node error with graphql@16 HOT 2
- Typescript 4.5 type generation is broken HOT 6
- Relay Compiler generates Typescript Synthax error HOT 3
- Error: Cannot find module 'relay-compiler-language-typescript' on 15.0
- Fragment Typematching
- Input Types generated as readonly HOT 1
- deprecate this package HOT 1
- Issue with import of the __generated__
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from relay-compiler-language-typescript.