-
Notifications
You must be signed in to change notification settings - Fork 25
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix(nova-react-test-utils): adjust types to properly support pure Rel…
…ay components (#113) * support relay * Change files --------- Co-authored-by: Stanislaw Wilczynski <[email protected]>
- Loading branch information
1 parent
02451e6
commit 5199008
Showing
14 changed files
with
884 additions
and
3 deletions.
There are no files selected for viewing
7 changes: 7 additions & 0 deletions
7
change/@nova-examples-2a4cbbef-025b-4b85-a815-97cb940d994e.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
{ | ||
"type": "minor", | ||
"comment": "showcase pure relay examples", | ||
"packageName": "@nova/examples", | ||
"email": "[email protected]", | ||
"dependentChangeType": "patch" | ||
} |
7 changes: 7 additions & 0 deletions
7
change/@nova-react-test-utils-7f1fc43c-3577-4483-97c6-258ed0c619c8.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
{ | ||
"type": "patch", | ||
"comment": "adjust types to work well with relay graphql tag", | ||
"packageName": "@nova/react-test-utils", | ||
"email": "[email protected]", | ||
"dependentChangeType": "patch" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
213 changes: 213 additions & 0 deletions
213
packages/examples/src/relay/pure-relay/Feedback.stories.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,213 @@ | ||
import { graphql } from "react-relay"; | ||
import { | ||
getNovaDecorator, | ||
getNovaEnvironmentForStory, | ||
MockPayloadGenerator as PayloadGenerator, | ||
type WithoutFragmentRefs, | ||
type WithNovaEnvironment, | ||
EventingProvider, | ||
getOperationName, | ||
getOperationType, | ||
} from "@nova/react-test-utils/relay"; | ||
import type { Meta, StoryObj } from "@storybook/react"; | ||
import { userEvent, waitFor, within, expect } from "@storybook/test"; | ||
import type { TypeMap } from "../../__generated__/schema.all.interface"; | ||
import { FeedbackComponent } from "./Feedback"; | ||
import type { FeedbackStoryRelayQuery } from "./__generated__/FeedbackStoryRelayQuery.graphql"; | ||
import { getSchema } from "../../testing-utils/getSchema"; | ||
import * as React from "react"; | ||
import type { events } from "../../events/events"; | ||
|
||
const schema = getSchema(); | ||
|
||
const MockPayloadGenerator = new PayloadGenerator(schema); | ||
|
||
const meta = { | ||
component: FeedbackComponent, | ||
decorators: [getNovaDecorator(schema)], | ||
parameters: { | ||
novaEnvironment: { | ||
query: graphql` | ||
query FeedbackStoryRelayQuery($id: ID!) @relay_test_operation { | ||
feedback(id: $id) { | ||
...Feedback_feedbackRelayFragment | ||
} | ||
} | ||
`, | ||
variables: { id: "42" }, | ||
referenceEntries: { | ||
feedback: (data) => data?.feedback, | ||
}, | ||
}, | ||
} satisfies WithNovaEnvironment<FeedbackStoryRelayQuery, TypeMap>, | ||
} satisfies Meta<typeof FeedbackComponent>; | ||
|
||
export default meta; | ||
type Story = StoryObj<WithoutFragmentRefs<typeof meta>>; | ||
|
||
export const AutoGeneratedDataOnly: Story = {}; | ||
|
||
export const Primary: Story = { | ||
parameters: { | ||
novaEnvironment: { | ||
resolvers: { | ||
Feedback: () => sampleFeedback, | ||
}, | ||
}, | ||
} satisfies WithNovaEnvironment<FeedbackStoryRelayQuery, TypeMap>, | ||
}; | ||
|
||
export const Liked: Story = { | ||
parameters: { | ||
novaEnvironment: { | ||
resolvers: { | ||
Feedback: () => ({ | ||
...sampleFeedback, | ||
doesViewerLike: true, | ||
}), | ||
}, | ||
}, | ||
} satisfies WithNovaEnvironment<FeedbackStoryRelayQuery, TypeMap>, | ||
}; | ||
|
||
const likeResolvers = { | ||
Feedback: () => sampleFeedback, | ||
FeedbackLikeMutationResult: () => ({ | ||
feedback: { | ||
...sampleFeedback, | ||
doesViewerLike: true, | ||
}, | ||
}), | ||
}; | ||
|
||
export const Like: Story = { | ||
parameters: { | ||
novaEnvironment: { | ||
resolvers: likeResolvers, | ||
}, | ||
} satisfies WithNovaEnvironment<FeedbackStoryRelayQuery, TypeMap>, | ||
play: async (context) => { | ||
const container = within(context.canvasElement); | ||
const likeButton = await container.findByRole("button", { name: "Like" }); | ||
await userEvent.click(likeButton); | ||
|
||
const { | ||
graphql: { mock }, | ||
} = getNovaEnvironmentForStory(context); | ||
|
||
await waitFor(async () => { | ||
const operation = mock.getMostRecentOperation(); | ||
await expect(operation).toBeDefined(); | ||
}); | ||
await mock.resolveMostRecentOperation((operation) => { | ||
return MockPayloadGenerator.generate(operation, likeResolvers); | ||
}); | ||
}, | ||
}; | ||
|
||
export const ArtificialFailureToShowcaseDecoratorBehaviorInCaseOfADevCausedError: Story = | ||
{ | ||
parameters: { | ||
novaEnvironment: { | ||
enableQueuedMockResolvers: false, | ||
}, | ||
} satisfies WithNovaEnvironment<FeedbackStoryRelayQuery, TypeMap>, | ||
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 LikeFailure: Story = { | ||
parameters: { | ||
novaEnvironment: { | ||
enableQueuedMockResolvers: false, | ||
}, | ||
} satisfies WithNovaEnvironment<FeedbackStoryRelayQuery, TypeMap>, | ||
play: async (context) => { | ||
const container = within(context.canvasElement); | ||
const { | ||
graphql: { mock }, | ||
} = getNovaEnvironmentForStory(context); | ||
|
||
await waitFor(async () => { | ||
const operation = mock.getMostRecentOperation(); | ||
await expect(operation).toBeDefined(); | ||
}); | ||
const operation = mock.getMostRecentOperation(); | ||
const operationName = getOperationName(operation); | ||
const operationType = getOperationType(operation); | ||
expect(operationName).toEqual("FeedbackStoryRelayQuery"); | ||
expect(operationType).toEqual("query"); | ||
await mock.resolveMostRecentOperation((operation) => { | ||
return MockPayloadGenerator.generate(operation, { | ||
Feedback: () => sampleFeedback, | ||
}); | ||
}); | ||
const likeButton = await container.findByRole("button", { name: "Like" }); | ||
userEvent.click(likeButton); | ||
await waitFor(async () => { | ||
const operation = mock.getMostRecentOperation(); | ||
expect(operation).toBeDefined(); | ||
}); | ||
const nextOperation = mock.getMostRecentOperation(); | ||
const nextOperationName = getOperationName(nextOperation); | ||
const nextOperationType = getOperationType(nextOperation); | ||
expect(nextOperationName).toEqual("FeedbackComponent_RelayLikeMutation"); | ||
expect(nextOperationType).toEqual("mutation"); | ||
await mock.rejectMostRecentOperation(new Error("Like failed")); | ||
await container.findByText("Something went wrong"); | ||
}, | ||
}; | ||
|
||
const FeedbackWithDeleteDialog = (props: Story["args"]) => { | ||
const [open, setOpen] = React.useState(false); | ||
const [text, setText] = React.useState(""); | ||
return ( | ||
<EventingProvider<typeof events> | ||
eventMap={{ | ||
onDeleteFeedback: (eventWrapper) => { | ||
setOpen(true); | ||
setText(eventWrapper.event.data().feedbackText); | ||
return Promise.resolve(); | ||
}, | ||
}} | ||
> | ||
{/* eslint-disable-next-line @typescript-eslint/ban-ts-comment */} | ||
{/* @ts-ignore we know feedback is passed through decorator */} | ||
<FeedbackComponent {...props} /> | ||
<dialog open={open}> | ||
<button onClick={() => setOpen(false)}>Cancel</button> | ||
Are you sure you want to delete feedback "{text}" | ||
</dialog> | ||
</EventingProvider> | ||
); | ||
}; | ||
|
||
export const WithDeleteDialog: Story = { | ||
...Primary, | ||
render: (args) => <FeedbackWithDeleteDialog {...args} />, | ||
play: async (context) => { | ||
const container = within(context.canvasElement); | ||
const deleteButton = await container.findByRole("button", { | ||
name: "Delete feedback", | ||
}); | ||
await userEvent.click(deleteButton); | ||
const dialog = await container.findByRole("dialog"); | ||
await expect(dialog).toBeInTheDocument(); | ||
}, | ||
}; | ||
|
||
const sampleFeedback = { | ||
id: "42", | ||
message: { | ||
text: "Feedback title", | ||
}, | ||
doesViewerLike: false, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
import { composeStories } from "@storybook/react"; | ||
import * as stories from "./Feedback.stories"; | ||
import { render, screen } 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, | ||
LikeFailure, | ||
} = composeStories(stories); | ||
|
||
describe("Feedback", () => { | ||
it("should show an error if the like button fails", async () => { | ||
const { container } = render(<LikeFailure />); | ||
await executePlayFunction( | ||
LikeFailure, | ||
prepareStoryContextForTest(LikeFailure, container), | ||
); | ||
const error = await screen.findByText("Something went wrong"); | ||
expect(error).toBeInTheDocument(); | ||
}); | ||
it("throws an error when the developer makes a mistake", async () => { | ||
const { container } = render( | ||
<ArtificialFailureToShowcaseDecoratorBehaviorInCaseOfADevCausedError />, | ||
); | ||
const context = prepareStoryContextForTest( | ||
ArtificialFailureToShowcaseDecoratorBehaviorInCaseOfADevCausedError, | ||
container, | ||
); | ||
expect(async () => { | ||
await executePlayFunction( | ||
ArtificialFailureToShowcaseDecoratorBehaviorInCaseOfADevCausedError, | ||
context, | ||
); | ||
}).rejects.toThrowError("Query failed"); | ||
}); | ||
}); |
Oops, something went wrong.