From 7f2a3b457812f13809bcd9e3a29779faf4ae478d Mon Sep 17 00:00:00 2001 From: Stanislaw Wilczynski Date: Thu, 19 Sep 2024 11:05:00 +0200 Subject: [PATCH 1/4] fix deps and improve comment --- packages/examples/jest.config.ts | 3 +-- packages/nova-react-test-utils/package.json | 3 --- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/packages/examples/jest.config.ts b/packages/examples/jest.config.ts index 5a3d94d..2cb9ee4 100644 --- a/packages/examples/jest.config.ts +++ b/packages/examples/jest.config.ts @@ -4,8 +4,7 @@ export default { ...config, transform: { "\\.(gql|graphql)$": "@graphql-tools/jest-transform", - // Needed for packages/examples to load relay compiler generated artifacts. - // It would be better if it could be config local to examples package but haven't found a way to configure it with just + // Needed to load relay compiler generated artifacts. ".+[\\\\/]relay[\\\\/].+\\.tsx?$": "@graphitation/embedded-document-artefact-loader/ts-jest", "^.+\\.tsx?$": [ diff --git a/packages/nova-react-test-utils/package.json b/packages/nova-react-test-utils/package.json index 474ef48..9afdafb 100644 --- a/packages/nova-react-test-utils/package.json +++ b/packages/nova-react-test-utils/package.json @@ -77,10 +77,7 @@ "@storybook/types": "^7.6.19", "@testing-library/react": "^15.0.0", "@types/jest": "^29.2.0", - "@types/react": "^18.3.1", "@types/react-relay": "^16.0.0", - "@types/relay-runtime": "^17.0.0", - "@types/relay-test-utils": "^17.0.0", "@types/react-test-renderer": "^18.3.0", "monorepo-scripts": "*", "react": "^18.3.1", From 8897ab58c2bcc51e862a07e3412b406378d8f7be Mon Sep 17 00:00:00 2001 From: Stanislaw Wilczynski Date: Thu, 19 Sep 2024 11:21:41 +0200 Subject: [PATCH 2/4] throw error from apollo and add tests to assert unified behavior --- .../src/apollo/Feedback/Feedback.stories.tsx | 22 ++++++++++++++- .../Feedback/FeedbackContainer.stories.ts | 18 ++++++++++++ .../src/apollo/Feedback/Feedbacl.test.tsx | 28 +++++++++++++++++++ .../src/relay/Feedback/Feedback.stories.tsx | 19 +++++++++++++ .../src/relay/Feedback/Feedback.test.tsx | 28 +++++++++++++++++++ .../Feedback/FeedbackContainer.stories.ts | 18 ++++++++++++ .../storybook-nova-decorator-shared.tsx | 8 ++++-- 7 files changed, 138 insertions(+), 3 deletions(-) create mode 100644 packages/examples/src/apollo/Feedback/Feedbacl.test.tsx create mode 100644 packages/examples/src/relay/Feedback/Feedback.test.tsx diff --git a/packages/examples/src/apollo/Feedback/Feedback.stories.tsx b/packages/examples/src/apollo/Feedback/Feedback.stories.tsx index 4734b60..44e198e 100644 --- a/packages/examples/src/apollo/Feedback/Feedback.stories.tsx +++ b/packages/examples/src/apollo/Feedback/Feedback.stories.tsx @@ -2,11 +2,12 @@ import { graphql } from "@nova/react"; import { EventingProvider, getNovaDecorator, + getNovaEnvironmentForStory, type WithNovaEnvironment, type WithoutFragmentRefs, } from "@nova/react-test-utils/apollo"; import type { Meta, StoryObj } from "@storybook/react"; -import { expect, userEvent, within } from "@storybook/test"; +import { expect, userEvent, waitFor, within } from "@storybook/test"; import { getSchema } from "../../testing-utils/getSchema"; import type { TypeMap } from "../../__generated__/schema.all.interface"; import { FeedbackComponent, Feedback_feedbackFragment } from "./Feedback"; @@ -84,6 +85,25 @@ export const Like: Story = { }, }; +export const ArtificialFailureToShowcaseDecoratorBehaviorInCaseOfADevCausedError: Story = + { + parameters: { + novaEnvironment: { + enableQueuedMockResolvers: false, + }, + } satisfies WithNovaEnvironment, + play: async (context) => { + const { + graphql: { mock }, + } = getNovaEnvironmentForStory(context); + await waitFor(async () => { + const operation = mock.getMostRecentOperation(); + await expect(operation).toBeDefined(); + }); + await mock.rejectMostRecentOperation(new Error("Query failed")); + }, + }; + const FeedbackWithDeleteDialog = (props: Story["args"]) => { const [open, setOpen] = React.useState(false); const [text, setText] = React.useState(""); diff --git a/packages/examples/src/apollo/Feedback/FeedbackContainer.stories.ts b/packages/examples/src/apollo/Feedback/FeedbackContainer.stories.ts index 4e1f340..68c91e0 100644 --- a/packages/examples/src/apollo/Feedback/FeedbackContainer.stories.ts +++ b/packages/examples/src/apollo/Feedback/FeedbackContainer.stories.ts @@ -96,6 +96,24 @@ export const LikeFailure: Story = { }, }; +export const QueryFailure: Story = { + parameters: { + novaEnvironment: { + enableQueuedMockResolvers: false, + }, + } satisfies WithNovaEnvironment, + play: async (context) => { + const { + graphql: { mock }, + } = getNovaEnvironmentForStory(context); + await waitFor(async () => { + const operation = mock.getMostRecentOperation(); + await expect(operation).toBeDefined(); + }); + await mock.rejectMostRecentOperation(new Error("Query failed")); + }, +}; + export const Loading: Story = { parameters: { novaEnvironment: { diff --git a/packages/examples/src/apollo/Feedback/Feedbacl.test.tsx b/packages/examples/src/apollo/Feedback/Feedbacl.test.tsx new file mode 100644 index 0000000..a131d87 --- /dev/null +++ b/packages/examples/src/apollo/Feedback/Feedbacl.test.tsx @@ -0,0 +1,28 @@ +import { composeStories } from "@storybook/react"; +import * as stories from "./Feedback.stories"; +import { render } from "@testing-library/react"; +import * as React from "react"; +import "@testing-library/jest-dom"; +import { prepareStoryContextForTest } from "@nova/react-test-utils/apollo"; +import { executePlayFunction } from "../../testing-utils/executePlayFunction"; + +const { ArtificialFailureToShowcaseDecoratorBehaviorInCaseOfADevCausedError } = + composeStories(stories); + +describe("Feedback", () => { + it("throws an error when the developer makes a mistake", async () => { + const { container } = render( + , + ); + const context = prepareStoryContextForTest( + ArtificialFailureToShowcaseDecoratorBehaviorInCaseOfADevCausedError, + container, + ); + expect(async () => { + await executePlayFunction( + ArtificialFailureToShowcaseDecoratorBehaviorInCaseOfADevCausedError, + context, + ); + }).rejects.toThrowError("Query failed"); + }); +}); diff --git a/packages/examples/src/relay/Feedback/Feedback.stories.tsx b/packages/examples/src/relay/Feedback/Feedback.stories.tsx index 7a52fca..7f26519 100644 --- a/packages/examples/src/relay/Feedback/Feedback.stories.tsx +++ b/packages/examples/src/relay/Feedback/Feedback.stories.tsx @@ -103,6 +103,25 @@ export const Like: Story = { }, }; +export const ArtificialFailureToShowcaseDecoratorBehaviorInCaseOfADevCausedError: Story = + { + parameters: { + novaEnvironment: { + enableQueuedMockResolvers: false, + }, + } satisfies WithNovaEnvironment, + play: async (context) => { + const { + graphql: { mock }, + } = getNovaEnvironmentForStory(context); + await waitFor(async () => { + const operation = mock.getMostRecentOperation(); + await expect(operation).toBeDefined(); + }); + await mock.rejectMostRecentOperation(new Error("Query failed")); + }, + }; + const FeedbackWithDeleteDialog = (props: Story["args"]) => { const [open, setOpen] = React.useState(false); const [text, setText] = React.useState(""); diff --git a/packages/examples/src/relay/Feedback/Feedback.test.tsx b/packages/examples/src/relay/Feedback/Feedback.test.tsx new file mode 100644 index 0000000..f523571 --- /dev/null +++ b/packages/examples/src/relay/Feedback/Feedback.test.tsx @@ -0,0 +1,28 @@ +import { composeStories } from "@storybook/react"; +import * as stories from "./Feedback.stories"; +import { render } from "@testing-library/react"; +import * as React from "react"; +import "@testing-library/jest-dom"; +import { executePlayFunction } from "../../testing-utils/executePlayFunction"; +import { prepareStoryContextForTest } from "@nova/react-test-utils/relay"; + +const { ArtificialFailureToShowcaseDecoratorBehaviorInCaseOfADevCausedError } = + composeStories(stories); + +describe("Feedback", () => { + it("throws an error when the developer makes a mistake", async () => { + const { container } = render( + , + ); + const context = prepareStoryContextForTest( + ArtificialFailureToShowcaseDecoratorBehaviorInCaseOfADevCausedError, + container, + ); + expect(async () => { + await executePlayFunction( + ArtificialFailureToShowcaseDecoratorBehaviorInCaseOfADevCausedError, + context, + ); + }).rejects.toThrowError("Query failed"); + }); +}); diff --git a/packages/examples/src/relay/Feedback/FeedbackContainer.stories.ts b/packages/examples/src/relay/Feedback/FeedbackContainer.stories.ts index f4fa32d..396d5b6 100644 --- a/packages/examples/src/relay/Feedback/FeedbackContainer.stories.ts +++ b/packages/examples/src/relay/Feedback/FeedbackContainer.stories.ts @@ -125,6 +125,24 @@ export const LikeFailure: Story = { }, }; +export const QueryFailure: Story = { + parameters: { + novaEnvironment: { + enableQueuedMockResolvers: false, + }, + } satisfies WithNovaEnvironment, + play: async (context) => { + const { + graphql: { mock }, + } = getNovaEnvironmentForStory(context); + await waitFor(async () => { + const operation = mock.getMostRecentOperation(); + await expect(operation).toBeDefined(); + }); + await mock.rejectMostRecentOperation(new Error("Query failed")); + }, +}; + export const Loading: Story = { parameters: { novaEnvironment: { diff --git a/packages/nova-react-test-utils/src/shared/storybook-nova-decorator-shared.tsx b/packages/nova-react-test-utils/src/shared/storybook-nova-decorator-shared.tsx index de1f00d..5231a50 100644 --- a/packages/nova-react-test-utils/src/shared/storybook-nova-decorator-shared.tsx +++ b/packages/nova-react-test-utils/src/shared/storybook-nova-decorator-shared.tsx @@ -82,10 +82,14 @@ export function getRenderer( ): React.FC> { if (query) { const Renderer: React.FC = () => { - const { data } = useLazyLoadQuery(query, variables); + const { data, error } = useLazyLoadQuery(query, variables); // apollo does not suspend, but returns undefined data if (!data) { + if (error) { + // apollo returns an error, while Relay throws, let's align the behavior + throw error; + } return
Loading...
; } @@ -95,7 +99,7 @@ export function getRenderer( Object.assign(context.args, Object.fromEntries(entries)); return <>{getStory(context)}; }; - return Renderer; + return () => ; } else { return () => { return <>{getStory(context)}; From f0bf9761267e8b9d0199a6f3293c6d243651979c Mon Sep 17 00:00:00 2001 From: Stanislaw Wilczynski Date: Thu, 19 Sep 2024 11:22:29 +0200 Subject: [PATCH 3/4] Change files --- ...nova-examples-de823586-089b-422c-8fcc-75c183c08260.json | 7 +++++++ ...ct-test-utils-ac20973e-e120-4dbc-bb0d-4adbfba1c1c7.json | 7 +++++++ 2 files changed, 14 insertions(+) create mode 100644 change/@nova-examples-de823586-089b-422c-8fcc-75c183c08260.json create mode 100644 change/@nova-react-test-utils-ac20973e-e120-4dbc-bb0d-4adbfba1c1c7.json diff --git a/change/@nova-examples-de823586-089b-422c-8fcc-75c183c08260.json b/change/@nova-examples-de823586-089b-422c-8fcc-75c183c08260.json new file mode 100644 index 0000000..594d3e4 --- /dev/null +++ b/change/@nova-examples-de823586-089b-422c-8fcc-75c183c08260.json @@ -0,0 +1,7 @@ +{ + "type": "minor", + "comment": "showcase error behavior", + "packageName": "@nova/examples", + "email": "Stanislaw.Wilczynski@microsoft.com", + "dependentChangeType": "patch" +} diff --git a/change/@nova-react-test-utils-ac20973e-e120-4dbc-bb0d-4adbfba1c1c7.json b/change/@nova-react-test-utils-ac20973e-e120-4dbc-bb0d-4adbfba1c1c7.json new file mode 100644 index 0000000..bd4b3a5 --- /dev/null +++ b/change/@nova-react-test-utils-ac20973e-e120-4dbc-bb0d-4adbfba1c1c7.json @@ -0,0 +1,7 @@ +{ + "type": "patch", + "comment": "throw error from apollo in decorator", + "packageName": "@nova/react-test-utils", + "email": "Stanislaw.Wilczynski@microsoft.com", + "dependentChangeType": "patch" +} From 6e330f14fd4a9a85764425a4ab95e5b4c41862d9 Mon Sep 17 00:00:00 2001 From: Stanislaw Wilczynski Date: Thu, 19 Sep 2024 11:33:29 +0200 Subject: [PATCH 4/4] rename file --- .../src/apollo/Feedback/{Feedbacl.test.tsx => Feedback.test.tsx} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename packages/examples/src/apollo/Feedback/{Feedbacl.test.tsx => Feedback.test.tsx} (100%) diff --git a/packages/examples/src/apollo/Feedback/Feedbacl.test.tsx b/packages/examples/src/apollo/Feedback/Feedback.test.tsx similarity index 100% rename from packages/examples/src/apollo/Feedback/Feedbacl.test.tsx rename to packages/examples/src/apollo/Feedback/Feedback.test.tsx