From 544e8218d9752c33359f6fbb83a9c0f346d25c32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stanis=C5=82aw?= Date: Fri, 25 Oct 2024 18:53:26 +0200 Subject: [PATCH] fix(nova-react-test-utils): bump relay, relax generate types in relay case, add example and docs for working with client extensions (#127) * bump relay to 18 and mention client extensions * add story with extensions * bump graphitation packages * fix readme * rearrange docs * fix readme again * fix one story * Change files --------- Co-authored-by: Stanislaw Wilczynski --- ...-8a8892ea-2b9e-4bc1-ae16-852559f733f7.json | 7 + ...-2428c3ea-ce99-4fe4-aca6-eda0689d53a3.json | 7 + ...-78375496-e653-4a6f-ace6-d99ef9abfed1.json | 7 + packages/examples/codegen.yml | 1 + packages/examples/package.json | 15 ++- packages/examples/relay.config.json | 15 ++- .../src/__generated__/schema.all.interface.ts | 18 +++ .../examples/src/clientExtensions.graphql | 7 + .../FeedbackComponent_LikeMutation.graphql.ts | 4 +- .../FeedbackContainerQuery.graphql.ts | 4 +- .../FeedbackStoryQuery.graphql.ts | 4 +- .../Feedback_feedbackFragment.graphql.ts | 4 +- .../src/relay/pure-relay/Feedback.stories.tsx | 44 ++++-- .../src/relay/pure-relay/Feedback.tsx | 10 ++ ...backComponent_RelayLikeMutation.graphql.ts | 4 +- .../FeedbackStoryRelayQuery.graphql.ts | 65 ++++++++- .../Feedback_feedbackRelayFragment.graphql.ts | 4 +- .../Feedback_viewDataRelayFragment.graphql.ts | 47 +++++++ packages/nova-react-test-utils/README.md | 61 ++++++++- packages/nova-react-test-utils/package.json | 23 ++-- .../relay/storybook-nova-decorator-relay.tsx | 23 +++- .../src/relay/test-utils.tsx | 2 +- packages/nova-react/package.json | 4 +- scripts/package.json | 2 +- yarn.lock | 125 +++++++++--------- 25 files changed, 386 insertions(+), 121 deletions(-) create mode 100644 change/@nova-examples-8a8892ea-2b9e-4bc1-ae16-852559f733f7.json create mode 100644 change/@nova-react-2428c3ea-ce99-4fe4-aca6-eda0689d53a3.json create mode 100644 change/@nova-react-test-utils-78375496-e653-4a6f-ace6-d99ef9abfed1.json create mode 100644 packages/examples/src/clientExtensions.graphql create mode 100644 packages/examples/src/relay/pure-relay/__generated__/Feedback_viewDataRelayFragment.graphql.ts diff --git a/change/@nova-examples-8a8892ea-2b9e-4bc1-ae16-852559f733f7.json b/change/@nova-examples-8a8892ea-2b9e-4bc1-ae16-852559f733f7.json new file mode 100644 index 0000000..d62ecb5 --- /dev/null +++ b/change/@nova-examples-8a8892ea-2b9e-4bc1-ae16-852559f733f7.json @@ -0,0 +1,7 @@ +{ + "type": "patch", + "comment": "add example of overriding generate function", + "packageName": "@nova/examples", + "email": "Stanislaw.Wilczynski@microsoft.com", + "dependentChangeType": "patch" +} diff --git a/change/@nova-react-2428c3ea-ce99-4fe4-aca6-eda0689d53a3.json b/change/@nova-react-2428c3ea-ce99-4fe4-aca6-eda0689d53a3.json new file mode 100644 index 0000000..1709dc6 --- /dev/null +++ b/change/@nova-react-2428c3ea-ce99-4fe4-aca6-eda0689d53a3.json @@ -0,0 +1,7 @@ +{ + "type": "patch", + "comment": "bump graphitation packages", + "packageName": "@nova/react", + "email": "Stanislaw.Wilczynski@microsoft.com", + "dependentChangeType": "patch" +} diff --git a/change/@nova-react-test-utils-78375496-e653-4a6f-ace6-d99ef9abfed1.json b/change/@nova-react-test-utils-78375496-e653-4a6f-ace6-d99ef9abfed1.json new file mode 100644 index 0000000..75608bf --- /dev/null +++ b/change/@nova-react-test-utils-78375496-e653-4a6f-ace6-d99ef9abfed1.json @@ -0,0 +1,7 @@ +{ + "type": "patch", + "comment": "relax types for overrding generator", + "packageName": "@nova/react-test-utils", + "email": "Stanislaw.Wilczynski@microsoft.com", + "dependentChangeType": "patch" +} diff --git a/packages/examples/codegen.yml b/packages/examples/codegen.yml index e57b605..71b8ef5 100644 --- a/packages/examples/codegen.yml +++ b/packages/examples/codegen.yml @@ -1,6 +1,7 @@ overwrite: true schema: - "./src/schema.graphql" + - "./src/clientExtensions.graphql" generates: src/__generated__/schema.all.interface.ts: plugins: diff --git a/packages/examples/package.json b/packages/examples/package.json index 5b27961..b4f1701 100644 --- a/packages/examples/package.json +++ b/packages/examples/package.json @@ -24,17 +24,17 @@ "graphql": "^15.5.0", "react": "^18.3.1", "react-dom": "^18.3.1", - "react-relay": "^17.0.0", - "relay-runtime": "^17.0.0" + "react-relay": "^18.0.0", + "relay-runtime": "^18.0.0" }, "devDependencies": { "@babel/core": "^7.20.2", "@babel/preset-env": "^7.20.2", "@babel/preset-react": "^7.18.6", "@babel/preset-typescript": "^7.21.0", - "@graphitation/embedded-document-artefact-loader": "^0.8.0", - "@graphitation/graphql-codegen-typescript-typemap-plugin": "^0.1.6", - "@graphitation/graphql-js-tag": "^0.9.1", + "@graphitation/embedded-document-artefact-loader": "^0.8.5", + "@graphitation/graphql-codegen-typescript-typemap-plugin": "^0.1.9", + "@graphitation/graphql-js-tag": "^0.9.4", "@graphql-codegen/add": "^4.0.1", "@graphql-codegen/cli": "^3.2.2", "@graphql-codegen/typescript": "^3.0.2", @@ -54,11 +54,12 @@ "@types/react": "^18.3.1", "@types/react-dom": "^18.3.0", "@types/react-relay": "^16.0.0", - "@types/relay-runtime": "^17.0.0", + "@types/relay-runtime": "^18.0.0", "esbuild-loader": "^3.0.1", "monorepo-scripts": "*", "prop-types": "15.8.1", - "relay-compiler": "^17.0.0", + "relay-compiler": "^18.0.0", + "relay-test-utils": "^18.0.0", "storybook": "^7.6.19", "typescript": "^5.6.0" }, diff --git a/packages/examples/relay.config.json b/packages/examples/relay.config.json index f81d2a1..e8013d1 100644 --- a/packages/examples/relay.config.json +++ b/packages/examples/relay.config.json @@ -1,5 +1,14 @@ { - "src": "./src/relay", - "schema": "./src/schema.graphql", - "language": "typescript" + "sources": { + "src/relay": "examples" + }, + "projects": { + "examples": { + "language": "typescript", + "schema": "src/schema.graphql", + "schemaExtensions": [ + "src/clientExtensions.graphql" + ] + } + } } diff --git a/packages/examples/src/__generated__/schema.all.interface.ts b/packages/examples/src/__generated__/schema.all.interface.ts index 766c9b1..2907794 100644 --- a/packages/examples/src/__generated__/schema.all.interface.ts +++ b/packages/examples/src/__generated__/schema.all.interface.ts @@ -49,7 +49,9 @@ export type MutationFeedbackLikeArgs = { export type Query = { __typename?: 'Query'; + clientExtension: Maybe; feedback: Feedback; + viewData: Maybe; }; @@ -57,6 +59,11 @@ export type QueryFeedbackArgs = { id: Scalars['ID']; }; +export type ViewData = { + __typename?: 'ViewData'; + viewDataField: Maybe; +}; + export type ResolverTypeWrapper = Promise | T; @@ -136,6 +143,7 @@ export type ResolversTypes = { Mutation: ResolverTypeWrapper<{}>; Query: ResolverTypeWrapper<{}>; String: ResolverTypeWrapper; + ViewData: ResolverTypeWrapper; }; /** Mapping between all available schema types and the resolvers parents */ @@ -149,6 +157,7 @@ export type ResolversParentTypes = { Mutation: {}; Query: {}; String: Scalars['String']; + ViewData: ViewData; }; export type FeedbackResolvers = { @@ -173,7 +182,14 @@ export type MutationResolvers = { + clientExtension: Resolver, ParentType, ContextType>; feedback: Resolver>; + viewData: Resolver, ParentType, ContextType>; +}; + +export type ViewDataResolvers = { + viewDataField: Resolver, ParentType, ContextType>; + __isTypeOf?: IsTypeOfResolverFn; }; export type Resolvers = { @@ -182,6 +198,7 @@ export type Resolvers = { Message: MessageResolvers; Mutation: MutationResolvers; Query: QueryResolvers; + ViewData: ViewDataResolvers; }; @@ -195,4 +212,5 @@ export type TypeMap = { "Mutation": Mutation; "Query": Query; "String": Scalars["String"]; + "ViewData": ViewData; }; diff --git a/packages/examples/src/clientExtensions.graphql b/packages/examples/src/clientExtensions.graphql new file mode 100644 index 0000000..7545002 --- /dev/null +++ b/packages/examples/src/clientExtensions.graphql @@ -0,0 +1,7 @@ +extend type Query { + clientExtension: String + viewData: ViewData +} +type ViewData { + viewDataField: String +} diff --git a/packages/examples/src/relay/Feedback/__generated__/FeedbackComponent_LikeMutation.graphql.ts b/packages/examples/src/relay/Feedback/__generated__/FeedbackComponent_LikeMutation.graphql.ts index 8555b48..ef7911c 100644 --- a/packages/examples/src/relay/Feedback/__generated__/FeedbackComponent_LikeMutation.graphql.ts +++ b/packages/examples/src/relay/Feedback/__generated__/FeedbackComponent_LikeMutation.graphql.ts @@ -1,5 +1,5 @@ /** - * @generated SignedSource<<9e8cbc821d04a9dc43d1118707b7cece>> + * @generated SignedSource<> * @lightSyntaxTransform * @nogrep */ @@ -8,7 +8,7 @@ /* eslint-disable */ // @ts-nocheck -import { ConcreteRequest, Mutation } from 'relay-runtime'; +import { ConcreteRequest } from 'relay-runtime'; export type FeedbackLikeInput = { doesViewerLike: boolean; feedbackId: string; diff --git a/packages/examples/src/relay/Feedback/__generated__/FeedbackContainerQuery.graphql.ts b/packages/examples/src/relay/Feedback/__generated__/FeedbackContainerQuery.graphql.ts index ad10e53..f2de9ff 100644 --- a/packages/examples/src/relay/Feedback/__generated__/FeedbackContainerQuery.graphql.ts +++ b/packages/examples/src/relay/Feedback/__generated__/FeedbackContainerQuery.graphql.ts @@ -1,5 +1,5 @@ /** - * @generated SignedSource<> + * @generated SignedSource<> * @lightSyntaxTransform * @nogrep */ @@ -8,7 +8,7 @@ /* eslint-disable */ // @ts-nocheck -import { ConcreteRequest, Query } from 'relay-runtime'; +import { ConcreteRequest } from 'relay-runtime'; import { FragmentRefs } from "relay-runtime"; export type FeedbackContainerQuery$variables = { id: string; diff --git a/packages/examples/src/relay/Feedback/__generated__/FeedbackStoryQuery.graphql.ts b/packages/examples/src/relay/Feedback/__generated__/FeedbackStoryQuery.graphql.ts index 2b336b1..05b4cfd 100644 --- a/packages/examples/src/relay/Feedback/__generated__/FeedbackStoryQuery.graphql.ts +++ b/packages/examples/src/relay/Feedback/__generated__/FeedbackStoryQuery.graphql.ts @@ -1,5 +1,5 @@ /** - * @generated SignedSource<<25ffc163fef147c94d952bb9a33226e3>> + * @generated SignedSource<> * @lightSyntaxTransform * @nogrep */ @@ -8,7 +8,7 @@ /* eslint-disable */ // @ts-nocheck -import { ConcreteRequest, Query } from 'relay-runtime'; +import { ConcreteRequest } from 'relay-runtime'; import { FragmentRefs } from "relay-runtime"; export type FeedbackStoryQuery$variables = { id: string; diff --git a/packages/examples/src/relay/Feedback/__generated__/Feedback_feedbackFragment.graphql.ts b/packages/examples/src/relay/Feedback/__generated__/Feedback_feedbackFragment.graphql.ts index 871dee5..0f01e21 100644 --- a/packages/examples/src/relay/Feedback/__generated__/Feedback_feedbackFragment.graphql.ts +++ b/packages/examples/src/relay/Feedback/__generated__/Feedback_feedbackFragment.graphql.ts @@ -1,5 +1,5 @@ /** - * @generated SignedSource<<9a9e9576834b9673626fc27f4e27f924>> + * @generated SignedSource<<63e21ec3b051b3e4642bf81fc2a9e0e3>> * @lightSyntaxTransform * @nogrep */ @@ -8,7 +8,7 @@ /* eslint-disable */ // @ts-nocheck -import { Fragment, ReaderFragment } from 'relay-runtime'; +import { ReaderFragment } from 'relay-runtime'; import { FragmentRefs } from "relay-runtime"; export type Feedback_feedbackFragment$data = { readonly doesViewerLike: boolean; diff --git a/packages/examples/src/relay/pure-relay/Feedback.stories.tsx b/packages/examples/src/relay/pure-relay/Feedback.stories.tsx index 8ae5880..808549f 100644 --- a/packages/examples/src/relay/pure-relay/Feedback.stories.tsx +++ b/packages/examples/src/relay/pure-relay/Feedback.stories.tsx @@ -2,13 +2,13 @@ import { graphql } from "react-relay"; import { getNovaDecorator, getNovaEnvironmentForStory, - MockPayloadGenerator as PayloadGenerator, type WithNovaEnvironment, EventingProvider, getOperationName, getOperationType, type StoryObjWithoutFragmentRefs, } from "@nova/react-test-utils/relay"; +import { MockPayloadGenerator } from "relay-test-utils"; import type { Meta } from "@storybook/react"; import { userEvent, waitFor, within, expect } from "@storybook/test"; import type { TypeMap } from "../../__generated__/schema.all.interface"; @@ -21,12 +21,23 @@ import { RecordSource, Store } from "relay-runtime"; const schema = getSchema(); -const MockPayloadGenerator = new PayloadGenerator(schema); - const novaDecorator = getNovaDecorator(schema, { getEnvironmentOptions: () => ({ store: new Store(new RecordSource()), }), + // We add this to verify scenario of using relay's MockPayloadGenerator + generateFunction: (operation, mockResolvers) => { + const result = MockPayloadGenerator.generateWithDefer( + operation, + mockResolvers ?? null, + { + mockClientData: true, + generateDeferredPayload: true, + }, + ); + + return result; + }, }); const meta = { @@ -39,11 +50,20 @@ const meta = { feedback(id: $id) { ...Feedback_feedbackRelayFragment } + viewData { + ...Feedback_viewDataRelayFragment + } } `, variables: { id: "42" }, referenceEntries: { feedback: (data) => data?.feedback, + viewData: (data) => data?.viewData, + }, + resolvers: { + ViewData: () => ({ + viewDataField: "View data field", + }), }, }, } satisfies WithNovaEnvironment, @@ -106,7 +126,7 @@ export const Like: Story = { const operation = mock.getMostRecentOperation(); await expect(operation).toBeDefined(); }); - await mock.resolveMostRecentOperation((operation) => { + mock.resolveMostRecentOperation((operation) => { return MockPayloadGenerator.generate(operation, likeResolvers); }); }, @@ -127,7 +147,7 @@ export const ArtificialFailureToShowcaseDecoratorBehaviorInCaseOfADevCausedError const operation = mock.getMostRecentOperation(); await expect(operation).toBeDefined(); }); - await mock.rejectMostRecentOperation(new Error("Query failed")); + mock.rejectMostRecentOperation(new Error("Query failed")); }, }; @@ -152,10 +172,14 @@ export const LikeFailure: Story = { const operationType = getOperationType(operation); expect(operationName).toEqual("FeedbackStoryRelayQuery"); expect(operationType).toEqual("query"); - await mock.resolveMostRecentOperation((operation) => { - return MockPayloadGenerator.generate(operation, { - Feedback: () => sampleFeedback, - }); + mock.resolveMostRecentOperation((operation) => { + return MockPayloadGenerator.generate( + operation, + { + Feedback: () => sampleFeedback, + }, + { mockClientData: true }, + ); }); const likeButton = await container.findByRole("button", { name: "Like" }); userEvent.click(likeButton); @@ -168,7 +192,7 @@ export const LikeFailure: Story = { const nextOperationType = getOperationType(nextOperation); expect(nextOperationName).toEqual("FeedbackComponent_RelayLikeMutation"); expect(nextOperationType).toEqual("mutation"); - await mock.rejectMostRecentOperation(new Error("Like failed")); + mock.rejectMostRecentOperation(new Error("Like failed")); await container.findByText("Something went wrong"); }, }; diff --git a/packages/examples/src/relay/pure-relay/Feedback.tsx b/packages/examples/src/relay/pure-relay/Feedback.tsx index 8113332..5cea7a6 100644 --- a/packages/examples/src/relay/pure-relay/Feedback.tsx +++ b/packages/examples/src/relay/pure-relay/Feedback.tsx @@ -6,9 +6,11 @@ import { useOnDeleteFeedback, useFeedbackTelemetry, } from "../../events/helpers"; +import type { Feedback_viewDataRelayFragment$key } from "./__generated__/Feedback_viewDataRelayFragment.graphql"; type Props = { feedback: Feedback_feedbackRelayFragment$key; + viewData: Feedback_viewDataRelayFragment$key; }; export const Feedback_feedbackRelayFragment = graphql` @@ -21,9 +23,16 @@ export const Feedback_feedbackRelayFragment = graphql` } `; +export const Feedback_viewDataRelayFragment = graphql` + fragment Feedback_viewDataRelayFragment on ViewData { + viewDataField + } +`; + export const FeedbackComponent = (props: Props) => { const [errorMessage, setErrorMessage] = React.useState(null); const feedback = useFragment(Feedback_feedbackRelayFragment, props.feedback); + const viewData = useFragment(Feedback_viewDataRelayFragment, props.viewData); const [like, isPending] = useMutation( graphql` mutation FeedbackComponent_RelayLikeMutation($input: FeedbackLikeInput!) { @@ -63,6 +72,7 @@ export const FeedbackComponent = (props: Props) => {
{errorMessage}
)} Feedback: {feedback.message.text} +
{viewData.viewDataField}